diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5bfb1846a5..2e59541056 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -166,7 +166,7 @@ #include "scripting/AccountServicesScriptingInterface.h" #include "scripting/HMDScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" -#include "graphics-scripting/ModelScriptingInterface.h" +#include "graphics-scripting/GraphicsScriptingInterface.h" #include "scripting/SettingsScriptingInterface.h" #include "scripting/WindowScriptingInterface.h" #include "scripting/ControllerScriptingInterface.h" @@ -199,6 +199,7 @@ #include #include #include +#include #include #include @@ -800,7 +801,6 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(true); - DependencyManager::set(); DependencyManager::registerInheritance(); DependencyManager::set(); DependencyManager::set(); @@ -5981,8 +5981,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe scriptEngine->registerGlobalObject("Scene", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Render", _renderEngine->getConfiguration().get()); - ModelScriptingInterface::registerMetaTypes(scriptEngine.data()); - scriptEngine->registerGlobalObject("Model", DependencyManager::get().data()); + GraphicsScriptingInterface::registerMetaTypes(scriptEngine.data()); + scriptEngine->registerGlobalObject("Graphics", DependencyManager::get().data()); scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Reticle", getApplicationCompositor().getReticleInterface()); diff --git a/interface/src/Application.h b/interface/src/Application.h index ae07ebd9dd..ddb8ce11e5 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -72,6 +72,7 @@ #include #include +#include #include "FrameTimingsScriptingInterface.h" #include "Sound.h" diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index 6ccad338c9..25bacf2e7e 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -16,7 +16,6 @@ #include #include "Overlay.h" -namespace model { class Mesh; } class Base3DOverlay : public Overlay, public SpatiallyNestable, public scriptable::ModelProvider { Q_OBJECT using Parent = Overlay; diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index e007591ce0..17bc9e2263 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -75,6 +75,7 @@ void ModelOverlay::update(float deltatime) { render::ScenePointer scene = qApp->getMain3DScene(); render::Transaction transaction; if (_model->needsFixupInScene()) { + emit DependencyManager::get()->modelRemovedFromScene(getID(), NestableType::Overlay, _model); _model->removeFromScene(scene, transaction); _model->addToScene(scene, transaction); @@ -83,6 +84,7 @@ void ModelOverlay::update(float deltatime) { auto modelOverlay = static_cast(&data); modelOverlay->setSubRenderItemIDs(newRenderItemIDs); }); + emit DependencyManager::get()->modelAddedToScene(getID(), NestableType::Overlay, _model); } if (_visibleDirty) { _visibleDirty = false; @@ -107,12 +109,14 @@ void ModelOverlay::update(float deltatime) { bool ModelOverlay::addToScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) { Volume3DOverlay::addToScene(overlay, scene, transaction); _model->addToScene(scene, transaction); + emit DependencyManager::get()->modelAddedToScene(getID(), NestableType::Overlay, _model); return true; } void ModelOverlay::removeFromScene(Overlay::Pointer overlay, const render::ScenePointer& scene, render::Transaction& transaction) { Volume3DOverlay::removeFromScene(overlay, scene, transaction); _model->removeFromScene(scene, transaction); + emit DependencyManager::get()->modelRemovedFromScene(getID(), NestableType::Overlay, _model); transaction.updateItem(getRenderItemID(), [](Overlay& data) { auto modelOverlay = static_cast(&data); modelOverlay->clearSubRenderItemIDs(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 066afac8f5..aaf86be4bf 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -221,7 +221,7 @@ void Avatar::updateAvatarEntities() { return; } - if (getID() == QUuid()) { + if (getID() == QUuid() || getID() == AVATAR_SELF_ID) { return; // wait until MyAvatar gets an ID before doing this. } @@ -577,6 +577,7 @@ void Avatar::addToScene(AvatarSharedPointer self, const render::ScenePointer& sc } _mustFadeIn = true; + emit DependencyManager::get()->modelAddedToScene(getSessionUUID(), NestableType::Avatar, _skeletonModel); } void Avatar::fadeIn(render::ScenePointer scene) { @@ -626,6 +627,7 @@ void Avatar::removeFromScene(AvatarSharedPointer self, const render::ScenePointe for (auto& attachmentModel : _attachmentModels) { attachmentModel->removeFromScene(scene, transaction); } + emit DependencyManager::get()->modelRemovedFromScene(getSessionUUID(), NestableType::Avatar, _skeletonModel); } void Avatar::updateRenderItem(render::Transaction& transaction) { @@ -1764,12 +1766,11 @@ float Avatar::getUnscaledEyeHeightFromSkeleton() const { } scriptable::ScriptableModelBase Avatar::getScriptableModel(bool* ok) { - qDebug() << "Avatar::getScriptableModel" ; if (!_skeletonModel || !_skeletonModel->isLoaded()) { return scriptable::ModelProvider::modelUnavailableError(ok); } scriptable::ScriptableModelBase result = _skeletonModel->getScriptableModel(ok); - result.objectID = getSessionUUID(); + result.objectID = getSessionUUID(); result.mixin({ { "avatarID", getSessionUUID().toString() }, { "url", _skeletonModelURL.toString() }, @@ -1787,4 +1788,4 @@ scriptable::ScriptableModelBase Avatar::getScriptableModel(bool* ok) { *ok = true; } return result; -} +} \ No newline at end of file diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 50301a2507..16c50e08ae 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -1,4 +1,3 @@ - // // Avatar.h // interface/src/avatar @@ -276,6 +275,7 @@ public: virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override; + public slots: // FIXME - these should be migrated to use Pose data instead diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 74759f4fe4..6cf745d7b4 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -56,6 +56,7 @@ public: const uint64_t& getUpdateTime() const { return _updateTime; } virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override { return scriptable::ModelProvider::modelUnavailableError(ok); } + protected: virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); } virtual void onAddToScene(const EntityItemPointer& entity); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 155580d885..8eb48cd1f6 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -950,6 +950,16 @@ QStringList RenderableModelEntityItem::getJointNames() const { return result; } +// FIXME: deprecated; remove >= RC67 +bool RenderableModelEntityItem::getMeshes(MeshProxyList& result) { + auto model = getModel(); + if (!model || !model->isLoaded()) { + return false; + } + BLOCKING_INVOKE_METHOD(model.get(), "getMeshes", Q_RETURN_ARG(MeshProxyList, result)); + return !result.isEmpty(); +} + scriptable::ScriptableModelBase render::entities::ModelEntityRenderer::getScriptableModel(bool* ok) { ModelPointer model; withReadLock([&] { model = _model; }); @@ -998,6 +1008,7 @@ void RenderableModelEntityItem::copyAnimationJointDataToModel() { return; } + bool changed { false }; // relay any inbound joint changes from scripts/animation/network to the model/rig _jointDataLock.withWriteLock([&] { for (int index = 0; index < _localJointData.size(); ++index) { @@ -1005,13 +1016,21 @@ void RenderableModelEntityItem::copyAnimationJointDataToModel() { if (jointData.rotationDirty) { model->setJointRotation(index, true, jointData.joint.rotation, 1.0f); jointData.rotationDirty = false; + changed = true; } if (jointData.translationDirty) { model->setJointTranslation(index, true, jointData.joint.translation, 1.0f); jointData.translationDirty = false; + changed = true; } } }); + + if (changed) { + forEachChild([&](SpatiallyNestablePointer object) { + object->locationChanged(false); + }); + } } using namespace render; @@ -1054,6 +1073,11 @@ void ModelEntityRenderer::removeFromScene(const ScenePointer& scene, Transaction void ModelEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity) { entity->setModel({}); + //emit DependencyManager::get()->modelRemovedFromScene(entity->getID(), NestableType::Entity, _model); +} + +void ModelEntityRenderer::onAddToSceneTyped(const TypedEntityPointer& entity) { + //emit DependencyManager::get()->modelAddedToScene(entity->getID(), NestableType::Entity, _model); } @@ -1280,6 +1304,7 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce auto entityRenderer = static_cast(&data); entityRenderer->clearSubRenderItemIDs(); }); + emit DependencyManager::get()->modelRemovedFromScene(entity->getEntityItemID(), NestableType::Entity, _model); } return; } @@ -1290,6 +1315,11 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce connect(model.get(), &Model::setURLFinished, this, [&](bool didVisualGeometryRequestSucceed) { setKey(didVisualGeometryRequestSucceed); emit requestRenderUpdate(); + auto factory = DependencyManager::get().data(); + qDebug() << "leopoly didVisualGeometryRequestSucceed" << didVisualGeometryRequestSucceed << QThread::currentThread() << _model.get(); + if(didVisualGeometryRequestSucceed) { + emit factory->modelAddedToScene(entity->getEntityItemID(), NestableType::Entity, _model); + } }); connect(model.get(), &Model::requestRenderUpdate, this, &ModelEntityRenderer::requestRenderUpdate); connect(entity.get(), &RenderableModelEntityItem::requestCollisionGeometryUpdate, this, &ModelEntityRenderer::flagForCollisionGeometryUpdate); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index ffb83d3609..f72ddad6b5 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -111,6 +111,7 @@ public: virtual int getJointIndex(const QString& name) const override; virtual QStringList getJointNames() const override; + bool getMeshes(MeshProxyList& result) override; // deprecated const void* getCollisionMeshKey() const { return _collisionMeshKey; } signals: @@ -137,6 +138,7 @@ namespace render { namespace entities { class ModelEntityRenderer : public TypedEntityRenderer { using Parent = TypedEntityRenderer; friend class EntityRenderer; + Q_OBJECT public: ModelEntityRenderer(const EntityItemPointer& entity); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 454fba4f94..931e55f7ef 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -20,6 +20,8 @@ #include #include +#include +#include #include #include #include @@ -97,7 +99,7 @@ const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5; In RenderablePolyVoxEntityItem::render, these flags are checked and changes are propagated along the chain. decompressVolumeData() is called to decompress _voxelData into _volData. recomputeMesh() is called to invoke the - polyVox surface extractor to create _mesh (as well as set Simulation _dirtyFlags). Because Simulation::DIRTY_SHAPE + polyVox surface extractor to create _mesh (as well as set Simulation _flags). Because Simulation::DIRTY_SHAPE is set, isReadyToComputeShape() gets called and _shape is created either from _volData or _shape, depending on the surface style. @@ -1414,6 +1416,39 @@ void RenderablePolyVoxEntityItem::bonkNeighbors() { } } +// deprecated +bool RenderablePolyVoxEntityItem::getMeshes(MeshProxyList& result) { + if (!updateDependents()) { + return false; + } + + bool success = false; + if (_mesh) { + MeshProxy* meshProxy = nullptr; + glm::mat4 transform = voxelToLocalMatrix(); + withReadLock([&] { + gpu::BufferView::Index numVertices = (gpu::BufferView::Index)_mesh->getNumVertices(); + if (!_meshReady) { + // we aren't ready to return a mesh. the caller will have to try again later. + success = false; + } else if (numVertices == 0) { + // we are ready, but there are no triangles in the mesh. + success = true; + } else { + success = true; + // the mesh will be in voxel-space. transform it into object-space + meshProxy = new SimpleMeshProxy( + _mesh->map([=](glm::vec3 position) { return glm::vec3(transform * glm::vec4(position, 1.0f)); }, + [=](glm::vec3 color) { return color; }, + [=](glm::vec3 normal) { return glm::normalize(glm::vec3(transform * glm::vec4(normal, 0.0f))); }, + [&](uint32_t index) { return index; })); + result << meshProxy; + } + }); + } + return success; +} + scriptable::ScriptableModelBase RenderablePolyVoxEntityItem::getScriptableModel(bool * ok) { if (!updateDependents() || !_mesh) { return scriptable::ModelProvider::modelUnavailableError(ok); diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 733d5b62f5..3fb79110e6 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -113,6 +113,7 @@ public: void setVolDataDirty() { withWriteLock([&] { _volDataDirty = true; _meshReady = false; }); } + bool getMeshes(MeshProxyList& result) override; // deprecated virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override; private: diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 5ea1c9edb7..aefcc196fa 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -16,8 +16,8 @@ #include #include -#include -#include +#include "render-utils/simple_vert.h" +#include "render-utils/simple_frag.h" //#define SHAPE_ENTITY_USE_FADE_EFFECT #ifdef SHAPE_ENTITY_USE_FADE_EFFECT @@ -112,8 +112,6 @@ bool ShapeEntityRenderer::isTransparent() const { return Parent::isTransparent(); } - - void ShapeEntityRenderer::doRender(RenderArgs* args) { PerformanceTimer perfTimer("RenderableShapeEntityItem::render"); Q_ASSERT(args->_batch); diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 5c9324fc8a..4c398b8a29 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -57,6 +57,8 @@ using EntityTreeElementExtraEncodeDataPointer = std::shared_ptrgetLocked()) { shouldDelete = false; } else { - _entityTree->deleteEntity(entityID); + // only delete local entities, server entities will round trip through the server filters + if (entity->getClientOnly()) { + _entityTree->deleteEntity(entityID); + } } } }); @@ -1805,6 +1808,30 @@ bool EntityScriptingInterface::AABoxIntersectsCapsule(const glm::vec3& low, cons return aaBox.findCapsulePenetration(start, end, radius, penetration); } +void EntityScriptingInterface::getMeshes(QUuid entityID, QScriptValue callback) { + PROFILE_RANGE(script_entities, __FUNCTION__); + + EntityItemPointer entity = static_cast(_entityTree->findEntityByEntityItemID(entityID)); + if (!entity) { + qCDebug(entities) << "EntityScriptingInterface::getMeshes no entity with ID" << entityID; + QScriptValueList args { callback.engine()->undefinedValue(), false }; + callback.call(QScriptValue(), args); + return; + } + + MeshProxyList result; + bool success = entity->getMeshes(result); + + if (success) { + QScriptValue resultAsScriptValue = meshesToScriptValue(callback.engine(), result); + QScriptValueList args { resultAsScriptValue, true }; + callback.call(QScriptValue(), args); + } else { + QScriptValueList args { callback.engine()->undefinedValue(), false }; + callback.call(QScriptValue(), args); + } +} + glm::mat4 EntityScriptingInterface::getEntityTransform(const QUuid& entityID) { glm::mat4 result; if (_entityTree) { diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index da201f93eb..d1b321dbca 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -37,6 +37,7 @@ #include "BaseScriptEngine.h" class EntityTree; +class MeshProxy; // helper factory to compose standardized, async metadata queries for "magic" Entity properties // like .script and .serverScripts. This is used for automated testing of core scripting features @@ -138,7 +139,7 @@ public slots: Q_INVOKABLE bool canRezTmpCertified(); /**jsdoc - * @function Entities.canWriteAsseets + * @function Entities.canWriteAssets * @return {bool} `true` if the DomainServer will allow this Node/Avatar to write to the asset server */ Q_INVOKABLE bool canWriteAssets(); @@ -400,6 +401,9 @@ public slots: Q_INVOKABLE bool AABoxIntersectsCapsule(const glm::vec3& low, const glm::vec3& dimensions, const glm::vec3& start, const glm::vec3& end, float radius); + // FIXME move to a renderable entity interface + Q_INVOKABLE void getMeshes(QUuid entityID, QScriptValue callback); + /**jsdoc * Returns object to world transform, excluding scale * diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 402afea2cc..837d7fe80e 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -408,7 +408,16 @@ static void createTangents(const FBXMesh& mesh, bool generateFromTexCoords, } } -static void createMeshTangents(FBXMesh& mesh, bool generateFromTexCoords) { +static void _createBlendShapeTangents(FBXMesh& mesh, bool generateFromTexCoords, FBXBlendshape& blendShape); + +void FBXMesh::createBlendShapeTangents(bool generateTangents) { + for (auto& blendShape : blendshapes) { + _createBlendShapeTangents(*this, generateTangents, blendShape); + } +} + +void FBXMesh::createMeshTangents(bool generateFromTexCoords) { + FBXMesh& mesh = *this; // This is the only workaround I've found to trick the compiler into understanding that mesh.tangents isn't // const in the lambda function. auto& tangents = mesh.tangents; @@ -421,7 +430,7 @@ static void createMeshTangents(FBXMesh& mesh, bool generateFromTexCoords) { }); } -static void createBlendShapeTangents(FBXMesh& mesh, bool generateFromTexCoords, FBXBlendshape& blendShape) { +static void _createBlendShapeTangents(FBXMesh& mesh, bool generateFromTexCoords, FBXBlendshape& blendShape) { // Create lookup to get index in blend shape from vertex index in mesh std::vector reverseIndices; reverseIndices.resize(mesh.vertices.size()); @@ -1455,18 +1464,22 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS QSet remainingModels; for (QHash::const_iterator model = models.constBegin(); model != models.constEnd(); model++) { // models with clusters must be parented to the cluster top - foreach (const QString& deformerID, _connectionChildMap.values(model.key())) { - foreach (const QString& clusterID, _connectionChildMap.values(deformerID)) { - if (!clusters.contains(clusterID)) { - continue; + // Unless the model is a root node. + bool isARootNode = !modelIDs.contains(_connectionParentMap.value(model.key())); + if (!isARootNode) { + foreach(const QString& deformerID, _connectionChildMap.values(model.key())) { + foreach(const QString& clusterID, _connectionChildMap.values(deformerID)) { + if (!clusters.contains(clusterID)) { + continue; + } + QString topID = getTopModelID(_connectionParentMap, models, _connectionChildMap.value(clusterID), url); + _connectionChildMap.remove(_connectionParentMap.take(model.key()), model.key()); + _connectionParentMap.insert(model.key(), topID); + goto outerBreak; } - QString topID = getTopModelID(_connectionParentMap, models, _connectionChildMap.value(clusterID), url); - _connectionChildMap.remove(_connectionParentMap.take(model.key()), model.key()); - _connectionParentMap.insert(model.key(), topID); - goto outerBreak; } + outerBreak: ; } - outerBreak: // make sure the parent is in the child map QString parent = _connectionParentMap.value(model.key()); @@ -1714,10 +1727,8 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } } - createMeshTangents(extracted.mesh, generateTangents); - for (auto& blendShape : extracted.mesh.blendshapes) { - createBlendShapeTangents(extracted.mesh, generateTangents, blendShape); - } + extracted.mesh.createMeshTangents(generateTangents); + extracted.mesh.createBlendShapeTangents(generateTangents); // find the clusters with which the mesh is associated QVector clusterIDs; diff --git a/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.cpp b/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.cpp index 68cebe2d78..e4eeee856e 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.cpp @@ -22,7 +22,7 @@ namespace glm { //#define DEBUG_BUFFERVIEW_SCRIPTING //#ifdef DEBUG_BUFFERVIEW_SCRIPTING - #include "DebugNames.h" +#include "DebugNames.h" //#endif namespace { @@ -59,8 +59,8 @@ void setBufferViewElement(const gpu::BufferView& view, quint32 index, const QVar view.edit(index) = glmVecFromVariant(v); } -//FIXME copied from Model.cpp -static void packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint32& packedNormal, glm::uint32& packedTangent) { + +void buffer_helpers::packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint32& packedNormal, glm::uint32& packedTangent) { auto absNormal = glm::abs(normal); auto absTangent = glm::abs(tangent); normal /= glm::max(1e-6f, glm::max(glm::max(absNormal.x, absNormal.y), absNormal.z)); @@ -152,9 +152,9 @@ bool boundsCheck(const gpu::BufferView& view, quint32 index) { return ( index < view.getNumElements() && index * byteLength < (view._size - 1) * byteLength - ); + ); } - + QVariant buffer_helpers::toVariant(const gpu::BufferView& view, quint32 index, bool asArray, const char* hint) { const auto& element = view._element; const auto vecN = element.getScalarCount(); @@ -189,7 +189,7 @@ QVariant buffer_helpers::toVariant(const gpu::BufferView& view, quint32 index, b return glmVecToVariant(glm::vec3(glm::unpackSnorm3x10_1x2(packedNormal))); } - return getBufferViewElement(view, index, asArray); + return getBufferViewElement(view, index, asArray); } } } else if (BYTES_PER_ELEMENT == 2) { @@ -271,12 +271,12 @@ gpu::BufferView buffer_helpers::fromVector(const QVector& elements, const gpu template<> gpu::BufferView buffer_helpers::fromVector(const QVector& elements, const gpu::Element& elementType) { return fromVector(elements, elementType); } template<> gpu::BufferView buffer_helpers::fromVector(const QVector& elements, const gpu::Element& elementType) { return fromVector(elements, elementType); } -template struct getVec4;// { static T get(const gpu::BufferView& view, quint32 index, const char *hint); }; +template struct GpuVec4ToGlm;// { static T get(const gpu::BufferView& view, quint32 index, const char *hint); }; template struct getScalar;// { static T get(const gpu::BufferView& view, quint32 index, const char *hint); }; -struct gotter { +struct GpuToGlmAdapter { static float error(const QString& name, const gpu::BufferView& view, quint32 index, const char *hint) { - qDebug() << QString("gotter:: unhandled type=%1(element=%2(%3)) size=%4(per=%5) vec%6 hint=%7 #%8") + qDebug() << QString("GpuToGlmAdapter:: unhandled type=%1(element=%2(%3)) size=%4(per=%5) vec%6 hint=%7 #%8") .arg(name) .arg(DebugNames::stringFrom(view._element.getType())) .arg(view._element.getType()) @@ -288,9 +288,9 @@ struct gotter { Q_ASSERT(false); assert(false); return NAN; - } + } }; -template struct getScalar : gotter { +template struct getScalar : GpuToGlmAdapter { static T get(const gpu::BufferView& view, quint32 index, const char *hint) { switch(view._element.getType()) { case gpu::UINT32: return view.get(index); case gpu::UINT16: return view.get(index); @@ -305,7 +305,7 @@ template struct getScalar : gotter { } }; -template struct getVec2 : gotter { static T get(const gpu::BufferView& view, quint32 index, const char *hint) { switch(view._element.getType()) { +template struct GpuVec2ToGlm : GpuToGlmAdapter { static T get(const gpu::BufferView& view, quint32 index, const char *hint) { switch(view._element.getType()) { case gpu::UINT32: return view.get(index); case gpu::UINT16: return view.get(index); case gpu::UINT8: return view.get(index); @@ -315,10 +315,10 @@ template struct getVec2 : gotter { static T get(const gpu::BufferVi case gpu::FLOAT: return view.get(index); case gpu::HALF: return glm::unpackSnorm2x8(view.get(index)); default: break; - } return T(error("getVec2", view, index, hint)); }}; + } return T(error("GpuVec2ToGlm", view, index, hint)); }}; -template struct getVec3 : gotter { static T get(const gpu::BufferView& view, quint32 index, const char *hint) { switch(view._element.getType()) { +template struct GpuVec3ToGlm : GpuToGlmAdapter { static T get(const gpu::BufferView& view, quint32 index, const char *hint) { switch(view._element.getType()) { case gpu::UINT32: return view.get(index); case gpu::UINT16: return view.get(index); case gpu::UINT8: return view.get(index); @@ -330,12 +330,12 @@ template struct getVec3 : gotter { static T get(const gpu::Buffer case gpu::NUINT8: case gpu::NINT2_10_10_10: if (view._element.getSize() == sizeof(glm::int32)) { - return getVec4::get(view, index, hint); + return GpuVec4ToGlm::get(view, index, hint); } default: break; - } return T(error("getVec3", view, index, hint)); }}; + } return T(error("GpuVec3ToGlm", view, index, hint)); }}; -template struct getVec4 : gotter { static T get(const gpu::BufferView& view, quint32 index, const char *hint) { +template struct GpuVec4ToGlm : GpuToGlmAdapter { static T get(const gpu::BufferView& view, quint32 index, const char *hint) { assert(view._element.getSize() == sizeof(glm::int32)); switch(view._element.getType()) { case gpu::UINT32: return view.get(index); @@ -346,7 +346,7 @@ template struct getVec4 : gotter { static T get(const gpu::BufferVi case gpu::INT8: return view.get(index); case gpu::NUINT32: break; case gpu::NUINT16: break; - case gpu::NUINT8: return glm::unpackUnorm4x8(view.get(index)); + case gpu::NUINT8: return glm::unpackUnorm4x8(view.get(index)); case gpu::NUINT2: break; case gpu::NINT32: break; case gpu::NINT16: break; @@ -356,7 +356,7 @@ template struct getVec4 : gotter { static T get(const gpu::BufferVi case gpu::FLOAT: return view.get(index); case gpu::HALF: return glm::unpackSnorm4x8(view.get(index)); case gpu::NINT2_10_10_10: return glm::unpackSnorm3x10_1x2(view.get(index)); - } return T(error("getVec4", view, index, hint)); }}; + } return T(error("GpuVec4ToGlm", view, index, hint)); }}; template @@ -376,16 +376,32 @@ struct getVec { } }; -template <> QVector buffer_helpers::toVector(const gpu::BufferView& view, const char *hint) { return getVec,int>::__to_vector__(view, hint); } -template <> QVector buffer_helpers::toVector(const gpu::BufferView& view, const char *hint) { return getVec,glm::vec2>::__to_vector__(view, hint); } -template <> QVector buffer_helpers::toVector(const gpu::BufferView& view, const char *hint) { return getVec,glm::vec3>::__to_vector__(view, hint); } -template <> QVector buffer_helpers::toVector(const gpu::BufferView& view, const char *hint) { return getVec,glm::vec4>::__to_vector__(view, hint); } +template <> QVector buffer_helpers::toVector(const gpu::BufferView& view, const char *hint) { + return getVec,int>::__to_vector__(view, hint); +} +template <> QVector buffer_helpers::toVector(const gpu::BufferView& view, const char *hint) { + return getVec,glm::vec2>::__to_vector__(view, hint); +} +template <> QVector buffer_helpers::toVector(const gpu::BufferView& view, const char *hint) { + return getVec,glm::vec3>::__to_vector__(view, hint); +} +template <> QVector buffer_helpers::toVector(const gpu::BufferView& view, const char *hint) { + return getVec,glm::vec4>::__to_vector__(view, hint); +} -template <> int buffer_helpers::convert(const gpu::BufferView& view, quint32 index, const char *hint) { return getVec,int>::__to_scalar__(view, index, hint); } -template <> glm::vec2 buffer_helpers::convert(const gpu::BufferView& view, quint32 index, const char *hint) { return getVec,glm::vec2>::__to_scalar__(view, index, hint); } -template <> glm::vec3 buffer_helpers::convert(const gpu::BufferView& view, quint32 index, const char *hint) { return getVec,glm::vec3>::__to_scalar__(view, index, hint); } -template <> glm::vec4 buffer_helpers::convert(const gpu::BufferView& view, quint32 index, const char *hint) { return getVec,glm::vec4>::__to_scalar__(view, index, hint); } +template <> int buffer_helpers::convert(const gpu::BufferView& view, quint32 index, const char *hint) { + return getVec,int>::__to_scalar__(view, index, hint); +} +template <> glm::vec2 buffer_helpers::convert(const gpu::BufferView& view, quint32 index, const char *hint) { + return getVec,glm::vec2>::__to_scalar__(view, index, hint); +} +template <> glm::vec3 buffer_helpers::convert(const gpu::BufferView& view, quint32 index, const char *hint) { + return getVec,glm::vec3>::__to_scalar__(view, index, hint); +} +template <> glm::vec4 buffer_helpers::convert(const gpu::BufferView& view, quint32 index, const char *hint) { + return getVec,glm::vec4>::__to_scalar__(view, index, hint); +} gpu::BufferView buffer_helpers::clone(const gpu::BufferView& input) { return gpu::BufferView( @@ -410,7 +426,7 @@ gpu::BufferView buffer_helpers::resize(const gpu::BufferView& input, quint32 num graphics::MeshPointer buffer_helpers::cloneMesh(graphics::MeshPointer mesh) { auto clone = std::make_shared(); //[](graphics::Mesh* blah) { - //qCDebug(bufferhelper_logging) << "--- DELETING MESH POINTER" << blah; + //qCDebug(bufferhelper_logging) << "--- DELETING MESH POINTER" << blah; // delete blah; //}); clone->displayName = (QString::fromStdString(mesh->displayName) + "-clone").toStdString(); @@ -437,12 +453,12 @@ graphics::MeshPointer buffer_helpers::cloneMesh(graphics::MeshPointer mesh) { namespace { // expand the corresponding attribute buffer (creating it if needed) so that it matches POSITIONS size and specified element type gpu::BufferView _expandedAttributeBuffer(const graphics::MeshPointer mesh, gpu::Stream::Slot slot) { - gpu::BufferView bufferView = buffer_helpers::getBufferView(mesh, slot); + gpu::BufferView bufferView = buffer_helpers::getBufferView(mesh, slot); const auto& elementType = bufferView._element; //auto vecN = element.getScalarCount(); //auto type = element.getType(); //gpu::Element elementType = getVecNElement(type, vecN); - + gpu::Size elementSize = elementType.getSize(); auto nPositions = mesh->getNumVertices(); auto vsize = nPositions * elementSize; @@ -484,8 +500,8 @@ namespace { typeName = DebugNames::stringFrom(bufferView._element.getType()); #endif qCDebug(bufferhelper_logging, "NOTE:: _expandedAttributeBuffer.%s vec%d %s (before count=%lu bytes=%lu // after count=%lu bytes=%lu)", - hint.toStdString().c_str(), bufferView._element.getScalarCount(), - typeName.toStdString().c_str(), beforeCount, beforeTotal, afterCount, afterTotal); + hint.toStdString().c_str(), bufferView._element.getScalarCount(), + typeName.toStdString().c_str(), beforeCount, beforeTotal, afterCount, afterTotal); } #endif return bufferView; @@ -529,7 +545,7 @@ std::map buffer_helpers::gatherBufferViews(graphics::M auto afterCount = attributeViews[name].getNumElements(); if (beforeTotal != afterTotal || beforeCount != afterCount) { qCDebug(bufferhelper_logging, "NOTE:: gatherBufferViews.%s vec%d %s (before count=%lu bytes=%lu // after count=%lu bytes=%lu)", - name.toStdString().c_str(), vecN, typeName.toStdString().c_str(), beforeCount, beforeTotal, afterCount, afterTotal); + name.toStdString().c_str(), vecN, typeName.toStdString().c_str(), beforeCount, beforeTotal, afterCount, afterTotal); } #endif } @@ -538,7 +554,7 @@ std::map buffer_helpers::gatherBufferViews(graphics::M } -bool buffer_helpers::recalculateNormals(graphics::MeshPointer mesh) { +bool buffer_helpers::recalculateNormals(graphics::MeshPointer mesh) { qCInfo(bufferhelper_logging) << "Recalculating normals" << !!mesh; if (!mesh) { return false; @@ -605,7 +621,7 @@ bool buffer_helpers::recalculateNormals(graphics::MeshPointer mesh) { } break; } - normals.edit(j) = glm::normalize(normal); + buffer_helpers::fromVariant(normals, j, glmVecToVariant(glm::normalize(normal))); } return true; } diff --git a/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.h b/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.h index d963fd4b22..e0c2e1eee1 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.h +++ b/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.h @@ -42,9 +42,11 @@ struct buffer_helpers { template static gpu::BufferView fromVector(const QVector& elements, const gpu::Element& elementType); - template static QVector toVector(const gpu::BufferView& view, const char *hint = ""); + template static QVector toVector(const gpu::BufferView& view, const char *hint = ""); template static T convert(const gpu::BufferView& view, quint32 index, const char* hint = ""); - + static gpu::BufferView clone(const gpu::BufferView& input); - static gpu::BufferView resize(const gpu::BufferView& input, quint32 numElements); + static gpu::BufferView resize(const gpu::BufferView& input, quint32 numElements); + + static void packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint32& packedNormal, glm::uint32& packedTangent); }; diff --git a/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp b/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp index ab6f2c92be..775aedad52 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp @@ -42,8 +42,6 @@ bool bufferViewElementFromScriptValue(const QScriptValue& v, const gpu::BufferVi return buffer_helpers::fromVariant(view, index, v.toVariant()); } -// - template QScriptValue glmVecToScriptValue(QScriptEngine *js, const T& v, bool asArray) { static const auto len = T().length(); diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h index 15973b5852..95fcf51921 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/Forward.h +++ b/libraries/graphics-scripting/src/graphics-scripting/Forward.h @@ -8,10 +8,12 @@ #include #include - +#include namespace graphics { class Mesh; } +class Model; +using ModelPointer = std::shared_ptr; namespace gpu { class BufferView; } @@ -83,7 +85,7 @@ namespace scriptable { // mixin class for Avatar/Entity/Overlay Rendering that expose their in-memory graphics::Meshes class ModelProvider { public: - QVariantMap metadata{ { "providerType", "unknown" } }; + NestableType modelProviderType; static scriptable::ScriptableModelBase modelUnavailableError(bool* ok) { if (ok) { *ok = false; } return {}; } virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) = 0; @@ -91,9 +93,13 @@ namespace scriptable { }; // mixin class for resolving UUIDs into a corresponding ModelProvider - class ModelProviderFactory : public Dependency { + class ModelProviderFactory : public QObject, public Dependency { + Q_OBJECT public: - virtual scriptable::ModelProviderPointer lookupModelProvider(QUuid uuid) = 0; + virtual scriptable::ModelProviderPointer lookupModelProvider(const QUuid& uuid) = 0; + signals: + void modelAddedToScene(const QUuid& objectID, NestableType nestableType, const ModelPointer& sender); + void modelRemovedFromScene(const QUuid& objectID, NestableType nestableType, const ModelPointer& sender); }; using uint32 = quint32; @@ -105,3 +111,5 @@ namespace scriptable { using ScriptableMeshPartPointer = QPointer; bool registerMetaTypes(QScriptEngine* engine); } + +Q_DECLARE_METATYPE(NestableType) diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp new file mode 100644 index 0000000000..71ef57bd82 --- /dev/null +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -0,0 +1,160 @@ +// +// GraphicsScriptingInterface.cpp +// libraries/script-engine/src +// +// Created by Seth Alves on 2017-1-27. +// Copyright 2017 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 "GraphicsScriptingInterface.h" +#include +#include +#include +#include +#include "BaseScriptEngine.h" +#include "ScriptEngineLogging.h" +#include "OBJWriter.h" + +#include +#include + +#include +#include + +#include "BufferViewScripting.h" +#include "ScriptableMesh.h" +#include "GraphicsScriptingUtil.h" + +#include "GraphicsScriptingInterface.moc" + +#include "RegisteredMetaTypes.h" + +GraphicsScriptingInterface::GraphicsScriptingInterface(QObject* parent) : QObject(parent) { + if (auto scriptEngine = qobject_cast(parent)) { + this->registerMetaTypes(scriptEngine); + } +} + +bool GraphicsScriptingInterface::updateMeshes(QUuid uuid, const scriptable::ScriptableMeshPointer mesh, int meshIndex, int partIndex) { + auto model = scriptable::make_qtowned(); + if (mesh) { + model->append(*mesh); + } + return updateMeshes(uuid, model.get()); +} + +bool GraphicsScriptingInterface::updateMeshes(QUuid uuid, const scriptable::ScriptableModelPointer model) { + auto appProvider = DependencyManager::get(); + scriptable::ModelProviderPointer provider = appProvider ? appProvider->lookupModelProvider(uuid) : nullptr; + QString providerType = provider ? SpatiallyNestable::nestableTypeToString(provider->modelProviderType) : QString(); + if (providerType.isEmpty()) { + providerType = "unknown"; + } + bool success = false; + if (provider) { + auto scriptableMeshes = provider->getScriptableModel(&success); + if (success) { + const scriptable::ScriptableModelBasePointer base = model->operator scriptable::ScriptableModelBasePointer(); + if (base) { + success = provider->replaceScriptableModelMeshPart(base, -1, -1); + } + } + } + return success; +} + +QScriptValue GraphicsScriptingInterface::getMeshes(QUuid uuid) { + scriptable::ScriptableModel* meshes{ nullptr }; + bool success = false; + QString error; + + auto appProvider = DependencyManager::get(); + qCDebug(graphics_scripting) << "appProvider" << appProvider.data(); + scriptable::ModelProviderPointer provider = appProvider ? appProvider->lookupModelProvider(uuid) : nullptr; + QString providerType = provider ? SpatiallyNestable::nestableTypeToString(provider->modelProviderType) : QString(); + if (providerType.isEmpty()) { + providerType = "unknown"; + } + if (provider) { + auto scriptableMeshes = provider->getScriptableModel(&success); + if (success) { + meshes = scriptable::make_scriptowned(scriptableMeshes); + if (meshes->objectName().isEmpty()) { + meshes->setObjectName(providerType+"::meshes"); + } + if (meshes->objectID.isNull()) { + meshes->objectID = uuid.toString(); + } + meshes->metadata["provider"] = SpatiallyNestable::nestableTypeToString(provider->modelProviderType); + } + } + if (!success) { + error = QString("failed to get meshes from %1 provider for uuid %2").arg(providerType).arg(uuid.toString()); + } + + QPointer scriptEngine = dynamic_cast(engine()); + QScriptValue result = error.isEmpty() ? scriptEngine->toScriptValue(meshes) : scriptEngine->makeError(error); + if (result.isError()) { + qCWarning(graphics_scripting) << "GraphicsScriptingInterface::getMeshes ERROR" << result.toString(); + if (context()) { + context()->throwValue(error); + } else { + qCWarning(graphics_scripting) << "GraphicsScriptingInterface::getMeshes ERROR" << result.toString(); + } + return QScriptValue::NullValue; + } + return scriptEngine->toScriptValue(meshes); +} + +QString GraphicsScriptingInterface::meshToOBJ(const scriptable::ScriptableModel& _in) { + const auto& in = _in.getConstMeshes(); + if (in.size()) { + QList meshes; + foreach (auto meshProxy, in) { + if (meshProxy) { + meshes.append(getMeshPointer(meshProxy)); + } + } + if (meshes.size()) { + return writeOBJToString(meshes); + } + } + if (context()) { + context()->throwError(QString("null mesh")); + } + return QString(); +} +void GraphicsScriptingInterface::registerMetaTypes(QScriptEngine* engine) { + scriptable::registerMetaTypes(engine); +} + +MeshPointer GraphicsScriptingInterface::getMeshPointer(const scriptable::ScriptableMesh& meshProxy) { + return meshProxy.getMeshPointer(); +} +MeshPointer GraphicsScriptingInterface::getMeshPointer(scriptable::ScriptableMesh& meshProxy) { + return getMeshPointer(&meshProxy); +} +MeshPointer GraphicsScriptingInterface::getMeshPointer(scriptable::ScriptableMeshPointer meshProxy) { + MeshPointer result; + if (!meshProxy) { + if (context()){ + context()->throwError("expected meshProxy as first parameter"); + } else { + qCDebug(graphics_scripting) << "expected meshProxy as first parameter"; + } + return result; + } + auto mesh = meshProxy->getMeshPointer(); + if (!mesh) { + if (context()) { + context()->throwError("expected valid meshProxy as first parameter"); + } else { + qCDebug(graphics_scripting) << "expected valid meshProxy as first parameter"; + } + return result; + } + return mesh; +} diff --git a/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h similarity index 54% rename from libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.h rename to libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h index fa7b885014..6afa549a19 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.h +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h @@ -1,16 +1,15 @@ // -// ModelScriptingInterface.h -// libraries/script-engine/src +// GraphicsScriptingInterface.h +// libraries/graphics-scripting/src // -// Created by Seth Alves on 2017-1-27. // Copyright 2017 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_ModelScriptingInterface_h -#define hifi_ModelScriptingInterface_h +#ifndef hifi_GraphicsScriptingInterface_h +#define hifi_GraphicsScriptingInterface_h #include #include @@ -21,35 +20,27 @@ #include "ScriptableMesh.h" #include -class ModelScriptingInterface : public QObject, public QScriptable, public Dependency { +class GraphicsScriptingInterface : public QObject, public QScriptable, public Dependency { Q_OBJECT public: - ModelScriptingInterface(QObject* parent = nullptr); + GraphicsScriptingInterface(QObject* parent = nullptr); public slots: /**jsdoc * Returns the meshes associated with a UUID (entityID, overlayID, or avatarID) * - * @function ModelScriptingInterface.getMeshes + * @function GraphicsScriptingInterface.getMeshes * @param {EntityID} entityID The ID of the entity whose meshes are to be retrieve */ - void getMeshes(QUuid uuid, QScriptValue callback); + QScriptValue getMeshes(QUuid uuid); bool updateMeshes(QUuid uuid, const scriptable::ScriptableModelPointer model); bool updateMeshes(QUuid uuid, const scriptable::ScriptableMeshPointer mesh, int meshIndex=0, int partIndex=0); QString meshToOBJ(const scriptable::ScriptableModel& in); - QScriptValue appendMeshes(scriptable::ScriptableModel in); - QScriptValue transformMesh(scriptable::ScriptableMeshPointer meshProxy, glm::mat4 transform); - QScriptValue newMesh(const QVector& vertices, - const QVector& normals, - const QVector& faces); - QScriptValue getVertexCount(scriptable::ScriptableMeshPointer meshProxy); - QScriptValue getVertex(scriptable::ScriptableMeshPointer meshProxy, quint32 vertexIndex); - static void registerMetaTypes(QScriptEngine* engine); - + private: scriptable::MeshPointer getMeshPointer(scriptable::ScriptableMeshPointer meshProxy); scriptable::MeshPointer getMeshPointer(scriptable::ScriptableMesh& meshProxy); @@ -57,4 +48,4 @@ private: }; -#endif // hifi_ModelScriptingInterface_h +#endif // hifi_GraphicsScriptingInterface_h diff --git a/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp deleted file mode 100644 index d78f646087..0000000000 --- a/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp +++ /dev/null @@ -1,407 +0,0 @@ -// -// ModelScriptingInterface.cpp -// libraries/script-engine/src -// -// Created by Seth Alves on 2017-1-27. -// Copyright 2017 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 "ModelScriptingInterface.h" -#include -#include -#include -#include -#include "BaseScriptEngine.h" -#include "ScriptEngineLogging.h" -#include "OBJWriter.h" - -#include -#include - -#include -#include - -#include "BufferViewScripting.h" -#include "ScriptableMesh.h" -#include "GraphicsScriptingUtil.h" - -#include "ModelScriptingInterface.moc" - -#include "RegisteredMetaTypes.h" - -ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(parent) { - if (auto scriptEngine = qobject_cast(parent)) { - this->registerMetaTypes(scriptEngine); - } -} - -bool ModelScriptingInterface::updateMeshes(QUuid uuid, const scriptable::ScriptableMeshPointer mesh, int meshIndex, int partIndex) { - auto model = scriptable::make_qtowned(); - if (mesh) { - model->append(*mesh); - } - return updateMeshes(uuid, model.get()); -} - -bool ModelScriptingInterface::updateMeshes(QUuid uuid, const scriptable::ScriptableModelPointer model) { - auto appProvider = DependencyManager::get(); - //qCDebug(graphics_scripting) << "appProvider" << appProvider.data(); - scriptable::ModelProviderPointer provider = appProvider ? appProvider->lookupModelProvider(uuid) : nullptr; - QString providerType = provider ? provider->metadata.value("providerType").toString() : QString(); - if (providerType.isEmpty()) { - providerType = "unknown"; - } - bool success = false; - if (provider) { - //qCDebug(graphics_scripting) << "fetching meshes from " << providerType << "..."; - auto scriptableMeshes = provider->getScriptableModel(&success); - //qCDebug(graphics_scripting) << "//fetched meshes from " << providerType << "success:" <operator scriptable::ScriptableModelBasePointer(); - //qCDebug(graphics_scripting) << "as base" << base; - if (base) { - //auto meshes = model->getConstMeshes(); - success = provider->replaceScriptableModelMeshPart(base, -1, -1); - - // for (uint32_t m = 0; success && m < meshes.size(); m++) { - // const auto& mesh = meshes.at(m); - // for (int p = 0; success && p < mesh->getNumParts(); p++) { - // qCDebug(graphics_scripting) << "provider->replaceScriptableModelMeshPart" << "meshIndex" << m << "partIndex" << p; - // success = provider->replaceScriptableModelMeshPart(base, m, p); - // //if (!success) { - // qCDebug(graphics_scripting) << "//provider->replaceScriptableModelMeshPart" << "meshIndex" << m << "partIndex" << p << success; - // } - // } - } - } - } - return success; -} - -void ModelScriptingInterface::getMeshes(QUuid uuid, QScriptValue callback) { - auto handler = scriptable::jsBindCallback(callback); - Q_ASSERT(handler.engine() == this->engine()); - QPointer engine = dynamic_cast(handler.engine()); - - scriptable::ScriptableModel* meshes{ nullptr }; - bool success = false; - QString error; - - auto appProvider = DependencyManager::get(); - qCDebug(graphics_scripting) << "appProvider" << appProvider.data(); - scriptable::ModelProviderPointer provider = appProvider ? appProvider->lookupModelProvider(uuid) : nullptr; - QString providerType = provider ? provider->metadata.value("providerType").toString() : QString(); - if (providerType.isEmpty()) { - providerType = "unknown"; - } - if (provider) { - qCDebug(graphics_scripting) << "fetching meshes from " << providerType << "..."; - auto scriptableMeshes = provider->getScriptableModel(&success); - qCDebug(graphics_scripting) << "//fetched meshes from " << providerType << "success:" <(scriptableMeshes); - QString debugString = scriptable::toDebugString(meshes); - QObject::connect(meshes, &QObject::destroyed, this, [=]() { - qCDebug(graphics_scripting) << "///fetched meshes" << debugString; - }); - - if (meshes->objectName().isEmpty()) { - meshes->setObjectName(providerType+"::meshes"); - } - if (meshes->objectID.isNull()) { - meshes->objectID = uuid.toString(); - } - meshes->metadata["provider"] = provider->metadata; - } - } - if (!success) { - error = QString("failed to get meshes from %1 provider for uuid %2").arg(providerType).arg(uuid.toString()); - } - - if (!error.isEmpty()) { - qCWarning(graphics_scripting) << "ModelScriptingInterface::getMeshes ERROR" << error; - callScopedHandlerObject(handler, engine->makeError(error), QScriptValue::NullValue); - } else { - callScopedHandlerObject(handler, QScriptValue::NullValue, engine->toScriptValue(meshes)); - } -} - -QString ModelScriptingInterface::meshToOBJ(const scriptable::ScriptableModel& _in) { - const auto& in = _in.getConstMeshes(); - qCDebug(graphics_scripting) << "meshToOBJ" << in.size(); - if (in.size()) { - QList meshes; - foreach (auto meshProxy, in) { - qCDebug(graphics_scripting) << "meshToOBJ" << meshProxy; - if (meshProxy) { - meshes.append(getMeshPointer(meshProxy)); - } - } - if (meshes.size()) { - return writeOBJToString(meshes); - } - } - context()->throwError(QString("null mesh")); - return QString(); -} - -QScriptValue ModelScriptingInterface::appendMeshes(scriptable::ScriptableModel _in) { - const auto& in = _in.getMeshes(); - - // figure out the size of the resulting mesh - size_t totalVertexCount { 0 }; - size_t totalColorCount { 0 }; - size_t totalNormalCount { 0 }; - size_t totalIndexCount { 0 }; - foreach (auto& meshProxy, in) { - scriptable::MeshPointer mesh = getMeshPointer(meshProxy); - totalVertexCount += mesh->getNumVertices(); - - int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h - const gpu::BufferView& colorsBufferView = mesh->getAttributeBuffer(attributeTypeColor); - gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements(); - totalColorCount += numColors; - - int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h - const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(attributeTypeNormal); - gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements(); - totalNormalCount += numNormals; - - totalIndexCount += mesh->getNumIndices(); - } - - // alloc the resulting mesh - gpu::Resource::Size combinedVertexSize = totalVertexCount * sizeof(glm::vec3); - unsigned char* combinedVertexData = new unsigned char[combinedVertexSize]; - unsigned char* combinedVertexDataCursor = combinedVertexData; - - gpu::Resource::Size combinedColorSize = totalColorCount * sizeof(glm::vec3); - unsigned char* combinedColorData = new unsigned char[combinedColorSize]; - unsigned char* combinedColorDataCursor = combinedColorData; - - gpu::Resource::Size combinedNormalSize = totalNormalCount * sizeof(glm::vec3); - unsigned char* combinedNormalData = new unsigned char[combinedNormalSize]; - unsigned char* combinedNormalDataCursor = combinedNormalData; - - gpu::Resource::Size combinedIndexSize = totalIndexCount * sizeof(uint32_t); - unsigned char* combinedIndexData = new unsigned char[combinedIndexSize]; - unsigned char* combinedIndexDataCursor = combinedIndexData; - - uint32_t indexStartOffset { 0 }; - - foreach (const auto& meshProxy, in) { - scriptable::MeshPointer mesh = getMeshPointer(meshProxy); - mesh->forEach( - [&](glm::vec3 position){ - memcpy(combinedVertexDataCursor, &position, sizeof(position)); - combinedVertexDataCursor += sizeof(position); - }, - [&](glm::vec3 color){ - memcpy(combinedColorDataCursor, &color, sizeof(color)); - combinedColorDataCursor += sizeof(color); - }, - [&](glm::vec3 normal){ - memcpy(combinedNormalDataCursor, &normal, sizeof(normal)); - combinedNormalDataCursor += sizeof(normal); - }, - [&](uint32_t index){ - index += indexStartOffset; - memcpy(combinedIndexDataCursor, &index, sizeof(index)); - combinedIndexDataCursor += sizeof(index); - }); - - gpu::BufferView::Index numVertices = (gpu::BufferView::Index)mesh->getNumVertices(); - indexStartOffset += numVertices; - } - - graphics::MeshPointer result(new graphics::Mesh()); - - gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); - gpu::Buffer* combinedVertexBuffer = new gpu::Buffer(combinedVertexSize, combinedVertexData); - gpu::BufferPointer combinedVertexBufferPointer(combinedVertexBuffer); - gpu::BufferView combinedVertexBufferView(combinedVertexBufferPointer, vertexElement); - result->setVertexBuffer(combinedVertexBufferView); - - int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h - gpu::Element colorElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); - gpu::Buffer* combinedColorsBuffer = new gpu::Buffer(combinedColorSize, combinedColorData); - gpu::BufferPointer combinedColorsBufferPointer(combinedColorsBuffer); - gpu::BufferView combinedColorsBufferView(combinedColorsBufferPointer, colorElement); - result->addAttribute(attributeTypeColor, combinedColorsBufferView); - - int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h - gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); - gpu::Buffer* combinedNormalsBuffer = new gpu::Buffer(combinedNormalSize, combinedNormalData); - gpu::BufferPointer combinedNormalsBufferPointer(combinedNormalsBuffer); - gpu::BufferView combinedNormalsBufferView(combinedNormalsBufferPointer, normalElement); - result->addAttribute(attributeTypeNormal, combinedNormalsBufferView); - - gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW); - gpu::Buffer* combinedIndexesBuffer = new gpu::Buffer(combinedIndexSize, combinedIndexData); - gpu::BufferPointer combinedIndexesBufferPointer(combinedIndexesBuffer); - gpu::BufferView combinedIndexesBufferView(combinedIndexesBufferPointer, indexElement); - result->setIndexBuffer(combinedIndexesBufferView); - - std::vector parts; - parts.emplace_back(graphics::Mesh::Part((graphics::Index)0, // startIndex - (graphics::Index)result->getNumIndices(), // numIndices - (graphics::Index)0, // baseVertex - graphics::Mesh::TRIANGLES)); // topology - result->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(graphics::Mesh::Part), - (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); - - - return engine()->toScriptValue(scriptable::make_scriptowned(result)); -} - -QScriptValue ModelScriptingInterface::transformMesh(scriptable::ScriptableMeshPointer meshProxy, glm::mat4 transform) { - auto mesh = getMeshPointer(meshProxy); - if (!mesh) { - return false; - } - - graphics::MeshPointer result = mesh->map([&](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); }, - [&](glm::vec3 color){ return color; }, - [&](glm::vec3 normal){ return glm::vec3(transform * glm::vec4(normal, 0.0f)); }, - [&](uint32_t index){ return index; }); - return engine()->toScriptValue(scriptable::make_scriptowned(result)); -} - -QScriptValue ModelScriptingInterface::getVertexCount(scriptable::ScriptableMeshPointer meshProxy) { - auto mesh = getMeshPointer(meshProxy); - if (!mesh) { - return -1; - } - return (uint32_t)mesh->getNumVertices(); -} - -QScriptValue ModelScriptingInterface::getVertex(scriptable::ScriptableMeshPointer meshProxy, quint32 vertexIndex) { - auto mesh = getMeshPointer(meshProxy); - if (!mesh) { - return QScriptValue(); - } - - const gpu::BufferView& vertexBufferView = mesh->getVertexBuffer(); - auto numVertices = mesh->getNumVertices(); - - if (vertexIndex >= numVertices) { - context()->throwError(QString("invalid index: %1 [0,%2)").arg(vertexIndex).arg(numVertices)); - return QScriptValue::NullValue; - } - - glm::vec3 pos = vertexBufferView.get(vertexIndex); - return engine()->toScriptValue(pos); -} - -QScriptValue ModelScriptingInterface::newMesh(const QVector& vertices, - const QVector& normals, - const QVector& faces) { - graphics::MeshPointer mesh(new graphics::Mesh()); - - // vertices - auto vertexBuffer = std::make_shared(vertices.size() * sizeof(glm::vec3), (gpu::Byte*)vertices.data()); - auto vertexBufferPtr = gpu::BufferPointer(vertexBuffer); - gpu::BufferView vertexBufferView(vertexBufferPtr, 0, vertexBufferPtr->getSize(), - sizeof(glm::vec3), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - mesh->setVertexBuffer(vertexBufferView); - - if (vertices.size() == normals.size()) { - // normals - auto normalBuffer = std::make_shared(normals.size() * sizeof(glm::vec3), (gpu::Byte*)normals.data()); - auto normalBufferPtr = gpu::BufferPointer(normalBuffer); - gpu::BufferView normalBufferView(normalBufferPtr, 0, normalBufferPtr->getSize(), - sizeof(glm::vec3), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); - mesh->addAttribute(gpu::Stream::NORMAL, normalBufferView); - } else { - qCWarning(graphics_scripting, "ModelScriptingInterface::newMesh normals must be same length as vertices"); - } - - // indices (faces) - int VERTICES_PER_TRIANGLE = 3; - int indexBufferSize = faces.size() * sizeof(uint32_t) * VERTICES_PER_TRIANGLE; - unsigned char* indexData = new unsigned char[indexBufferSize]; - unsigned char* indexDataCursor = indexData; - foreach(const mesh::MeshFace& meshFace, faces) { - for (int i = 0; i < VERTICES_PER_TRIANGLE; i++) { - memcpy(indexDataCursor, &meshFace.vertexIndices[i], sizeof(uint32_t)); - indexDataCursor += sizeof(uint32_t); - } - } - auto indexBuffer = std::make_shared(indexBufferSize, (gpu::Byte*)indexData); - auto indexBufferPtr = gpu::BufferPointer(indexBuffer); - gpu::BufferView indexBufferView(indexBufferPtr, gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW)); - mesh->setIndexBuffer(indexBufferView); - - // parts - std::vector parts; - parts.emplace_back(graphics::Mesh::Part((graphics::Index)0, // startIndex - (graphics::Index)faces.size() * 3, // numIndices - (graphics::Index)0, // baseVertex - graphics::Mesh::TRIANGLES)); // topology - mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(graphics::Mesh::Part), - (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); - - - - return engine()->toScriptValue(scriptable::make_scriptowned(mesh)); -} - -namespace { - - // FIXME: MESHFACES: - // QScriptValue meshFaceToScriptValue(QScriptEngine* engine, const mesh::MeshFace &meshFace) { - // QScriptValue obj = engine->newObject(); - // obj.setProperty("vertices", qVectorIntToScriptValue(engine, meshFace.vertexIndices)); - // return obj; - // } - // void meshFaceFromScriptValue(const QScriptValue &object, mesh::MeshFace& meshFaceResult) { - // qScriptValueToSequence(object.property("vertices"), meshFaceResult.vertexIndices); - // } - // QScriptValue qVectorMeshFaceToScriptValue(QScriptEngine* engine, const QVector& vector) { - // return qScriptValueFromSequence(engine, vector); - // } - // void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector& result) { - // qScriptValueToSequence(array, result); - // } - -} - - -void ModelScriptingInterface::registerMetaTypes(QScriptEngine* engine) { - scriptable::registerMetaTypes(engine); - // FIXME: MESHFACES: remove if MeshFace is not needed anywhere - // qScriptRegisterSequenceMetaType(engine); - // qScriptRegisterMetaType(engine, meshFaceToScriptValue, meshFaceFromScriptValue); - // qScriptRegisterMetaType(engine, qVectorMeshFaceToScriptValue, qVectorMeshFaceFromScriptValue); -} - -MeshPointer ModelScriptingInterface::getMeshPointer(const scriptable::ScriptableMesh& meshProxy) { - return meshProxy.getMeshPointer(); -} -MeshPointer ModelScriptingInterface::getMeshPointer(scriptable::ScriptableMesh& meshProxy) { - return getMeshPointer(&meshProxy); -} -MeshPointer ModelScriptingInterface::getMeshPointer(scriptable::ScriptableMeshPointer meshProxy) { - MeshPointer result; - if (!meshProxy) { - if (context()){ - context()->throwError("expected meshProxy as first parameter"); - } else { - qCDebug(graphics_scripting) << "expected meshProxy as first parameter"; - } - return result; - } - auto mesh = meshProxy->getMeshPointer(); - if (!mesh) { - if (context()) { - context()->throwError("expected valid meshProxy as first parameter"); - } else { - qCDebug(graphics_scripting) << "expected valid meshProxy as first parameter"; - } - return result; - } - return mesh; -} diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp index c8f1975249..05ff043820 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp @@ -38,6 +38,7 @@ scriptable::ScriptableModelBase::~ScriptableModelBase() { meshes.clear(); //qCDebug(graphics_scripting) << "//~ScriptableModelBase" << this; } + void scriptable::ScriptableModelBase::append(scriptable::WeakMeshPointer mesh, const QVariantMap& metadata) { //qCDebug(graphics_scripting) << "+ APPEND WeakMeshPointer" << mesh.lock().get(); meshes << ScriptableMeshBase{ provider, this, mesh, metadata }; diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h index d2c50bd768..bdfaac5d7c 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h @@ -16,7 +16,6 @@ namespace scriptable { ScriptableModel(const ScriptableModel& other) : ScriptableModelBase(other) {} ScriptableModel(const ScriptableModelBase& other) : ScriptableModelBase(other) {} ScriptableModel& operator=(const ScriptableModelBase& view) { ScriptableModelBase::operator=(view); return *this; } - //virtual ~ScriptableModel() { qDebug() << "~ScriptableModel" << this; } Q_INVOKABLE scriptable::ScriptableModelPointer cloneModel(const QVariantMap& options = QVariantMap()); // TODO: in future accessors for these could go here diff --git a/libraries/model-networking/src/model-networking/SimpleMeshProxy.cpp b/libraries/model-networking/src/model-networking/SimpleMeshProxy.cpp new file mode 100644 index 0000000000..741478789e --- /dev/null +++ b/libraries/model-networking/src/model-networking/SimpleMeshProxy.cpp @@ -0,0 +1,27 @@ +// +// SimpleMeshProxy.cpp +// libraries/model-networking/src/model-networking/ +// +// Created by Seth Alves on 2017-3-22. +// Copyright 2017 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 "SimpleMeshProxy.h" + +#include + +MeshPointer SimpleMeshProxy::getMeshPointer() const { + return _mesh; +} + +int SimpleMeshProxy::getNumVertices() const { + return (int)_mesh->getNumVertices(); +} + +glm::vec3 SimpleMeshProxy::getPos3(int index) const { + return _mesh->getPos3(index); +} + diff --git a/libraries/model-networking/src/model-networking/SimpleMeshProxy.h b/libraries/model-networking/src/model-networking/SimpleMeshProxy.h new file mode 100644 index 0000000000..24c3fca27e --- /dev/null +++ b/libraries/model-networking/src/model-networking/SimpleMeshProxy.h @@ -0,0 +1,36 @@ +// +// SimpleMeshProxy.h +// libraries/model-networking/src/model-networking/ +// +// Created by Seth Alves on 2017-1-27. +// Copyright 2017 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_SimpleMeshProxy_h +#define hifi_SimpleMeshProxy_h + +#include +#include +#include + +#include + +class SimpleMeshProxy : public MeshProxy { +public: + SimpleMeshProxy(const MeshPointer& mesh) : _mesh(mesh) { } + + MeshPointer getMeshPointer() const override; + + int getNumVertices() const override; + + glm::vec3 getPos3(int index) const override; + + +protected: + const MeshPointer _mesh; +}; + +#endif // hifi_SimpleMeshProxy_h diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 95985a48cb..3856a333c3 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/Model.cpp b/libraries/render-utils/src/Model.cpp index 81ff5433f7..281fc89b16 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -31,6 +31,7 @@ namespace { QLoggingCategory wtf{ "tim.Model.cpp" }; } #include #include +#include #include #include @@ -333,34 +334,6 @@ void Model::reset() { } } -#if FBX_PACK_NORMALS -static void packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint32& packedNormal, glm::uint32& packedTangent) { - auto absNormal = glm::abs(normal); - auto absTangent = glm::abs(tangent); - normal /= glm::max(1e-6f, glm::max(glm::max(absNormal.x, absNormal.y), absNormal.z)); - tangent /= glm::max(1e-6f, glm::max(glm::max(absTangent.x, absTangent.y), absTangent.z)); - normal = glm::clamp(normal, -1.0f, 1.0f); - tangent = glm::clamp(tangent, -1.0f, 1.0f); - normal *= 511.0f; - tangent *= 511.0f; - normal = glm::round(normal); - tangent = glm::round(tangent); - - glm::detail::i10i10i10i2 normalStruct; - glm::detail::i10i10i10i2 tangentStruct; - normalStruct.data.x = int(normal.x); - normalStruct.data.y = int(normal.y); - normalStruct.data.z = int(normal.z); - normalStruct.data.w = 0; - tangentStruct.data.x = int(tangent.x); - tangentStruct.data.y = int(tangent.y); - tangentStruct.data.z = int(tangent.z); - tangentStruct.data.w = 0; - packedNormal = normalStruct.pack; - packedTangent = tangentStruct.pack; -} -#endif - bool Model::updateGeometry() { bool needFullUpdate = false; @@ -591,23 +564,26 @@ bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointe // FIXME: temporary workaround for updating the whole FBXGeometry (to keep findRayIntersection in sync) auto newRenderGeometry = new MyGeometryMappingResource( _url, _renderGeometry, _newModel ? scriptable::make_qtowned(*_newModel) : nullptr - ); - _needsUpdateTextures = true; + ); + //_needsUpdateTextures = true; _visualGeometryRequestFailed = false; - invalidCalculatedMeshBoxes(); + //invalidCalculatedMeshBoxes(); deleteGeometry(); _renderGeometry.reset(newRenderGeometry); - onInvalidate(); - reset(); + //onInvalidate(); + //reset(); _rig.destroyAnimGraph(); - assert(_rig.jointStatesEmpty()); + //assert(_rig.jointStatesEmpty()); updateGeometry(); calculateTriangleSets(); - computeMeshPartLocalBounds(); - _needsReload = false; + //computeMeshPartLocalBounds(); + //_needsReload = false; _needsFixupInScene = true; - invalidCalculatedMeshBoxes(); + //invalidCalculatedMeshBoxes(); setRenderItemsNeedUpdate(); + //_hasCalculatedTextureInfo = false; + //calculateTextureInfo(); + //updateRenderItems(); } return true; } @@ -615,7 +591,6 @@ bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointe scriptable::ScriptableModelBase Model::getScriptableModel(bool* ok) { QMutexLocker lock(&_mutex); scriptable::ScriptableModelBase result; - const Geometry::Pointer& renderGeometry = getGeometry(); if (!isLoaded()) { qCDebug(wtf) << "Model::getScriptableModel -- !isLoaded"; @@ -623,107 +598,26 @@ scriptable::ScriptableModelBase Model::getScriptableModel(bool* ok) { } const FBXGeometry& geometry = getFBXGeometry(); - Transform offset; - offset.setScale(_scale); - offset.postTranslate(_offset); - glm::mat4 offsetMat = offset.getMatrix(); - glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset); - glm::mat4 meshToWorldMatrix = createMatFromQuatAndPos(_rotation, _translation) * meshToModelMatrix; - result.metadata = { - { "url", _url.toString() }, - { "textures", renderGeometry->getTextures() }, - { "offset", vec3toVariant(_offset) }, - { "scale", vec3toVariant(getScale()) }, - { "rotation", quatToVariant(getRotation()) }, - { "translation", vec3toVariant(getTranslation()) }, - { "meshToModel", buffer_helpers::toVariant(meshToModelMatrix) }, - { "meshToWorld", buffer_helpers::toVariant(meshToWorldMatrix) }, - { "geometryOffset", buffer_helpers::toVariant(geometry.offset) }, - { "naturalDimensions", vec3toVariant(getNaturalDimensions()) }, - { "meshExtents", buffer_helpers::toVariant(getMeshExtents()) }, - { "unscaledMeshExtents", buffer_helpers::toVariant(getUnscaledMeshExtents()) }, - { "meshBound", buffer_helpers::toVariant(Extents(getRenderableMeshBound())) }, - { "bindExtents", buffer_helpers::toVariant(getBindExtents()) }, - { "offsetMat", buffer_helpers::toVariant(offsetMat) }, - { "transform", buffer_helpers::toVariant(getTransform().getMatrix()) }, - }; - { - Transform transform; - transform.setScale(getScale()); - transform.setTranslation(getTranslation()); - transform.setRotation(getRotation()); - result.metadata["_transform"] = buffer_helpers::toVariant(transform.getMatrix()); - } - { - glm::vec3 position = _translation; - glm::mat4 rotation = glm::mat4_cast(_rotation); - glm::mat4 translation = glm::translate(position); - glm::mat4 modelToWorldMatrix = translation * rotation; - //glm::mat4 worldToModelMatrix = glm::inverse(modelToWorldMatrix); - result.metadata["_modelToWorld"] = buffer_helpers::toVariant(modelToWorldMatrix); - } - { - glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset); - glm::mat4 meshToWorldMatrix = createMatFromQuatAndPos(_rotation, _translation) * meshToModelMatrix; - //glm::mat4 worldToMeshMatrix = glm::inverse(meshToWorldMatrix); - result.metadata["_meshToWorld"] = buffer_helpers::toVariant(meshToWorldMatrix); - } - { - Transform transform; - transform.setTranslation(_translation); - transform.setRotation(_rotation); - Transform offset; - offset.setScale(_scale); - offset.postTranslate(_offset); - Transform output; - Transform::mult( output, transform, offset); - result.metadata["_renderTransform"] = buffer_helpers::toVariant(output.getMatrix()); - } - - QVariantList submeshes; int numberOfMeshes = geometry.meshes.size(); for (int i = 0; i < numberOfMeshes; i++) { const FBXMesh& fbxMesh = geometry.meshes.at(i); - auto mesh = fbxMesh._mesh; - if (!mesh) { - continue; + if (auto mesh = fbxMesh._mesh) { + auto name = geometry.getModelNameOfMesh(i); + result.append(std::const_pointer_cast(mesh), { + { "index", i }, + { "name", name }, + { "meshIndex", fbxMesh.meshIndex }, + { "displayName", QString::fromStdString(mesh->displayName) }, + { "modelName", QString::fromStdString(mesh->modelName) }, + { "modelTransform", buffer_helpers::toVariant(fbxMesh.modelTransform) }, + { "transform", buffer_helpers::toVariant(geometry.offset * fbxMesh.modelTransform) }, + { "extents", buffer_helpers::toVariant(fbxMesh.meshExtents) }, + }); } - auto name = geometry.getModelNameOfMesh(i); - qCDebug(wtf) << "Model::getScriptableModel #" << i << QString::fromStdString(mesh->displayName) << name; - const AABox& box = _modelSpaceMeshTriangleSets.value(i).getBounds(); - AABox hardbounds; - auto meshTransform = geometry.offset * fbxMesh.modelTransform; - for (const auto& v : fbxMesh.vertices) { - hardbounds += glm::vec3(meshTransform * glm::vec4(v,1)); - } - QVariantList renderIDs; - for (uint32_t m = 0; m < _modelMeshRenderItemIDs.size(); m++) { - auto meshIndex = _modelMeshRenderItemShapes.size() > m ? _modelMeshRenderItemShapes.at(m).meshIndex : -1; - if (meshIndex == i) { - renderIDs << _modelMeshRenderItemIDs[m]; - break; - } - } - - result.append(std::const_pointer_cast(mesh), { - { "index", i }, - { "name", name }, - { "renderIDs", renderIDs }, - { "meshIndex", fbxMesh.meshIndex }, - { "displayName", QString::fromStdString(mesh->displayName) }, - { "modelName", QString::fromStdString(mesh->modelName) }, - { "modelTransform", buffer_helpers::toVariant(fbxMesh.modelTransform) }, - { "transform", buffer_helpers::toVariant(geometry.offset * fbxMesh.modelTransform) }, - { "extents", buffer_helpers::toVariant(fbxMesh.meshExtents) }, - { "bounds", buffer_helpers::toVariant(Extents(box)) }, - { "hardbounds", buffer_helpers::toVariant(Extents(hardbounds)) }, - }); } if (ok) { *ok = true; } - qCDebug(wtf) << "//Model::getScriptableModel -- #" << result.meshes.size(); - result.metadata["submeshes"] = submeshes; return result; } @@ -1458,7 +1352,7 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo #if FBX_PACK_NORMALS glm::uint32 finalNormal; glm::uint32 finalTangent; - packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent); + buffer_helpers::packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent); #else const auto finalNormal = *normalIt; const auto finalTangent = *tangentIt; @@ -1481,7 +1375,7 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo #if FBX_PACK_NORMALS glm::uint32 finalNormal; glm::uint32 finalTangent; - packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent); + buffer_helpers::packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent); #else const auto finalNormal = *normalIt; const auto finalTangent = *tangentIt; @@ -1555,7 +1449,7 @@ void Model::createVisibleRenderItemSet() { // all of our mesh vectors must match in size if (meshes.size() != _meshStates.size()) { - qCDebug(renderutils) << "WARNING!!!! Mesh Sizes don't match! We will not segregate mesh groups yet."; + qCDebug(renderutils) << "WARNING!!!! Mesh Sizes don't match! " << meshes.size() << _meshStates.size() << " We will not segregate mesh groups yet."; return; } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 375ee016ca..a0fad7b11f 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -140,7 +140,7 @@ public: /// Returns a reference to the shared collision geometry. const Geometry::Pointer& getCollisionGeometry() const { return _collisionGeometry; } - const QVariantMap getTextures() const { assert(isLoaded()); return _renderGeometry->getTextures(); } + const QVariantMap getTextures() const { assert(isLoaded()); return getGeometry()->getTextures(); } Q_INVOKABLE virtual void setTextures(const QVariantMap& textures); /// Provided as a convenience, will crash if !isLoaded() @@ -314,7 +314,7 @@ public: int getResourceDownloadAttempts() { return _renderWatcher.getResourceDownloadAttempts(); } int getResourceDownloadAttemptsRemaining() { return _renderWatcher.getResourceDownloadAttemptsRemaining(); } - Q_INVOKABLE virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override; + virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override; virtual bool replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer model, int meshIndex, int partIndex) override; void scaleToFit(); @@ -407,7 +407,7 @@ protected: int _blendNumber; int _appliedBlendNumber; - QMutex _mutex; + mutable QMutex _mutex{ QMutex::Recursive }; bool _overrideModelTransform { false }; bool _triangleSetsValid { false }; diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp new file mode 100644 index 0000000000..c693083ebf --- /dev/null +++ b/libraries/script-engine/src/ModelScriptingInterface.cpp @@ -0,0 +1,251 @@ +// +// ModelScriptingInterface.cpp +// libraries/script-engine/src +// +// Created by Seth Alves on 2017-1-27. +// Copyright 2017 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 "ModelScriptingInterface.h" +#include +#include +#include +#include +#include "ScriptEngine.h" +#include "ScriptEngineLogging.h" +#include "OBJWriter.h" + +ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(parent) { + _modelScriptEngine = qobject_cast(parent); + + qScriptRegisterSequenceMetaType>(_modelScriptEngine); + qScriptRegisterMetaType(_modelScriptEngine, meshFaceToScriptValue, meshFaceFromScriptValue); + qScriptRegisterMetaType(_modelScriptEngine, qVectorMeshFaceToScriptValue, qVectorMeshFaceFromScriptValue); +} + +QString ModelScriptingInterface::meshToOBJ(MeshProxyList in) { + QList meshes; + foreach (const MeshProxy* meshProxy, in) { + meshes.append(meshProxy->getMeshPointer()); + } + + return writeOBJToString(meshes); +} + +QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) { + // figure out the size of the resulting mesh + size_t totalVertexCount { 0 }; + size_t totalColorCount { 0 }; + size_t totalNormalCount { 0 }; + size_t totalIndexCount { 0 }; + foreach (const MeshProxy* meshProxy, in) { + MeshPointer mesh = meshProxy->getMeshPointer(); + totalVertexCount += mesh->getNumVertices(); + + int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h + const gpu::BufferView& colorsBufferView = mesh->getAttributeBuffer(attributeTypeColor); + gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements(); + totalColorCount += numColors; + + int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h + const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(attributeTypeNormal); + gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements(); + totalNormalCount += numNormals; + + totalIndexCount += mesh->getNumIndices(); + } + + // alloc the resulting mesh + gpu::Resource::Size combinedVertexSize = totalVertexCount * sizeof(glm::vec3); + std::unique_ptr combinedVertexData{ new unsigned char[combinedVertexSize] }; + unsigned char* combinedVertexDataCursor = combinedVertexData.get(); + + gpu::Resource::Size combinedColorSize = totalColorCount * sizeof(glm::vec3); + std::unique_ptr combinedColorData{ new unsigned char[combinedColorSize] }; + unsigned char* combinedColorDataCursor = combinedColorData.get(); + + gpu::Resource::Size combinedNormalSize = totalNormalCount * sizeof(glm::vec3); + std::unique_ptr combinedNormalData{ new unsigned char[combinedNormalSize] }; + unsigned char* combinedNormalDataCursor = combinedNormalData.get(); + + gpu::Resource::Size combinedIndexSize = totalIndexCount * sizeof(uint32_t); + std::unique_ptr combinedIndexData{ new unsigned char[combinedIndexSize] }; + unsigned char* combinedIndexDataCursor = combinedIndexData.get(); + + uint32_t indexStartOffset { 0 }; + + foreach (const MeshProxy* meshProxy, in) { + MeshPointer mesh = meshProxy->getMeshPointer(); + mesh->forEach( + [&](glm::vec3 position){ + memcpy(combinedVertexDataCursor, &position, sizeof(position)); + combinedVertexDataCursor += sizeof(position); + }, + [&](glm::vec3 color){ + memcpy(combinedColorDataCursor, &color, sizeof(color)); + combinedColorDataCursor += sizeof(color); + }, + [&](glm::vec3 normal){ + memcpy(combinedNormalDataCursor, &normal, sizeof(normal)); + combinedNormalDataCursor += sizeof(normal); + }, + [&](uint32_t index){ + index += indexStartOffset; + memcpy(combinedIndexDataCursor, &index, sizeof(index)); + combinedIndexDataCursor += sizeof(index); + }); + + gpu::BufferView::Index numVertices = (gpu::BufferView::Index)mesh->getNumVertices(); + indexStartOffset += numVertices; + } + + graphics::MeshPointer result(new graphics::Mesh()); + + gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); + gpu::Buffer* combinedVertexBuffer = new gpu::Buffer(combinedVertexSize, combinedVertexData.get()); + gpu::BufferPointer combinedVertexBufferPointer(combinedVertexBuffer); + gpu::BufferView combinedVertexBufferView(combinedVertexBufferPointer, vertexElement); + result->setVertexBuffer(combinedVertexBufferView); + + int attributeTypeColor = gpu::Stream::InputSlot::COLOR; // libraries/gpu/src/gpu/Stream.h + gpu::Element colorElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); + gpu::Buffer* combinedColorsBuffer = new gpu::Buffer(combinedColorSize, combinedColorData.get()); + gpu::BufferPointer combinedColorsBufferPointer(combinedColorsBuffer); + gpu::BufferView combinedColorsBufferView(combinedColorsBufferPointer, colorElement); + result->addAttribute(attributeTypeColor, combinedColorsBufferView); + + int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h + gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); + gpu::Buffer* combinedNormalsBuffer = new gpu::Buffer(combinedNormalSize, combinedNormalData.get()); + gpu::BufferPointer combinedNormalsBufferPointer(combinedNormalsBuffer); + gpu::BufferView combinedNormalsBufferView(combinedNormalsBufferPointer, normalElement); + result->addAttribute(attributeTypeNormal, combinedNormalsBufferView); + + gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW); + gpu::Buffer* combinedIndexesBuffer = new gpu::Buffer(combinedIndexSize, combinedIndexData.get()); + gpu::BufferPointer combinedIndexesBufferPointer(combinedIndexesBuffer); + gpu::BufferView combinedIndexesBufferView(combinedIndexesBufferPointer, indexElement); + result->setIndexBuffer(combinedIndexesBufferView); + + std::vector parts; + parts.emplace_back(graphics::Mesh::Part((graphics::Index)0, // startIndex + (graphics::Index)result->getNumIndices(), // numIndices + (graphics::Index)0, // baseVertex + graphics::Mesh::TRIANGLES)); // topology + result->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(graphics::Mesh::Part), + (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); + + + MeshProxy* resultProxy = new SimpleMeshProxy(result); + return meshToScriptValue(_modelScriptEngine, resultProxy); +} + +QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshProxy* meshProxy) { + if (!meshProxy) { + return QScriptValue(false); + } + MeshPointer mesh = meshProxy->getMeshPointer(); + if (!mesh) { + return QScriptValue(false); + } + + const auto inverseTransposeTransform = glm::inverse(glm::transpose(transform)); + graphics::MeshPointer result = mesh->map([&](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); }, + [&](glm::vec3 color){ return color; }, + [&](glm::vec3 normal){ return glm::vec3(inverseTransposeTransform * glm::vec4(normal, 0.0f)); }, + [&](uint32_t index){ return index; }); + MeshProxy* resultProxy = new SimpleMeshProxy(result); + return meshToScriptValue(_modelScriptEngine, resultProxy); +} + +QScriptValue ModelScriptingInterface::getVertexCount(MeshProxy* meshProxy) { + if (!meshProxy) { + return QScriptValue(false); + } + MeshPointer mesh = meshProxy->getMeshPointer(); + if (!mesh) { + return QScriptValue(false); + } + + gpu::BufferView::Index numVertices = (gpu::BufferView::Index)mesh->getNumVertices(); + + return numVertices; +} + +QScriptValue ModelScriptingInterface::getVertex(MeshProxy* meshProxy, int vertexIndex) { + if (!meshProxy) { + return QScriptValue(false); + } + MeshPointer mesh = meshProxy->getMeshPointer(); + if (!mesh) { + return QScriptValue(false); + } + + const gpu::BufferView& vertexBufferView = mesh->getVertexBuffer(); + gpu::BufferView::Index numVertices = (gpu::BufferView::Index)mesh->getNumVertices(); + + if (vertexIndex < 0 || vertexIndex >= numVertices) { + return QScriptValue(false); + } + + glm::vec3 pos = vertexBufferView.get(vertexIndex); + return vec3toScriptValue(_modelScriptEngine, pos); +} + + +QScriptValue ModelScriptingInterface::newMesh(const QVector& vertices, + const QVector& normals, + const QVector& faces) { + graphics::MeshPointer mesh(new graphics::Mesh()); + + // vertices + auto vertexBuffer = std::make_shared(vertices.size() * sizeof(glm::vec3), (gpu::Byte*)vertices.data()); + auto vertexBufferPtr = gpu::BufferPointer(vertexBuffer); + gpu::BufferView vertexBufferView(vertexBufferPtr, 0, vertexBufferPtr->getSize(), + sizeof(glm::vec3), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + mesh->setVertexBuffer(vertexBufferView); + + if (vertices.size() == normals.size()) { + // normals + auto normalBuffer = std::make_shared(normals.size() * sizeof(glm::vec3), (gpu::Byte*)normals.data()); + auto normalBufferPtr = gpu::BufferPointer(normalBuffer); + gpu::BufferView normalBufferView(normalBufferPtr, 0, normalBufferPtr->getSize(), + sizeof(glm::vec3), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); + mesh->addAttribute(gpu::Stream::NORMAL, normalBufferView); + } else { + qCDebug(scriptengine) << "ModelScriptingInterface::newMesh normals must be same length as vertices"; + } + + // indices (faces) + int VERTICES_PER_TRIANGLE = 3; + int indexBufferSize = faces.size() * sizeof(uint32_t) * VERTICES_PER_TRIANGLE; + unsigned char* indexData = new unsigned char[indexBufferSize]; + unsigned char* indexDataCursor = indexData; + foreach(const MeshFace& meshFace, faces) { + for (int i = 0; i < VERTICES_PER_TRIANGLE; i++) { + memcpy(indexDataCursor, &meshFace.vertexIndices[i], sizeof(uint32_t)); + indexDataCursor += sizeof(uint32_t); + } + } + auto indexBuffer = std::make_shared(indexBufferSize, (gpu::Byte*)indexData); + auto indexBufferPtr = gpu::BufferPointer(indexBuffer); + gpu::BufferView indexBufferView(indexBufferPtr, gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW)); + mesh->setIndexBuffer(indexBufferView); + + // parts + std::vector parts; + parts.emplace_back(graphics::Mesh::Part((graphics::Index)0, // startIndex + (graphics::Index)faces.size() * 3, // numIndices + (graphics::Index)0, // baseVertex + graphics::Mesh::TRIANGLES)); // topology + mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(graphics::Mesh::Part), + (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); + + + + MeshProxy* meshProxy = new SimpleMeshProxy(mesh); + return meshToScriptValue(_modelScriptEngine, meshProxy); +} diff --git a/libraries/script-engine/src/ModelScriptingInterface.h b/libraries/script-engine/src/ModelScriptingInterface.h new file mode 100644 index 0000000000..3c239f006f --- /dev/null +++ b/libraries/script-engine/src/ModelScriptingInterface.h @@ -0,0 +1,39 @@ +// +// ModelScriptingInterface.h +// libraries/script-engine/src +// +// Created by Seth Alves on 2017-1-27. +// Copyright 2017 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_ModelScriptingInterface_h +#define hifi_ModelScriptingInterface_h + +#include + +#include +class QScriptEngine; + +class ModelScriptingInterface : public QObject { + Q_OBJECT + +public: + ModelScriptingInterface(QObject* parent); + + Q_INVOKABLE QString meshToOBJ(MeshProxyList in); + Q_INVOKABLE QScriptValue appendMeshes(MeshProxyList in); + Q_INVOKABLE QScriptValue transformMesh(glm::mat4 transform, MeshProxy* meshProxy); + Q_INVOKABLE QScriptValue newMesh(const QVector& vertices, + const QVector& normals, + const QVector& faces); + Q_INVOKABLE QScriptValue getVertexCount(MeshProxy* meshProxy); + Q_INVOKABLE QScriptValue getVertex(MeshProxy* meshProxy, int vertexIndex); + +private: + QScriptEngine* _modelScriptEngine { nullptr }; +}; + +#endif // hifi_ModelScriptingInterface_h diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index ffb1b7bc74..c79ffffec7 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -73,6 +73,8 @@ #include "WebSocketClass.h" #include "RecordingScriptingInterface.h" #include "ScriptEngines.h" +#include "ModelScriptingInterface.h" + #include @@ -709,6 +711,10 @@ void ScriptEngine::init() { registerGlobalObject("DebugDraw", &DebugDraw::getInstance()); + registerGlobalObject("Model", new ModelScriptingInterface(this)); + qScriptRegisterMetaType(this, meshToScriptValue, meshFromScriptValue); + qScriptRegisterMetaType(this, meshesToScriptValue, meshesFromScriptValue); + registerGlobalObject("UserActivityLogger", DependencyManager::get().data()); } diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 9ad5c27072..7b455beae5 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -855,3 +855,68 @@ QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const Animatio void animationDetailsFromScriptValue(const QScriptValue& object, AnimationDetails& details) { // nothing for now... } + +QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in) { + return engine->newQObject(in, QScriptEngine::QtOwnership, + QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); +} + +void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out) { + out = qobject_cast(value.toQObject()); +} + +QScriptValue meshesToScriptValue(QScriptEngine* engine, const MeshProxyList &in) { + // QScriptValueList result; + QScriptValue result = engine->newArray(); + int i = 0; + foreach(MeshProxy* const meshProxy, in) { + result.setProperty(i++, meshToScriptValue(engine, meshProxy)); + } + return result; +} + +void meshesFromScriptValue(const QScriptValue& value, MeshProxyList &out) { + QScriptValueIterator itr(value); + + qDebug() << "in meshesFromScriptValue, value.length =" << value.property("length").toInt32(); + + while (itr.hasNext()) { + itr.next(); + MeshProxy* meshProxy = qscriptvalue_cast(itr.value()); + if (meshProxy) { + out.append(meshProxy); + } else { + qDebug() << "null meshProxy"; + } + } +} + + +QScriptValue meshFaceToScriptValue(QScriptEngine* engine, const MeshFace &meshFace) { + QScriptValue obj = engine->newObject(); + obj.setProperty("vertices", qVectorIntToScriptValue(engine, meshFace.vertexIndices)); + return obj; +} + +void meshFaceFromScriptValue(const QScriptValue &object, MeshFace& meshFaceResult) { + qVectorIntFromScriptValue(object.property("vertices"), meshFaceResult.vertexIndices); +} + +QScriptValue qVectorMeshFaceToScriptValue(QScriptEngine* engine, const QVector& vector) { + QScriptValue array = engine->newArray(); + for (int i = 0; i < vector.size(); i++) { + array.setProperty(i, meshFaceToScriptValue(engine, vector.at(i))); + } + return array; +} + +void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector& result) { + int length = array.property("length").toInteger(); + result.clear(); + + for (int i = 0; i < length; i++) { + MeshFace meshFace = MeshFace(); + meshFaceFromScriptValue(array.property(i), meshFace); + result << meshFace; + } +} diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index e8390c3a86..25b2cec331 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -315,9 +315,51 @@ Q_DECLARE_METATYPE(AnimationDetails); QScriptValue animationDetailsToScriptValue(QScriptEngine* engine, const AnimationDetails& event); void animationDetailsFromScriptValue(const QScriptValue& object, AnimationDetails& event); +namespace graphics { + class Mesh; +} + +using MeshPointer = std::shared_ptr; +class MeshProxy : public QObject { + Q_OBJECT +public: + virtual MeshPointer getMeshPointer() const = 0; + Q_INVOKABLE virtual int getNumVertices() const = 0; + Q_INVOKABLE virtual glm::vec3 getPos3(int index) const = 0; +}; + +Q_DECLARE_METATYPE(MeshProxy*); + +class MeshProxyList : public QList {}; // typedef and using fight with the Qt macros/templates, do this instead +Q_DECLARE_METATYPE(MeshProxyList); + + +QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in); +void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out); + +QScriptValue meshesToScriptValue(QScriptEngine* engine, const MeshProxyList &in); +void meshesFromScriptValue(const QScriptValue& value, MeshProxyList &out); + +class MeshFace { + +public: + MeshFace() {} + ~MeshFace() {} + + QVector vertexIndices; + // TODO -- material... +}; + +Q_DECLARE_METATYPE(MeshFace) +Q_DECLARE_METATYPE(QVector) + +QScriptValue meshFaceToScriptValue(QScriptEngine* engine, const MeshFace &meshFace); +void meshFaceFromScriptValue(const QScriptValue &object, MeshFace& meshFaceResult); +QScriptValue qVectorMeshFaceToScriptValue(QScriptEngine* engine, const QVector& vector); +void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector& result); #endif // hifi_RegisteredMetaTypes_h diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 090ca4c266..5d4793ba4e 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -207,6 +207,10 @@ public: void dump(const QString& prefix = "") const; + virtual void locationChanged(bool tellPhysics = true); // called when a this object's location has changed + virtual void dimensionsChanged() { _queryAACubeSet = false; } // called when a this object's dimensions have changed + virtual void parentDeleted() { } // called on children of a deleted parent + protected: const NestableType _nestableType; // EntityItem or an AvatarData QUuid _id; @@ -218,10 +222,6 @@ protected: mutable ReadWriteLockable _childrenLock; mutable QHash _children; - virtual void locationChanged(bool tellPhysics = true); // called when a this object's location has changed - virtual void dimensionsChanged() { _queryAACubeSet = false; } // called when a this object's dimensions have changed - virtual void parentDeleted() { } // called on children of a deleted parent - // _queryAACube is used to decide where something lives in the octree mutable AACube _queryAACube; mutable bool _queryAACubeSet { false };