From 06afaa7470f90a6b152c474958315d4c7e80ddd5 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 21 Dec 2017 15:13:57 -0500 Subject: [PATCH 01/29] BufferView <-> QVariant/QScriptValue conversion update MeshProxy/SimpleMeshProxy and ScriptableModel ModelScriptingInterface / scriptable::ModelProvider integration update to RC-63 initial graphics-scripting refactoring graphics-scripting baseline commit wip commit Geometry -> MeshPart remove SimpleMeshProxy collapse graphics-utils -> graphics-scripting scriptable::Model => scriptable::ScriptableModel --- interface/CMakeLists.txt | 2 +- interface/src/Application.cpp | 68 +- interface/src/Application.h | 1 - interface/src/ui/overlays/Base3DOverlay.h | 6 +- interface/src/ui/overlays/ModelOverlay.cpp | 7 + interface/src/ui/overlays/ModelOverlay.h | 1 + interface/src/ui/overlays/Shape3DOverlay.cpp | 15 + interface/src/ui/overlays/Shape3DOverlay.h | 1 + libraries/avatars-renderer/CMakeLists.txt | 1 + .../src/avatars-renderer/Avatar.cpp | 28 + .../src/avatars-renderer/Avatar.h | 5 +- libraries/entities-renderer/CMakeLists.txt | 1 + .../src/RenderableEntityItem.h | 6 +- .../src/RenderableModelEntityItem.cpp | 15 +- .../src/RenderableModelEntityItem.h | 2 +- .../src/RenderablePolyLineEntityItem.cpp | 6 +- .../src/RenderablePolyLineEntityItem.h | 1 + .../src/RenderablePolyVoxEntityItem.cpp | 54 +- .../src/RenderablePolyVoxEntityItem.h | 7 +- .../src/RenderableShapeEntityItem.cpp | 21 + .../src/RenderableShapeEntityItem.h | 2 + libraries/entities/src/EntityItem.h | 4 - .../entities/src/EntityScriptingInterface.cpp | 24 - .../entities/src/EntityScriptingInterface.h | 4 - libraries/fbx/src/FBXReader.cpp | 16 +- libraries/fbx/src/OBJWriter.cpp | 78 +- libraries/graphics-scripting/CMakeLists.txt | 5 + .../graphics-scripting/BufferViewHelpers.cpp | 195 ++++ .../graphics-scripting/BufferViewHelpers.h | 18 + .../BufferViewScripting.cpp | 83 ++ .../graphics-scripting/BufferViewScripting.h | 11 + .../src/graphics-scripting/DebugNames.h | 72 ++ .../ModelScriptingInterface.cpp | 836 ++++++++++++++++++ .../ModelScriptingInterface.h | 68 ++ .../src/graphics-scripting/ScriptableMesh.cpp | 359 ++++++++ .../src/graphics-scripting/ScriptableMesh.h | 127 +++ .../src/graphics-scripting/ScriptableModel.h | 80 ++ libraries/graphics/src/graphics/Geometry.cpp | 6 + libraries/graphics/src/graphics/Geometry.h | 1 + .../src/model-networking/SimpleMeshProxy.cpp | 27 - .../src/model-networking/SimpleMeshProxy.h | 36 - libraries/render-utils/CMakeLists.txt | 1 + libraries/render-utils/src/GeometryCache.cpp | 45 + libraries/render-utils/src/GeometryCache.h | 1 + libraries/render-utils/src/Model.cpp | 87 +- libraries/render-utils/src/Model.h | 5 +- .../src/ModelScriptingInterface.cpp | 251 ------ .../src/ModelScriptingInterface.h | 39 - libraries/script-engine/src/ScriptEngine.cpp | 6 - libraries/shared/src/RegisteredMetaTypes.cpp | 65 -- libraries/shared/src/RegisteredMetaTypes.h | 42 - 51 files changed, 2242 insertions(+), 600 deletions(-) create mode 100644 libraries/graphics-scripting/CMakeLists.txt create mode 100644 libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.cpp create mode 100644 libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.h create mode 100644 libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp create mode 100644 libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.h create mode 100644 libraries/graphics-scripting/src/graphics-scripting/DebugNames.h create mode 100644 libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp create mode 100644 libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.h create mode 100644 libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp create mode 100644 libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h create mode 100644 libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h delete mode 100644 libraries/model-networking/src/model-networking/SimpleMeshProxy.cpp delete mode 100644 libraries/model-networking/src/model-networking/SimpleMeshProxy.h delete mode 100644 libraries/script-engine/src/ModelScriptingInterface.cpp delete mode 100644 libraries/script-engine/src/ModelScriptingInterface.h diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index ee2997e216..081eeae02e 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -191,7 +191,7 @@ endif() # link required hifi libraries link_hifi_libraries( - shared octree ktx gpu gl procedural graphics render + shared octree ktx gpu gl procedural graphics graphics-scripting render pointers recording fbx networking model-networking entities avatars trackers audio audio-client animation script-engine physics diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 99bd4d5758..f24969ce60 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -166,6 +166,7 @@ #include "scripting/AccountServicesScriptingInterface.h" #include "scripting/HMDScriptingInterface.h" #include "scripting/MenuScriptingInterface.h" +#include "graphics-scripting/ModelScriptingInterface.h" #include "scripting/SettingsScriptingInterface.h" #include "scripting/WindowScriptingInterface.h" #include "scripting/ControllerScriptingInterface.h" @@ -198,7 +199,6 @@ #include #include #include -#include #include #include @@ -593,6 +593,66 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt } } + +class ApplicationMeshProvider : public scriptable::ModelProviderFactory { +public: + virtual scriptable::ModelProviderPointer lookupModelProvider(QUuid uuid) { + QString error; + + scriptable::ModelProviderPointer provider; + if (auto entityInterface = getEntityModelProvider(static_cast(uuid))) { + provider = entityInterface; + } else if (auto overlayInterface = getOverlayModelProvider(static_cast(uuid))) { + provider = overlayInterface; + } else if (auto avatarInterface = getAvatarModelProvider(uuid)) { + provider = avatarInterface; + } + + return provider; + } + + scriptable::ModelProviderPointer getEntityModelProvider(EntityItemID entityID) { + scriptable::ModelProviderPointer provider; + auto entityTreeRenderer = qApp->getEntities(); + auto entityTree = entityTreeRenderer->getTree(); + if (auto entity = entityTree->findEntityByID(entityID)) { + if (auto renderer = entityTreeRenderer->renderableForEntityId(entityID)) { + provider = std::dynamic_pointer_cast(renderer); + provider->metadata["providerType"] = "entity"; + } else { + qCWarning(interfaceapp) << "no renderer for entity ID" << entityID.toString(); + } + } + return provider; + } + + scriptable::ModelProviderPointer getOverlayModelProvider(OverlayID overlayID) { + scriptable::ModelProviderPointer provider; + auto &overlays = qApp->getOverlays(); + if (auto overlay = overlays.getOverlay(overlayID)) { + if (auto base3d = std::dynamic_pointer_cast(overlay)) { + provider = std::dynamic_pointer_cast(base3d); + provider->metadata["providerType"] = "overlay"; + } else { + qCWarning(interfaceapp) << "no renderer for overlay ID" << overlayID.toString(); + } + } + return provider; + } + + scriptable::ModelProviderPointer getAvatarModelProvider(QUuid sessionUUID) { + scriptable::ModelProviderPointer provider; + auto avatarManager = DependencyManager::get(); + if (auto avatar = avatarManager->getAvatarBySessionID(sessionUUID)) { + if (avatar->getSessionUUID() == sessionUUID) { + provider = std::dynamic_pointer_cast(avatar); + provider->metadata["providerType"] = "avatar"; + } + } + return provider; + } +}; + static const QString STATE_IN_HMD = "InHMD"; static const QString STATE_CAMERA_FULL_SCREEN_MIRROR = "CameraFSM"; static const QString STATE_CAMERA_FIRST_PERSON = "CameraFirstPerson"; @@ -737,6 +797,9 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(true); + DependencyManager::set(); + DependencyManager::registerInheritance(); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -5915,6 +5978,9 @@ 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()); + scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get().data()); scriptEngine->registerGlobalObject("Reticle", getApplicationCompositor().getReticleInterface()); diff --git a/interface/src/Application.h b/interface/src/Application.h index ddb8ce11e5..ae07ebd9dd 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -72,7 +72,6 @@ #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 df0f3c4728..0c8bc5aacb 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -13,10 +13,11 @@ #include #include - +#include #include "Overlay.h" -class Base3DOverlay : public Overlay, public SpatiallyNestable { +namespace model { class Mesh; } +class Base3DOverlay : public Overlay, public SpatiallyNestable, public scriptable::ModelProvider { Q_OBJECT using Parent = Overlay; @@ -36,6 +37,7 @@ public: virtual bool is3D() const override { return true; } virtual uint32_t fetchMetaSubItems(render::ItemIDs& subItems) const override { subItems.push_back(getRenderItemID()); return (uint32_t) subItems.size(); } + virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override { return scriptable::ModelProvider::modelUnavailableError(ok); } // TODO: consider implementing registration points in this class glm::vec3 getCenter() const { return getWorldPosition(); } diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 310dbf78d8..5a80ca1abf 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -629,3 +629,10 @@ uint32_t ModelOverlay::fetchMetaSubItems(render::ItemIDs& subItems) const { } return 0; } + +scriptable::ScriptableModel ModelOverlay::getScriptableModel(bool* ok) { + if (!_model || !_model->isLoaded()) { + return Base3DOverlay::getScriptableModel(ok); + } + return _model->getScriptableModel(ok); +} diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index 60ba90e568..32d9a08c70 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -59,6 +59,7 @@ public: void setDrawInFront(bool drawInFront) override; void setDrawHUDLayer(bool drawHUDLayer) override; + virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override; protected: Transform evalRenderTransform() override; diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp index 97342a80ab..8bb3d16888 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.cpp +++ b/interface/src/ui/overlays/Shape3DOverlay.cpp @@ -179,3 +179,18 @@ Transform Shape3DOverlay::evalRenderTransform() { transform.setRotation(rotation); return transform; } + +scriptable::ScriptableModel Shape3DOverlay::getScriptableModel(bool* ok) { + auto geometryCache = DependencyManager::get(); + auto vertexColor = ColorUtils::toVec3(_color); + scriptable::ScriptableModel result; + result.metadata = { + { "origin", "Shape3DOverlay::"+shapeStrings[_shape] }, + { "overlayID", getID() }, + }; + result.meshes << geometryCache->meshFromShape(_shape, vertexColor); + if (ok) { + *ok = true; + } + return result; +} diff --git a/interface/src/ui/overlays/Shape3DOverlay.h b/interface/src/ui/overlays/Shape3DOverlay.h index 7fc95ec981..34f82af278 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.h +++ b/interface/src/ui/overlays/Shape3DOverlay.h @@ -37,6 +37,7 @@ public: void setProperties(const QVariantMap& properties) override; QVariant getProperty(const QString& property) override; + virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override; protected: Transform evalRenderTransform() override; diff --git a/libraries/avatars-renderer/CMakeLists.txt b/libraries/avatars-renderer/CMakeLists.txt index 53edc692f2..f7e951500b 100644 --- a/libraries/avatars-renderer/CMakeLists.txt +++ b/libraries/avatars-renderer/CMakeLists.txt @@ -13,5 +13,6 @@ include_hifi_library_headers(entities-renderer) include_hifi_library_headers(audio) include_hifi_library_headers(entities) include_hifi_library_headers(octree) +include_hifi_library_headers(graphics-scripting) # for ScriptableModel.h target_bullet() diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 500a24763d..8e22f355e4 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -35,6 +35,8 @@ #include "ModelEntityItem.h" #include "RenderableModelEntityItem.h" +#include + #include "Logging.h" using namespace std; @@ -1760,3 +1762,29 @@ float Avatar::getUnscaledEyeHeightFromSkeleton() const { return DEFAULT_AVATAR_EYE_HEIGHT; } } + +scriptable::ScriptableModel Avatar::getScriptableModel(bool* ok) { + qDebug() << "Avatar::getScriptableModel" ; + if (!_skeletonModel || !_skeletonModel->isLoaded()) { + return scriptable::ModelProvider::modelUnavailableError(ok); + } + scriptable::ScriptableModel result; + result.metadata = { + { "avatarID", getSessionUUID().toString() }, + { "url", _skeletonModelURL.toString() }, + { "origin", "Avatar/avatar::" + _displayName }, + { "textures", _skeletonModel->getTextures() }, + }; + result.mixin(_skeletonModel->getScriptableModel(ok)); + + // FIXME: for now access to attachment models are merged with the main avatar model + for (auto& attachmentModel : _attachmentModels) { + if (attachmentModel->isLoaded()) { + result.mixin(attachmentModel->getScriptableModel(ok)); + } + } + if (ok) { + *ok = true; + } + return result; +} diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index c2b404a925..5cfc399b65 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -53,7 +54,7 @@ class Texture; using AvatarPhysicsCallback = std::function; -class Avatar : public AvatarData { +class Avatar : public AvatarData, public scriptable::ModelProvider { Q_OBJECT /**jsdoc @@ -272,6 +273,8 @@ public: virtual void setAvatarEntityDataChanged(bool value) override; + + virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override; public slots: // FIXME - these should be migrated to use Pose data instead diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index ea75367e1e..27ea04f642 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -13,6 +13,7 @@ include_hifi_library_headers(fbx) include_hifi_library_headers(entities) include_hifi_library_headers(avatars) include_hifi_library_headers(controllers) +include_hifi_library_headers(graphics-scripting) # for ScriptableModel.h target_bullet() target_polyvox() diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 8eb82e2c6e..f07b67fbd0 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -17,13 +17,14 @@ #include #include "AbstractViewStateInterface.h" #include "EntitiesRendererLogging.h" +#include class EntityTreeRenderer; namespace render { namespace entities { // Base class for all renderable entities -class EntityRenderer : public QObject, public std::enable_shared_from_this, public PayloadProxyInterface, protected ReadWriteLockable { +class EntityRenderer : public QObject, public std::enable_shared_from_this, public PayloadProxyInterface, protected ReadWriteLockable, public scriptable::ModelProvider { Q_OBJECT using Pointer = std::shared_ptr; @@ -37,7 +38,7 @@ public: virtual bool wantsKeyboardFocus() const { return false; } virtual void setProxyWindow(QWindow* proxyWindow) {} virtual QObject* getEventHandler() { return nullptr; } - const EntityItemPointer& getEntity() { return _entity; } + const EntityItemPointer& getEntity() const { return _entity; } const ItemID& getRenderItemID() const { return _renderItemID; } const SharedSoundPointer& getCollisionSound() { return _collisionSound; } @@ -54,6 +55,7 @@ public: const uint64_t& getUpdateTime() const { return _updateTime; } + virtual scriptable::ScriptableModel 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 9fcb7640ef..7b022fefac 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -950,13 +950,18 @@ QStringList RenderableModelEntityItem::getJointNames() const { return result; } -bool RenderableModelEntityItem::getMeshes(MeshProxyList& result) { - auto model = getModel(); + +scriptable::ScriptableModel render::entities::ModelEntityRenderer::getScriptableModel(bool* ok) { + ModelPointer model; + withReadLock([&] { + model = _model; + }); + if (!model || !model->isLoaded()) { - return false; + return scriptable::ModelProvider::modelUnavailableError(ok); } - BLOCKING_INVOKE_METHOD(model.get(), "getMeshes", Q_RETURN_ARG(MeshProxyList, result)); - return !result.isEmpty(); + + return _model->getScriptableModel(ok); } void RenderableModelEntityItem::simulateRelayedJoints() { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 33fc9910a0..3e952cb9a7 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -111,7 +111,6 @@ public: virtual int getJointIndex(const QString& name) const override; virtual QStringList getJointNames() const override; - bool getMeshes(MeshProxyList& result) override; const void* getCollisionMeshKey() const { return _collisionMeshKey; } signals: @@ -141,6 +140,7 @@ class ModelEntityRenderer : public TypedEntityRenderer PolyLineEntityRenderer::updateVertic return vertices; } +scriptable::ScriptableModel PolyLineEntityRenderer::getScriptableModel(bool *ok) { + // TODO: adapt polyline into a triangles mesh... + return EntityRenderer::getScriptableModel(ok); +} void PolyLineEntityRenderer::doRender(RenderArgs* args) { if (_empty) { @@ -319,4 +323,4 @@ void PolyLineEntityRenderer::doRender(RenderArgs* args) { #endif batch.draw(gpu::TRIANGLE_STRIP, _numVertices, 0); -} \ No newline at end of file +} diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index 1e27ac9ae7..3bb8901178 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -25,6 +25,7 @@ class PolyLineEntityRenderer : public TypedEntityRenderer { public: PolyLineEntityRenderer(const EntityItemPointer& entity); + virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override; protected: virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index ade3790df6..fd923c40b0 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -20,8 +20,6 @@ #include #include -#include -#include #include #include #include @@ -1416,36 +1414,36 @@ void RenderablePolyVoxEntityItem::bonkNeighbors() { } } -bool RenderablePolyVoxEntityItem::getMeshes(MeshProxyList& result) { - if (!updateDependents()) { - return false; +scriptable::ScriptableModel RenderablePolyVoxEntityItem::getScriptableModel(bool * ok) { + if (!updateDependents() || !_mesh) { + return scriptable::ModelProvider::modelUnavailableError(ok); } 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; - } - }); + glm::mat4 transform = voxelToLocalMatrix(); + scriptable::ScriptableModel result; + 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 + result.meshes << + _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; }); + } + }); + if (ok) { + *ok = success; } - return success; + return result; } using namespace render; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index db0f0b729a..55b9be23d8 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -32,7 +32,7 @@ namespace render { namespace entities { class PolyVoxEntityRenderer; } } -class RenderablePolyVoxEntityItem : public PolyVoxEntityItem { +class RenderablePolyVoxEntityItem : public PolyVoxEntityItem, public scriptable::ModelProvider { friend class render::entities::PolyVoxEntityRenderer; public: @@ -113,7 +113,7 @@ public: void setVolDataDirty() { withWriteLock([&] { _volDataDirty = true; _meshReady = false; }); } - bool getMeshes(MeshProxyList& result) override; + virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override; private: bool updateOnCount(const ivec3& v, uint8_t toValue); @@ -163,6 +163,9 @@ class PolyVoxEntityRenderer : public TypedEntityRenderer()->getScriptableModel(ok); + } protected: virtual ItemKey getKey() override { return ItemKey::Builder::opaqueShape(); } diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index cdee2c5ec9..746102681c 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -156,3 +156,24 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { const auto triCount = geometryCache->getShapeTriangleCount(geometryShape); args->_details._trianglesRendered += (int)triCount; } + +scriptable::ScriptableModel ShapeEntityRenderer::getScriptableModel(bool* ok) { + scriptable::ScriptableModel result; + result.metadata = { + { "entityID", getEntity()->getID().toString() }, + { "shape", entity::stringFromShape(_shape) }, + { "userData", getEntity()->getUserData() }, + }; + auto geometryCache = DependencyManager::get(); + auto geometryShape = geometryCache->getShapeForEntityShape(_shape); + auto vertexColor = glm::vec3(_color); + auto success = false; + if (auto mesh = geometryCache->meshFromShape(geometryShape, vertexColor)) { + result.meshes << mesh; + success = true; + } + if (ok) { + *ok = success; + } + return result; +} diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 433cb41ad2..6ada7e7317 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -22,6 +22,8 @@ class ShapeEntityRenderer : public TypedEntityRenderer { public: ShapeEntityRenderer(const EntityItemPointer& entity); + virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override; + private: virtual bool needsRenderUpdate() const override; virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 4c398b8a29..5c9324fc8a 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -57,8 +57,6 @@ using EntityTreeElementExtraEncodeDataPointer = std::shared_ptr(_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 4c4e2ffbfd..da201f93eb 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -37,7 +37,6 @@ #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 @@ -401,9 +400,6 @@ 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 14462e0558..50abe7928f 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1967,19 +1967,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } } } - { - int i = 0; - for (const auto& mesh : geometry.meshes) { - auto name = geometry.getModelNameOfMesh(i++); - if (!name.isEmpty()) { - if (mesh._mesh) { - mesh._mesh->displayName += "#" + name; - } else { - qDebug() << "modelName but no mesh._mesh" << name; - } - } - } - } + return geometryPtr; } @@ -1995,7 +1983,7 @@ FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QStri reader._loadLightmaps = loadLightmaps; reader._lightmapLevel = lightmapLevel; - qCDebug(modelformat) << "Reading FBX: " << url; + qDebug() << "Reading FBX: " << url; return reader.extractFBXGeometry(mapping, url); } diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp index 4441ae6649..37bced8458 100644 --- a/libraries/fbx/src/OBJWriter.cpp +++ b/libraries/fbx/src/OBJWriter.cpp @@ -46,9 +46,12 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { QList meshNormalStartOffset; int currentVertexStartOffset = 0; int currentNormalStartOffset = 0; + int subMeshIndex = 0; + out << "# OBJWriter::writeOBJToTextStream\n"; // write out vertices (and maybe colors) foreach (const MeshPointer& mesh, meshes) { + out << "# vertices::subMeshIndex " << subMeshIndex++ << "\n"; meshVertexStartOffset.append(currentVertexStartOffset); const gpu::BufferView& vertexBuffer = mesh->getVertexBuffer(); @@ -81,7 +84,9 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { // write out normals bool haveNormals = true; + subMeshIndex = 0; foreach (const MeshPointer& mesh, meshes) { + out << "# normals::subMeshIndex " << subMeshIndex++ << "\n"; meshNormalStartOffset.append(currentNormalStartOffset); const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(gpu::Stream::InputSlot::NORMAL); gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements(); @@ -98,7 +103,9 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { // write out faces int nth = 0; + subMeshIndex = 0; foreach (const MeshPointer& mesh, meshes) { + out << "# faces::subMeshIndex " << subMeshIndex++ << "\n"; currentVertexStartOffset = meshVertexStartOffset.takeFirst(); currentNormalStartOffset = meshNormalStartOffset.takeFirst(); @@ -106,35 +113,25 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { const gpu::BufferView& indexBuffer = mesh->getIndexBuffer(); graphics::Index partCount = (graphics::Index)mesh->getNumParts(); + QString name = (!mesh->displayName.size() ? QString("mesh-%1-part").arg(nth) : QString(mesh->displayName)) + .replace(QRegExp("[^-_a-zA-Z0-9]"), "_"); for (int partIndex = 0; partIndex < partCount; partIndex++) { const graphics::Mesh::Part& part = partBuffer.get(partIndex); - out << "g part-" << nth++ << "\n"; + out << QString("g %1-%2-%3\n").arg(subMeshIndex, 3, 10, QChar('0')).arg(name).arg(partIndex); - // graphics::Mesh::TRIANGLES - // TODO -- handle other formats - gpu::BufferView::Iterator indexItr = indexBuffer.cbegin(); - indexItr += part._startIndex; - - int indexCount = 0; - while (indexItr != indexBuffer.cend() && indexCount < part._numIndices) { - uint32_t index0 = *indexItr; - indexItr++; - indexCount++; - if (indexItr == indexBuffer.cend() || indexCount >= part._numIndices) { - qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3"; - break; + const bool shorts = indexBuffer._element == gpu::Element::INDEX_UINT16; + auto face = [&](uint32_t i0, uint32_t i1, uint32_t i2) { + uint32_t index0, index1, index2; + if (shorts) { + index0 = indexBuffer.get(i0); + index1 = indexBuffer.get(i1); + index2 = indexBuffer.get(i2); + } else { + index0 = indexBuffer.get(i0); + index1 = indexBuffer.get(i1); + index2 = indexBuffer.get(i2); } - uint32_t index1 = *indexItr; - indexItr++; - indexCount++; - if (indexItr == indexBuffer.cend() || indexCount >= part._numIndices) { - qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3"; - break; - } - uint32_t index2 = *indexItr; - indexItr++; - indexCount++; out << "f "; if (haveNormals) { @@ -146,6 +143,39 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { out << currentVertexStartOffset + index1 + 1 << " "; out << currentVertexStartOffset + index2 + 1 << "\n"; } + }; + + // graphics::Mesh::TRIANGLES / graphics::Mesh::QUADS + // TODO -- handle other formats + uint32_t len = part._startIndex + part._numIndices; + auto stringFromTopology = [&](graphics::Mesh::Topology topo) -> QString { + return topo == graphics::Mesh::Topology::QUADS ? "QUADS" : + topo == graphics::Mesh::Topology::QUAD_STRIP ? "QUAD_STRIP" : + topo == graphics::Mesh::Topology::TRIANGLES ? "TRIANGLES" : + topo == graphics::Mesh::Topology::TRIANGLE_STRIP ? "TRIANGLE_STRIP" : + topo == graphics::Mesh::Topology::QUAD_STRIP ? "QUAD_STRIP" : + QString("topo:%1").arg((int)topo); + }; + + qCDebug(modelformat) << "OBJWriter -- part" << partIndex << "topo" << stringFromTopology(part._topology) << "index elements" << (shorts ? "uint16_t" : "uint32_t"); + if (part._topology == graphics::Mesh::TRIANGLES && len % 3 != 0) { + qCDebug(modelformat) << "OBJWriter -- index buffer length isn't a multiple of 3" << len; + } + if (part._topology == graphics::Mesh::QUADS && len % 4 != 0) { + qCDebug(modelformat) << "OBJWriter -- index buffer length isn't a multiple of 4" << len; + } + if (len > indexBuffer.getNumElements()) { + qCDebug(modelformat) << "OBJWriter -- len > index size" << len << indexBuffer.getNumElements(); + } + if (part._topology == graphics::Mesh::QUADS) { + for (uint32_t idx = part._startIndex; idx+3 < len; idx += 4) { + face(idx+0, idx+1, idx+3); + face(idx+1, idx+2, idx+3); + } + } else if (part._topology == graphics::Mesh::TRIANGLES) { + for (uint32_t idx = part._startIndex; idx+2 < len; idx += 3) { + face(idx+0, idx+1, idx+2); + } } out << "\n"; } diff --git a/libraries/graphics-scripting/CMakeLists.txt b/libraries/graphics-scripting/CMakeLists.txt new file mode 100644 index 0000000000..e7fa3de155 --- /dev/null +++ b/libraries/graphics-scripting/CMakeLists.txt @@ -0,0 +1,5 @@ +set(TARGET_NAME graphics-scripting) +setup_hifi_library() +link_hifi_libraries(shared networking graphics fbx model-networking script-engine) +include_hifi_library_headers(gpu) +include_hifi_library_headers(graphics-scripting) diff --git a/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.cpp b/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.cpp new file mode 100644 index 0000000000..e865ed0e5a --- /dev/null +++ b/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.cpp @@ -0,0 +1,195 @@ +#include "./graphics-scripting/BufferViewHelpers.h" + +#include +#include + +#include +#include +#include + +#include + +#ifdef DEBUG_BUFFERVIEW_SCRIPTING + #include "DebugNames.h" +#endif + +namespace { + const std::array XYZW = {{ "x", "y", "z", "w" }}; + const std::array ZERO123 = {{ "0", "1", "2", "3" }}; +} + +template +QVariant getBufferViewElement(const gpu::BufferView& view, quint32 index, bool asArray = false) { + return glmVecToVariant(view.get(index), asArray); +} + +template +void setBufferViewElement(const gpu::BufferView& view, quint32 index, const QVariant& v) { + 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) { + 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; +} + +bool bufferViewElementFromVariant(const gpu::BufferView& view, quint32 index, const QVariant& v) { + const auto& element = view._element; + const auto vecN = element.getScalarCount(); + const auto dataType = element.getType(); + const auto byteLength = element.getSize(); + const auto BYTES_PER_ELEMENT = byteLength / vecN; + if (BYTES_PER_ELEMENT == 1) { + switch(vecN) { + case 2: setBufferViewElement(view, index, v); return true; + case 3: setBufferViewElement(view, index, v); return true; + case 4: { + if (element == gpu::Element::COLOR_RGBA_32) { + glm::uint32 rawColor;// = glm::packUnorm4x8(glm::vec4(glmVecFromVariant(v), 0.0f)); + glm::uint32 unused; + packNormalAndTangent(glmVecFromVariant(v), glm::vec3(), rawColor, unused); + view.edit(index) = rawColor; + } else if (element == gpu::Element::VEC4F_NORMALIZED_XYZ10W2) { + glm::uint32 packedNormal;// = glm::packSnorm3x10_1x2(glm::vec4(glmVecFromVariant(v), 0.0f)); + glm::uint32 unused; + packNormalAndTangent(glm::vec3(), glmVecFromVariant(v), unused, packedNormal); + view.edit(index) = packedNormal; + } + setBufferViewElement(view, index, v); return true; + } + } + } else if (BYTES_PER_ELEMENT == 2) { + switch(vecN) { + case 2: setBufferViewElement(view, index, v); return true; + case 3: setBufferViewElement(view, index, v); return true; + case 4: setBufferViewElement(view, index, v); return true; + } + } else if (BYTES_PER_ELEMENT == 4) { + if (dataType == gpu::FLOAT) { + switch(vecN) { + case 2: setBufferViewElement(view, index, v); return true; + case 3: setBufferViewElement(view, index, v); return true; + case 4: setBufferViewElement(view, index, v); return true; + } + } else { + switch(vecN) { + case 2: setBufferViewElement(view, index, v); return true; + case 3: setBufferViewElement(view, index, v); return true; + case 4: setBufferViewElement(view, index, v); return true; + } + } + } + return false; +} + +QVariant bufferViewElementToVariant(const gpu::BufferView& view, quint32 index, bool asArray, const char* hint) { + const auto& element = view._element; + const auto vecN = element.getScalarCount(); + const auto dataType = element.getType(); + const auto byteLength = element.getSize(); + const auto BYTES_PER_ELEMENT = byteLength / vecN; + Q_ASSERT(index < view.getNumElements()); + Q_ASSERT(index * vecN * BYTES_PER_ELEMENT < (view._size - vecN * BYTES_PER_ELEMENT)); + if (BYTES_PER_ELEMENT == 1) { + switch(vecN) { + case 2: return getBufferViewElement(view, index, asArray); + case 3: return getBufferViewElement(view, index, asArray); + case 4: { + if (element == gpu::Element::COLOR_RGBA_32) { + auto rawColor = view.get(index); + return glmVecToVariant(glm::vec3(glm::unpackUnorm4x8(rawColor))); + } else if (element == gpu::Element::VEC4F_NORMALIZED_XYZ10W2) { + auto packedNormal = view.get(index); + return glmVecToVariant(glm::vec3(glm::unpackSnorm3x10_1x2(packedNormal))); + } + + return getBufferViewElement(view, index, asArray); + } + } + } else if (BYTES_PER_ELEMENT == 2) { + switch(vecN) { + case 2: return getBufferViewElement(view, index, asArray); + case 3: return getBufferViewElement(view, index, asArray); + case 4: return getBufferViewElement(view, index, asArray); + } + } else if (BYTES_PER_ELEMENT == 4) { + if (dataType == gpu::FLOAT) { + switch(vecN) { + case 2: return getBufferViewElement(view, index, asArray); + case 3: return getBufferViewElement(view, index, asArray); + case 4: return getBufferViewElement(view, index, asArray); + } + } else { + switch(vecN) { + case 2: return getBufferViewElement(view, index, asArray); + case 3: return getBufferViewElement(view, index, asArray); + case 4: return getBufferViewElement(view, index, asArray); + } + } + } + return QVariant(); +} + +template +QVariant glmVecToVariant(const T& v, bool asArray /*= false*/) { + static const auto len = T().length(); + if (asArray) { + QVariantList list; + for (int i = 0; i < len ; i++) { + list << v[i]; + } + return list; + } else { + QVariantMap obj; + for (int i = 0; i < len ; i++) { + obj[XYZW[i]] = v[i]; + } + return obj; + } +} +template +const T glmVecFromVariant(const QVariant& v) { + auto isMap = v.type() == (QVariant::Type)QMetaType::QVariantMap; + static const auto len = T().length(); + const auto& components = isMap ? XYZW : ZERO123; + T result; + QVariantMap map; + QVariantList list; + if (isMap) map = v.toMap(); else list = v.toList(); + for (int i = 0; i < len ; i++) { + float value; + if (isMap) { + value = map.value(components[i]).toFloat(); + } else { + value = list.value(i).toFloat(); + } + if (value != value) { // NAN + qWarning().nospace()<< "vec" << len << "." << components[i] << " NAN received from script.... " << v.toString(); + } + result[i] = value; + } + return result; +} + diff --git a/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.h b/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.h new file mode 100644 index 0000000000..0fe2602f6c --- /dev/null +++ b/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.h @@ -0,0 +1,18 @@ +// +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#pragma once + +#include + +namespace gpu { class BufferView; } + +template QVariant glmVecToVariant(const T& v, bool asArray = false); +template const T glmVecFromVariant(const QVariant& v); +QVariant bufferViewElementToVariant(const gpu::BufferView& view, quint32 index, bool asArray = false, const char* hint = ""); +bool bufferViewElementFromVariant(const gpu::BufferView& view, quint32 index, const QVariant& v); + + diff --git a/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp b/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp new file mode 100644 index 0000000000..367c0589e9 --- /dev/null +++ b/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp @@ -0,0 +1,83 @@ +#include "BufferViewScripting.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#ifdef DEBUG_BUFFERVIEW_SCRIPTING + #include +#endif + +namespace { + const std::array XYZW = {{ "x", "y", "z", "w" }}; + const std::array ZERO123 = {{ "0", "1", "2", "3" }}; +} + +template +QScriptValue getBufferViewElement(QScriptEngine* js, const gpu::BufferView& view, quint32 index, bool asArray = false) { + return glmVecToScriptValue(js, view.get(index), asArray); +} + +QScriptValue bufferViewElementToScriptValue(QScriptEngine* engine, const gpu::BufferView& view, quint32 index, bool asArray, const char* hint) { + QVariant result = bufferViewElementToVariant(view, index, asArray, hint); + if (!result.isValid()) { + return QScriptValue::NullValue; + } + return engine->toScriptValue(result); +} + +template +void setBufferViewElement(const gpu::BufferView& view, quint32 index, const QScriptValue& v) { + view.edit(index) = glmVecFromScriptValue(v); +} + +bool bufferViewElementFromScriptValue(const QScriptValue& v, const gpu::BufferView& view, quint32 index) { + return bufferViewElementFromVariant(view, index, v.toVariant()); +} + +// + +template +QScriptValue glmVecToScriptValue(QScriptEngine *js, const T& v, bool asArray) { + static const auto len = T().length(); + const auto& components = asArray ? ZERO123 : XYZW; + auto obj = asArray ? js->newArray() : js->newObject(); + for (int i = 0; i < len ; i++) { + const auto key = components[i]; + const auto value = v[i]; + if (value != value) { // NAN +#ifdef DEV_BUILD + qWarning().nospace()<< "vec" << len << "." << key << " converting NAN to javascript NaN.... " << value; +#endif + obj.setProperty(key, js->globalObject().property("NaN")); + } else { + obj.setProperty(key, value); + } + } + return obj; +} + +template +const T glmVecFromScriptValue(const QScriptValue& v) { + static const auto len = T().length(); + const auto& components = v.property("x").isValid() ? XYZW : ZERO123; + T result; + for (int i = 0; i < len ; i++) { + const auto key = components[i]; + const auto value = v.property(key).toNumber(); +#ifdef DEV_BUILD + if (value != value) { // NAN + qWarning().nospace()<< "vec" << len << "." << key << " NAN received from script.... " << v.toVariant().toString(); + } +#endif + result[i] = value; + } + return result; +} diff --git a/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.h b/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.h new file mode 100644 index 0000000000..f2e3fe734e --- /dev/null +++ b/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace gpu { class BufferView; } +class QScriptValue; +class QScriptEngine; +template QScriptValue glmVecToScriptValue(QScriptEngine *js, const T& v, bool asArray = false); +template const T glmVecFromScriptValue(const QScriptValue& v); +QScriptValue bufferViewElementToScriptValue(QScriptEngine* engine, const gpu::BufferView& view, quint32 index, bool asArray = false, const char* hint = ""); +bool bufferViewElementFromScriptValue(const QScriptValue& v, const gpu::BufferView& view, quint32 index); diff --git a/libraries/graphics-scripting/src/graphics-scripting/DebugNames.h b/libraries/graphics-scripting/src/graphics-scripting/DebugNames.h new file mode 100644 index 0000000000..e5edf1c9d8 --- /dev/null +++ b/libraries/graphics-scripting/src/graphics-scripting/DebugNames.h @@ -0,0 +1,72 @@ +#pragma once +#include +#include +#include +#include +#include +//#include + +Q_DECLARE_METATYPE(gpu::Type); +#ifdef QT_MOC_RUN +class DebugNames { + Q_OBJECT +public: +#else + namespace DebugNames { + Q_NAMESPACE + #endif + +enum Type : uint8_t { + + FLOAT = 0, + INT32, + UINT32, + HALF, + INT16, + UINT16, + INT8, + UINT8, + + NINT32, + NUINT32, + NINT16, + NUINT16, + NINT8, + NUINT8, + + COMPRESSED, + + NUM_TYPES, + + BOOL = UINT8, + NORMALIZED_START = NINT32, +}; + + Q_ENUM_NS(Type) + enum InputSlot { + POSITION = 0, + NORMAL = 1, + COLOR = 2, + TEXCOORD0 = 3, + TEXCOORD = TEXCOORD0, + TANGENT = 4, + SKIN_CLUSTER_INDEX = 5, + SKIN_CLUSTER_WEIGHT = 6, + TEXCOORD1 = 7, + TEXCOORD2 = 8, + TEXCOORD3 = 9, + TEXCOORD4 = 10, + + NUM_INPUT_SLOTS, + + DRAW_CALL_INFO = 15, // Reserve last input slot for draw call infos + }; + + Q_ENUM_NS(InputSlot) + inline QString stringFrom(Type t) { return QVariant::fromValue(t).toString(); } + inline QString stringFrom(InputSlot t) { return QVariant::fromValue(t).toString(); } + inline QString stringFrom(gpu::Type t) { return stringFrom((Type)t); } + inline QString stringFrom(gpu::Stream::Slot t) { return stringFrom((InputSlot)t); } + + extern const QMetaObject staticMetaObject; + }; diff --git a/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp new file mode 100644 index 0000000000..68a00bc02c --- /dev/null +++ b/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp @@ -0,0 +1,836 @@ +// +// 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 "OBJReader.h" +//#include "ui/overlays/Base3DOverlay.h" +//#include "EntityTreeRenderer.h" +//#include "avatar/AvatarManager.h" +//#include "RenderableEntityItem.h" + +#include +#include + +#include + + +#include + +#include +#include "BufferViewScripting.h" + +#include "ScriptableMesh.h" + +using ScriptableMesh = scriptable::ScriptableMesh; + +#include "ModelScriptingInterface.moc" + +namespace { + QLoggingCategory model_scripting { "hifi.model.scripting" }; +} + +ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(parent) { + if (auto scriptEngine = qobject_cast(parent)) { + this->registerMetaTypes(scriptEngine); + } +} + +QString ModelScriptingInterface::meshToOBJ(const scriptable::ScriptableModel& _in) { + const auto& in = _in.getMeshes(); + qCDebug(model_scripting) << "meshToOBJ" << in.size(); + if (in.size()) { + QList meshes; + foreach (const auto meshProxy, in) { + qCDebug(model_scripting) << "meshToOBJ" << meshProxy.get(); + 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 (const scriptable::ScriptableMeshPointer 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 scriptable::ScriptableMeshPointer 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)); + + + scriptable::ScriptableMeshPointer resultProxy = scriptable::ScriptableMeshPointer(new ScriptableMesh(nullptr, result)); + return engine()->toScriptValue(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; }); + scriptable::ScriptableMeshPointer resultProxy = scriptable::ScriptableMeshPointer(new ScriptableMesh(nullptr, result)); + return engine()->toScriptValue(resultProxy); +} + +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, mesh::uint32 vertexIndex) { + auto mesh = getMeshPointer(meshProxy); + + 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(model_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)); + + + + scriptable::ScriptableMeshPointer meshProxy = scriptable::ScriptableMeshPointer(new ScriptableMesh(nullptr, mesh)); + return engine()->toScriptValue(meshProxy); +} + +QScriptValue ModelScriptingInterface::mapAttributeValues( + QScriptValue _in, + QScriptValue scopeOrCallback, + QScriptValue methodOrName + ) { + qCInfo(model_scripting) << "mapAttributeValues" << _in.toVariant().typeName() << _in.toVariant().toString() << _in.toQObject(); + auto in = qscriptvalue_cast(_in).getMeshes(); + if (in.size()) { + foreach (scriptable::ScriptableMeshPointer meshProxy, in) { + mapMeshAttributeValues(meshProxy, scopeOrCallback, methodOrName); + } + return thisObject(); + } else if (auto meshProxy = qobject_cast(_in.toQObject())) { + return mapMeshAttributeValues(meshProxy->shared_from_this(), scopeOrCallback, methodOrName); + } else { + context()->throwError("invalid ModelProxy || MeshProxyPointer"); + } + return false; +} + + +QScriptValue ModelScriptingInterface::unrollVertices(scriptable::ScriptableMeshPointer meshProxy, bool recalcNormals) { + auto mesh = getMeshPointer(meshProxy); + qCInfo(model_scripting) << "ModelScriptingInterface::unrollVertices" << !!mesh<< !!meshProxy; + if (!mesh) { + return QScriptValue(); + } + + auto positions = mesh->getVertexBuffer(); + auto indices = mesh->getIndexBuffer(); + quint32 numPoints = (quint32)indices.getNumElements(); + auto buffer = new gpu::Buffer(); + buffer->resize(numPoints * sizeof(uint32_t)); + auto newindices = gpu::BufferView(buffer, { gpu::SCALAR, gpu::UINT32, gpu::INDEX }); + qCInfo(model_scripting) << "ModelScriptingInterface::unrollVertices numPoints" << numPoints; + auto attributeViews = ScriptableMesh::gatherBufferViews(mesh); + for (const auto& a : attributeViews) { + auto& view = a.second; + auto sz = view._element.getSize(); + auto buffer = new gpu::Buffer(); + buffer->resize(numPoints * sz); + auto points = gpu::BufferView(buffer, view._element); + auto src = (uint8_t*)view._buffer->getData(); + auto dest = (uint8_t*)points._buffer->getData(); + auto slot = ScriptableMesh::ATTRIBUTES[a.first]; + qCInfo(model_scripting) << "ModelScriptingInterface::unrollVertices buffer" << a.first; + qCInfo(model_scripting) << "ModelScriptingInterface::unrollVertices source" << view.getNumElements(); + qCInfo(model_scripting) << "ModelScriptingInterface::unrollVertices dest" << points.getNumElements(); + qCInfo(model_scripting) << "ModelScriptingInterface::unrollVertices sz" << sz << src << dest << slot; + auto esize = indices._element.getSize(); + const char* hint= a.first.toStdString().c_str(); + for(quint32 i = 0; i < numPoints; i++) { + quint32 index = esize == 4 ? indices.get(i) : indices.get(i); + newindices.edit(i) = i; + bufferViewElementFromVariant( + points, i, + bufferViewElementToVariant(view, index, false, hint) + ); + } + if (slot == gpu::Stream::POSITION) { + mesh->setVertexBuffer(points); + } else { + mesh->addAttribute(slot, points); + } + } + mesh->setIndexBuffer(newindices); + if (recalcNormals) { + recalculateNormals(meshProxy); + } + return true; +} + +namespace { + template + gpu::BufferView bufferViewFromVector(QVector elements, gpu::Element elementType) { + auto vertexBuffer = std::make_shared( + elements.size() * sizeof(T), + (gpu::Byte*)elements.data() + ); + return { vertexBuffer, 0, vertexBuffer->getSize(),sizeof(T), elementType }; + } + + gpu::BufferView cloneBufferView(const gpu::BufferView& input) { + //qCInfo(model_scripting) << "input" << input.getNumElements() << input._buffer->getSize(); + auto output = gpu::BufferView( + std::make_shared(input._buffer->getSize(), input._buffer->getData()), + input._offset, + input._size, + input._stride, + input._element + ); + //qCInfo(model_scripting) << "after" << output.getNumElements() << output._buffer->getSize(); + return output; + } + + gpu::BufferView resizedBufferView(const gpu::BufferView& input, quint32 numElements) { + auto effectiveSize = input._buffer->getSize() / input.getNumElements(); + qCInfo(model_scripting) << "resize input" << input.getNumElements() << input._buffer->getSize() << "effectiveSize" << effectiveSize; + auto vsize = input._element.getSize() * numElements; + gpu::Byte *data = new gpu::Byte[vsize]; + memset(data, 0, vsize); + auto buffer = new gpu::Buffer(vsize, (gpu::Byte*)data); + delete[] data; + auto output = gpu::BufferView(buffer, input._element); + qCInfo(model_scripting) << "resized output" << output.getNumElements() << output._buffer->getSize(); + return output; + } +} + +bool ModelScriptingInterface::replaceMeshData(scriptable::ScriptableMeshPointer dest, scriptable::ScriptableMeshPointer src, const QVector& attributeNames) { + auto target = getMeshPointer(dest); + auto source = getMeshPointer(src); + if (!target || !source) { + context()->throwError("ModelScriptingInterface::replaceMeshData -- expected dest and src to be valid mesh proxy pointers"); + return false; + } + + QVector attributes = attributeNames.isEmpty() ? src->getAttributeNames() : attributeNames; + + //qCInfo(model_scripting) << "ModelScriptingInterface::replaceMeshData -- source:" << source->displayName << "target:" << target->displayName << "attributes:" << attributes; + + // remove attributes only found on target mesh, unless user has explicitly specified the relevant attribute names + if (attributeNames.isEmpty()) { + auto attributeViews = ScriptableMesh::gatherBufferViews(target); + for (const auto& a : attributeViews) { + auto slot = ScriptableMesh::ATTRIBUTES[a.first]; + if (!attributes.contains(a.first)) { + //qCInfo(model_scripting) << "ModelScriptingInterface::replaceMeshData -- pruning target attribute" << a.first << slot; + target->removeAttribute(slot); + } + } + } + + target->setVertexBuffer(cloneBufferView(source->getVertexBuffer())); + target->setIndexBuffer(cloneBufferView(source->getIndexBuffer())); + target->setPartBuffer(cloneBufferView(source->getPartBuffer())); + + for (const auto& a : attributes) { + auto slot = ScriptableMesh::ATTRIBUTES[a]; + if (slot == gpu::Stream::POSITION) { + continue; + } + // auto& before = target->getAttributeBuffer(slot); + auto& input = source->getAttributeBuffer(slot); + if (input.getNumElements() == 0) { + //qCInfo(model_scripting) << "ModelScriptingInterface::replaceMeshData buffer is empty -- pruning" << a << slot; + target->removeAttribute(slot); + } else { + // if (before.getNumElements() == 0) { + // qCInfo(model_scripting) << "ModelScriptingInterface::replaceMeshData target buffer is empty -- adding" << a << slot; + // } else { + // qCInfo(model_scripting) << "ModelScriptingInterface::replaceMeshData target buffer exists -- updating" << a << slot; + // } + target->addAttribute(slot, cloneBufferView(input)); + } + // auto& after = target->getAttributeBuffer(slot); + // qCInfo(model_scripting) << "ModelScriptingInterface::replaceMeshData" << a << slot << before.getNumElements() << " -> " << after.getNumElements(); + } + + + return true; +} + +bool ModelScriptingInterface::dedupeVertices(scriptable::ScriptableMeshPointer meshProxy, float epsilon) { + auto mesh = getMeshPointer(meshProxy); + if (!mesh) { + return false; + } + auto positions = mesh->getVertexBuffer(); + auto numPositions = positions.getNumElements(); + const auto epsilon2 = epsilon*epsilon; + + QVector uniqueVerts; + uniqueVerts.reserve((int)numPositions); + QMap remapIndices; + + for (quint32 i = 0; i < numPositions; i++) { + const quint32 numUnique = uniqueVerts.size(); + const auto& position = positions.get(i); + bool unique = true; + for (quint32 j = 0; j < numUnique; j++) { + if (glm::length2(uniqueVerts[j] - position) <= epsilon2) { + remapIndices[i] = j; + unique = false; + break; + } + } + if (unique) { + uniqueVerts << position; + remapIndices[i] = numUnique; + } + } + + qCInfo(model_scripting) << "//VERTS before" << numPositions << "after" << uniqueVerts.size(); + + auto indices = mesh->getIndexBuffer(); + auto numIndices = indices.getNumElements(); + auto esize = indices._element.getSize(); + QVector newIndices; + newIndices.reserve((int)numIndices); + for (quint32 i = 0; i < numIndices; i++) { + quint32 index = esize == 4 ? indices.get(i) : indices.get(i); + if (remapIndices.contains(index)) { + //qCInfo(model_scripting) << i << index << "->" << remapIndices[index]; + newIndices << remapIndices[index]; + } else { + qCInfo(model_scripting) << i << index << "!remapIndices[index]"; + } + } + + mesh->setIndexBuffer(bufferViewFromVector(newIndices, { gpu::SCALAR, gpu::UINT32, gpu::INDEX })); + mesh->setVertexBuffer(bufferViewFromVector(uniqueVerts, { gpu::VEC3, gpu::FLOAT, gpu::XYZ })); + + auto attributeViews = ScriptableMesh::gatherBufferViews(mesh); + quint32 numUniqueVerts = uniqueVerts.size(); + for (const auto& a : attributeViews) { + auto& view = a.second; + auto slot = ScriptableMesh::ATTRIBUTES[a.first]; + if (slot == gpu::Stream::POSITION) { + continue; + } + qCInfo(model_scripting) << "ModelScriptingInterface::dedupeVertices" << a.first << slot << view.getNumElements(); + auto newView = resizedBufferView(view, numUniqueVerts); + qCInfo(model_scripting) << a.first << "before: #" << view.getNumElements() << "after: #" << newView.getNumElements(); + quint32 numElements = (quint32)view.getNumElements(); + for (quint32 i = 0; i < numElements; i++) { + quint32 fromVertexIndex = i; + quint32 toVertexIndex = remapIndices.contains(fromVertexIndex) ? remapIndices[fromVertexIndex] : fromVertexIndex; + bufferViewElementFromVariant( + newView, toVertexIndex, + bufferViewElementToVariant(view, fromVertexIndex, false, "dedupe") + ); + } + mesh->addAttribute(slot, newView); + } + return true; +} + +QScriptValue ModelScriptingInterface::cloneMesh(scriptable::ScriptableMeshPointer meshProxy, bool recalcNormals) { + auto mesh = getMeshPointer(meshProxy); + if (!mesh) { + return QScriptValue::NullValue; + } + graphics::MeshPointer clone(new graphics::Mesh()); + clone->displayName = mesh->displayName + "-clone"; + qCInfo(model_scripting) << "ModelScriptingInterface::cloneMesh" << !!mesh<< !!meshProxy; + if (!mesh) { + return QScriptValue::NullValue; + } + + clone->setIndexBuffer(cloneBufferView(mesh->getIndexBuffer())); + clone->setPartBuffer(cloneBufferView(mesh->getPartBuffer())); + auto attributeViews = ScriptableMesh::gatherBufferViews(mesh); + for (const auto& a : attributeViews) { + auto& view = a.second; + auto slot = ScriptableMesh::ATTRIBUTES[a.first]; + qCInfo(model_scripting) << "ModelScriptingInterface::cloneVertices buffer" << a.first << slot; + auto points = cloneBufferView(view); + qCInfo(model_scripting) << "ModelScriptingInterface::cloneVertices source" << view.getNumElements(); + qCInfo(model_scripting) << "ModelScriptingInterface::cloneVertices dest" << points.getNumElements(); + if (slot == gpu::Stream::POSITION) { + clone->setVertexBuffer(points); + } else { + clone->addAttribute(slot, points); + } + } + + auto result = scriptable::ScriptableMeshPointer(new ScriptableMesh(nullptr, clone)); + if (recalcNormals) { + recalculateNormals(result); + } + return engine()->toScriptValue(result); +} + +bool ModelScriptingInterface::recalculateNormals(scriptable::ScriptableMeshPointer meshProxy) { + qCInfo(model_scripting) << "Recalculating normals" << !!meshProxy; + auto mesh = getMeshPointer(meshProxy); + if (!mesh) { + return false; + } + ScriptableMesh::gatherBufferViews(mesh, { "normal", "color" }); // ensures #normals >= #positions + auto normals = mesh->getAttributeBuffer(gpu::Stream::NORMAL); + auto verts = mesh->getVertexBuffer(); + auto indices = mesh->getIndexBuffer(); + auto esize = indices._element.getSize(); + auto numPoints = indices.getNumElements(); + const auto TRIANGLE = 3; + quint32 numFaces = (quint32)numPoints / TRIANGLE; + //QVector faces; + QVector faceNormals; + QMap> vertexToFaces; + //faces.resize(numFaces); + faceNormals.resize(numFaces); + auto numNormals = normals.getNumElements(); + qCInfo(model_scripting) << QString("numFaces: %1, numNormals: %2, numPoints: %3").arg(numFaces).arg(numNormals).arg(numPoints); + if (normals.getNumElements() != verts.getNumElements()) { + return false; + } + for (quint32 i = 0; i < numFaces; i++) { + quint32 I = TRIANGLE * i; + quint32 i0 = esize == 4 ? indices.get(I+0) : indices.get(I+0); + quint32 i1 = esize == 4 ? indices.get(I+1) : indices.get(I+1); + quint32 i2 = esize == 4 ? indices.get(I+2) : indices.get(I+2); + + Triangle face = { + verts.get(i1), + verts.get(i2), + verts.get(i0) + }; + faceNormals[i] = face.getNormal(); + if (glm::isnan(faceNormals[i].x)) { + qCInfo(model_scripting) << i << i0 << i1 << i2 << vec3toVariant(face.v0) << vec3toVariant(face.v1) << vec3toVariant(face.v2); + break; + } + vertexToFaces[glm::to_string(face.v0).c_str()] << i; + vertexToFaces[glm::to_string(face.v1).c_str()] << i; + vertexToFaces[glm::to_string(face.v2).c_str()] << i; + } + for (quint32 j = 0; j < numNormals; j++) { + //auto v = verts.get(j); + glm::vec3 normal { 0.0f, 0.0f, 0.0f }; + QString key { glm::to_string(verts.get(j)).c_str() }; + const auto& faces = vertexToFaces.value(key); + if (faces.size()) { + for (const auto i : faces) { + normal += faceNormals[i]; + } + normal *= 1.0f / (float)faces.size(); + } else { + static int logged = 0; + if (logged++ < 10) { + qCInfo(model_scripting) << "no faces for key!?" << key; + } + normal = verts.get(j); + } + if (glm::isnan(normal.x)) { + static int logged = 0; + if (logged++ < 10) { + qCInfo(model_scripting) << "isnan(normal.x)" << j << vec3toVariant(normal); + } + break; + } + normals.edit(j) = glm::normalize(normal); + } + return true; +} + +QScriptValue ModelScriptingInterface::mapMeshAttributeValues( + scriptable::ScriptableMeshPointer meshProxy, QScriptValue scopeOrCallback, QScriptValue methodOrName +) { + auto mesh = getMeshPointer(meshProxy); + if (!mesh) { + return false; + } + auto scopedHandler = makeScopedHandlerObject(scopeOrCallback, methodOrName); + + // input buffers + gpu::BufferView positions = mesh->getVertexBuffer(); + + const auto nPositions = positions.getNumElements(); + + // destructure so we can still invoke callback scoped, but with a custom signature (obj, i, jsMesh) + auto scope = scopedHandler.property("scope"); + auto callback = scopedHandler.property("callback"); + auto js = engine(); // cache value to avoid resolving each iteration + auto meshPart = js->toScriptValue(meshProxy); + + auto obj = js->newObject(); + auto attributeViews = ScriptableMesh::gatherBufferViews(mesh, { "normal", "color" }); + for (uint32_t i=0; i < nPositions; i++) { + for (const auto& a : attributeViews) { + bool asArray = a.second._element.getType() != gpu::FLOAT; + obj.setProperty(a.first, bufferViewElementToScriptValue(js, a.second, i, asArray, a.first.toStdString().c_str())); + } + auto result = callback.call(scope, { obj, i, meshPart }); + if (js->hasUncaughtException()) { + context()->throwValue(js->uncaughtException()); + return false; + } + + if (result.isBool() && !result.toBool()) { + // bail without modifying data if user explicitly returns false + continue; + } + if (result.isObject() && !result.strictlyEquals(obj)) { + // user returned a new object (ie: instead of modifying input properties) + obj = result; + } + + for (const auto& a : attributeViews) { + const auto& attribute = obj.property(a.first); + auto& view = a.second; + if (attribute.isValid()) { + bufferViewElementFromScriptValue(attribute, view, i); + } + } + } + return thisObject(); +} + +void ModelScriptingInterface::getMeshes(QUuid uuid, QScriptValue scopeOrCallback, QScriptValue methodOrName) { + auto handler = makeScopedHandlerObject(scopeOrCallback, methodOrName); + Q_ASSERT(handler.engine() == this->engine()); + QPointer engine = dynamic_cast(handler.engine()); + + scriptable::ScriptableModel meshes; + bool success = false; + QString error; + + auto appProvider = DependencyManager::get(); + qDebug() << "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(model_scripting) << "fetching meshes from " << providerType << "..."; + auto scriptableMeshes = provider->getScriptableModel(&success); + qCDebug(model_scripting) << "//fetched meshes from " << providerType << "success:" <makeError(error), QScriptValue::NullValue); + } else { + callScopedHandlerObject(handler, QScriptValue::NullValue, engine->toScriptValue(meshes)); + } +} + +namespace { + QScriptValue meshToScriptValue(QScriptEngine* engine, scriptable::ScriptableMeshPointer const &in) { + return engine->newQObject(in.get(), QScriptEngine::QtOwnership, + QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects + ); + } + + void meshFromScriptValue(const QScriptValue& value, scriptable::ScriptableMeshPointer &out) { + auto obj = value.toQObject(); + //qDebug() << "meshFromScriptValue" << obj; + if (auto tmp = qobject_cast(obj)) { + out = tmp->shared_from_this(); + } + // FIXME: Why does above cast not work on Win32!? + if (!out) { + auto smp = static_cast(obj); + //qDebug() << "meshFromScriptValue2" << smp; + out = smp->shared_from_this(); + } + } + + QScriptValue meshesToScriptValue(QScriptEngine* engine, const scriptable::ScriptableModelPointer &in) { + // QScriptValueList result; + QScriptValue result = engine->newArray(); + int i = 0; + foreach(scriptable::ScriptableMeshPointer const meshProxy, in->getMeshes()) { + result.setProperty(i++, meshToScriptValue(engine, meshProxy)); + } + return result; + } + + void meshesFromScriptValue(const QScriptValue& value, scriptable::ScriptableModelPointer &out) { + const auto length = value.property("length").toInt32(); + qCDebug(model_scripting) << "in meshesFromScriptValue, length =" << length; + for (int i = 0; i < length; i++) { + if (const auto meshProxy = qobject_cast(value.property(i).toQObject())) { + out->meshes.append(meshProxy->getMeshPointer()); + } else { + qCDebug(model_scripting) << "null meshProxy" << i; + } + } + } + + void modelProxyFromScriptValue(const QScriptValue& object, scriptable::ScriptableModel &meshes) { + auto meshesProperty = object.property("meshes"); + if (meshesProperty.property("length").toInt32() > 0) { + //meshes._meshes = qobject_cast(meshesProperty.toQObject()); + // qDebug() << "modelProxyFromScriptValue" << meshesProperty.property("length").toInt32() << meshesProperty.toVariant().typeName(); + qScriptValueToSequence(meshesProperty, meshes.meshes); + } else if (auto mesh = qobject_cast(object.toQObject())) { + meshes.meshes << mesh->getMeshPointer(); + } else { + qDebug() << "modelProxyFromScriptValue -- unrecognized input" << object.toVariant().toString(); + } + + meshes.metadata = object.property("metadata").toVariant().toMap(); + } + + QScriptValue modelProxyToScriptValue(QScriptEngine* engine, const scriptable::ScriptableModel &in) { + QScriptValue obj = engine->newObject(); + obj.setProperty("meshes", qScriptValueFromSequence(engine, in.meshes)); + obj.setProperty("metadata", engine->toScriptValue(in.metadata)); + return obj; + } + + 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); + } + + QScriptValue qVectorUInt32ToScriptValue(QScriptEngine* engine, const QVector& vector) { + return qScriptValueFromSequence(engine, vector); + } + + void qVectorUInt32FromScriptValue(const QScriptValue& array, QVector& result) { + qScriptValueToSequence(array, result); + } +} + +int meshUint32 = qRegisterMetaType(); +namespace mesh { + int meshUint32 = qRegisterMetaType(); +} +int qVectorMeshUint32 = qRegisterMetaType>(); + +void ModelScriptingInterface::registerMetaTypes(QScriptEngine* engine) { + qScriptRegisterSequenceMetaType>(engine); + qScriptRegisterSequenceMetaType(engine); + qScriptRegisterSequenceMetaType>(engine); + qScriptRegisterMetaType(engine, modelProxyToScriptValue, modelProxyFromScriptValue); + + qScriptRegisterMetaType(engine, qVectorUInt32ToScriptValue, qVectorUInt32FromScriptValue); + qScriptRegisterMetaType(engine, meshToScriptValue, meshFromScriptValue); + qScriptRegisterMetaType(engine, meshesToScriptValue, meshesFromScriptValue); + qScriptRegisterMetaType(engine, meshFaceToScriptValue, meshFaceFromScriptValue); + qScriptRegisterMetaType(engine, qVectorMeshFaceToScriptValue, qVectorMeshFaceFromScriptValue); +} + +MeshPointer ModelScriptingInterface::getMeshPointer(scriptable::ScriptableMeshPointer meshProxy) { + MeshPointer result; + if (!meshProxy) { + if (context()){ + context()->throwError("expected meshProxy as first parameter"); + } + return result; + } + auto mesh = meshProxy->getMeshPointer(); + if (!mesh) { + if (context()) { + context()->throwError("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/ModelScriptingInterface.h new file mode 100644 index 0000000000..d10fd28170 --- /dev/null +++ b/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.h @@ -0,0 +1,68 @@ +// +// 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 + +#include + +#include +#include + +#include "ScriptableMesh.h" +#include +class ModelScriptingInterface : public QObject, public QScriptable, public Dependency { + Q_OBJECT + +public: + ModelScriptingInterface(QObject* parent = nullptr); + static void registerMetaTypes(QScriptEngine* engine); + +public slots: + /**jsdoc + * Returns the meshes associated with a UUID (entityID, overlayID, or avatarID) + * + * @function ModelScriptingInterface.getMeshes + * @param {EntityID} entityID The ID of the entity whose meshes are to be retrieve + */ + void getMeshes(QUuid uuid, QScriptValue scopeOrCallback, QScriptValue methodOrName = QScriptValue()); + + bool dedupeVertices(scriptable::ScriptableMeshPointer meshProxy, float epsilon = 1e-6); + bool recalculateNormals(scriptable::ScriptableMeshPointer meshProxy); + QScriptValue cloneMesh(scriptable::ScriptableMeshPointer meshProxy, bool recalcNormals = true); + QScriptValue unrollVertices(scriptable::ScriptableMeshPointer meshProxy, bool recalcNormals = true); + QScriptValue mapAttributeValues(QScriptValue in, + QScriptValue scopeOrCallback, + QScriptValue methodOrName = QScriptValue()); + QScriptValue mapMeshAttributeValues(scriptable::ScriptableMeshPointer meshProxy, + QScriptValue scopeOrCallback, + QScriptValue methodOrName = QScriptValue()); + + QString meshToOBJ(const scriptable::ScriptableModel& in); + + bool replaceMeshData(scriptable::ScriptableMeshPointer dest, scriptable::ScriptableMeshPointer source, const QVector& attributeNames = QVector()); + 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, mesh::uint32 vertexIndex); + +private: + scriptable::MeshPointer getMeshPointer(scriptable::ScriptableMeshPointer meshProxy); + +}; + +#endif // hifi_ModelScriptingInterface_h diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp new file mode 100644 index 0000000000..47d91e9e59 --- /dev/null +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp @@ -0,0 +1,359 @@ +// +// 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 "ScriptableMesh.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "ScriptableMesh.moc" + +#include + +QLoggingCategory mesh_logging { "hifi.scripting.mesh" }; + +// FIXME: unroll/resolve before PR +using namespace scriptable; +QMap ScriptableMesh::ATTRIBUTES{ + {"position", gpu::Stream::POSITION }, + {"normal", gpu::Stream::NORMAL }, + {"color", gpu::Stream::COLOR }, + {"tangent", gpu::Stream::TEXCOORD0 }, + {"skin_cluster_index", gpu::Stream::SKIN_CLUSTER_INDEX }, + {"skin_cluster_weight", gpu::Stream::SKIN_CLUSTER_WEIGHT }, + {"texcoord0", gpu::Stream::TEXCOORD0 }, + {"texcoord1", gpu::Stream::TEXCOORD1 }, + {"texcoord2", gpu::Stream::TEXCOORD2 }, + {"texcoord3", gpu::Stream::TEXCOORD3 }, + {"texcoord4", gpu::Stream::TEXCOORD4 }, +}; + +QVector scriptable::ScriptableModel::getMeshes() const { + QVector out; + for(auto& mesh : meshes) { + out << scriptable::ScriptableMeshPointer(new ScriptableMesh(std::const_pointer_cast(this->shared_from_this()), mesh)); + } + return out; +} + +quint32 ScriptableMesh::getNumVertices() const { + if (auto mesh = getMeshPointer()) { + return (quint32)mesh->getNumVertices(); + } + return 0; +} + +// glm::vec3 ScriptableMesh::getPos3(quint32 index) const { +// if (auto mesh = getMeshPointer()) { +// if (index < getNumVertices()) { +// return mesh->getPos3(index); +// } +// } +// return glm::vec3(NAN); +// } + +namespace { + gpu::BufferView getBufferView(scriptable::MeshPointer mesh, gpu::Stream::Slot slot) { + return slot == gpu::Stream::POSITION ? mesh->getVertexBuffer() : mesh->getAttributeBuffer(slot); + } +} + +QVector ScriptableMesh::findNearbyIndices(const glm::vec3& origin, float epsilon) const { + QVector result; + if (auto mesh = getMeshPointer()) { + const auto& pos = getBufferView(mesh, gpu::Stream::POSITION); + const uint32_t num = (uint32_t)pos.getNumElements(); + for (uint32_t i = 0; i < num; i++) { + const auto& position = pos.get(i); + if (glm::distance(position, origin) <= epsilon) { + result << i; + } + } + } + return result; +} + +QVector ScriptableMesh::getIndices() const { + QVector result; + if (auto mesh = getMeshPointer()) { + qCDebug(mesh_logging, "getTriangleIndices mesh %p", mesh.get()); + gpu::BufferView indexBufferView = mesh->getIndexBuffer(); + if (quint32 count = (quint32)indexBufferView.getNumElements()) { + result.resize(count); + auto buffer = indexBufferView._buffer; + if (indexBufferView._element.getSize() == 4) { + // memcpy(result.data(), buffer->getData(), result.size()*sizeof(quint32)); + for (quint32 i = 0; i < count; i++) { + result[i] = indexBufferView.get(i); + } + } else { + for (quint32 i = 0; i < count; i++) { + result[i] = indexBufferView.get(i); + } + } + } + } + return result; +} + +quint32 ScriptableMesh::getNumAttributes() const { + if (auto mesh = getMeshPointer()) { + return (quint32)mesh->getNumAttributes(); + } + return 0; +} +QVector ScriptableMesh::getAttributeNames() const { + QVector result; + if (auto mesh = getMeshPointer()) { + for (const auto& a : ATTRIBUTES.toStdMap()) { + auto bufferView = getBufferView(mesh, a.second); + if (bufferView.getNumElements() > 0) { + result << a.first; + } + } + } + return result; +} + +// override +QVariantMap ScriptableMesh::getVertexAttributes(quint32 vertexIndex) const { + return getVertexAttributes(vertexIndex, getAttributeNames()); +} + +bool ScriptableMesh::setVertexAttributes(quint32 vertexIndex, QVariantMap attributes) { + qDebug() << "setVertexAttributes" << vertexIndex << attributes; + for (auto& a : gatherBufferViews(getMeshPointer())) { + const auto& name = a.first; + const auto& value = attributes.value(name); + if (value.isValid()) { + auto& view = a.second; + bufferViewElementFromVariant(view, vertexIndex, value); + } else { + qCDebug(mesh_logging) << "setVertexAttributes" << vertexIndex << name; + } + } + return true; +} + +int ScriptableMesh::_getSlotNumber(const QString& attributeName) const { + if (auto mesh = getMeshPointer()) { + return ATTRIBUTES.value(attributeName, -1); + } + return -1; +} + + +QVariantMap ScriptableMesh::getMeshExtents() const { + auto mesh = getMeshPointer(); + auto box = mesh ? mesh->evalPartsBound(0, (int)mesh->getNumParts()) : AABox(); + return { + { "brn", glmVecToVariant(box.getCorner()) }, + { "tfl", glmVecToVariant(box.calcTopFarLeft()) }, + { "center", glmVecToVariant(box.calcCenter()) }, + { "min", glmVecToVariant(box.getMinimumPoint()) }, + { "max", glmVecToVariant(box.getMaximumPoint()) }, + { "dimensions", glmVecToVariant(box.getDimensions()) }, + }; +} + +quint32 ScriptableMesh::getNumParts() const { + if (auto mesh = getMeshPointer()) { + return (quint32)mesh->getNumParts(); + } + return 0; +} + +QVariantMap ScriptableMesh::scaleToFit(float unitScale) { + if (auto mesh = getMeshPointer()) { + auto box = mesh->evalPartsBound(0, (int)mesh->getNumParts()); + auto center = box.calcCenter(); + float maxDimension = glm::distance(box.getMaximumPoint(), box.getMinimumPoint()); + return scale(glm::vec3(unitScale / maxDimension), center); + } + return {}; +} +QVariantMap ScriptableMesh::translate(const glm::vec3& translation) { + return transform(glm::translate(translation)); +} +QVariantMap ScriptableMesh::scale(const glm::vec3& scale, const glm::vec3& origin) { + if (auto mesh = getMeshPointer()) { + auto box = mesh->evalPartsBound(0, (int)mesh->getNumParts()); + glm::vec3 center = glm::isnan(origin.x) ? box.calcCenter() : origin; + return transform(glm::translate(center) * glm::scale(scale)); + } + return {}; +} +QVariantMap ScriptableMesh::rotateDegrees(const glm::vec3& eulerAngles, const glm::vec3& origin) { + return rotate(glm::quat(glm::radians(eulerAngles)), origin); +} +QVariantMap ScriptableMesh::rotate(const glm::quat& rotation, const glm::vec3& origin) { + if (auto mesh = getMeshPointer()) { + auto box = mesh->evalPartsBound(0, (int)mesh->getNumParts()); + glm::vec3 center = glm::isnan(origin.x) ? box.calcCenter() : origin; + return transform(glm::translate(center) * glm::toMat4(rotation)); + } + return {}; +} +QVariantMap ScriptableMesh::transform(const glm::mat4& transform) { + if (auto mesh = getMeshPointer()) { + const auto& pos = getBufferView(mesh, gpu::Stream::POSITION); + const uint32_t num = (uint32_t)pos.getNumElements(); + for (uint32_t i = 0; i < num; i++) { + auto& position = pos.edit(i); + position = transform * glm::vec4(position, 1.0f); + } + } + return getMeshExtents(); +} + +QVariantList ScriptableMesh::getAttributeValues(const QString& attributeName) const { + QVariantList result; + auto slotNum = _getSlotNumber(attributeName); + if (slotNum >= 0) { + auto slot = (gpu::Stream::Slot)slotNum; + const auto& bufferView = getBufferView(getMeshPointer(), slot); + if (auto len = bufferView.getNumElements()) { + bool asArray = bufferView._element.getType() != gpu::FLOAT; + for (quint32 i = 0; i < len; i++) { + result << bufferViewElementToVariant(bufferView, i, asArray, attributeName.toStdString().c_str()); + } + } + } + return result; +} +QVariantMap ScriptableMesh::getVertexAttributes(quint32 vertexIndex, QVector names) const { + QVariantMap result; + auto mesh = getMeshPointer(); + if (!mesh || vertexIndex >= getNumVertices()) { + return result; + } + for (const auto& a : ATTRIBUTES.toStdMap()) { + auto name = a.first; + if (!names.contains(name)) { + continue; + } + auto slot = a.second; + const gpu::BufferView& bufferView = getBufferView(mesh, slot); + if (vertexIndex < bufferView.getNumElements()) { + bool asArray = bufferView._element.getType() != gpu::FLOAT; + result[name] = bufferViewElementToVariant(bufferView, vertexIndex, asArray, name.toStdString().c_str()); + } + } + return result; +} + +/// --- buffer view <-> variant helpers + +namespace { + // expand the corresponding attribute buffer (creating it if needed) so that it matches POSITIONS size and specified element type + gpu::BufferView _expandedAttributeBuffer(const scriptable::MeshPointer mesh, gpu::Stream::Slot slot, const gpu::Element& elementType) { + gpu::Size elementSize = elementType.getSize(); + gpu::BufferView bufferView = getBufferView(mesh, slot); + auto nPositions = mesh->getNumVertices(); + auto vsize = nPositions * elementSize; + auto diffTypes = (elementType.getType() != bufferView._element.getType() || + elementType.getSize() > bufferView._element.getSize() || + elementType.getScalarCount() > bufferView._element.getScalarCount() || + vsize > bufferView._size + ); + auto hint = DebugNames::stringFrom(slot); + +#ifdef DEV_BUILD + auto beforeCount = bufferView.getNumElements(); + auto beforeTotal = bufferView._size; +#endif + if (bufferView.getNumElements() < nPositions || diffTypes) { + if (!bufferView._buffer || bufferView.getNumElements() == 0) { + qCInfo(mesh_logging).nospace() << "ScriptableMesh -- adding missing mesh attribute '" << hint << "' for BufferView"; + gpu::Byte *data = new gpu::Byte[vsize]; + memset(data, 0, vsize); + auto buffer = new gpu::Buffer(vsize, (gpu::Byte*)data); + delete[] data; + bufferView = gpu::BufferView(buffer, elementType); + mesh->addAttribute(slot, bufferView); + } else { + qCInfo(mesh_logging) << "ScriptableMesh -- resizing Buffer current:" << hint << bufferView._buffer->getSize() << "wanted:" << vsize; + bufferView._element = elementType; + bufferView._buffer->resize(vsize); + bufferView._size = bufferView._buffer->getSize(); + } + } +#ifdef DEV_BUILD + auto afterCount = bufferView.getNumElements(); + auto afterTotal = bufferView._size; + if (beforeTotal != afterTotal || beforeCount != afterCount) { + auto typeName = DebugNames::stringFrom(bufferView._element.getType()); + qCDebug(mesh_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); + } +#endif + return bufferView; + } + const gpu::Element UNUSED{ gpu::SCALAR, gpu::UINT8, gpu::RAW }; + + gpu::Element getVecNElement(gpu::Type T, int N) { + switch(N) { + case 2: return { gpu::VEC2, T, gpu::XY }; + case 3: return { gpu::VEC3, T, gpu::XYZ }; + case 4: return { gpu::VEC4, T, gpu::XYZW }; + } + Q_ASSERT(false); + return UNUSED; + } + + gpu::BufferView expandAttributeToMatchPositions(scriptable::MeshPointer mesh, gpu::Stream::Slot slot) { + if (slot == gpu::Stream::POSITION) { + return getBufferView(mesh, slot); + } + return _expandedAttributeBuffer(mesh, slot, getVecNElement(gpu::FLOAT, 3)); + } +} + +std::map ScriptableMesh::gatherBufferViews(scriptable::MeshPointer mesh, const QStringList& expandToMatchPositions) { + std::map attributeViews; + if (!mesh) { + return attributeViews; + } + for (const auto& a : ScriptableMesh::ATTRIBUTES.toStdMap()) { + auto name = a.first; + auto slot = a.second; + if (expandToMatchPositions.contains(name)) { + expandAttributeToMatchPositions(mesh, slot); + } + auto view = getBufferView(mesh, slot); + auto beforeCount = view.getNumElements(); + if (beforeCount > 0) { + auto element = view._element; + auto vecN = element.getScalarCount(); + auto type = element.getType(); + QString typeName = DebugNames::stringFrom(element.getType()); + auto beforeTotal = view._size; + + attributeViews[name] = _expandedAttributeBuffer(mesh, slot, getVecNElement(type, vecN)); + +#if DEV_BUILD + auto afterTotal = attributeViews[name]._size; + auto afterCount = attributeViews[name].getNumElements(); + if (beforeTotal != afterTotal || beforeCount != afterCount) { + qCDebug(mesh_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); + } +#endif + } + } + return attributeViews; +} diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h new file mode 100644 index 0000000000..da11002906 --- /dev/null +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h @@ -0,0 +1,127 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace graphics { + class Mesh; +} +namespace gpu { + class BufferView; +} +namespace scriptable { + class ScriptableMesh : public QObject, public std::enable_shared_from_this { + Q_OBJECT + public: + ScriptableModelPointer _model; + scriptable::MeshPointer _mesh; + QVariantMap _metadata; + ScriptableMesh() : QObject() {} + ScriptableMesh(ScriptableModelPointer parent, scriptable::MeshPointer mesh) : QObject(), _model(parent), _mesh(mesh) {} + ScriptableMesh(const ScriptableMesh& other) : QObject(), _model(other._model), _mesh(other._mesh), _metadata(other._metadata) {} + ~ScriptableMesh() { qDebug() << "~ScriptableMesh" << this; } + Q_PROPERTY(quint32 numParts READ getNumParts) + Q_PROPERTY(quint32 numAttributes READ getNumAttributes) + Q_PROPERTY(quint32 numVertices READ getNumVertices) + Q_PROPERTY(quint32 numIndices READ getNumIndices) + Q_PROPERTY(QVector attributeNames READ getAttributeNames) + + virtual scriptable::MeshPointer getMeshPointer() const { return _mesh; } + Q_INVOKABLE virtual quint32 getNumParts() const; + Q_INVOKABLE virtual quint32 getNumVertices() const; + Q_INVOKABLE virtual quint32 getNumAttributes() const; + Q_INVOKABLE virtual quint32 getNumIndices() const { return 0; } + Q_INVOKABLE virtual QVector getAttributeNames() const; + Q_INVOKABLE virtual QVariantMap getVertexAttributes(quint32 vertexIndex) const; + Q_INVOKABLE virtual QVariantMap getVertexAttributes(quint32 vertexIndex, QVector attributes) const; + + Q_INVOKABLE virtual QVector getIndices() const; + Q_INVOKABLE virtual QVector findNearbyIndices(const glm::vec3& origin, float epsilon = 1e-6) const; + Q_INVOKABLE virtual QVariantMap getMeshExtents() const; + Q_INVOKABLE virtual bool setVertexAttributes(quint32 vertexIndex, QVariantMap attributes); + Q_INVOKABLE virtual QVariantMap scaleToFit(float unitScale); + + static QMap ATTRIBUTES; + static std::map gatherBufferViews(MeshPointer mesh, const QStringList& expandToMatchPositions = QStringList()); + + Q_INVOKABLE QVariantList getAttributeValues(const QString& attributeName) const; + + Q_INVOKABLE int _getSlotNumber(const QString& attributeName) const; + + QVariantMap translate(const glm::vec3& translation); + QVariantMap scale(const glm::vec3& scale, const glm::vec3& origin = glm::vec3(NAN)); + QVariantMap rotateDegrees(const glm::vec3& eulerAngles, const glm::vec3& origin = glm::vec3(NAN)); + QVariantMap rotate(const glm::quat& rotation, const glm::vec3& origin = glm::vec3(NAN)); + Q_INVOKABLE QVariantMap transform(const glm::mat4& transform); + }; + + // TODO: for now this is a part-specific wrapper around ScriptableMesh + class ScriptableMeshPart : public ScriptableMesh { + Q_OBJECT + public: + ScriptableMeshPart& operator=(const ScriptableMeshPart& view) { _model=view._model; _mesh=view._mesh; return *this; }; + ScriptableMeshPart(const ScriptableMeshPart& other) : ScriptableMesh(other._model, other._mesh) {} + ScriptableMeshPart() : ScriptableMesh(nullptr, nullptr) {} + ~ScriptableMeshPart() { qDebug() << "~ScriptableMeshPart" << this; } + ScriptableMeshPart(ScriptableMeshPointer mesh) : ScriptableMesh(mesh->_model, mesh->_mesh) {} + Q_PROPERTY(QString topology READ getTopology) + Q_PROPERTY(quint32 numFaces READ getNumFaces) + + scriptable::MeshPointer parentMesh; + int partIndex; + QString getTopology() const { return "triangles"; } + Q_INVOKABLE virtual quint32 getNumFaces() const { return getIndices().size() / 3; } + Q_INVOKABLE virtual QVector getFace(quint32 faceIndex) const { + auto inds = getIndices(); + return faceIndex+2 < (quint32)inds.size() ? inds.mid(faceIndex*3, 3) : QVector(); + } + }; + + class GraphicsScriptingInterface : public QObject { + Q_OBJECT + public: + GraphicsScriptingInterface(QObject* parent = nullptr) : QObject(parent) {} + GraphicsScriptingInterface(const GraphicsScriptingInterface& other) {} + public slots: + ScriptableMeshPart exportMeshPart(ScriptableMesh mesh, int part) { return {}; } + + }; +} + +Q_DECLARE_METATYPE(scriptable::ScriptableMesh) +Q_DECLARE_METATYPE(scriptable::ScriptableMeshPointer) +Q_DECLARE_METATYPE(QVector) +Q_DECLARE_METATYPE(scriptable::ScriptableMeshPart) +Q_DECLARE_METATYPE(scriptable::GraphicsScriptingInterface) + +// FIXME: faces were supported in the original Model.* API -- are they still needed/used/useful for anything yet? +#include + +namespace mesh { + using uint32 = quint32; + class MeshFace; + using MeshFaces = QVector; + class MeshFace { + public: + MeshFace() {} + MeshFace(QVector vertexIndices) : vertexIndices(vertexIndices) {} + ~MeshFace() {} + + QVector vertexIndices; + // TODO -- material... + }; +}; + +Q_DECLARE_METATYPE(mesh::MeshFace) +Q_DECLARE_METATYPE(QVector) +Q_DECLARE_METATYPE(mesh::uint32) +Q_DECLARE_METATYPE(QVector) diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h new file mode 100644 index 0000000000..e8cf6f1656 --- /dev/null +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h @@ -0,0 +1,80 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace graphics { + class Mesh; +} +namespace gpu { + class BufferView; +} +namespace scriptable { + using Mesh = graphics::Mesh; + using MeshPointer = std::shared_ptr; + + class ScriptableModel; + class ScriptableMesh; + class ScriptableMeshPart; + using ScriptableModelPointer = std::shared_ptr; + using ScriptableMeshPointer = std::shared_ptr; + using ScriptableMeshPartPointer = std::shared_ptr; + class ScriptableModel : public QObject, public std::enable_shared_from_this { + Q_OBJECT + public: + Q_PROPERTY(QVector meshes READ getMeshes) + + Q_INVOKABLE QString toString() { return "[ScriptableModel " + objectName()+"]"; } + ScriptableModel(QObject* parent = nullptr) : QObject(parent) {} + ScriptableModel(const ScriptableModel& other) : objectID(other.objectID), metadata(other.metadata), meshes(other.meshes) {} + ScriptableModel& operator=(const ScriptableModel& view) { + objectID = view.objectID; + metadata = view.metadata; + meshes = view.meshes; + return *this; + } + ~ScriptableModel() { qDebug() << "~ScriptableModel" << this; } + void mixin(const ScriptableModel& other) { + for (const auto& key : other.metadata.keys()) { + metadata[key] = other.metadata[key]; + } + for(const auto&mesh : other.meshes) { + meshes << mesh; + } + } + QUuid objectID; + QVariantMap metadata; + QVector meshes; + // TODO: in future accessors for these could go here + QVariantMap shapes; + QVariantMap materials; + QVariantMap armature; + + QVector getMeshes() const; + }; + + class ModelProvider { + public: + QVariantMap metadata; + static scriptable::ScriptableModel modelUnavailableError(bool* ok) { if (ok) { *ok = false; } return {}; } + virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) = 0; + }; + using ModelProviderPointer = std::shared_ptr; + class ModelProviderFactory : public Dependency { + public: + virtual scriptable::ModelProviderPointer lookupModelProvider(QUuid uuid) = 0; + }; + +} + +Q_DECLARE_METATYPE(scriptable::MeshPointer) +Q_DECLARE_METATYPE(scriptable::ScriptableModel) +Q_DECLARE_METATYPE(scriptable::ScriptableModelPointer) + diff --git a/libraries/graphics/src/graphics/Geometry.cpp b/libraries/graphics/src/graphics/Geometry.cpp index ba5afcbc62..d43c773249 100755 --- a/libraries/graphics/src/graphics/Geometry.cpp +++ b/libraries/graphics/src/graphics/Geometry.cpp @@ -42,6 +42,11 @@ void Mesh::addAttribute(Slot slot, const BufferView& buffer) { evalVertexFormat(); } +void Mesh::removeAttribute(Slot slot) { + _attributeBuffers.erase(slot); + evalVertexFormat(); +} + const BufferView Mesh::getAttributeBuffer(int attrib) const { auto attribBuffer = _attributeBuffers.find(attrib); if (attribBuffer != _attributeBuffers.end()) { @@ -224,6 +229,7 @@ graphics::MeshPointer Mesh::map(std::function vertexFunc, } graphics::MeshPointer result(new graphics::Mesh()); + result->displayName = displayName; gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ); gpu::Buffer* resultVertexBuffer = new gpu::Buffer(vertexSize, resultVertexData.get()); diff --git a/libraries/graphics/src/graphics/Geometry.h b/libraries/graphics/src/graphics/Geometry.h index 642aa9e38d..23ebec2965 100755 --- a/libraries/graphics/src/graphics/Geometry.h +++ b/libraries/graphics/src/graphics/Geometry.h @@ -56,6 +56,7 @@ public: // Attribute Buffers size_t getNumAttributes() const { return _attributeBuffers.size(); } void addAttribute(Slot slot, const BufferView& buffer); + void removeAttribute(Slot slot); const BufferView getAttributeBuffer(int attrib) const; // Stream format diff --git a/libraries/model-networking/src/model-networking/SimpleMeshProxy.cpp b/libraries/model-networking/src/model-networking/SimpleMeshProxy.cpp deleted file mode 100644 index 741478789e..0000000000 --- a/libraries/model-networking/src/model-networking/SimpleMeshProxy.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// -// 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 deleted file mode 100644 index 24c3fca27e..0000000000 --- a/libraries/model-networking/src/model-networking/SimpleMeshProxy.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// 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/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 6be3057c93..55762e38fd 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -7,6 +7,7 @@ link_hifi_libraries(shared ktx gpu graphics model-networking render animation fb include_hifi_library_headers(networking) include_hifi_library_headers(octree) include_hifi_library_headers(audio) +include_hifi_library_headers(graphics-scripting) # for ScriptableModel.h if (NOT ANDROID) target_nsight() diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 23473e74f2..6aa42cf6df 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -2407,3 +2407,48 @@ void GeometryCache::renderWireCubeInstance(RenderArgs* args, gpu::Batch& batch, assert(pipeline != nullptr); renderInstances(args, batch, color, true, pipeline, GeometryCache::Cube); } + +graphics::MeshPointer GeometryCache::meshFromShape(Shape geometryShape, glm::vec3 color) { + auto shapeData = getShapeData(geometryShape); + + qDebug() << "GeometryCache::getMeshProxyListFromShape" << shapeData << stringFromShape(geometryShape); + + auto cloneBufferView = [](const gpu::BufferView& in) -> gpu::BufferView { + auto buffer = std::make_shared(*in._buffer); // copy + // FIXME: gpu::BufferView seems to have a bug where constructing a new instance from an existing one + // results in over-multiplied buffer/view sizes -- hence constructing manually here from each input prop + auto out = gpu::BufferView(buffer, in._offset, in._size, in._stride, in._element); + Q_ASSERT(out.getNumElements() == in.getNumElements()); + Q_ASSERT(out._size == in._size); + Q_ASSERT(out._buffer->getSize() == in._buffer->getSize()); + return out; + }; + + auto positionsBufferView = cloneBufferView(shapeData->_positionView); + auto normalsBufferView = cloneBufferView(shapeData->_normalView); + auto indexBufferView = cloneBufferView(shapeData->_indicesView); + + gpu::BufferView::Size numVertices = positionsBufferView.getNumElements(); + Q_ASSERT(numVertices == normalsBufferView.getNumElements()); + + // apply input color across all vertices + auto colorsBufferView = cloneBufferView(shapeData->_normalView); + for (gpu::BufferView::Size i = 0; i < numVertices; i++) { + colorsBufferView.edit((gpu::BufferView::Index)i) = color; + } + + graphics::MeshPointer mesh(new graphics::Mesh()); + mesh->setVertexBuffer(positionsBufferView); + mesh->setIndexBuffer(indexBufferView); + mesh->addAttribute(gpu::Stream::NORMAL, normalsBufferView); + mesh->addAttribute(gpu::Stream::COLOR, colorsBufferView); + + const auto startIndex = 0, baseVertex = 0; + graphics::Mesh::Part part(startIndex, (graphics::Index)indexBufferView.getNumElements(), baseVertex, graphics::Mesh::TRIANGLES); + auto partBuffer = new gpu::Buffer(sizeof(graphics::Mesh::Part), (gpu::Byte*)&part); + mesh->setPartBuffer(gpu::BufferView(partBuffer, gpu::Element::PART_DRAWCALL)); + + mesh->displayName = QString("GeometryCache/shape::%1").arg(GeometryCache::stringFromShape(geometryShape)); + + return mesh; +} diff --git a/libraries/render-utils/src/GeometryCache.h b/libraries/render-utils/src/GeometryCache.h index 63af30bb79..998043b80e 100644 --- a/libraries/render-utils/src/GeometryCache.h +++ b/libraries/render-utils/src/GeometryCache.h @@ -375,6 +375,7 @@ public: /// otherwise nullptr in the event of an error. const ShapeData * getShapeData(Shape shape) const; + graphics::MeshPointer meshFromShape(Shape geometryShape, glm::vec3 color); private: GeometryCache(); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index b0763c0fb3..d595136c56 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -26,7 +26,7 @@ #include #include -#include +#include #include #include @@ -573,15 +573,21 @@ bool Model::convexHullContains(glm::vec3 point) { return false; } -MeshProxyList Model::getMeshes() const { - MeshProxyList result; +scriptable::ScriptableModel Model::getScriptableModel(bool* ok) { + scriptable::ScriptableModel result; const Geometry::Pointer& renderGeometry = getGeometry(); - const Geometry::GeometryMeshes& meshes = renderGeometry->getMeshes(); if (!isLoaded()) { + qDebug() << "Model::getScriptableModel -- !isLoaded"; + if (ok) { + *ok = false; + } return result; } +// TODO: remove -- this was an earlier approach using renderGeometry instead of FBXGeometry +#if 0 // renderGeometry approach + const Geometry::GeometryMeshes& meshes = renderGeometry->getMeshes(); Transform offset; offset.setScale(_scale); offset.postTranslate(_offset); @@ -591,20 +597,67 @@ MeshProxyList Model::getMeshes() const { if (!mesh) { continue; } - - MeshProxy* meshProxy = new SimpleMeshProxy( - mesh->map( - [=](glm::vec3 position) { - return glm::vec3(offsetMat * glm::vec4(position, 1.0f)); - }, - [=](glm::vec3 color) { return color; }, - [=](glm::vec3 normal) { - return glm::normalize(glm::vec3(offsetMat * glm::vec4(normal, 0.0f))); - }, - [&](uint32_t index) { return index; })); - result << meshProxy; + qDebug() << "Model::getScriptableModel #" << i++ << mesh->displayName; + auto newmesh = mesh->map( + [=](glm::vec3 position) { + return glm::vec3(offsetMat * glm::vec4(position, 1.0f)); + }, + [=](glm::vec3 color) { return color; }, + [=](glm::vec3 normal) { + return glm::normalize(glm::vec3(offsetMat * glm::vec4(normal, 0.0f))); + }, + [&](uint32_t index) { return index; }); + newmesh->displayName = mesh->displayName; + result << newmesh; } - +#endif + const FBXGeometry& geometry = getFBXGeometry(); + auto mat4toVariant = [](const glm::mat4& mat4) -> QVariant { + QVector floats; + floats.resize(16); + memcpy(floats.data(), &mat4, sizeof(glm::mat4)); + QVariant v; + v.setValue>(floats); + return v; + }; + result.metadata = { + { "url", _url.toString() }, + { "textures", renderGeometry->getTextures() }, + { "offset", vec3toVariant(_offset) }, + { "scale", vec3toVariant(_scale) }, + { "rotation", quatToVariant(_rotation) }, + { "translation", vec3toVariant(_translation) }, + { "meshToModel", mat4toVariant(glm::scale(_scale) * glm::translate(_offset)) }, + { "meshToWorld", mat4toVariant(createMatFromQuatAndPos(_rotation, _translation) * (glm::scale(_scale) * glm::translate(_offset))) }, + { "geometryOffset", mat4toVariant(geometry.offset) }, + }; + 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; + } + result.meshes << std::const_pointer_cast(mesh); + auto extraInfo = geometry.getModelNameOfMesh(i); + qDebug() << "Model::getScriptableModel #" << i << QString(mesh->displayName) << extraInfo; + submeshes << QVariantMap{ + { "index", i }, + { "meshIndex", fbxMesh.meshIndex }, + { "modelName", extraInfo }, + { "transform", mat4toVariant(fbxMesh.modelTransform) }, + { "extents", QVariantMap({ + { "minimum", vec3toVariant(fbxMesh.meshExtents.minimum) }, + { "maximum", vec3toVariant(fbxMesh.meshExtents.maximum) }, + })}, + }; + } + if (ok) { + *ok = true; + } + qDebug() << "//Model::getScriptableModel -- #" << result.meshes.size(); + result.metadata["submeshes"] = submeshes; return result; } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 027d52ecfd..4fd00c9f9a 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -64,7 +65,7 @@ using ModelWeakPointer = std::weak_ptr; /// A generic 3D model displaying geometry loaded from a URL. -class Model : public QObject, public std::enable_shared_from_this { +class Model : public QObject, public std::enable_shared_from_this, public scriptable::ModelProvider { Q_OBJECT public: @@ -313,7 +314,7 @@ public: int getResourceDownloadAttempts() { return _renderWatcher.getResourceDownloadAttempts(); } int getResourceDownloadAttemptsRemaining() { return _renderWatcher.getResourceDownloadAttemptsRemaining(); } - Q_INVOKABLE MeshProxyList getMeshes() const; + Q_INVOKABLE virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override; void scaleToFit(); diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp deleted file mode 100644 index c693083ebf..0000000000 --- a/libraries/script-engine/src/ModelScriptingInterface.cpp +++ /dev/null @@ -1,251 +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 "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 deleted file mode 100644 index 3c239f006f..0000000000 --- a/libraries/script-engine/src/ModelScriptingInterface.h +++ /dev/null @@ -1,39 +0,0 @@ -// -// 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 c79ffffec7..ffb1b7bc74 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -73,8 +73,6 @@ #include "WebSocketClass.h" #include "RecordingScriptingInterface.h" #include "ScriptEngines.h" -#include "ModelScriptingInterface.h" - #include @@ -711,10 +709,6 @@ 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 7b455beae5..9ad5c27072 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -855,68 +855,3 @@ 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 25b2cec331..e8390c3a86 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -315,51 +315,9 @@ 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 From a08770c816e46a2d0c2f13e1c4c92076ac130b91 Mon Sep 17 00:00:00 2001 From: humbletim Date: Fri, 9 Feb 2018 02:14:32 -0500 Subject: [PATCH 02/29] cleanup --- libraries/fbx/src/OBJWriter.cpp | 6 +- .../graphics-scripting/BufferViewHelpers.cpp | 55 ++ .../graphics-scripting/BufferViewHelpers.h | 9 +- .../ModelScriptingInterface.cpp | 641 ++++-------------- .../ModelScriptingInterface.h | 16 +- .../src/graphics-scripting/ScriptableMesh.cpp | 403 ++++++++++- .../src/graphics-scripting/ScriptableMesh.h | 123 ++-- .../src/graphics-scripting/ScriptableModel.h | 67 +- libraries/render-utils/src/Model.cpp | 58 +- 9 files changed, 722 insertions(+), 656 deletions(-) diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp index 37bced8458..5307f49f36 100644 --- a/libraries/fbx/src/OBJWriter.cpp +++ b/libraries/fbx/src/OBJWriter.cpp @@ -15,6 +15,9 @@ #include "OBJWriter.h" #include "ModelFormatLogging.h" +// FIXME: should this live in shared? (it depends on gpu/) +#include <../graphics-scripting/src/graphics-scripting/BufferViewHelpers.h> + static QString formatFloat(double n) { // limit precision to 6, but don't output trailing zeros. QString s = QString::number(n, 'f', 6); @@ -91,7 +94,8 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(gpu::Stream::InputSlot::NORMAL); gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements(); for (gpu::BufferView::Index i = 0; i < numNormals; i++) { - glm::vec3 normal = normalsBufferView.get(i); + glm::vec3 normal = glmVecFromVariant(bufferViewElementToVariant(normalsBufferView, i)); + //glm::vec3 normal = normalsBufferView.get(i); out << "vn "; out << formatFloat(normal[0]) << " "; out << formatFloat(normal[1]) << " "; diff --git a/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.cpp b/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.cpp index e865ed0e5a..d83322f360 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.cpp @@ -8,9 +8,15 @@ #include #include +namespace glm { + using hvec2 = glm::tvec2; + using hvec4 = glm::tvec4; +} +//#define DEBUG_BUFFERVIEW_SCRIPTING #ifdef DEBUG_BUFFERVIEW_SCRIPTING #include "DebugNames.h" + QLoggingCategory bufferview_helpers{"hifi.bufferview"}; #endif namespace { @@ -61,6 +67,9 @@ bool bufferViewElementFromVariant(const gpu::BufferView& view, quint32 index, co const auto dataType = element.getType(); const auto byteLength = element.getSize(); const auto BYTES_PER_ELEMENT = byteLength / vecN; +#ifdef DEBUG_BUFFERVIEW_SCRIPTING + qCDebug(bufferview_helpers) << "bufferViewElementFromVariant" << index << DebugNames::stringFrom(dataType) << BYTES_PER_ELEMENT << vecN; +#endif if (BYTES_PER_ELEMENT == 1) { switch(vecN) { case 2: setBufferViewElement(view, index, v); return true; @@ -71,16 +80,25 @@ bool bufferViewElementFromVariant(const gpu::BufferView& view, quint32 index, co glm::uint32 unused; packNormalAndTangent(glmVecFromVariant(v), glm::vec3(), rawColor, unused); view.edit(index) = rawColor; + return true; } else if (element == gpu::Element::VEC4F_NORMALIZED_XYZ10W2) { glm::uint32 packedNormal;// = glm::packSnorm3x10_1x2(glm::vec4(glmVecFromVariant(v), 0.0f)); glm::uint32 unused; packNormalAndTangent(glm::vec3(), glmVecFromVariant(v), unused, packedNormal); view.edit(index) = packedNormal; + return true; } setBufferViewElement(view, index, v); return true; } } } else if (BYTES_PER_ELEMENT == 2) { + if (dataType == gpu::HALF) { + switch(vecN) { + case 2: view.edit(index) = glm::packSnorm2x8(glmVecFromVariant(v)); return true; + case 4: view.edit(index) = glm::packSnorm4x8(glmVecFromVariant(v)); return true; + default: return false; + } + } switch(vecN) { case 2: setBufferViewElement(view, index, v); return true; case 3: setBufferViewElement(view, index, v); return true; @@ -112,6 +130,9 @@ QVariant bufferViewElementToVariant(const gpu::BufferView& view, quint32 index, const auto BYTES_PER_ELEMENT = byteLength / vecN; Q_ASSERT(index < view.getNumElements()); Q_ASSERT(index * vecN * BYTES_PER_ELEMENT < (view._size - vecN * BYTES_PER_ELEMENT)); +#ifdef DEBUG_BUFFERVIEW_SCRIPTING + qCDebug(bufferview_helpers) << "bufferViewElementToVariant" << index << DebugNames::stringFrom(dataType) << BYTES_PER_ELEMENT << vecN; +#endif if (BYTES_PER_ELEMENT == 1) { switch(vecN) { case 2: return getBufferViewElement(view, index, asArray); @@ -129,6 +150,12 @@ QVariant bufferViewElementToVariant(const gpu::BufferView& view, quint32 index, } } } else if (BYTES_PER_ELEMENT == 2) { + if (dataType == gpu::HALF) { + switch(vecN) { + case 2: return glmVecToVariant(glm::vec2(glm::unpackSnorm2x8(view.get(index)))); + case 4: return glmVecToVariant(glm::vec4(glm::unpackSnorm4x8(view.get(index)))); + } + } switch(vecN) { case 2: return getBufferViewElement(view, index, asArray); case 3: return getBufferViewElement(view, index, asArray); @@ -193,3 +220,31 @@ const T glmVecFromVariant(const QVariant& v) { return result; } +template +gpu::BufferView bufferViewFromVector(QVector elements, gpu::Element elementType) { + auto vertexBuffer = std::make_shared(elements.size() * sizeof(T), (gpu::Byte*)elements.data()); + return { vertexBuffer, 0, vertexBuffer->getSize(),sizeof(T), elementType }; +} + +template<> gpu::BufferView bufferViewFromVector(QVector elements, gpu::Element elementType) { return bufferViewFromVector(elements, elementType); } +template<> gpu::BufferView bufferViewFromVector(QVector elements, gpu::Element elementType) { return bufferViewFromVector(elements, elementType); } + +gpu::BufferView cloneBufferView(const gpu::BufferView& input) { + return gpu::BufferView( + std::make_shared(input._buffer->getSize(), input._buffer->getData()), + input._offset, input._size, input._stride, input._element + ); +} + +gpu::BufferView resizedBufferView(const gpu::BufferView& input, quint32 numElements) { + auto effectiveSize = input._buffer->getSize() / input.getNumElements(); + qDebug() << "resize input" << input.getNumElements() << input._buffer->getSize() << "effectiveSize" << effectiveSize; + auto vsize = input._element.getSize() * numElements; + gpu::Byte *data = new gpu::Byte[vsize]; + memset(data, 0, vsize); + auto buffer = new gpu::Buffer(vsize, (gpu::Byte*)data); + delete[] data; + auto output = gpu::BufferView(buffer, input._element); + qDebug() << "resized output" << output.getNumElements() << output._buffer->getSize(); + return output; +} diff --git a/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.h b/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.h index 0fe2602f6c..d0d42ca419 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.h +++ b/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.h @@ -8,11 +8,18 @@ #include -namespace gpu { class BufferView; } +namespace gpu { + class BufferView; + class Element; +} template QVariant glmVecToVariant(const T& v, bool asArray = false); template const T glmVecFromVariant(const QVariant& v); QVariant bufferViewElementToVariant(const gpu::BufferView& view, quint32 index, bool asArray = false, const char* hint = ""); bool bufferViewElementFromVariant(const gpu::BufferView& view, quint32 index, const QVariant& v); +template gpu::BufferView bufferViewFromVector(QVector elements, gpu::Element elementType); + +gpu::BufferView cloneBufferView(const gpu::BufferView& input); +gpu::BufferView resizedBufferView(const gpu::BufferView& input, quint32 numElements); diff --git a/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp index 68a00bc02c..ab85fb8265 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp @@ -17,27 +17,16 @@ #include "BaseScriptEngine.h" #include "ScriptEngineLogging.h" #include "OBJWriter.h" -#include "OBJReader.h" -//#include "ui/overlays/Base3DOverlay.h" -//#include "EntityTreeRenderer.h" -//#include "avatar/AvatarManager.h" -//#include "RenderableEntityItem.h" -#include #include - #include - #include - #include + #include "BufferViewScripting.h" - #include "ScriptableMesh.h" -using ScriptableMesh = scriptable::ScriptableMesh; - #include "ModelScriptingInterface.moc" namespace { @@ -50,13 +39,56 @@ ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(pare } } +void ModelScriptingInterface::getMeshes(QUuid uuid, QScriptValue scopeOrCallback, QScriptValue methodOrName) { + auto handler = makeScopedHandlerObject(scopeOrCallback, methodOrName); + 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(); + qDebug() << "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(model_scripting) << "fetching meshes from " << providerType << "..."; + auto scriptableMeshes = provider->getScriptableModel(&success); + qCDebug(model_scripting) << "//fetched meshes from " << providerType << "success:" <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(model_scripting) << "ModelScriptingInterface::getMeshes ERROR" << error; + callScopedHandlerObject(handler, engine->makeError(error), QScriptValue::NullValue); + } else { + callScopedHandlerObject(handler, QScriptValue::NullValue, engine->newQObject(meshes, QScriptEngine::ScriptOwnership)); + } +} + QString ModelScriptingInterface::meshToOBJ(const scriptable::ScriptableModel& _in) { - const auto& in = _in.getMeshes(); + const auto& in = _in.getConstMeshes(); qCDebug(model_scripting) << "meshToOBJ" << in.size(); if (in.size()) { QList meshes; - foreach (const auto meshProxy, in) { - qCDebug(model_scripting) << "meshToOBJ" << meshProxy.get(); + foreach (auto meshProxy, in) { + qCDebug(model_scripting) << "meshToOBJ" << meshProxy; if (meshProxy) { meshes.append(getMeshPointer(meshProxy)); } @@ -77,7 +109,7 @@ QScriptValue ModelScriptingInterface::appendMeshes(scriptable::ScriptableModel _ size_t totalColorCount { 0 }; size_t totalNormalCount { 0 }; size_t totalIndexCount { 0 }; - foreach (const scriptable::ScriptableMeshPointer meshProxy, in) { + foreach (auto& meshProxy, in) { scriptable::MeshPointer mesh = getMeshPointer(meshProxy); totalVertexCount += mesh->getNumVertices(); @@ -113,7 +145,7 @@ QScriptValue ModelScriptingInterface::appendMeshes(scriptable::ScriptableModel _ uint32_t indexStartOffset { 0 }; - foreach (const scriptable::ScriptableMeshPointer meshProxy, in) { + foreach (const auto& meshProxy, in) { scriptable::MeshPointer mesh = getMeshPointer(meshProxy); mesh->forEach( [&](glm::vec3 position){ @@ -175,8 +207,7 @@ QScriptValue ModelScriptingInterface::appendMeshes(scriptable::ScriptableModel _ (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); - scriptable::ScriptableMeshPointer resultProxy = scriptable::ScriptableMeshPointer(new ScriptableMesh(nullptr, result)); - return engine()->toScriptValue(result); + return engine()->toScriptValue(scriptable::ScriptableMeshPointer(new scriptable::ScriptableMesh(nullptr, result))); } QScriptValue ModelScriptingInterface::transformMesh(scriptable::ScriptableMeshPointer meshProxy, glm::mat4 transform) { @@ -189,7 +220,7 @@ QScriptValue ModelScriptingInterface::transformMesh(scriptable::ScriptableMeshPo [&](glm::vec3 color){ return color; }, [&](glm::vec3 normal){ return glm::vec3(transform * glm::vec4(normal, 0.0f)); }, [&](uint32_t index){ return index; }); - scriptable::ScriptableMeshPointer resultProxy = scriptable::ScriptableMeshPointer(new ScriptableMesh(nullptr, result)); + scriptable::ScriptableMeshPointer resultProxy = scriptable::ScriptableMeshPointer(new scriptable::ScriptableMesh(nullptr, result)); return engine()->toScriptValue(resultProxy); } @@ -201,8 +232,11 @@ QScriptValue ModelScriptingInterface::getVertexCount(scriptable::ScriptableMeshP return (uint32_t)mesh->getNumVertices(); } -QScriptValue ModelScriptingInterface::getVertex(scriptable::ScriptableMeshPointer meshProxy, mesh::uint32 vertexIndex) { +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(); @@ -266,480 +300,46 @@ QScriptValue ModelScriptingInterface::newMesh(const QVector& vertices - scriptable::ScriptableMeshPointer meshProxy = scriptable::ScriptableMeshPointer(new ScriptableMesh(nullptr, mesh)); + scriptable::ScriptableMeshPointer meshProxy = scriptable::ScriptableMeshPointer(new scriptable::ScriptableMesh(nullptr, mesh)); return engine()->toScriptValue(meshProxy); } -QScriptValue ModelScriptingInterface::mapAttributeValues( - QScriptValue _in, - QScriptValue scopeOrCallback, - QScriptValue methodOrName - ) { - qCInfo(model_scripting) << "mapAttributeValues" << _in.toVariant().typeName() << _in.toVariant().toString() << _in.toQObject(); - auto in = qscriptvalue_cast(_in).getMeshes(); - if (in.size()) { - foreach (scriptable::ScriptableMeshPointer meshProxy, in) { - mapMeshAttributeValues(meshProxy, scopeOrCallback, methodOrName); - } - return thisObject(); - } else if (auto meshProxy = qobject_cast(_in.toQObject())) { - return mapMeshAttributeValues(meshProxy->shared_from_this(), scopeOrCallback, methodOrName); - } else { - context()->throwError("invalid ModelProxy || MeshProxyPointer"); - } - return false; -} - - -QScriptValue ModelScriptingInterface::unrollVertices(scriptable::ScriptableMeshPointer meshProxy, bool recalcNormals) { - auto mesh = getMeshPointer(meshProxy); - qCInfo(model_scripting) << "ModelScriptingInterface::unrollVertices" << !!mesh<< !!meshProxy; - if (!mesh) { - return QScriptValue(); - } - - auto positions = mesh->getVertexBuffer(); - auto indices = mesh->getIndexBuffer(); - quint32 numPoints = (quint32)indices.getNumElements(); - auto buffer = new gpu::Buffer(); - buffer->resize(numPoints * sizeof(uint32_t)); - auto newindices = gpu::BufferView(buffer, { gpu::SCALAR, gpu::UINT32, gpu::INDEX }); - qCInfo(model_scripting) << "ModelScriptingInterface::unrollVertices numPoints" << numPoints; - auto attributeViews = ScriptableMesh::gatherBufferViews(mesh); - for (const auto& a : attributeViews) { - auto& view = a.second; - auto sz = view._element.getSize(); - auto buffer = new gpu::Buffer(); - buffer->resize(numPoints * sz); - auto points = gpu::BufferView(buffer, view._element); - auto src = (uint8_t*)view._buffer->getData(); - auto dest = (uint8_t*)points._buffer->getData(); - auto slot = ScriptableMesh::ATTRIBUTES[a.first]; - qCInfo(model_scripting) << "ModelScriptingInterface::unrollVertices buffer" << a.first; - qCInfo(model_scripting) << "ModelScriptingInterface::unrollVertices source" << view.getNumElements(); - qCInfo(model_scripting) << "ModelScriptingInterface::unrollVertices dest" << points.getNumElements(); - qCInfo(model_scripting) << "ModelScriptingInterface::unrollVertices sz" << sz << src << dest << slot; - auto esize = indices._element.getSize(); - const char* hint= a.first.toStdString().c_str(); - for(quint32 i = 0; i < numPoints; i++) { - quint32 index = esize == 4 ? indices.get(i) : indices.get(i); - newindices.edit(i) = i; - bufferViewElementFromVariant( - points, i, - bufferViewElementToVariant(view, index, false, hint) - ); - } - if (slot == gpu::Stream::POSITION) { - mesh->setVertexBuffer(points); - } else { - mesh->addAttribute(slot, points); - } - } - mesh->setIndexBuffer(newindices); - if (recalcNormals) { - recalculateNormals(meshProxy); - } - return true; -} - namespace { - template - gpu::BufferView bufferViewFromVector(QVector elements, gpu::Element elementType) { - auto vertexBuffer = std::make_shared( - elements.size() * sizeof(T), - (gpu::Byte*)elements.data() - ); - return { vertexBuffer, 0, vertexBuffer->getSize(),sizeof(T), elementType }; - } - - gpu::BufferView cloneBufferView(const gpu::BufferView& input) { - //qCInfo(model_scripting) << "input" << input.getNumElements() << input._buffer->getSize(); - auto output = gpu::BufferView( - std::make_shared(input._buffer->getSize(), input._buffer->getData()), - input._offset, - input._size, - input._stride, - input._element - ); - //qCInfo(model_scripting) << "after" << output.getNumElements() << output._buffer->getSize(); - return output; - } - - gpu::BufferView resizedBufferView(const gpu::BufferView& input, quint32 numElements) { - auto effectiveSize = input._buffer->getSize() / input.getNumElements(); - qCInfo(model_scripting) << "resize input" << input.getNumElements() << input._buffer->getSize() << "effectiveSize" << effectiveSize; - auto vsize = input._element.getSize() * numElements; - gpu::Byte *data = new gpu::Byte[vsize]; - memset(data, 0, vsize); - auto buffer = new gpu::Buffer(vsize, (gpu::Byte*)data); - delete[] data; - auto output = gpu::BufferView(buffer, input._element); - qCInfo(model_scripting) << "resized output" << output.getNumElements() << output._buffer->getSize(); - return output; - } -} - -bool ModelScriptingInterface::replaceMeshData(scriptable::ScriptableMeshPointer dest, scriptable::ScriptableMeshPointer src, const QVector& attributeNames) { - auto target = getMeshPointer(dest); - auto source = getMeshPointer(src); - if (!target || !source) { - context()->throwError("ModelScriptingInterface::replaceMeshData -- expected dest and src to be valid mesh proxy pointers"); - return false; - } - - QVector attributes = attributeNames.isEmpty() ? src->getAttributeNames() : attributeNames; - - //qCInfo(model_scripting) << "ModelScriptingInterface::replaceMeshData -- source:" << source->displayName << "target:" << target->displayName << "attributes:" << attributes; - - // remove attributes only found on target mesh, unless user has explicitly specified the relevant attribute names - if (attributeNames.isEmpty()) { - auto attributeViews = ScriptableMesh::gatherBufferViews(target); - for (const auto& a : attributeViews) { - auto slot = ScriptableMesh::ATTRIBUTES[a.first]; - if (!attributes.contains(a.first)) { - //qCInfo(model_scripting) << "ModelScriptingInterface::replaceMeshData -- pruning target attribute" << a.first << slot; - target->removeAttribute(slot); - } + QScriptValue meshPointerToScriptValue(QScriptEngine* engine, scriptable::ScriptableMeshPointer const &in) { + if (!in) { + return QScriptValue::NullValue; } + return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); } - target->setVertexBuffer(cloneBufferView(source->getVertexBuffer())); - target->setIndexBuffer(cloneBufferView(source->getIndexBuffer())); - target->setPartBuffer(cloneBufferView(source->getPartBuffer())); - - for (const auto& a : attributes) { - auto slot = ScriptableMesh::ATTRIBUTES[a]; - if (slot == gpu::Stream::POSITION) { - continue; - } - // auto& before = target->getAttributeBuffer(slot); - auto& input = source->getAttributeBuffer(slot); - if (input.getNumElements() == 0) { - //qCInfo(model_scripting) << "ModelScriptingInterface::replaceMeshData buffer is empty -- pruning" << a << slot; - target->removeAttribute(slot); - } else { - // if (before.getNumElements() == 0) { - // qCInfo(model_scripting) << "ModelScriptingInterface::replaceMeshData target buffer is empty -- adding" << a << slot; - // } else { - // qCInfo(model_scripting) << "ModelScriptingInterface::replaceMeshData target buffer exists -- updating" << a << slot; - // } - target->addAttribute(slot, cloneBufferView(input)); - } - // auto& after = target->getAttributeBuffer(slot); - // qCInfo(model_scripting) << "ModelScriptingInterface::replaceMeshData" << a << slot << before.getNumElements() << " -> " << after.getNumElements(); - } - - - return true; -} - -bool ModelScriptingInterface::dedupeVertices(scriptable::ScriptableMeshPointer meshProxy, float epsilon) { - auto mesh = getMeshPointer(meshProxy); - if (!mesh) { - return false; - } - auto positions = mesh->getVertexBuffer(); - auto numPositions = positions.getNumElements(); - const auto epsilon2 = epsilon*epsilon; - - QVector uniqueVerts; - uniqueVerts.reserve((int)numPositions); - QMap remapIndices; - - for (quint32 i = 0; i < numPositions; i++) { - const quint32 numUnique = uniqueVerts.size(); - const auto& position = positions.get(i); - bool unique = true; - for (quint32 j = 0; j < numUnique; j++) { - if (glm::length2(uniqueVerts[j] - position) <= epsilon2) { - remapIndices[i] = j; - unique = false; - break; - } - } - if (unique) { - uniqueVerts << position; - remapIndices[i] = numUnique; - } - } - - qCInfo(model_scripting) << "//VERTS before" << numPositions << "after" << uniqueVerts.size(); - - auto indices = mesh->getIndexBuffer(); - auto numIndices = indices.getNumElements(); - auto esize = indices._element.getSize(); - QVector newIndices; - newIndices.reserve((int)numIndices); - for (quint32 i = 0; i < numIndices; i++) { - quint32 index = esize == 4 ? indices.get(i) : indices.get(i); - if (remapIndices.contains(index)) { - //qCInfo(model_scripting) << i << index << "->" << remapIndices[index]; - newIndices << remapIndices[index]; - } else { - qCInfo(model_scripting) << i << index << "!remapIndices[index]"; - } - } - - mesh->setIndexBuffer(bufferViewFromVector(newIndices, { gpu::SCALAR, gpu::UINT32, gpu::INDEX })); - mesh->setVertexBuffer(bufferViewFromVector(uniqueVerts, { gpu::VEC3, gpu::FLOAT, gpu::XYZ })); - - auto attributeViews = ScriptableMesh::gatherBufferViews(mesh); - quint32 numUniqueVerts = uniqueVerts.size(); - for (const auto& a : attributeViews) { - auto& view = a.second; - auto slot = ScriptableMesh::ATTRIBUTES[a.first]; - if (slot == gpu::Stream::POSITION) { - continue; - } - qCInfo(model_scripting) << "ModelScriptingInterface::dedupeVertices" << a.first << slot << view.getNumElements(); - auto newView = resizedBufferView(view, numUniqueVerts); - qCInfo(model_scripting) << a.first << "before: #" << view.getNumElements() << "after: #" << newView.getNumElements(); - quint32 numElements = (quint32)view.getNumElements(); - for (quint32 i = 0; i < numElements; i++) { - quint32 fromVertexIndex = i; - quint32 toVertexIndex = remapIndices.contains(fromVertexIndex) ? remapIndices[fromVertexIndex] : fromVertexIndex; - bufferViewElementFromVariant( - newView, toVertexIndex, - bufferViewElementToVariant(view, fromVertexIndex, false, "dedupe") - ); - } - mesh->addAttribute(slot, newView); - } - return true; -} - -QScriptValue ModelScriptingInterface::cloneMesh(scriptable::ScriptableMeshPointer meshProxy, bool recalcNormals) { - auto mesh = getMeshPointer(meshProxy); - if (!mesh) { - return QScriptValue::NullValue; - } - graphics::MeshPointer clone(new graphics::Mesh()); - clone->displayName = mesh->displayName + "-clone"; - qCInfo(model_scripting) << "ModelScriptingInterface::cloneMesh" << !!mesh<< !!meshProxy; - if (!mesh) { - return QScriptValue::NullValue; - } - - clone->setIndexBuffer(cloneBufferView(mesh->getIndexBuffer())); - clone->setPartBuffer(cloneBufferView(mesh->getPartBuffer())); - auto attributeViews = ScriptableMesh::gatherBufferViews(mesh); - for (const auto& a : attributeViews) { - auto& view = a.second; - auto slot = ScriptableMesh::ATTRIBUTES[a.first]; - qCInfo(model_scripting) << "ModelScriptingInterface::cloneVertices buffer" << a.first << slot; - auto points = cloneBufferView(view); - qCInfo(model_scripting) << "ModelScriptingInterface::cloneVertices source" << view.getNumElements(); - qCInfo(model_scripting) << "ModelScriptingInterface::cloneVertices dest" << points.getNumElements(); - if (slot == gpu::Stream::POSITION) { - clone->setVertexBuffer(points); - } else { - clone->addAttribute(slot, points); - } - } - - auto result = scriptable::ScriptableMeshPointer(new ScriptableMesh(nullptr, clone)); - if (recalcNormals) { - recalculateNormals(result); - } - return engine()->toScriptValue(result); -} - -bool ModelScriptingInterface::recalculateNormals(scriptable::ScriptableMeshPointer meshProxy) { - qCInfo(model_scripting) << "Recalculating normals" << !!meshProxy; - auto mesh = getMeshPointer(meshProxy); - if (!mesh) { - return false; - } - ScriptableMesh::gatherBufferViews(mesh, { "normal", "color" }); // ensures #normals >= #positions - auto normals = mesh->getAttributeBuffer(gpu::Stream::NORMAL); - auto verts = mesh->getVertexBuffer(); - auto indices = mesh->getIndexBuffer(); - auto esize = indices._element.getSize(); - auto numPoints = indices.getNumElements(); - const auto TRIANGLE = 3; - quint32 numFaces = (quint32)numPoints / TRIANGLE; - //QVector faces; - QVector faceNormals; - QMap> vertexToFaces; - //faces.resize(numFaces); - faceNormals.resize(numFaces); - auto numNormals = normals.getNumElements(); - qCInfo(model_scripting) << QString("numFaces: %1, numNormals: %2, numPoints: %3").arg(numFaces).arg(numNormals).arg(numPoints); - if (normals.getNumElements() != verts.getNumElements()) { - return false; - } - for (quint32 i = 0; i < numFaces; i++) { - quint32 I = TRIANGLE * i; - quint32 i0 = esize == 4 ? indices.get(I+0) : indices.get(I+0); - quint32 i1 = esize == 4 ? indices.get(I+1) : indices.get(I+1); - quint32 i2 = esize == 4 ? indices.get(I+2) : indices.get(I+2); - - Triangle face = { - verts.get(i1), - verts.get(i2), - verts.get(i0) - }; - faceNormals[i] = face.getNormal(); - if (glm::isnan(faceNormals[i].x)) { - qCInfo(model_scripting) << i << i0 << i1 << i2 << vec3toVariant(face.v0) << vec3toVariant(face.v1) << vec3toVariant(face.v2); - break; - } - vertexToFaces[glm::to_string(face.v0).c_str()] << i; - vertexToFaces[glm::to_string(face.v1).c_str()] << i; - vertexToFaces[glm::to_string(face.v2).c_str()] << i; - } - for (quint32 j = 0; j < numNormals; j++) { - //auto v = verts.get(j); - glm::vec3 normal { 0.0f, 0.0f, 0.0f }; - QString key { glm::to_string(verts.get(j)).c_str() }; - const auto& faces = vertexToFaces.value(key); - if (faces.size()) { - for (const auto i : faces) { - normal += faceNormals[i]; - } - normal *= 1.0f / (float)faces.size(); - } else { - static int logged = 0; - if (logged++ < 10) { - qCInfo(model_scripting) << "no faces for key!?" << key; - } - normal = verts.get(j); - } - if (glm::isnan(normal.x)) { - static int logged = 0; - if (logged++ < 10) { - qCInfo(model_scripting) << "isnan(normal.x)" << j << vec3toVariant(normal); - } - break; - } - normals.edit(j) = glm::normalize(normal); - } - return true; -} - -QScriptValue ModelScriptingInterface::mapMeshAttributeValues( - scriptable::ScriptableMeshPointer meshProxy, QScriptValue scopeOrCallback, QScriptValue methodOrName -) { - auto mesh = getMeshPointer(meshProxy); - if (!mesh) { - return false; - } - auto scopedHandler = makeScopedHandlerObject(scopeOrCallback, methodOrName); - - // input buffers - gpu::BufferView positions = mesh->getVertexBuffer(); - - const auto nPositions = positions.getNumElements(); - - // destructure so we can still invoke callback scoped, but with a custom signature (obj, i, jsMesh) - auto scope = scopedHandler.property("scope"); - auto callback = scopedHandler.property("callback"); - auto js = engine(); // cache value to avoid resolving each iteration - auto meshPart = js->toScriptValue(meshProxy); - - auto obj = js->newObject(); - auto attributeViews = ScriptableMesh::gatherBufferViews(mesh, { "normal", "color" }); - for (uint32_t i=0; i < nPositions; i++) { - for (const auto& a : attributeViews) { - bool asArray = a.second._element.getType() != gpu::FLOAT; - obj.setProperty(a.first, bufferViewElementToScriptValue(js, a.second, i, asArray, a.first.toStdString().c_str())); - } - auto result = callback.call(scope, { obj, i, meshPart }); - if (js->hasUncaughtException()) { - context()->throwValue(js->uncaughtException()); - return false; - } - - if (result.isBool() && !result.toBool()) { - // bail without modifying data if user explicitly returns false - continue; - } - if (result.isObject() && !result.strictlyEquals(obj)) { - // user returned a new object (ie: instead of modifying input properties) - obj = result; - } - - for (const auto& a : attributeViews) { - const auto& attribute = obj.property(a.first); - auto& view = a.second; - if (attribute.isValid()) { - bufferViewElementFromScriptValue(attribute, view, i); - } - } - } - return thisObject(); -} - -void ModelScriptingInterface::getMeshes(QUuid uuid, QScriptValue scopeOrCallback, QScriptValue methodOrName) { - auto handler = makeScopedHandlerObject(scopeOrCallback, methodOrName); - Q_ASSERT(handler.engine() == this->engine()); - QPointer engine = dynamic_cast(handler.engine()); - - scriptable::ScriptableModel meshes; - bool success = false; - QString error; - - auto appProvider = DependencyManager::get(); - qDebug() << "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(model_scripting) << "fetching meshes from " << providerType << "..."; - auto scriptableMeshes = provider->getScriptableModel(&success); - qCDebug(model_scripting) << "//fetched meshes from " << providerType << "success:" <makeError(error), QScriptValue::NullValue); - } else { - callScopedHandlerObject(handler, QScriptValue::NullValue, engine->toScriptValue(meshes)); - } -} - -namespace { - QScriptValue meshToScriptValue(QScriptEngine* engine, scriptable::ScriptableMeshPointer const &in) { - return engine->newQObject(in.get(), QScriptEngine::QtOwnership, - QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects - ); - } - - void meshFromScriptValue(const QScriptValue& value, scriptable::ScriptableMeshPointer &out) { + void meshPointerFromScriptValue(const QScriptValue& value, scriptable::ScriptableMeshPointer &out) { auto obj = value.toQObject(); - //qDebug() << "meshFromScriptValue" << obj; + qDebug() << "meshPointerFromScriptValue" << obj; if (auto tmp = qobject_cast(obj)) { - out = tmp->shared_from_this(); + out = tmp; } // FIXME: Why does above cast not work on Win32!? if (!out) { - auto smp = static_cast(obj); - //qDebug() << "meshFromScriptValue2" << smp; - out = smp->shared_from_this(); + if (auto smp = static_cast(obj)) { + qDebug() << "meshPointerFromScriptValue2" << smp; + out = smp; + } } } - QScriptValue meshesToScriptValue(QScriptEngine* engine, const scriptable::ScriptableModelPointer &in) { - // QScriptValueList result; - QScriptValue result = engine->newArray(); - int i = 0; - foreach(scriptable::ScriptableMeshPointer const meshProxy, in->getMeshes()) { - result.setProperty(i++, meshToScriptValue(engine, meshProxy)); - } - return result; + QScriptValue modelPointerToScriptValue(QScriptEngine* engine, const scriptable::ScriptableModelPointer &in) { + return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); + // QScriptValue result = engine->newArray(); + // int i = 0; + // foreach(auto& mesh, in->getMeshes()) { + // result.setProperty(i++, meshPointerToScriptValue(engine, mesh)); + // } + // return result; } - void meshesFromScriptValue(const QScriptValue& value, scriptable::ScriptableModelPointer &out) { + void modelPointerFromScriptValue(const QScriptValue& value, scriptable::ScriptableModelPointer &out) { const auto length = value.property("length").toInt32(); - qCDebug(model_scripting) << "in meshesFromScriptValue, length =" << length; + qCDebug(model_scripting) << "in modelPointerFromScriptValue, length =" << length; for (int i = 0; i < length; i++) { if (const auto meshProxy = qobject_cast(value.property(i).toQObject())) { out->meshes.append(meshProxy->getMeshPointer()); @@ -749,79 +349,64 @@ namespace { } } - void modelProxyFromScriptValue(const QScriptValue& object, scriptable::ScriptableModel &meshes) { - auto meshesProperty = object.property("meshes"); - if (meshesProperty.property("length").toInt32() > 0) { - //meshes._meshes = qobject_cast(meshesProperty.toQObject()); - // qDebug() << "modelProxyFromScriptValue" << meshesProperty.property("length").toInt32() << meshesProperty.toVariant().typeName(); - qScriptValueToSequence(meshesProperty, meshes.meshes); - } else if (auto mesh = qobject_cast(object.toQObject())) { - meshes.meshes << mesh->getMeshPointer(); - } else { - qDebug() << "modelProxyFromScriptValue -- unrecognized input" << object.toVariant().toString(); - } + // 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); + // } - meshes.metadata = object.property("metadata").toVariant().toMap(); - } - - QScriptValue modelProxyToScriptValue(QScriptEngine* engine, const scriptable::ScriptableModel &in) { - QScriptValue obj = engine->newObject(); - obj.setProperty("meshes", qScriptValueFromSequence(engine, in.meshes)); - obj.setProperty("metadata", engine->toScriptValue(in.metadata)); - return obj; - } - - 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) { + QScriptValue qVectorUInt32ToScriptValue(QScriptEngine* engine, const QVector& vector) { return qScriptValueFromSequence(engine, vector); } - void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector& result) { - qScriptValueToSequence(array, result); - } - - QScriptValue qVectorUInt32ToScriptValue(QScriptEngine* engine, const QVector& vector) { - return qScriptValueFromSequence(engine, vector); - } - - void qVectorUInt32FromScriptValue(const QScriptValue& array, QVector& result) { + void qVectorUInt32FromScriptValue(const QScriptValue& array, QVector& result) { qScriptValueToSequence(array, result); } } -int meshUint32 = qRegisterMetaType(); +int meshUint32 = qRegisterMetaType(); namespace mesh { int meshUint32 = qRegisterMetaType(); } -int qVectorMeshUint32 = qRegisterMetaType>(); +int qVectorMeshUint32 = qRegisterMetaType>(); void ModelScriptingInterface::registerMetaTypes(QScriptEngine* engine) { qScriptRegisterSequenceMetaType>(engine); - qScriptRegisterSequenceMetaType(engine); - qScriptRegisterSequenceMetaType>(engine); - qScriptRegisterMetaType(engine, modelProxyToScriptValue, modelProxyFromScriptValue); + qScriptRegisterSequenceMetaType>(engine); qScriptRegisterMetaType(engine, qVectorUInt32ToScriptValue, qVectorUInt32FromScriptValue); - qScriptRegisterMetaType(engine, meshToScriptValue, meshFromScriptValue); - qScriptRegisterMetaType(engine, meshesToScriptValue, meshesFromScriptValue); - qScriptRegisterMetaType(engine, meshFaceToScriptValue, meshFaceFromScriptValue); - qScriptRegisterMetaType(engine, qVectorMeshFaceToScriptValue, qVectorMeshFaceFromScriptValue); + qScriptRegisterMetaType(engine, meshPointerToScriptValue, meshPointerFromScriptValue); + qScriptRegisterMetaType(engine, modelPointerToScriptValue, modelPointerFromScriptValue); + + // 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._mesh;//getMeshPointer(&meshProxy); +} +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 { + qDebug() << "expected meshProxy as first parameter"; } return result; } @@ -829,6 +414,8 @@ MeshPointer ModelScriptingInterface::getMeshPointer(scriptable::ScriptableMeshPo if (!mesh) { if (context()) { context()->throwError("expected valid meshProxy as first parameter"); + } else { + qDebug() << "expected valid meshProxy as first parameter"; } return result; } diff --git a/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.h b/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.h index d10fd28170..eac4df3216 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.h @@ -38,30 +38,20 @@ public slots: */ void getMeshes(QUuid uuid, QScriptValue scopeOrCallback, QScriptValue methodOrName = QScriptValue()); - bool dedupeVertices(scriptable::ScriptableMeshPointer meshProxy, float epsilon = 1e-6); - bool recalculateNormals(scriptable::ScriptableMeshPointer meshProxy); - QScriptValue cloneMesh(scriptable::ScriptableMeshPointer meshProxy, bool recalcNormals = true); - QScriptValue unrollVertices(scriptable::ScriptableMeshPointer meshProxy, bool recalcNormals = true); - QScriptValue mapAttributeValues(QScriptValue in, - QScriptValue scopeOrCallback, - QScriptValue methodOrName = QScriptValue()); - QScriptValue mapMeshAttributeValues(scriptable::ScriptableMeshPointer meshProxy, - QScriptValue scopeOrCallback, - QScriptValue methodOrName = QScriptValue()); - QString meshToOBJ(const scriptable::ScriptableModel& in); - bool replaceMeshData(scriptable::ScriptableMeshPointer dest, scriptable::ScriptableMeshPointer source, const QVector& attributeNames = QVector()); 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, mesh::uint32 vertexIndex); + QScriptValue getVertex(scriptable::ScriptableMeshPointer meshProxy, quint32 vertexIndex); private: scriptable::MeshPointer getMeshPointer(scriptable::ScriptableMeshPointer meshProxy); + scriptable::MeshPointer getMeshPointer(scriptable::ScriptableMesh& meshProxy); + scriptable::MeshPointer getMeshPointer(const scriptable::ScriptableMesh& meshProxy); }; diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp index 47d91e9e59..1b16a6d263 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp @@ -14,14 +14,20 @@ #include #include #include +#include #include #include #include +#include #include #include "ScriptableMesh.moc" #include +#include +#include + +#include "OBJWriter.h" QLoggingCategory mesh_logging { "hifi.scripting.mesh" }; @@ -41,10 +47,26 @@ QMap ScriptableMesh::ATTRIBUTES{ {"texcoord4", gpu::Stream::TEXCOORD4 }, }; -QVector scriptable::ScriptableModel::getMeshes() const { + +QString scriptable::ScriptableModel::toString() const { + return QString("[ScriptableModel%1%2]") + .arg(objectID.isNull() ? "" : " objectID="+objectID.toString()) + .arg(objectName().isEmpty() ? "" : " name=" +objectName()); +} + +const QVector scriptable::ScriptableModel::getConstMeshes() const { + QVector out; + for(const auto& mesh : meshes) { + const scriptable::ScriptableMeshPointer m = scriptable::ScriptableMeshPointer(new scriptable::ScriptableMesh(const_cast(this), mesh)); + out << m; + } + return out; +} +QVector scriptable::ScriptableModel::getMeshes() { QVector out; for(auto& mesh : meshes) { - out << scriptable::ScriptableMeshPointer(new ScriptableMesh(std::const_pointer_cast(this->shared_from_this()), mesh)); + scriptable::ScriptableMeshPointer m{new scriptable::ScriptableMesh(this, mesh)}; + out << m; } return out; } @@ -134,15 +156,16 @@ QVariantMap ScriptableMesh::getVertexAttributes(quint32 vertexIndex) const { } bool ScriptableMesh::setVertexAttributes(quint32 vertexIndex, QVariantMap attributes) { - qDebug() << "setVertexAttributes" << vertexIndex << attributes; + //qDebug() << "setVertexAttributes" << vertexIndex << attributes; for (auto& a : gatherBufferViews(getMeshPointer())) { const auto& name = a.first; const auto& value = attributes.value(name); if (value.isValid()) { auto& view = a.second; + //qCDebug(mesh_logging) << "setVertexAttributes" << vertexIndex << name; bufferViewElementFromVariant(view, vertexIndex, value); } else { - qCDebug(mesh_logging) << "setVertexAttributes" << vertexIndex << name; + //qCDebug(mesh_logging) << "(skipping) setVertexAttributes" << vertexIndex << name; } } return true; @@ -357,3 +380,375 @@ std::map ScriptableMesh::gatherBufferViews(scriptable: } return attributeViews; } + +QScriptValue ScriptableModel::mapAttributeValues(QScriptValue scopeOrCallback, QScriptValue methodOrName) { + auto context = scopeOrCallback.engine()->currentContext(); + auto _in = context->thisObject(); + qCInfo(mesh_logging) << "mapAttributeValues" << _in.toVariant().typeName() << _in.toVariant().toString() << _in.toQObject(); + auto model = qscriptvalue_cast(_in); + QVector in = model.getMeshes(); + if (in.size()) { + foreach (scriptable::ScriptableMeshPointer meshProxy, in) { + meshProxy->mapAttributeValues(scopeOrCallback, methodOrName); + } + return _in; + } else if (auto meshProxy = qobject_cast(_in.toQObject())) { + return meshProxy->mapAttributeValues(scopeOrCallback, methodOrName); + } else { + context->throwError("invalid ModelProxy || MeshProxyPointer"); + } + return false; +} + + + +QScriptValue ScriptableMesh::mapAttributeValues(QScriptValue scopeOrCallback, QScriptValue methodOrName) { + auto mesh = getMeshPointer(); + if (!mesh) { + return false; + } + auto scopedHandler = makeScopedHandlerObject(scopeOrCallback, methodOrName); + + // input buffers + gpu::BufferView positions = mesh->getVertexBuffer(); + + const auto nPositions = positions.getNumElements(); + + // destructure so we can still invoke callback scoped, but with a custom signature (obj, i, jsMesh) + auto scope = scopedHandler.property("scope"); + auto callback = scopedHandler.property("callback"); + auto js = engine(); // cache value to avoid resolving each iteration + auto meshPart = thisObject();//js->toScriptValue(meshProxy); + + auto obj = js->newObject(); + auto attributeViews = ScriptableMesh::gatherBufferViews(mesh, { "normal", "color" }); + for (uint32_t i=0; i < nPositions; i++) { + for (const auto& a : attributeViews) { + bool asArray = a.second._element.getType() != gpu::FLOAT; + obj.setProperty(a.first, bufferViewElementToScriptValue(js, a.second, i, asArray, a.first.toStdString().c_str())); + } + auto result = callback.call(scope, { obj, i, meshPart }); + if (js->hasUncaughtException()) { + context()->throwValue(js->uncaughtException()); + return false; + } + + if (result.isBool() && !result.toBool()) { + // bail without modifying data if user explicitly returns false + continue; + } + if (result.isObject() && !result.strictlyEquals(obj)) { + // user returned a new object (ie: instead of modifying input properties) + obj = result; + } + + for (const auto& a : attributeViews) { + const auto& attribute = obj.property(a.first); + auto& view = a.second; + if (attribute.isValid()) { + bufferViewElementFromScriptValue(attribute, view, i); + } + } + } + return thisObject(); +} + +QScriptValue ScriptableMesh::unrollVertices(bool recalcNormals) { + auto meshProxy = this; + auto mesh = getMeshPointer(); + qCInfo(mesh_logging) << "ScriptableMesh::unrollVertices" << !!mesh<< !!meshProxy; + if (!mesh) { + return QScriptValue(); + } + + auto positions = mesh->getVertexBuffer(); + auto indices = mesh->getIndexBuffer(); + quint32 numPoints = (quint32)indices.getNumElements(); + auto buffer = new gpu::Buffer(); + buffer->resize(numPoints * sizeof(uint32_t)); + auto newindices = gpu::BufferView(buffer, { gpu::SCALAR, gpu::UINT32, gpu::INDEX }); + qCInfo(mesh_logging) << "ScriptableMesh::unrollVertices numPoints" << numPoints; + auto attributeViews = ScriptableMesh::gatherBufferViews(mesh); + for (const auto& a : attributeViews) { + auto& view = a.second; + auto sz = view._element.getSize(); + auto buffer = new gpu::Buffer(); + buffer->resize(numPoints * sz); + auto points = gpu::BufferView(buffer, view._element); + auto src = (uint8_t*)view._buffer->getData(); + auto dest = (uint8_t*)points._buffer->getData(); + auto slot = ScriptableMesh::ATTRIBUTES[a.first]; + qCInfo(mesh_logging) << "ScriptableMesh::unrollVertices buffer" << a.first; + qCInfo(mesh_logging) << "ScriptableMesh::unrollVertices source" << view.getNumElements(); + qCInfo(mesh_logging) << "ScriptableMesh::unrollVertices dest" << points.getNumElements(); + qCInfo(mesh_logging) << "ScriptableMesh::unrollVertices sz" << sz << src << dest << slot; + auto esize = indices._element.getSize(); + const char* hint= a.first.toStdString().c_str(); + for(quint32 i = 0; i < numPoints; i++) { + quint32 index = esize == 4 ? indices.get(i) : indices.get(i); + newindices.edit(i) = i; + bufferViewElementFromVariant( + points, i, + bufferViewElementToVariant(view, index, false, hint) + ); + } + if (slot == gpu::Stream::POSITION) { + mesh->setVertexBuffer(points); + } else { + mesh->addAttribute(slot, points); + } + } + mesh->setIndexBuffer(newindices); + if (recalcNormals) { + recalculateNormals(); + } + return true; +} + +bool ScriptableMesh::replaceMeshData(scriptable::ScriptableMeshPointer src, const QVector& attributeNames) { + auto target = getMeshPointer(); + auto source = src ? src->getMeshPointer() : nullptr; + if (!target || !source) { + context()->throwError("ScriptableMesh::replaceMeshData -- expected dest and src to be valid mesh proxy pointers"); + return false; + } + + QVector attributes = attributeNames.isEmpty() ? src->getAttributeNames() : attributeNames; + + //qCInfo(mesh_logging) << "ScriptableMesh::replaceMeshData -- source:" << source->displayName << "target:" << target->displayName << "attributes:" << attributes; + + // remove attributes only found on target mesh, unless user has explicitly specified the relevant attribute names + if (attributeNames.isEmpty()) { + auto attributeViews = ScriptableMesh::gatherBufferViews(target); + for (const auto& a : attributeViews) { + auto slot = ScriptableMesh::ATTRIBUTES[a.first]; + if (!attributes.contains(a.first)) { + //qCInfo(mesh_logging) << "ScriptableMesh::replaceMeshData -- pruning target attribute" << a.first << slot; + target->removeAttribute(slot); + } + } + } + + target->setVertexBuffer(cloneBufferView(source->getVertexBuffer())); + target->setIndexBuffer(cloneBufferView(source->getIndexBuffer())); + target->setPartBuffer(cloneBufferView(source->getPartBuffer())); + + for (const auto& a : attributes) { + auto slot = ScriptableMesh::ATTRIBUTES[a]; + if (slot == gpu::Stream::POSITION) { + continue; + } + // auto& before = target->getAttributeBuffer(slot); + auto& input = source->getAttributeBuffer(slot); + if (input.getNumElements() == 0) { + //qCInfo(mesh_logging) << "ScriptableMesh::replaceMeshData buffer is empty -- pruning" << a << slot; + target->removeAttribute(slot); + } else { + // if (before.getNumElements() == 0) { + // qCInfo(mesh_logging) << "ScriptableMesh::replaceMeshData target buffer is empty -- adding" << a << slot; + // } else { + // qCInfo(mesh_logging) << "ScriptableMesh::replaceMeshData target buffer exists -- updating" << a << slot; + // } + target->addAttribute(slot, cloneBufferView(input)); + } + // auto& after = target->getAttributeBuffer(slot); + // qCInfo(mesh_logging) << "ScriptableMesh::replaceMeshData" << a << slot << before.getNumElements() << " -> " << after.getNumElements(); + } + + + return true; +} + +bool ScriptableMesh::dedupeVertices(float epsilon) { + scriptable::ScriptableMeshPointer meshProxy = this; + auto mesh = getMeshPointer(); + if (!mesh) { + return false; + } + auto positions = mesh->getVertexBuffer(); + auto numPositions = positions.getNumElements(); + const auto epsilon2 = epsilon*epsilon; + + QVector uniqueVerts; + uniqueVerts.reserve((int)numPositions); + QMap remapIndices; + + for (quint32 i = 0; i < numPositions; i++) { + const quint32 numUnique = uniqueVerts.size(); + const auto& position = positions.get(i); + bool unique = true; + for (quint32 j = 0; j < numUnique; j++) { + if (glm::length2(uniqueVerts[j] - position) <= epsilon2) { + remapIndices[i] = j; + unique = false; + break; + } + } + if (unique) { + uniqueVerts << position; + remapIndices[i] = numUnique; + } + } + + qCInfo(mesh_logging) << "//VERTS before" << numPositions << "after" << uniqueVerts.size(); + + auto indices = mesh->getIndexBuffer(); + auto numIndices = indices.getNumElements(); + auto esize = indices._element.getSize(); + QVector newIndices; + newIndices.reserve((int)numIndices); + for (quint32 i = 0; i < numIndices; i++) { + quint32 index = esize == 4 ? indices.get(i) : indices.get(i); + if (remapIndices.contains(index)) { + //qCInfo(mesh_logging) << i << index << "->" << remapIndices[index]; + newIndices << remapIndices[index]; + } else { + qCInfo(mesh_logging) << i << index << "!remapIndices[index]"; + } + } + + mesh->setIndexBuffer(bufferViewFromVector(newIndices, { gpu::SCALAR, gpu::UINT32, gpu::INDEX })); + mesh->setVertexBuffer(bufferViewFromVector(uniqueVerts, { gpu::VEC3, gpu::FLOAT, gpu::XYZ })); + + auto attributeViews = ScriptableMesh::gatherBufferViews(mesh); + quint32 numUniqueVerts = uniqueVerts.size(); + for (const auto& a : attributeViews) { + auto& view = a.second; + auto slot = ScriptableMesh::ATTRIBUTES[a.first]; + if (slot == gpu::Stream::POSITION) { + continue; + } + qCInfo(mesh_logging) << "ScriptableMesh::dedupeVertices" << a.first << slot << view.getNumElements(); + auto newView = resizedBufferView(view, numUniqueVerts); + qCInfo(mesh_logging) << a.first << "before: #" << view.getNumElements() << "after: #" << newView.getNumElements(); + quint32 numElements = (quint32)view.getNumElements(); + for (quint32 i = 0; i < numElements; i++) { + quint32 fromVertexIndex = i; + quint32 toVertexIndex = remapIndices.contains(fromVertexIndex) ? remapIndices[fromVertexIndex] : fromVertexIndex; + bufferViewElementFromVariant( + newView, toVertexIndex, + bufferViewElementToVariant(view, fromVertexIndex, false, "dedupe") + ); + } + mesh->addAttribute(slot, newView); + } + return true; +} + +QScriptValue ScriptableMesh::cloneMesh(bool recalcNormals) { + auto mesh = getMeshPointer(); + if (!mesh) { + return QScriptValue::NullValue; + } + graphics::MeshPointer clone(new graphics::Mesh()); + clone->displayName = mesh->displayName + "-clone"; + qCInfo(mesh_logging) << "ScriptableMesh::cloneMesh" << !!mesh; + if (!mesh) { + return QScriptValue::NullValue; + } + + clone->setIndexBuffer(cloneBufferView(mesh->getIndexBuffer())); + clone->setPartBuffer(cloneBufferView(mesh->getPartBuffer())); + auto attributeViews = ScriptableMesh::gatherBufferViews(mesh); + for (const auto& a : attributeViews) { + auto& view = a.second; + auto slot = ScriptableMesh::ATTRIBUTES[a.first]; + qCInfo(mesh_logging) << "ScriptableMesh::cloneVertices buffer" << a.first << slot; + auto points = cloneBufferView(view); + qCInfo(mesh_logging) << "ScriptableMesh::cloneVertices source" << view.getNumElements(); + qCInfo(mesh_logging) << "ScriptableMesh::cloneVertices dest" << points.getNumElements(); + if (slot == gpu::Stream::POSITION) { + clone->setVertexBuffer(points); + } else { + clone->addAttribute(slot, points); + } + } + + auto result = scriptable::ScriptableMeshPointer(new ScriptableMesh(nullptr, clone)); + if (recalcNormals) { + result->recalculateNormals(); + } + return engine()->toScriptValue(result); +} + +bool ScriptableMesh::recalculateNormals() { + scriptable::ScriptableMeshPointer meshProxy = this; + qCInfo(mesh_logging) << "Recalculating normals" << !!meshProxy; + auto mesh = getMeshPointer(); + if (!mesh) { + return false; + } + ScriptableMesh::gatherBufferViews(mesh, { "normal", "color" }); // ensures #normals >= #positions + auto normals = mesh->getAttributeBuffer(gpu::Stream::NORMAL); + auto verts = mesh->getVertexBuffer(); + auto indices = mesh->getIndexBuffer(); + auto esize = indices._element.getSize(); + auto numPoints = indices.getNumElements(); + const auto TRIANGLE = 3; + quint32 numFaces = (quint32)numPoints / TRIANGLE; + //QVector faces; + QVector faceNormals; + QMap> vertexToFaces; + //faces.resize(numFaces); + faceNormals.resize(numFaces); + auto numNormals = normals.getNumElements(); + qCInfo(mesh_logging) << QString("numFaces: %1, numNormals: %2, numPoints: %3").arg(numFaces).arg(numNormals).arg(numPoints); + if (normals.getNumElements() != verts.getNumElements()) { + return false; + } + for (quint32 i = 0; i < numFaces; i++) { + quint32 I = TRIANGLE * i; + quint32 i0 = esize == 4 ? indices.get(I+0) : indices.get(I+0); + quint32 i1 = esize == 4 ? indices.get(I+1) : indices.get(I+1); + quint32 i2 = esize == 4 ? indices.get(I+2) : indices.get(I+2); + + Triangle face = { + verts.get(i1), + verts.get(i2), + verts.get(i0) + }; + faceNormals[i] = face.getNormal(); + if (glm::isnan(faceNormals[i].x)) { + qCInfo(mesh_logging) << i << i0 << i1 << i2 << vec3toVariant(face.v0) << vec3toVariant(face.v1) << vec3toVariant(face.v2); + break; + } + vertexToFaces[glm::to_string(face.v0).c_str()] << i; + vertexToFaces[glm::to_string(face.v1).c_str()] << i; + vertexToFaces[glm::to_string(face.v2).c_str()] << i; + } + for (quint32 j = 0; j < numNormals; j++) { + //auto v = verts.get(j); + glm::vec3 normal { 0.0f, 0.0f, 0.0f }; + QString key { glm::to_string(verts.get(j)).c_str() }; + const auto& faces = vertexToFaces.value(key); + if (faces.size()) { + for (const auto i : faces) { + normal += faceNormals[i]; + } + normal *= 1.0f / (float)faces.size(); + } else { + static int logged = 0; + if (logged++ < 10) { + qCInfo(mesh_logging) << "no faces for key!?" << key; + } + normal = verts.get(j); + } + if (glm::isnan(normal.x)) { + static int logged = 0; + if (logged++ < 10) { + qCInfo(mesh_logging) << "isnan(normal.x)" << j << vec3toVariant(normal); + } + break; + } + normals.edit(j) = glm::normalize(normal); + } + return true; +} + +QString ScriptableMesh::toOBJ() { + if (!getMeshPointer()) { + context()->throwError(QString("null mesh")); + } + return writeOBJToString({ getMeshPointer() }); +} + diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h index da11002906..257285fa90 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h @@ -6,12 +6,16 @@ #include #include #include +#include #include #include #include +#include +#include + namespace graphics { class Mesh; } @@ -19,91 +23,112 @@ namespace gpu { class BufferView; } namespace scriptable { - class ScriptableMesh : public QObject, public std::enable_shared_from_this { + class ScriptableMeshPart; + using ScriptableMeshPartPointer = QPointer; + class ScriptableMesh : public QObject, QScriptable { Q_OBJECT public: - ScriptableModelPointer _model; - scriptable::MeshPointer _mesh; - QVariantMap _metadata; - ScriptableMesh() : QObject() {} - ScriptableMesh(ScriptableModelPointer parent, scriptable::MeshPointer mesh) : QObject(), _model(parent), _mesh(mesh) {} - ScriptableMesh(const ScriptableMesh& other) : QObject(), _model(other._model), _mesh(other._mesh), _metadata(other._metadata) {} - ~ScriptableMesh() { qDebug() << "~ScriptableMesh" << this; } Q_PROPERTY(quint32 numParts READ getNumParts) Q_PROPERTY(quint32 numAttributes READ getNumAttributes) Q_PROPERTY(quint32 numVertices READ getNumVertices) Q_PROPERTY(quint32 numIndices READ getNumIndices) + Q_PROPERTY(QVariantMap metadata MEMBER _metadata) Q_PROPERTY(QVector attributeNames READ getAttributeNames) - virtual scriptable::MeshPointer getMeshPointer() const { return _mesh; } - Q_INVOKABLE virtual quint32 getNumParts() const; - Q_INVOKABLE virtual quint32 getNumVertices() const; - Q_INVOKABLE virtual quint32 getNumAttributes() const; - Q_INVOKABLE virtual quint32 getNumIndices() const { return 0; } - Q_INVOKABLE virtual QVector getAttributeNames() const; - Q_INVOKABLE virtual QVariantMap getVertexAttributes(quint32 vertexIndex) const; - Q_INVOKABLE virtual QVariantMap getVertexAttributes(quint32 vertexIndex, QVector attributes) const; - - Q_INVOKABLE virtual QVector getIndices() const; - Q_INVOKABLE virtual QVector findNearbyIndices(const glm::vec3& origin, float epsilon = 1e-6) const; - Q_INVOKABLE virtual QVariantMap getMeshExtents() const; - Q_INVOKABLE virtual bool setVertexAttributes(quint32 vertexIndex, QVariantMap attributes); - Q_INVOKABLE virtual QVariantMap scaleToFit(float unitScale); + static QMap ATTRIBUTES; + static std::map gatherBufferViews(MeshPointer mesh, const QStringList& expandToMatchPositions = QStringList()); - static QMap ATTRIBUTES; - static std::map gatherBufferViews(MeshPointer mesh, const QStringList& expandToMatchPositions = QStringList()); + ScriptableMesh& operator=(const ScriptableMesh& other) { _model=other._model; _mesh=other._mesh; _metadata=other._metadata; return *this; }; + ScriptableMesh() : QObject(), _model(nullptr) {} + ScriptableMesh(ScriptableModelPointer parent, scriptable::MeshPointer mesh) : QObject(), _model(parent), _mesh(mesh) {} + ScriptableMesh(const ScriptableMesh& other) : QObject(), _model(other._model), _mesh(other._mesh), _metadata(other._metadata) {} + ~ScriptableMesh() { qDebug() << "~ScriptableMesh" << this; } - Q_INVOKABLE QVariantList getAttributeValues(const QString& attributeName) const; + scriptable::MeshPointer getMeshPointer() const { return _mesh; } + public slots: + quint32 getNumParts() const; + quint32 getNumVertices() const; + quint32 getNumAttributes() const; + quint32 getNumIndices() const { return 0; } + QVector getAttributeNames() const; - Q_INVOKABLE int _getSlotNumber(const QString& attributeName) const; + QVariantMap getVertexAttributes(quint32 vertexIndex) const; + QVariantMap getVertexAttributes(quint32 vertexIndex, QVector attributes) const; - QVariantMap translate(const glm::vec3& translation); - QVariantMap scale(const glm::vec3& scale, const glm::vec3& origin = glm::vec3(NAN)); - QVariantMap rotateDegrees(const glm::vec3& eulerAngles, const glm::vec3& origin = glm::vec3(NAN)); - QVariantMap rotate(const glm::quat& rotation, const glm::vec3& origin = glm::vec3(NAN)); - Q_INVOKABLE QVariantMap transform(const glm::mat4& transform); + QVector getIndices() const; + QVector findNearbyIndices(const glm::vec3& origin, float epsilon = 1e-6) const; + QVariantMap getMeshExtents() const; + bool setVertexAttributes(quint32 vertexIndex, QVariantMap attributes); + QVariantMap scaleToFit(float unitScale); + + QVariantList getAttributeValues(const QString& attributeName) const; + + int _getSlotNumber(const QString& attributeName) const; + + QVariantMap translate(const glm::vec3& translation); + QVariantMap scale(const glm::vec3& scale, const glm::vec3& origin = glm::vec3(NAN)); + QVariantMap rotateDegrees(const glm::vec3& eulerAngles, const glm::vec3& origin = glm::vec3(NAN)); + QVariantMap rotate(const glm::quat& rotation, const glm::vec3& origin = glm::vec3(NAN)); + QVariantMap transform(const glm::mat4& transform); + + public: + operator bool() const { return _mesh != nullptr; } + ScriptableModelPointer _model; + scriptable::MeshPointer _mesh; + QVariantMap _metadata; + + public slots: + // QScriptEngine-specific wrappers + QScriptValue mapAttributeValues(QScriptValue scopeOrCallback, QScriptValue methodOrName = QScriptValue()); + bool dedupeVertices(float epsilon = 1e-6); + bool recalculateNormals(); + QScriptValue cloneMesh(bool recalcNormals = true); + QScriptValue unrollVertices(bool recalcNormals = true); + bool replaceMeshData(scriptable::ScriptableMeshPointer source, const QVector& attributeNames = QVector()); + QString toOBJ(); }; - // TODO: for now this is a part-specific wrapper around ScriptableMesh - class ScriptableMeshPart : public ScriptableMesh { + // TODO: part-specific wrapper for working with raw geometries + class ScriptableMeshPart : public QObject { Q_OBJECT public: - ScriptableMeshPart& operator=(const ScriptableMeshPart& view) { _model=view._model; _mesh=view._mesh; return *this; }; - ScriptableMeshPart(const ScriptableMeshPart& other) : ScriptableMesh(other._model, other._mesh) {} - ScriptableMeshPart() : ScriptableMesh(nullptr, nullptr) {} - ~ScriptableMeshPart() { qDebug() << "~ScriptableMeshPart" << this; } - ScriptableMeshPart(ScriptableMeshPointer mesh) : ScriptableMesh(mesh->_model, mesh->_mesh) {} Q_PROPERTY(QString topology READ getTopology) Q_PROPERTY(quint32 numFaces READ getNumFaces) - scriptable::MeshPointer parentMesh; - int partIndex; - QString getTopology() const { return "triangles"; } - Q_INVOKABLE virtual quint32 getNumFaces() const { return getIndices().size() / 3; } - Q_INVOKABLE virtual QVector getFace(quint32 faceIndex) const { - auto inds = getIndices(); + ScriptableMeshPart& operator=(const ScriptableMeshPart& view) { parentMesh=view.parentMesh; return *this; }; + ScriptableMeshPart(const ScriptableMeshPart& other) : parentMesh(other.parentMesh) {} + ScriptableMeshPart() {} + ~ScriptableMeshPart() { qDebug() << "~ScriptableMeshPart" << this; } + + public slots: + QString getTopology() const { return "triangles"; } + quint32 getNumFaces() const { return parentMesh.getIndices().size() / 3; } + QVector getFace(quint32 faceIndex) const { + auto inds = parentMesh.getIndices(); return faceIndex+2 < (quint32)inds.size() ? inds.mid(faceIndex*3, 3) : QVector(); } + + public: + scriptable::ScriptableMesh parentMesh; + int partIndex; }; class GraphicsScriptingInterface : public QObject { Q_OBJECT public: GraphicsScriptingInterface(QObject* parent = nullptr) : QObject(parent) {} - GraphicsScriptingInterface(const GraphicsScriptingInterface& other) {} + GraphicsScriptingInterface(const GraphicsScriptingInterface& other) {} public slots: ScriptableMeshPart exportMeshPart(ScriptableMesh mesh, int part) { return {}; } - }; } -Q_DECLARE_METATYPE(scriptable::ScriptableMesh) Q_DECLARE_METATYPE(scriptable::ScriptableMeshPointer) Q_DECLARE_METATYPE(QVector) -Q_DECLARE_METATYPE(scriptable::ScriptableMeshPart) +Q_DECLARE_METATYPE(scriptable::ScriptableMeshPartPointer) Q_DECLARE_METATYPE(scriptable::GraphicsScriptingInterface) -// FIXME: faces were supported in the original Model.* API -- are they still needed/used/useful for anything yet? +// FIXME: MESHFACES: faces were supported in the original Model.* API -- are they still needed/used/useful for anything yet? #include namespace mesh { diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h index e8cf6f1656..4ba5a993b1 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -16,50 +17,53 @@ namespace graphics { namespace gpu { class BufferView; } +class QScriptValue; + namespace scriptable { using Mesh = graphics::Mesh; using MeshPointer = std::shared_ptr; class ScriptableModel; + using ScriptableModelPointer = QPointer; class ScriptableMesh; - class ScriptableMeshPart; - using ScriptableModelPointer = std::shared_ptr; - using ScriptableMeshPointer = std::shared_ptr; - using ScriptableMeshPartPointer = std::shared_ptr; - class ScriptableModel : public QObject, public std::enable_shared_from_this { + using ScriptableMeshPointer = QPointer; + + // abstract container for holding one or more scriptable meshes + class ScriptableModel : public QObject { Q_OBJECT public: - Q_PROPERTY(QVector meshes READ getMeshes) - - Q_INVOKABLE QString toString() { return "[ScriptableModel " + objectName()+"]"; } - ScriptableModel(QObject* parent = nullptr) : QObject(parent) {} - ScriptableModel(const ScriptableModel& other) : objectID(other.objectID), metadata(other.metadata), meshes(other.meshes) {} - ScriptableModel& operator=(const ScriptableModel& view) { - objectID = view.objectID; - metadata = view.metadata; - meshes = view.meshes; - return *this; - } - ~ScriptableModel() { qDebug() << "~ScriptableModel" << this; } - void mixin(const ScriptableModel& other) { - for (const auto& key : other.metadata.keys()) { - metadata[key] = other.metadata[key]; - } - for(const auto&mesh : other.meshes) { - meshes << mesh; - } - } QUuid objectID; QVariantMap metadata; QVector meshes; - // TODO: in future accessors for these could go here - QVariantMap shapes; - QVariantMap materials; - QVariantMap armature; - QVector getMeshes() const; + Q_PROPERTY(QVector meshes READ getMeshes) + Q_PROPERTY(QUuid objectID MEMBER objectID CONSTANT) + Q_PROPERTY(QVariantMap metadata MEMBER metadata CONSTANT) + Q_INVOKABLE QString toString() const; + + ScriptableModel(QObject* parent = nullptr) : QObject(parent) {} + ScriptableModel(const ScriptableModel& other) : objectID(other.objectID), metadata(other.metadata), meshes(other.meshes) {} + ScriptableModel& operator=(const ScriptableModel& view) { objectID = view.objectID; metadata = view.metadata; meshes = view.meshes; return *this; } + ~ScriptableModel() { qDebug() << "~ScriptableModel" << this; } + + void mixin(const ScriptableModel& other) { + for (const auto& key : other.metadata.keys()) { metadata[key] = other.metadata[key]; } + for (const auto& mesh : other.meshes) { meshes << mesh; } + } + + // TODO: in future accessors for these could go here + // QVariantMap shapes; + // QVariantMap materials; + // QVariantMap armature; + + QVector getMeshes(); + const QVector getConstMeshes() const; + + // QScriptEngine-specific wrappers + Q_INVOKABLE QScriptValue mapAttributeValues(QScriptValue scopeOrCallback, QScriptValue methodOrName); }; + // mixin class for Avatar/Entity/Overlay Rendering that expose their in-memory graphics::Meshes class ModelProvider { public: QVariantMap metadata; @@ -67,11 +71,12 @@ namespace scriptable { virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) = 0; }; using ModelProviderPointer = std::shared_ptr; + + // mixin class for Application to resolve UUIDs into a corresponding ModelProvider class ModelProviderFactory : public Dependency { public: virtual scriptable::ModelProviderPointer lookupModelProvider(QUuid uuid) = 0; }; - } Q_DECLARE_METATYPE(scriptable::MeshPointer) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index d595136c56..ae5ac5d61c 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -579,38 +579,9 @@ scriptable::ScriptableModel Model::getScriptableModel(bool* ok) { if (!isLoaded()) { qDebug() << "Model::getScriptableModel -- !isLoaded"; - if (ok) { - *ok = false; - } - return result; + return scriptable::ModelProvider::modelUnavailableError(ok); } -// TODO: remove -- this was an earlier approach using renderGeometry instead of FBXGeometry -#if 0 // renderGeometry approach - const Geometry::GeometryMeshes& meshes = renderGeometry->getMeshes(); - Transform offset; - offset.setScale(_scale); - offset.postTranslate(_offset); - glm::mat4 offsetMat = offset.getMatrix(); - - for (std::shared_ptr mesh : meshes) { - if (!mesh) { - continue; - } - qDebug() << "Model::getScriptableModel #" << i++ << mesh->displayName; - auto newmesh = mesh->map( - [=](glm::vec3 position) { - return glm::vec3(offsetMat * glm::vec4(position, 1.0f)); - }, - [=](glm::vec3 color) { return color; }, - [=](glm::vec3 normal) { - return glm::normalize(glm::vec3(offsetMat * glm::vec4(normal, 0.0f))); - }, - [&](uint32_t index) { return index; }); - newmesh->displayName = mesh->displayName; - result << newmesh; - } -#endif const FBXGeometry& geometry = getFBXGeometry(); auto mat4toVariant = [](const glm::mat4& mat4) -> QVariant { QVector floats; @@ -659,6 +630,33 @@ scriptable::ScriptableModel Model::getScriptableModel(bool* ok) { qDebug() << "//Model::getScriptableModel -- #" << result.meshes.size(); result.metadata["submeshes"] = submeshes; return result; + +// TODO: remove -- this was an earlier approach using renderGeometry instead of FBXGeometry +#if 0 // renderGeometry approach + const Geometry::GeometryMeshes& meshes = renderGeometry->getMeshes(); + Transform offset; + offset.setScale(_scale); + offset.postTranslate(_offset); + glm::mat4 offsetMat = offset.getMatrix(); + + for (std::shared_ptr mesh : meshes) { + if (!mesh) { + continue; + } + qDebug() << "Model::getScriptableModel #" << i++ << mesh->displayName; + auto newmesh = mesh->map( + [=](glm::vec3 position) { + return glm::vec3(offsetMat * glm::vec4(position, 1.0f)); + }, + [=](glm::vec3 color) { return color; }, + [=](glm::vec3 normal) { + return glm::normalize(glm::vec3(offsetMat * glm::vec4(normal, 0.0f))); + }, + [&](uint32_t index) { return index; }); + newmesh->displayName = mesh->displayName; + result << newmesh; + } +#endif } void Model::calculateTriangleSets() { From 145a0df082654b960a225c276ff1328ccac67a29 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 15 Feb 2018 14:14:07 -0500 Subject: [PATCH 03/29] interim checkin --- interface/src/Application.cpp | 4 +- interface/src/ui/overlays/Base3DOverlay.h | 4 +- interface/src/ui/overlays/ModelOverlay.cpp | 2 +- interface/src/ui/overlays/ModelOverlay.h | 2 +- interface/src/ui/overlays/Shape3DOverlay.cpp | 6 +- interface/src/ui/overlays/Shape3DOverlay.h | 2 +- libraries/entities-renderer/CMakeLists.txt | 2 +- .../src/RenderableEntityItem.h | 4 +- .../src/RenderableModelEntityItem.cpp | 19 +- .../src/RenderableModelEntityItem.h | 3 +- .../graphics-scripting/BufferViewHelpers.cpp | 416 +++++++++- .../graphics-scripting/BufferViewHelpers.h | 35 +- .../BufferViewScripting.cpp | 4 +- .../src/graphics-scripting/Forward.h | 107 +++ .../GraphicsScriptingUtil.cpp | 3 + .../GraphicsScriptingUtil.h | 85 +++ .../ModelScriptingInterface.cpp | 152 ++-- .../ModelScriptingInterface.h | 10 +- .../src/graphics-scripting/ScriptableMesh.cpp | 714 +++++++++--------- .../src/graphics-scripting/ScriptableMesh.h | 198 +++-- .../src/graphics-scripting/ScriptableModel.h | 81 +- 21 files changed, 1219 insertions(+), 634 deletions(-) create mode 100644 libraries/graphics-scripting/src/graphics-scripting/Forward.h create mode 100644 libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp create mode 100644 libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f24969ce60..bdef2f456b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -600,7 +600,9 @@ public: QString error; scriptable::ModelProviderPointer provider; - if (auto entityInterface = getEntityModelProvider(static_cast(uuid))) { + if (uuid.isNull()) { + provider = nullptr; + } else if (auto entityInterface = getEntityModelProvider(static_cast(uuid))) { provider = entityInterface; } else if (auto overlayInterface = getOverlayModelProvider(static_cast(uuid))) { provider = overlayInterface; diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index 0c8bc5aacb..6ccad338c9 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -13,7 +13,7 @@ #include #include -#include +#include #include "Overlay.h" namespace model { class Mesh; } @@ -37,7 +37,7 @@ public: virtual bool is3D() const override { return true; } virtual uint32_t fetchMetaSubItems(render::ItemIDs& subItems) const override { subItems.push_back(getRenderItemID()); return (uint32_t) subItems.size(); } - virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override { return scriptable::ModelProvider::modelUnavailableError(ok); } + virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override { return scriptable::ModelProvider::modelUnavailableError(ok); } // TODO: consider implementing registration points in this class glm::vec3 getCenter() const { return getWorldPosition(); } diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 5a80ca1abf..e007591ce0 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -630,7 +630,7 @@ uint32_t ModelOverlay::fetchMetaSubItems(render::ItemIDs& subItems) const { return 0; } -scriptable::ScriptableModel ModelOverlay::getScriptableModel(bool* ok) { +scriptable::ScriptableModelBase ModelOverlay::getScriptableModel(bool* ok) { if (!_model || !_model->isLoaded()) { return Base3DOverlay::getScriptableModel(ok); } diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index 32d9a08c70..8dc386c733 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -59,7 +59,7 @@ public: void setDrawInFront(bool drawInFront) override; void setDrawHUDLayer(bool drawHUDLayer) override; - virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override; + virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override; protected: Transform evalRenderTransform() override; diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp index 8bb3d16888..54423feef6 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.cpp +++ b/interface/src/ui/overlays/Shape3DOverlay.cpp @@ -180,15 +180,15 @@ Transform Shape3DOverlay::evalRenderTransform() { return transform; } -scriptable::ScriptableModel Shape3DOverlay::getScriptableModel(bool* ok) { +scriptable::ScriptableModelBase Shape3DOverlay::getScriptableModel(bool* ok) { auto geometryCache = DependencyManager::get(); auto vertexColor = ColorUtils::toVec3(_color); - scriptable::ScriptableModel result; + scriptable::ScriptableModelBase result; result.metadata = { { "origin", "Shape3DOverlay::"+shapeStrings[_shape] }, { "overlayID", getID() }, }; - result.meshes << geometryCache->meshFromShape(_shape, vertexColor); + result.append(geometryCache->meshFromShape(_shape, vertexColor), {{ "shape", shapeStrings[_shape] }}); if (ok) { *ok = true; } diff --git a/interface/src/ui/overlays/Shape3DOverlay.h b/interface/src/ui/overlays/Shape3DOverlay.h index 34f82af278..f5246d95ac 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.h +++ b/interface/src/ui/overlays/Shape3DOverlay.h @@ -37,7 +37,7 @@ public: void setProperties(const QVariantMap& properties) override; QVariant getProperty(const QString& property) override; - virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override; + virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override; protected: Transform evalRenderTransform() override; diff --git a/libraries/entities-renderer/CMakeLists.txt b/libraries/entities-renderer/CMakeLists.txt index 27ea04f642..3aa561f927 100644 --- a/libraries/entities-renderer/CMakeLists.txt +++ b/libraries/entities-renderer/CMakeLists.txt @@ -13,7 +13,7 @@ include_hifi_library_headers(fbx) include_hifi_library_headers(entities) include_hifi_library_headers(avatars) include_hifi_library_headers(controllers) -include_hifi_library_headers(graphics-scripting) # for ScriptableModel.h +include_hifi_library_headers(graphics-scripting) # for Forward.h target_bullet() target_polyvox() diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index f07b67fbd0..74759f4fe4 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -17,7 +17,7 @@ #include #include "AbstractViewStateInterface.h" #include "EntitiesRendererLogging.h" -#include +#include class EntityTreeRenderer; @@ -55,7 +55,7 @@ public: const uint64_t& getUpdateTime() const { return _updateTime; } - virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override { return scriptable::ModelProvider::modelUnavailableError(ok); } + 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 7b022fefac..3d6714a400 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -950,12 +950,9 @@ QStringList RenderableModelEntityItem::getJointNames() const { return result; } - -scriptable::ScriptableModel render::entities::ModelEntityRenderer::getScriptableModel(bool* ok) { +scriptable::ScriptableModelBase render::entities::ModelEntityRenderer::getScriptableModel(bool* ok) { ModelPointer model; - withReadLock([&] { - model = _model; - }); + withReadLock([&] { model = _model; }); if (!model || !model->isLoaded()) { return scriptable::ModelProvider::modelUnavailableError(ok); @@ -964,6 +961,18 @@ scriptable::ScriptableModel render::entities::ModelEntityRenderer::getScriptable return _model->getScriptableModel(ok); } +bool render::entities::ModelEntityRenderer::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer newModel, int meshIndex, int partIndex) { + qCDebug(entitiesrenderer) << "REPLACING RenderableModelEntityItem" << newModel->objectName(); + ModelPointer model; + withReadLock([&] { model = _model; }); + + if (!model || !model->isLoaded()) { + return false; + } + + return _model->replaceScriptableModelMeshPart(newModel, meshIndex, partIndex); +} + void RenderableModelEntityItem::simulateRelayedJoints() { ModelPointer model = getModel(); if (model && model->isLoaded()) { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 3e952cb9a7..ffb83d3609 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -140,7 +140,8 @@ class ModelEntityRenderer : public TypedEntityRenderer #include +#include + +#include +#include + +#include #include +#include namespace glm { using hvec2 = glm::tvec2; using hvec4 = glm::tvec4; } //#define DEBUG_BUFFERVIEW_SCRIPTING -#ifdef DEBUG_BUFFERVIEW_SCRIPTING +//#ifdef DEBUG_BUFFERVIEW_SCRIPTING #include "DebugNames.h" - QLoggingCategory bufferview_helpers{"hifi.bufferview"}; -#endif +//#endif namespace { + QLoggingCategory bufferhelper_logging{"hifi.bufferview"}; const std::array XYZW = {{ "x", "y", "z", "w" }}; const std::array ZERO123 = {{ "0", "1", "2", "3" }}; } +gpu::BufferView buffer_helpers::getBufferView(graphics::MeshPointer mesh, gpu::Stream::Slot slot) { + return slot == gpu::Stream::POSITION ? mesh->getVertexBuffer() : mesh->getAttributeBuffer(slot); +} +QMap buffer_helpers::ATTRIBUTES{ + {"position", gpu::Stream::POSITION }, + {"normal", gpu::Stream::NORMAL }, + {"color", gpu::Stream::COLOR }, + {"tangent", gpu::Stream::TEXCOORD0 }, + {"skin_cluster_index", gpu::Stream::SKIN_CLUSTER_INDEX }, + {"skin_cluster_weight", gpu::Stream::SKIN_CLUSTER_WEIGHT }, + {"texcoord0", gpu::Stream::TEXCOORD0 }, + {"texcoord1", gpu::Stream::TEXCOORD1 }, + {"texcoord2", gpu::Stream::TEXCOORD2 }, + {"texcoord3", gpu::Stream::TEXCOORD3 }, + {"texcoord4", gpu::Stream::TEXCOORD4 }, +}; + + template QVariant getBufferViewElement(const gpu::BufferView& view, quint32 index, bool asArray = false) { return glmVecToVariant(view.get(index), asArray); @@ -61,14 +86,14 @@ static void packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint3 packedTangent = tangentStruct.pack; } -bool bufferViewElementFromVariant(const gpu::BufferView& view, quint32 index, const QVariant& v) { +bool buffer_helpers::fromVariant(const gpu::BufferView& view, quint32 index, const QVariant& v) { const auto& element = view._element; const auto vecN = element.getScalarCount(); const auto dataType = element.getType(); const auto byteLength = element.getSize(); const auto BYTES_PER_ELEMENT = byteLength / vecN; #ifdef DEBUG_BUFFERVIEW_SCRIPTING - qCDebug(bufferview_helpers) << "bufferViewElementFromVariant" << index << DebugNames::stringFrom(dataType) << BYTES_PER_ELEMENT << vecN; + qCDebug(bufferhelper_logging) << "bufferViewElementFromVariant" << index << DebugNames::stringFrom(dataType) << BYTES_PER_ELEMENT << vecN; #endif if (BYTES_PER_ELEMENT == 1) { switch(vecN) { @@ -122,16 +147,34 @@ bool bufferViewElementFromVariant(const gpu::BufferView& view, quint32 index, co return false; } -QVariant bufferViewElementToVariant(const gpu::BufferView& view, quint32 index, bool asArray, const char* hint) { +bool boundsCheck(const gpu::BufferView& view, quint32 index) { + const auto byteLength = view._element.getSize(); + 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(); const auto dataType = element.getType(); const auto byteLength = element.getSize(); const auto BYTES_PER_ELEMENT = byteLength / vecN; Q_ASSERT(index < view.getNumElements()); - Q_ASSERT(index * vecN * BYTES_PER_ELEMENT < (view._size - vecN * BYTES_PER_ELEMENT)); + if (!boundsCheck(view, index)) { + // sanity checks + auto byteOffset = index * vecN * BYTES_PER_ELEMENT; + auto maxByteOffset = (view._size - 1) * vecN * BYTES_PER_ELEMENT; + if (byteOffset > maxByteOffset) { + qDebug() << "bufferViewElementToVariant -- byteOffset out of range " << byteOffset << " < " << maxByteOffset << DebugNames::stringFrom(dataType); + qDebug() << "bufferViewElementToVariant -- index: " << index << "numElements" << view.getNumElements(); + qDebug() << "bufferViewElementToVariant -- vecN: " << vecN << "byteLength" << byteLength << "BYTES_PER_ELEMENT" << BYTES_PER_ELEMENT; + } + Q_ASSERT(byteOffset <= maxByteOffset); + } #ifdef DEBUG_BUFFERVIEW_SCRIPTING - qCDebug(bufferview_helpers) << "bufferViewElementToVariant" << index << DebugNames::stringFrom(dataType) << BYTES_PER_ELEMENT << vecN; + qCDebug(bufferhelper_logging) << "bufferViewElementToVariant" << index << DebugNames::stringFrom(dataType) << BYTES_PER_ELEMENT << vecN; #endif if (BYTES_PER_ELEMENT == 1) { switch(vecN) { @@ -221,22 +264,137 @@ const T glmVecFromVariant(const QVariant& v) { } template -gpu::BufferView bufferViewFromVector(QVector elements, gpu::Element elementType) { +gpu::BufferView buffer_helpers::fromVector(const QVector& elements, const gpu::Element& elementType) { auto vertexBuffer = std::make_shared(elements.size() * sizeof(T), (gpu::Byte*)elements.data()); return { vertexBuffer, 0, vertexBuffer->getSize(),sizeof(T), elementType }; } +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<> gpu::BufferView bufferViewFromVector(QVector elements, gpu::Element elementType) { return bufferViewFromVector(elements, elementType); } -template<> gpu::BufferView bufferViewFromVector(QVector elements, gpu::Element elementType) { return bufferViewFromVector(elements, elementType); } +template struct getVec4;// { 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); }; -gpu::BufferView cloneBufferView(const gpu::BufferView& input) { +struct gotter { + 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") + .arg(name) + .arg(DebugNames::stringFrom(view._element.getType())) + .arg(view._element.getType()) + .arg(view._element.getSize()) + .arg(view._element.getSize() / view._element.getScalarCount()) + .arg(view._element.getScalarCount()) + .arg(hint) + .arg(view.getNumElements()); + Q_ASSERT(false); + assert(false); + return NAN; + } +}; +template struct getScalar : gotter { + 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); + case gpu::INT32: return view.get(index); + case gpu::INT16: return view.get(index); + case gpu::INT8: return view.get(index); + case gpu::FLOAT: return view.get(index); + case gpu::HALF: return T(glm::unpackSnorm1x8(view.get(index))); + default: break; + } return T(error("getScalar", view, index, hint)); + } +}; + +template struct getVec2 : gotter { 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); + case gpu::INT32: return view.get(index); + case gpu::INT16: return view.get(index); + case gpu::INT8: return view.get(index); + 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)); }}; + + +template struct getVec3 : gotter { 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); + case gpu::INT32: return view.get(index); + case gpu::INT16: return view.get(index); + case gpu::INT8: return view.get(index); + case gpu::FLOAT: return view.get(index); + case gpu::HALF: + case gpu::NUINT8: + case gpu::NINT2_10_10_10: + if (view._element.getSize() == sizeof(glm::int32)) { + return getVec4::get(view, index, hint); + } + default: break; + } return T(error("getVec3", view, index, hint)); }}; + +template struct getVec4 : gotter { 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); + case gpu::UINT16: return view.get(index); + case gpu::UINT8: return view.get(index); + case gpu::INT32: return view.get(index); + case gpu::INT16: return view.get(index); + 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::NUINT2: break; + case gpu::NINT32: break; + case gpu::NINT16: break; + case gpu::NINT8: break; + case gpu::COMPRESSED: break; + case gpu::NUM_TYPES: break; + 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)); }}; + + +template +struct getVec { + static QVector __to_vector__(const gpu::BufferView& view, const char *hint) { + QVector result; + const quint32 count = (quint32)view.getNumElements(); + result.resize(count); + for (quint32 i = 0; i < count; i++) { + result[i] = FUNC::get(view, i, hint); + } + return result; + } + static T __to_scalar__(const gpu::BufferView& view, quint32 index, const char *hint) { + assert(boundsCheck(view, index)); + return FUNC::get(view, index, 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); } + +gpu::BufferView buffer_helpers::clone(const gpu::BufferView& input) { return gpu::BufferView( std::make_shared(input._buffer->getSize(), input._buffer->getData()), input._offset, input._size, input._stride, input._element ); } -gpu::BufferView resizedBufferView(const gpu::BufferView& input, quint32 numElements) { +gpu::BufferView buffer_helpers::resize(const gpu::BufferView& input, quint32 numElements) { auto effectiveSize = input._buffer->getSize() / input.getNumElements(); qDebug() << "resize input" << input.getNumElements() << input._buffer->getSize() << "effectiveSize" << effectiveSize; auto vsize = input._element.getSize() * numElements; @@ -248,3 +406,235 @@ gpu::BufferView resizedBufferView(const gpu::BufferView& input, quint32 numEleme qDebug() << "resized output" << output.getNumElements() << output._buffer->getSize(); return output; } + +graphics::MeshPointer buffer_helpers::cloneMesh(graphics::MeshPointer mesh) { + auto clone = std::make_shared(); + //[](graphics::Mesh* blah) { + //qCDebug(bufferhelper_logging) << "--- DELETING MESH POINTER" << blah; + // delete blah; + //}); + clone->displayName = (QString::fromStdString(mesh->displayName) + "-clone").toStdString(); + //qCInfo(bufferhelper_logging) << "+++ ALLOCATED MESH POINTER ScriptableMesh::cloneMesh" << clone->displayName << clone.get() << !!mesh; + clone->setIndexBuffer(buffer_helpers::clone(mesh->getIndexBuffer())); + clone->setPartBuffer(buffer_helpers::clone(mesh->getPartBuffer())); + auto attributeViews = buffer_helpers::gatherBufferViews(mesh); + for (const auto& a : attributeViews) { + auto& view = a.second; + auto slot = buffer_helpers::ATTRIBUTES[a.first]; + auto points = buffer_helpers::clone(view); + if (slot == gpu::Stream::POSITION) { + clone->setVertexBuffer(points); + } else { + clone->addAttribute(slot, points); + } + } + return clone; +} + + +/// --- buffer view <-> variant helpers + +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); + 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; + auto diffTypes = (elementType.getType() != bufferView._element.getType() || + elementType.getSize() > bufferView._element.getSize() || + elementType.getScalarCount() > bufferView._element.getScalarCount() || + vsize > bufferView._size + ); + QString hint = QString("%1").arg(slot); +#ifdef DEBUG_BUFFERVIEW_SCRIPTING + hint = DebugNames::stringFrom(slot); +#endif +#ifdef DEV_BUILD + auto beforeCount = bufferView.getNumElements(); + auto beforeTotal = bufferView._size; +#endif + if (bufferView.getNumElements() < nPositions || diffTypes) { + if (!bufferView._buffer || bufferView.getNumElements() == 0) { + qCInfo(bufferhelper_logging).nospace() << "ScriptableMesh -- adding missing mesh attribute '" << hint << "' for BufferView"; + gpu::Byte *data = new gpu::Byte[vsize]; + memset(data, 0, vsize); + auto buffer = new gpu::Buffer(vsize, (gpu::Byte*)data); + delete[] data; + bufferView = gpu::BufferView(buffer, elementType); + mesh->addAttribute(slot, bufferView); + } else { + qCInfo(bufferhelper_logging) << "ScriptableMesh -- resizing Buffer current:" << hint << bufferView._buffer->getSize() << "wanted:" << vsize; + bufferView._element = elementType; + bufferView._buffer->resize(vsize); + bufferView._size = bufferView._buffer->getSize(); + } + } +#ifdef DEV_BUILD + auto afterCount = bufferView.getNumElements(); + auto afterTotal = bufferView._size; + if (beforeTotal != afterTotal || beforeCount != afterCount) { + QString typeName = QString("%1").arg(bufferView._element.getType()); +#ifdef DEBUG_BUFFERVIEW_SCRIPTING + 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); + } +#endif + return bufferView; + } + + gpu::BufferView expandAttributeToMatchPositions(graphics::MeshPointer mesh, gpu::Stream::Slot slot) { + if (slot == gpu::Stream::POSITION) { + return buffer_helpers::getBufferView(mesh, slot); + } + return _expandedAttributeBuffer(mesh, slot); + } +} + +std::map buffer_helpers::gatherBufferViews(graphics::MeshPointer mesh, const QStringList& expandToMatchPositions) { + std::map attributeViews; + if (!mesh) { + return attributeViews; + } + for (const auto& a : buffer_helpers::ATTRIBUTES.toStdMap()) { + auto name = a.first; + auto slot = a.second; + auto view = getBufferView(mesh, slot); + auto beforeCount = view.getNumElements(); + auto beforeTotal = view._size; + if (expandToMatchPositions.contains(name)) { + expandAttributeToMatchPositions(mesh, slot); + } + if (beforeCount > 0) { + auto element = view._element; + auto vecN = element.getScalarCount(); + //auto type = element.getType(); + QString typeName = QString("%1").arg(element.getType()); +#ifdef DEBUG_BUFFERVIEW_SCRIPTING + typeName = DebugNames::stringFrom(element.getType()); +#endif + + attributeViews[name] = getBufferView(mesh, slot); + +#if DEV_BUILD + auto afterTotal = attributeViews[name]._size; + 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); + } +#endif + } + } + return attributeViews; +} + + +bool buffer_helpers::recalculateNormals(graphics::MeshPointer mesh) { + qCInfo(bufferhelper_logging) << "Recalculating normals" << !!mesh; + if (!mesh) { + return false; + } + buffer_helpers::gatherBufferViews(mesh, { "normal", "color" }); // ensures #normals >= #positions + auto normals = mesh->getAttributeBuffer(gpu::Stream::NORMAL); + auto verts = mesh->getVertexBuffer(); + auto indices = mesh->getIndexBuffer(); + auto esize = indices._element.getSize(); + auto numPoints = indices.getNumElements(); + const auto TRIANGLE = 3; + quint32 numFaces = (quint32)numPoints / TRIANGLE; + //QVector faces; + QVector faceNormals; + QMap> vertexToFaces; + //faces.resize(numFaces); + faceNormals.resize(numFaces); + auto numNormals = normals.getNumElements(); + qCInfo(bufferhelper_logging) << QString("numFaces: %1, numNormals: %2, numPoints: %3").arg(numFaces).arg(numNormals).arg(numPoints); + if (normals.getNumElements() != verts.getNumElements()) { + return false; + } + for (quint32 i = 0; i < numFaces; i++) { + quint32 I = TRIANGLE * i; + quint32 i0 = esize == 4 ? indices.get(I+0) : indices.get(I+0); + quint32 i1 = esize == 4 ? indices.get(I+1) : indices.get(I+1); + quint32 i2 = esize == 4 ? indices.get(I+2) : indices.get(I+2); + + Triangle face = { + verts.get(i1), + verts.get(i2), + verts.get(i0) + }; + faceNormals[i] = face.getNormal(); + if (glm::isnan(faceNormals[i].x)) { + qCInfo(bufferhelper_logging) << i << i0 << i1 << i2 << glmVecToVariant(face.v0) << glmVecToVariant(face.v1) << glmVecToVariant(face.v2); + break; + } + vertexToFaces[glm::to_string(face.v0).c_str()] << i; + vertexToFaces[glm::to_string(face.v1).c_str()] << i; + vertexToFaces[glm::to_string(face.v2).c_str()] << i; + } + for (quint32 j = 0; j < numNormals; j++) { + //auto v = verts.get(j); + glm::vec3 normal { 0.0f, 0.0f, 0.0f }; + QString key { glm::to_string(verts.get(j)).c_str() }; + const auto& faces = vertexToFaces.value(key); + if (faces.size()) { + for (const auto i : faces) { + normal += faceNormals[i]; + } + normal *= 1.0f / (float)faces.size(); + } else { + static int logged = 0; + if (logged++ < 10) { + qCInfo(bufferhelper_logging) << "no faces for key!?" << key; + } + normal = verts.get(j); + } + if (glm::isnan(normal.x)) { + static int logged = 0; + if (logged++ < 10) { + qCInfo(bufferhelper_logging) << "isnan(normal.x)" << j << glmVecToVariant(normal); + } + break; + } + normals.edit(j) = glm::normalize(normal); + } + return true; +} + +QVariant buffer_helpers::toVariant(const glm::mat4& mat4) { + QVector floats; + floats.resize(16); + memcpy(floats.data(), &mat4, sizeof(glm::mat4)); + QVariant v; + v.setValue>(floats); + return v; +}; + +QVariant buffer_helpers::toVariant(const Extents& box) { + return QVariantMap{ + { "center", glmVecToVariant(box.minimum + (box.size() / 2.0f)) }, + { "minimum", glmVecToVariant(box.minimum) }, + { "maximum", glmVecToVariant(box.maximum) }, + { "dimensions", glmVecToVariant(box.size()) }, + }; +} + +QVariant buffer_helpers::toVariant(const AABox& box) { + return QVariantMap{ + { "brn", glmVecToVariant(box.getCorner()) }, + { "tfl", glmVecToVariant(box.calcTopFarLeft()) }, + { "center", glmVecToVariant(box.calcCenter()) }, + { "minimum", glmVecToVariant(box.getMinimumPoint()) }, + { "maximum", glmVecToVariant(box.getMaximumPoint()) }, + { "dimensions", glmVecToVariant(box.getDimensions()) }, + }; +} diff --git a/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.h b/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.h index d0d42ca419..d963fd4b22 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.h +++ b/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.h @@ -7,6 +7,8 @@ #pragma once #include +#include +#include namespace gpu { class BufferView; @@ -15,11 +17,34 @@ namespace gpu { template QVariant glmVecToVariant(const T& v, bool asArray = false); template const T glmVecFromVariant(const QVariant& v); -QVariant bufferViewElementToVariant(const gpu::BufferView& view, quint32 index, bool asArray = false, const char* hint = ""); -bool bufferViewElementFromVariant(const gpu::BufferView& view, quint32 index, const QVariant& v); -template gpu::BufferView bufferViewFromVector(QVector elements, gpu::Element elementType); +namespace graphics { + class Mesh; + using MeshPointer = std::shared_ptr; +} -gpu::BufferView cloneBufferView(const gpu::BufferView& input); -gpu::BufferView resizedBufferView(const gpu::BufferView& input, quint32 numElements); +class Extents; +class AABox; +struct buffer_helpers { + static graphics::MeshPointer cloneMesh(graphics::MeshPointer mesh); + static QMap ATTRIBUTES; + static std::map gatherBufferViews(graphics::MeshPointer mesh, const QStringList& expandToMatchPositions = QStringList()); + static bool recalculateNormals(graphics::MeshPointer meshProxy); + static gpu::BufferView getBufferView(graphics::MeshPointer mesh, quint8 slot); + + static QVariant toVariant(const Extents& box); + static QVariant toVariant(const AABox& box); + static QVariant toVariant(const glm::mat4& mat4); + static QVariant toVariant(const gpu::BufferView& view, quint32 index, bool asArray = false, const char* hint = ""); + + static bool fromVariant(const gpu::BufferView& view, quint32 index, const QVariant& v); + + 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 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); +}; diff --git a/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp b/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp index 367c0589e9..ab6f2c92be 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp @@ -26,7 +26,7 @@ QScriptValue getBufferViewElement(QScriptEngine* js, const gpu::BufferView& view } QScriptValue bufferViewElementToScriptValue(QScriptEngine* engine, const gpu::BufferView& view, quint32 index, bool asArray, const char* hint) { - QVariant result = bufferViewElementToVariant(view, index, asArray, hint); + QVariant result = buffer_helpers::toVariant(view, index, asArray, hint); if (!result.isValid()) { return QScriptValue::NullValue; } @@ -39,7 +39,7 @@ void setBufferViewElement(const gpu::BufferView& view, quint32 index, const QScr } bool bufferViewElementFromScriptValue(const QScriptValue& v, const gpu::BufferView& view, quint32 index) { - return bufferViewElementFromVariant(view, index, v.toVariant()); + return buffer_helpers::fromVariant(view, index, v.toVariant()); } // diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h new file mode 100644 index 0000000000..15973b5852 --- /dev/null +++ b/libraries/graphics-scripting/src/graphics-scripting/Forward.h @@ -0,0 +1,107 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +namespace graphics { + class Mesh; +} +namespace gpu { + class BufferView; +} +class QScriptEngine; + +namespace scriptable { + using Mesh = graphics::Mesh; + using MeshPointer = std::shared_ptr; + using WeakMeshPointer = std::weak_ptr; + + class ScriptableModelBase; + using ScriptableModelBasePointer = QPointer; + + class ModelProvider; + using ModelProviderPointer = std::shared_ptr; + using WeakModelProviderPointer = std::weak_ptr; + + class ScriptableMeshBase : public QObject { + Q_OBJECT + public: + WeakModelProviderPointer provider; + ScriptableModelBasePointer model; + WeakMeshPointer mesh; + MeshPointer ownedMesh; + QVariantMap metadata; + ScriptableMeshBase(WeakModelProviderPointer provider, ScriptableModelBasePointer model, WeakMeshPointer mesh, const QVariantMap& metadata); + ScriptableMeshBase(WeakMeshPointer mesh = WeakMeshPointer()); + ScriptableMeshBase(MeshPointer mesh, const QVariantMap& metadata); + ScriptableMeshBase(const ScriptableMeshBase& other) { *this = other; } + ScriptableMeshBase& operator=(const ScriptableMeshBase& view); + virtual ~ScriptableMeshBase(); + Q_INVOKABLE const scriptable::MeshPointer getMeshPointer() const { return mesh.lock(); } + Q_INVOKABLE const scriptable::ModelProviderPointer getModelProviderPointer() const { return provider.lock(); } + Q_INVOKABLE const scriptable::ScriptableModelBasePointer getModelBasePointer() const { return model; } + }; + + // abstract container for holding one or more references to mesh pointers + class ScriptableModelBase : public QObject { + Q_OBJECT + public: + WeakModelProviderPointer provider; + QUuid objectID; // spatially nestable ID + QVariantMap metadata; + QVector meshes; + + ScriptableModelBase(QObject* parent = nullptr) : QObject(parent) {} + ScriptableModelBase(const ScriptableModelBase& other) { *this = other; } + ScriptableModelBase& operator=(const ScriptableModelBase& other) { + provider = other.provider; + objectID = other.objectID; + metadata = other.metadata; + for (auto& mesh : other.meshes) { + append(mesh); + } + return *this; + } + virtual ~ScriptableModelBase(); + + void mixin(const QVariantMap& other); + void append(const ScriptableModelBase& other, const QVariantMap& modelMetadata = QVariantMap()); + void append(scriptable::WeakMeshPointer mesh, const QVariantMap& metadata = QVariantMap()); + void append(const ScriptableMeshBase& mesh, const QVariantMap& metadata = QVariantMap()); + // TODO: in future containers for these could go here + // QVariantMap shapes; + // QVariantMap materials; + // QVariantMap armature; + }; + + // mixin class for Avatar/Entity/Overlay Rendering that expose their in-memory graphics::Meshes + class ModelProvider { + public: + QVariantMap metadata{ { "providerType", "unknown" } }; + static scriptable::ScriptableModelBase modelUnavailableError(bool* ok) { if (ok) { *ok = false; } return {}; } + virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) = 0; + + virtual bool replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer model, int meshIndex, int partIndex) { return false; } + }; + + // mixin class for resolving UUIDs into a corresponding ModelProvider + class ModelProviderFactory : public Dependency { + public: + virtual scriptable::ModelProviderPointer lookupModelProvider(QUuid uuid) = 0; + }; + + using uint32 = quint32; + class ScriptableModel; + using ScriptableModelPointer = QPointer; + class ScriptableMesh; + using ScriptableMeshPointer = QPointer; + class ScriptableMeshPart; + using ScriptableMeshPartPointer = QPointer; + bool registerMetaTypes(QScriptEngine* engine); +} diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp new file mode 100644 index 0000000000..aabf83ff66 --- /dev/null +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp @@ -0,0 +1,3 @@ +#include "GraphicsScriptingUtil.h" + +Q_LOGGING_CATEGORY(graphics_scripting, "hifi.scripting.graphics") diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h new file mode 100644 index 0000000000..a536fc413c --- /dev/null +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h @@ -0,0 +1,85 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +Q_DECLARE_LOGGING_CATEGORY(graphics_scripting) + +namespace scriptable { + // derive current context's C++ QObject (based on current JS "this" value) + template T this_qobject_cast(QScriptEngine* engine) { + auto context = engine ? engine->currentContext() : nullptr; + return qscriptvalue_cast(context ? context->thisObject() : QScriptValue::NullValue); + } + // JS => QPointer + template QPointer qpointer_qobject_cast(const QScriptValue& value) { + auto obj = value.toQObject(); + qCInfo(graphics_scripting) << "qpointer_qobject_cast" << obj << value.toString(); + if (auto tmp = qobject_cast(obj)) { + return QPointer(tmp); + } + if (auto tmp = static_cast(obj)) { + return QPointer(tmp); + } + return nullptr; + } + inline QString toDebugString(QObject* tmp) { + return QString("%0 (0x%1%2)") + .arg(tmp ? tmp->metaObject()->className() : "QObject") + .arg(qulonglong(tmp), 16, 16, QChar('0')) + .arg(tmp && tmp->objectName().size() ? " name=" + tmp->objectName() : ""); + } + template QString toDebugString(std::shared_ptr tmp) { + return toDebugString(qobject_cast(tmp.get())); + } + + // C++ > QtOwned instance + template std::shared_ptr make_qtowned(Rest... rest) { + T* tmp = new T(rest...); + qCInfo(graphics_scripting) << "scriptable::make_qtowned" << toDebugString(tmp); + QString debug = toDebugString(tmp); + if (tmp) { + tmp->metadata["__ownership__"] = QScriptEngine::QtOwnership; + QObject::connect(tmp, &QObject::destroyed, [=]() { qCInfo(graphics_scripting) << "-------- ~scriptable::make_qtowned" << debug; }); + auto ptr = std::shared_ptr(tmp, [debug](T* tmp) { + //qDebug() << "~std::shared_ptr" << debug; + delete tmp; + }); + return ptr; + } else { + return std::shared_ptr(tmp); + } + } + // C++ > ScriptOwned JS instance + template QPointer make_scriptowned(Rest... rest) { + T* tmp = new T(rest...); + qCInfo(graphics_scripting) << "scriptable::make_scriptowned" << toDebugString(tmp); + if (tmp) { + tmp->metadata["__ownership__"] = QScriptEngine::ScriptOwnership; + //auto blah = (DeleterFunction)[](void* delme) { }; + return add_scriptowned_destructor(tmp); + } else { + return QPointer(tmp); + } + } + // C++ > ScriptOwned JS instance + template QPointer add_scriptowned_destructor(T* tmp) { + QString debug = toDebugString(tmp); + if (tmp) { + QObject::connect(tmp, &QObject::destroyed, [=]() { + qCInfo(graphics_scripting) << "-------- ~scriptable::make_scriptowned" << debug;// << !!customDeleter; + //if (customDeleter) { + // customDeleter(tmp); + //} + }); + } else { + qCInfo(graphics_scripting) << "add_scriptowned_destructor -- not connecting to null value" << debug; + } + return QPointer(tmp); + } +} diff --git a/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp index ab85fb8265..ab9403a8ed 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp @@ -26,12 +26,11 @@ #include "BufferViewScripting.h" #include "ScriptableMesh.h" +#include "GraphicsScriptingUtil.h" #include "ModelScriptingInterface.moc" -namespace { - QLoggingCategory model_scripting { "hifi.model.scripting" }; -} +#include "RegisteredMetaTypes.h" ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(parent) { if (auto scriptEngine = qobject_cast(parent)) { @@ -39,8 +38,51 @@ ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(pare } } -void ModelScriptingInterface::getMeshes(QUuid uuid, QScriptValue scopeOrCallback, QScriptValue methodOrName) { - auto handler = makeScopedHandlerObject(scopeOrCallback, methodOrName); +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()); @@ -49,18 +91,23 @@ void ModelScriptingInterface::getMeshes(QUuid uuid, QScriptValue scopeOrCallback QString error; auto appProvider = DependencyManager::get(); - qDebug() << "appProvider" << appProvider.data(); + 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(model_scripting) << "fetching meshes from " << providerType << "..."; + qCDebug(graphics_scripting) << "fetching meshes from " << providerType << "..."; auto scriptableMeshes = provider->getScriptableModel(&success); - qCDebug(model_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"); } @@ -75,20 +122,20 @@ void ModelScriptingInterface::getMeshes(QUuid uuid, QScriptValue scopeOrCallback } if (!error.isEmpty()) { - qCWarning(model_scripting) << "ModelScriptingInterface::getMeshes ERROR" << error; + qCWarning(graphics_scripting) << "ModelScriptingInterface::getMeshes ERROR" << error; callScopedHandlerObject(handler, engine->makeError(error), QScriptValue::NullValue); } else { - callScopedHandlerObject(handler, QScriptValue::NullValue, engine->newQObject(meshes, QScriptEngine::ScriptOwnership)); + callScopedHandlerObject(handler, QScriptValue::NullValue, engine->toScriptValue(meshes)); } } QString ModelScriptingInterface::meshToOBJ(const scriptable::ScriptableModel& _in) { const auto& in = _in.getConstMeshes(); - qCDebug(model_scripting) << "meshToOBJ" << in.size(); + qCDebug(graphics_scripting) << "meshToOBJ" << in.size(); if (in.size()) { QList meshes; foreach (auto meshProxy, in) { - qCDebug(model_scripting) << "meshToOBJ" << meshProxy; + qCDebug(graphics_scripting) << "meshToOBJ" << meshProxy; if (meshProxy) { meshes.append(getMeshPointer(meshProxy)); } @@ -207,7 +254,7 @@ QScriptValue ModelScriptingInterface::appendMeshes(scriptable::ScriptableModel _ (gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL)); - return engine()->toScriptValue(scriptable::ScriptableMeshPointer(new scriptable::ScriptableMesh(nullptr, result))); + return engine()->toScriptValue(scriptable::make_scriptowned(result)); } QScriptValue ModelScriptingInterface::transformMesh(scriptable::ScriptableMeshPointer meshProxy, glm::mat4 transform) { @@ -220,8 +267,7 @@ QScriptValue ModelScriptingInterface::transformMesh(scriptable::ScriptableMeshPo [&](glm::vec3 color){ return color; }, [&](glm::vec3 normal){ return glm::vec3(transform * glm::vec4(normal, 0.0f)); }, [&](uint32_t index){ return index; }); - scriptable::ScriptableMeshPointer resultProxy = scriptable::ScriptableMeshPointer(new scriptable::ScriptableMesh(nullptr, result)); - return engine()->toScriptValue(resultProxy); + return engine()->toScriptValue(scriptable::make_scriptowned(result)); } QScriptValue ModelScriptingInterface::getVertexCount(scriptable::ScriptableMeshPointer meshProxy) { @@ -270,7 +316,7 @@ QScriptValue ModelScriptingInterface::newMesh(const QVector& vertices sizeof(glm::vec3), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)); mesh->addAttribute(gpu::Stream::NORMAL, normalBufferView); } else { - qCWarning(model_scripting, "ModelScriptingInterface::newMesh normals must be same length as vertices"); + qCWarning(graphics_scripting, "ModelScriptingInterface::newMesh normals must be same length as vertices"); } // indices (faces) @@ -300,54 +346,10 @@ QScriptValue ModelScriptingInterface::newMesh(const QVector& vertices - scriptable::ScriptableMeshPointer meshProxy = scriptable::ScriptableMeshPointer(new scriptable::ScriptableMesh(nullptr, mesh)); - return engine()->toScriptValue(meshProxy); + return engine()->toScriptValue(scriptable::make_scriptowned(mesh)); } namespace { - QScriptValue meshPointerToScriptValue(QScriptEngine* engine, scriptable::ScriptableMeshPointer const &in) { - if (!in) { - return QScriptValue::NullValue; - } - return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); - } - - void meshPointerFromScriptValue(const QScriptValue& value, scriptable::ScriptableMeshPointer &out) { - auto obj = value.toQObject(); - qDebug() << "meshPointerFromScriptValue" << obj; - if (auto tmp = qobject_cast(obj)) { - out = tmp; - } - // FIXME: Why does above cast not work on Win32!? - if (!out) { - if (auto smp = static_cast(obj)) { - qDebug() << "meshPointerFromScriptValue2" << smp; - out = smp; - } - } - } - - QScriptValue modelPointerToScriptValue(QScriptEngine* engine, const scriptable::ScriptableModelPointer &in) { - return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects); - // QScriptValue result = engine->newArray(); - // int i = 0; - // foreach(auto& mesh, in->getMeshes()) { - // result.setProperty(i++, meshPointerToScriptValue(engine, mesh)); - // } - // return result; - } - - void modelPointerFromScriptValue(const QScriptValue& value, scriptable::ScriptableModelPointer &out) { - const auto length = value.property("length").toInt32(); - qCDebug(model_scripting) << "in modelPointerFromScriptValue, length =" << length; - for (int i = 0; i < length; i++) { - if (const auto meshProxy = qobject_cast(value.property(i).toQObject())) { - out->meshes.append(meshProxy->getMeshPointer()); - } else { - qCDebug(model_scripting) << "null meshProxy" << i; - } - } - } // FIXME: MESHFACES: // QScriptValue meshFaceToScriptValue(QScriptEngine* engine, const mesh::MeshFace &meshFace) { @@ -365,29 +367,11 @@ namespace { // qScriptValueToSequence(array, result); // } - QScriptValue qVectorUInt32ToScriptValue(QScriptEngine* engine, const QVector& vector) { - return qScriptValueFromSequence(engine, vector); - } - - void qVectorUInt32FromScriptValue(const QScriptValue& array, QVector& result) { - qScriptValueToSequence(array, result); - } } -int meshUint32 = qRegisterMetaType(); -namespace mesh { - int meshUint32 = qRegisterMetaType(); -} -int qVectorMeshUint32 = qRegisterMetaType>(); void ModelScriptingInterface::registerMetaTypes(QScriptEngine* engine) { - qScriptRegisterSequenceMetaType>(engine); - qScriptRegisterSequenceMetaType>(engine); - - qScriptRegisterMetaType(engine, qVectorUInt32ToScriptValue, qVectorUInt32FromScriptValue); - qScriptRegisterMetaType(engine, meshPointerToScriptValue, meshPointerFromScriptValue); - qScriptRegisterMetaType(engine, modelPointerToScriptValue, modelPointerFromScriptValue); - + scriptable::registerMetaTypes(engine); // FIXME: MESHFACES: remove if MeshFace is not needed anywhere // qScriptRegisterSequenceMetaType(engine); // qScriptRegisterMetaType(engine, meshFaceToScriptValue, meshFaceFromScriptValue); @@ -395,7 +379,7 @@ void ModelScriptingInterface::registerMetaTypes(QScriptEngine* engine) { } MeshPointer ModelScriptingInterface::getMeshPointer(const scriptable::ScriptableMesh& meshProxy) { - return meshProxy._mesh;//getMeshPointer(&meshProxy); + return meshProxy.getMeshPointer(); } MeshPointer ModelScriptingInterface::getMeshPointer(scriptable::ScriptableMesh& meshProxy) { return getMeshPointer(&meshProxy); @@ -406,7 +390,7 @@ MeshPointer ModelScriptingInterface::getMeshPointer(scriptable::ScriptableMeshPo if (context()){ context()->throwError("expected meshProxy as first parameter"); } else { - qDebug() << "expected meshProxy as first parameter"; + qCDebug(graphics_scripting) << "expected meshProxy as first parameter"; } return result; } @@ -415,7 +399,7 @@ MeshPointer ModelScriptingInterface::getMeshPointer(scriptable::ScriptableMeshPo if (context()) { context()->throwError("expected valid meshProxy as first parameter"); } else { - qDebug() << "expected valid meshProxy as first parameter"; + qCDebug(graphics_scripting) << "expected valid meshProxy as first parameter"; } return result; } diff --git a/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.h b/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.h index eac4df3216..fa7b885014 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.h @@ -15,19 +15,17 @@ #include #include -#include - #include #include #include "ScriptableMesh.h" #include + class ModelScriptingInterface : public QObject, public QScriptable, public Dependency { Q_OBJECT public: ModelScriptingInterface(QObject* parent = nullptr); - static void registerMetaTypes(QScriptEngine* engine); public slots: /**jsdoc @@ -36,7 +34,9 @@ public slots: * @function ModelScriptingInterface.getMeshes * @param {EntityID} entityID The ID of the entity whose meshes are to be retrieve */ - void getMeshes(QUuid uuid, QScriptValue scopeOrCallback, QScriptValue methodOrName = QScriptValue()); + void getMeshes(QUuid uuid, QScriptValue callback); + 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); @@ -48,6 +48,8 @@ public slots: 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); diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp index 1b16a6d263..b83b901acd 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp @@ -9,17 +9,16 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "GraphicsScriptingUtil.h" #include "ScriptableMesh.h" #include #include #include -#include #include #include #include #include -#include #include "ScriptableMesh.moc" @@ -29,49 +28,45 @@ #include "OBJWriter.h" -QLoggingCategory mesh_logging { "hifi.scripting.mesh" }; - -// FIXME: unroll/resolve before PR -using namespace scriptable; -QMap ScriptableMesh::ATTRIBUTES{ - {"position", gpu::Stream::POSITION }, - {"normal", gpu::Stream::NORMAL }, - {"color", gpu::Stream::COLOR }, - {"tangent", gpu::Stream::TEXCOORD0 }, - {"skin_cluster_index", gpu::Stream::SKIN_CLUSTER_INDEX }, - {"skin_cluster_weight", gpu::Stream::SKIN_CLUSTER_WEIGHT }, - {"texcoord0", gpu::Stream::TEXCOORD0 }, - {"texcoord1", gpu::Stream::TEXCOORD1 }, - {"texcoord2", gpu::Stream::TEXCOORD2 }, - {"texcoord3", gpu::Stream::TEXCOORD3 }, - {"texcoord4", gpu::Stream::TEXCOORD4 }, -}; - - -QString scriptable::ScriptableModel::toString() const { - return QString("[ScriptableModel%1%2]") - .arg(objectID.isNull() ? "" : " objectID="+objectID.toString()) - .arg(objectName().isEmpty() ? "" : " name=" +objectName()); +namespace scriptable { + // QScriptValue jsBindCallback(QScriptValue callback); + // template QPointer qpointer_qobject_cast(const QScriptValue& value); + // template T this_qobject_cast(QScriptEngine* engine); + // template QPointer make_scriptowned(Rest... rest); } -const QVector scriptable::ScriptableModel::getConstMeshes() const { - QVector out; - for(const auto& mesh : meshes) { - const scriptable::ScriptableMeshPointer m = scriptable::ScriptableMeshPointer(new scriptable::ScriptableMesh(const_cast(this), mesh)); - out << m; +scriptable::ScriptableMeshPart::ScriptableMeshPart(scriptable::ScriptableMeshPointer parentMesh, int partIndex) + : parentMesh(parentMesh), partIndex(partIndex) { + setObjectName(QString("%1.part[%2]").arg(parentMesh ? parentMesh->objectName() : "").arg(partIndex)); +} + +scriptable::ScriptableMesh::ScriptableMesh(const ScriptableMeshBase& other) + : ScriptableMeshBase(other) { + auto mesh = getMeshPointer(); + QString name = mesh ? QString::fromStdString(mesh->modelName) : ""; + if (name.isEmpty()) { + name = mesh ? QString::fromStdString(mesh->displayName) : ""; } - return out; + auto parentModel = getParentModel(); + setObjectName(QString("%1#%2").arg(parentModel ? parentModel->objectName() : "").arg(name)); } -QVector scriptable::ScriptableModel::getMeshes() { - QVector out; - for(auto& mesh : meshes) { - scriptable::ScriptableMeshPointer m{new scriptable::ScriptableMesh(this, mesh)}; - out << m; + +QVector scriptable::ScriptableMesh::getMeshParts() const { + QVector out; + for (quint32 i = 0; i < getNumParts(); i++) { + out << scriptable::make_scriptowned(getSelf(), i); } return out; } -quint32 ScriptableMesh::getNumVertices() const { +quint32 scriptable::ScriptableMesh::getNumIndices() const { + if (auto mesh = getMeshPointer()) { + return (quint32)mesh->getNumIndices(); + } + return 0; +} + +quint32 scriptable::ScriptableMesh::getNumVertices() const { if (auto mesh = getMeshPointer()) { return (quint32)mesh->getNumVertices(); } @@ -87,16 +82,10 @@ quint32 ScriptableMesh::getNumVertices() const { // return glm::vec3(NAN); // } -namespace { - gpu::BufferView getBufferView(scriptable::MeshPointer mesh, gpu::Stream::Slot slot) { - return slot == gpu::Stream::POSITION ? mesh->getVertexBuffer() : mesh->getAttributeBuffer(slot); - } -} - -QVector ScriptableMesh::findNearbyIndices(const glm::vec3& origin, float epsilon) const { +QVector scriptable::ScriptableMesh::findNearbyIndices(const glm::vec3& origin, float epsilon) const { QVector result; if (auto mesh = getMeshPointer()) { - const auto& pos = getBufferView(mesh, gpu::Stream::POSITION); + const auto& pos = buffer_helpers::getBufferView(mesh, gpu::Stream::POSITION); const uint32_t num = (uint32_t)pos.getNumElements(); for (uint32_t i = 0; i < num; i++) { const auto& position = pos.get(i); @@ -108,40 +97,45 @@ QVector ScriptableMesh::findNearbyIndices(const glm::vec3& origin, floa return result; } -QVector ScriptableMesh::getIndices() const { +QVector scriptable::ScriptableMesh::getIndices() const { QVector result; if (auto mesh = getMeshPointer()) { - qCDebug(mesh_logging, "getTriangleIndices mesh %p", mesh.get()); + qCDebug(graphics_scripting, "getTriangleIndices mesh %p", mesh.get()); gpu::BufferView indexBufferView = mesh->getIndexBuffer(); if (quint32 count = (quint32)indexBufferView.getNumElements()) { result.resize(count); - auto buffer = indexBufferView._buffer; - if (indexBufferView._element.getSize() == 4) { + switch(indexBufferView._element.getType()) { + case gpu::UINT32: // memcpy(result.data(), buffer->getData(), result.size()*sizeof(quint32)); for (quint32 i = 0; i < count; i++) { result[i] = indexBufferView.get(i); } - } else { + break; + case gpu::UINT16: for (quint32 i = 0; i < count; i++) { result[i] = indexBufferView.get(i); } + break; + default: + assert(false); + Q_ASSERT(false); } } } return result; } -quint32 ScriptableMesh::getNumAttributes() const { +quint32 scriptable::ScriptableMesh::getNumAttributes() const { if (auto mesh = getMeshPointer()) { return (quint32)mesh->getNumAttributes(); } return 0; } -QVector ScriptableMesh::getAttributeNames() const { +QVector scriptable::ScriptableMesh::getAttributeNames() const { QVector result; if (auto mesh = getMeshPointer()) { - for (const auto& a : ATTRIBUTES.toStdMap()) { - auto bufferView = getBufferView(mesh, a.second); + for (const auto& a : buffer_helpers::ATTRIBUTES.toStdMap()) { + auto bufferView = buffer_helpers::getBufferView(mesh, a.second); if (bufferView.getNumElements() > 0) { result << a.first; } @@ -151,55 +145,49 @@ QVector ScriptableMesh::getAttributeNames() const { } // override -QVariantMap ScriptableMesh::getVertexAttributes(quint32 vertexIndex) const { +QVariantMap scriptable::ScriptableMesh::getVertexAttributes(quint32 vertexIndex) const { return getVertexAttributes(vertexIndex, getAttributeNames()); } -bool ScriptableMesh::setVertexAttributes(quint32 vertexIndex, QVariantMap attributes) { - //qDebug() << "setVertexAttributes" << vertexIndex << attributes; - for (auto& a : gatherBufferViews(getMeshPointer())) { +bool scriptable::ScriptableMesh::setVertexAttributes(quint32 vertexIndex, QVariantMap attributes) { + //qCInfo(graphics_scripting) << "setVertexAttributes" << vertexIndex << attributes; + metadata["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate); + for (auto& a : buffer_helpers::gatherBufferViews(getMeshPointer())) { const auto& name = a.first; const auto& value = attributes.value(name); if (value.isValid()) { auto& view = a.second; - //qCDebug(mesh_logging) << "setVertexAttributes" << vertexIndex << name; - bufferViewElementFromVariant(view, vertexIndex, value); + //qCDebug(graphics_scripting) << "setVertexAttributes" << vertexIndex << name; + buffer_helpers::fromVariant(view, vertexIndex, value); } else { - //qCDebug(mesh_logging) << "(skipping) setVertexAttributes" << vertexIndex << name; + //qCDebug(graphics_scripting) << "(skipping) setVertexAttributes" << vertexIndex << name; } } return true; } -int ScriptableMesh::_getSlotNumber(const QString& attributeName) const { +int scriptable::ScriptableMesh::_getSlotNumber(const QString& attributeName) const { if (auto mesh = getMeshPointer()) { - return ATTRIBUTES.value(attributeName, -1); + return buffer_helpers::ATTRIBUTES.value(attributeName, -1); } return -1; } -QVariantMap ScriptableMesh::getMeshExtents() const { +QVariantMap scriptable::ScriptableMesh::getMeshExtents() const { auto mesh = getMeshPointer(); auto box = mesh ? mesh->evalPartsBound(0, (int)mesh->getNumParts()) : AABox(); - return { - { "brn", glmVecToVariant(box.getCorner()) }, - { "tfl", glmVecToVariant(box.calcTopFarLeft()) }, - { "center", glmVecToVariant(box.calcCenter()) }, - { "min", glmVecToVariant(box.getMinimumPoint()) }, - { "max", glmVecToVariant(box.getMaximumPoint()) }, - { "dimensions", glmVecToVariant(box.getDimensions()) }, - }; + return buffer_helpers::toVariant(box).toMap(); } -quint32 ScriptableMesh::getNumParts() const { +quint32 scriptable::ScriptableMesh::getNumParts() const { if (auto mesh = getMeshPointer()) { return (quint32)mesh->getNumParts(); } return 0; } -QVariantMap ScriptableMesh::scaleToFit(float unitScale) { +QVariantMap scriptable::ScriptableMeshPart::scaleToFit(float unitScale) { if (auto mesh = getMeshPointer()) { auto box = mesh->evalPartsBound(0, (int)mesh->getNumParts()); auto center = box.calcCenter(); @@ -208,10 +196,10 @@ QVariantMap ScriptableMesh::scaleToFit(float unitScale) { } return {}; } -QVariantMap ScriptableMesh::translate(const glm::vec3& translation) { +QVariantMap scriptable::ScriptableMeshPart::translate(const glm::vec3& translation) { return transform(glm::translate(translation)); } -QVariantMap ScriptableMesh::scale(const glm::vec3& scale, const glm::vec3& origin) { +QVariantMap scriptable::ScriptableMeshPart::scale(const glm::vec3& scale, const glm::vec3& origin) { if (auto mesh = getMeshPointer()) { auto box = mesh->evalPartsBound(0, (int)mesh->getNumParts()); glm::vec3 center = glm::isnan(origin.x) ? box.calcCenter() : origin; @@ -219,10 +207,10 @@ QVariantMap ScriptableMesh::scale(const glm::vec3& scale, const glm::vec3& origi } return {}; } -QVariantMap ScriptableMesh::rotateDegrees(const glm::vec3& eulerAngles, const glm::vec3& origin) { +QVariantMap scriptable::ScriptableMeshPart::rotateDegrees(const glm::vec3& eulerAngles, const glm::vec3& origin) { return rotate(glm::quat(glm::radians(eulerAngles)), origin); } -QVariantMap ScriptableMesh::rotate(const glm::quat& rotation, const glm::vec3& origin) { +QVariantMap scriptable::ScriptableMeshPart::rotate(const glm::quat& rotation, const glm::vec3& origin) { if (auto mesh = getMeshPointer()) { auto box = mesh->evalPartsBound(0, (int)mesh->getNumParts()); glm::vec3 center = glm::isnan(origin.x) ? box.calcCenter() : origin; @@ -230,184 +218,61 @@ QVariantMap ScriptableMesh::rotate(const glm::quat& rotation, const glm::vec3& o } return {}; } -QVariantMap ScriptableMesh::transform(const glm::mat4& transform) { +QVariantMap scriptable::ScriptableMeshPart::transform(const glm::mat4& transform) { if (auto mesh = getMeshPointer()) { - const auto& pos = getBufferView(mesh, gpu::Stream::POSITION); + const auto& pos = buffer_helpers::getBufferView(mesh, gpu::Stream::POSITION); const uint32_t num = (uint32_t)pos.getNumElements(); for (uint32_t i = 0; i < num; i++) { auto& position = pos.edit(i); position = transform * glm::vec4(position, 1.0f); } + return parentMesh->getMeshExtents(); } - return getMeshExtents(); + return {}; } -QVariantList ScriptableMesh::getAttributeValues(const QString& attributeName) const { +QVariantList scriptable::ScriptableMesh::getAttributeValues(const QString& attributeName) const { QVariantList result; auto slotNum = _getSlotNumber(attributeName); if (slotNum >= 0) { auto slot = (gpu::Stream::Slot)slotNum; - const auto& bufferView = getBufferView(getMeshPointer(), slot); + const auto& bufferView = buffer_helpers::getBufferView(getMeshPointer(), slot); if (auto len = bufferView.getNumElements()) { bool asArray = bufferView._element.getType() != gpu::FLOAT; for (quint32 i = 0; i < len; i++) { - result << bufferViewElementToVariant(bufferView, i, asArray, attributeName.toStdString().c_str()); + result << buffer_helpers::toVariant(bufferView, i, asArray, attributeName.toStdString().c_str()); } } } return result; } -QVariantMap ScriptableMesh::getVertexAttributes(quint32 vertexIndex, QVector names) const { +QVariantMap scriptable::ScriptableMesh::getVertexAttributes(quint32 vertexIndex, QVector names) const { QVariantMap result; auto mesh = getMeshPointer(); if (!mesh || vertexIndex >= getNumVertices()) { return result; } - for (const auto& a : ATTRIBUTES.toStdMap()) { + for (const auto& a : buffer_helpers::ATTRIBUTES.toStdMap()) { auto name = a.first; if (!names.contains(name)) { continue; } auto slot = a.second; - const gpu::BufferView& bufferView = getBufferView(mesh, slot); + const gpu::BufferView& bufferView = buffer_helpers::getBufferView(mesh, slot); if (vertexIndex < bufferView.getNumElements()) { bool asArray = bufferView._element.getType() != gpu::FLOAT; - result[name] = bufferViewElementToVariant(bufferView, vertexIndex, asArray, name.toStdString().c_str()); + result[name] = buffer_helpers::toVariant(bufferView, vertexIndex, asArray, name.toStdString().c_str()); } } return result; } -/// --- buffer view <-> variant helpers - -namespace { - // expand the corresponding attribute buffer (creating it if needed) so that it matches POSITIONS size and specified element type - gpu::BufferView _expandedAttributeBuffer(const scriptable::MeshPointer mesh, gpu::Stream::Slot slot, const gpu::Element& elementType) { - gpu::Size elementSize = elementType.getSize(); - gpu::BufferView bufferView = getBufferView(mesh, slot); - auto nPositions = mesh->getNumVertices(); - auto vsize = nPositions * elementSize; - auto diffTypes = (elementType.getType() != bufferView._element.getType() || - elementType.getSize() > bufferView._element.getSize() || - elementType.getScalarCount() > bufferView._element.getScalarCount() || - vsize > bufferView._size - ); - auto hint = DebugNames::stringFrom(slot); - -#ifdef DEV_BUILD - auto beforeCount = bufferView.getNumElements(); - auto beforeTotal = bufferView._size; -#endif - if (bufferView.getNumElements() < nPositions || diffTypes) { - if (!bufferView._buffer || bufferView.getNumElements() == 0) { - qCInfo(mesh_logging).nospace() << "ScriptableMesh -- adding missing mesh attribute '" << hint << "' for BufferView"; - gpu::Byte *data = new gpu::Byte[vsize]; - memset(data, 0, vsize); - auto buffer = new gpu::Buffer(vsize, (gpu::Byte*)data); - delete[] data; - bufferView = gpu::BufferView(buffer, elementType); - mesh->addAttribute(slot, bufferView); - } else { - qCInfo(mesh_logging) << "ScriptableMesh -- resizing Buffer current:" << hint << bufferView._buffer->getSize() << "wanted:" << vsize; - bufferView._element = elementType; - bufferView._buffer->resize(vsize); - bufferView._size = bufferView._buffer->getSize(); - } - } -#ifdef DEV_BUILD - auto afterCount = bufferView.getNumElements(); - auto afterTotal = bufferView._size; - if (beforeTotal != afterTotal || beforeCount != afterCount) { - auto typeName = DebugNames::stringFrom(bufferView._element.getType()); - qCDebug(mesh_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); - } -#endif - return bufferView; - } - const gpu::Element UNUSED{ gpu::SCALAR, gpu::UINT8, gpu::RAW }; - - gpu::Element getVecNElement(gpu::Type T, int N) { - switch(N) { - case 2: return { gpu::VEC2, T, gpu::XY }; - case 3: return { gpu::VEC3, T, gpu::XYZ }; - case 4: return { gpu::VEC4, T, gpu::XYZW }; - } - Q_ASSERT(false); - return UNUSED; - } - - gpu::BufferView expandAttributeToMatchPositions(scriptable::MeshPointer mesh, gpu::Stream::Slot slot) { - if (slot == gpu::Stream::POSITION) { - return getBufferView(mesh, slot); - } - return _expandedAttributeBuffer(mesh, slot, getVecNElement(gpu::FLOAT, 3)); - } -} - -std::map ScriptableMesh::gatherBufferViews(scriptable::MeshPointer mesh, const QStringList& expandToMatchPositions) { - std::map attributeViews; - if (!mesh) { - return attributeViews; - } - for (const auto& a : ScriptableMesh::ATTRIBUTES.toStdMap()) { - auto name = a.first; - auto slot = a.second; - if (expandToMatchPositions.contains(name)) { - expandAttributeToMatchPositions(mesh, slot); - } - auto view = getBufferView(mesh, slot); - auto beforeCount = view.getNumElements(); - if (beforeCount > 0) { - auto element = view._element; - auto vecN = element.getScalarCount(); - auto type = element.getType(); - QString typeName = DebugNames::stringFrom(element.getType()); - auto beforeTotal = view._size; - - attributeViews[name] = _expandedAttributeBuffer(mesh, slot, getVecNElement(type, vecN)); - -#if DEV_BUILD - auto afterTotal = attributeViews[name]._size; - auto afterCount = attributeViews[name].getNumElements(); - if (beforeTotal != afterTotal || beforeCount != afterCount) { - qCDebug(mesh_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); - } -#endif - } - } - return attributeViews; -} - -QScriptValue ScriptableModel::mapAttributeValues(QScriptValue scopeOrCallback, QScriptValue methodOrName) { - auto context = scopeOrCallback.engine()->currentContext(); - auto _in = context->thisObject(); - qCInfo(mesh_logging) << "mapAttributeValues" << _in.toVariant().typeName() << _in.toVariant().toString() << _in.toQObject(); - auto model = qscriptvalue_cast(_in); - QVector in = model.getMeshes(); - if (in.size()) { - foreach (scriptable::ScriptableMeshPointer meshProxy, in) { - meshProxy->mapAttributeValues(scopeOrCallback, methodOrName); - } - return _in; - } else if (auto meshProxy = qobject_cast(_in.toQObject())) { - return meshProxy->mapAttributeValues(scopeOrCallback, methodOrName); - } else { - context->throwError("invalid ModelProxy || MeshProxyPointer"); - } - return false; -} - - - -QScriptValue ScriptableMesh::mapAttributeValues(QScriptValue scopeOrCallback, QScriptValue methodOrName) { +quint32 scriptable::ScriptableMesh::mapAttributeValues(QScriptValue _callback) { auto mesh = getMeshPointer(); if (!mesh) { - return false; + return 0; } - auto scopedHandler = makeScopedHandlerObject(scopeOrCallback, methodOrName); + auto scopedHandler = jsBindCallback(_callback); // input buffers gpu::BufferView positions = mesh->getVertexBuffer(); @@ -417,20 +282,25 @@ QScriptValue ScriptableMesh::mapAttributeValues(QScriptValue scopeOrCallback, QS // destructure so we can still invoke callback scoped, but with a custom signature (obj, i, jsMesh) auto scope = scopedHandler.property("scope"); auto callback = scopedHandler.property("callback"); - auto js = engine(); // cache value to avoid resolving each iteration - auto meshPart = thisObject();//js->toScriptValue(meshProxy); - + auto js = engine() ? engine() : scopedHandler.engine(); // cache value to avoid resolving each iteration + if (!js) { + return 0; + } + auto meshPart = js ? js->toScriptValue(getSelf()) : QScriptValue::NullValue; + qCInfo(graphics_scripting) << "mapAttributeValues" << mesh.get() << js->currentContext()->thisObject().toQObject(); auto obj = js->newObject(); - auto attributeViews = ScriptableMesh::gatherBufferViews(mesh, { "normal", "color" }); - for (uint32_t i=0; i < nPositions; i++) { + auto attributeViews = buffer_helpers::gatherBufferViews(mesh, { "normal", "color" }); + metadata["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate); + uint32_t i = 0; + for (; i < nPositions; i++) { for (const auto& a : attributeViews) { bool asArray = a.second._element.getType() != gpu::FLOAT; obj.setProperty(a.first, bufferViewElementToScriptValue(js, a.second, i, asArray, a.first.toStdString().c_str())); } auto result = callback.call(scope, { obj, i, meshPart }); if (js->hasUncaughtException()) { - context()->throwValue(js->uncaughtException()); - return false; + js->currentContext()->throwValue(js->uncaughtException()); + return i; } if (result.isBool() && !result.toBool()) { @@ -450,15 +320,19 @@ QScriptValue ScriptableMesh::mapAttributeValues(QScriptValue scopeOrCallback, QS } } } - return thisObject(); + return i; } -QScriptValue ScriptableMesh::unrollVertices(bool recalcNormals) { +quint32 scriptable::ScriptableMeshPart::mapAttributeValues(QScriptValue callback) { + return parentMesh ? parentMesh->mapAttributeValues(callback) : 0; +} + +bool scriptable::ScriptableMeshPart::unrollVertices(bool recalcNormals) { auto meshProxy = this; auto mesh = getMeshPointer(); - qCInfo(mesh_logging) << "ScriptableMesh::unrollVertices" << !!mesh<< !!meshProxy; + qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices" << !!mesh<< !!meshProxy; if (!mesh) { - return QScriptValue(); + return false; } auto positions = mesh->getVertexBuffer(); @@ -467,8 +341,9 @@ QScriptValue ScriptableMesh::unrollVertices(bool recalcNormals) { auto buffer = new gpu::Buffer(); buffer->resize(numPoints * sizeof(uint32_t)); auto newindices = gpu::BufferView(buffer, { gpu::SCALAR, gpu::UINT32, gpu::INDEX }); - qCInfo(mesh_logging) << "ScriptableMesh::unrollVertices numPoints" << numPoints; - auto attributeViews = ScriptableMesh::gatherBufferViews(mesh); + metadata["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate); + qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices numPoints" << numPoints; + auto attributeViews = buffer_helpers::gatherBufferViews(mesh); for (const auto& a : attributeViews) { auto& view = a.second; auto sz = view._element.getSize(); @@ -477,19 +352,21 @@ QScriptValue ScriptableMesh::unrollVertices(bool recalcNormals) { auto points = gpu::BufferView(buffer, view._element); auto src = (uint8_t*)view._buffer->getData(); auto dest = (uint8_t*)points._buffer->getData(); - auto slot = ScriptableMesh::ATTRIBUTES[a.first]; - qCInfo(mesh_logging) << "ScriptableMesh::unrollVertices buffer" << a.first; - qCInfo(mesh_logging) << "ScriptableMesh::unrollVertices source" << view.getNumElements(); - qCInfo(mesh_logging) << "ScriptableMesh::unrollVertices dest" << points.getNumElements(); - qCInfo(mesh_logging) << "ScriptableMesh::unrollVertices sz" << sz << src << dest << slot; + auto slot = buffer_helpers::ATTRIBUTES[a.first]; + if (0) { + qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices buffer" << a.first; + qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices source" << view.getNumElements(); + qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices dest" << points.getNumElements(); + qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices sz" << sz << src << dest << slot; + } auto esize = indices._element.getSize(); const char* hint= a.first.toStdString().c_str(); for(quint32 i = 0; i < numPoints; i++) { quint32 index = esize == 4 ? indices.get(i) : indices.get(i); newindices.edit(i) = i; - bufferViewElementFromVariant( + buffer_helpers::fromVariant( points, i, - bufferViewElementToVariant(view, index, false, hint) + buffer_helpers::toVariant(view, index, false, hint) ); } if (slot == gpu::Stream::POSITION) { @@ -505,62 +382,70 @@ QScriptValue ScriptableMesh::unrollVertices(bool recalcNormals) { return true; } -bool ScriptableMesh::replaceMeshData(scriptable::ScriptableMeshPointer src, const QVector& attributeNames) { +bool scriptable::ScriptableMeshPart::replaceMeshData(scriptable::ScriptableMeshPartPointer src, const QVector& attributeNames) { auto target = getMeshPointer(); auto source = src ? src->getMeshPointer() : nullptr; if (!target || !source) { - context()->throwError("ScriptableMesh::replaceMeshData -- expected dest and src to be valid mesh proxy pointers"); + if (context()) { + context()->throwError("ScriptableMeshPart::replaceMeshData -- expected dest and src to be valid mesh proxy pointers"); + } else { + qCWarning(graphics_scripting) << "ScriptableMeshPart::replaceMeshData -- expected dest and src to be valid mesh proxy pointers"; + } return false; } - QVector attributes = attributeNames.isEmpty() ? src->getAttributeNames() : attributeNames; + QVector attributes = attributeNames.isEmpty() ? src->parentMesh->getAttributeNames() : attributeNames; - //qCInfo(mesh_logging) << "ScriptableMesh::replaceMeshData -- source:" << source->displayName << "target:" << target->displayName << "attributes:" << attributes; + qCInfo(graphics_scripting) << "ScriptableMeshPart::replaceMeshData -- " << + "source:" << QString::fromStdString(source->displayName) << + "target:" << QString::fromStdString(target->displayName) << + "attributes:" << attributes; + + metadata["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate); // remove attributes only found on target mesh, unless user has explicitly specified the relevant attribute names if (attributeNames.isEmpty()) { - auto attributeViews = ScriptableMesh::gatherBufferViews(target); + auto attributeViews = buffer_helpers::gatherBufferViews(target); for (const auto& a : attributeViews) { - auto slot = ScriptableMesh::ATTRIBUTES[a.first]; + auto slot = buffer_helpers::ATTRIBUTES[a.first]; if (!attributes.contains(a.first)) { - //qCInfo(mesh_logging) << "ScriptableMesh::replaceMeshData -- pruning target attribute" << a.first << slot; + qCInfo(graphics_scripting) << "ScriptableMesh::replaceMeshData -- pruning target attribute" << a.first << slot; target->removeAttribute(slot); } } } - target->setVertexBuffer(cloneBufferView(source->getVertexBuffer())); - target->setIndexBuffer(cloneBufferView(source->getIndexBuffer())); - target->setPartBuffer(cloneBufferView(source->getPartBuffer())); + target->setVertexBuffer(buffer_helpers::clone(source->getVertexBuffer())); + target->setIndexBuffer(buffer_helpers::clone(source->getIndexBuffer())); + target->setPartBuffer(buffer_helpers::clone(source->getPartBuffer())); for (const auto& a : attributes) { - auto slot = ScriptableMesh::ATTRIBUTES[a]; + auto slot = buffer_helpers::ATTRIBUTES[a]; if (slot == gpu::Stream::POSITION) { continue; } - // auto& before = target->getAttributeBuffer(slot); + auto& before = target->getAttributeBuffer(slot); auto& input = source->getAttributeBuffer(slot); if (input.getNumElements() == 0) { - //qCInfo(mesh_logging) << "ScriptableMesh::replaceMeshData buffer is empty -- pruning" << a << slot; + qCInfo(graphics_scripting) << "ScriptableMeshPart::replaceMeshData buffer is empty -- pruning" << a << slot; target->removeAttribute(slot); } else { - // if (before.getNumElements() == 0) { - // qCInfo(mesh_logging) << "ScriptableMesh::replaceMeshData target buffer is empty -- adding" << a << slot; - // } else { - // qCInfo(mesh_logging) << "ScriptableMesh::replaceMeshData target buffer exists -- updating" << a << slot; - // } - target->addAttribute(slot, cloneBufferView(input)); + if (before.getNumElements() == 0) { + qCInfo(graphics_scripting) << "ScriptableMeshPart::replaceMeshData target buffer is empty -- adding" << a << slot; + } else { + qCInfo(graphics_scripting) << "ScriptableMeshPart::replaceMeshData target buffer exists -- updating" << a << slot; + } + target->addAttribute(slot, buffer_helpers::clone(input)); } - // auto& after = target->getAttributeBuffer(slot); - // qCInfo(mesh_logging) << "ScriptableMesh::replaceMeshData" << a << slot << before.getNumElements() << " -> " << after.getNumElements(); + auto& after = target->getAttributeBuffer(slot); + qCInfo(graphics_scripting) << "ScriptableMeshPart::replaceMeshData" << a << slot << before.getNumElements() << " -> " << after.getNumElements(); } return true; } -bool ScriptableMesh::dedupeVertices(float epsilon) { - scriptable::ScriptableMeshPointer meshProxy = this; +bool scriptable::ScriptableMeshPart::dedupeVertices(float epsilon) { auto mesh = getMeshPointer(); if (!mesh) { return false; @@ -573,6 +458,7 @@ bool ScriptableMesh::dedupeVertices(float epsilon) { uniqueVerts.reserve((int)numPositions); QMap remapIndices; + metadata["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate); for (quint32 i = 0; i < numPositions; i++) { const quint32 numUnique = uniqueVerts.size(); const auto& position = positions.get(i); @@ -590,7 +476,7 @@ bool ScriptableMesh::dedupeVertices(float epsilon) { } } - qCInfo(mesh_logging) << "//VERTS before" << numPositions << "after" << uniqueVerts.size(); + qCInfo(graphics_scripting) << "//VERTS before" << numPositions << "after" << uniqueVerts.size(); auto indices = mesh->getIndexBuffer(); auto numIndices = indices.getNumElements(); @@ -600,34 +486,34 @@ bool ScriptableMesh::dedupeVertices(float epsilon) { for (quint32 i = 0; i < numIndices; i++) { quint32 index = esize == 4 ? indices.get(i) : indices.get(i); if (remapIndices.contains(index)) { - //qCInfo(mesh_logging) << i << index << "->" << remapIndices[index]; + //qCInfo(graphics_scripting) << i << index << "->" << remapIndices[index]; newIndices << remapIndices[index]; } else { - qCInfo(mesh_logging) << i << index << "!remapIndices[index]"; + qCInfo(graphics_scripting) << i << index << "!remapIndices[index]"; } } - mesh->setIndexBuffer(bufferViewFromVector(newIndices, { gpu::SCALAR, gpu::UINT32, gpu::INDEX })); - mesh->setVertexBuffer(bufferViewFromVector(uniqueVerts, { gpu::VEC3, gpu::FLOAT, gpu::XYZ })); + mesh->setIndexBuffer(buffer_helpers::fromVector(newIndices, { gpu::SCALAR, gpu::UINT32, gpu::INDEX })); + mesh->setVertexBuffer(buffer_helpers::fromVector(uniqueVerts, { gpu::VEC3, gpu::FLOAT, gpu::XYZ })); - auto attributeViews = ScriptableMesh::gatherBufferViews(mesh); + auto attributeViews = buffer_helpers::gatherBufferViews(mesh); quint32 numUniqueVerts = uniqueVerts.size(); for (const auto& a : attributeViews) { auto& view = a.second; - auto slot = ScriptableMesh::ATTRIBUTES[a.first]; + auto slot = buffer_helpers::ATTRIBUTES[a.first]; if (slot == gpu::Stream::POSITION) { continue; } - qCInfo(mesh_logging) << "ScriptableMesh::dedupeVertices" << a.first << slot << view.getNumElements(); - auto newView = resizedBufferView(view, numUniqueVerts); - qCInfo(mesh_logging) << a.first << "before: #" << view.getNumElements() << "after: #" << newView.getNumElements(); + qCInfo(graphics_scripting) << "ScriptableMeshPart::dedupeVertices" << a.first << slot << view.getNumElements(); + auto newView = buffer_helpers::resize(view, numUniqueVerts); + qCInfo(graphics_scripting) << a.first << "before: #" << view.getNumElements() << "after: #" << newView.getNumElements(); quint32 numElements = (quint32)view.getNumElements(); for (quint32 i = 0; i < numElements; i++) { quint32 fromVertexIndex = i; quint32 toVertexIndex = remapIndices.contains(fromVertexIndex) ? remapIndices[fromVertexIndex] : fromVertexIndex; - bufferViewElementFromVariant( + buffer_helpers::fromVariant( newView, toVertexIndex, - bufferViewElementToVariant(view, fromVertexIndex, false, "dedupe") + buffer_helpers::toVariant(view, fromVertexIndex, false, "dedupe") ); } mesh->addAttribute(slot, newView); @@ -635,120 +521,202 @@ bool ScriptableMesh::dedupeVertices(float epsilon) { return true; } -QScriptValue ScriptableMesh::cloneMesh(bool recalcNormals) { +scriptable::ScriptableMeshPointer scriptable::ScriptableMesh::cloneMesh(bool recalcNormals) { auto mesh = getMeshPointer(); if (!mesh) { - return QScriptValue::NullValue; + qCInfo(graphics_scripting) << "ScriptableMesh::cloneMesh -- !meshPointer"; + return nullptr; } - graphics::MeshPointer clone(new graphics::Mesh()); - clone->displayName = mesh->displayName + "-clone"; - qCInfo(mesh_logging) << "ScriptableMesh::cloneMesh" << !!mesh; - if (!mesh) { - return QScriptValue::NullValue; - } - - clone->setIndexBuffer(cloneBufferView(mesh->getIndexBuffer())); - clone->setPartBuffer(cloneBufferView(mesh->getPartBuffer())); - auto attributeViews = ScriptableMesh::gatherBufferViews(mesh); - for (const auto& a : attributeViews) { - auto& view = a.second; - auto slot = ScriptableMesh::ATTRIBUTES[a.first]; - qCInfo(mesh_logging) << "ScriptableMesh::cloneVertices buffer" << a.first << slot; - auto points = cloneBufferView(view); - qCInfo(mesh_logging) << "ScriptableMesh::cloneVertices source" << view.getNumElements(); - qCInfo(mesh_logging) << "ScriptableMesh::cloneVertices dest" << points.getNumElements(); - if (slot == gpu::Stream::POSITION) { - clone->setVertexBuffer(points); - } else { - clone->addAttribute(slot, points); - } - } - - auto result = scriptable::ScriptableMeshPointer(new ScriptableMesh(nullptr, clone)); + qCInfo(graphics_scripting) << "ScriptableMesh::cloneMesh..."; + auto clone = buffer_helpers::cloneMesh(mesh); + + qCInfo(graphics_scripting) << "ScriptableMesh::cloneMesh..."; if (recalcNormals) { - result->recalculateNormals(); + buffer_helpers::recalculateNormals(clone); } - return engine()->toScriptValue(result); + qCDebug(graphics_scripting) << clone.get();// << metadata; + auto meshPointer = scriptable::make_scriptowned(provider, model, clone, metadata); + clone.reset(); // free local reference + qCInfo(graphics_scripting) << "========= ScriptableMesh::cloneMesh..." << meshPointer << meshPointer->ownedMesh.use_count(); + //scriptable::MeshPointer* ppMesh = new scriptable::MeshPointer(); + //*ppMesh = clone; + + if (meshPointer) { + scriptable::WeakMeshPointer delme = meshPointer->mesh; + QString debugString = scriptable::toDebugString(meshPointer); + QObject::connect(meshPointer, &QObject::destroyed, meshPointer, [=]() { + qCWarning(graphics_scripting) << "*************** cloneMesh/Destroy"; + qCWarning(graphics_scripting) << "*************** " << debugString << delme.lock().get(); + if (!delme.expired()) { + qCWarning(graphics_scripting) << "cloneMesh -- potential memory leak..." << debugString << delme.lock().get(); + } + }); + } + + meshPointer->metadata["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate); + return scriptable::ScriptableMeshPointer(meshPointer); } -bool ScriptableMesh::recalculateNormals() { - scriptable::ScriptableMeshPointer meshProxy = this; - qCInfo(mesh_logging) << "Recalculating normals" << !!meshProxy; - auto mesh = getMeshPointer(); - if (!mesh) { - return false; - } - ScriptableMesh::gatherBufferViews(mesh, { "normal", "color" }); // ensures #normals >= #positions - auto normals = mesh->getAttributeBuffer(gpu::Stream::NORMAL); - auto verts = mesh->getVertexBuffer(); - auto indices = mesh->getIndexBuffer(); - auto esize = indices._element.getSize(); - auto numPoints = indices.getNumElements(); - const auto TRIANGLE = 3; - quint32 numFaces = (quint32)numPoints / TRIANGLE; - //QVector faces; - QVector faceNormals; - QMap> vertexToFaces; - //faces.resize(numFaces); - faceNormals.resize(numFaces); - auto numNormals = normals.getNumElements(); - qCInfo(mesh_logging) << QString("numFaces: %1, numNormals: %2, numPoints: %3").arg(numFaces).arg(numNormals).arg(numPoints); - if (normals.getNumElements() != verts.getNumElements()) { - return false; - } - for (quint32 i = 0; i < numFaces; i++) { - quint32 I = TRIANGLE * i; - quint32 i0 = esize == 4 ? indices.get(I+0) : indices.get(I+0); - quint32 i1 = esize == 4 ? indices.get(I+1) : indices.get(I+1); - quint32 i2 = esize == 4 ? indices.get(I+2) : indices.get(I+2); - - Triangle face = { - verts.get(i1), - verts.get(i2), - verts.get(i0) - }; - faceNormals[i] = face.getNormal(); - if (glm::isnan(faceNormals[i].x)) { - qCInfo(mesh_logging) << i << i0 << i1 << i2 << vec3toVariant(face.v0) << vec3toVariant(face.v1) << vec3toVariant(face.v2); - break; - } - vertexToFaces[glm::to_string(face.v0).c_str()] << i; - vertexToFaces[glm::to_string(face.v1).c_str()] << i; - vertexToFaces[glm::to_string(face.v2).c_str()] << i; - } - for (quint32 j = 0; j < numNormals; j++) { - //auto v = verts.get(j); - glm::vec3 normal { 0.0f, 0.0f, 0.0f }; - QString key { glm::to_string(verts.get(j)).c_str() }; - const auto& faces = vertexToFaces.value(key); - if (faces.size()) { - for (const auto i : faces) { - normal += faceNormals[i]; - } - normal *= 1.0f / (float)faces.size(); - } else { - static int logged = 0; - if (logged++ < 10) { - qCInfo(mesh_logging) << "no faces for key!?" << key; - } - normal = verts.get(j); - } - if (glm::isnan(normal.x)) { - static int logged = 0; - if (logged++ < 10) { - qCInfo(mesh_logging) << "isnan(normal.x)" << j << vec3toVariant(normal); - } - break; - } - normals.edit(j) = glm::normalize(normal); - } - return true; +scriptable::ScriptableMeshBase::ScriptableMeshBase(scriptable::WeakModelProviderPointer provider, scriptable::ScriptableModelBasePointer model, scriptable::WeakMeshPointer mesh, const QVariantMap& metadata) + : provider(provider), model(model), mesh(mesh), metadata(metadata) {} +scriptable::ScriptableMeshBase::ScriptableMeshBase(scriptable::WeakMeshPointer mesh) : scriptable::ScriptableMeshBase(scriptable::WeakModelProviderPointer(), nullptr, mesh, QVariantMap()) { } +scriptable::ScriptableMeshBase::ScriptableMeshBase(scriptable::MeshPointer mesh, const QVariantMap& metadata) + : ScriptableMeshBase(WeakModelProviderPointer(), nullptr, mesh, metadata) { + ownedMesh = mesh; +} +//scriptable::ScriptableMeshBase::ScriptableMeshBase(const scriptable::ScriptableMeshBase& other) { *this = other; } +scriptable::ScriptableMeshBase& scriptable::ScriptableMeshBase::operator=(const scriptable::ScriptableMeshBase& view) { + provider = view.provider; + model = view.model; + mesh = view.mesh; + ownedMesh = view.ownedMesh; + metadata = view.metadata; + return *this; +} + scriptable::ScriptableMeshBase::~ScriptableMeshBase() { + ownedMesh.reset(); + qCInfo(graphics_scripting) << "//~ScriptableMeshBase" << this << "ownedMesh:" << ownedMesh.use_count() << "mesh:" << mesh.use_count(); } -QString ScriptableMesh::toOBJ() { +scriptable::ScriptableMesh::~ScriptableMesh() { + ownedMesh.reset(); + qCInfo(graphics_scripting) << "//~ScriptableMesh" << this << "ownedMesh:" << ownedMesh.use_count() << "mesh:" << mesh.use_count(); +} + +QString scriptable::ScriptableMeshPart::toOBJ() { if (!getMeshPointer()) { - context()->throwError(QString("null mesh")); + if (context()) { + context()->throwError(QString("null mesh")); + } else { + qCWarning(graphics_scripting) << "null mesh"; + } + return QString(); } return writeOBJToString({ getMeshPointer() }); } +namespace { + template + QScriptValue qObjectToScriptValue(QScriptEngine* engine, const T& object) { + if (!object) { + return QScriptValue::NullValue; + } + auto ownership = object->metadata.value("__ownership__"); + return engine->newQObject( + object, + ownership.isValid() ? static_cast(ownership.toInt()) : QScriptEngine::QtOwnership + //, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects + ); + } + + QScriptValue meshPointerToScriptValue(QScriptEngine* engine, const scriptable::ScriptableMeshPointer& in) { + return qObjectToScriptValue(engine, in); + } + QScriptValue meshPartPointerToScriptValue(QScriptEngine* engine, const scriptable::ScriptableMeshPartPointer& in) { + return qObjectToScriptValue(engine, in); + } + QScriptValue modelPointerToScriptValue(QScriptEngine* engine, const scriptable::ScriptableModelPointer& in) { + return qObjectToScriptValue(engine, in); + } + + void meshPointerFromScriptValue(const QScriptValue& value, scriptable::ScriptableMeshPointer &out) { + out = scriptable::qpointer_qobject_cast(value); + } + void modelPointerFromScriptValue(const QScriptValue& value, scriptable::ScriptableModelPointer &out) { + out = scriptable::qpointer_qobject_cast(value); + } + void meshPartPointerFromScriptValue(const QScriptValue& value, scriptable::ScriptableMeshPartPointer &out) { + out = scriptable::qpointer_qobject_cast(value); + } + + // 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); + // } + + QScriptValue qVectorUInt32ToScriptValue(QScriptEngine* engine, const QVector& vector) { + return qScriptValueFromSequence(engine, vector); + } + + void qVectorUInt32FromScriptValue(const QScriptValue& array, QVector& result) { + qScriptValueToSequence(array, result); + } + + QVector metaTypeIds{ + qRegisterMetaType("uint32"), + qRegisterMetaType("scriptable::uint32"), + qRegisterMetaType>(), + qRegisterMetaType>("QVector"), + qRegisterMetaType(), + qRegisterMetaType(), + qRegisterMetaType(), + }; +} + +namespace scriptable { + bool registerMetaTypes(QScriptEngine* engine) { + qScriptRegisterSequenceMetaType>(engine); + qScriptRegisterSequenceMetaType>(engine); + qScriptRegisterSequenceMetaType>(engine); + + qScriptRegisterMetaType(engine, qVectorUInt32ToScriptValue, qVectorUInt32FromScriptValue); + qScriptRegisterMetaType(engine, modelPointerToScriptValue, modelPointerFromScriptValue); + qScriptRegisterMetaType(engine, meshPointerToScriptValue, meshPointerFromScriptValue); + qScriptRegisterMetaType(engine, meshPartPointerToScriptValue, meshPartPointerFromScriptValue); + + return metaTypeIds.size(); + } + // callback helper that lets C++ method signatures remain simple (ie: taking a single callback argument) while + // still supporting extended Qt signal-like (scope, "methodName") and (scope, function(){}) "this" binding conventions + QScriptValue jsBindCallback(QScriptValue callback) { + if (callback.isObject() && callback.property("callback").isFunction()) { + return callback; + } + auto engine = callback.engine(); + auto context = engine ? engine->currentContext() : nullptr; + auto length = context ? context->argumentCount() : 0; + QScriptValue scope = context ? context->thisObject() : QScriptValue::NullValue; + QScriptValue method; + qCInfo(graphics_scripting) << "jsBindCallback" << engine << length << scope.toQObject() << method.toString(); + int i = 0; + for (; context && i < length; i++) { + if (context->argument(i).strictlyEquals(callback)) { + method = context->argument(i+1); + } + } + if (method.isFunction() || method.isString()) { + scope = callback; + } else { + method = callback; + } + qCInfo(graphics_scripting) << "scope:" << scope.toQObject() << "method:" << method.toString(); + return ::makeScopedHandlerObject(scope, method); + } +} + +bool scriptable::GraphicsScriptingInterface::updateMeshPart(ScriptableMeshPointer mesh, ScriptableMeshPartPointer part) { + Q_ASSERT(mesh); + Q_ASSERT(part); + Q_ASSERT(part->parentMesh); + auto tmp = exportMeshPart(mesh, part->partIndex); + if (part->parentMesh == mesh) { + qCInfo(graphics_scripting) << "updateMeshPart -- update via clone" << mesh << part; + tmp->replaceMeshData(part->cloneMeshPart()); + return false; + } else { + qCInfo(graphics_scripting) << "updateMeshPart -- update via inplace" << mesh << part; + tmp->replaceMeshData(part); + return true; + } +} diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h index 257285fa90..c655167c2b 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h @@ -11,142 +11,188 @@ #include +//#include #include +#include #include #include -namespace graphics { - class Mesh; -} -namespace gpu { - class BufferView; -} namespace scriptable { - class ScriptableMeshPart; - using ScriptableMeshPartPointer = QPointer; - class ScriptableMesh : public QObject, QScriptable { + + QScriptValue jsBindCallback(QScriptValue callback); + class ScriptableMesh : public ScriptableMeshBase, QScriptable { Q_OBJECT public: - Q_PROPERTY(quint32 numParts READ getNumParts) - Q_PROPERTY(quint32 numAttributes READ getNumAttributes) - Q_PROPERTY(quint32 numVertices READ getNumVertices) - Q_PROPERTY(quint32 numIndices READ getNumIndices) - Q_PROPERTY(QVariantMap metadata MEMBER _metadata) + Q_PROPERTY(uint32 numParts READ getNumParts) + Q_PROPERTY(uint32 numAttributes READ getNumAttributes) + Q_PROPERTY(uint32 numVertices READ getNumVertices) + Q_PROPERTY(uint32 numIndices READ getNumIndices) + Q_PROPERTY(QVariantMap metadata MEMBER metadata) Q_PROPERTY(QVector attributeNames READ getAttributeNames) + Q_PROPERTY(QVector parts READ getMeshParts) + Q_PROPERTY(bool valid READ hasValidMesh) + bool hasValidMesh() const { return (bool)getMeshPointer(); } + Q_PROPERTY(bool validOwned READ hasValidOwnedMesh) + bool hasValidOwnedMesh() const { return (bool)getOwnedMeshPointer(); } - static QMap ATTRIBUTES; - static std::map gatherBufferViews(MeshPointer mesh, const QStringList& expandToMatchPositions = QStringList()); + operator const ScriptableMeshBase*() const { return (qobject_cast(this)); } + ScriptableMesh(scriptable::MeshPointer mesh) : ScriptableMeshBase(mesh) { ownedMesh = mesh; } + ScriptableMesh(WeakModelProviderPointer provider, ScriptableModelBasePointer model, MeshPointer mesh, const QVariantMap& metadata) + : ScriptableMeshBase(provider, model, mesh, metadata) { ownedMesh = mesh; } + //ScriptableMesh& operator=(const ScriptableMesh& other) { model=other.model; mesh=other.mesh; metadata=other.metadata; return *this; }; + //ScriptableMesh() : QObject(), model(nullptr) {} + //ScriptableMesh(const ScriptableMesh& other) : QObject(), model(other.model), mesh(other.mesh), metadata(other.metadata) {} + ScriptableMesh(const ScriptableMeshBase& other); + ScriptableMesh(const ScriptableMesh& other) : ScriptableMeshBase(other) {}; + virtual ~ScriptableMesh(); - ScriptableMesh& operator=(const ScriptableMesh& other) { _model=other._model; _mesh=other._mesh; _metadata=other._metadata; return *this; }; - ScriptableMesh() : QObject(), _model(nullptr) {} - ScriptableMesh(ScriptableModelPointer parent, scriptable::MeshPointer mesh) : QObject(), _model(parent), _mesh(mesh) {} - ScriptableMesh(const ScriptableMesh& other) : QObject(), _model(other._model), _mesh(other._mesh), _metadata(other._metadata) {} - ~ScriptableMesh() { qDebug() << "~ScriptableMesh" << this; } - - scriptable::MeshPointer getMeshPointer() const { return _mesh; } + Q_INVOKABLE const scriptable::ScriptableModelPointer getParentModel() const { return qobject_cast(model); } + Q_INVOKABLE const scriptable::MeshPointer getOwnedMeshPointer() const { return ownedMesh; } + scriptable::ScriptableMeshPointer getSelf() const { return const_cast(this); } public slots: - quint32 getNumParts() const; - quint32 getNumVertices() const; - quint32 getNumAttributes() const; - quint32 getNumIndices() const { return 0; } + uint32 getNumParts() const; + uint32 getNumVertices() const; + uint32 getNumAttributes() const; + uint32 getNumIndices() const; QVector getAttributeNames() const; + QVector getMeshParts() const; - QVariantMap getVertexAttributes(quint32 vertexIndex) const; - QVariantMap getVertexAttributes(quint32 vertexIndex, QVector attributes) const; + QVariantMap getVertexAttributes(uint32 vertexIndex) const; + QVariantMap getVertexAttributes(uint32 vertexIndex, QVector attributes) const; - QVector getIndices() const; - QVector findNearbyIndices(const glm::vec3& origin, float epsilon = 1e-6) const; + QVector getIndices() const; + QVector findNearbyIndices(const glm::vec3& origin, float epsilon = 1e-6) const; QVariantMap getMeshExtents() const; - bool setVertexAttributes(quint32 vertexIndex, QVariantMap attributes); - QVariantMap scaleToFit(float unitScale); + bool setVertexAttributes(uint32 vertexIndex, QVariantMap attributes); QVariantList getAttributeValues(const QString& attributeName) const; int _getSlotNumber(const QString& attributeName) const; + scriptable::ScriptableMeshPointer cloneMesh(bool recalcNormals = false); + public: + operator bool() const { return !mesh.expired(); } + + public slots: + // QScriptEngine-specific wrappers + uint32 mapAttributeValues(QScriptValue callback); + }; + + // TODO: part-specific wrapper for working with raw geometries + class ScriptableMeshPart : public QObject, QScriptable { + Q_OBJECT + public: + Q_PROPERTY(uint32 partIndex MEMBER partIndex CONSTANT) + Q_PROPERTY(int numElementsPerFace MEMBER _elementsPerFace CONSTANT) + Q_PROPERTY(QString topology MEMBER _topology CONSTANT) + + Q_PROPERTY(uint32 numFaces READ getNumFaces) + Q_PROPERTY(uint32 numAttributes READ getNumAttributes) + Q_PROPERTY(uint32 numVertices READ getNumVertices) + Q_PROPERTY(uint32 numIndices READ getNumIndices) + Q_PROPERTY(QVector attributeNames READ getAttributeNames) + + Q_PROPERTY(QVariantMap metadata MEMBER metadata) + + //Q_PROPERTY(scriptable::ScriptableMeshPointer parentMesh MEMBER parentMesh CONSTANT HIDE) + + ScriptableMeshPart(scriptable::ScriptableMeshPointer parentMesh, int partIndex); + ScriptableMeshPart& operator=(const ScriptableMeshPart& view) { parentMesh=view.parentMesh; return *this; }; + ScriptableMeshPart(const ScriptableMeshPart& other) : parentMesh(other.parentMesh), partIndex(other.partIndex) {} + ~ScriptableMeshPart() { qDebug() << "~ScriptableMeshPart" << this; } + + public slots: + scriptable::ScriptableMeshPointer getParentMesh() const { return parentMesh; } + uint32 getNumAttributes() const { return parentMesh ? parentMesh->getNumAttributes() : 0; } + uint32 getNumVertices() const { return parentMesh ? parentMesh->getNumVertices() : 0; } + uint32 getNumIndices() const { return parentMesh ? parentMesh->getNumIndices() : 0; } + uint32 getNumFaces() const { return parentMesh ? parentMesh->getNumIndices() / _elementsPerFace : 0; } + QVector getAttributeNames() const { return parentMesh ? parentMesh->getAttributeNames() : QVector(); } + QVector getFace(uint32 faceIndex) const { + auto inds = parentMesh ? parentMesh->getIndices() : QVector(); + return faceIndex+2 < (uint32)inds.size() ? inds.mid(faceIndex*3, 3) : QVector(); + } + QVariantMap scaleToFit(float unitScale); QVariantMap translate(const glm::vec3& translation); QVariantMap scale(const glm::vec3& scale, const glm::vec3& origin = glm::vec3(NAN)); QVariantMap rotateDegrees(const glm::vec3& eulerAngles, const glm::vec3& origin = glm::vec3(NAN)); QVariantMap rotate(const glm::quat& rotation, const glm::vec3& origin = glm::vec3(NAN)); QVariantMap transform(const glm::mat4& transform); - public: - operator bool() const { return _mesh != nullptr; } - ScriptableModelPointer _model; - scriptable::MeshPointer _mesh; - QVariantMap _metadata; + bool unrollVertices(bool recalcNormals = false); + bool dedupeVertices(float epsilon = 1e-6); + bool recalculateNormals() { return buffer_helpers::recalculateNormals(getMeshPointer()); } + + bool replaceMeshData(scriptable::ScriptableMeshPartPointer source, const QVector& attributeNames = QVector()); + scriptable::ScriptableMeshPartPointer cloneMeshPart(bool recalcNormals = false) { + if (parentMesh) { + if (auto clone = parentMesh->cloneMesh(recalcNormals)) { + return clone->getMeshParts().value(partIndex); + } + } + return nullptr; + } + QString toOBJ(); public slots: // QScriptEngine-specific wrappers - QScriptValue mapAttributeValues(QScriptValue scopeOrCallback, QScriptValue methodOrName = QScriptValue()); - bool dedupeVertices(float epsilon = 1e-6); - bool recalculateNormals(); - QScriptValue cloneMesh(bool recalcNormals = true); - QScriptValue unrollVertices(bool recalcNormals = true); - bool replaceMeshData(scriptable::ScriptableMeshPointer source, const QVector& attributeNames = QVector()); - QString toOBJ(); - }; - - // TODO: part-specific wrapper for working with raw geometries - class ScriptableMeshPart : public QObject { - Q_OBJECT - public: - Q_PROPERTY(QString topology READ getTopology) - Q_PROPERTY(quint32 numFaces READ getNumFaces) - - ScriptableMeshPart& operator=(const ScriptableMeshPart& view) { parentMesh=view.parentMesh; return *this; }; - ScriptableMeshPart(const ScriptableMeshPart& other) : parentMesh(other.parentMesh) {} - ScriptableMeshPart() {} - ~ScriptableMeshPart() { qDebug() << "~ScriptableMeshPart" << this; } - - public slots: - QString getTopology() const { return "triangles"; } - quint32 getNumFaces() const { return parentMesh.getIndices().size() / 3; } - QVector getFace(quint32 faceIndex) const { - auto inds = parentMesh.getIndices(); - return faceIndex+2 < (quint32)inds.size() ? inds.mid(faceIndex*3, 3) : QVector(); - } + uint32 mapAttributeValues(QScriptValue callback); public: - scriptable::ScriptableMesh parentMesh; - int partIndex; + scriptable::ScriptableMeshPointer parentMesh; + uint32 partIndex; + QVariantMap metadata; + protected: + int _elementsPerFace{ 3 }; + QString _topology{ "triangles" }; + scriptable::MeshPointer getMeshPointer() const { return parentMesh ? parentMesh->getMeshPointer() : nullptr; } }; - class GraphicsScriptingInterface : public QObject { + class GraphicsScriptingInterface : public QObject, QScriptable { Q_OBJECT public: GraphicsScriptingInterface(QObject* parent = nullptr) : QObject(parent) {} GraphicsScriptingInterface(const GraphicsScriptingInterface& other) {} - public slots: - ScriptableMeshPart exportMeshPart(ScriptableMesh mesh, int part) { return {}; } + public slots: + ScriptableMeshPartPointer exportMeshPart(ScriptableMeshPointer mesh, int part=0) { + return ScriptableMeshPartPointer(new ScriptableMeshPart(mesh, part)); + } + bool updateMeshPart(ScriptableMeshPointer mesh, ScriptableMeshPartPointer part); }; + + // callback helper that lets C++ method signatures remain simple (ie: taking a single callback argument) while + // still supporting extended Qt signal-like (scope, "methodName") and (scope, function(){}) "this" binding conventions + QScriptValue jsBindCallback(QScriptValue callback); + + // derive a corresponding C++ class instance from the current script engine's thisObject + template T this_qobject_cast(QScriptEngine* engine); } Q_DECLARE_METATYPE(scriptable::ScriptableMeshPointer) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(scriptable::ScriptableMeshPartPointer) +Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(scriptable::GraphicsScriptingInterface) // FIXME: MESHFACES: faces were supported in the original Model.* API -- are they still needed/used/useful for anything yet? #include namespace mesh { - using uint32 = quint32; class MeshFace; using MeshFaces = QVector; class MeshFace { public: MeshFace() {} - MeshFace(QVector vertexIndices) : vertexIndices(vertexIndices) {} + MeshFace(QVector vertexIndices) : vertexIndices(vertexIndices) {} ~MeshFace() {} - QVector vertexIndices; + QVector vertexIndices; // TODO -- material... }; }; Q_DECLARE_METATYPE(mesh::MeshFace) Q_DECLARE_METATYPE(QVector) -Q_DECLARE_METATYPE(mesh::uint32) -Q_DECLARE_METATYPE(QVector) +Q_DECLARE_METATYPE(scriptable::uint32) +Q_DECLARE_METATYPE(QVector) diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h index 4ba5a993b1..97a73ddd61 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h @@ -1,56 +1,24 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include -#include +#include "Forward.h" -#include - -namespace graphics { - class Mesh; -} -namespace gpu { - class BufferView; -} class QScriptValue; - namespace scriptable { - using Mesh = graphics::Mesh; - using MeshPointer = std::shared_ptr; - - class ScriptableModel; - using ScriptableModelPointer = QPointer; - class ScriptableMesh; - using ScriptableMeshPointer = QPointer; - - // abstract container for holding one or more scriptable meshes - class ScriptableModel : public QObject { + class ScriptableModel : public ScriptableModelBase { Q_OBJECT public: - QUuid objectID; - QVariantMap metadata; - QVector meshes; - - Q_PROPERTY(QVector meshes READ getMeshes) Q_PROPERTY(QUuid objectID MEMBER objectID CONSTANT) Q_PROPERTY(QVariantMap metadata MEMBER metadata CONSTANT) - Q_INVOKABLE QString toString() const; + Q_PROPERTY(uint32 numMeshes READ getNumMeshes) + Q_PROPERTY(QVector meshes READ getMeshes) - ScriptableModel(QObject* parent = nullptr) : QObject(parent) {} - ScriptableModel(const ScriptableModel& other) : objectID(other.objectID), metadata(other.metadata), meshes(other.meshes) {} - ScriptableModel& operator=(const ScriptableModel& view) { objectID = view.objectID; metadata = view.metadata; meshes = view.meshes; return *this; } - ~ScriptableModel() { qDebug() << "~ScriptableModel" << this; } - - void mixin(const ScriptableModel& other) { - for (const auto& key : other.metadata.keys()) { metadata[key] = other.metadata[key]; } - for (const auto& mesh : other.meshes) { meshes << mesh; } - } + ScriptableModel(QObject* parent = nullptr) : ScriptableModelBase(parent) {} + 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 // QVariantMap shapes; // QVariantMap materials; @@ -58,28 +26,23 @@ namespace scriptable { QVector getMeshes(); const QVector getConstMeshes() const; + operator scriptable::ScriptableModelBasePointer() { + QPointer p; + p = qobject_cast(this); + return p; + } + // QScriptEngine-specific wrappers - Q_INVOKABLE QScriptValue mapAttributeValues(QScriptValue scopeOrCallback, QScriptValue methodOrName); + Q_INVOKABLE uint32 mapAttributeValues(QScriptValue callback); + Q_INVOKABLE QString toString() const; + Q_INVOKABLE uint32 getNumMeshes() { return meshes.size(); } }; - // mixin class for Avatar/Entity/Overlay Rendering that expose their in-memory graphics::Meshes - class ModelProvider { - public: - QVariantMap metadata; - static scriptable::ScriptableModel modelUnavailableError(bool* ok) { if (ok) { *ok = false; } return {}; } - virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) = 0; - }; - using ModelProviderPointer = std::shared_ptr; - - // mixin class for Application to resolve UUIDs into a corresponding ModelProvider - class ModelProviderFactory : public Dependency { - public: - virtual scriptable::ModelProviderPointer lookupModelProvider(QUuid uuid) = 0; - }; } Q_DECLARE_METATYPE(scriptable::MeshPointer) -Q_DECLARE_METATYPE(scriptable::ScriptableModel) +Q_DECLARE_METATYPE(scriptable::WeakMeshPointer) Q_DECLARE_METATYPE(scriptable::ScriptableModelPointer) - +Q_DECLARE_METATYPE(scriptable::ScriptableModelBase) +Q_DECLARE_METATYPE(scriptable::ScriptableModelBasePointer) From 5791ca4c5111faa42e0f8f64bd9b0806bdf3606c Mon Sep 17 00:00:00 2001 From: humbletim Date: Tue, 20 Feb 2018 12:22:04 -0500 Subject: [PATCH 04/29] interim checkin --- interface/src/Application.cpp | 1 + .../src/avatars-renderer/Avatar.cpp | 22 +-- .../src/avatars-renderer/Avatar.h | 5 +- .../src/RenderableModelEntityItem.cpp | 3 +- .../src/RenderablePolyLineEntityItem.cpp | 2 +- .../src/RenderablePolyLineEntityItem.h | 2 +- .../src/RenderablePolyVoxEntityItem.cpp | 15 +- .../src/RenderablePolyVoxEntityItem.h | 4 +- .../src/RenderableShapeEntityItem.cpp | 9 +- .../src/RenderableShapeEntityItem.h | 2 +- libraries/fbx/src/FBXReader.cpp | 5 +- libraries/fbx/src/OBJWriter.cpp | 7 +- .../GraphicsScriptingUtil.h | 10 + .../ModelScriptingInterface.cpp | 8 +- .../src/graphics-scripting/ScriptableMesh.cpp | 31 ++- .../src/graphics-scripting/ScriptableMesh.h | 2 +- .../graphics-scripting/ScriptableModel.cpp | 149 ++++++++++++++ .../src/graphics-scripting/ScriptableModel.h | 2 +- libraries/graphics/src/graphics/Geometry.h | 3 +- libraries/render-utils/src/GeometryCache.cpp | 3 +- libraries/render-utils/src/Model.cpp | 184 ++++++++++++------ libraries/render-utils/src/Model.h | 5 +- .../src/Model_temporary_hack.cpp.h | 121 ++++++++++++ .../src/AssetScriptingInterface.cpp | 2 +- libraries/script-engine/src/ScriptEngines.cpp | 30 ++- libraries/script-engine/src/ScriptEngines.h | 11 +- .../src/shared/ScriptInitializerMixin.h | 38 ++++ 27 files changed, 557 insertions(+), 119 deletions(-) create mode 100644 libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp create mode 100644 libraries/render-utils/src/Model_temporary_hack.cpp.h create mode 100644 libraries/shared/src/shared/ScriptInitializerMixin.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bdef2f456b..5bfb1846a5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -771,6 +771,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(std::bind(&Application::getUserAgent, qApp)); DependencyManager::set(); DependencyManager::set(ScriptEngine::CLIENT_SCRIPT); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 8e22f355e4..066afac8f5 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -35,7 +35,7 @@ #include "ModelEntityItem.h" #include "RenderableModelEntityItem.h" -#include +#include #include "Logging.h" @@ -1763,24 +1763,24 @@ float Avatar::getUnscaledEyeHeightFromSkeleton() const { } } -scriptable::ScriptableModel Avatar::getScriptableModel(bool* ok) { +scriptable::ScriptableModelBase Avatar::getScriptableModel(bool* ok) { qDebug() << "Avatar::getScriptableModel" ; if (!_skeletonModel || !_skeletonModel->isLoaded()) { return scriptable::ModelProvider::modelUnavailableError(ok); } - scriptable::ScriptableModel result; - result.metadata = { + scriptable::ScriptableModelBase result = _skeletonModel->getScriptableModel(ok); + result.objectID = getSessionUUID(); + result.mixin({ { "avatarID", getSessionUUID().toString() }, { "url", _skeletonModelURL.toString() }, { "origin", "Avatar/avatar::" + _displayName }, { "textures", _skeletonModel->getTextures() }, - }; - result.mixin(_skeletonModel->getScriptableModel(ok)); - - // FIXME: for now access to attachment models are merged with the main avatar model - for (auto& attachmentModel : _attachmentModels) { - if (attachmentModel->isLoaded()) { - result.mixin(attachmentModel->getScriptableModel(ok)); + }); + // FIXME: for now access to attachment models are merged into the main avatar ScriptableModel set + for (int i = 0; i < (int)_attachmentModels.size(); i++) { + auto& model = _attachmentModels.at(i); + if (model->isLoaded()) { + result.append(model->getScriptableModel(ok), _attachmentData.at(i).toVariant().toMap()); } } if (ok) { diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index 5cfc399b65..50301a2507 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -1,3 +1,4 @@ + // // Avatar.h // interface/src/avatar @@ -20,7 +21,7 @@ #include #include #include -#include +#include #include @@ -274,7 +275,7 @@ public: virtual void setAvatarEntityDataChanged(bool value) override; - virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override; + 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/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 3d6714a400..155580d885 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -962,7 +962,6 @@ scriptable::ScriptableModelBase render::entities::ModelEntityRenderer::getScript } bool render::entities::ModelEntityRenderer::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer newModel, int meshIndex, int partIndex) { - qCDebug(entitiesrenderer) << "REPLACING RenderableModelEntityItem" << newModel->objectName(); ModelPointer model; withReadLock([&] { model = _model; }); @@ -970,7 +969,7 @@ bool render::entities::ModelEntityRenderer::replaceScriptableModelMeshPart(scrip return false; } - return _model->replaceScriptableModelMeshPart(newModel, meshIndex, partIndex); + return model->replaceScriptableModelMeshPart(newModel, meshIndex, partIndex); } void RenderableModelEntityItem::simulateRelayedJoints() { diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp index e18bd2a7fe..a70a1613c3 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.cpp @@ -281,7 +281,7 @@ std::vector PolyLineEntityRenderer::updateVertic return vertices; } -scriptable::ScriptableModel PolyLineEntityRenderer::getScriptableModel(bool *ok) { +scriptable::ScriptableModelBase PolyLineEntityRenderer::getScriptableModel(bool *ok) { // TODO: adapt polyline into a triangles mesh... return EntityRenderer::getScriptableModel(ok); } diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index 3bb8901178..d9d770e64f 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -25,7 +25,7 @@ class PolyLineEntityRenderer : public TypedEntityRenderer { public: PolyLineEntityRenderer(const EntityItemPointer& entity); - virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override; + virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override; protected: virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index fd923c40b0..454fba4f94 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -1414,14 +1414,14 @@ void RenderablePolyVoxEntityItem::bonkNeighbors() { } } -scriptable::ScriptableModel RenderablePolyVoxEntityItem::getScriptableModel(bool * ok) { +scriptable::ScriptableModelBase RenderablePolyVoxEntityItem::getScriptableModel(bool * ok) { if (!updateDependents() || !_mesh) { return scriptable::ModelProvider::modelUnavailableError(ok); } bool success = false; glm::mat4 transform = voxelToLocalMatrix(); - scriptable::ScriptableModel result; + scriptable::ScriptableModelBase result; withReadLock([&] { gpu::BufferView::Index numVertices = (gpu::BufferView::Index)_mesh->getNumVertices(); if (!_meshReady) { @@ -1433,11 +1433,12 @@ scriptable::ScriptableModel RenderablePolyVoxEntityItem::getScriptableModel(bool } else { success = true; // the mesh will be in voxel-space. transform it into object-space - result.meshes << - _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.append(_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; } + )); } }); if (ok) { diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 55b9be23d8..733d5b62f5 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -113,7 +113,7 @@ public: void setVolDataDirty() { withWriteLock([&] { _volDataDirty = true; _meshReady = false; }); } - virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override; + virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override; private: bool updateOnCount(const ivec3& v, uint8_t toValue); @@ -163,7 +163,7 @@ class PolyVoxEntityRenderer : public TypedEntityRenderer()->getScriptableModel(ok); } diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 746102681c..5ea1c9edb7 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -157,8 +157,8 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { args->_details._trianglesRendered += (int)triCount; } -scriptable::ScriptableModel ShapeEntityRenderer::getScriptableModel(bool* ok) { - scriptable::ScriptableModel result; +scriptable::ScriptableModelBase ShapeEntityRenderer::getScriptableModel(bool* ok) { + scriptable::ScriptableModelBase result; result.metadata = { { "entityID", getEntity()->getID().toString() }, { "shape", entity::stringFromShape(_shape) }, @@ -169,7 +169,10 @@ scriptable::ScriptableModel ShapeEntityRenderer::getScriptableModel(bool* ok) { auto vertexColor = glm::vec3(_color); auto success = false; if (auto mesh = geometryCache->meshFromShape(geometryShape, vertexColor)) { - result.meshes << mesh; + scriptable::ScriptableMeshBase base{ mesh, { + { "shape", entity::stringFromShape(_shape) }, + }}; + result.append(base); success = true; } if (ok) { diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index 6ada7e7317..57f899641a 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -22,7 +22,7 @@ class ShapeEntityRenderer : public TypedEntityRenderer { public: ShapeEntityRenderer(const EntityItemPointer& entity); - virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override; + virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override; private: virtual bool needsRenderUpdate() const override; diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 50abe7928f..402afea2cc 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1897,7 +1897,8 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS geometry.meshes.append(extracted.mesh); int meshIndex = geometry.meshes.size() - 1; if (extracted.mesh._mesh) { - extracted.mesh._mesh->displayName = QString("%1#/mesh/%2").arg(url).arg(meshIndex); + extracted.mesh._mesh->displayName = QString("%1#/mesh/%2").arg(url).arg(meshIndex).toStdString(); + extracted.mesh._mesh->modelName = modelIDsToNames.value(modelID).toStdString(); } meshIDsToMeshIndices.insert(it.key(), meshIndex); } @@ -1983,7 +1984,7 @@ FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QStri reader._loadLightmaps = loadLightmaps; reader._lightmapLevel = lightmapLevel; - qDebug() << "Reading FBX: " << url; + qCDebug(modelformat) << "Reading FBX: " << url; return reader.extractFBXGeometry(mapping, url); } diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp index 5307f49f36..621852f591 100644 --- a/libraries/fbx/src/OBJWriter.cpp +++ b/libraries/fbx/src/OBJWriter.cpp @@ -71,7 +71,8 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { out << formatFloat(v[1]) << " "; out << formatFloat(v[2]); if (colorIndex < numColors) { - glm::vec3 color = colorsBufferView.get(colorIndex); + glm::vec3 color = glmVecFromVariant(buffer_helpers::toVariant(colorsBufferView, colorIndex)); + //glm::vec3 color = colorsBufferView.get(colorIndex); out << " " << formatFloat(color[0]); out << " " << formatFloat(color[1]); out << " " << formatFloat(color[2]); @@ -94,7 +95,7 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(gpu::Stream::InputSlot::NORMAL); gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements(); for (gpu::BufferView::Index i = 0; i < numNormals; i++) { - glm::vec3 normal = glmVecFromVariant(bufferViewElementToVariant(normalsBufferView, i)); + glm::vec3 normal = glmVecFromVariant(buffer_helpers::toVariant(normalsBufferView, i)); //glm::vec3 normal = normalsBufferView.get(i); out << "vn "; out << formatFloat(normal[0]) << " "; @@ -117,7 +118,7 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { const gpu::BufferView& indexBuffer = mesh->getIndexBuffer(); graphics::Index partCount = (graphics::Index)mesh->getNumParts(); - QString name = (!mesh->displayName.size() ? QString("mesh-%1-part").arg(nth) : QString(mesh->displayName)) + QString name = (!mesh->displayName.size() ? QString("mesh-%1-part").arg(nth) : QString::fromStdString(mesh->displayName)) .replace(QRegExp("[^-_a-zA-Z0-9]"), "_"); for (int partIndex = 0; partIndex < partCount; partIndex++) { const graphics::Mesh::Part& part = partBuffer.get(partIndex); diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h index a536fc413c..cfa510f87f 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h @@ -19,7 +19,9 @@ namespace scriptable { // JS => QPointer template QPointer qpointer_qobject_cast(const QScriptValue& value) { auto obj = value.toQObject(); +#ifdef SCRIPTABLE_MESH_DEBUG qCInfo(graphics_scripting) << "qpointer_qobject_cast" << obj << value.toString(); +#endif if (auto tmp = qobject_cast(obj)) { return QPointer(tmp); } @@ -41,11 +43,15 @@ namespace scriptable { // C++ > QtOwned instance template std::shared_ptr make_qtowned(Rest... rest) { T* tmp = new T(rest...); +#ifdef SCRIPTABLE_MESH_DEBUG qCInfo(graphics_scripting) << "scriptable::make_qtowned" << toDebugString(tmp); +#endif QString debug = toDebugString(tmp); if (tmp) { tmp->metadata["__ownership__"] = QScriptEngine::QtOwnership; +#ifdef SCRIPTABLE_MESH_DEBUG QObject::connect(tmp, &QObject::destroyed, [=]() { qCInfo(graphics_scripting) << "-------- ~scriptable::make_qtowned" << debug; }); +#endif auto ptr = std::shared_ptr(tmp, [debug](T* tmp) { //qDebug() << "~std::shared_ptr" << debug; delete tmp; @@ -58,7 +64,9 @@ namespace scriptable { // C++ > ScriptOwned JS instance template QPointer make_scriptowned(Rest... rest) { T* tmp = new T(rest...); +#ifdef SCRIPTABLE_MESH_DEBUG qCInfo(graphics_scripting) << "scriptable::make_scriptowned" << toDebugString(tmp); +#endif if (tmp) { tmp->metadata["__ownership__"] = QScriptEngine::ScriptOwnership; //auto blah = (DeleterFunction)[](void* delme) { }; @@ -71,12 +79,14 @@ namespace scriptable { template QPointer add_scriptowned_destructor(T* tmp) { QString debug = toDebugString(tmp); if (tmp) { +#ifdef SCRIPTABLE_MESH_DEBUG QObject::connect(tmp, &QObject::destroyed, [=]() { qCInfo(graphics_scripting) << "-------- ~scriptable::make_scriptowned" << debug;// << !!customDeleter; //if (customDeleter) { // customDeleter(tmp); //} }); +#endif } else { qCInfo(graphics_scripting) << "add_scriptowned_destructor -- not connecting to null value" << debug; } diff --git a/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp index ab9403a8ed..d78f646087 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp @@ -48,7 +48,7 @@ bool ModelScriptingInterface::updateMeshes(QUuid uuid, const scriptable::Scripta bool ModelScriptingInterface::updateMeshes(QUuid uuid, const scriptable::ScriptableModelPointer model) { auto appProvider = DependencyManager::get(); - qCDebug(graphics_scripting) << "appProvider" << appProvider.data(); + //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()) { @@ -56,12 +56,12 @@ bool ModelScriptingInterface::updateMeshes(QUuid uuid, const scriptable::Scripta } bool success = false; if (provider) { - qCDebug(graphics_scripting) << "fetching meshes from " << providerType << "..."; + //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; + //qCDebug(graphics_scripting) << "as base" << base; if (base) { //auto meshes = model->getConstMeshes(); success = provider->replaceScriptableModelMeshPart(base, -1, -1); diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp index b83b901acd..c662371c89 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp @@ -28,6 +28,8 @@ #include "OBJWriter.h" +// #define SCRIPTABLE_MESH_DEBUG + namespace scriptable { // QScriptValue jsBindCallback(QScriptValue callback); // template QPointer qpointer_qobject_cast(const QScriptValue& value); @@ -287,7 +289,9 @@ quint32 scriptable::ScriptableMesh::mapAttributeValues(QScriptValue _callback) { return 0; } auto meshPart = js ? js->toScriptValue(getSelf()) : QScriptValue::NullValue; +#ifdef SCRIPTABLE_MESH_DEBUG qCInfo(graphics_scripting) << "mapAttributeValues" << mesh.get() << js->currentContext()->thisObject().toQObject(); +#endif auto obj = js->newObject(); auto attributeViews = buffer_helpers::gatherBufferViews(mesh, { "normal", "color" }); metadata["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate); @@ -328,9 +332,10 @@ quint32 scriptable::ScriptableMeshPart::mapAttributeValues(QScriptValue callback } bool scriptable::ScriptableMeshPart::unrollVertices(bool recalcNormals) { - auto meshProxy = this; auto mesh = getMeshPointer(); +#ifdef SCRIPTABLE_MESH_DEBUG qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices" << !!mesh<< !!meshProxy; +#endif if (!mesh) { return false; } @@ -527,28 +532,32 @@ scriptable::ScriptableMeshPointer scriptable::ScriptableMesh::cloneMesh(bool rec qCInfo(graphics_scripting) << "ScriptableMesh::cloneMesh -- !meshPointer"; return nullptr; } - qCInfo(graphics_scripting) << "ScriptableMesh::cloneMesh..."; + // qCInfo(graphics_scripting) << "ScriptableMesh::cloneMesh..."; auto clone = buffer_helpers::cloneMesh(mesh); - qCInfo(graphics_scripting) << "ScriptableMesh::cloneMesh..."; + // qCInfo(graphics_scripting) << "ScriptableMesh::cloneMesh..."; if (recalcNormals) { buffer_helpers::recalculateNormals(clone); } - qCDebug(graphics_scripting) << clone.get();// << metadata; + //qCDebug(graphics_scripting) << clone.get();// << metadata; auto meshPointer = scriptable::make_scriptowned(provider, model, clone, metadata); clone.reset(); // free local reference - qCInfo(graphics_scripting) << "========= ScriptableMesh::cloneMesh..." << meshPointer << meshPointer->ownedMesh.use_count(); + // qCInfo(graphics_scripting) << "========= ScriptableMesh::cloneMesh..." << meshPointer << meshPointer->ownedMesh.use_count(); //scriptable::MeshPointer* ppMesh = new scriptable::MeshPointer(); //*ppMesh = clone; - if (meshPointer) { + if (0 && meshPointer) { scriptable::WeakMeshPointer delme = meshPointer->mesh; QString debugString = scriptable::toDebugString(meshPointer); QObject::connect(meshPointer, &QObject::destroyed, meshPointer, [=]() { - qCWarning(graphics_scripting) << "*************** cloneMesh/Destroy"; - qCWarning(graphics_scripting) << "*************** " << debugString << delme.lock().get(); + // qCWarning(graphics_scripting) << "*************** cloneMesh/Destroy"; + // qCWarning(graphics_scripting) << "*************** " << debugString << delme.lock().get(); if (!delme.expired()) { - qCWarning(graphics_scripting) << "cloneMesh -- potential memory leak..." << debugString << delme.lock().get(); + QTimer::singleShot(250, this, [=]{ + if (!delme.expired()) { + qCWarning(graphics_scripting) << "cloneMesh -- potential memory leak..." << debugString << delme.use_count(); + } + }); } }); } @@ -575,12 +584,16 @@ scriptable::ScriptableMeshBase& scriptable::ScriptableMeshBase::operator=(const } scriptable::ScriptableMeshBase::~ScriptableMeshBase() { ownedMesh.reset(); +#ifdef SCRIPTABLE_MESH_DEBUG qCInfo(graphics_scripting) << "//~ScriptableMeshBase" << this << "ownedMesh:" << ownedMesh.use_count() << "mesh:" << mesh.use_count(); +#endif } scriptable::ScriptableMesh::~ScriptableMesh() { ownedMesh.reset(); +#ifdef SCRIPTABLE_MESH_DEBUG qCInfo(graphics_scripting) << "//~ScriptableMesh" << this << "ownedMesh:" << ownedMesh.use_count() << "mesh:" << mesh.use_count(); +#endif } QString scriptable::ScriptableMeshPart::toOBJ() { diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h index c655167c2b..ba0efa007d 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h @@ -100,7 +100,7 @@ namespace scriptable { ScriptableMeshPart(scriptable::ScriptableMeshPointer parentMesh, int partIndex); ScriptableMeshPart& operator=(const ScriptableMeshPart& view) { parentMesh=view.parentMesh; return *this; }; ScriptableMeshPart(const ScriptableMeshPart& other) : parentMesh(other.parentMesh), partIndex(other.partIndex) {} - ~ScriptableMeshPart() { qDebug() << "~ScriptableMeshPart" << this; } + // ~ScriptableMeshPart() { qDebug() << "~ScriptableMeshPart" << this; } public slots: scriptable::ScriptableMeshPointer getParentMesh() const { return parentMesh; } diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp new file mode 100644 index 0000000000..c8f1975249 --- /dev/null +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp @@ -0,0 +1,149 @@ +// +// ScriptableModel.cpp +// libraries/graphics-scripting +// +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "GraphicsScriptingUtil.h" +#include "ScriptableModel.h" +#include "ScriptableMesh.h" + +#include +//#include "ScriptableModel.moc" + +void scriptable::ScriptableModelBase::mixin(const QVariantMap& modelMetaData) { + for (const auto& key : modelMetaData.keys()) { + const auto& value = modelMetaData[key]; + if (metadata.contains(key) && metadata[key].type() == (QVariant::Type)QMetaType::QVariantList) { + qCDebug(graphics_scripting) << "CONCATENATING" << key << metadata[key].toList().size() << "+" << value.toList().size(); + metadata[key] = metadata[key].toList() + value.toList(); + } else { + metadata[key] = modelMetaData[key]; + } + } +} + +scriptable::ScriptableModelBase::~ScriptableModelBase() { +#ifdef SCRIPTABLE_MESH_DEBUG + qCDebug(graphics_scripting) << "~ScriptableModelBase" << this; +#endif + for (auto& m : meshes) { + m.ownedMesh.reset(); + //qCDebug(graphics_scripting) << "~~~~ScriptableModelBase" << &m << m.mesh.use_count(); + } + 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 }; +} + +void scriptable::ScriptableModelBase::append(const ScriptableMeshBase& mesh, const QVariantMap& modelMetaData) { + //qCDebug(graphics_scripting) << "+ APPEND ScriptableMeshBase" << &mesh; + if (mesh.provider.lock().get() != provider.lock().get()) { + qCDebug(graphics_scripting) << "warning: appending mesh from different provider..." << mesh.provider.lock().get() << " != " << provider.lock().get(); + } + //if (mesh.model && mesh.model != this) { + // qCDebug(graphics_scripting) << "warning: appending mesh from different model..." << mesh.model << " != " << this; + //} + meshes << mesh; + mixin(modelMetaData); +} + +void scriptable::ScriptableModelBase::append(const ScriptableModelBase& other, const QVariantMap& modelMetaData) { + //qCDebug(graphics_scripting) << "+ APPEND ScriptableModelBase" << &other; + for (const auto& mesh : other.meshes) { append(mesh); } + mixin(other.metadata); + mixin(modelMetaData); +} + + +QString scriptable::ScriptableModel::toString() const { + return QString("[ScriptableModel%1%2]") + .arg(objectID.isNull() ? "" : " objectID="+objectID.toString()) + .arg(objectName().isEmpty() ? "" : " name=" +objectName()); +} + +scriptable::ScriptableModelPointer scriptable::ScriptableModel::cloneModel(const QVariantMap& options) { + scriptable::ScriptableModelPointer clone = scriptable::ScriptableModelPointer(new scriptable::ScriptableModel(*this)); + qCDebug(graphics_scripting) << "clone->getNumMeshes" << clone->getNumMeshes(); + clone->meshes.clear(); + qCDebug(graphics_scripting) << "..clone->getNumMeshes" << clone->getNumMeshes(); + for (const auto &mesh : getConstMeshes()) { + auto cloned = mesh->cloneMesh(options.value("recalculateNormals").toBool()); + if (auto tmp = qobject_cast(cloned)) { + qCDebug(graphics_scripting) << "++ APPEND" << tmp << tmp->ownedMesh.use_count() << tmp->metadata.value("__ownership__") << tmp->metadata.value("__native__"); + clone->meshes << *tmp; + tmp->deleteLater(); + qCDebug(graphics_scripting) << "//++ APPEND" << clone->meshes.constLast().ownedMesh.use_count();; + } else { + qCDebug(graphics_scripting) << "error cloning mesh" << cloned; + } + } + qCDebug(graphics_scripting) << "//clone->getNumMeshes" << clone->getNumMeshes(); + return clone; +} + + +const QVector scriptable::ScriptableModel::getConstMeshes() const { + QVector out; + for(const auto& mesh : meshes) { + const scriptable::ScriptableMesh* m = qobject_cast(&mesh); + if (!m) { + m = scriptable::make_scriptowned(mesh); + } else { + qCDebug(graphics_scripting) << "reusing scriptable mesh" << m; + } + const scriptable::ScriptableMeshPointer mp = scriptable::ScriptableMeshPointer(const_cast(m)); + out << mp; + } + return out; +} + +QVector scriptable::ScriptableModel::getMeshes() { + QVector out; + for(auto& mesh : meshes) { + scriptable::ScriptableMesh* m = qobject_cast(&mesh); + if (!m) { + m = scriptable::make_scriptowned(mesh); + } else { + qCDebug(graphics_scripting) << "reusing scriptable mesh" << m; + } + scriptable::ScriptableMeshPointer mp = scriptable::ScriptableMeshPointer(m); + out << mp; + } + return out; +} + +quint32 scriptable::ScriptableModel::mapAttributeValues(QScriptValue callback) { + quint32 result = 0; + QVector in = getMeshes(); + if (in.size()) { + foreach (scriptable::ScriptableMeshPointer meshProxy, in) { + result += meshProxy->mapAttributeValues(callback); + } + } + return result; +} + +/*namespace { + QScriptValue modelPointerToScriptValue(QScriptEngine* engine, scriptable::ScriptableModelPointer const &in) { + return qObjectToScriptValue(engine, in); + } + void modelPointerFromScriptValue(const QScriptValue& value, scriptable::ScriptableModelPointer &out) { + out = scriptable::qpointer_qobject_cast(value); + } +} + +namespace scriptable { + bool registerMetaTypes(QScriptEngine* engine) { + qScriptRegisterMetaType(engine, modelPointerToScriptValue, modelPointerFromScriptValue); + return true; + } +} +*/ diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h index 97a73ddd61..d2c50bd768 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h @@ -16,7 +16,7 @@ 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; } + //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/graphics/src/graphics/Geometry.h b/libraries/graphics/src/graphics/Geometry.h index 23ebec2965..a75fb1bf62 100755 --- a/libraries/graphics/src/graphics/Geometry.h +++ b/libraries/graphics/src/graphics/Geometry.h @@ -136,7 +136,8 @@ public: static MeshPointer createIndexedTriangles_P3F(uint32_t numVertices, uint32_t numTriangles, const glm::vec3* vertices = nullptr, const uint32_t* indices = nullptr); - QString displayName; + std::string modelName; + std::string displayName; protected: diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 6aa42cf6df..95985a48cb 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -2448,7 +2448,8 @@ graphics::MeshPointer GeometryCache::meshFromShape(Shape geometryShape, glm::vec auto partBuffer = new gpu::Buffer(sizeof(graphics::Mesh::Part), (gpu::Byte*)&part); mesh->setPartBuffer(gpu::BufferView(partBuffer, gpu::Element::PART_DRAWCALL)); - mesh->displayName = QString("GeometryCache/shape::%1").arg(GeometryCache::stringFromShape(geometryShape)); + mesh->modelName = GeometryCache::stringFromShape(geometryShape).toStdString(); + mesh->displayName = QString("GeometryCache/shape::%1").arg(GeometryCache::stringFromShape(geometryShape)).toStdString(); return mesh; } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index ae5ac5d61c..81ff5433f7 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -9,6 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include +namespace { QLoggingCategory wtf{ "tim.Model.cpp" }; } + + #include "Model.h" #include @@ -26,7 +30,7 @@ #include #include -#include +#include #include #include @@ -75,7 +79,7 @@ void initCollisionMaterials() { graphics::MaterialPointer material; material = std::make_shared(); int index = j * sectionWidth + i; - float red = component[index]; + float red = component[index % NUM_COLLISION_HULL_COLORS]; float green = component[(index + greenPhase) % NUM_COLLISION_HULL_COLORS]; float blue = component[(index + bluePhase) % NUM_COLLISION_HULL_COLORS]; material->setAlbedo(glm::vec3(red, green, blue)); @@ -573,35 +577,109 @@ bool Model::convexHullContains(glm::vec3 point) { return false; } -scriptable::ScriptableModel Model::getScriptableModel(bool* ok) { - scriptable::ScriptableModel result; +// FIXME: temporary workaround that updates the whole FBXGeometry (to keep findRayIntersection in sync) +#include "Model_temporary_hack.cpp.h" + +bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer _newModel, int meshIndex, int partIndex) { + QMutexLocker lock(&_mutex); + + if (!isLoaded()) { + return false; + } + + { + // 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; + _visualGeometryRequestFailed = false; + invalidCalculatedMeshBoxes(); + deleteGeometry(); + _renderGeometry.reset(newRenderGeometry); + onInvalidate(); + reset(); + _rig.destroyAnimGraph(); + assert(_rig.jointStatesEmpty()); + updateGeometry(); + calculateTriangleSets(); + computeMeshPartLocalBounds(); + _needsReload = false; + _needsFixupInScene = true; + invalidCalculatedMeshBoxes(); + setRenderItemsNeedUpdate(); + } + return true; +} + +scriptable::ScriptableModelBase Model::getScriptableModel(bool* ok) { + QMutexLocker lock(&_mutex); + scriptable::ScriptableModelBase result; const Geometry::Pointer& renderGeometry = getGeometry(); if (!isLoaded()) { - qDebug() << "Model::getScriptableModel -- !isLoaded"; + qCDebug(wtf) << "Model::getScriptableModel -- !isLoaded"; return scriptable::ModelProvider::modelUnavailableError(ok); } const FBXGeometry& geometry = getFBXGeometry(); - auto mat4toVariant = [](const glm::mat4& mat4) -> QVariant { - QVector floats; - floats.resize(16); - memcpy(floats.data(), &mat4, sizeof(glm::mat4)); - QVariant v; - v.setValue>(floats); - return v; - }; + 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(_scale) }, - { "rotation", quatToVariant(_rotation) }, - { "translation", vec3toVariant(_translation) }, - { "meshToModel", mat4toVariant(glm::scale(_scale) * glm::translate(_offset)) }, - { "meshToWorld", mat4toVariant(createMatFromQuatAndPos(_rotation, _translation) * (glm::scale(_scale) * glm::translate(_offset))) }, - { "geometryOffset", mat4toVariant(geometry.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++) { @@ -610,53 +688,43 @@ scriptable::ScriptableModel Model::getScriptableModel(bool* ok) { if (!mesh) { continue; } - result.meshes << std::const_pointer_cast(mesh); - auto extraInfo = geometry.getModelNameOfMesh(i); - qDebug() << "Model::getScriptableModel #" << i << QString(mesh->displayName) << extraInfo; - submeshes << QVariantMap{ + 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 }, - { "modelName", extraInfo }, - { "transform", mat4toVariant(fbxMesh.modelTransform) }, - { "extents", QVariantMap({ - { "minimum", vec3toVariant(fbxMesh.meshExtents.minimum) }, - { "maximum", vec3toVariant(fbxMesh.meshExtents.maximum) }, - })}, - }; + { "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; } - qDebug() << "//Model::getScriptableModel -- #" << result.meshes.size(); + qCDebug(wtf) << "//Model::getScriptableModel -- #" << result.meshes.size(); result.metadata["submeshes"] = submeshes; return result; - -// TODO: remove -- this was an earlier approach using renderGeometry instead of FBXGeometry -#if 0 // renderGeometry approach - const Geometry::GeometryMeshes& meshes = renderGeometry->getMeshes(); - Transform offset; - offset.setScale(_scale); - offset.postTranslate(_offset); - glm::mat4 offsetMat = offset.getMatrix(); - - for (std::shared_ptr mesh : meshes) { - if (!mesh) { - continue; - } - qDebug() << "Model::getScriptableModel #" << i++ << mesh->displayName; - auto newmesh = mesh->map( - [=](glm::vec3 position) { - return glm::vec3(offsetMat * glm::vec4(position, 1.0f)); - }, - [=](glm::vec3 color) { return color; }, - [=](glm::vec3 normal) { - return glm::normalize(glm::vec3(offsetMat * glm::vec4(normal, 0.0f))); - }, - [&](uint32_t index) { return index; }); - newmesh->displayName = mesh->displayName; - result << newmesh; - } -#endif } void Model::calculateTriangleSets() { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 4fd00c9f9a..375ee016ca 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #include #include @@ -314,7 +314,8 @@ public: int getResourceDownloadAttempts() { return _renderWatcher.getResourceDownloadAttempts(); } int getResourceDownloadAttemptsRemaining() { return _renderWatcher.getResourceDownloadAttemptsRemaining(); } - Q_INVOKABLE virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override; + Q_INVOKABLE virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override; + virtual bool replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer model, int meshIndex, int partIndex) override; void scaleToFit(); diff --git a/libraries/render-utils/src/Model_temporary_hack.cpp.h b/libraries/render-utils/src/Model_temporary_hack.cpp.h new file mode 100644 index 0000000000..bc31abcb37 --- /dev/null +++ b/libraries/render-utils/src/Model_temporary_hack.cpp.h @@ -0,0 +1,121 @@ +#include +#include +class MyGeometryMappingResource : public GeometryResource { +// Q_OBJECT +public: + virtual void init(bool resetLoaded = true) override { + qCDebug(wtf) << "############################# Snarfing init()..."; + } + + virtual void deleter() override { + qCDebug(wtf) << "############################# Snarfing deleter()..."; + } + shared_ptr fbxGeometry; + MyGeometryMappingResource(const QUrl& url, Geometry::Pointer originalGeometry, std::shared_ptr newModel) : GeometryResource(url) { + fbxGeometry = std::make_shared(); + FBXGeometry& geometry = *fbxGeometry.get(); + const FBXGeometry* original; + shared_ptr tmpGeometry; + if (originalGeometry) { + original = &originalGeometry->getFBXGeometry(); + } else { + tmpGeometry = std::make_shared(); + original = tmpGeometry.get(); + } + geometry.originalURL = original->originalURL; + + geometry.author = original->author; + geometry.applicationName = original->applicationName; + for (const auto &j : original->joints) { + geometry.joints << j; + } + geometry.jointIndices = QHash{ original->jointIndices }; + + geometry.animationFrames = QVector{ original->animationFrames }; + geometry.meshIndicesToModelNames = QHash{ original->meshIndicesToModelNames }; + geometry.blendshapeChannelNames = QList{ original->blendshapeChannelNames }; + + geometry.hasSkeletonJoints = original->hasSkeletonJoints; + geometry.offset = original->offset; + geometry.leftEyeJointIndex = original->leftEyeJointIndex; + geometry.rightEyeJointIndex = original->rightEyeJointIndex; + geometry.neckJointIndex = original->neckJointIndex; + geometry.rootJointIndex = original->rootJointIndex; + geometry.leanJointIndex = original->leanJointIndex; + geometry.headJointIndex = original->headJointIndex; + geometry.leftHandJointIndex = original->leftHandJointIndex; + geometry.rightHandJointIndex = original->rightHandJointIndex; + geometry.leftToeJointIndex = original->leftToeJointIndex; + geometry.rightToeJointIndex = original->rightToeJointIndex; + geometry.leftEyeSize = original->leftEyeSize; + geometry.rightEyeSize = original->rightEyeSize; + geometry.humanIKJointIndices = original->humanIKJointIndices; + geometry.palmDirection = original->palmDirection; + geometry.neckPivot = original->neckPivot; + geometry.bindExtents = original->bindExtents; + + // Copy materials + QHash materialIDAtlas; + for (const FBXMaterial& material : original->materials) { + materialIDAtlas[material.materialID] = _materials.size(); + _materials.push_back(std::make_shared(material, _textureBaseUrl)); + } + std::shared_ptr meshes = std::make_shared(); + std::shared_ptr parts = std::make_shared(); + int meshID = 0; + if (newModel) { + geometry.meshExtents.reset(); + for (const auto& newMesh : newModel->meshes) { + FBXMesh mesh; + if (meshID < original->meshes.size()) { + mesh = original->meshes.at(meshID); // copy + } + mesh._mesh = newMesh.getMeshPointer(); + // duplicate the buffers + mesh.vertices = buffer_helpers::toVector(mesh._mesh->getVertexBuffer(), "mesh.vertices"); + mesh.normals = buffer_helpers::toVector(buffer_helpers::getBufferView(mesh._mesh, gpu::Stream::NORMAL), "mesh.normals"); + mesh.colors = buffer_helpers::toVector(buffer_helpers::getBufferView(mesh._mesh, gpu::Stream::COLOR), "mesh.colors"); + mesh.texCoords = buffer_helpers::toVector(buffer_helpers::getBufferView(mesh._mesh, gpu::Stream::TEXCOORD0), "mesh.texCoords"); + mesh.texCoords1 = buffer_helpers::toVector(buffer_helpers::getBufferView(mesh._mesh, gpu::Stream::TEXCOORD1), "mesh.texCoords1"); + geometry.meshes << mesh; + // Copy mesh pointers + meshes->emplace_back(newMesh.getMeshPointer());//buffer_helpers::cloneMesh(ptr)); + int partID = 0; + const auto oldParts = mesh.parts; + mesh.parts.clear(); + for (const FBXMeshPart& fbxPart : oldParts) { + FBXMeshPart part; // copy; + part.materialID = fbxPart.materialID; + // Construct local parts + ///qCDebug(wtf) << "GeometryMappingResource -- meshes part" << meshID << partID << part.materialID; + part.triangleIndices = buffer_helpers::toVector(mesh._mesh->getIndexBuffer(), "part.triangleIndices"); + mesh.parts << part; + auto p = std::make_shared(meshID, partID, (int)materialIDAtlas[part.materialID]); + parts->push_back(p); + partID++; + } + { + // accumulate local transforms + // compute the mesh extents from the transformed vertices + foreach (const glm::vec3& vertex, mesh.vertices) { + glm::vec3 transformedVertex = glm::vec3(mesh.modelTransform * glm::vec4(vertex, 1.0f)); + geometry.meshExtents.minimum = glm::min(geometry.meshExtents.minimum, transformedVertex); + geometry.meshExtents.maximum = glm::max(geometry.meshExtents.maximum, transformedVertex); + + mesh.meshExtents.minimum = glm::min(mesh.meshExtents.minimum, transformedVertex); + mesh.meshExtents.maximum = glm::max(mesh.meshExtents.maximum, transformedVertex); + } + } + + meshID++; + } + } + _meshes = meshes; + _meshParts = parts; + _animGraphOverrideUrl = originalGeometry ? originalGeometry->getAnimGraphOverrideUrl() : QUrl(); + _loaded = true; + _fbxGeometry = fbxGeometry; + finishedLoading(true); + }; +}; + diff --git a/libraries/script-engine/src/AssetScriptingInterface.cpp b/libraries/script-engine/src/AssetScriptingInterface.cpp index 1c811573fb..750e612781 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.cpp +++ b/libraries/script-engine/src/AssetScriptingInterface.cpp @@ -440,7 +440,7 @@ void AssetScriptingInterface::saveToCache(const QUrl& rawURL, const QByteArray& JS_VERIFY(url.scheme() == "atp" || url.scheme() == "cache", "only 'atp' and 'cache' URL schemes supported"); JS_VERIFY(hash.isEmpty() || hash == hashDataHex(data), QString("invalid checksum hash for atp:HASH style URL (%1 != %2)").arg(hash, hashDataHex(data))); - qCDebug(scriptengine) << "saveToCache" << url.toDisplayString() << data << hash << metadata; + //qCDebug(scriptengine) << "saveToCache" << url.toDisplayString() << data << hash << metadata; jsPromiseReady(Parent::saveToCache(url, data, metadata), scope, callback); } diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 78cb05fa0d..873c205706 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -132,6 +132,20 @@ QUrl expandScriptUrl(const QUrl& rawScriptURL) { QObject* scriptsModel(); +bool NativeScriptInitializers::registerNativeScriptInitializer(NativeScriptInitializer initializer) { + return registerScriptInitializer([=](ScriptEnginePointer engine) { + initializer(qobject_cast(engine.data())); + }); +} + +bool NativeScriptInitializers::registerScriptInitializer(ScriptInitializer initializer) { + if (auto scriptEngines = DependencyManager::get().data()) { + scriptEngines->registerScriptInitializer(initializer); + return true; + } + return false; +} + void ScriptEngines::registerScriptInitializer(ScriptInitializer initializer) { _scriptInitializers.push_back(initializer); } @@ -520,6 +534,17 @@ void ScriptEngines::onScriptEngineLoaded(const QString& rawScriptURL) { emit scriptCountChanged(); } +int ScriptEngines::runScriptInitializers(ScriptEnginePointer scriptEngine) { + // register our application services and set it off on its own thread + int ii=0; + for (auto initializer : _scriptInitializers) { + ii++; + qDebug() << "initializer" << ii; + initializer(scriptEngine); + } + return ii; +} + void ScriptEngines::launchScriptEngine(ScriptEnginePointer scriptEngine) { connect(scriptEngine.data(), &ScriptEngine::finished, this, &ScriptEngines::onScriptFinished, Qt::DirectConnection); connect(scriptEngine.data(), &ScriptEngine::loadScript, [&](const QString& scriptName, bool userLoaded) { @@ -529,10 +554,7 @@ void ScriptEngines::launchScriptEngine(ScriptEnginePointer scriptEngine) { loadScript(scriptName, userLoaded, false, false, true); }); - // register our application services and set it off on its own thread - for (auto initializer : _scriptInitializers) { - initializer(scriptEngine); - } + runScriptInitializers(scriptEngine); // FIXME disabling 'shift key' debugging for now. If you start up the application with // the shift key held down, it triggers a deadlock because of script interfaces running diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h index 5a4b8f2f47..ea07ebe840 100644 --- a/libraries/script-engine/src/ScriptEngines.h +++ b/libraries/script-engine/src/ScriptEngines.h @@ -19,6 +19,7 @@ #include #include +#include #include "ScriptEngine.h" #include "ScriptsModel.h" @@ -26,6 +27,12 @@ class ScriptEngine; +class NativeScriptInitializers : public ScriptInitializerMixin { +public: + bool registerNativeScriptInitializer(NativeScriptInitializer initializer) override; + bool registerScriptInitializer(ScriptInitializer initializer) override; +}; + class ScriptEngines : public QObject, public Dependency { Q_OBJECT @@ -34,11 +41,11 @@ class ScriptEngines : public QObject, public Dependency { Q_PROPERTY(QString debugScriptUrl READ getDebugScriptUrl WRITE setDebugScriptUrl) public: - using ScriptInitializer = std::function; + using ScriptInitializer = ScriptInitializerMixin::ScriptInitializer; ScriptEngines(ScriptEngine::Context context); void registerScriptInitializer(ScriptInitializer initializer); - + int runScriptInitializers(ScriptEnginePointer engine); void loadScripts(); void saveScripts(); diff --git a/libraries/shared/src/shared/ScriptInitializerMixin.h b/libraries/shared/src/shared/ScriptInitializerMixin.h new file mode 100644 index 0000000000..50de553b0b --- /dev/null +++ b/libraries/shared/src/shared/ScriptInitializerMixin.h @@ -0,0 +1,38 @@ +// +// ScriptInitializerMixin.h +// libraries/shared/src/shared +// +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. + +#pragma once + +#include +#include +#include "../DependencyManager.h" + +class QScriptEngine; +class ScriptEngine; + +class ScriptInitializerMixin : public QObject, public Dependency { + Q_OBJECT +public: + // Lightweight `QScriptEngine*` initializer (only depends on built-in Qt components) + // example registration: + // eg: [&](QScriptEngine* engine) -> bool { + // engine->globalObject().setProperties("API", engine->newQObject(...instance...)) + // return true; + // } + using NativeScriptInitializer = std::function; + virtual bool registerNativeScriptInitializer(NativeScriptInitializer initializer) = 0; + + // Heavyweight `ScriptEngine*` initializer (tightly coupled to Interface and script-engine library internals) + // eg: [&](ScriptEnginePointer scriptEngine) -> bool { + // engine->registerGlobalObject("API", ...instance..); + // return true; + // } + using ScriptEnginePointer = QSharedPointer; + using ScriptInitializer = std::function; + virtual bool registerScriptInitializer(ScriptInitializer initializer) { return false; }; +}; From 4f0f6709c11d0c77fd63c8999e8b4656a9a46539 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 20 Feb 2018 13:56:06 -0800 Subject: [PATCH 05/29] fix build errors --- .../src/graphics-scripting/ScriptableModel.cpp | 1 + .../src/graphics-scripting/ScriptableModel.h | 1 - libraries/render-utils/CMakeLists.txt | 3 +-- tests/render-perf/CMakeLists.txt | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) 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/render-utils/CMakeLists.txt b/libraries/render-utils/CMakeLists.txt index 55762e38fd..54c0821569 100644 --- a/libraries/render-utils/CMakeLists.txt +++ b/libraries/render-utils/CMakeLists.txt @@ -3,11 +3,10 @@ AUTOSCRIBE_SHADER_LIB(gpu graphics render) # pull in the resources.qrc file qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc") setup_hifi_library(Widgets OpenGL Network Qml Quick Script) -link_hifi_libraries(shared ktx gpu graphics model-networking render animation fbx image procedural) +link_hifi_libraries(shared ktx gpu graphics graphics-scripting model-networking render animation fbx image procedural) include_hifi_library_headers(networking) include_hifi_library_headers(octree) include_hifi_library_headers(audio) -include_hifi_library_headers(graphics-scripting) # for ScriptableModel.h if (NOT ANDROID) target_nsight() diff --git a/tests/render-perf/CMakeLists.txt b/tests/render-perf/CMakeLists.txt index 5314f7a45b..a4570e9b54 100644 --- a/tests/render-perf/CMakeLists.txt +++ b/tests/render-perf/CMakeLists.txt @@ -16,7 +16,7 @@ link_hifi_libraries( shared networking animation ktx image octree gl gpu render render-utils - graphics fbx model-networking + graphics fbx model-networking graphics-scripting entities entities-renderer audio avatars script-engine physics procedural midi ui ${PLATFORM_GL_BACKEND} From be8d79f53f74a4995319552703cf3a4f0a0499a3 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 22 Feb 2018 05:15:01 -0500 Subject: [PATCH 06/29] CR fedback and cleanup --- interface/src/Application.cpp | 8 +- interface/src/Application.h | 1 + interface/src/ui/overlays/Base3DOverlay.h | 1 - interface/src/ui/overlays/ModelOverlay.cpp | 4 + .../src/avatars-renderer/Avatar.cpp | 9 +- .../src/avatars-renderer/Avatar.h | 2 +- .../src/RenderableEntityItem.h | 1 + .../src/RenderableModelEntityItem.cpp | 30 ++ .../src/RenderableModelEntityItem.h | 2 + .../src/RenderablePolyVoxEntityItem.cpp | 37 +- .../src/RenderablePolyVoxEntityItem.h | 1 + .../src/RenderableShapeEntityItem.cpp | 6 +- libraries/entities/src/EntityItem.h | 4 + .../entities/src/EntityScriptingInterface.cpp | 29 +- .../entities/src/EntityScriptingInterface.h | 6 +- libraries/fbx/src/FBXReader.cpp | 41 +- .../graphics-scripting/BufferViewHelpers.cpp | 86 ++-- .../graphics-scripting/BufferViewHelpers.h | 8 +- .../BufferViewScripting.cpp | 2 - .../src/graphics-scripting/Forward.h | 16 +- .../GraphicsScriptingInterface.cpp | 160 +++++++ ...terface.h => GraphicsScriptingInterface.h} | 29 +- .../ModelScriptingInterface.cpp | 407 ------------------ .../graphics-scripting/ScriptableModel.cpp | 1 + .../src/graphics-scripting/ScriptableModel.h | 1 - .../src/model-networking/SimpleMeshProxy.cpp | 27 ++ .../src/model-networking/SimpleMeshProxy.h | 36 ++ libraries/render-utils/src/GeometryCache.cpp | 1 - libraries/render-utils/src/Model.cpp | 162 ++----- libraries/render-utils/src/Model.h | 6 +- .../src/ModelScriptingInterface.cpp | 251 +++++++++++ .../src/ModelScriptingInterface.h | 39 ++ libraries/script-engine/src/ScriptEngine.cpp | 6 + libraries/shared/src/RegisteredMetaTypes.cpp | 65 +++ libraries/shared/src/RegisteredMetaTypes.h | 42 ++ libraries/shared/src/SpatiallyNestable.h | 8 +- 36 files changed, 890 insertions(+), 645 deletions(-) create mode 100644 libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp rename libraries/graphics-scripting/src/graphics-scripting/{ModelScriptingInterface.h => GraphicsScriptingInterface.h} (54%) delete mode 100644 libraries/graphics-scripting/src/graphics-scripting/ModelScriptingInterface.cpp create mode 100644 libraries/model-networking/src/model-networking/SimpleMeshProxy.cpp create mode 100644 libraries/model-networking/src/model-networking/SimpleMeshProxy.h create mode 100644 libraries/script-engine/src/ModelScriptingInterface.cpp create mode 100644 libraries/script-engine/src/ModelScriptingInterface.h 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 }; From caab532d80babef35e2075fe3fc6fb23fe3cf6e4 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 22 Feb 2018 06:00:03 -0500 Subject: [PATCH 07/29] remove dupe line --- interface/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 80e47a220f..3ed5445493 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -204,7 +204,6 @@ endif() # link required hifi libraries link_hifi_libraries( - shared octree ktx gpu gl procedural graphics graphics-scripting render shared task octree ktx gpu gl procedural graphics graphics-scripting render pointers recording fbx networking model-networking entities avatars trackers From e4a2a589a51ba243568f9553966870b150c28a85 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 22 Feb 2018 07:56:09 -0500 Subject: [PATCH 08/29] CR fedback and cleanup --- interface/src/Application.cpp | 9 +- interface/src/ui/overlays/ModelOverlay.cpp | 1 + .../src/RenderableModelEntityItem.cpp | 6 - libraries/fbx/src/FBX.h | 3 + libraries/fbx/src/OBJWriter.cpp | 21 +-- libraries/graphics-scripting/CMakeLists.txt | 1 - .../BufferViewScripting.cpp | 4 +- .../GraphicsScriptingInterface.cpp | 29 ++-- .../GraphicsScriptingUtil.h | 52 ++----- .../src/graphics-scripting/ScriptableMesh.cpp | 39 ++--- .../src/graphics-scripting/ScriptableMesh.h | 26 ++-- .../src/graphics}/BufferViewHelpers.cpp | 145 ++++++++++-------- .../src/graphics}/BufferViewHelpers.h | 6 +- libraries/render-utils/src/Model.cpp | 22 +-- .../src/Model_temporary_hack.cpp.h | 16 +- 15 files changed, 159 insertions(+), 221 deletions(-) rename libraries/{graphics-scripting/src/graphics-scripting => graphics/src/graphics}/BufferViewHelpers.cpp (87%) rename libraries/{graphics-scripting/src/graphics-scripting => graphics/src/graphics}/BufferViewHelpers.h (91%) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0a0c6e15d1..1be22d5087 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -607,7 +607,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt class ApplicationMeshProvider : public scriptable::ModelProviderFactory { public: - virtual scriptable::ModelProviderPointer lookupModelProvider(QUuid uuid) { + virtual scriptable::ModelProviderPointer lookupModelProvider(const QUuid& uuid) { QString error; scriptable::ModelProviderPointer provider; @@ -631,7 +631,7 @@ public: if (auto entity = entityTree->findEntityByID(entityID)) { if (auto renderer = entityTreeRenderer->renderableForEntityId(entityID)) { provider = std::dynamic_pointer_cast(renderer); - provider->metadata["providerType"] = "entity"; + provider->modelProviderType = NestableType::Entity; } else { qCWarning(interfaceapp) << "no renderer for entity ID" << entityID.toString(); } @@ -645,7 +645,7 @@ public: if (auto overlay = overlays.getOverlay(overlayID)) { if (auto base3d = std::dynamic_pointer_cast(overlay)) { provider = std::dynamic_pointer_cast(base3d); - provider->metadata["providerType"] = "overlay"; + provider->modelProviderType = NestableType::Overlay; } else { qCWarning(interfaceapp) << "no renderer for overlay ID" << overlayID.toString(); } @@ -659,7 +659,7 @@ public: if (auto avatar = avatarManager->getAvatarBySessionID(sessionUUID)) { if (avatar->getSessionUUID() == sessionUUID) { provider = std::dynamic_pointer_cast(avatar); - provider->metadata["providerType"] = "avatar"; + provider->modelProviderType = NestableType::Avatar; } } return provider; @@ -811,6 +811,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(true); + DependencyManager::set(); DependencyManager::registerInheritance(); DependencyManager::set(); DependencyManager::set(); diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index e610fa5c2e..b6b79fd8c9 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -119,6 +119,7 @@ bool ModelOverlay::addToScene(Overlay::Pointer overlay, const render::ScenePoint 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/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 1c4a7f1055..c93bcb2055 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1073,14 +1073,8 @@ 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); -} - - void ModelEntityRenderer::animate(const TypedEntityPointer& entity) { if (!_animation || !_animation->isLoaded()) { return; diff --git a/libraries/fbx/src/FBX.h b/libraries/fbx/src/FBX.h index 224d19fe96..a609d85fc8 100644 --- a/libraries/fbx/src/FBX.h +++ b/libraries/fbx/src/FBX.h @@ -242,6 +242,9 @@ public: graphics::MeshPointer _mesh; bool wasCompressed { false }; + + void createMeshTangents(bool generateFromTexCoords); + void createBlendShapeTangents(bool generateTangents); }; class ExtractedMesh { diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp index 621852f591..a53a3c7c5d 100644 --- a/libraries/fbx/src/OBJWriter.cpp +++ b/libraries/fbx/src/OBJWriter.cpp @@ -9,15 +9,14 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "OBJWriter.h" + #include #include -#include "graphics/Geometry.h" -#include "OBJWriter.h" +#include +#include #include "ModelFormatLogging.h" -// FIXME: should this live in shared? (it depends on gpu/) -#include <../graphics-scripting/src/graphics-scripting/BufferViewHelpers.h> - static QString formatFloat(double n) { // limit precision to 6, but don't output trailing zeros. QString s = QString::number(n, 'f', 6); @@ -71,8 +70,10 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { out << formatFloat(v[1]) << " "; out << formatFloat(v[2]); if (colorIndex < numColors) { - glm::vec3 color = glmVecFromVariant(buffer_helpers::toVariant(colorsBufferView, colorIndex)); - //glm::vec3 color = colorsBufferView.get(colorIndex); + glm::vec3 color = buffer_helpers::convert(colorsBufferView, colorIndex); + // TODO: still verifying that the above decodes properly; previous variations were: + // glm::vec3 color = buffer_helpers::glmVecFromVariant(buffer_helpers::toVariant(colorsBufferView, colorIndex)); + // glm::vec3 color = colorsBufferView.get(colorIndex); out << " " << formatFloat(color[0]); out << " " << formatFloat(color[1]); out << " " << formatFloat(color[2]); @@ -95,8 +96,10 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(gpu::Stream::InputSlot::NORMAL); gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements(); for (gpu::BufferView::Index i = 0; i < numNormals; i++) { - glm::vec3 normal = glmVecFromVariant(buffer_helpers::toVariant(normalsBufferView, i)); - //glm::vec3 normal = normalsBufferView.get(i); + glm::vec3 normal = buffer_helpers::convert(normalsBufferView, i); + // TODO: still verifying that the above decodes properly; previous variations were: + // glm::vec3 normal = buffer_helpers::glmVecFromVariant(buffer_helpers::toVariant(normalsBufferView, i)); + // glm::vec3 normal = normalsBufferView.get(i); out << "vn "; out << formatFloat(normal[0]) << " "; out << formatFloat(normal[1]) << " "; diff --git a/libraries/graphics-scripting/CMakeLists.txt b/libraries/graphics-scripting/CMakeLists.txt index e7fa3de155..ad8055b647 100644 --- a/libraries/graphics-scripting/CMakeLists.txt +++ b/libraries/graphics-scripting/CMakeLists.txt @@ -2,4 +2,3 @@ set(TARGET_NAME graphics-scripting) setup_hifi_library() link_hifi_libraries(shared networking graphics fbx model-networking script-engine) include_hifi_library_headers(gpu) -include_hifi_library_headers(graphics-scripting) diff --git a/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp b/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp index 775aedad52..31cf5ff8f3 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp @@ -9,10 +9,10 @@ #include #include -#include +#include #ifdef DEBUG_BUFFERVIEW_SCRIPTING - #include + #include "DebugNames.h" #endif namespace { diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index 71ef57bd82..58fcd7a064 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -1,8 +1,7 @@ // // GraphicsScriptingInterface.cpp -// libraries/script-engine/src +// 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. @@ -10,28 +9,24 @@ // #include "GraphicsScriptingInterface.h" -#include -#include -#include -#include #include "BaseScriptEngine.h" -#include "ScriptEngineLogging.h" +#include "BufferViewScripting.h" +#include "DebugNames.h" +#include "GraphicsScriptingUtil.h" #include "OBJWriter.h" - +#include "RegisteredMetaTypes.h" +#include "ScriptEngineLogging.h" +#include "ScriptableMesh.h" #include +#include +#include +#include +#include +#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); diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h index cfa510f87f..594e09bb32 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h @@ -12,12 +12,15 @@ Q_DECLARE_LOGGING_CATEGORY(graphics_scripting) namespace scriptable { // derive current context's C++ QObject (based on current JS "this" value) - template T this_qobject_cast(QScriptEngine* engine) { + template + T this_qobject_cast(QScriptEngine* engine) { auto context = engine ? engine->currentContext() : nullptr; return qscriptvalue_cast(context ? context->thisObject() : QScriptValue::NullValue); } + // JS => QPointer - template QPointer qpointer_qobject_cast(const QScriptValue& value) { + template + QPointer qpointer_qobject_cast(const QScriptValue& value) { auto obj = value.toQObject(); #ifdef SCRIPTABLE_MESH_DEBUG qCInfo(graphics_scripting) << "qpointer_qobject_cast" << obj << value.toString(); @@ -41,54 +44,21 @@ namespace scriptable { } // C++ > QtOwned instance - template std::shared_ptr make_qtowned(Rest... rest) { + template + std::shared_ptr make_qtowned(Rest... rest) { T* tmp = new T(rest...); -#ifdef SCRIPTABLE_MESH_DEBUG - qCInfo(graphics_scripting) << "scriptable::make_qtowned" << toDebugString(tmp); -#endif - QString debug = toDebugString(tmp); if (tmp) { tmp->metadata["__ownership__"] = QScriptEngine::QtOwnership; -#ifdef SCRIPTABLE_MESH_DEBUG - QObject::connect(tmp, &QObject::destroyed, [=]() { qCInfo(graphics_scripting) << "-------- ~scriptable::make_qtowned" << debug; }); -#endif - auto ptr = std::shared_ptr(tmp, [debug](T* tmp) { - //qDebug() << "~std::shared_ptr" << debug; - delete tmp; - }); - return ptr; - } else { - return std::shared_ptr(tmp); } + return std::shared_ptr(tmp); } + // C++ > ScriptOwned JS instance - template QPointer make_scriptowned(Rest... rest) { + template + QPointer make_scriptowned(Rest... rest) { T* tmp = new T(rest...); -#ifdef SCRIPTABLE_MESH_DEBUG - qCInfo(graphics_scripting) << "scriptable::make_scriptowned" << toDebugString(tmp); -#endif if (tmp) { tmp->metadata["__ownership__"] = QScriptEngine::ScriptOwnership; - //auto blah = (DeleterFunction)[](void* delme) { }; - return add_scriptowned_destructor(tmp); - } else { - return QPointer(tmp); - } - } - // C++ > ScriptOwned JS instance - template QPointer add_scriptowned_destructor(T* tmp) { - QString debug = toDebugString(tmp); - if (tmp) { -#ifdef SCRIPTABLE_MESH_DEBUG - QObject::connect(tmp, &QObject::destroyed, [=]() { - qCInfo(graphics_scripting) << "-------- ~scriptable::make_scriptowned" << debug;// << !!customDeleter; - //if (customDeleter) { - // customDeleter(tmp); - //} - }); -#endif - } else { - qCInfo(graphics_scripting) << "add_scriptowned_destructor -- not connecting to null value" << debug; } return QPointer(tmp); } diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp index c662371c89..28e57692f6 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp @@ -1,41 +1,26 @@ // -// SimpleMeshProxy.cpp -// libraries/model-networking/src/model-networking/ -// -// Created by Seth Alves on 2017-3-22. -// Copyright 2017 High Fidelity, Inc. +// Copyright 2018 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "GraphicsScriptingUtil.h" #include "ScriptableMesh.h" -#include -#include -#include -#include -#include -#include -#include - -#include "ScriptableMesh.moc" - -#include +#include "BufferViewScripting.h" +#include "DebugNames.h" +#include "GraphicsScriptingUtil.h" +#include "OBJWriter.h" #include #include +#include +#include +#include +#include +#include +#include -#include "OBJWriter.h" - -// #define SCRIPTABLE_MESH_DEBUG - -namespace scriptable { - // QScriptValue jsBindCallback(QScriptValue callback); - // template QPointer qpointer_qobject_cast(const QScriptValue& value); - // template T this_qobject_cast(QScriptEngine* engine); - // template QPointer make_scriptowned(Rest... rest); -} +#include "ScriptableMesh.moc" scriptable::ScriptableMeshPart::ScriptableMeshPart(scriptable::ScriptableMeshPointer parentMesh, int partIndex) : parentMesh(parentMesh), partIndex(partIndex) { diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h index ba0efa007d..2b9399d6ef 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h @@ -1,26 +1,22 @@ #pragma once -#include -#include -#include -#include -#include -#include -#include -#include +#include "ScriptableModel.h" +#include +#include #include -//#include -#include -#include - -#include +#include +#include +#include +#include +#include +#include +#include #include +#include namespace scriptable { - - QScriptValue jsBindCallback(QScriptValue callback); class ScriptableMesh : public ScriptableMeshBase, QScriptable { Q_OBJECT public: diff --git a/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.cpp b/libraries/graphics/src/graphics/BufferViewHelpers.cpp similarity index 87% rename from libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.cpp rename to libraries/graphics/src/graphics/BufferViewHelpers.cpp index e4eeee856e..ea511d82f4 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.cpp +++ b/libraries/graphics/src/graphics/BufferViewHelpers.cpp @@ -1,4 +1,11 @@ -#include "./graphics-scripting/BufferViewHelpers.h" +// +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "BufferViewHelpers.h" #include #include @@ -7,7 +14,7 @@ #include #include -#include +#include "Geometry.h" #include #include @@ -15,25 +22,26 @@ #include #include #include + namespace glm { using hvec2 = glm::tvec2; using hvec4 = glm::tvec4; } -//#define DEBUG_BUFFERVIEW_SCRIPTING -//#ifdef DEBUG_BUFFERVIEW_SCRIPTING -#include "DebugNames.h" -//#endif +#ifdef DEBUG_BUFFERVIEW_SCRIPTING +#include "../../graphics-scripting/src/graphics-scripting/DebugNames.h" +#endif namespace { - QLoggingCategory bufferhelper_logging{"hifi.bufferview"}; - const std::array XYZW = {{ "x", "y", "z", "w" }}; - const std::array ZERO123 = {{ "0", "1", "2", "3" }}; + QLoggingCategory bufferhelper_logging{ "hifi.bufferview" }; + const std::array XYZW = { { "x", "y", "z", "w" } }; + const std::array ZERO123 = { { "0", "1", "2", "3" } }; } gpu::BufferView buffer_helpers::getBufferView(graphics::MeshPointer mesh, gpu::Stream::Slot slot) { return slot == gpu::Stream::POSITION ? mesh->getVertexBuffer() : mesh->getAttributeBuffer(slot); } + QMap buffer_helpers::ATTRIBUTES{ {"position", gpu::Stream::POSITION }, {"normal", gpu::Stream::NORMAL }, @@ -49,16 +57,23 @@ QMap buffer_helpers::ATTRIBUTES{ }; -template -QVariant getBufferViewElement(const gpu::BufferView& view, quint32 index, bool asArray = false) { - return glmVecToVariant(view.get(index), asArray); -} +namespace { + bool boundsCheck(const gpu::BufferView& view, quint32 index) { + const auto byteLength = view._element.getSize(); + return ( + index < view.getNumElements() && + index * byteLength < (view._size - 1) * byteLength + ); + } -template -void setBufferViewElement(const gpu::BufferView& view, quint32 index, const QVariant& v) { - view.edit(index) = glmVecFromVariant(v); -} + template QVariant getBufferViewElement(const gpu::BufferView& view, quint32 index, bool asArray = false) { + return buffer_helpers::glmVecToVariant(view.get(index), asArray); + } + template void setBufferViewElement(const gpu::BufferView& view, quint32 index, const QVariant& v) { + view.edit(index) = buffer_helpers::glmVecFromVariant(v); + } +} void buffer_helpers::packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint32& packedNormal, glm::uint32& packedTangent) { auto absNormal = glm::abs(normal); @@ -147,14 +162,6 @@ bool buffer_helpers::fromVariant(const gpu::BufferView& view, quint32 index, con return false; } -bool boundsCheck(const gpu::BufferView& view, quint32 index) { - const auto byteLength = view._element.getSize(); - 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(); @@ -167,14 +174,17 @@ QVariant buffer_helpers::toVariant(const gpu::BufferView& view, quint32 index, b auto byteOffset = index * vecN * BYTES_PER_ELEMENT; auto maxByteOffset = (view._size - 1) * vecN * BYTES_PER_ELEMENT; if (byteOffset > maxByteOffset) { - qDebug() << "bufferViewElementToVariant -- byteOffset out of range " << byteOffset << " < " << maxByteOffset << DebugNames::stringFrom(dataType); - qDebug() << "bufferViewElementToVariant -- index: " << index << "numElements" << view.getNumElements(); - qDebug() << "bufferViewElementToVariant -- vecN: " << vecN << "byteLength" << byteLength << "BYTES_PER_ELEMENT" << BYTES_PER_ELEMENT; +#ifdef DEBUG_BUFFERVIEW_SCRIPTING + qDebug() << "toVariant -- " << DebugNames::stringFrom(dataType) +#endif + qDebug() << "toVariant -- byteOffset out of range " << byteOffset << " < " << maxByteOffset; + qDebug() << "toVariant -- index: " << index << "numElements" << view.getNumElements(); + qDebug() << "toVariant -- vecN: " << vecN << "byteLength" << byteLength << "BYTES_PER_ELEMENT" << BYTES_PER_ELEMENT; } Q_ASSERT(byteOffset <= maxByteOffset); } #ifdef DEBUG_BUFFERVIEW_SCRIPTING - qCDebug(bufferhelper_logging) << "bufferViewElementToVariant" << index << DebugNames::stringFrom(dataType) << BYTES_PER_ELEMENT << vecN; + qCDebug(bufferhelper_logging) << "toVariant -- " << index << DebugNames::stringFrom(dataType) << BYTES_PER_ELEMENT << vecN; #endif if (BYTES_PER_ELEMENT == 1) { switch(vecN) { @@ -223,7 +233,7 @@ QVariant buffer_helpers::toVariant(const gpu::BufferView& view, quint32 index, b } template -QVariant glmVecToVariant(const T& v, bool asArray /*= false*/) { +QVariant buffer_helpers::glmVecToVariant(const T& v, bool asArray /*= false*/) { static const auto len = T().length(); if (asArray) { QVariantList list; @@ -239,8 +249,9 @@ QVariant glmVecToVariant(const T& v, bool asArray /*= false*/) { return obj; } } + template -const T glmVecFromVariant(const QVariant& v) { +const T buffer_helpers::glmVecFromVariant(const QVariant& v) { auto isMap = v.type() == (QVariant::Type)QMetaType::QVariantMap; static const auto len = T().length(); const auto& components = isMap ? XYZW : ZERO123; @@ -255,9 +266,11 @@ const T glmVecFromVariant(const QVariant& v) { } else { value = list.value(i).toFloat(); } +#ifdef DEBUG_BUFFERVIEW_SCRIPTING if (value != value) { // NAN qWarning().nospace()<< "vec" << len << "." << components[i] << " NAN received from script.... " << v.toString(); } +#endif result[i] = value; } return result; @@ -268,17 +281,26 @@ gpu::BufferView buffer_helpers::fromVector(const QVector& elements, const gpu auto vertexBuffer = std::make_shared(elements.size() * sizeof(T), (gpu::Byte*)elements.data()); return { vertexBuffer, 0, vertexBuffer->getSize(),sizeof(T), elementType }; } -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<> gpu::BufferView buffer_helpers::fromVector( + const QVector& elements, const gpu::Element& elementType +) { return fromVector(elements, elementType); } -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); }; +template<> gpu::BufferView buffer_helpers::fromVector( + const QVector& elements, const gpu::Element& elementType +) { return fromVector(elements, elementType); } + +template struct GpuVec4ToGlm; +template struct GpuScalarToGlm; struct GpuToGlmAdapter { static float error(const QString& name, const gpu::BufferView& view, quint32 index, const char *hint) { + QString debugName; +#ifdef DEBUG_BUFFERVIEW_SCRIPTING + debugName = DebugNames::stringFrom(view._element.getType()) +#endif 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(debugName) .arg(view._element.getType()) .arg(view._element.getSize()) .arg(view._element.getSize() / view._element.getScalarCount()) @@ -290,7 +312,8 @@ struct GpuToGlmAdapter { return NAN; } }; -template struct getScalar : GpuToGlmAdapter { + +template struct GpuScalarToGlm : 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); @@ -301,7 +324,7 @@ template struct getScalar : GpuToGlmAdapter { case gpu::FLOAT: return view.get(index); case gpu::HALF: return T(glm::unpackSnorm1x8(view.get(index))); default: break; - } return T(error("getScalar", view, index, hint)); + } return T(error("GpuScalarToGlm", view, index, hint)); } }; @@ -376,8 +399,9 @@ struct getVec { } }; +// BufferView => QVector template <> QVector buffer_helpers::toVector(const gpu::BufferView& view, const char *hint) { - return getVec,int>::__to_vector__(view, 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); @@ -390,8 +414,9 @@ template <> QVector buffer_helpers::toVector(const gpu::Bu } +// indexed conversion accessors (similar to "view.convert(i)" existed) template <> int buffer_helpers::convert(const gpu::BufferView& view, quint32 index, const char *hint) { - return getVec,int>::__to_scalar__(view, index, 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); @@ -407,30 +432,25 @@ gpu::BufferView buffer_helpers::clone(const gpu::BufferView& input) { return gpu::BufferView( std::make_shared(input._buffer->getSize(), input._buffer->getData()), input._offset, input._size, input._stride, input._element - ); + ); } +// TODO: preserve existing data gpu::BufferView buffer_helpers::resize(const gpu::BufferView& input, quint32 numElements) { auto effectiveSize = input._buffer->getSize() / input.getNumElements(); - qDebug() << "resize input" << input.getNumElements() << input._buffer->getSize() << "effectiveSize" << effectiveSize; + qCDebug(bufferhelper_logging) << "resize input" << input.getNumElements() << input._buffer->getSize() << "effectiveSize" << effectiveSize; auto vsize = input._element.getSize() * numElements; - gpu::Byte *data = new gpu::Byte[vsize]; - memset(data, 0, vsize); - auto buffer = new gpu::Buffer(vsize, (gpu::Byte*)data); - delete[] data; + std::unique_ptr data{ new gpu::Byte[vsize] }; + memset(data.get(), 0, vsize); + auto buffer = new gpu::Buffer(vsize, data.get()); auto output = gpu::BufferView(buffer, input._element); - qDebug() << "resized output" << output.getNumElements() << output._buffer->getSize(); + qCDebug(bufferhelper_logging) << "resized output" << output.getNumElements() << output._buffer->getSize(); return output; } graphics::MeshPointer buffer_helpers::cloneMesh(graphics::MeshPointer mesh) { auto clone = std::make_shared(); - //[](graphics::Mesh* blah) { - //qCDebug(bufferhelper_logging) << "--- DELETING MESH POINTER" << blah; - // delete blah; - //}); clone->displayName = (QString::fromStdString(mesh->displayName) + "-clone").toStdString(); - //qCInfo(bufferhelper_logging) << "+++ ALLOCATED MESH POINTER ScriptableMesh::cloneMesh" << clone->displayName << clone.get() << !!mesh; clone->setIndexBuffer(buffer_helpers::clone(mesh->getIndexBuffer())); clone->setPartBuffer(buffer_helpers::clone(mesh->getPartBuffer())); auto attributeViews = buffer_helpers::gatherBufferViews(mesh); @@ -447,18 +467,11 @@ graphics::MeshPointer buffer_helpers::cloneMesh(graphics::MeshPointer mesh) { return clone; } - -/// --- buffer view <-> variant helpers - 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); 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; @@ -478,10 +491,9 @@ namespace { if (bufferView.getNumElements() < nPositions || diffTypes) { if (!bufferView._buffer || bufferView.getNumElements() == 0) { qCInfo(bufferhelper_logging).nospace() << "ScriptableMesh -- adding missing mesh attribute '" << hint << "' for BufferView"; - gpu::Byte *data = new gpu::Byte[vsize]; - memset(data, 0, vsize); - auto buffer = new gpu::Buffer(vsize, (gpu::Byte*)data); - delete[] data; + std::unique_ptr data{ new gpu::Byte[vsize] }; + memset(data.get(), 0, vsize); + auto buffer = new gpu::Buffer(vsize, data.get()); bufferView = gpu::BufferView(buffer, elementType); mesh->addAttribute(slot, bufferView); } else { @@ -553,7 +565,6 @@ std::map buffer_helpers::gatherBufferViews(graphics::M return attributeViews; } - bool buffer_helpers::recalculateNormals(graphics::MeshPointer mesh) { qCInfo(bufferhelper_logging) << "Recalculating normals" << !!mesh; if (!mesh) { @@ -567,10 +578,8 @@ bool buffer_helpers::recalculateNormals(graphics::MeshPointer mesh) { auto numPoints = indices.getNumElements(); const auto TRIANGLE = 3; quint32 numFaces = (quint32)numPoints / TRIANGLE; - //QVector faces; QVector faceNormals; QMap> vertexToFaces; - //faces.resize(numFaces); faceNormals.resize(numFaces); auto numNormals = normals.getNumElements(); qCInfo(bufferhelper_logging) << QString("numFaces: %1, numNormals: %2, numPoints: %3").arg(numFaces).arg(numNormals).arg(numPoints); @@ -590,7 +599,9 @@ bool buffer_helpers::recalculateNormals(graphics::MeshPointer mesh) { }; faceNormals[i] = face.getNormal(); if (glm::isnan(faceNormals[i].x)) { +#ifdef DEBUG_BUFFERVIEW_SCRIPTING qCInfo(bufferhelper_logging) << i << i0 << i1 << i2 << glmVecToVariant(face.v0) << glmVecToVariant(face.v1) << glmVecToVariant(face.v2); +#endif break; } vertexToFaces[glm::to_string(face.v0).c_str()] << i; @@ -615,10 +626,12 @@ bool buffer_helpers::recalculateNormals(graphics::MeshPointer mesh) { normal = verts.get(j); } if (glm::isnan(normal.x)) { +#ifdef DEBUG_BUFFERVIEW_SCRIPTING static int logged = 0; if (logged++ < 10) { qCInfo(bufferhelper_logging) << "isnan(normal.x)" << j << glmVecToVariant(normal); } +#endif break; } buffer_helpers::fromVariant(normals, j, glmVecToVariant(glm::normalize(normal))); diff --git a/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.h b/libraries/graphics/src/graphics/BufferViewHelpers.h similarity index 91% rename from libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.h rename to libraries/graphics/src/graphics/BufferViewHelpers.h index e0c2e1eee1..53fddfe581 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/BufferViewHelpers.h +++ b/libraries/graphics/src/graphics/BufferViewHelpers.h @@ -15,9 +15,6 @@ namespace gpu { class Element; } -template QVariant glmVecToVariant(const T& v, bool asArray = false); -template const T glmVecFromVariant(const QVariant& v); - namespace graphics { class Mesh; using MeshPointer = std::shared_ptr; @@ -27,6 +24,9 @@ class Extents; class AABox; struct buffer_helpers { + template static QVariant glmVecToVariant(const T& v, bool asArray = false); + template static const T glmVecFromVariant(const QVariant& v); + static graphics::MeshPointer cloneMesh(graphics::MeshPointer mesh); static QMap ATTRIBUTES; static std::map gatherBufferViews(graphics::MeshPointer mesh, const QStringList& expandToMatchPositions = QStringList()); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index ffc93340b0..9dbef5c6fc 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -9,10 +9,6 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include -namespace { QLoggingCategory wtf{ "tim.Model.cpp" }; } - - #include "Model.h" #include @@ -31,7 +27,7 @@ namespace { QLoggingCategory wtf{ "tim.Model.cpp" }; } #include #include -#include +#include #include #include @@ -372,7 +368,7 @@ bool Model::updateGeometry() { #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; @@ -569,25 +565,15 @@ bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointe auto newRenderGeometry = new MyGeometryMappingResource( _url, _renderGeometry, _newModel ? scriptable::make_qtowned(*_newModel) : nullptr ); - //_needsUpdateTextures = true; _visualGeometryRequestFailed = false; - //invalidCalculatedMeshBoxes(); deleteGeometry(); _renderGeometry.reset(newRenderGeometry); - //onInvalidate(); - //reset(); _rig.destroyAnimGraph(); - //assert(_rig.jointStatesEmpty()); updateGeometry(); calculateTriangleSets(); - //computeMeshPartLocalBounds(); - //_needsReload = false; + _needsReload = false; _needsFixupInScene = true; - //invalidCalculatedMeshBoxes(); setRenderItemsNeedUpdate(); - //_hasCalculatedTextureInfo = false; - //calculateTextureInfo(); - //updateRenderItems(); } return true; } @@ -597,7 +583,7 @@ scriptable::ScriptableModelBase Model::getScriptableModel(bool* ok) { scriptable::ScriptableModelBase result; if (!isLoaded()) { - qCDebug(wtf) << "Model::getScriptableModel -- !isLoaded"; + qCDebug(renderutils) << "Model::getScriptableModel -- !isLoaded"; return scriptable::ModelProvider::modelUnavailableError(ok); } diff --git a/libraries/render-utils/src/Model_temporary_hack.cpp.h b/libraries/render-utils/src/Model_temporary_hack.cpp.h index bc31abcb37..9b0e0bcf9a 100644 --- a/libraries/render-utils/src/Model_temporary_hack.cpp.h +++ b/libraries/render-utils/src/Model_temporary_hack.cpp.h @@ -1,15 +1,7 @@ -#include +#include #include class MyGeometryMappingResource : public GeometryResource { -// Q_OBJECT public: - virtual void init(bool resetLoaded = true) override { - qCDebug(wtf) << "############################# Snarfing init()..."; - } - - virtual void deleter() override { - qCDebug(wtf) << "############################# Snarfing deleter()..."; - } shared_ptr fbxGeometry; MyGeometryMappingResource(const QUrl& url, Geometry::Pointer originalGeometry, std::shared_ptr newModel) : GeometryResource(url) { fbxGeometry = std::make_shared(); @@ -77,6 +69,8 @@ public: mesh.colors = buffer_helpers::toVector(buffer_helpers::getBufferView(mesh._mesh, gpu::Stream::COLOR), "mesh.colors"); mesh.texCoords = buffer_helpers::toVector(buffer_helpers::getBufferView(mesh._mesh, gpu::Stream::TEXCOORD0), "mesh.texCoords"); mesh.texCoords1 = buffer_helpers::toVector(buffer_helpers::getBufferView(mesh._mesh, gpu::Stream::TEXCOORD1), "mesh.texCoords1"); + mesh.createMeshTangents(true); + mesh.createBlendShapeTangents(false); geometry.meshes << mesh; // Copy mesh pointers meshes->emplace_back(newMesh.getMeshPointer());//buffer_helpers::cloneMesh(ptr)); @@ -84,10 +78,9 @@ public: const auto oldParts = mesh.parts; mesh.parts.clear(); for (const FBXMeshPart& fbxPart : oldParts) { - FBXMeshPart part; // copy; + FBXMeshPart part; // new copy part.materialID = fbxPart.materialID; // Construct local parts - ///qCDebug(wtf) << "GeometryMappingResource -- meshes part" << meshID << partID << part.materialID; part.triangleIndices = buffer_helpers::toVector(mesh._mesh->getIndexBuffer(), "part.triangleIndices"); mesh.parts << part; auto p = std::make_shared(meshID, partID, (int)materialIDAtlas[part.materialID]); @@ -115,7 +108,6 @@ public: _animGraphOverrideUrl = originalGeometry ? originalGeometry->getAnimGraphOverrideUrl() : QUrl(); _loaded = true; _fbxGeometry = fbxGeometry; - finishedLoading(true); }; }; From fe9b67344b854b31d537deea3b01452d0ce94a15 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 22 Feb 2018 07:58:49 -0500 Subject: [PATCH 09/29] remove unnecessary UUID check --- interface/src/Application.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1be22d5087..f344c94161 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -657,10 +657,8 @@ public: scriptable::ModelProviderPointer provider; auto avatarManager = DependencyManager::get(); if (auto avatar = avatarManager->getAvatarBySessionID(sessionUUID)) { - if (avatar->getSessionUUID() == sessionUUID) { - provider = std::dynamic_pointer_cast(avatar); - provider->modelProviderType = NestableType::Avatar; - } + provider = std::dynamic_pointer_cast(avatar); + provider->modelProviderType = NestableType::Avatar; } return provider; } From 4fd3d9bf7bc4d1ef7f36a4de52b3e768864c44fe Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 22 Feb 2018 08:18:38 -0500 Subject: [PATCH 10/29] cleanup; formalize data structures --- interface/src/ui/overlays/ModelOverlay.cpp | 4 +++- interface/src/ui/overlays/Shape3DOverlay.cpp | 5 +---- .../avatars-renderer/src/avatars-renderer/Avatar.cpp | 7 +------ .../src/RenderableModelEntityItem.cpp | 4 +++- .../src/RenderablePolyVoxEntityItem.cpp | 1 + .../src/RenderableShapeEntityItem.cpp | 11 ++--------- 6 files changed, 11 insertions(+), 21 deletions(-) diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index b6b79fd8c9..4ff5499a12 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -669,5 +669,7 @@ scriptable::ScriptableModelBase ModelOverlay::getScriptableModel(bool* ok) { if (!_model || !_model->isLoaded()) { return Base3DOverlay::getScriptableModel(ok); } - return _model->getScriptableModel(ok); + auto result = _model->getScriptableModel(ok); + result.objectID = getID(); + return result; } \ No newline at end of file diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp index 54423feef6..04557bcf5d 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.cpp +++ b/interface/src/ui/overlays/Shape3DOverlay.cpp @@ -184,10 +184,7 @@ scriptable::ScriptableModelBase Shape3DOverlay::getScriptableModel(bool* ok) { auto geometryCache = DependencyManager::get(); auto vertexColor = ColorUtils::toVec3(_color); scriptable::ScriptableModelBase result; - result.metadata = { - { "origin", "Shape3DOverlay::"+shapeStrings[_shape] }, - { "overlayID", getID() }, - }; + result.objectID = getID(); result.append(geometryCache->meshFromShape(_shape, vertexColor), {{ "shape", shapeStrings[_shape] }}); if (ok) { *ok = true; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 5934ffed12..48f0c70dfa 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1801,12 +1801,7 @@ scriptable::ScriptableModelBase Avatar::getScriptableModel(bool* ok) { } scriptable::ScriptableModelBase result = _skeletonModel->getScriptableModel(ok); result.objectID = getSessionUUID(); - result.mixin({ - { "avatarID", getSessionUUID().toString() }, - { "url", _skeletonModelURL.toString() }, - { "origin", "Avatar/avatar::" + _displayName }, - { "textures", _skeletonModel->getTextures() }, - }); + result.mixin({{ "textures", _skeletonModel->getTextures() }}); // FIXME: for now access to attachment models are merged into the main avatar ScriptableModel set for (int i = 0; i < (int)_attachmentModels.size(); i++) { auto& model = _attachmentModels.at(i); diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index c93bcb2055..0e23ff3bff 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -968,7 +968,9 @@ scriptable::ScriptableModelBase render::entities::ModelEntityRenderer::getScript return scriptable::ModelProvider::modelUnavailableError(ok); } - return _model->getScriptableModel(ok); + auto result = _model->getScriptableModel(ok); + result.objectID = getEntity()->getID(); + return result; } bool render::entities::ModelEntityRenderer::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer newModel, int meshIndex, int partIndex) { diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index a547926343..37e03b2590 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -1459,6 +1459,7 @@ scriptable::ScriptableModelBase RenderablePolyVoxEntityItem::getScriptableModel( bool success = false; glm::mat4 transform = voxelToLocalMatrix(); scriptable::ScriptableModelBase result; + result.objectID = getThisPointer()->getID(); withReadLock([&] { gpu::BufferView::Index numVertices = (gpu::BufferView::Index)_mesh->getNumVertices(); if (!_meshReady) { diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 0ff8ac17f0..2e65eba5e5 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -166,11 +166,7 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { scriptable::ScriptableModelBase ShapeEntityRenderer::getScriptableModel(bool* ok) { scriptable::ScriptableModelBase result; - result.metadata = { - { "entityID", getEntity()->getID().toString() }, - { "shape", entity::stringFromShape(_shape) }, - { "userData", getEntity()->getUserData() }, - }; + result.objectID = getEntity()->getID(); auto geometryCache = DependencyManager::get(); auto geometryShape = geometryCache->getShapeForEntityShape(_shape); glm::vec3 vertexColor; @@ -179,10 +175,7 @@ scriptable::ScriptableModelBase ShapeEntityRenderer::getScriptableModel(bool* ok } auto success = false; if (auto mesh = geometryCache->meshFromShape(geometryShape, vertexColor)) { - scriptable::ScriptableMeshBase base{ mesh, { - { "shape", entity::stringFromShape(_shape) }, - }}; - result.append(base); + result.append({ mesh, {{ "shape", entity::stringFromShape(_shape) }}}); success = true; } if (ok) { From e3c269d5820207e6521e8bf2ad64b2529205b3a1 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 22 Feb 2018 08:22:13 -0500 Subject: [PATCH 11/29] remove debug string --- libraries/fbx/src/OBJWriter.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp index a53a3c7c5d..ffc34573d4 100644 --- a/libraries/fbx/src/OBJWriter.cpp +++ b/libraries/fbx/src/OBJWriter.cpp @@ -156,16 +156,7 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { // graphics::Mesh::TRIANGLES / graphics::Mesh::QUADS // TODO -- handle other formats uint32_t len = part._startIndex + part._numIndices; - auto stringFromTopology = [&](graphics::Mesh::Topology topo) -> QString { - return topo == graphics::Mesh::Topology::QUADS ? "QUADS" : - topo == graphics::Mesh::Topology::QUAD_STRIP ? "QUAD_STRIP" : - topo == graphics::Mesh::Topology::TRIANGLES ? "TRIANGLES" : - topo == graphics::Mesh::Topology::TRIANGLE_STRIP ? "TRIANGLE_STRIP" : - topo == graphics::Mesh::Topology::QUAD_STRIP ? "QUAD_STRIP" : - QString("topo:%1").arg((int)topo); - }; - - qCDebug(modelformat) << "OBJWriter -- part" << partIndex << "topo" << stringFromTopology(part._topology) << "index elements" << (shorts ? "uint16_t" : "uint32_t"); + qCDebug(modelformat) << "OBJWriter -- part" << partIndex << "topo" << part._topology << "index elements" << (shorts ? "uint16_t" : "uint32_t"); if (part._topology == graphics::Mesh::TRIANGLES && len % 3 != 0) { qCDebug(modelformat) << "OBJWriter -- index buffer length isn't a multiple of 3" << len; } From d52dce8e092847024e7cdece7091d0568fd9fa67 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 22 Feb 2018 08:24:56 -0500 Subject: [PATCH 12/29] restore code for debugging --- libraries/fbx/src/FBXReader.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 837d7fe80e..278a68f312 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1979,7 +1979,22 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS } } } - + { + int i = 0; + for (const auto& mesh : geometry.meshes) { + auto name = geometry.getModelNameOfMesh(i++); + if (!name.isEmpty()) { + if (mesh._mesh) { + mesh._mesh->modelName = name.toStdString(); + if (!mesh._mesh->displayName.size()) { + mesh._mesh->displayName = mesh._mesh->displayName + "#" + name; + } + } else { + qDebug() << "modelName but no mesh._mesh" << name; + } + } + } + } return geometryPtr; } From ec4f9fdc11bc2584f289276b6dc577e2559699f5 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 22 Feb 2018 08:31:05 -0500 Subject: [PATCH 13/29] fix displayName --- .../entities-renderer/src/RenderableModelEntityItem.cpp | 8 ++++---- libraries/fbx/src/FBXReader.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 0e23ff3bff..6eed62522f 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -1300,7 +1300,8 @@ void ModelEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& sce auto entityRenderer = static_cast(&data); entityRenderer->clearSubRenderItemIDs(); }); - emit DependencyManager::get()->modelRemovedFromScene(entity->getEntityItemID(), NestableType::Entity, _model); + emit DependencyManager::get()-> + modelRemovedFromScene(entity->getEntityItemID(), NestableType::Entity, _model); } return; } @@ -1311,10 +1312,9 @@ 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); + emit DependencyManager::get()-> + modelAddedToScene(entity->getEntityItemID(), NestableType::Entity, _model); } }); connect(model.get(), &Model::requestRenderUpdate, this, &ModelEntityRenderer::requestRenderUpdate); diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 278a68f312..1e59646795 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -1987,7 +1987,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS if (mesh._mesh) { mesh._mesh->modelName = name.toStdString(); if (!mesh._mesh->displayName.size()) { - mesh._mesh->displayName = mesh._mesh->displayName + "#" + name; + mesh._mesh->displayName = QString("#%1").arg(name).toStdString(); } } else { qDebug() << "modelName but no mesh._mesh" << name; From b0f444e179824dd6c91a1da790dc923d098d4e32 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 22 Feb 2018 08:36:37 -0500 Subject: [PATCH 14/29] remove dead code --- .../src/graphics-scripting/ScriptableModel.cpp | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp index 05ff043820..5d0d459446 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp @@ -131,20 +131,3 @@ quint32 scriptable::ScriptableModel::mapAttributeValues(QScriptValue callback) { } return result; } - -/*namespace { - QScriptValue modelPointerToScriptValue(QScriptEngine* engine, scriptable::ScriptableModelPointer const &in) { - return qObjectToScriptValue(engine, in); - } - void modelPointerFromScriptValue(const QScriptValue& value, scriptable::ScriptableModelPointer &out) { - out = scriptable::qpointer_qobject_cast(value); - } -} - -namespace scriptable { - bool registerMetaTypes(QScriptEngine* engine) { - qScriptRegisterMetaType(engine, modelPointerToScriptValue, modelPointerFromScriptValue); - return true; - } -} -*/ From 4d43e543466bdb2138f87042e55d889588359bc6 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 22 Feb 2018 08:41:15 -0500 Subject: [PATCH 15/29] use shared buffer_helpers::clone --- libraries/render-utils/src/GeometryCache.cpp | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/libraries/render-utils/src/GeometryCache.cpp b/libraries/render-utils/src/GeometryCache.cpp index 525ef51a57..7455da13b6 100644 --- a/libraries/render-utils/src/GeometryCache.cpp +++ b/libraries/render-utils/src/GeometryCache.cpp @@ -32,6 +32,7 @@ #include "gpu/StandardShaderLib.h" #include "graphics/TextureMap.h" +#include "graphics/BufferViewHelpers.h" #include "render/Args.h" #include "standardTransformPNTC_vert.h" @@ -2409,26 +2410,15 @@ graphics::MeshPointer GeometryCache::meshFromShape(Shape geometryShape, glm::vec qDebug() << "GeometryCache::getMeshProxyListFromShape" << shapeData << stringFromShape(geometryShape); - auto cloneBufferView = [](const gpu::BufferView& in) -> gpu::BufferView { - auto buffer = std::make_shared(*in._buffer); // copy - // FIXME: gpu::BufferView seems to have a bug where constructing a new instance from an existing one - // results in over-multiplied buffer/view sizes -- hence constructing manually here from each input prop - auto out = gpu::BufferView(buffer, in._offset, in._size, in._stride, in._element); - Q_ASSERT(out.getNumElements() == in.getNumElements()); - Q_ASSERT(out._size == in._size); - Q_ASSERT(out._buffer->getSize() == in._buffer->getSize()); - return out; - }; - - auto positionsBufferView = cloneBufferView(shapeData->_positionView); - auto normalsBufferView = cloneBufferView(shapeData->_normalView); - auto indexBufferView = cloneBufferView(shapeData->_indicesView); + auto positionsBufferView = buffer_helpers::clone(shapeData->_positionView); + auto normalsBufferView = buffer_helpers::clone(shapeData->_normalView); + auto indexBufferView = buffer_helpers::clone(shapeData->_indicesView); gpu::BufferView::Size numVertices = positionsBufferView.getNumElements(); Q_ASSERT(numVertices == normalsBufferView.getNumElements()); // apply input color across all vertices - auto colorsBufferView = cloneBufferView(shapeData->_normalView); + auto colorsBufferView = buffer_helpers::clone(shapeData->_normalView); for (gpu::BufferView::Size i = 0; i < numVertices; i++) { colorsBufferView.edit((gpu::BufferView::Index)i) = color; } From 010c714abcd9f86464c07e52501048e856831c35 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 22 Feb 2018 08:51:54 -0500 Subject: [PATCH 16/29] restore original Entities.getMeshes --- libraries/render-utils/src/Model.cpp | 38 ++++++++++++++++++++++++++++ libraries/render-utils/src/Model.h | 2 ++ 2 files changed, 40 insertions(+) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 9dbef5c6fc..783659a6a1 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -550,6 +551,43 @@ bool Model::convexHullContains(glm::vec3 point) { return false; } +// TODO: deprecate and remove +MeshProxyList Model::getMeshes() const { + MeshProxyList result; + const Geometry::Pointer& renderGeometry = getGeometry(); + const Geometry::GeometryMeshes& meshes = renderGeometry->getMeshes(); + + if (!isLoaded()) { + return result; + } + + Transform offset; + offset.setScale(_scale); + offset.postTranslate(_offset); + glm::mat4 offsetMat = offset.getMatrix(); + + for (std::shared_ptr mesh : meshes) { + if (!mesh) { + continue; + } + + MeshProxy* meshProxy = new SimpleMeshProxy( + mesh->map( + [=](glm::vec3 position) { + return glm::vec3(offsetMat * glm::vec4(position, 1.0f)); + }, + [=](glm::vec3 color) { return color; }, + [=](glm::vec3 normal) { + return glm::normalize(glm::vec3(offsetMat * glm::vec4(normal, 0.0f))); + }, + [&](uint32_t index) { return index; })); + meshProxy->setObjectName(mesh->displayName.c_str()); + result << meshProxy; + } + + return result; +} + // FIXME: temporary workaround that updates the whole FBXGeometry (to keep findRayIntersection in sync) #include "Model_temporary_hack.cpp.h" diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 3a86b3b7e4..5cbdb2d300 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -322,6 +322,8 @@ public: void scaleToFit(); + Q_INVOKABLE MeshProxyList getMeshes() const; + void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName); void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName); From dd90c8c515561ec4c480d12975a84ae722ee2d15 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 22 Feb 2018 09:36:17 -0500 Subject: [PATCH 17/29] fix ubuntu compile warnings --- interface/src/Application.cpp | 2 +- .../src/graphics-scripting/Forward.h | 14 +++------- .../GraphicsScriptingInterface.cpp | 2 +- .../src/graphics-scripting/ScriptableMesh.cpp | 13 ++-------- .../src/graphics-scripting/ScriptableMesh.h | 18 +++++-------- .../graphics-scripting/ScriptableModel.cpp | 10 +++++++ .../src/graphics/BufferViewHelpers.cpp | 26 +++++++++++++------ 7 files changed, 41 insertions(+), 44 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f344c94161..69be01fc0c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -607,7 +607,7 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt class ApplicationMeshProvider : public scriptable::ModelProviderFactory { public: - virtual scriptable::ModelProviderPointer lookupModelProvider(const QUuid& uuid) { + virtual scriptable::ModelProviderPointer lookupModelProvider(const QUuid& uuid) override { QString error; scriptable::ModelProviderPointer provider; diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h index 95fcf51921..7b2126cf8f 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/Forward.h +++ b/libraries/graphics-scripting/src/graphics-scripting/Forward.h @@ -42,7 +42,7 @@ namespace scriptable { ScriptableMeshBase(WeakModelProviderPointer provider, ScriptableModelBasePointer model, WeakMeshPointer mesh, const QVariantMap& metadata); ScriptableMeshBase(WeakMeshPointer mesh = WeakMeshPointer()); ScriptableMeshBase(MeshPointer mesh, const QVariantMap& metadata); - ScriptableMeshBase(const ScriptableMeshBase& other) { *this = other; } + ScriptableMeshBase(const ScriptableMeshBase& other) : QObject() { *this = other; } ScriptableMeshBase& operator=(const ScriptableMeshBase& view); virtual ~ScriptableMeshBase(); Q_INVOKABLE const scriptable::MeshPointer getMeshPointer() const { return mesh.lock(); } @@ -60,16 +60,8 @@ namespace scriptable { QVector meshes; ScriptableModelBase(QObject* parent = nullptr) : QObject(parent) {} - ScriptableModelBase(const ScriptableModelBase& other) { *this = other; } - ScriptableModelBase& operator=(const ScriptableModelBase& other) { - provider = other.provider; - objectID = other.objectID; - metadata = other.metadata; - for (auto& mesh : other.meshes) { - append(mesh); - } - return *this; - } + ScriptableModelBase(const ScriptableModelBase& other) : QObject() { *this = other; } + ScriptableModelBase& operator=(const ScriptableModelBase& other); virtual ~ScriptableModelBase(); void mixin(const QVariantMap& other); diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index 58fcd7a064..28d5d0edfc 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -27,7 +27,7 @@ #include "GraphicsScriptingInterface.moc" -GraphicsScriptingInterface::GraphicsScriptingInterface(QObject* parent) : QObject(parent) { +GraphicsScriptingInterface::GraphicsScriptingInterface(QObject* parent) : QObject(parent), QScriptable() { if (auto scriptEngine = qobject_cast(parent)) { this->registerMetaTypes(scriptEngine); } diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp index 28e57692f6..3678c54b7b 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp @@ -23,12 +23,12 @@ #include "ScriptableMesh.moc" scriptable::ScriptableMeshPart::ScriptableMeshPart(scriptable::ScriptableMeshPointer parentMesh, int partIndex) - : parentMesh(parentMesh), partIndex(partIndex) { + : QObject(), parentMesh(parentMesh), partIndex(partIndex) { setObjectName(QString("%1.part[%2]").arg(parentMesh ? parentMesh->objectName() : "").arg(partIndex)); } scriptable::ScriptableMesh::ScriptableMesh(const ScriptableMeshBase& other) - : ScriptableMeshBase(other) { + : ScriptableMeshBase(other), QScriptable() { auto mesh = getMeshPointer(); QString name = mesh ? QString::fromStdString(mesh->modelName) : ""; if (name.isEmpty()) { @@ -60,15 +60,6 @@ quint32 scriptable::ScriptableMesh::getNumVertices() const { return 0; } -// glm::vec3 ScriptableMesh::getPos3(quint32 index) const { -// if (auto mesh = getMeshPointer()) { -// if (index < getNumVertices()) { -// return mesh->getPos3(index); -// } -// } -// return glm::vec3(NAN); -// } - QVector scriptable::ScriptableMesh::findNearbyIndices(const glm::vec3& origin, float epsilon) const { QVector result; if (auto mesh = getMeshPointer()) { diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h index 2b9399d6ef..2cba33edde 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h @@ -33,14 +33,11 @@ namespace scriptable { bool hasValidOwnedMesh() const { return (bool)getOwnedMeshPointer(); } operator const ScriptableMeshBase*() const { return (qobject_cast(this)); } - ScriptableMesh(scriptable::MeshPointer mesh) : ScriptableMeshBase(mesh) { ownedMesh = mesh; } + ScriptableMesh(scriptable::MeshPointer mesh) : ScriptableMeshBase(mesh), QScriptable() { ownedMesh = mesh; } ScriptableMesh(WeakModelProviderPointer provider, ScriptableModelBasePointer model, MeshPointer mesh, const QVariantMap& metadata) - : ScriptableMeshBase(provider, model, mesh, metadata) { ownedMesh = mesh; } - //ScriptableMesh& operator=(const ScriptableMesh& other) { model=other.model; mesh=other.mesh; metadata=other.metadata; return *this; }; - //ScriptableMesh() : QObject(), model(nullptr) {} - //ScriptableMesh(const ScriptableMesh& other) : QObject(), model(other.model), mesh(other.mesh), metadata(other.metadata) {} + : ScriptableMeshBase(provider, model, mesh, metadata), QScriptable() { ownedMesh = mesh; } ScriptableMesh(const ScriptableMeshBase& other); - ScriptableMesh(const ScriptableMesh& other) : ScriptableMeshBase(other) {}; + ScriptableMesh(const ScriptableMesh& other) : ScriptableMeshBase(other), QScriptable() {}; virtual ~ScriptableMesh(); Q_INVOKABLE const scriptable::ScriptableModelPointer getParentModel() const { return qobject_cast(model); } @@ -91,12 +88,9 @@ namespace scriptable { Q_PROPERTY(QVariantMap metadata MEMBER metadata) - //Q_PROPERTY(scriptable::ScriptableMeshPointer parentMesh MEMBER parentMesh CONSTANT HIDE) - ScriptableMeshPart(scriptable::ScriptableMeshPointer parentMesh, int partIndex); ScriptableMeshPart& operator=(const ScriptableMeshPart& view) { parentMesh=view.parentMesh; return *this; }; - ScriptableMeshPart(const ScriptableMeshPart& other) : parentMesh(other.parentMesh), partIndex(other.partIndex) {} - // ~ScriptableMeshPart() { qDebug() << "~ScriptableMeshPart" << this; } + ScriptableMeshPart(const ScriptableMeshPart& other) : QObject(), QScriptable(), parentMesh(other.parentMesh), partIndex(other.partIndex) {} public slots: scriptable::ScriptableMeshPointer getParentMesh() const { return parentMesh; } @@ -148,8 +142,8 @@ namespace scriptable { class GraphicsScriptingInterface : public QObject, QScriptable { Q_OBJECT public: - GraphicsScriptingInterface(QObject* parent = nullptr) : QObject(parent) {} - GraphicsScriptingInterface(const GraphicsScriptingInterface& other) {} + GraphicsScriptingInterface(QObject* parent = nullptr) : QObject(parent), QScriptable() {} + GraphicsScriptingInterface(const GraphicsScriptingInterface& other) : QObject(), QScriptable() {} public slots: ScriptableMeshPartPointer exportMeshPart(ScriptableMeshPointer mesh, int part=0) { return ScriptableMeshPartPointer(new ScriptableMeshPart(mesh, part)); diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp index 5d0d459446..cc882aa7bb 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp @@ -27,6 +27,16 @@ void scriptable::ScriptableModelBase::mixin(const QVariantMap& modelMetaData) { } } +scriptable::ScriptableModelBase& scriptable::ScriptableModelBase::operator=(const scriptable::ScriptableModelBase& other) { + provider = other.provider; + objectID = other.objectID; + metadata = other.metadata; + for (auto& mesh : other.meshes) { + append(mesh); + } + return *this; +} + scriptable::ScriptableModelBase::~ScriptableModelBase() { #ifdef SCRIPTABLE_MESH_DEBUG qCDebug(graphics_scripting) << "~ScriptableModelBase" << this; diff --git a/libraries/graphics/src/graphics/BufferViewHelpers.cpp b/libraries/graphics/src/graphics/BufferViewHelpers.cpp index ea511d82f4..a15ce1b998 100644 --- a/libraries/graphics/src/graphics/BufferViewHelpers.cpp +++ b/libraries/graphics/src/graphics/BufferViewHelpers.cpp @@ -281,13 +281,22 @@ gpu::BufferView buffer_helpers::fromVector(const QVector& elements, const gpu auto vertexBuffer = std::make_shared(elements.size() * sizeof(T), (gpu::Byte*)elements.data()); return { vertexBuffer, 0, vertexBuffer->getSize(),sizeof(T), elementType }; } + +namespace { + template + gpu::BufferView _fromVector(const QVector& elements, const gpu::Element& elementType) { + auto vertexBuffer = std::make_shared(elements.size() * sizeof(T), (gpu::Byte*)elements.data()); + return { vertexBuffer, 0, vertexBuffer->getSize(),sizeof(T), elementType }; + } +} + template<> gpu::BufferView buffer_helpers::fromVector( const QVector& elements, const gpu::Element& elementType -) { return fromVector(elements, elementType); } +) { return _fromVector(elements, elementType); } template<> gpu::BufferView buffer_helpers::fromVector( const QVector& elements, const gpu::Element& elementType -) { return fromVector(elements, elementType); } +) { return _fromVector(elements, elementType); } template struct GpuVec4ToGlm; template struct GpuScalarToGlm; @@ -537,14 +546,14 @@ std::map buffer_helpers::gatherBufferViews(graphics::M auto slot = a.second; auto view = getBufferView(mesh, slot); auto beforeCount = view.getNumElements(); +#if DEV_BUILD auto beforeTotal = view._size; +#endif if (expandToMatchPositions.contains(name)) { expandAttributeToMatchPositions(mesh, slot); } if (beforeCount > 0) { auto element = view._element; - auto vecN = element.getScalarCount(); - //auto type = element.getType(); QString typeName = QString("%1").arg(element.getType()); #ifdef DEBUG_BUFFERVIEW_SCRIPTING typeName = DebugNames::stringFrom(element.getType()); @@ -553,6 +562,7 @@ std::map buffer_helpers::gatherBufferViews(graphics::M attributeViews[name] = getBufferView(mesh, slot); #if DEV_BUILD + const auto vecN = element.getScalarCount(); auto afterTotal = attributeViews[name]._size; auto afterCount = attributeViews[name].getNumElements(); if (beforeTotal != afterTotal || beforeCount != afterCount) { @@ -604,14 +614,14 @@ bool buffer_helpers::recalculateNormals(graphics::MeshPointer mesh) { #endif break; } - vertexToFaces[glm::to_string(face.v0).c_str()] << i; - vertexToFaces[glm::to_string(face.v1).c_str()] << i; - vertexToFaces[glm::to_string(face.v2).c_str()] << i; + vertexToFaces[glm::to_string(glm::dvec3(face.v0)).c_str()] << i; + vertexToFaces[glm::to_string(glm::dvec3(face.v1)).c_str()] << i; + vertexToFaces[glm::to_string(glm::dvec3(face.v2)).c_str()] << i; } for (quint32 j = 0; j < numNormals; j++) { //auto v = verts.get(j); glm::vec3 normal { 0.0f, 0.0f, 0.0f }; - QString key { glm::to_string(verts.get(j)).c_str() }; + QString key { glm::to_string(glm::dvec3(verts.get(j))).c_str() }; const auto& faces = vertexToFaces.value(key); if (faces.size()) { for (const auto i : faces) { From d1c4bde677bb5949751488eeb8ed8937c83fe38a Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 22 Feb 2018 20:53:27 -0500 Subject: [PATCH 18/29] revert OBJWriter.cpp --- libraries/fbx/src/OBJWriter.cpp | 85 +++++++++++---------------------- 1 file changed, 28 insertions(+), 57 deletions(-) diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp index ffc34573d4..4441ae6649 100644 --- a/libraries/fbx/src/OBJWriter.cpp +++ b/libraries/fbx/src/OBJWriter.cpp @@ -9,12 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "OBJWriter.h" - #include #include -#include -#include +#include "graphics/Geometry.h" +#include "OBJWriter.h" #include "ModelFormatLogging.h" static QString formatFloat(double n) { @@ -48,12 +46,9 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { QList meshNormalStartOffset; int currentVertexStartOffset = 0; int currentNormalStartOffset = 0; - int subMeshIndex = 0; - out << "# OBJWriter::writeOBJToTextStream\n"; // write out vertices (and maybe colors) foreach (const MeshPointer& mesh, meshes) { - out << "# vertices::subMeshIndex " << subMeshIndex++ << "\n"; meshVertexStartOffset.append(currentVertexStartOffset); const gpu::BufferView& vertexBuffer = mesh->getVertexBuffer(); @@ -70,10 +65,7 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { out << formatFloat(v[1]) << " "; out << formatFloat(v[2]); if (colorIndex < numColors) { - glm::vec3 color = buffer_helpers::convert(colorsBufferView, colorIndex); - // TODO: still verifying that the above decodes properly; previous variations were: - // glm::vec3 color = buffer_helpers::glmVecFromVariant(buffer_helpers::toVariant(colorsBufferView, colorIndex)); - // glm::vec3 color = colorsBufferView.get(colorIndex); + glm::vec3 color = colorsBufferView.get(colorIndex); out << " " << formatFloat(color[0]); out << " " << formatFloat(color[1]); out << " " << formatFloat(color[2]); @@ -89,17 +81,12 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { // write out normals bool haveNormals = true; - subMeshIndex = 0; foreach (const MeshPointer& mesh, meshes) { - out << "# normals::subMeshIndex " << subMeshIndex++ << "\n"; meshNormalStartOffset.append(currentNormalStartOffset); const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(gpu::Stream::InputSlot::NORMAL); gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements(); for (gpu::BufferView::Index i = 0; i < numNormals; i++) { - glm::vec3 normal = buffer_helpers::convert(normalsBufferView, i); - // TODO: still verifying that the above decodes properly; previous variations were: - // glm::vec3 normal = buffer_helpers::glmVecFromVariant(buffer_helpers::toVariant(normalsBufferView, i)); - // glm::vec3 normal = normalsBufferView.get(i); + glm::vec3 normal = normalsBufferView.get(i); out << "vn "; out << formatFloat(normal[0]) << " "; out << formatFloat(normal[1]) << " "; @@ -111,9 +98,7 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { // write out faces int nth = 0; - subMeshIndex = 0; foreach (const MeshPointer& mesh, meshes) { - out << "# faces::subMeshIndex " << subMeshIndex++ << "\n"; currentVertexStartOffset = meshVertexStartOffset.takeFirst(); currentNormalStartOffset = meshNormalStartOffset.takeFirst(); @@ -121,25 +106,35 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { const gpu::BufferView& indexBuffer = mesh->getIndexBuffer(); graphics::Index partCount = (graphics::Index)mesh->getNumParts(); - QString name = (!mesh->displayName.size() ? QString("mesh-%1-part").arg(nth) : QString::fromStdString(mesh->displayName)) - .replace(QRegExp("[^-_a-zA-Z0-9]"), "_"); for (int partIndex = 0; partIndex < partCount; partIndex++) { const graphics::Mesh::Part& part = partBuffer.get(partIndex); - out << QString("g %1-%2-%3\n").arg(subMeshIndex, 3, 10, QChar('0')).arg(name).arg(partIndex); + out << "g part-" << nth++ << "\n"; - const bool shorts = indexBuffer._element == gpu::Element::INDEX_UINT16; - auto face = [&](uint32_t i0, uint32_t i1, uint32_t i2) { - uint32_t index0, index1, index2; - if (shorts) { - index0 = indexBuffer.get(i0); - index1 = indexBuffer.get(i1); - index2 = indexBuffer.get(i2); - } else { - index0 = indexBuffer.get(i0); - index1 = indexBuffer.get(i1); - index2 = indexBuffer.get(i2); + // graphics::Mesh::TRIANGLES + // TODO -- handle other formats + gpu::BufferView::Iterator indexItr = indexBuffer.cbegin(); + indexItr += part._startIndex; + + int indexCount = 0; + while (indexItr != indexBuffer.cend() && indexCount < part._numIndices) { + uint32_t index0 = *indexItr; + indexItr++; + indexCount++; + if (indexItr == indexBuffer.cend() || indexCount >= part._numIndices) { + qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3"; + break; } + uint32_t index1 = *indexItr; + indexItr++; + indexCount++; + if (indexItr == indexBuffer.cend() || indexCount >= part._numIndices) { + qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3"; + break; + } + uint32_t index2 = *indexItr; + indexItr++; + indexCount++; out << "f "; if (haveNormals) { @@ -151,30 +146,6 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { out << currentVertexStartOffset + index1 + 1 << " "; out << currentVertexStartOffset + index2 + 1 << "\n"; } - }; - - // graphics::Mesh::TRIANGLES / graphics::Mesh::QUADS - // TODO -- handle other formats - uint32_t len = part._startIndex + part._numIndices; - qCDebug(modelformat) << "OBJWriter -- part" << partIndex << "topo" << part._topology << "index elements" << (shorts ? "uint16_t" : "uint32_t"); - if (part._topology == graphics::Mesh::TRIANGLES && len % 3 != 0) { - qCDebug(modelformat) << "OBJWriter -- index buffer length isn't a multiple of 3" << len; - } - if (part._topology == graphics::Mesh::QUADS && len % 4 != 0) { - qCDebug(modelformat) << "OBJWriter -- index buffer length isn't a multiple of 4" << len; - } - if (len > indexBuffer.getNumElements()) { - qCDebug(modelformat) << "OBJWriter -- len > index size" << len << indexBuffer.getNumElements(); - } - if (part._topology == graphics::Mesh::QUADS) { - for (uint32_t idx = part._startIndex; idx+3 < len; idx += 4) { - face(idx+0, idx+1, idx+3); - face(idx+1, idx+2, idx+3); - } - } else if (part._topology == graphics::Mesh::TRIANGLES) { - for (uint32_t idx = part._startIndex; idx+2 < len; idx += 3) { - face(idx+0, idx+1, idx+2); - } } out << "\n"; } From 0dd367216278a5bf640eca89774ddf1043c62b57 Mon Sep 17 00:00:00 2001 From: humbletim Date: Thu, 22 Feb 2018 21:20:56 -0500 Subject: [PATCH 19/29] CR feedback / cleanup --- .../BufferViewScripting.cpp | 13 +--- .../src/graphics-scripting/DebugNames.h | 72 ------------------- .../GraphicsScriptingInterface.cpp | 1 - .../GraphicsScriptingInterface.h | 3 +- .../src/graphics-scripting/ScriptableMesh.cpp | 25 ++----- .../src/graphics-scripting/ScriptableMesh.h | 2 + .../graphics-scripting/ScriptableModel.cpp | 20 ++---- .../src/graphics-scripting/ScriptableModel.h | 1 + .../src/graphics/BufferViewHelpers.cpp | 6 +- .../graphics/src/graphics/BufferViewHelpers.h | 3 + 10 files changed, 22 insertions(+), 124 deletions(-) delete mode 100644 libraries/graphics-scripting/src/graphics-scripting/DebugNames.h diff --git a/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp b/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp index 31cf5ff8f3..9d7a0e1f30 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp @@ -11,15 +11,6 @@ #include -#ifdef DEBUG_BUFFERVIEW_SCRIPTING - #include "DebugNames.h" -#endif - -namespace { - const std::array XYZW = {{ "x", "y", "z", "w" }}; - const std::array ZERO123 = {{ "0", "1", "2", "3" }}; -} - template QScriptValue getBufferViewElement(QScriptEngine* js, const gpu::BufferView& view, quint32 index, bool asArray = false) { return glmVecToScriptValue(js, view.get(index), asArray); @@ -45,7 +36,7 @@ bool bufferViewElementFromScriptValue(const QScriptValue& v, const gpu::BufferVi template QScriptValue glmVecToScriptValue(QScriptEngine *js, const T& v, bool asArray) { static const auto len = T().length(); - const auto& components = asArray ? ZERO123 : XYZW; + const auto& components = asArray ? buffer_helpers::ZERO123 : buffer_helpers::XYZW; auto obj = asArray ? js->newArray() : js->newObject(); for (int i = 0; i < len ; i++) { const auto key = components[i]; @@ -65,7 +56,7 @@ QScriptValue glmVecToScriptValue(QScriptEngine *js, const T& v, bool asArray) { template const T glmVecFromScriptValue(const QScriptValue& v) { static const auto len = T().length(); - const auto& components = v.property("x").isValid() ? XYZW : ZERO123; + const auto& components = v.property("x").isValid() ? buffer_helpers::XYZW : buffer_helpers::ZERO123; T result; for (int i = 0; i < len ; i++) { const auto key = components[i]; diff --git a/libraries/graphics-scripting/src/graphics-scripting/DebugNames.h b/libraries/graphics-scripting/src/graphics-scripting/DebugNames.h deleted file mode 100644 index e5edf1c9d8..0000000000 --- a/libraries/graphics-scripting/src/graphics-scripting/DebugNames.h +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -//#include - -Q_DECLARE_METATYPE(gpu::Type); -#ifdef QT_MOC_RUN -class DebugNames { - Q_OBJECT -public: -#else - namespace DebugNames { - Q_NAMESPACE - #endif - -enum Type : uint8_t { - - FLOAT = 0, - INT32, - UINT32, - HALF, - INT16, - UINT16, - INT8, - UINT8, - - NINT32, - NUINT32, - NINT16, - NUINT16, - NINT8, - NUINT8, - - COMPRESSED, - - NUM_TYPES, - - BOOL = UINT8, - NORMALIZED_START = NINT32, -}; - - Q_ENUM_NS(Type) - enum InputSlot { - POSITION = 0, - NORMAL = 1, - COLOR = 2, - TEXCOORD0 = 3, - TEXCOORD = TEXCOORD0, - TANGENT = 4, - SKIN_CLUSTER_INDEX = 5, - SKIN_CLUSTER_WEIGHT = 6, - TEXCOORD1 = 7, - TEXCOORD2 = 8, - TEXCOORD3 = 9, - TEXCOORD4 = 10, - - NUM_INPUT_SLOTS, - - DRAW_CALL_INFO = 15, // Reserve last input slot for draw call infos - }; - - Q_ENUM_NS(InputSlot) - inline QString stringFrom(Type t) { return QVariant::fromValue(t).toString(); } - inline QString stringFrom(InputSlot t) { return QVariant::fromValue(t).toString(); } - inline QString stringFrom(gpu::Type t) { return stringFrom((Type)t); } - inline QString stringFrom(gpu::Stream::Slot t) { return stringFrom((InputSlot)t); } - - extern const QMetaObject staticMetaObject; - }; diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index 28d5d0edfc..01e68c5328 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -11,7 +11,6 @@ #include "GraphicsScriptingInterface.h" #include "BaseScriptEngine.h" #include "BufferViewScripting.h" -#include "DebugNames.h" #include "GraphicsScriptingUtil.h" #include "OBJWriter.h" #include "RegisteredMetaTypes.h" diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h index 6afa549a19..ab2e5467db 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h @@ -24,6 +24,7 @@ class GraphicsScriptingInterface : public QObject, public QScriptable, public De Q_OBJECT public: + static void registerMetaTypes(QScriptEngine* engine); GraphicsScriptingInterface(QObject* parent = nullptr); public slots: @@ -39,8 +40,6 @@ public slots: QString meshToOBJ(const scriptable::ScriptableModel& in); - static void registerMetaTypes(QScriptEngine* engine); - private: scriptable::MeshPointer getMeshPointer(scriptable::ScriptableMeshPointer meshProxy); scriptable::MeshPointer getMeshPointer(scriptable::ScriptableMesh& meshProxy); diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp index 3678c54b7b..643debf475 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp @@ -8,7 +8,6 @@ #include "ScriptableMesh.h" #include "BufferViewScripting.h" -#include "DebugNames.h" #include "GraphicsScriptingUtil.h" #include "OBJWriter.h" #include @@ -508,14 +507,11 @@ scriptable::ScriptableMeshPointer scriptable::ScriptableMesh::cloneMesh(bool rec qCInfo(graphics_scripting) << "ScriptableMesh::cloneMesh -- !meshPointer"; return nullptr; } - // qCInfo(graphics_scripting) << "ScriptableMesh::cloneMesh..."; auto clone = buffer_helpers::cloneMesh(mesh); - // qCInfo(graphics_scripting) << "ScriptableMesh::cloneMesh..."; if (recalcNormals) { buffer_helpers::recalculateNormals(clone); } - //qCDebug(graphics_scripting) << clone.get();// << metadata; auto meshPointer = scriptable::make_scriptowned(provider, model, clone, metadata); clone.reset(); // free local reference // qCInfo(graphics_scripting) << "========= ScriptableMesh::cloneMesh..." << meshPointer << meshPointer->ownedMesh.use_count(); @@ -549,7 +545,6 @@ scriptable::ScriptableMeshBase::ScriptableMeshBase(scriptable::MeshPointer mesh, : ScriptableMeshBase(WeakModelProviderPointer(), nullptr, mesh, metadata) { ownedMesh = mesh; } -//scriptable::ScriptableMeshBase::ScriptableMeshBase(const scriptable::ScriptableMeshBase& other) { *this = other; } scriptable::ScriptableMeshBase& scriptable::ScriptableMeshBase::operator=(const scriptable::ScriptableMeshBase& view) { provider = view.provider; model = view.model; @@ -617,22 +612,6 @@ namespace { void meshPartPointerFromScriptValue(const QScriptValue& value, scriptable::ScriptableMeshPartPointer &out) { out = scriptable::qpointer_qobject_cast(value); } - - // 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); - // } QScriptValue qVectorUInt32ToScriptValue(QScriptEngine* engine, const QVector& vector) { return qScriptValueFromSequence(engine, vector); @@ -700,11 +679,15 @@ bool scriptable::GraphicsScriptingInterface::updateMeshPart(ScriptableMeshPointe Q_ASSERT(part->parentMesh); auto tmp = exportMeshPart(mesh, part->partIndex); if (part->parentMesh == mesh) { +#ifdef SCRIPTABLE_MESH_DEBUG qCInfo(graphics_scripting) << "updateMeshPart -- update via clone" << mesh << part; +#endif tmp->replaceMeshData(part->cloneMeshPart()); return false; } else { +#ifdef SCRIPTABLE_MESH_DEBUG qCInfo(graphics_scripting) << "updateMeshPart -- update via inplace" << mesh << part; +#endif tmp->replaceMeshData(part); return true; } diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h index 2cba33edde..459613135a 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h @@ -16,6 +16,8 @@ #include #include +#include "GraphicsScriptingUtil.h" + namespace scriptable { class ScriptableMesh : public ScriptableMeshBase, QScriptable { Q_OBJECT diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp index cc882aa7bb..229d56adab 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp @@ -41,34 +41,29 @@ scriptable::ScriptableModelBase::~ScriptableModelBase() { #ifdef SCRIPTABLE_MESH_DEBUG qCDebug(graphics_scripting) << "~ScriptableModelBase" << this; #endif + // makes cleanup order more deterministic to help with debugging for (auto& m : meshes) { m.ownedMesh.reset(); - //qCDebug(graphics_scripting) << "~~~~ScriptableModelBase" << &m << m.mesh.use_count(); } 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 }; } void scriptable::ScriptableModelBase::append(const ScriptableMeshBase& mesh, const QVariantMap& modelMetaData) { - //qCDebug(graphics_scripting) << "+ APPEND ScriptableMeshBase" << &mesh; if (mesh.provider.lock().get() != provider.lock().get()) { qCDebug(graphics_scripting) << "warning: appending mesh from different provider..." << mesh.provider.lock().get() << " != " << provider.lock().get(); } - //if (mesh.model && mesh.model != this) { - // qCDebug(graphics_scripting) << "warning: appending mesh from different model..." << mesh.model << " != " << this; - //} meshes << mesh; mixin(modelMetaData); } void scriptable::ScriptableModelBase::append(const ScriptableModelBase& other, const QVariantMap& modelMetaData) { - //qCDebug(graphics_scripting) << "+ APPEND ScriptableModelBase" << &other; - for (const auto& mesh : other.meshes) { append(mesh); } + for (const auto& mesh : other.meshes) { + append(mesh); + } mixin(other.metadata); mixin(modelMetaData); } @@ -82,21 +77,16 @@ QString scriptable::ScriptableModel::toString() const { scriptable::ScriptableModelPointer scriptable::ScriptableModel::cloneModel(const QVariantMap& options) { scriptable::ScriptableModelPointer clone = scriptable::ScriptableModelPointer(new scriptable::ScriptableModel(*this)); - qCDebug(graphics_scripting) << "clone->getNumMeshes" << clone->getNumMeshes(); clone->meshes.clear(); - qCDebug(graphics_scripting) << "..clone->getNumMeshes" << clone->getNumMeshes(); for (const auto &mesh : getConstMeshes()) { auto cloned = mesh->cloneMesh(options.value("recalculateNormals").toBool()); if (auto tmp = qobject_cast(cloned)) { - qCDebug(graphics_scripting) << "++ APPEND" << tmp << tmp->ownedMesh.use_count() << tmp->metadata.value("__ownership__") << tmp->metadata.value("__native__"); clone->meshes << *tmp; - tmp->deleteLater(); - qCDebug(graphics_scripting) << "//++ APPEND" << clone->meshes.constLast().ownedMesh.use_count();; + tmp->deleteLater(); // schedule our copy for cleanup } else { qCDebug(graphics_scripting) << "error cloning mesh" << cloned; } } - qCDebug(graphics_scripting) << "//clone->getNumMeshes" << clone->getNumMeshes(); return clone; } diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h index bdfaac5d7c..a78c9a4ef5 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h @@ -1,6 +1,7 @@ #pragma once #include "Forward.h" +#include "GraphicsScriptingUtil.h" class QScriptValue; namespace scriptable { diff --git a/libraries/graphics/src/graphics/BufferViewHelpers.cpp b/libraries/graphics/src/graphics/BufferViewHelpers.cpp index a15ce1b998..ddf3d84c89 100644 --- a/libraries/graphics/src/graphics/BufferViewHelpers.cpp +++ b/libraries/graphics/src/graphics/BufferViewHelpers.cpp @@ -34,10 +34,12 @@ namespace glm { namespace { QLoggingCategory bufferhelper_logging{ "hifi.bufferview" }; - const std::array XYZW = { { "x", "y", "z", "w" } }; - const std::array ZERO123 = { { "0", "1", "2", "3" } }; } + +const std::array buffer_helpers::XYZW = { { "x", "y", "z", "w" } }; +const std::array buffer_helpers::ZERO123 = { { "0", "1", "2", "3" } }; + gpu::BufferView buffer_helpers::getBufferView(graphics::MeshPointer mesh, gpu::Stream::Slot slot) { return slot == gpu::Stream::POSITION ? mesh->getVertexBuffer() : mesh->getAttributeBuffer(slot); } diff --git a/libraries/graphics/src/graphics/BufferViewHelpers.h b/libraries/graphics/src/graphics/BufferViewHelpers.h index 53fddfe581..6d4908a2c7 100644 --- a/libraries/graphics/src/graphics/BufferViewHelpers.h +++ b/libraries/graphics/src/graphics/BufferViewHelpers.h @@ -49,4 +49,7 @@ struct buffer_helpers { 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); + + static const std::array XYZW; + static const std::array ZERO123; }; From 6ea4b660b747745bde7c3466c3721713f6ee99c7 Mon Sep 17 00:00:00 2001 From: humbletim Date: Fri, 23 Feb 2018 07:47:39 -0500 Subject: [PATCH 20/29] CR feedback; remove ok bool pointer and all metadata; cleanup --- interface/src/ui/overlays/Base3DOverlay.h | 2 +- interface/src/ui/overlays/ModelOverlay.cpp | 8 +- interface/src/ui/overlays/ModelOverlay.h | 2 +- interface/src/ui/overlays/Shape3DOverlay.cpp | 7 +- interface/src/ui/overlays/Shape3DOverlay.h | 2 +- .../src/avatars-renderer/Avatar.cpp | 17 +- .../src/avatars-renderer/Avatar.h | 2 +- .../src/RenderableEntityItem.h | 2 +- .../src/RenderableModelEntityItem.cpp | 8 +- .../src/RenderableModelEntityItem.h | 2 +- .../src/RenderablePolyLineEntityItem.cpp | 4 +- .../src/RenderablePolyLineEntityItem.h | 2 +- .../src/RenderablePolyVoxEntityItem.cpp | 7 +- .../src/RenderablePolyVoxEntityItem.h | 6 +- .../src/RenderableShapeEntityItem.cpp | 11 +- .../src/RenderableShapeEntityItem.h | 2 +- .../src/graphics-scripting/Forward.h | 26 +-- .../GraphicsScriptingInterface.cpp | 174 +++++++++++------- .../GraphicsScriptingInterface.h | 22 ++- .../GraphicsScriptingUtil.h | 22 +-- .../src/graphics-scripting/ScriptableMesh.cpp | 149 ++++++--------- .../src/graphics-scripting/ScriptableMesh.h | 68 ++----- .../graphics-scripting/ScriptableModel.cpp | 41 ++--- .../src/graphics-scripting/ScriptableModel.h | 15 +- .../src/graphics/BufferViewHelpers.cpp | 42 +---- libraries/render-utils/src/Model.cpp | 53 +++--- libraries/render-utils/src/Model.h | 2 +- .../src/Model_temporary_hack.cpp.h | 46 +---- tools/jsdoc/package-lock.json | 138 -------------- 29 files changed, 307 insertions(+), 575 deletions(-) delete mode 100644 tools/jsdoc/package-lock.json diff --git a/interface/src/ui/overlays/Base3DOverlay.h b/interface/src/ui/overlays/Base3DOverlay.h index 25bacf2e7e..bbf064fddd 100644 --- a/interface/src/ui/overlays/Base3DOverlay.h +++ b/interface/src/ui/overlays/Base3DOverlay.h @@ -36,7 +36,7 @@ public: virtual bool is3D() const override { return true; } virtual uint32_t fetchMetaSubItems(render::ItemIDs& subItems) const override { subItems.push_back(getRenderItemID()); return (uint32_t) subItems.size(); } - virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override { return scriptable::ModelProvider::modelUnavailableError(ok); } + virtual scriptable::ScriptableModelBase getScriptableModel() override { return scriptable::ScriptableModelBase(); } // TODO: consider implementing registration points in this class glm::vec3 getCenter() const { return getWorldPosition(); } diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index 4ff5499a12..ffcc18032c 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -665,11 +665,11 @@ void ModelOverlay::processMaterials() { } } -scriptable::ScriptableModelBase ModelOverlay::getScriptableModel(bool* ok) { +scriptable::ScriptableModelBase ModelOverlay::getScriptableModel() { if (!_model || !_model->isLoaded()) { - return Base3DOverlay::getScriptableModel(ok); + return Base3DOverlay::getScriptableModel(); } - auto result = _model->getScriptableModel(ok); + auto result = _model->getScriptableModel(); result.objectID = getID(); return result; -} \ No newline at end of file +} diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index 60b970425d..fa399d40f9 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -62,7 +62,7 @@ public: void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) override; void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) override; - virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override; + virtual scriptable::ScriptableModelBase getScriptableModel() override; protected: Transform evalRenderTransform() override; diff --git a/interface/src/ui/overlays/Shape3DOverlay.cpp b/interface/src/ui/overlays/Shape3DOverlay.cpp index 04557bcf5d..8f7f8b70d9 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.cpp +++ b/interface/src/ui/overlays/Shape3DOverlay.cpp @@ -180,14 +180,13 @@ Transform Shape3DOverlay::evalRenderTransform() { return transform; } -scriptable::ScriptableModelBase Shape3DOverlay::getScriptableModel(bool* ok) { +scriptable::ScriptableModelBase Shape3DOverlay::getScriptableModel() { auto geometryCache = DependencyManager::get(); auto vertexColor = ColorUtils::toVec3(_color); scriptable::ScriptableModelBase result; result.objectID = getID(); - result.append(geometryCache->meshFromShape(_shape, vertexColor), {{ "shape", shapeStrings[_shape] }}); - if (ok) { - *ok = true; + if (auto mesh = geometryCache->meshFromShape(_shape, vertexColor)) { + result.append(mesh); } return result; } diff --git a/interface/src/ui/overlays/Shape3DOverlay.h b/interface/src/ui/overlays/Shape3DOverlay.h index f5246d95ac..447ee47e13 100644 --- a/interface/src/ui/overlays/Shape3DOverlay.h +++ b/interface/src/ui/overlays/Shape3DOverlay.h @@ -37,7 +37,7 @@ public: void setProperties(const QVariantMap& properties) override; QVariant getProperty(const QString& property) override; - virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override; + virtual scriptable::ScriptableModelBase getScriptableModel() override; protected: Transform evalRenderTransform() override; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 48f0c70dfa..25ddfba670 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1795,22 +1795,11 @@ void Avatar::processMaterials() { } } -scriptable::ScriptableModelBase Avatar::getScriptableModel(bool* ok) { +scriptable::ScriptableModelBase Avatar::getScriptableModel() { if (!_skeletonModel || !_skeletonModel->isLoaded()) { - return scriptable::ModelProvider::modelUnavailableError(ok); + return scriptable::ScriptableModelBase(); } - scriptable::ScriptableModelBase result = _skeletonModel->getScriptableModel(ok); + auto result = _skeletonModel->getScriptableModel(); result.objectID = getSessionUUID(); - result.mixin({{ "textures", _skeletonModel->getTextures() }}); - // FIXME: for now access to attachment models are merged into the main avatar ScriptableModel set - for (int i = 0; i < (int)_attachmentModels.size(); i++) { - auto& model = _attachmentModels.at(i); - if (model->isLoaded()) { - result.append(model->getScriptableModel(ok), _attachmentData.at(i).toVariant().toMap()); - } - } - if (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 3b3917ad4d..bf0adc5c05 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -276,7 +276,7 @@ public: void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) override; void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) override; - virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override; + virtual scriptable::ScriptableModelBase getScriptableModel() override; public slots: diff --git a/libraries/entities-renderer/src/RenderableEntityItem.h b/libraries/entities-renderer/src/RenderableEntityItem.h index 5526927c13..d34a1127ae 100644 --- a/libraries/entities-renderer/src/RenderableEntityItem.h +++ b/libraries/entities-renderer/src/RenderableEntityItem.h @@ -58,7 +58,7 @@ public: virtual void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName); virtual void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName); - virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override { return scriptable::ModelProvider::modelUnavailableError(ok); } + virtual scriptable::ScriptableModelBase getScriptableModel() override { return scriptable::ScriptableModelBase(); } protected: virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); } diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index 6eed62522f..f3c99cde2d 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -960,15 +960,15 @@ bool RenderableModelEntityItem::getMeshes(MeshProxyList& result) { return !result.isEmpty(); } -scriptable::ScriptableModelBase render::entities::ModelEntityRenderer::getScriptableModel(bool* ok) { +scriptable::ScriptableModelBase render::entities::ModelEntityRenderer::getScriptableModel() { ModelPointer model; withReadLock([&] { model = _model; }); if (!model || !model->isLoaded()) { - return scriptable::ModelProvider::modelUnavailableError(ok); + return scriptable::ScriptableModelBase(); } - auto result = _model->getScriptableModel(ok); + auto result = _model->getScriptableModel(); result.objectID = getEntity()->getID(); return result; } @@ -981,7 +981,7 @@ bool render::entities::ModelEntityRenderer::replaceScriptableModelMeshPart(scrip return false; } - return model->replaceScriptableModelMeshPart(newModel, meshIndex, partIndex); + return model->replaceScriptableModelMeshPart(newModel, meshIndex, partIndex); } void RenderableModelEntityItem::simulateRelayedJoints() { diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index dc73add823..7edaef264d 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -142,7 +142,7 @@ class ModelEntityRenderer : public TypedEntityRenderer PolyLineEntityRenderer::updateVertic return vertices; } -scriptable::ScriptableModelBase PolyLineEntityRenderer::getScriptableModel(bool *ok) { +scriptable::ScriptableModelBase PolyLineEntityRenderer::getScriptableModel() { // TODO: adapt polyline into a triangles mesh... - return EntityRenderer::getScriptableModel(ok); + return EntityRenderer::getScriptableModel(); } void PolyLineEntityRenderer::doRender(RenderArgs* args) { diff --git a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h index d9d770e64f..f460baac59 100644 --- a/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyLineEntityItem.h @@ -25,7 +25,7 @@ class PolyLineEntityRenderer : public TypedEntityRenderer { public: PolyLineEntityRenderer(const EntityItemPointer& entity); - virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override; + virtual scriptable::ScriptableModelBase getScriptableModel() override; protected: virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override; virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene, diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 37e03b2590..2b1de8d11b 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -1451,9 +1451,9 @@ bool RenderablePolyVoxEntityItem::getMeshes(MeshProxyList& result) { return success; } -scriptable::ScriptableModelBase RenderablePolyVoxEntityItem::getScriptableModel(bool * ok) { +scriptable::ScriptableModelBase RenderablePolyVoxEntityItem::getScriptableModel() { if (!updateDependents() || !_mesh) { - return scriptable::ModelProvider::modelUnavailableError(ok); + return scriptable::ScriptableModelBase(); } bool success = false; @@ -1479,9 +1479,6 @@ scriptable::ScriptableModelBase RenderablePolyVoxEntityItem::getScriptableModel( )); } }); - if (ok) { - *ok = success; - } return result; } diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index 295a4066ba..0a00d1cb73 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -114,7 +114,7 @@ public: void setVolDataDirty() { withWriteLock([&] { _volDataDirty = true; _meshReady = false; }); } bool getMeshes(MeshProxyList& result) override; // deprecated - virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override; + virtual scriptable::ScriptableModelBase getScriptableModel() override; private: bool updateOnCount(const ivec3& v, uint8_t toValue); @@ -164,8 +164,8 @@ class PolyVoxEntityRenderer : public TypedEntityRenderer()->getScriptableModel(ok); + virtual scriptable::ScriptableModelBase getScriptableModel() override { + return asTypedEntity()->getScriptableModel(); } protected: diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp index 2e65eba5e5..22cd98b08a 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.cpp @@ -164,22 +164,17 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) { args->_details._trianglesRendered += (int)triCount; } -scriptable::ScriptableModelBase ShapeEntityRenderer::getScriptableModel(bool* ok) { +scriptable::ScriptableModelBase ShapeEntityRenderer::getScriptableModel() { scriptable::ScriptableModelBase result; - result.objectID = getEntity()->getID(); auto geometryCache = DependencyManager::get(); auto geometryShape = geometryCache->getShapeForEntityShape(_shape); glm::vec3 vertexColor; if (_materials["0"].top().material) { vertexColor = _materials["0"].top().material->getAlbedo(); } - auto success = false; if (auto mesh = geometryCache->meshFromShape(geometryShape, vertexColor)) { - result.append({ mesh, {{ "shape", entity::stringFromShape(_shape) }}}); - success = true; - } - if (ok) { - *ok = success; + result.objectID = getEntity()->getID(); + result.append(mesh); } return result; } diff --git a/libraries/entities-renderer/src/RenderableShapeEntityItem.h b/libraries/entities-renderer/src/RenderableShapeEntityItem.h index bd6c77d81d..de855ce0c6 100644 --- a/libraries/entities-renderer/src/RenderableShapeEntityItem.h +++ b/libraries/entities-renderer/src/RenderableShapeEntityItem.h @@ -22,7 +22,7 @@ class ShapeEntityRenderer : public TypedEntityRenderer { public: ShapeEntityRenderer(const EntityItemPointer& entity); - virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override; + virtual scriptable::ScriptableModelBase getScriptableModel() override; private: virtual bool needsRenderUpdate() const override; diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h index 7b2126cf8f..94a96446a0 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/Forward.h +++ b/libraries/graphics-scripting/src/graphics-scripting/Forward.h @@ -36,16 +36,14 @@ namespace scriptable { public: WeakModelProviderPointer provider; ScriptableModelBasePointer model; - WeakMeshPointer mesh; - MeshPointer ownedMesh; - QVariantMap metadata; - ScriptableMeshBase(WeakModelProviderPointer provider, ScriptableModelBasePointer model, WeakMeshPointer mesh, const QVariantMap& metadata); - ScriptableMeshBase(WeakMeshPointer mesh = WeakMeshPointer()); - ScriptableMeshBase(MeshPointer mesh, const QVariantMap& metadata); - ScriptableMeshBase(const ScriptableMeshBase& other) : QObject() { *this = other; } + WeakMeshPointer weakMesh; + MeshPointer strongMesh; + ScriptableMeshBase(WeakModelProviderPointer provider, ScriptableModelBasePointer model, WeakMeshPointer weakMesh, QObject* parent); + ScriptableMeshBase(WeakMeshPointer weakMesh = WeakMeshPointer(), QObject* parent = nullptr); + ScriptableMeshBase(const ScriptableMeshBase& other, QObject* parent = nullptr) : QObject(parent) { *this = other; } ScriptableMeshBase& operator=(const ScriptableMeshBase& view); virtual ~ScriptableMeshBase(); - Q_INVOKABLE const scriptable::MeshPointer getMeshPointer() const { return mesh.lock(); } + Q_INVOKABLE const scriptable::MeshPointer getMeshPointer() const { return weakMesh.lock(); } Q_INVOKABLE const scriptable::ModelProviderPointer getModelProviderPointer() const { return provider.lock(); } Q_INVOKABLE const scriptable::ScriptableModelBasePointer getModelBasePointer() const { return model; } }; @@ -56,18 +54,15 @@ namespace scriptable { public: WeakModelProviderPointer provider; QUuid objectID; // spatially nestable ID - QVariantMap metadata; QVector meshes; ScriptableModelBase(QObject* parent = nullptr) : QObject(parent) {} - ScriptableModelBase(const ScriptableModelBase& other) : QObject() { *this = other; } + ScriptableModelBase(const ScriptableModelBase& other) : QObject(other.parent()) { *this = other; } ScriptableModelBase& operator=(const ScriptableModelBase& other); virtual ~ScriptableModelBase(); - void mixin(const QVariantMap& other); - void append(const ScriptableModelBase& other, const QVariantMap& modelMetadata = QVariantMap()); - void append(scriptable::WeakMeshPointer mesh, const QVariantMap& metadata = QVariantMap()); - void append(const ScriptableMeshBase& mesh, const QVariantMap& metadata = QVariantMap()); + void append(const ScriptableMeshBase& mesh); + void append(scriptable::WeakMeshPointer mesh); // TODO: in future containers for these could go here // QVariantMap shapes; // QVariantMap materials; @@ -78,8 +73,7 @@ namespace scriptable { class ModelProvider { public: NestableType modelProviderType; - static scriptable::ScriptableModelBase modelUnavailableError(bool* ok) { if (ok) { *ok = false; } return {}; } - virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) = 0; + virtual scriptable::ScriptableModelBase getScriptableModel() = 0; virtual bool replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer model, int meshIndex, int partIndex) { return false; } }; diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index 01e68c5328..c336e77762 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -9,7 +9,6 @@ // #include "GraphicsScriptingInterface.h" -#include "BaseScriptEngine.h" #include "BufferViewScripting.h" #include "GraphicsScriptingUtil.h" #include "OBJWriter.h" @@ -24,86 +23,119 @@ #include #include -#include "GraphicsScriptingInterface.moc" - GraphicsScriptingInterface::GraphicsScriptingInterface(QObject* parent) : QObject(parent), QScriptable() { 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); +bool GraphicsScriptingInterface::updateModelObject(QUuid uuid, const scriptable::ScriptableModelPointer model) { + if (auto provider = getModelProvider(uuid)) { + if (auto base = model->operator scriptable::ScriptableModelBasePointer()) { +#ifdef SCRIPTABLE_MESH_DEBUG + qDebug() << "replaceScriptableModelMeshPart" << model->toString() << -1 << -1; +#endif + return provider->replaceScriptableModelMeshPart(base, -1, -1); } else { - qCWarning(graphics_scripting) << "GraphicsScriptingInterface::getMeshes ERROR" << result.toString(); + qDebug() << "replaceScriptableModelMeshPart -- !base" << model << base << -1 << -1; } - return QScriptValue::NullValue; + } else { + qDebug() << "replaceScriptableModelMeshPart -- !provider"; } - return scriptEngine->toScriptValue(meshes); + + return false; } -QString GraphicsScriptingInterface::meshToOBJ(const scriptable::ScriptableModel& _in) { +scriptable::ModelProviderPointer GraphicsScriptingInterface::getModelProvider(QUuid uuid) { + QString error; + if (auto appProvider = DependencyManager::get()) { + if (auto provider = appProvider->lookupModelProvider(uuid)) { + return provider; + } else { + error = "provider unavailable for " + uuid.toString(); + } + } else { + error = "appProvider unavailable"; + } + if (context()) { + context()->throwError(error); + } else { + qCWarning(graphics_scripting) << "GraphicsScriptingInterface::getModelProvider ERROR" << error; + } + return nullptr; +} + +scriptable::ScriptableModelPointer GraphicsScriptingInterface::newModelObject(QVector meshes) { + auto modelWrapper = scriptable::make_scriptowned(); + modelWrapper->setObjectName("js::model"); + if (meshes.isEmpty()) { + if (context()) { + context()->throwError("expected [meshes] array as first argument"); + } + } else { + int i = 0; + for (const auto& mesh : meshes) { + if (mesh) { + modelWrapper->append(*mesh); + } else if (context()) { + context()->throwError(QString("invalid mesh at index: %1").arg(i)); + } + i++; + } + } + return modelWrapper; +} + +scriptable::ScriptableModelPointer GraphicsScriptingInterface::getModelObject(QUuid uuid) { + QString error, providerType = "unknown"; + if (auto provider = getModelProvider(uuid)) { + providerType = SpatiallyNestable::nestableTypeToString(provider->modelProviderType); + auto modelObject = provider->getScriptableModel(); + if (modelObject.objectID == uuid) { + if (modelObject.meshes.size()) { + auto modelWrapper = scriptable::make_scriptowned(modelObject); + modelWrapper->setObjectName(providerType+"::"+uuid.toString()+"::model"); + return modelWrapper; + } else { + error = "no meshes available: " + modelObject.objectID.toString(); + } + } else { + error = "objectID mismatch: " + modelObject.objectID.toString(); + } + } else { + error = "provider unavailable"; + } + auto errorMessage = QString("failed to get meshes from %1 provider for uuid %2 (%3)").arg(providerType).arg(uuid.toString()).arg(error); + qCWarning(graphics_scripting) << "GraphicsScriptingInterface::getModelObject ERROR" << errorMessage; + if (context()) { + context()->throwError(errorMessage); + } + return nullptr; +} + +#ifdef SCRIPTABLE_MESH_TODO +bool GraphicsScriptingInterface::updateMeshPart(scriptable::ScriptableMeshPointer mesh, scriptable::ScriptableMeshPartPointer part) { + Q_ASSERT(mesh); + Q_ASSERT(part); + Q_ASSERT(part->parentMesh); + auto tmp = exportMeshPart(mesh, part->partIndex); + if (part->parentMesh == mesh) { +#ifdef SCRIPTABLE_MESH_DEBUG + qCInfo(graphics_scripting) << "updateMeshPart -- update via clone" << mesh << part; +#endif + tmp->replaceMeshData(part->cloneMeshPart()); + return false; + } else { +#ifdef SCRIPTABLE_MESH_DEBUG + qCInfo(graphics_scripting) << "updateMeshPart -- update via inplace" << mesh << part; +#endif + tmp->replaceMeshData(part); + return true; + } +} +#endif + +QString GraphicsScriptingInterface::exportModelToOBJ(const scriptable::ScriptableModel& _in) { const auto& in = _in.getConstMeshes(); if (in.size()) { QList meshes; @@ -152,3 +184,5 @@ MeshPointer GraphicsScriptingInterface::getMeshPointer(scriptable::ScriptableMes } return mesh; } + +#include "GraphicsScriptingInterface.moc" diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h index ab2e5467db..9866b5585c 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h @@ -29,16 +29,24 @@ public: public slots: /**jsdoc - * Returns the meshes associated with a UUID (entityID, overlayID, or avatarID) + * Returns the model/meshes associated with a UUID (entityID, overlayID, or avatarID) * - * @function GraphicsScriptingInterface.getMeshes - * @param {EntityID} entityID The ID of the entity whose meshes are to be retrieve + * @function GraphicsScriptingInterface.getModel + * @param {UUID} The objectID of the model whose meshes are to be retrieve */ - 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); + scriptable::ModelProviderPointer getModelProvider(QUuid uuid); + scriptable::ScriptableModelPointer getModelObject(QUuid uuid); + bool updateModelObject(QUuid uuid, const scriptable::ScriptableModelPointer model); + scriptable::ScriptableModelPointer newModelObject(QVector meshes); - QString meshToOBJ(const scriptable::ScriptableModel& in); +#ifdef SCRIPTABLE_MESH_TODO + scriptable::ScriptableMeshPartPointer exportMeshPart(scriptable::ScriptableMeshPointer mesh, int part=0) { + return scriptable::make_scriptowned(mesh, part); + } + bool updateMeshPart(scriptable::ScriptableMeshPointer mesh, scriptable::ScriptableMeshPartPointer part); +#endif + + QString exportModelToOBJ(const scriptable::ScriptableModel& in); private: scriptable::MeshPointer getMeshPointer(scriptable::ScriptableMeshPointer meshProxy); diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h index 594e09bb32..9b86ddee82 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h @@ -43,23 +43,13 @@ namespace scriptable { return toDebugString(qobject_cast(tmp.get())); } - // C++ > QtOwned instance - template - std::shared_ptr make_qtowned(Rest... rest) { - T* tmp = new T(rest...); - if (tmp) { - tmp->metadata["__ownership__"] = QScriptEngine::QtOwnership; - } - return std::shared_ptr(tmp); - } - - // C++ > ScriptOwned JS instance + // Helper for creating C++ > ScriptOwned JS instances + // (NOTE: this also helps track in the code where we need to update later if switching to + // std::shared_ptr's -- something currently non-trivial given mixed JS/C++ object ownership) template QPointer make_scriptowned(Rest... rest) { - T* tmp = new T(rest...); - if (tmp) { - tmp->metadata["__ownership__"] = QScriptEngine::ScriptOwnership; - } - return QPointer(tmp); + auto instance = QPointer(new T(rest...)); + Q_ASSERT(instance && instance->parent()); + return instance; } } diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp index 643debf475..52cd225fba 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp @@ -5,6 +5,8 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include "Forward.h" + #include "ScriptableMesh.h" #include "BufferViewScripting.h" @@ -19,7 +21,7 @@ #include #include -#include "ScriptableMesh.moc" +// #define SCRIPTABLE_MESH_DEBUG 1 scriptable::ScriptableMeshPart::ScriptableMeshPart(scriptable::ScriptableMeshPointer parentMesh, int partIndex) : QObject(), parentMesh(parentMesh), partIndex(partIndex) { @@ -77,7 +79,9 @@ QVector scriptable::ScriptableMesh::findNearbyIndices(const glm::vec3& QVector scriptable::ScriptableMesh::getIndices() const { QVector result; if (auto mesh = getMeshPointer()) { +#ifdef SCRIPTABLE_MESH_DEBUG qCDebug(graphics_scripting, "getTriangleIndices mesh %p", mesh.get()); +#endif gpu::BufferView indexBufferView = mesh->getIndexBuffer(); if (quint32 count = (quint32)indexBufferView.getNumElements()) { result.resize(count); @@ -121,20 +125,16 @@ QVector scriptable::ScriptableMesh::getAttributeNames() const { return result; } -// override QVariantMap scriptable::ScriptableMesh::getVertexAttributes(quint32 vertexIndex) const { return getVertexAttributes(vertexIndex, getAttributeNames()); } bool scriptable::ScriptableMesh::setVertexAttributes(quint32 vertexIndex, QVariantMap attributes) { - //qCInfo(graphics_scripting) << "setVertexAttributes" << vertexIndex << attributes; - metadata["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate); for (auto& a : buffer_helpers::gatherBufferViews(getMeshPointer())) { const auto& name = a.first; const auto& value = attributes.value(name); if (value.isValid()) { auto& view = a.second; - //qCDebug(graphics_scripting) << "setVertexAttributes" << vertexIndex << name; buffer_helpers::fromVariant(view, vertexIndex, value); } else { //qCDebug(graphics_scripting) << "(skipping) setVertexAttributes" << vertexIndex << name; @@ -269,7 +269,6 @@ quint32 scriptable::ScriptableMesh::mapAttributeValues(QScriptValue _callback) { #endif auto obj = js->newObject(); auto attributeViews = buffer_helpers::gatherBufferViews(mesh, { "normal", "color" }); - metadata["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate); uint32_t i = 0; for (; i < nPositions; i++) { for (const auto& a : attributeViews) { @@ -303,13 +302,13 @@ quint32 scriptable::ScriptableMesh::mapAttributeValues(QScriptValue _callback) { } quint32 scriptable::ScriptableMeshPart::mapAttributeValues(QScriptValue callback) { - return parentMesh ? parentMesh->mapAttributeValues(callback) : 0; + return parentMesh ? parentMesh->mapAttributeValues(callback) : 0; } bool scriptable::ScriptableMeshPart::unrollVertices(bool recalcNormals) { auto mesh = getMeshPointer(); #ifdef SCRIPTABLE_MESH_DEBUG - qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices" << !!mesh<< !!meshProxy; + qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices" << !!mesh<< !!parentMesh; #endif if (!mesh) { return false; @@ -321,8 +320,9 @@ bool scriptable::ScriptableMeshPart::unrollVertices(bool recalcNormals) { auto buffer = new gpu::Buffer(); buffer->resize(numPoints * sizeof(uint32_t)); auto newindices = gpu::BufferView(buffer, { gpu::SCALAR, gpu::UINT32, gpu::INDEX }); - metadata["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate); +#ifdef SCRIPTABLE_MESH_DEBUG qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices numPoints" << numPoints; +#endif auto attributeViews = buffer_helpers::gatherBufferViews(mesh); for (const auto& a : attributeViews) { auto& view = a.second; @@ -330,15 +330,17 @@ bool scriptable::ScriptableMeshPart::unrollVertices(bool recalcNormals) { auto buffer = new gpu::Buffer(); buffer->resize(numPoints * sz); auto points = gpu::BufferView(buffer, view._element); - auto src = (uint8_t*)view._buffer->getData(); - auto dest = (uint8_t*)points._buffer->getData(); auto slot = buffer_helpers::ATTRIBUTES[a.first]; +#ifdef SCRIPTABLE_MESH_DEBUG if (0) { + auto src = (uint8_t*)view._buffer->getData(); + auto dest = (uint8_t*)points._buffer->getData(); qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices buffer" << a.first; qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices source" << view.getNumElements(); qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices dest" << points.getNumElements(); qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices sz" << sz << src << dest << slot; } +#endif auto esize = indices._element.getSize(); const char* hint= a.first.toStdString().c_str(); for(quint32 i = 0; i < numPoints; i++) { @@ -381,8 +383,6 @@ bool scriptable::ScriptableMeshPart::replaceMeshData(scriptable::ScriptableMeshP "target:" << QString::fromStdString(target->displayName) << "attributes:" << attributes; - metadata["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate); - // remove attributes only found on target mesh, unless user has explicitly specified the relevant attribute names if (attributeNames.isEmpty()) { auto attributeViews = buffer_helpers::gatherBufferViews(target); @@ -438,7 +438,6 @@ bool scriptable::ScriptableMeshPart::dedupeVertices(float epsilon) { uniqueVerts.reserve((int)numPositions); QMap remapIndices; - metadata["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate); for (quint32 i = 0; i < numPositions; i++) { const quint32 numUnique = uniqueVerts.size(); const auto& position = positions.get(i); @@ -508,63 +507,51 @@ scriptable::ScriptableMeshPointer scriptable::ScriptableMesh::cloneMesh(bool rec return nullptr; } auto clone = buffer_helpers::cloneMesh(mesh); - + if (recalcNormals) { buffer_helpers::recalculateNormals(clone); } - auto meshPointer = scriptable::make_scriptowned(provider, model, clone, metadata); - clone.reset(); // free local reference - // qCInfo(graphics_scripting) << "========= ScriptableMesh::cloneMesh..." << meshPointer << meshPointer->ownedMesh.use_count(); - //scriptable::MeshPointer* ppMesh = new scriptable::MeshPointer(); - //*ppMesh = clone; - - if (0 && meshPointer) { - scriptable::WeakMeshPointer delme = meshPointer->mesh; - QString debugString = scriptable::toDebugString(meshPointer); - QObject::connect(meshPointer, &QObject::destroyed, meshPointer, [=]() { - // qCWarning(graphics_scripting) << "*************** cloneMesh/Destroy"; - // qCWarning(graphics_scripting) << "*************** " << debugString << delme.lock().get(); - if (!delme.expired()) { - QTimer::singleShot(250, this, [=]{ - if (!delme.expired()) { - qCWarning(graphics_scripting) << "cloneMesh -- potential memory leak..." << debugString << delme.use_count(); - } - }); - } - }); - } - - meshPointer->metadata["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate); + auto meshPointer = scriptable::make_scriptowned(provider, model, clone, nullptr); return scriptable::ScriptableMeshPointer(meshPointer); } -scriptable::ScriptableMeshBase::ScriptableMeshBase(scriptable::WeakModelProviderPointer provider, scriptable::ScriptableModelBasePointer model, scriptable::WeakMeshPointer mesh, const QVariantMap& metadata) - : provider(provider), model(model), mesh(mesh), metadata(metadata) {} -scriptable::ScriptableMeshBase::ScriptableMeshBase(scriptable::WeakMeshPointer mesh) : scriptable::ScriptableMeshBase(scriptable::WeakModelProviderPointer(), nullptr, mesh, QVariantMap()) { } -scriptable::ScriptableMeshBase::ScriptableMeshBase(scriptable::MeshPointer mesh, const QVariantMap& metadata) - : ScriptableMeshBase(WeakModelProviderPointer(), nullptr, mesh, metadata) { - ownedMesh = mesh; + + +// note: we don't always want the JS side to prevent mesh data from being freed + +scriptable::ScriptableMeshBase::ScriptableMeshBase( + scriptable::WeakModelProviderPointer provider, scriptable::ScriptableModelBasePointer model, scriptable::WeakMeshPointer weakMesh, QObject* parent + ) : QObject(parent), provider(provider), model(model), weakMesh(weakMesh) { + if (parent) { + qCDebug(graphics_scripting) << "ScriptableMeshBase -- have parent QObject, creating strong neshref" << weakMesh.lock().get() << parent; + strongMesh = weakMesh.lock(); + } } + +scriptable::ScriptableMeshBase::ScriptableMeshBase(scriptable::WeakMeshPointer weakMesh, QObject* parent) : + scriptable::ScriptableMeshBase(scriptable::WeakModelProviderPointer(), nullptr, weakMesh, parent) { +} + scriptable::ScriptableMeshBase& scriptable::ScriptableMeshBase::operator=(const scriptable::ScriptableMeshBase& view) { provider = view.provider; model = view.model; - mesh = view.mesh; - ownedMesh = view.ownedMesh; - metadata = view.metadata; + weakMesh = view.weakMesh; + strongMesh = view.strongMesh; return *this; } - scriptable::ScriptableMeshBase::~ScriptableMeshBase() { - ownedMesh.reset(); + +scriptable::ScriptableMeshBase::~ScriptableMeshBase() { #ifdef SCRIPTABLE_MESH_DEBUG - qCInfo(graphics_scripting) << "//~ScriptableMeshBase" << this << "ownedMesh:" << ownedMesh.use_count() << "mesh:" << mesh.use_count(); + qCInfo(graphics_scripting) << "//~ScriptableMeshBase" << this << "strongMesh:" << strongMesh.use_count() << "weakMesh:" << weakMesh.use_count(); #endif + strongMesh.reset(); } scriptable::ScriptableMesh::~ScriptableMesh() { - ownedMesh.reset(); #ifdef SCRIPTABLE_MESH_DEBUG - qCInfo(graphics_scripting) << "//~ScriptableMesh" << this << "ownedMesh:" << ownedMesh.use_count() << "mesh:" << mesh.use_count(); + qCInfo(graphics_scripting) << "//~ScriptableMesh" << this << "strongMesh:" << strongMesh.use_count() << "weakMesh:" << weakMesh.use_count(); #endif + strongMesh.reset(); } QString scriptable::ScriptableMeshPart::toOBJ() { @@ -573,7 +560,7 @@ QString scriptable::ScriptableMeshPart::toOBJ() { context()->throwError(QString("null mesh")); } else { qCWarning(graphics_scripting) << "null mesh"; - } + } return QString(); } return writeOBJToString({ getMeshPointer() }); @@ -585,12 +572,7 @@ namespace { if (!object) { return QScriptValue::NullValue; } - auto ownership = object->metadata.value("__ownership__"); - return engine->newQObject( - object, - ownership.isValid() ? static_cast(ownership.toInt()) : QScriptEngine::QtOwnership - //, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects - ); + return engine->newQObject(object, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater); } QScriptValue meshPointerToScriptValue(QScriptEngine* engine, const scriptable::ScriptableMeshPointer& in) { @@ -637,7 +619,7 @@ namespace scriptable { qScriptRegisterSequenceMetaType>(engine); qScriptRegisterSequenceMetaType>(engine); qScriptRegisterSequenceMetaType>(engine); - + qScriptRegisterMetaType(engine, qVectorUInt32ToScriptValue, qVectorUInt32FromScriptValue); qScriptRegisterMetaType(engine, modelPointerToScriptValue, modelPointerFromScriptValue); qScriptRegisterMetaType(engine, meshPointerToScriptValue, meshPointerFromScriptValue); @@ -645,50 +627,41 @@ namespace scriptable { return metaTypeIds.size(); } + // callback helper that lets C++ method signatures remain simple (ie: taking a single callback argument) while // still supporting extended Qt signal-like (scope, "methodName") and (scope, function(){}) "this" binding conventions - QScriptValue jsBindCallback(QScriptValue callback) { - if (callback.isObject() && callback.property("callback").isFunction()) { - return callback; + QScriptValue jsBindCallback(QScriptValue value) { + if (value.isObject() && value.property("callback").isFunction()) { + // value is already a bound callback + return value; } - auto engine = callback.engine(); + auto engine = value.engine(); auto context = engine ? engine->currentContext() : nullptr; auto length = context ? context->argumentCount() : 0; QScriptValue scope = context ? context->thisObject() : QScriptValue::NullValue; QScriptValue method; +#ifdef SCRIPTABLE_MESH_DEBUG qCInfo(graphics_scripting) << "jsBindCallback" << engine << length << scope.toQObject() << method.toString(); - int i = 0; - for (; context && i < length; i++) { - if (context->argument(i).strictlyEquals(callback)) { +#endif + + // find position in the incoming JS Function.arguments array (so we can test for the two-argument case) + for (int i = 0; context && i < length; i++) { + if (context->argument(i).strictlyEquals(value)) { method = context->argument(i+1); } } if (method.isFunction() || method.isString()) { - scope = callback; + // interpret as `API.func(..., scope, function callback(){})` or `API.func(..., scope, "methodName")` + scope = value; } else { - method = callback; + // interpret as `API.func(..., function callback(){})` + method = value; } +#ifdef SCRIPTABLE_MESH_DEBUG qCInfo(graphics_scripting) << "scope:" << scope.toQObject() << "method:" << method.toString(); +#endif return ::makeScopedHandlerObject(scope, method); } } -bool scriptable::GraphicsScriptingInterface::updateMeshPart(ScriptableMeshPointer mesh, ScriptableMeshPartPointer part) { - Q_ASSERT(mesh); - Q_ASSERT(part); - Q_ASSERT(part->parentMesh); - auto tmp = exportMeshPart(mesh, part->partIndex); - if (part->parentMesh == mesh) { -#ifdef SCRIPTABLE_MESH_DEBUG - qCInfo(graphics_scripting) << "updateMeshPart -- update via clone" << mesh << part; -#endif - tmp->replaceMeshData(part->cloneMeshPart()); - return false; - } else { -#ifdef SCRIPTABLE_MESH_DEBUG - qCInfo(graphics_scripting) << "updateMeshPart -- update via inplace" << mesh << part; -#endif - tmp->replaceMeshData(part); - return true; - } -} +#include "ScriptableMesh.moc" diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h index 459613135a..f070fdf21e 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h @@ -1,3 +1,10 @@ +// +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + #pragma once #include "ScriptableModel.h" @@ -26,25 +33,26 @@ namespace scriptable { Q_PROPERTY(uint32 numAttributes READ getNumAttributes) Q_PROPERTY(uint32 numVertices READ getNumVertices) Q_PROPERTY(uint32 numIndices READ getNumIndices) - Q_PROPERTY(QVariantMap metadata MEMBER metadata) Q_PROPERTY(QVector attributeNames READ getAttributeNames) Q_PROPERTY(QVector parts READ getMeshParts) Q_PROPERTY(bool valid READ hasValidMesh) - bool hasValidMesh() const { return (bool)getMeshPointer(); } - Q_PROPERTY(bool validOwned READ hasValidOwnedMesh) - bool hasValidOwnedMesh() const { return (bool)getOwnedMeshPointer(); } + Q_PROPERTY(bool strong READ hasValidStrongMesh) operator const ScriptableMeshBase*() const { return (qobject_cast(this)); } - ScriptableMesh(scriptable::MeshPointer mesh) : ScriptableMeshBase(mesh), QScriptable() { ownedMesh = mesh; } - ScriptableMesh(WeakModelProviderPointer provider, ScriptableModelBasePointer model, MeshPointer mesh, const QVariantMap& metadata) - : ScriptableMeshBase(provider, model, mesh, metadata), QScriptable() { ownedMesh = mesh; } + + ScriptableMesh(WeakModelProviderPointer provider, ScriptableModelBasePointer model, MeshPointer mesh, QObject* parent) + : ScriptableMeshBase(provider, model, mesh, parent), QScriptable() { strongMesh = mesh; } + ScriptableMesh(MeshPointer mesh, QObject* parent) + : ScriptableMeshBase(WeakModelProviderPointer(), nullptr, mesh, parent), QScriptable() { strongMesh = mesh; } ScriptableMesh(const ScriptableMeshBase& other); ScriptableMesh(const ScriptableMesh& other) : ScriptableMeshBase(other), QScriptable() {}; virtual ~ScriptableMesh(); Q_INVOKABLE const scriptable::ScriptableModelPointer getParentModel() const { return qobject_cast(model); } - Q_INVOKABLE const scriptable::MeshPointer getOwnedMeshPointer() const { return ownedMesh; } + Q_INVOKABLE const scriptable::MeshPointer getOwnedMeshPointer() const { return strongMesh; } scriptable::ScriptableMeshPointer getSelf() const { return const_cast(this); } + bool hasValidMesh() const { return !weakMesh.expired(); } + bool hasValidStrongMesh() const { return (bool)strongMesh; } public slots: uint32 getNumParts() const; uint32 getNumVertices() const; @@ -65,9 +73,9 @@ namespace scriptable { int _getSlotNumber(const QString& attributeName) const; - scriptable::ScriptableMeshPointer cloneMesh(bool recalcNormals = false); + scriptable::ScriptableMeshPointer cloneMesh(bool recalcNormals = false); public: - operator bool() const { return !mesh.expired(); } + operator bool() const { return !weakMesh.expired(); } public slots: // QScriptEngine-specific wrappers @@ -88,11 +96,9 @@ namespace scriptable { Q_PROPERTY(uint32 numIndices READ getNumIndices) Q_PROPERTY(QVector attributeNames READ getAttributeNames) - Q_PROPERTY(QVariantMap metadata MEMBER metadata) - ScriptableMeshPart(scriptable::ScriptableMeshPointer parentMesh, int partIndex); ScriptableMeshPart& operator=(const ScriptableMeshPart& view) { parentMesh=view.parentMesh; return *this; }; - ScriptableMeshPart(const ScriptableMeshPart& other) : QObject(), QScriptable(), parentMesh(other.parentMesh), partIndex(other.partIndex) {} + ScriptableMeshPart(const ScriptableMeshPart& other) : QObject(other.parent()), QScriptable(), parentMesh(other.parentMesh), partIndex(other.partIndex) {} public slots: scriptable::ScriptableMeshPointer getParentMesh() const { return parentMesh; } @@ -134,23 +140,10 @@ namespace scriptable { public: scriptable::ScriptableMeshPointer parentMesh; uint32 partIndex; - QVariantMap metadata; protected: int _elementsPerFace{ 3 }; QString _topology{ "triangles" }; - scriptable::MeshPointer getMeshPointer() const { return parentMesh ? parentMesh->getMeshPointer() : nullptr; } - }; - - class GraphicsScriptingInterface : public QObject, QScriptable { - Q_OBJECT - public: - GraphicsScriptingInterface(QObject* parent = nullptr) : QObject(parent), QScriptable() {} - GraphicsScriptingInterface(const GraphicsScriptingInterface& other) : QObject(), QScriptable() {} - public slots: - ScriptableMeshPartPointer exportMeshPart(ScriptableMeshPointer mesh, int part=0) { - return ScriptableMeshPartPointer(new ScriptableMeshPart(mesh, part)); - } - bool updateMeshPart(ScriptableMeshPointer mesh, ScriptableMeshPartPointer part); + scriptable::MeshPointer getMeshPointer() const { return parentMesh ? parentMesh->getMeshPointer() : nullptr; } }; // callback helper that lets C++ method signatures remain simple (ie: taking a single callback argument) while @@ -165,26 +158,5 @@ Q_DECLARE_METATYPE(scriptable::ScriptableMeshPointer) Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(scriptable::ScriptableMeshPartPointer) Q_DECLARE_METATYPE(QVector) -Q_DECLARE_METATYPE(scriptable::GraphicsScriptingInterface) - -// FIXME: MESHFACES: faces were supported in the original Model.* API -- are they still needed/used/useful for anything yet? -#include - -namespace mesh { - class MeshFace; - using MeshFaces = QVector; - class MeshFace { - public: - MeshFace() {} - MeshFace(QVector vertexIndices) : vertexIndices(vertexIndices) {} - ~MeshFace() {} - - QVector vertexIndices; - // TODO -- material... - }; -}; - -Q_DECLARE_METATYPE(mesh::MeshFace) -Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(scriptable::uint32) Q_DECLARE_METATYPE(QVector) diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp index 229d56adab..306f1b18af 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp @@ -13,25 +13,13 @@ #include "ScriptableMesh.h" #include -//#include "ScriptableModel.moc" -void scriptable::ScriptableModelBase::mixin(const QVariantMap& modelMetaData) { - for (const auto& key : modelMetaData.keys()) { - const auto& value = modelMetaData[key]; - if (metadata.contains(key) && metadata[key].type() == (QVariant::Type)QMetaType::QVariantList) { - qCDebug(graphics_scripting) << "CONCATENATING" << key << metadata[key].toList().size() << "+" << value.toList().size(); - metadata[key] = metadata[key].toList() + value.toList(); - } else { - metadata[key] = modelMetaData[key]; - } - } -} +// #define SCRIPTABLE_MESH_DEBUG 1 scriptable::ScriptableModelBase& scriptable::ScriptableModelBase::operator=(const scriptable::ScriptableModelBase& other) { provider = other.provider; objectID = other.objectID; - metadata = other.metadata; - for (auto& mesh : other.meshes) { + for (const auto& mesh : other.meshes) { append(mesh); } return *this; @@ -43,36 +31,27 @@ scriptable::ScriptableModelBase::~ScriptableModelBase() { #endif // makes cleanup order more deterministic to help with debugging for (auto& m : meshes) { - m.ownedMesh.reset(); + m.strongMesh.reset(); } meshes.clear(); } -void scriptable::ScriptableModelBase::append(scriptable::WeakMeshPointer mesh, const QVariantMap& metadata) { - meshes << ScriptableMeshBase{ provider, this, mesh, metadata }; +void scriptable::ScriptableModelBase::append(scriptable::WeakMeshPointer mesh) { + meshes << ScriptableMeshBase{ provider, this, mesh, this /*parent*/ }; } -void scriptable::ScriptableModelBase::append(const ScriptableMeshBase& mesh, const QVariantMap& modelMetaData) { +void scriptable::ScriptableModelBase::append(const ScriptableMeshBase& mesh) { if (mesh.provider.lock().get() != provider.lock().get()) { qCDebug(graphics_scripting) << "warning: appending mesh from different provider..." << mesh.provider.lock().get() << " != " << provider.lock().get(); } meshes << mesh; - mixin(modelMetaData); } -void scriptable::ScriptableModelBase::append(const ScriptableModelBase& other, const QVariantMap& modelMetaData) { - for (const auto& mesh : other.meshes) { - append(mesh); - } - mixin(other.metadata); - mixin(modelMetaData); -} - - QString scriptable::ScriptableModel::toString() const { - return QString("[ScriptableModel%1%2]") + return QString("[ScriptableModel%1%2 numMeshes=%3]") .arg(objectID.isNull() ? "" : " objectID="+objectID.toString()) - .arg(objectName().isEmpty() ? "" : " name=" +objectName()); + .arg(objectName().isEmpty() ? "" : " name=" +objectName()) + .arg(meshes.size()); } scriptable::ScriptableModelPointer scriptable::ScriptableModel::cloneModel(const QVariantMap& options) { @@ -131,3 +110,5 @@ quint32 scriptable::ScriptableModel::mapAttributeValues(QScriptValue callback) { } return result; } + +#include "ScriptableModel.moc" diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h index a78c9a4ef5..4ed1cc9554 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h @@ -1,15 +1,22 @@ +// +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + #pragma once #include "Forward.h" #include "GraphicsScriptingUtil.h" class QScriptValue; + namespace scriptable { class ScriptableModel : public ScriptableModelBase { Q_OBJECT public: Q_PROPERTY(QUuid objectID MEMBER objectID CONSTANT) - Q_PROPERTY(QVariantMap metadata MEMBER metadata CONSTANT) Q_PROPERTY(uint32 numMeshes READ getNumMeshes) Q_PROPERTY(QVector meshes READ getMeshes) @@ -27,11 +34,9 @@ namespace scriptable { QVector getMeshes(); const QVector getConstMeshes() const; operator scriptable::ScriptableModelBasePointer() { - QPointer p; - p = qobject_cast(this); - return p; + return QPointer(qobject_cast(this)); } - + // QScriptEngine-specific wrappers Q_INVOKABLE uint32 mapAttributeValues(QScriptValue callback); diff --git a/libraries/graphics/src/graphics/BufferViewHelpers.cpp b/libraries/graphics/src/graphics/BufferViewHelpers.cpp index ddf3d84c89..29dcbd58e3 100644 --- a/libraries/graphics/src/graphics/BufferViewHelpers.cpp +++ b/libraries/graphics/src/graphics/BufferViewHelpers.cpp @@ -28,10 +28,6 @@ namespace glm { using hvec4 = glm::tvec4; } -#ifdef DEBUG_BUFFERVIEW_SCRIPTING -#include "../../graphics-scripting/src/graphics-scripting/DebugNames.h" -#endif - namespace { QLoggingCategory bufferhelper_logging{ "hifi.bufferview" }; } @@ -109,9 +105,7 @@ bool buffer_helpers::fromVariant(const gpu::BufferView& view, quint32 index, con const auto dataType = element.getType(); const auto byteLength = element.getSize(); const auto BYTES_PER_ELEMENT = byteLength / vecN; -#ifdef DEBUG_BUFFERVIEW_SCRIPTING - qCDebug(bufferhelper_logging) << "bufferViewElementFromVariant" << index << DebugNames::stringFrom(dataType) << BYTES_PER_ELEMENT << vecN; -#endif + if (BYTES_PER_ELEMENT == 1) { switch(vecN) { case 2: setBufferViewElement(view, index, v); return true; @@ -176,18 +170,12 @@ QVariant buffer_helpers::toVariant(const gpu::BufferView& view, quint32 index, b auto byteOffset = index * vecN * BYTES_PER_ELEMENT; auto maxByteOffset = (view._size - 1) * vecN * BYTES_PER_ELEMENT; if (byteOffset > maxByteOffset) { -#ifdef DEBUG_BUFFERVIEW_SCRIPTING - qDebug() << "toVariant -- " << DebugNames::stringFrom(dataType) -#endif qDebug() << "toVariant -- byteOffset out of range " << byteOffset << " < " << maxByteOffset; qDebug() << "toVariant -- index: " << index << "numElements" << view.getNumElements(); qDebug() << "toVariant -- vecN: " << vecN << "byteLength" << byteLength << "BYTES_PER_ELEMENT" << BYTES_PER_ELEMENT; } Q_ASSERT(byteOffset <= maxByteOffset); } -#ifdef DEBUG_BUFFERVIEW_SCRIPTING - qCDebug(bufferhelper_logging) << "toVariant -- " << index << DebugNames::stringFrom(dataType) << BYTES_PER_ELEMENT << vecN; -#endif if (BYTES_PER_ELEMENT == 1) { switch(vecN) { case 2: return getBufferViewElement(view, index, asArray); @@ -305,13 +293,8 @@ template struct GpuScalarToGlm; struct GpuToGlmAdapter { static float error(const QString& name, const gpu::BufferView& view, quint32 index, const char *hint) { - QString debugName; -#ifdef DEBUG_BUFFERVIEW_SCRIPTING - debugName = DebugNames::stringFrom(view._element.getType()) -#endif - qDebug() << QString("GpuToGlmAdapter:: unhandled type=%1(element=%2(%3)) size=%4(per=%5) vec%6 hint=%7 #%8") + qDebug() << QString("GpuToGlmAdapter:: unhandled type=%1(element=%2) size=%3(per=%4) vec%5 hint=%6 #%7") .arg(name) - .arg(debugName) .arg(view._element.getType()) .arg(view._element.getSize()) .arg(view._element.getSize() / view._element.getScalarCount()) @@ -404,7 +387,7 @@ struct getVec { } return result; } - static T __to_scalar__(const gpu::BufferView& view, quint32 index, const char *hint) { + static T __to_value__(const gpu::BufferView& view, quint32 index, const char *hint) { assert(boundsCheck(view, index)); return FUNC::get(view, index, hint); } @@ -425,18 +408,18 @@ template <> QVector buffer_helpers::toVector(const gpu::Bu } -// indexed conversion accessors (similar to "view.convert(i)" existed) +// indexed conversion accessors (like the hypothetical "view.convert(i)") template <> int buffer_helpers::convert(const gpu::BufferView& view, quint32 index, const char *hint) { - return getVec,int>::__to_scalar__(view, index, hint); + return getVec,int>::__to_value__(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); + return getVec,glm::vec2>::__to_value__(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); + return getVec,glm::vec3>::__to_value__(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); + return getVec,glm::vec4>::__to_value__(view, index, hint); } gpu::BufferView buffer_helpers::clone(const gpu::BufferView& input) { @@ -492,9 +475,6 @@ namespace { vsize > bufferView._size ); QString hint = QString("%1").arg(slot); -#ifdef DEBUG_BUFFERVIEW_SCRIPTING - hint = DebugNames::stringFrom(slot); -#endif #ifdef DEV_BUILD auto beforeCount = bufferView.getNumElements(); auto beforeTotal = bufferView._size; @@ -519,9 +499,6 @@ namespace { auto afterTotal = bufferView._size; if (beforeTotal != afterTotal || beforeCount != afterCount) { QString typeName = QString("%1").arg(bufferView._element.getType()); -#ifdef DEBUG_BUFFERVIEW_SCRIPTING - 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); @@ -557,9 +534,6 @@ std::map buffer_helpers::gatherBufferViews(graphics::M if (beforeCount > 0) { auto element = view._element; QString typeName = QString("%1").arg(element.getType()); -#ifdef DEBUG_BUFFERVIEW_SCRIPTING - typeName = DebugNames::stringFrom(element.getType()); -#endif attributeViews[name] = getBufferView(mesh, slot); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 783659a6a1..a083b027f3 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -492,7 +492,6 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g { "v2", vec3toVariant(bestModelTriangle.v2) }, }; } - } } @@ -591,38 +590,41 @@ MeshProxyList Model::getMeshes() const { // FIXME: temporary workaround that updates the whole FBXGeometry (to keep findRayIntersection in sync) #include "Model_temporary_hack.cpp.h" -bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer _newModel, int meshIndex, int partIndex) { +bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer newModel, int meshIndex, int partIndex) { QMutexLocker lock(&_mutex); if (!isLoaded()) { + qDebug() << "!isLoaded" << this; return false; } - { - // 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 - ); - _visualGeometryRequestFailed = false; - deleteGeometry(); - _renderGeometry.reset(newRenderGeometry); - _rig.destroyAnimGraph(); - updateGeometry(); - calculateTriangleSets(); - _needsReload = false; - _needsFixupInScene = true; - setRenderItemsNeedUpdate(); + if (!newModel || !newModel->meshes.size()) { + qDebug() << "!newModel.meshes.size()" << this; + return false; } + + auto resource = new MyGeometryResource(_url, _renderGeometry, newModel); + _needsReload = false; + _needsUpdateTextures = false; + _visualGeometryRequestFailed = false; + _needsFixupInScene = true; + + invalidCalculatedMeshBoxes(); + deleteGeometry(); + _renderGeometry.reset(resource); + updateGeometry(); + calculateTriangleSets(); + setRenderItemsNeedUpdate(); return true; } -scriptable::ScriptableModelBase Model::getScriptableModel(bool* ok) { +scriptable::ScriptableModelBase Model::getScriptableModel() { QMutexLocker lock(&_mutex); scriptable::ScriptableModelBase result; if (!isLoaded()) { qCDebug(renderutils) << "Model::getScriptableModel -- !isLoaded"; - return scriptable::ModelProvider::modelUnavailableError(ok); + return result; } const FBXGeometry& geometry = getFBXGeometry(); @@ -630,22 +632,9 @@ scriptable::ScriptableModelBase Model::getScriptableModel(bool* ok) { for (int i = 0; i < numberOfMeshes; i++) { const FBXMesh& fbxMesh = geometry.meshes.at(i); 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) }, - }); + result.append(mesh); } } - if (ok) { - *ok = true; - } return result; } diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 5cbdb2d300..603153d535 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -317,7 +317,7 @@ public: int getResourceDownloadAttempts() { return _renderWatcher.getResourceDownloadAttempts(); } int getResourceDownloadAttemptsRemaining() { return _renderWatcher.getResourceDownloadAttemptsRemaining(); } - virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override; + virtual scriptable::ScriptableModelBase getScriptableModel() override; virtual bool replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer model, int meshIndex, int partIndex) override; void scaleToFit(); diff --git a/libraries/render-utils/src/Model_temporary_hack.cpp.h b/libraries/render-utils/src/Model_temporary_hack.cpp.h index 9b0e0bcf9a..51318656e0 100644 --- a/libraries/render-utils/src/Model_temporary_hack.cpp.h +++ b/libraries/render-utils/src/Model_temporary_hack.cpp.h @@ -1,9 +1,9 @@ #include #include -class MyGeometryMappingResource : public GeometryResource { +class MyGeometryResource : public GeometryResource { public: shared_ptr fbxGeometry; - MyGeometryMappingResource(const QUrl& url, Geometry::Pointer originalGeometry, std::shared_ptr newModel) : GeometryResource(url) { + MyGeometryResource(const QUrl& url, Geometry::Pointer originalGeometry, scriptable::ScriptableModelBasePointer newModel) : GeometryResource(url) { fbxGeometry = std::make_shared(); FBXGeometry& geometry = *fbxGeometry.get(); const FBXGeometry* original; @@ -15,41 +15,12 @@ public: original = tmpGeometry.get(); } geometry.originalURL = original->originalURL; + geometry.bindExtents = original->bindExtents; - geometry.author = original->author; - geometry.applicationName = original->applicationName; for (const auto &j : original->joints) { geometry.joints << j; } - geometry.jointIndices = QHash{ original->jointIndices }; - - geometry.animationFrames = QVector{ original->animationFrames }; - geometry.meshIndicesToModelNames = QHash{ original->meshIndicesToModelNames }; - geometry.blendshapeChannelNames = QList{ original->blendshapeChannelNames }; - - geometry.hasSkeletonJoints = original->hasSkeletonJoints; - geometry.offset = original->offset; - geometry.leftEyeJointIndex = original->leftEyeJointIndex; - geometry.rightEyeJointIndex = original->rightEyeJointIndex; - geometry.neckJointIndex = original->neckJointIndex; - geometry.rootJointIndex = original->rootJointIndex; - geometry.leanJointIndex = original->leanJointIndex; - geometry.headJointIndex = original->headJointIndex; - geometry.leftHandJointIndex = original->leftHandJointIndex; - geometry.rightHandJointIndex = original->rightHandJointIndex; - geometry.leftToeJointIndex = original->leftToeJointIndex; - geometry.rightToeJointIndex = original->rightToeJointIndex; - geometry.leftEyeSize = original->leftEyeSize; - geometry.rightEyeSize = original->rightEyeSize; - geometry.humanIKJointIndices = original->humanIKJointIndices; - geometry.palmDirection = original->palmDirection; - geometry.neckPivot = original->neckPivot; - geometry.bindExtents = original->bindExtents; - - // Copy materials - QHash materialIDAtlas; for (const FBXMaterial& material : original->materials) { - materialIDAtlas[material.materialID] = _materials.size(); _materials.push_back(std::make_shared(material, _textureBaseUrl)); } std::shared_ptr meshes = std::make_shared(); @@ -58,6 +29,7 @@ public: if (newModel) { geometry.meshExtents.reset(); for (const auto& newMesh : newModel->meshes) { + // qDebug() << "newMesh #" << meshID; FBXMesh mesh; if (meshID < original->meshes.size()) { mesh = original->meshes.at(meshID); // copy @@ -73,7 +45,7 @@ public: mesh.createBlendShapeTangents(false); geometry.meshes << mesh; // Copy mesh pointers - meshes->emplace_back(newMesh.getMeshPointer());//buffer_helpers::cloneMesh(ptr)); + meshes->emplace_back(newMesh.getMeshPointer()); int partID = 0; const auto oldParts = mesh.parts; mesh.parts.clear(); @@ -83,7 +55,7 @@ public: // Construct local parts part.triangleIndices = buffer_helpers::toVector(mesh._mesh->getIndexBuffer(), "part.triangleIndices"); mesh.parts << part; - auto p = std::make_shared(meshID, partID, (int)materialIDAtlas[part.materialID]); + auto p = std::make_shared(meshID, partID, 0); parts->push_back(p); partID++; } @@ -94,20 +66,18 @@ public: glm::vec3 transformedVertex = glm::vec3(mesh.modelTransform * glm::vec4(vertex, 1.0f)); geometry.meshExtents.minimum = glm::min(geometry.meshExtents.minimum, transformedVertex); geometry.meshExtents.maximum = glm::max(geometry.meshExtents.maximum, transformedVertex); - + mesh.meshExtents.minimum = glm::min(mesh.meshExtents.minimum, transformedVertex); mesh.meshExtents.maximum = glm::max(mesh.meshExtents.maximum, transformedVertex); } } - meshID++; } } _meshes = meshes; _meshParts = parts; - _animGraphOverrideUrl = originalGeometry ? originalGeometry->getAnimGraphOverrideUrl() : QUrl(); _loaded = true; _fbxGeometry = fbxGeometry; - }; + }; }; diff --git a/tools/jsdoc/package-lock.json b/tools/jsdoc/package-lock.json deleted file mode 100644 index 073bbf60f6..0000000000 --- a/tools/jsdoc/package-lock.json +++ /dev/null @@ -1,138 +0,0 @@ -{ - "name": "hifiJSDoc", - "requires": true, - "lockfileVersion": 1, - "dependencies": { - "babylon": { - "version": "7.0.0-beta.19", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.19.tgz", - "integrity": "sha512-Vg0C9s/REX6/WIXN37UKpv5ZhRi6A4pjHlpkE34+8/a6c2W1Q692n3hmc+SZG5lKRnaExLUbxtJ1SVT+KaCQ/A==" - }, - "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" - }, - "catharsis": { - "version": "0.8.9", - "resolved": "https://registry.npmjs.org/catharsis/-/catharsis-0.8.9.tgz", - "integrity": "sha1-mMyJDKZS3S7w5ws3klMQ/56Q/Is=", - "requires": { - "underscore-contrib": "0.3.0" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" - }, - "js2xmlparser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/js2xmlparser/-/js2xmlparser-3.0.0.tgz", - "integrity": "sha1-P7YOqgicVED5MZ9RdgzNB+JJlzM=", - "requires": { - "xmlcreate": "1.0.2" - } - }, - "jsdoc": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/jsdoc/-/jsdoc-3.5.5.tgz", - "integrity": "sha512-6PxB65TAU4WO0Wzyr/4/YhlGovXl0EVYfpKbpSroSj0qBxT4/xod/l40Opkm38dRHRdQgdeY836M0uVnJQG7kg==", - "requires": { - "babylon": "7.0.0-beta.19", - "bluebird": "3.5.1", - "catharsis": "0.8.9", - "escape-string-regexp": "1.0.5", - "js2xmlparser": "3.0.0", - "klaw": "2.0.0", - "marked": "0.3.12", - "mkdirp": "0.5.1", - "requizzle": "0.2.1", - "strip-json-comments": "2.0.1", - "taffydb": "2.6.2", - "underscore": "1.8.3" - } - }, - "klaw": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-2.0.0.tgz", - "integrity": "sha1-WcEo4Nxc5BAgEVEZTuucv4WGUPY=", - "requires": { - "graceful-fs": "4.1.11" - } - }, - "marked": { - "version": "0.3.12", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.12.tgz", - "integrity": "sha512-k4NaW+vS7ytQn6MgJn3fYpQt20/mOgYM5Ft9BYMfQJDz2QT6yEeS9XJ8k2Nw8JTeWK/znPPW2n3UJGzyYEiMoA==" - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "requires": { - "minimist": "0.0.8" - } - }, - "requizzle": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/requizzle/-/requizzle-0.2.1.tgz", - "integrity": "sha1-aUPDUwxNmn5G8c3dUcFY/GcM294=", - "requires": { - "underscore": "1.6.0" - }, - "dependencies": { - "underscore": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" - } - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" - }, - "taffydb": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/taffydb/-/taffydb-2.6.2.tgz", - "integrity": "sha1-fLy2S1oUG2ou/CxdLGe04VCyomg=" - }, - "underscore": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", - "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" - }, - "underscore-contrib": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/underscore-contrib/-/underscore-contrib-0.3.0.tgz", - "integrity": "sha1-ZltmwkeD+PorGMn4y7Dix9SMJsc=", - "requires": { - "underscore": "1.6.0" - }, - "dependencies": { - "underscore": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.6.0.tgz", - "integrity": "sha1-izixDKze9jM3uLJOT/htRa6lKag=" - } - } - }, - "xmlcreate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/xmlcreate/-/xmlcreate-1.0.2.tgz", - "integrity": "sha1-+mv3YqYKQT+z3Y9LA8WyaSONMI8=" - } - } -} From ebdb5b3c173f7fe4e14e849ae64174772caee489 Mon Sep 17 00:00:00 2001 From: humbletim Date: Fri, 23 Feb 2018 08:57:48 -0500 Subject: [PATCH 21/29] CR feedback; remove unrollVertices; update getFace; guard more debug logging --- .../src/graphics-scripting/ScriptableMesh.cpp | 81 ++++--------------- .../src/graphics-scripting/ScriptableMesh.h | 7 +- 2 files changed, 19 insertions(+), 69 deletions(-) diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp index 52cd225fba..0f689e3d2f 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp @@ -305,65 +305,6 @@ quint32 scriptable::ScriptableMeshPart::mapAttributeValues(QScriptValue callback return parentMesh ? parentMesh->mapAttributeValues(callback) : 0; } -bool scriptable::ScriptableMeshPart::unrollVertices(bool recalcNormals) { - auto mesh = getMeshPointer(); -#ifdef SCRIPTABLE_MESH_DEBUG - qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices" << !!mesh<< !!parentMesh; -#endif - if (!mesh) { - return false; - } - - auto positions = mesh->getVertexBuffer(); - auto indices = mesh->getIndexBuffer(); - quint32 numPoints = (quint32)indices.getNumElements(); - auto buffer = new gpu::Buffer(); - buffer->resize(numPoints * sizeof(uint32_t)); - auto newindices = gpu::BufferView(buffer, { gpu::SCALAR, gpu::UINT32, gpu::INDEX }); -#ifdef SCRIPTABLE_MESH_DEBUG - qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices numPoints" << numPoints; -#endif - auto attributeViews = buffer_helpers::gatherBufferViews(mesh); - for (const auto& a : attributeViews) { - auto& view = a.second; - auto sz = view._element.getSize(); - auto buffer = new gpu::Buffer(); - buffer->resize(numPoints * sz); - auto points = gpu::BufferView(buffer, view._element); - auto slot = buffer_helpers::ATTRIBUTES[a.first]; -#ifdef SCRIPTABLE_MESH_DEBUG - if (0) { - auto src = (uint8_t*)view._buffer->getData(); - auto dest = (uint8_t*)points._buffer->getData(); - qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices buffer" << a.first; - qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices source" << view.getNumElements(); - qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices dest" << points.getNumElements(); - qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices sz" << sz << src << dest << slot; - } -#endif - auto esize = indices._element.getSize(); - const char* hint= a.first.toStdString().c_str(); - for(quint32 i = 0; i < numPoints; i++) { - quint32 index = esize == 4 ? indices.get(i) : indices.get(i); - newindices.edit(i) = i; - buffer_helpers::fromVariant( - points, i, - buffer_helpers::toVariant(view, index, false, hint) - ); - } - if (slot == gpu::Stream::POSITION) { - mesh->setVertexBuffer(points); - } else { - mesh->addAttribute(slot, points); - } - } - mesh->setIndexBuffer(newindices); - if (recalcNormals) { - recalculateNormals(); - } - return true; -} - bool scriptable::ScriptableMeshPart::replaceMeshData(scriptable::ScriptableMeshPartPointer src, const QVector& attributeNames) { auto target = getMeshPointer(); auto source = src ? src->getMeshPointer() : nullptr; @@ -389,7 +330,9 @@ bool scriptable::ScriptableMeshPart::replaceMeshData(scriptable::ScriptableMeshP for (const auto& a : attributeViews) { auto slot = buffer_helpers::ATTRIBUTES[a.first]; if (!attributes.contains(a.first)) { +#ifdef SCRIPTABLE_MESH_DEBUG qCInfo(graphics_scripting) << "ScriptableMesh::replaceMeshData -- pruning target attribute" << a.first << slot; +#endif target->removeAttribute(slot); } } @@ -404,21 +347,29 @@ bool scriptable::ScriptableMeshPart::replaceMeshData(scriptable::ScriptableMeshP if (slot == gpu::Stream::POSITION) { continue; } +#ifdef SCRIPTABLE_MESH_DEBUG auto& before = target->getAttributeBuffer(slot); +#endif auto& input = source->getAttributeBuffer(slot); if (input.getNumElements() == 0) { +#ifdef SCRIPTABLE_MESH_DEBUG qCInfo(graphics_scripting) << "ScriptableMeshPart::replaceMeshData buffer is empty -- pruning" << a << slot; +#endif target->removeAttribute(slot); } else { +#ifdef SCRIPTABLE_MESH_DEBUG if (before.getNumElements() == 0) { qCInfo(graphics_scripting) << "ScriptableMeshPart::replaceMeshData target buffer is empty -- adding" << a << slot; } else { qCInfo(graphics_scripting) << "ScriptableMeshPart::replaceMeshData target buffer exists -- updating" << a << slot; } +#endif target->addAttribute(slot, buffer_helpers::clone(input)); } +#ifdef SCRIPTABLE_MESH_DEBUG auto& after = target->getAttributeBuffer(slot); qCInfo(graphics_scripting) << "ScriptableMeshPart::replaceMeshData" << a << slot << before.getNumElements() << " -> " << after.getNumElements(); +#endif } @@ -465,7 +416,6 @@ bool scriptable::ScriptableMeshPart::dedupeVertices(float epsilon) { for (quint32 i = 0; i < numIndices; i++) { quint32 index = esize == 4 ? indices.get(i) : indices.get(i); if (remapIndices.contains(index)) { - //qCInfo(graphics_scripting) << i << index << "->" << remapIndices[index]; newIndices << remapIndices[index]; } else { qCInfo(graphics_scripting) << i << index << "!remapIndices[index]"; @@ -483,9 +433,11 @@ bool scriptable::ScriptableMeshPart::dedupeVertices(float epsilon) { if (slot == gpu::Stream::POSITION) { continue; } - qCInfo(graphics_scripting) << "ScriptableMeshPart::dedupeVertices" << a.first << slot << view.getNumElements(); auto newView = buffer_helpers::resize(view, numUniqueVerts); +#ifdef SCRIPTABLE_MESH_DEBUG + qCInfo(graphics_scripting) << "ScriptableMeshPart::dedupeVertices" << a.first << slot << view.getNumElements(); qCInfo(graphics_scripting) << a.first << "before: #" << view.getNumElements() << "after: #" << newView.getNumElements(); +#endif quint32 numElements = (quint32)view.getNumElements(); for (quint32 i = 0; i < numElements; i++) { quint32 fromVertexIndex = i; @@ -493,7 +445,7 @@ bool scriptable::ScriptableMeshPart::dedupeVertices(float epsilon) { buffer_helpers::fromVariant( newView, toVertexIndex, buffer_helpers::toVariant(view, fromVertexIndex, false, "dedupe") - ); + ); } mesh->addAttribute(slot, newView); } @@ -515,10 +467,7 @@ scriptable::ScriptableMeshPointer scriptable::ScriptableMesh::cloneMesh(bool rec return scriptable::ScriptableMeshPointer(meshPointer); } - - -// note: we don't always want the JS side to prevent mesh data from being freed - +// note: we don't always want the JS side to prevent mesh data from being freed (hence weak pointers unless parented QObject) scriptable::ScriptableMeshBase::ScriptableMeshBase( scriptable::WeakModelProviderPointer provider, scriptable::ScriptableModelBasePointer model, scriptable::WeakMeshPointer weakMesh, QObject* parent ) : QObject(parent), provider(provider), model(model), weakMesh(weakMesh) { diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h index f070fdf21e..48ef6c3a81 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h @@ -108,8 +108,10 @@ namespace scriptable { uint32 getNumFaces() const { return parentMesh ? parentMesh->getNumIndices() / _elementsPerFace : 0; } QVector getAttributeNames() const { return parentMesh ? parentMesh->getAttributeNames() : QVector(); } QVector getFace(uint32 faceIndex) const { - auto inds = parentMesh ? parentMesh->getIndices() : QVector(); - return faceIndex+2 < (uint32)inds.size() ? inds.mid(faceIndex*3, 3) : QVector(); + if (parentMesh && faceIndex + 2 < parentMesh->getNumIndices()) { + return parentMesh->getIndices().mid(faceIndex*3, 3); + } + return QVector(); } QVariantMap scaleToFit(float unitScale); QVariantMap translate(const glm::vec3& translation); @@ -118,7 +120,6 @@ namespace scriptable { QVariantMap rotate(const glm::quat& rotation, const glm::vec3& origin = glm::vec3(NAN)); QVariantMap transform(const glm::mat4& transform); - bool unrollVertices(bool recalcNormals = false); bool dedupeVertices(float epsilon = 1e-6); bool recalculateNormals() { return buffer_helpers::recalculateNormals(getMeshPointer()); } From f8fe06213d56156687333a28878f36219bf3f45a Mon Sep 17 00:00:00 2001 From: humbletim Date: Fri, 23 Feb 2018 09:11:23 -0500 Subject: [PATCH 22/29] CR feedback; whitespace / remove unnecessary diffs --- .../src/graphics-scripting/ScriptableModel.cpp | 4 ++-- libraries/render-utils/src/Model.cpp | 4 ++-- libraries/render-utils/src/Model.h | 3 +-- libraries/render-utils/src/Model_temporary_hack.cpp.h | 1 + libraries/script-engine/src/AssetScriptingInterface.cpp | 2 +- libraries/script-engine/src/ScriptEngines.cpp | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp index 306f1b18af..8ceb7de6a2 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp @@ -72,7 +72,7 @@ scriptable::ScriptableModelPointer scriptable::ScriptableModel::cloneModel(const const QVector scriptable::ScriptableModel::getConstMeshes() const { QVector out; - for(const auto& mesh : meshes) { + for (const auto& mesh : meshes) { const scriptable::ScriptableMesh* m = qobject_cast(&mesh); if (!m) { m = scriptable::make_scriptowned(mesh); @@ -87,7 +87,7 @@ const QVector scriptable::ScriptableModel::ge QVector scriptable::ScriptableModel::getMeshes() { QVector out; - for(auto& mesh : meshes) { + for (auto& mesh : meshes) { scriptable::ScriptableMesh* m = qobject_cast(&mesh); if (!m) { m = scriptable::make_scriptowned(mesh); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index a083b027f3..6d735497c0 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -24,9 +24,9 @@ #include #include #include -#include #include +#include #include #include #include @@ -885,7 +885,7 @@ void Model::renderDebugMeshBoxes(gpu::Batch& batch) { DependencyManager::get()->bindSimpleProgram(batch, false, false, false, true, true); - for(const auto& triangleSet : _modelSpaceMeshTriangleSets) { + for (const auto& triangleSet : _modelSpaceMeshTriangleSets) { auto box = triangleSet.getBounds(); if (_debugMeshBoxesID == GeometryCache::UNKNOWN_ID) { diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 603153d535..b3c7551eb3 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -317,13 +317,12 @@ public: int getResourceDownloadAttempts() { return _renderWatcher.getResourceDownloadAttempts(); } int getResourceDownloadAttemptsRemaining() { return _renderWatcher.getResourceDownloadAttemptsRemaining(); } + Q_INVOKABLE MeshProxyList getMeshes() const; virtual scriptable::ScriptableModelBase getScriptableModel() override; virtual bool replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer model, int meshIndex, int partIndex) override; void scaleToFit(); - Q_INVOKABLE MeshProxyList getMeshes() const; - void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName); void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName); diff --git a/libraries/render-utils/src/Model_temporary_hack.cpp.h b/libraries/render-utils/src/Model_temporary_hack.cpp.h index 51318656e0..cfa6945571 100644 --- a/libraries/render-utils/src/Model_temporary_hack.cpp.h +++ b/libraries/render-utils/src/Model_temporary_hack.cpp.h @@ -1,3 +1,4 @@ +// FIXME: temporary workaround for duplicating the FBXModel when dynamically replacing an underlying mesh part #include #include class MyGeometryResource : public GeometryResource { diff --git a/libraries/script-engine/src/AssetScriptingInterface.cpp b/libraries/script-engine/src/AssetScriptingInterface.cpp index 750e612781..68c24ecc33 100644 --- a/libraries/script-engine/src/AssetScriptingInterface.cpp +++ b/libraries/script-engine/src/AssetScriptingInterface.cpp @@ -440,7 +440,7 @@ void AssetScriptingInterface::saveToCache(const QUrl& rawURL, const QByteArray& JS_VERIFY(url.scheme() == "atp" || url.scheme() == "cache", "only 'atp' and 'cache' URL schemes supported"); JS_VERIFY(hash.isEmpty() || hash == hashDataHex(data), QString("invalid checksum hash for atp:HASH style URL (%1 != %2)").arg(hash, hashDataHex(data))); - //qCDebug(scriptengine) << "saveToCache" << url.toDisplayString() << data << hash << metadata; + // qCDebug(scriptengine) << "saveToCache" << url.toDisplayString() << data << hash << metadata; jsPromiseReady(Parent::saveToCache(url, data, metadata), scope, callback); } diff --git a/libraries/script-engine/src/ScriptEngines.cpp b/libraries/script-engine/src/ScriptEngines.cpp index 873c205706..871705d74b 100644 --- a/libraries/script-engine/src/ScriptEngines.cpp +++ b/libraries/script-engine/src/ScriptEngines.cpp @@ -535,7 +535,6 @@ void ScriptEngines::onScriptEngineLoaded(const QString& rawScriptURL) { } int ScriptEngines::runScriptInitializers(ScriptEnginePointer scriptEngine) { - // register our application services and set it off on its own thread int ii=0; for (auto initializer : _scriptInitializers) { ii++; @@ -554,6 +553,7 @@ void ScriptEngines::launchScriptEngine(ScriptEnginePointer scriptEngine) { loadScript(scriptName, userLoaded, false, false, true); }); + // register our application services and set it off on its own thread runScriptInitializers(scriptEngine); // FIXME disabling 'shift key' debugging for now. If you start up the application with From 058e4d1926874246054870581415b933f4de1ab9 Mon Sep 17 00:00:00 2001 From: humbletim Date: Fri, 23 Feb 2018 09:12:33 -0500 Subject: [PATCH 23/29] CR feedback; whitespace / remove unnecessary diffs --- libraries/render-utils/src/Model.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index b3c7551eb3..bbb323d1e7 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -143,7 +143,7 @@ public: /// Returns a reference to the shared collision geometry. const Geometry::Pointer& getCollisionGeometry() const { return _collisionGeometry; } - const QVariantMap getTextures() const { assert(isLoaded()); return getGeometry()->getTextures(); } + const QVariantMap getTextures() const { assert(isLoaded()); return _renderGeometry->getTextures(); } Q_INVOKABLE virtual void setTextures(const QVariantMap& textures); /// Provided as a convenience, will crash if !isLoaded() From 7c571cd43198f7cce1594983ed1f887b67c0971c Mon Sep 17 00:00:00 2001 From: humbletim Date: Fri, 23 Feb 2018 10:31:57 -0500 Subject: [PATCH 24/29] cleanup / addition error message detail --- .../src/graphics-scripting/GraphicsScriptingInterface.cpp | 2 +- .../src/graphics-scripting/ScriptableMesh.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index c336e77762..787905520a 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -100,7 +100,7 @@ scriptable::ScriptableModelPointer GraphicsScriptingInterface::getModelObject(QU error = "no meshes available: " + modelObject.objectID.toString(); } } else { - error = "objectID mismatch: " + modelObject.objectID.toString(); + error = QString("objectID mismatch: %1 (containing %2 meshes)").arg(modelObject.objectID.toString()).arg(modelObject.meshes.size()); } } else { error = "provider unavailable"; diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp index 0f689e3d2f..76741947fd 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp @@ -472,7 +472,9 @@ scriptable::ScriptableMeshBase::ScriptableMeshBase( scriptable::WeakModelProviderPointer provider, scriptable::ScriptableModelBasePointer model, scriptable::WeakMeshPointer weakMesh, QObject* parent ) : QObject(parent), provider(provider), model(model), weakMesh(weakMesh) { if (parent) { +#ifdef SCRIPTABLE_MESH_DEBUG qCDebug(graphics_scripting) << "ScriptableMeshBase -- have parent QObject, creating strong neshref" << weakMesh.lock().get() << parent; +#endif strongMesh = weakMesh.lock(); } } From f824edd04e98d3cbd5da12a4a60f5779f4c5bd7a Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 26 Feb 2018 04:58:22 -0500 Subject: [PATCH 25/29] * remove Model_temporary_hack * split gpuhelpers and mesh part * fix objwriter * more work on bufferview helpers * cr cleanup --- interface/src/Application.cpp | 31 +- interface/src/ui/overlays/Cube3DOverlay.cpp | 11 + interface/src/ui/overlays/Cube3DOverlay.h | 1 + interface/src/ui/overlays/ModelOverlay.cpp | 10 + interface/src/ui/overlays/ModelOverlay.h | 3 + interface/src/ui/overlays/Sphere3DOverlay.cpp | 12 + interface/src/ui/overlays/Sphere3DOverlay.h | 1 + .../src/avatars-renderer/Avatar.cpp | 2 +- .../src/RenderableModelEntityItem.cpp | 12 +- .../src/RenderableModelEntityItem.h | 1 + libraries/fbx/src/OBJWriter.cpp | 110 +-- libraries/gpu/src/gpu/Stream.cpp | 2 +- libraries/gpu/src/gpu/Stream.h | 4 + .../BufferViewScripting.cpp | 72 -- .../graphics-scripting/BufferViewScripting.h | 11 - .../src/graphics-scripting/Forward.h | 3 +- .../GraphicsScriptingInterface.cpp | 310 ++++++-- .../GraphicsScriptingInterface.h | 15 +- .../GraphicsScriptingUtil.cpp | 111 +++ .../GraphicsScriptingUtil.h | 57 +- .../src/graphics-scripting/ScriptableMesh.cpp | 622 +++++---------- .../src/graphics-scripting/ScriptableMesh.h | 142 +--- .../graphics-scripting/ScriptableMeshPart.cpp | 438 +++++++++++ .../graphics-scripting/ScriptableMeshPart.h | 106 +++ .../graphics-scripting/ScriptableModel.cpp | 18 +- .../src/graphics-scripting/ScriptableModel.h | 29 +- .../src/graphics/BufferViewHelpers.cpp | 733 ++++++++---------- .../graphics/src/graphics/BufferViewHelpers.h | 57 +- .../graphics/src/graphics/GpuHelpers.cpp | 122 +++ libraries/graphics/src/graphics/GpuHelpers.h | 47 ++ libraries/render-utils/src/Model.cpp | 76 +- libraries/render-utils/src/Model.h | 2 +- .../src/Model_temporary_hack.cpp.h | 84 -- 33 files changed, 1938 insertions(+), 1317 deletions(-) delete mode 100644 libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp delete mode 100644 libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.h create mode 100644 libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp create mode 100644 libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h create mode 100644 libraries/graphics/src/graphics/GpuHelpers.cpp create mode 100644 libraries/graphics/src/graphics/GpuHelpers.h delete mode 100644 libraries/render-utils/src/Model_temporary_hack.cpp.h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 69be01fc0c..e8847849dc 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -608,22 +608,25 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt class ApplicationMeshProvider : public scriptable::ModelProviderFactory { public: virtual scriptable::ModelProviderPointer lookupModelProvider(const QUuid& uuid) override { - QString error; - - scriptable::ModelProviderPointer provider; - if (uuid.isNull()) { - provider = nullptr; - } else if (auto entityInterface = getEntityModelProvider(static_cast(uuid))) { - provider = entityInterface; - } else if (auto overlayInterface = getOverlayModelProvider(static_cast(uuid))) { - provider = overlayInterface; - } else if (auto avatarInterface = getAvatarModelProvider(uuid)) { - provider = avatarInterface; + bool success; + if (auto nestable = DependencyManager::get()->find(uuid, success).lock()) { + auto type = nestable->getNestableType(); +#ifdef SCRIPTABLE_MESH_DEBUG + qCDebug(interfaceapp) << "ApplicationMeshProvider::lookupModelProvider" << uuid << SpatiallyNestable::nestableTypeToString(type); +#endif + switch (type) { + case NestableType::Entity: + return getEntityModelProvider(static_cast(uuid)); + case NestableType::Overlay: + return getOverlayModelProvider(static_cast(uuid)); + case NestableType::Avatar: + return getAvatarModelProvider(uuid); + } } - - return provider; + return nullptr; } +private: scriptable::ModelProviderPointer getEntityModelProvider(EntityItemID entityID) { scriptable::ModelProviderPointer provider; auto entityTreeRenderer = qApp->getEntities(); @@ -649,6 +652,8 @@ public: } else { qCWarning(interfaceapp) << "no renderer for overlay ID" << overlayID.toString(); } + } else { + qCWarning(interfaceapp) << "overlay not found" << overlayID.toString(); } return provider; } diff --git a/interface/src/ui/overlays/Cube3DOverlay.cpp b/interface/src/ui/overlays/Cube3DOverlay.cpp index f13f782482..810c7b0fca 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.cpp +++ b/interface/src/ui/overlays/Cube3DOverlay.cpp @@ -200,3 +200,14 @@ Transform Cube3DOverlay::evalRenderTransform() { transform.setRotation(rotation); return transform; } + +scriptable::ScriptableModelBase Cube3DOverlay::getScriptableModel() { + auto geometryCache = DependencyManager::get(); + auto vertexColor = ColorUtils::toVec3(_color); + scriptable::ScriptableModelBase result; + if (auto mesh = geometryCache->meshFromShape(GeometryCache::Cube, vertexColor)) { + result.objectID = getID(); + result.append(mesh); + } + return result; +} diff --git a/interface/src/ui/overlays/Cube3DOverlay.h b/interface/src/ui/overlays/Cube3DOverlay.h index e7b58ad911..0b1748c00f 100644 --- a/interface/src/ui/overlays/Cube3DOverlay.h +++ b/interface/src/ui/overlays/Cube3DOverlay.h @@ -36,6 +36,7 @@ public: void setProperties(const QVariantMap& properties) override; QVariant getProperty(const QString& property) override; + virtual scriptable::ScriptableModelBase getScriptableModel() override; protected: Transform evalRenderTransform() override; diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp index ffcc18032c..6603a44d46 100644 --- a/interface/src/ui/overlays/ModelOverlay.cpp +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -665,6 +665,16 @@ void ModelOverlay::processMaterials() { } } +bool ModelOverlay::canReplaceModelMeshPart(int meshIndex, int partIndex) { + // TODO: bounds checking; for now just used to indicate provider generally supports mesh updates + return _model && _model->isLoaded(); +} + +bool ModelOverlay::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer newModel, int meshIndex, int partIndex) { + return canReplaceModelMeshPart(meshIndex, partIndex) && + _model->replaceScriptableModelMeshPart(newModel, meshIndex, partIndex); +} + scriptable::ScriptableModelBase ModelOverlay::getScriptableModel() { if (!_model || !_model->isLoaded()) { return Base3DOverlay::getScriptableModel(); diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h index fa399d40f9..88a1729d68 100644 --- a/interface/src/ui/overlays/ModelOverlay.h +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -63,6 +63,9 @@ public: void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) override; virtual scriptable::ScriptableModelBase getScriptableModel() override; + virtual bool canReplaceModelMeshPart(int meshIndex, int partIndex) override; + virtual bool replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer model, int meshIndex, int partIndex) override; + protected: Transform evalRenderTransform() override; diff --git a/interface/src/ui/overlays/Sphere3DOverlay.cpp b/interface/src/ui/overlays/Sphere3DOverlay.cpp index 3021aa4404..4743e1ed3a 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.cpp +++ b/interface/src/ui/overlays/Sphere3DOverlay.cpp @@ -123,3 +123,15 @@ Transform Sphere3DOverlay::evalRenderTransform() { return transform; } + + +scriptable::ScriptableModelBase Sphere3DOverlay::getScriptableModel() { + auto geometryCache = DependencyManager::get(); + auto vertexColor = ColorUtils::toVec3(_color); + scriptable::ScriptableModelBase result; + if (auto mesh = geometryCache->meshFromShape(GeometryCache::Sphere, vertexColor)) { + result.objectID = getID(); + result.append(mesh); + } + return result; +} diff --git a/interface/src/ui/overlays/Sphere3DOverlay.h b/interface/src/ui/overlays/Sphere3DOverlay.h index ebe6dc8d83..9a434e7182 100644 --- a/interface/src/ui/overlays/Sphere3DOverlay.h +++ b/interface/src/ui/overlays/Sphere3DOverlay.h @@ -28,6 +28,7 @@ public: virtual Sphere3DOverlay* createClone() const override; + virtual scriptable::ScriptableModelBase getScriptableModel() override; protected: Transform evalRenderTransform() override; }; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 25ddfba670..f493bb14dd 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1800,6 +1800,6 @@ scriptable::ScriptableModelBase Avatar::getScriptableModel() { return scriptable::ScriptableModelBase(); } auto result = _skeletonModel->getScriptableModel(); - result.objectID = getSessionUUID(); + result.objectID = getSessionUUID().isNull() ? AVATAR_SELF_ID : getSessionUUID(); return result; } \ No newline at end of file diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp index f3c99cde2d..ae7bb26e87 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.cpp @@ -961,8 +961,7 @@ bool RenderableModelEntityItem::getMeshes(MeshProxyList& result) { } scriptable::ScriptableModelBase render::entities::ModelEntityRenderer::getScriptableModel() { - ModelPointer model; - withReadLock([&] { model = _model; }); + auto model = resultWithReadLock([this]{ return _model; }); if (!model || !model->isLoaded()) { return scriptable::ScriptableModelBase(); @@ -973,9 +972,14 @@ scriptable::ScriptableModelBase render::entities::ModelEntityRenderer::getScript return result; } +bool render::entities::ModelEntityRenderer::canReplaceModelMeshPart(int meshIndex, int partIndex) { + // TODO: for now this method is just used to indicate that this provider generally supports mesh updates + auto model = resultWithReadLock([this]{ return _model; }); + return model && model->isLoaded(); +} + bool render::entities::ModelEntityRenderer::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer newModel, int meshIndex, int partIndex) { - ModelPointer model; - withReadLock([&] { model = _model; }); + auto model = resultWithReadLock([this]{ return _model; }); if (!model || !model->isLoaded()) { return false; diff --git a/libraries/entities-renderer/src/RenderableModelEntityItem.h b/libraries/entities-renderer/src/RenderableModelEntityItem.h index 7edaef264d..5d7d84b7bc 100644 --- a/libraries/entities-renderer/src/RenderableModelEntityItem.h +++ b/libraries/entities-renderer/src/RenderableModelEntityItem.h @@ -143,6 +143,7 @@ class ModelEntityRenderer : public TypedEntityRenderer #include -#include "graphics/Geometry.h" -#include "OBJWriter.h" +#include +#include #include "ModelFormatLogging.h" static QString formatFloat(double n) { @@ -46,59 +48,60 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { QList meshNormalStartOffset; int currentVertexStartOffset = 0; int currentNormalStartOffset = 0; + int subMeshIndex = 0; + out << "# OBJWriter::writeOBJToTextStream\n"; // write out vertices (and maybe colors) foreach (const MeshPointer& mesh, meshes) { + out << "# vertices::subMeshIndex " << subMeshIndex++ << "\n"; meshVertexStartOffset.append(currentVertexStartOffset); - const gpu::BufferView& vertexBuffer = mesh->getVertexBuffer(); - const gpu::BufferView& colorsBufferView = mesh->getAttributeBuffer(gpu::Stream::COLOR); - gpu::BufferView::Index numColors = (gpu::BufferView::Index)colorsBufferView.getNumElements(); - gpu::BufferView::Index colorIndex = 0; + auto vertices = buffer_helpers::bufferToVector(mesh->getVertexBuffer(), "mesh.vertices"); + auto colors = buffer_helpers::mesh::attributeToVector(mesh, gpu::Stream::COLOR); - int vertexCount = 0; - gpu::BufferView::Iterator vertexItr = vertexBuffer.cbegin(); - while (vertexItr != vertexBuffer.cend()) { - glm::vec3 v = *vertexItr; + gpu::BufferView::Index numColors = colors.size(); + + int i = 0; + for (const auto& v : vertices) { out << "v "; out << formatFloat(v[0]) << " "; out << formatFloat(v[1]) << " "; out << formatFloat(v[2]); - if (colorIndex < numColors) { - glm::vec3 color = colorsBufferView.get(colorIndex); + if (i < numColors) { + const glm::vec3& color = colors[i]; out << " " << formatFloat(color[0]); out << " " << formatFloat(color[1]); out << " " << formatFloat(color[2]); - colorIndex++; } out << "\n"; - vertexItr++; - vertexCount++; + i++; } - currentVertexStartOffset += vertexCount; + currentVertexStartOffset += i; } out << "\n"; // write out normals bool haveNormals = true; + subMeshIndex = 0; foreach (const MeshPointer& mesh, meshes) { + out << "# normals::subMeshIndex " << subMeshIndex++ << "\n"; meshNormalStartOffset.append(currentNormalStartOffset); - const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(gpu::Stream::InputSlot::NORMAL); - gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements(); - for (gpu::BufferView::Index i = 0; i < numNormals; i++) { - glm::vec3 normal = normalsBufferView.get(i); + auto normals = buffer_helpers::mesh::attributeToVector(mesh, gpu::Stream::NORMAL); + for (const auto& normal : normals) { out << "vn "; out << formatFloat(normal[0]) << " "; out << formatFloat(normal[1]) << " "; out << formatFloat(normal[2]) << "\n"; } - currentNormalStartOffset += numNormals; + currentNormalStartOffset += normals.size(); } out << "\n"; // write out faces int nth = 0; + subMeshIndex = 0; foreach (const MeshPointer& mesh, meshes) { + out << "# faces::subMeshIndex " << subMeshIndex++ << "\n"; currentVertexStartOffset = meshVertexStartOffset.takeFirst(); currentNormalStartOffset = meshNormalStartOffset.takeFirst(); @@ -106,45 +109,46 @@ bool writeOBJToTextStream(QTextStream& out, QList meshes) { const gpu::BufferView& indexBuffer = mesh->getIndexBuffer(); graphics::Index partCount = (graphics::Index)mesh->getNumParts(); + QString name = (!mesh->displayName.size() ? QString("mesh-%1-part").arg(nth) : QString::fromStdString(mesh->displayName)) + .replace(QRegExp("[^-_a-zA-Z0-9]"), "_"); for (int partIndex = 0; partIndex < partCount; partIndex++) { const graphics::Mesh::Part& part = partBuffer.get(partIndex); - out << "g part-" << nth++ << "\n"; - - // graphics::Mesh::TRIANGLES - // TODO -- handle other formats - gpu::BufferView::Iterator indexItr = indexBuffer.cbegin(); - indexItr += part._startIndex; - - int indexCount = 0; - while (indexItr != indexBuffer.cend() && indexCount < part._numIndices) { - uint32_t index0 = *indexItr; - indexItr++; - indexCount++; - if (indexItr == indexBuffer.cend() || indexCount >= part._numIndices) { - qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3"; - break; - } - uint32_t index1 = *indexItr; - indexItr++; - indexCount++; - if (indexItr == indexBuffer.cend() || indexCount >= part._numIndices) { - qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3"; - break; - } - uint32_t index2 = *indexItr; - indexItr++; - indexCount++; + out << QString("g %1-%2-%3\n").arg(subMeshIndex, 3, 10, QChar('0')).arg(name).arg(partIndex); + auto indices = buffer_helpers::bufferToVector(mesh->getIndexBuffer(), "mesh.indices"); + auto face = [&](uint32_t i0, uint32_t i1, uint32_t i2) { out << "f "; if (haveNormals) { - out << currentVertexStartOffset + index0 + 1 << "//" << currentVertexStartOffset + index0 + 1 << " "; - out << currentVertexStartOffset + index1 + 1 << "//" << currentVertexStartOffset + index1 + 1 << " "; - out << currentVertexStartOffset + index2 + 1 << "//" << currentVertexStartOffset + index2 + 1 << "\n"; + out << currentVertexStartOffset + indices[i0] + 1 << "//" << currentVertexStartOffset + indices[i0] + 1 << " "; + out << currentVertexStartOffset + indices[i1] + 1 << "//" << currentVertexStartOffset + indices[i1] + 1 << " "; + out << currentVertexStartOffset + indices[i2] + 1 << "//" << currentVertexStartOffset + indices[i2] + 1 << "\n"; } else { - out << currentVertexStartOffset + index0 + 1 << " "; - out << currentVertexStartOffset + index1 + 1 << " "; - out << currentVertexStartOffset + index2 + 1 << "\n"; + out << currentVertexStartOffset + indices[i0] + 1 << " "; + out << currentVertexStartOffset + indices[i1] + 1 << " "; + out << currentVertexStartOffset + indices[i2] + 1 << "\n"; + } + }; + + uint32_t len = part._startIndex + part._numIndices; + qCDebug(modelformat) << "OBJWriter -- part" << partIndex << "topo" << part._topology << "index elements"; + if (part._topology == graphics::Mesh::TRIANGLES && len % 3 != 0) { + qCDebug(modelformat) << "OBJWriter -- index buffer length isn't a multiple of 3" << len; + } + if (part._topology == graphics::Mesh::QUADS && len % 4 != 0) { + qCDebug(modelformat) << "OBJWriter -- index buffer length isn't a multiple of 4" << len; + } + if (len > indexBuffer.getNumElements()) { + qCDebug(modelformat) << "OBJWriter -- len > index size" << len << indexBuffer.getNumElements(); + } + if (part._topology == graphics::Mesh::QUADS) { + for (uint32_t idx = part._startIndex; idx+3 < len; idx += 4) { + face(idx+0, idx+1, idx+3); + face(idx+1, idx+2, idx+3); + } + } else if (part._topology == graphics::Mesh::TRIANGLES) { + for (uint32_t idx = part._startIndex; idx+2 < len; idx += 3) { + face(idx+0, idx+1, idx+2); } } out << "\n"; diff --git a/libraries/gpu/src/gpu/Stream.cpp b/libraries/gpu/src/gpu/Stream.cpp index caa1ecbc06..f83900be42 100644 --- a/libraries/gpu/src/gpu/Stream.cpp +++ b/libraries/gpu/src/gpu/Stream.cpp @@ -19,7 +19,7 @@ using namespace gpu; using ElementArray = std::array; -const ElementArray& getDefaultElements() { +const ElementArray& Stream::getDefaultElements() { static ElementArray defaultElements{{ //POSITION = 0, Element::VEC3F_XYZ, diff --git a/libraries/gpu/src/gpu/Stream.h b/libraries/gpu/src/gpu/Stream.h index 5562980a91..0def1ab201 100644 --- a/libraries/gpu/src/gpu/Stream.h +++ b/libraries/gpu/src/gpu/Stream.h @@ -23,6 +23,8 @@ namespace gpu { +class Element; + // Stream namespace class class Stream { public: @@ -49,6 +51,8 @@ public: typedef uint8 Slot; + static const std::array& getDefaultElements(); + // Frequency describer enum Frequency { PER_VERTEX = 0, diff --git a/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp b/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp deleted file mode 100644 index 9d7a0e1f30..0000000000 --- a/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "BufferViewScripting.h" - -#include -#include -#include -#include - -#include -#include -#include - -#include - -template -QScriptValue getBufferViewElement(QScriptEngine* js, const gpu::BufferView& view, quint32 index, bool asArray = false) { - return glmVecToScriptValue(js, view.get(index), asArray); -} - -QScriptValue bufferViewElementToScriptValue(QScriptEngine* engine, const gpu::BufferView& view, quint32 index, bool asArray, const char* hint) { - QVariant result = buffer_helpers::toVariant(view, index, asArray, hint); - if (!result.isValid()) { - return QScriptValue::NullValue; - } - return engine->toScriptValue(result); -} - -template -void setBufferViewElement(const gpu::BufferView& view, quint32 index, const QScriptValue& v) { - view.edit(index) = glmVecFromScriptValue(v); -} - -bool bufferViewElementFromScriptValue(const QScriptValue& v, const gpu::BufferView& view, quint32 index) { - return buffer_helpers::fromVariant(view, index, v.toVariant()); -} - -template -QScriptValue glmVecToScriptValue(QScriptEngine *js, const T& v, bool asArray) { - static const auto len = T().length(); - const auto& components = asArray ? buffer_helpers::ZERO123 : buffer_helpers::XYZW; - auto obj = asArray ? js->newArray() : js->newObject(); - for (int i = 0; i < len ; i++) { - const auto key = components[i]; - const auto value = v[i]; - if (value != value) { // NAN -#ifdef DEV_BUILD - qWarning().nospace()<< "vec" << len << "." << key << " converting NAN to javascript NaN.... " << value; -#endif - obj.setProperty(key, js->globalObject().property("NaN")); - } else { - obj.setProperty(key, value); - } - } - return obj; -} - -template -const T glmVecFromScriptValue(const QScriptValue& v) { - static const auto len = T().length(); - const auto& components = v.property("x").isValid() ? buffer_helpers::XYZW : buffer_helpers::ZERO123; - T result; - for (int i = 0; i < len ; i++) { - const auto key = components[i]; - const auto value = v.property(key).toNumber(); -#ifdef DEV_BUILD - if (value != value) { // NAN - qWarning().nospace()<< "vec" << len << "." << key << " NAN received from script.... " << v.toVariant().toString(); - } -#endif - result[i] = value; - } - return result; -} diff --git a/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.h b/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.h deleted file mode 100644 index f2e3fe734e..0000000000 --- a/libraries/graphics-scripting/src/graphics-scripting/BufferViewScripting.h +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -namespace gpu { class BufferView; } -class QScriptValue; -class QScriptEngine; -template QScriptValue glmVecToScriptValue(QScriptEngine *js, const T& v, bool asArray = false); -template const T glmVecFromScriptValue(const QScriptValue& v); -QScriptValue bufferViewElementToScriptValue(QScriptEngine* engine, const gpu::BufferView& view, quint32 index, bool asArray = false, const char* hint = ""); -bool bufferViewElementFromScriptValue(const QScriptValue& v, const gpu::BufferView& view, quint32 index); diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h index 94a96446a0..2f2f191dce 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/Forward.h +++ b/libraries/graphics-scripting/src/graphics-scripting/Forward.h @@ -74,7 +74,7 @@ namespace scriptable { public: NestableType modelProviderType; virtual scriptable::ScriptableModelBase getScriptableModel() = 0; - + virtual bool canReplaceModelMeshPart(int meshIndex, int partIndex) { return false; } virtual bool replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer model, int meshIndex, int partIndex) { return false; } }; @@ -88,7 +88,6 @@ namespace scriptable { void modelRemovedFromScene(const QUuid& objectID, NestableType nestableType, const ModelPointer& sender); }; - using uint32 = quint32; class ScriptableModel; using ScriptableModelPointer = QPointer; class ScriptableMesh; diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index 787905520a..22ef346df6 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -15,13 +15,16 @@ #include "RegisteredMetaTypes.h" #include "ScriptEngineLogging.h" #include "ScriptableMesh.h" +#include "ScriptableMeshPart.h" #include #include #include #include #include #include +#include #include +#include GraphicsScriptingInterface::GraphicsScriptingInterface(QObject* parent) : QObject(parent), QScriptable() { if (auto scriptEngine = qobject_cast(parent)) { @@ -29,21 +32,45 @@ GraphicsScriptingInterface::GraphicsScriptingInterface(QObject* parent) : QObjec } } -bool GraphicsScriptingInterface::updateModelObject(QUuid uuid, const scriptable::ScriptableModelPointer model) { - if (auto provider = getModelProvider(uuid)) { - if (auto base = model->operator scriptable::ScriptableModelBasePointer()) { -#ifdef SCRIPTABLE_MESH_DEBUG - qDebug() << "replaceScriptableModelMeshPart" << model->toString() << -1 << -1; -#endif - return provider->replaceScriptableModelMeshPart(base, -1, -1); - } else { - qDebug() << "replaceScriptableModelMeshPart -- !base" << model << base << -1 << -1; - } +void GraphicsScriptingInterface::jsThrowError(const QString& error) { + if (context()) { + context()->throwError(error); } else { - qDebug() << "replaceScriptableModelMeshPart -- !provider"; + qCWarning(graphics_scripting) << "GraphicsScriptingInterface::jsThrowError (without valid JS context):" << error; + } +} + +bool GraphicsScriptingInterface::canUpdateModel(QUuid uuid, int meshIndex, int partNumber) { + auto provider = getModelProvider(uuid); + return provider && provider->canReplaceModelMeshPart(meshIndex, partNumber); +} + +bool GraphicsScriptingInterface::updateModel(QUuid uuid, const scriptable::ScriptableModelPointer& model) { + if (!model) { + jsThrowError("null model argument"); } - return false; + auto base = model->operator scriptable::ScriptableModelBasePointer(); + if (!base) { + jsThrowError("could not get base model pointer"); + return false; + } + + auto provider = getModelProvider(uuid); + if (!provider) { + jsThrowError("provider unavailable"); + return false; + } + + if (!provider->canReplaceModelMeshPart(-1, -1)) { + jsThrowError("provider does not support updating mesh parts"); + return false; + } + +#ifdef SCRIPTABLE_MESH_DEBUG + qDebug() << "replaceScriptableModelMeshPart" << model->toString() << -1 << -1; +#endif + return provider->replaceScriptableModelMeshPart(base, -1, -1); } scriptable::ModelProviderPointer GraphicsScriptingInterface::getModelProvider(QUuid uuid) { @@ -57,28 +84,26 @@ scriptable::ModelProviderPointer GraphicsScriptingInterface::getModelProvider(QU } else { error = "appProvider unavailable"; } - if (context()) { - context()->throwError(error); - } else { - qCWarning(graphics_scripting) << "GraphicsScriptingInterface::getModelProvider ERROR" << error; - } + jsThrowError(error); return nullptr; } -scriptable::ScriptableModelPointer GraphicsScriptingInterface::newModelObject(QVector meshes) { +scriptable::ScriptableModelPointer GraphicsScriptingInterface::newModel(const scriptable::ScriptableMeshes& meshes) { auto modelWrapper = scriptable::make_scriptowned(); modelWrapper->setObjectName("js::model"); if (meshes.isEmpty()) { - if (context()) { - context()->throwError("expected [meshes] array as first argument"); - } + jsThrowError("expected [meshes] array as first argument"); } else { int i = 0; for (const auto& mesh : meshes) { +#ifdef SCRIPTABLE_MESH_DEBUG + qDebug() << "newModel" << i << meshes.size() << mesh; +#endif if (mesh) { modelWrapper->append(*mesh); - } else if (context()) { - context()->throwError(QString("invalid mesh at index: %1").arg(i)); + } else { + jsThrowError(QString("invalid mesh at index: %1").arg(i)); + break; } i++; } @@ -86,30 +111,37 @@ scriptable::ScriptableModelPointer GraphicsScriptingInterface::newModelObject(QV return modelWrapper; } -scriptable::ScriptableModelPointer GraphicsScriptingInterface::getModelObject(QUuid uuid) { - QString error, providerType = "unknown"; - if (auto provider = getModelProvider(uuid)) { - providerType = SpatiallyNestable::nestableTypeToString(provider->modelProviderType); - auto modelObject = provider->getScriptableModel(); - if (modelObject.objectID == uuid) { - if (modelObject.meshes.size()) { - auto modelWrapper = scriptable::make_scriptowned(modelObject); - modelWrapper->setObjectName(providerType+"::"+uuid.toString()+"::model"); - return modelWrapper; +scriptable::ScriptableModelPointer GraphicsScriptingInterface::getModel(QUuid uuid) { + QString error; + bool success; + QString providerType = "unknown"; + if (auto nestable = DependencyManager::get()->find(uuid, success).lock()) { + providerType = SpatiallyNestable::nestableTypeToString(nestable->getNestableType()); + if (auto provider = getModelProvider(uuid)) { + auto modelObject = provider->getScriptableModel(); + const bool found = !modelObject.objectID.isNull(); + if (found && uuid == AVATAR_SELF_ID) { + // special case override so that scripts can rely on matching intput/output UUIDs + modelObject.objectID = AVATAR_SELF_ID; + } + if (modelObject.objectID == uuid) { + if (modelObject.meshes.size()) { + auto modelWrapper = scriptable::make_scriptowned(modelObject); + modelWrapper->setObjectName(providerType+"::"+uuid.toString()+"::model"); + return modelWrapper; + } else { + error = "no meshes available: " + modelObject.objectID.toString(); + } } else { - error = "no meshes available: " + modelObject.objectID.toString(); + error = QString("objectID mismatch: %1 (result contained %2 meshes)").arg(modelObject.objectID.toString()).arg(modelObject.meshes.size()); } } else { - error = QString("objectID mismatch: %1 (containing %2 meshes)").arg(modelObject.objectID.toString()).arg(modelObject.meshes.size()); + error = "model provider unavailable"; } } else { - error = "provider unavailable"; - } - auto errorMessage = QString("failed to get meshes from %1 provider for uuid %2 (%3)").arg(providerType).arg(uuid.toString()).arg(error); - qCWarning(graphics_scripting) << "GraphicsScriptingInterface::getModelObject ERROR" << errorMessage; - if (context()) { - context()->throwError(errorMessage); + error = "model object not found"; } + jsThrowError(QString("failed to get meshes from %1 provider for uuid %2 (%3)").arg(providerType).arg(uuid.toString()).arg(error)); return nullptr; } @@ -135,6 +167,85 @@ bool GraphicsScriptingInterface::updateMeshPart(scriptable::ScriptableMeshPointe } #endif +scriptable::ScriptableMeshPointer GraphicsScriptingInterface::newMesh(const QVariantMap& ifsMeshData) { + // TODO: this is bare-bones way for now to improvise a new mesh from the scripting side + // in the future we want to support a formal C++ structure data type here instead + QString meshName = ifsMeshData.value("name").toString(); + QString topologyName = ifsMeshData.value("topology").toString(); + QVector indices = buffer_helpers::variantToVector(ifsMeshData.value("indices")); + QVector vertices = buffer_helpers::variantToVector(ifsMeshData.value("positions")); + QVector normals = buffer_helpers::variantToVector(ifsMeshData.value("normals")); + QVector colors = buffer_helpers::variantToVector(ifsMeshData.value("colors")); + QVector texCoords0 = buffer_helpers::variantToVector(ifsMeshData.value("texCoords0")); + + const auto numVertices = vertices.size(); + const auto numIndices = indices.size(); + const auto topology = graphics::Mesh::TRIANGLES; + + // sanity checks + QString error; + if (!topologyName.isEmpty() && topologyName != "triangles") { + error = "expected 'triangles' or undefined for .topology"; + } else if (!numIndices) { + error = QString("expected non-empty [uint32,...] array for .indices (got type=%1)").arg(ifsMeshData.value("indices").typeName()); + } else if (numIndices % 3 != 0) { + error = QString("expected 'triangle faces' for .indices (ie: length to be divisible by 3) length=%1").arg(numIndices); + } else if (!numVertices) { + error = "expected non-empty [glm::vec3(),...] array for .positions"; + } else { + const gpu::uint32 maxVertexIndex = numVertices; + int i = 0; + for (const auto& ind : indices) { + if (ind >= maxVertexIndex) { + error = QString("index out of .indices[%1] index=%2 >= maxVertexIndex=%3").arg(i).arg(ind).arg(maxVertexIndex); + break; + } + i++; + } + } + if (!error.isEmpty()) { + jsThrowError(error); + return nullptr; + } + + if (ifsMeshData.contains("normals") && normals.size() < numVertices) { + qCInfo(graphics_scripting) << "newMesh -- expanding .normals to #" << numVertices; + normals.resize(numVertices); + } + if (ifsMeshData.contains("colors") && colors.size() < numVertices) { + qCInfo(graphics_scripting) << "newMesh -- expanding .colors to #" << numVertices; + colors.resize(numVertices); + } + if (ifsMeshData.contains("texCoords0") && texCoords0.size() < numVertices) { + qCInfo(graphics_scripting) << "newMesh -- expanding .texCoords0 to #" << numVertices; + texCoords0.resize(numVertices); + } + if (ifsMeshData.contains("texCoords1")) { + qCWarning(graphics_scripting) << "newMesh - texCoords1 not yet supported; ignoring"; + } + + graphics::MeshPointer mesh(new graphics::Mesh()); + mesh->modelName = "graphics::newMesh"; + mesh->displayName = meshName.toStdString(); + + // TODO: newFromVector does no conversion -- later we could autodetect if fitting into gpu::INDEX_UINT16 + // and also pack other values (like NORMAL / TEXCOORD0 where relevant) + mesh->setIndexBuffer(buffer_helpers::newFromVector(indices, gpu::Format::INDEX_INT32)); + mesh->setVertexBuffer(buffer_helpers::newFromVector(vertices, gpu::Format::VEC3F_XYZ)); + if (normals.size()) { + mesh->addAttribute(gpu::Stream::NORMAL, buffer_helpers::newFromVector(normals, gpu::Format::VEC3F_XYZ)); + } + if (colors.size()) { + mesh->addAttribute(gpu::Stream::COLOR, buffer_helpers::newFromVector(colors, gpu::Format::VEC3F_XYZ)); + } + if (texCoords0.size()) { + mesh->addAttribute(gpu::Stream::TEXCOORD0, buffer_helpers::newFromVector(texCoords0, gpu::Format::VEC2F_UV)); + } + QVector parts = {{ 0, indices.size(), 0, topology}}; + mesh->setPartBuffer(buffer_helpers::newFromVector(parts, gpu::Element::PART_DRAWCALL)); + return scriptable::make_scriptowned(mesh, nullptr); +} + QString GraphicsScriptingInterface::exportModelToOBJ(const scriptable::ScriptableModel& _in) { const auto& in = _in.getConstMeshes(); if (in.size()) { @@ -148,11 +259,10 @@ QString GraphicsScriptingInterface::exportModelToOBJ(const scriptable::Scriptabl return writeOBJToString(meshes); } } - if (context()) { - context()->throwError(QString("null mesh")); - } + jsThrowError("null mesh"); return QString(); } + void GraphicsScriptingInterface::registerMetaTypes(QScriptEngine* engine) { scriptable::registerMetaTypes(engine); } @@ -166,23 +276,115 @@ MeshPointer GraphicsScriptingInterface::getMeshPointer(scriptable::ScriptableMes 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"; - } + jsThrowError("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"; - } + jsThrowError("expected valid meshProxy as first parameter"); return result; } return mesh; } +namespace { + QScriptValue qVectorUInt32ToScriptValue(QScriptEngine* engine, const QVector& vector) { + return qScriptValueFromSequence(engine, vector); + } + + void qVectorUInt32FromScriptValue(const QScriptValue& array, QVector& result) { + qScriptValueToSequence(array, result); + } + + QVector metaTypeIds{ + qRegisterMetaType("uint32"), + qRegisterMetaType("glm::uint32"), + qRegisterMetaType>(), + qRegisterMetaType>("QVector"), + qRegisterMetaType(), + qRegisterMetaType("ScriptableMeshes"), + qRegisterMetaType("scriptable::ScriptableMeshes"), + qRegisterMetaType>("QVector"), + qRegisterMetaType(), + qRegisterMetaType(), + qRegisterMetaType(), + qRegisterMetaType(), + }; +} + +namespace scriptable { + template int registerQPointerThing(QScriptEngine* engine) { + qScriptRegisterSequenceMetaType>>(engine); + return qScriptRegisterMetaType>( + engine, + [](QScriptEngine* engine, const QPointer& object) -> QScriptValue { + if (!object) { + return QScriptValue::NullValue; + } + return engine->newQObject(object, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::AutoCreateDynamicProperties); + }, + [](const QScriptValue& value, QPointer& out) { + auto obj = value.toQObject(); +#ifdef SCRIPTABLE_MESH_DEBUG + qCInfo(graphics_scripting) << "qpointer_qobject_cast" << obj << value.toString(); +#endif + if (auto tmp = qobject_cast(obj)) { + out = QPointer(tmp); + return; + } +#if 0 + if (auto tmp = static_cast(obj)) { +#ifdef SCRIPTABLE_MESH_DEBUG + qCInfo(graphics_scripting) << "qpointer_qobject_cast -- via static_cast" << obj << tmp << value.toString(); +#endif + out = QPointer(tmp); + return; + } +#endif + out = nullptr; + } + ); + } + + template int registerDebugEnum(QScriptEngine* engine, const DebugEnums& debugEnums) { + static const DebugEnums& poop = debugEnums; + return qScriptRegisterMetaType( + engine, + [](QScriptEngine* engine, const T& topology) -> QScriptValue { + return poop.value(topology); + }, + [](const QScriptValue& value, T& topology) { + topology = poop.key(value.toString()); + } + ); + } + + bool registerMetaTypes(QScriptEngine* engine) { + qScriptRegisterSequenceMetaType>(engine); + + qScriptRegisterMetaType(engine, qVectorUInt32ToScriptValue, qVectorUInt32FromScriptValue); + + registerQPointerThing(engine); + registerQPointerThing(engine); + registerQPointerThing(engine); + qScriptRegisterMetaType>( + engine, + [](QScriptEngine* engine, const QVector& vector) -> QScriptValue { + return qScriptValueFromSequence(engine, vector); + }, + [](const QScriptValue& array, QVector& result) { + qScriptValueToSequence(array, result); + } + ); + + registerDebugEnum(engine, graphics::TOPOLOGIES); + registerDebugEnum(engine, gpu::TYPES); + registerDebugEnum(engine, gpu::SEMANTICS); + registerDebugEnum(engine, gpu::DIMENSIONS); + + return metaTypeIds.size(); + } + +} + #include "GraphicsScriptingInterface.moc" diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h index 9866b5585c..a66e382bc7 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h @@ -34,13 +34,14 @@ public slots: * @function GraphicsScriptingInterface.getModel * @param {UUID} The objectID of the model whose meshes are to be retrieve */ - scriptable::ModelProviderPointer getModelProvider(QUuid uuid); - scriptable::ScriptableModelPointer getModelObject(QUuid uuid); - bool updateModelObject(QUuid uuid, const scriptable::ScriptableModelPointer model); - scriptable::ScriptableModelPointer newModelObject(QVector meshes); + scriptable::ScriptableModelPointer getModel(QUuid uuid); + bool updateModel(QUuid uuid, const scriptable::ScriptableModelPointer& model); + bool canUpdateModel(QUuid uuid, int meshIndex = -1, int partNumber = -1); + scriptable::ScriptableModelPointer newModel(const scriptable::ScriptableMeshes& meshes); + scriptable::ScriptableMeshPointer newMesh(const QVariantMap& ifsMeshData); #ifdef SCRIPTABLE_MESH_TODO - scriptable::ScriptableMeshPartPointer exportMeshPart(scriptable::ScriptableMeshPointer mesh, int part=0) { + scriptable::ScriptableMeshPartPointer exportMeshPart(scriptable::ScriptableMeshPointer mesh, int partNumber = -1) { return scriptable::make_scriptowned(mesh, part); } bool updateMeshPart(scriptable::ScriptableMeshPointer mesh, scriptable::ScriptableMeshPartPointer part); @@ -49,10 +50,14 @@ public slots: QString exportModelToOBJ(const scriptable::ScriptableModel& in); private: + scriptable::ModelProviderPointer getModelProvider(QUuid uuid); + void jsThrowError(const QString& error); scriptable::MeshPointer getMeshPointer(scriptable::ScriptableMeshPointer meshProxy); scriptable::MeshPointer getMeshPointer(scriptable::ScriptableMesh& meshProxy); scriptable::MeshPointer getMeshPointer(const scriptable::ScriptableMesh& meshProxy); }; +Q_DECLARE_METATYPE(scriptable::ModelProviderPointer) + #endif // hifi_GraphicsScriptingInterface_h diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp index aabf83ff66..da582b2d21 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.cpp @@ -1,3 +1,114 @@ +// +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + #include "GraphicsScriptingUtil.h" +#include + +#include +#include +#include + +using buffer_helpers::glmVecToVariant; + Q_LOGGING_CATEGORY(graphics_scripting, "hifi.scripting.graphics") + +namespace scriptable { + +QVariant toVariant(const glm::mat4& mat4) { + QVector floats; + floats.resize(16); + memcpy(floats.data(), &mat4, sizeof(glm::mat4)); + QVariant v; + v.setValue>(floats); + return v; +}; + +QVariant toVariant(const Extents& box) { + return QVariantMap{ + { "center", glmVecToVariant(box.minimum + (box.size() / 2.0f)) }, + { "minimum", glmVecToVariant(box.minimum) }, + { "maximum", glmVecToVariant(box.maximum) }, + { "dimensions", glmVecToVariant(box.size()) }, + }; +} + +QVariant toVariant(const AABox& box) { + return QVariantMap{ + { "brn", glmVecToVariant(box.getCorner()) }, + { "tfl", glmVecToVariant(box.calcTopFarLeft()) }, + { "center", glmVecToVariant(box.calcCenter()) }, + { "minimum", glmVecToVariant(box.getMinimumPoint()) }, + { "maximum", glmVecToVariant(box.getMaximumPoint()) }, + { "dimensions", glmVecToVariant(box.getDimensions()) }, + }; +} + +QVariant toVariant(const gpu::Element& element) { + return QVariantMap{ + { "type", gpu::toString(element.getType()) }, + { "semantic", gpu::toString(element.getSemantic()) }, + { "dimension", gpu::toString(element.getDimension()) }, + { "scalarCount", element.getScalarCount() }, + { "byteSize", element.getSize() }, + { "BYTES_PER_ELEMENT", element.getSize() / element.getScalarCount() }, + }; +} + +QScriptValue jsBindCallback(QScriptValue value) { + if (value.isObject() && value.property("callback").isFunction()) { + // value is already a bound callback + return value; + } + auto engine = value.engine(); + auto context = engine ? engine->currentContext() : nullptr; + auto length = context ? context->argumentCount() : 0; + QScriptValue scope = context ? context->thisObject() : QScriptValue::NullValue; + QScriptValue method; +#ifdef SCRIPTABLE_MESH_DEBUG + qCInfo(graphics_scripting) << "jsBindCallback" << engine << length << scope.toQObject() << method.toString(); +#endif + + // find position in the incoming JS Function.arguments array (so we can test for the two-argument case) + for (int i = 0; context && i < length; i++) { + if (context->argument(i).strictlyEquals(value)) { + method = context->argument(i+1); + } + } + if (method.isFunction() || method.isString()) { + // interpret as `API.func(..., scope, function callback(){})` or `API.func(..., scope, "methodName")` + scope = value; + } else { + // interpret as `API.func(..., function callback(){})` + method = value; + } +#ifdef SCRIPTABLE_MESH_DEBUG + qCInfo(graphics_scripting) << "scope:" << scope.toQObject() << "method:" << method.toString(); +#endif + return ::makeScopedHandlerObject(scope, method); +} + +template +T this_qobject_cast(QScriptEngine* engine) { + auto context = engine ? engine->currentContext() : nullptr; + return qscriptvalue_cast(context ? context->thisObject() : QScriptValue::NullValue); +} +QString toDebugString(QObject* tmp) { + QString s; + QTextStream out(&s); + out << tmp; + return s; + // return QString("%0 (0x%1%2)") + // .arg(tmp ? tmp->metaObject()->className() : "QObject") + // .arg(qulonglong(tmp), 16, 16, QChar('0')) + // .arg(tmp && tmp->objectName().size() ? " name=" + tmp->objectName() : ""); +} +template QString toDebugString(std::shared_ptr tmp) { + return toDebugString(qobject_cast(tmp.get())); +} + +} diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h index 9b86ddee82..1ca62277ff 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingUtil.h @@ -8,46 +8,39 @@ #include #include #include +#include + +class Extents; +class AABox; +namespace gpu { + class Element; +} Q_DECLARE_LOGGING_CATEGORY(graphics_scripting) namespace scriptable { - // derive current context's C++ QObject (based on current JS "this" value) - template - T this_qobject_cast(QScriptEngine* engine) { - auto context = engine ? engine->currentContext() : nullptr; - return qscriptvalue_cast(context ? context->thisObject() : QScriptValue::NullValue); - } + QVariant toVariant(const Extents& box); + QVariant toVariant(const AABox& box); + QVariant toVariant(const gpu::Element& element); + QVariant toVariant(const glm::mat4& mat4); - // JS => QPointer - template - QPointer qpointer_qobject_cast(const QScriptValue& value) { - auto obj = value.toQObject(); -#ifdef SCRIPTABLE_MESH_DEBUG - qCInfo(graphics_scripting) << "qpointer_qobject_cast" << obj << value.toString(); -#endif - if (auto tmp = qobject_cast(obj)) { - return QPointer(tmp); - } - if (auto tmp = static_cast(obj)) { - return QPointer(tmp); - } - return nullptr; - } - inline QString toDebugString(QObject* tmp) { - return QString("%0 (0x%1%2)") - .arg(tmp ? tmp->metaObject()->className() : "QObject") - .arg(qulonglong(tmp), 16, 16, QChar('0')) - .arg(tmp && tmp->objectName().size() ? " name=" + tmp->objectName() : ""); - } - template QString toDebugString(std::shared_ptr tmp) { - return toDebugString(qobject_cast(tmp.get())); - } + // helper that automatically resolves Qt-signal-like scoped callbacks + // ... C++ side: `void MyClass::asyncMethod(..., QScriptValue callback)` + // ... JS side: + // * `API.asyncMethod(..., function(){})` + // * `API.asyncMethod(..., scope, function(){})` + // * `API.asyncMethod(..., scope, "methodName")` + QScriptValue jsBindCallback(QScriptValue callback); + + // cast engine->thisObject() => C++ class instance + template T this_qobject_cast(QScriptEngine* engine); + + QString toDebugString(QObject* tmp); + template QString toDebugString(std::shared_ptr tmp); // Helper for creating C++ > ScriptOwned JS instances // (NOTE: this also helps track in the code where we need to update later if switching to // std::shared_ptr's -- something currently non-trivial given mixed JS/C++ object ownership) - template - QPointer make_scriptowned(Rest... rest) { + template inline QPointer make_scriptowned(Rest... rest) { auto instance = QPointer(new T(rest...)); Q_ASSERT(instance && instance->parent()); return instance; diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp index 76741947fd..2cef4fef64 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp @@ -8,6 +8,7 @@ #include "Forward.h" #include "ScriptableMesh.h" +#include "ScriptableMeshPart.h" #include "BufferViewScripting.h" #include "GraphicsScriptingUtil.h" @@ -19,15 +20,11 @@ #include #include #include +#include #include // #define SCRIPTABLE_MESH_DEBUG 1 -scriptable::ScriptableMeshPart::ScriptableMeshPart(scriptable::ScriptableMeshPointer parentMesh, int partIndex) - : QObject(), parentMesh(parentMesh), partIndex(partIndex) { - setObjectName(QString("%1.part[%2]").arg(parentMesh ? parentMesh->objectName() : "").arg(partIndex)); -} - scriptable::ScriptableMesh::ScriptableMesh(const ScriptableMeshBase& other) : ScriptableMeshBase(other), QScriptable() { auto mesh = getMeshPointer(); @@ -41,74 +38,55 @@ scriptable::ScriptableMesh::ScriptableMesh(const ScriptableMeshBase& other) QVector scriptable::ScriptableMesh::getMeshParts() const { QVector out; - for (quint32 i = 0; i < getNumParts(); i++) { + for (glm::uint32 i = 0; i < getNumParts(); i++) { out << scriptable::make_scriptowned(getSelf(), i); } return out; } -quint32 scriptable::ScriptableMesh::getNumIndices() const { +glm::uint32 scriptable::ScriptableMesh::getNumIndices() const { if (auto mesh = getMeshPointer()) { - return (quint32)mesh->getNumIndices(); + return (glm::uint32)mesh->getNumIndices(); } return 0; } -quint32 scriptable::ScriptableMesh::getNumVertices() const { +glm::uint32 scriptable::ScriptableMesh::getNumVertices() const { if (auto mesh = getMeshPointer()) { - return (quint32)mesh->getNumVertices(); + return (glm::uint32)mesh->getNumVertices(); } return 0; } -QVector scriptable::ScriptableMesh::findNearbyIndices(const glm::vec3& origin, float epsilon) const { - QVector result; - if (auto mesh = getMeshPointer()) { - const auto& pos = buffer_helpers::getBufferView(mesh, gpu::Stream::POSITION); - const uint32_t num = (uint32_t)pos.getNumElements(); - for (uint32_t i = 0; i < num; i++) { - const auto& position = pos.get(i); - if (glm::distance(position, origin) <= epsilon) { - result << i; - } +QVector scriptable::ScriptableMesh::findNearbyVertexIndices(const glm::vec3& origin, float epsilon) const { + QVector result; + if (!isValid()) { + return result; + } + const auto epsilon2 = epsilon*epsilon; + buffer_helpers::forEach(buffer_helpers::mesh::getBufferView(getMeshPointer(), gpu::Stream::POSITION), [&](glm::uint32 index, const glm::vec3& position) { + if (glm::length2(position - origin) <= epsilon2) { + result << index; } - } + return true; + }); return result; } -QVector scriptable::ScriptableMesh::getIndices() const { - QVector result; +QVector scriptable::ScriptableMesh::getIndices() const { if (auto mesh = getMeshPointer()) { #ifdef SCRIPTABLE_MESH_DEBUG - qCDebug(graphics_scripting, "getTriangleIndices mesh %p", mesh.get()); + qCDebug(graphics_scripting, "getIndices mesh %p", mesh.get()); #endif - gpu::BufferView indexBufferView = mesh->getIndexBuffer(); - if (quint32 count = (quint32)indexBufferView.getNumElements()) { - result.resize(count); - switch(indexBufferView._element.getType()) { - case gpu::UINT32: - // memcpy(result.data(), buffer->getData(), result.size()*sizeof(quint32)); - for (quint32 i = 0; i < count; i++) { - result[i] = indexBufferView.get(i); - } - break; - case gpu::UINT16: - for (quint32 i = 0; i < count; i++) { - result[i] = indexBufferView.get(i); - } - break; - default: - assert(false); - Q_ASSERT(false); - } - } + return buffer_helpers::bufferToVector(mesh->getIndexBuffer()); } - return result; + return QVector(); } -quint32 scriptable::ScriptableMesh::getNumAttributes() const { + +glm::uint32 scriptable::ScriptableMesh::getNumAttributes() const { if (auto mesh = getMeshPointer()) { - return (quint32)mesh->getNumAttributes(); + return (glm::uint32)mesh->getNumAttributes() + 1; } return 0; } @@ -116,7 +94,7 @@ QVector scriptable::ScriptableMesh::getAttributeNames() const { QVector result; if (auto mesh = getMeshPointer()) { for (const auto& a : buffer_helpers::ATTRIBUTES.toStdMap()) { - auto bufferView = buffer_helpers::getBufferView(mesh, a.second); + auto bufferView = buffer_helpers::mesh::getBufferView(mesh, a.second); if (bufferView.getNumElements() > 0) { result << a.first; } @@ -125,22 +103,20 @@ QVector scriptable::ScriptableMesh::getAttributeNames() const { return result; } -QVariantMap scriptable::ScriptableMesh::getVertexAttributes(quint32 vertexIndex) const { - return getVertexAttributes(vertexIndex, getAttributeNames()); +QVariantMap scriptable::ScriptableMesh::getVertexAttributes(glm::uint32 vertexIndex) const { + if (!isValidIndex(vertexIndex)) { + return QVariantMap(); + } + return buffer_helpers::mesh::getVertexAttributes(getMeshPointer(), vertexIndex).toMap(); } -bool scriptable::ScriptableMesh::setVertexAttributes(quint32 vertexIndex, QVariantMap attributes) { - for (auto& a : buffer_helpers::gatherBufferViews(getMeshPointer())) { - const auto& name = a.first; - const auto& value = attributes.value(name); - if (value.isValid()) { - auto& view = a.second; - buffer_helpers::fromVariant(view, vertexIndex, value); - } else { - //qCDebug(graphics_scripting) << "(skipping) setVertexAttributes" << vertexIndex << name; +bool scriptable::ScriptableMesh::setVertexAttributes(glm::uint32 vertexIndex, const QVariantMap& attributes) { + for (const auto& name : attributes.keys()) { + if (!isValidIndex(vertexIndex, name)) { + return false; } } - return true; + return buffer_helpers::mesh::setVertexAttributes(getMeshPointer(), vertexIndex, attributes); } int scriptable::ScriptableMesh::_getSlotNumber(const QString& attributeName) const { @@ -150,112 +126,133 @@ int scriptable::ScriptableMesh::_getSlotNumber(const QString& attributeName) con return -1; } - -QVariantMap scriptable::ScriptableMesh::getMeshExtents() const { - auto mesh = getMeshPointer(); - auto box = mesh ? mesh->evalPartsBound(0, (int)mesh->getNumParts()) : AABox(); - return buffer_helpers::toVariant(box).toMap(); +QVariantMap scriptable::ScriptableMesh::getBufferFormats() const { + QVariantMap result; + for (const auto& a : buffer_helpers::ATTRIBUTES.toStdMap()) { + auto bufferView = buffer_helpers::mesh::getBufferView(getMeshPointer(), a.second); + result[a.first] = QVariantMap{ + { "slot", a.second }, + { "length", (quint32)bufferView.getNumElements() }, + { "byteLength", (quint32)bufferView._size }, + { "offset", (quint32) bufferView._offset }, + { "stride", (quint32)bufferView._stride }, + { "element", scriptable::toVariant(bufferView._element) }, + }; + } + return result; } -quint32 scriptable::ScriptableMesh::getNumParts() const { - if (auto mesh = getMeshPointer()) { - return (quint32)mesh->getNumParts(); +bool scriptable::ScriptableMesh::removeAttribute(const QString& attributeName) { + auto slot = isValid() ? _getSlotNumber(attributeName) : -1; + if (slot < 0) { + return 0; + } + if (slot == gpu::Stream::POSITION) { + context()->throwError("cannot remove .position attribute"); + return false; + } + if (buffer_helpers::mesh::getBufferView(getMeshPointer(), slot).getNumElements()) { + getMeshPointer()->removeAttribute(slot); + return true; + } + return false; +} + +glm::uint32 scriptable::ScriptableMesh::addAttribute(const QString& attributeName, const QVariant& defaultValue) { + auto slot = isValid() ? _getSlotNumber(attributeName) : -1; + if (slot < 0) { + return 0; + } + auto mesh = getMeshPointer(); + auto numVertices = getNumVertices(); + if (!getAttributeNames().contains(attributeName)) { + QVector values; + values.fill(defaultValue, numVertices); + mesh->addAttribute(slot, buffer_helpers::newFromVector(values, gpu::Stream::getDefaultElements()[slot])); + return values.size(); + } else { + auto bufferView = buffer_helpers::mesh::getBufferView(mesh, slot); + auto current = bufferView.getNumElements(); + if (current < numVertices) { + bufferView = buffer_helpers::resized(bufferView, numVertices); + for (glm::uint32 i = current; i < numVertices; i++) { + buffer_helpers::setValue(bufferView, i, defaultValue); + } + return numVertices - current; + } else if (current > numVertices) { + qCDebug(graphics_scripting) << QString("current=%1 > numVertices=%2").arg(current).arg(numVertices); + return 0; + } } return 0; } -QVariantMap scriptable::ScriptableMeshPart::scaleToFit(float unitScale) { - if (auto mesh = getMeshPointer()) { - auto box = mesh->evalPartsBound(0, (int)mesh->getNumParts()); - auto center = box.calcCenter(); - float maxDimension = glm::distance(box.getMaximumPoint(), box.getMinimumPoint()); - return scale(glm::vec3(unitScale / maxDimension), center); +glm::uint32 scriptable::ScriptableMesh::fillAttribute(const QString& attributeName, const QVariant& value) { + auto slot = isValid() ? _getSlotNumber(attributeName) : -1; + if (slot < 0) { + return 0; } - return {}; -} -QVariantMap scriptable::ScriptableMeshPart::translate(const glm::vec3& translation) { - return transform(glm::translate(translation)); -} -QVariantMap scriptable::ScriptableMeshPart::scale(const glm::vec3& scale, const glm::vec3& origin) { - if (auto mesh = getMeshPointer()) { - auto box = mesh->evalPartsBound(0, (int)mesh->getNumParts()); - glm::vec3 center = glm::isnan(origin.x) ? box.calcCenter() : origin; - return transform(glm::translate(center) * glm::scale(scale)); - } - return {}; -} -QVariantMap scriptable::ScriptableMeshPart::rotateDegrees(const glm::vec3& eulerAngles, const glm::vec3& origin) { - return rotate(glm::quat(glm::radians(eulerAngles)), origin); -} -QVariantMap scriptable::ScriptableMeshPart::rotate(const glm::quat& rotation, const glm::vec3& origin) { - if (auto mesh = getMeshPointer()) { - auto box = mesh->evalPartsBound(0, (int)mesh->getNumParts()); - glm::vec3 center = glm::isnan(origin.x) ? box.calcCenter() : origin; - return transform(glm::translate(center) * glm::toMat4(rotation)); - } - return {}; -} -QVariantMap scriptable::ScriptableMeshPart::transform(const glm::mat4& transform) { - if (auto mesh = getMeshPointer()) { - const auto& pos = buffer_helpers::getBufferView(mesh, gpu::Stream::POSITION); - const uint32_t num = (uint32_t)pos.getNumElements(); - for (uint32_t i = 0; i < num; i++) { - auto& position = pos.edit(i); - position = transform * glm::vec4(position, 1.0f); - } - return parentMesh->getMeshExtents(); - } - return {}; + auto mesh = getMeshPointer(); + auto numVertices = getNumVertices(); + QVector values; + values.fill(value, numVertices); + mesh->addAttribute(slot, buffer_helpers::newFromVector(values, gpu::Stream::getDefaultElements()[slot])); + return true; } -QVariantList scriptable::ScriptableMesh::getAttributeValues(const QString& attributeName) const { - QVariantList result; - auto slotNum = _getSlotNumber(attributeName); - if (slotNum >= 0) { - auto slot = (gpu::Stream::Slot)slotNum; - const auto& bufferView = buffer_helpers::getBufferView(getMeshPointer(), slot); - if (auto len = bufferView.getNumElements()) { - bool asArray = bufferView._element.getType() != gpu::FLOAT; - for (quint32 i = 0; i < len; i++) { - result << buffer_helpers::toVariant(bufferView, i, asArray, attributeName.toStdString().c_str()); - } - } - } - return result; -} -QVariantMap scriptable::ScriptableMesh::getVertexAttributes(quint32 vertexIndex, QVector names) const { - QVariantMap result; +QVariantMap scriptable::ScriptableMesh::getMeshExtents() const { auto mesh = getMeshPointer(); - if (!mesh || vertexIndex >= getNumVertices()) { + auto box = mesh ? mesh->evalPartsBound(0, (int)mesh->getNumParts()) : AABox(); + return scriptable::toVariant(box).toMap(); +} + +glm::uint32 scriptable::ScriptableMesh::getNumParts() const { + if (auto mesh = getMeshPointer()) { + return (glm::uint32)mesh->getNumParts(); + } + return 0; +} + +QVariantList scriptable::ScriptableMesh::queryVertexAttributes(QVariant selector) const { + QVariantList result; + const auto& attributeName = selector.toString(); + if (!isValidIndex(0, attributeName)) { return result; } - for (const auto& a : buffer_helpers::ATTRIBUTES.toStdMap()) { - auto name = a.first; - if (!names.contains(name)) { - continue; - } - auto slot = a.second; - const gpu::BufferView& bufferView = buffer_helpers::getBufferView(mesh, slot); - if (vertexIndex < bufferView.getNumElements()) { - bool asArray = bufferView._element.getType() != gpu::FLOAT; - result[name] = buffer_helpers::toVariant(bufferView, vertexIndex, asArray, name.toStdString().c_str()); - } + auto slotNum = _getSlotNumber(attributeName); + const auto& bufferView = buffer_helpers::mesh::getBufferView(getMeshPointer(), static_cast(slotNum)); + glm::uint32 numElements = bufferView.getNumElements(); + for (glm::uint32 i = 0; i < numElements; i++) { + result << buffer_helpers::getValue(bufferView, i, qUtf8Printable(attributeName)); } return result; } -quint32 scriptable::ScriptableMesh::mapAttributeValues(QScriptValue _callback) { +QVariant scriptable::ScriptableMesh::getVertexProperty(glm::uint32 vertexIndex, const QString& attributeName) const { + if (!isValidIndex(vertexIndex, attributeName)) { + return QVariant(); + } + auto slotNum = _getSlotNumber(attributeName); + const auto& bufferView = buffer_helpers::mesh::getBufferView(getMeshPointer(), static_cast(slotNum)); + return buffer_helpers::getValue(bufferView, vertexIndex, qUtf8Printable(attributeName)); +} + +bool scriptable::ScriptableMesh::setVertexProperty(glm::uint32 vertexIndex, const QString& attributeName, const QVariant& value) { + if (!isValidIndex(vertexIndex, attributeName)) { + return false; + } + auto slotNum = _getSlotNumber(attributeName); + const auto& bufferView = buffer_helpers::mesh::getBufferView(getMeshPointer(), static_cast(slotNum)); + return buffer_helpers::setValue(bufferView, vertexIndex, value); +} + +glm::uint32 scriptable::ScriptableMesh::forEachVertex(QScriptValue _callback) { auto mesh = getMeshPointer(); if (!mesh) { return 0; } auto scopedHandler = jsBindCallback(_callback); - // input buffers - gpu::BufferView positions = mesh->getVertexBuffer(); - - const auto nPositions = positions.getNumElements(); - // destructure so we can still invoke callback scoped, but with a custom signature (obj, i, jsMesh) auto scope = scopedHandler.property("scope"); auto callback = scopedHandler.property("callback"); @@ -264,205 +261,104 @@ quint32 scriptable::ScriptableMesh::mapAttributeValues(QScriptValue _callback) { return 0; } auto meshPart = js ? js->toScriptValue(getSelf()) : QScriptValue::NullValue; -#ifdef SCRIPTABLE_MESH_DEBUG - qCInfo(graphics_scripting) << "mapAttributeValues" << mesh.get() << js->currentContext()->thisObject().toQObject(); -#endif - auto obj = js->newObject(); - auto attributeViews = buffer_helpers::gatherBufferViews(mesh, { "normal", "color" }); - uint32_t i = 0; - for (; i < nPositions; i++) { - for (const auto& a : attributeViews) { - bool asArray = a.second._element.getType() != gpu::FLOAT; - obj.setProperty(a.first, bufferViewElementToScriptValue(js, a.second, i, asArray, a.first.toStdString().c_str())); - } - auto result = callback.call(scope, { obj, i, meshPart }); + int numProcessed = 0; + buffer_helpers::mesh::forEachVertex(mesh, [&](glm::uint32 index, const QVariantMap& values) { + auto result = callback.call(scope, { js->toScriptValue(values), index, meshPart }); if (js->hasUncaughtException()) { js->currentContext()->throwValue(js->uncaughtException()); - return i; + return false; } + numProcessed++; + return true; + }); + return numProcessed; +} + +glm::uint32 scriptable::ScriptableMesh::updateVertexAttributes(QScriptValue _callback) { + auto mesh = getMeshPointer(); + if (!mesh) { + return 0; + } + auto scopedHandler = jsBindCallback(_callback); + + // destructure so we can still invoke callback scoped, but with a custom signature (obj, i, jsMesh) + auto scope = scopedHandler.property("scope"); + auto callback = scopedHandler.property("callback"); + auto js = engine() ? engine() : scopedHandler.engine(); // cache value to avoid resolving each iteration + if (!js) { + return 0; + } + auto meshPart = js ? js->toScriptValue(getSelf()) : QScriptValue::NullValue; + int numProcessed = 0; + auto attributeViews = buffer_helpers::mesh::getAllBufferViews(mesh); + buffer_helpers::mesh::forEachVertex(mesh, [&](glm::uint32 index, const QVariantMap& values) { + auto obj = js->toScriptValue(values); + auto result = callback.call(scope, { obj, index, meshPart }); + if (js->hasUncaughtException()) { + js->currentContext()->throwValue(js->uncaughtException()); + return false; + } if (result.isBool() && !result.toBool()) { // bail without modifying data if user explicitly returns false - continue; + return true; } if (result.isObject() && !result.strictlyEquals(obj)) { // user returned a new object (ie: instead of modifying input properties) obj = result; } - for (const auto& a : attributeViews) { const auto& attribute = obj.property(a.first); - auto& view = a.second; if (attribute.isValid()) { - bufferViewElementFromScriptValue(attribute, view, i); + buffer_helpers::setValue(a.second, index, attribute.toVariant()); } } + numProcessed++; + return true; + }); + return numProcessed; +} + +// protect against user scripts sending bogus values +bool scriptable::ScriptableMesh::isValidIndex(glm::uint32 vertexIndex, const QString& attributeName) const { + if (!isValid()) { + return false; } - return i; -} - -quint32 scriptable::ScriptableMeshPart::mapAttributeValues(QScriptValue callback) { - return parentMesh ? parentMesh->mapAttributeValues(callback) : 0; -} - -bool scriptable::ScriptableMeshPart::replaceMeshData(scriptable::ScriptableMeshPartPointer src, const QVector& attributeNames) { - auto target = getMeshPointer(); - auto source = src ? src->getMeshPointer() : nullptr; - if (!target || !source) { + const auto last = getNumVertices() - 1; + if (vertexIndex > last) { if (context()) { - context()->throwError("ScriptableMeshPart::replaceMeshData -- expected dest and src to be valid mesh proxy pointers"); - } else { - qCWarning(graphics_scripting) << "ScriptableMeshPart::replaceMeshData -- expected dest and src to be valid mesh proxy pointers"; + context()->throwError(QString("vertexIndex=%1 out of range (firstVertexIndex=%2, lastVertexIndex=%3)").arg(vertexIndex).arg(0).arg(last)); } return false; } - - QVector attributes = attributeNames.isEmpty() ? src->parentMesh->getAttributeNames() : attributeNames; - - qCInfo(graphics_scripting) << "ScriptableMeshPart::replaceMeshData -- " << - "source:" << QString::fromStdString(source->displayName) << - "target:" << QString::fromStdString(target->displayName) << - "attributes:" << attributes; - - // remove attributes only found on target mesh, unless user has explicitly specified the relevant attribute names - if (attributeNames.isEmpty()) { - auto attributeViews = buffer_helpers::gatherBufferViews(target); - for (const auto& a : attributeViews) { - auto slot = buffer_helpers::ATTRIBUTES[a.first]; - if (!attributes.contains(a.first)) { -#ifdef SCRIPTABLE_MESH_DEBUG - qCInfo(graphics_scripting) << "ScriptableMesh::replaceMeshData -- pruning target attribute" << a.first << slot; -#endif - target->removeAttribute(slot); + if (!attributeName.isEmpty()) { + auto slotNum = _getSlotNumber(attributeName); + if (slotNum < 0) { + if (context()) { + context()->throwError(QString("invalid attributeName=%1").arg(attributeName)); } + return false; } - } - - target->setVertexBuffer(buffer_helpers::clone(source->getVertexBuffer())); - target->setIndexBuffer(buffer_helpers::clone(source->getIndexBuffer())); - target->setPartBuffer(buffer_helpers::clone(source->getPartBuffer())); - - for (const auto& a : attributes) { - auto slot = buffer_helpers::ATTRIBUTES[a]; - if (slot == gpu::Stream::POSITION) { - continue; - } -#ifdef SCRIPTABLE_MESH_DEBUG - auto& before = target->getAttributeBuffer(slot); -#endif - auto& input = source->getAttributeBuffer(slot); - if (input.getNumElements() == 0) { -#ifdef SCRIPTABLE_MESH_DEBUG - qCInfo(graphics_scripting) << "ScriptableMeshPart::replaceMeshData buffer is empty -- pruning" << a << slot; -#endif - target->removeAttribute(slot); - } else { -#ifdef SCRIPTABLE_MESH_DEBUG - if (before.getNumElements() == 0) { - qCInfo(graphics_scripting) << "ScriptableMeshPart::replaceMeshData target buffer is empty -- adding" << a << slot; - } else { - qCInfo(graphics_scripting) << "ScriptableMeshPart::replaceMeshData target buffer exists -- updating" << a << slot; + auto view = buffer_helpers::mesh::getBufferView(getMeshPointer(), static_cast(slotNum)); + if (vertexIndex >= view.getNumElements()) { + if (context()) { + context()->throwError(QString("vertexIndex=%1 out of range (attribute=%2, numElements=%3)").arg(vertexIndex).arg(attributeName).arg(view.getNumElements())); } -#endif - target->addAttribute(slot, buffer_helpers::clone(input)); + return false; } -#ifdef SCRIPTABLE_MESH_DEBUG - auto& after = target->getAttributeBuffer(slot); - qCInfo(graphics_scripting) << "ScriptableMeshPart::replaceMeshData" << a << slot << before.getNumElements() << " -> " << after.getNumElements(); -#endif - } - - - return true; -} - -bool scriptable::ScriptableMeshPart::dedupeVertices(float epsilon) { - auto mesh = getMeshPointer(); - if (!mesh) { - return false; - } - auto positions = mesh->getVertexBuffer(); - auto numPositions = positions.getNumElements(); - const auto epsilon2 = epsilon*epsilon; - - QVector uniqueVerts; - uniqueVerts.reserve((int)numPositions); - QMap remapIndices; - - for (quint32 i = 0; i < numPositions; i++) { - const quint32 numUnique = uniqueVerts.size(); - const auto& position = positions.get(i); - bool unique = true; - for (quint32 j = 0; j < numUnique; j++) { - if (glm::length2(uniqueVerts[j] - position) <= epsilon2) { - remapIndices[i] = j; - unique = false; - break; - } - } - if (unique) { - uniqueVerts << position; - remapIndices[i] = numUnique; - } - } - - qCInfo(graphics_scripting) << "//VERTS before" << numPositions << "after" << uniqueVerts.size(); - - auto indices = mesh->getIndexBuffer(); - auto numIndices = indices.getNumElements(); - auto esize = indices._element.getSize(); - QVector newIndices; - newIndices.reserve((int)numIndices); - for (quint32 i = 0; i < numIndices; i++) { - quint32 index = esize == 4 ? indices.get(i) : indices.get(i); - if (remapIndices.contains(index)) { - newIndices << remapIndices[index]; - } else { - qCInfo(graphics_scripting) << i << index << "!remapIndices[index]"; - } - } - - mesh->setIndexBuffer(buffer_helpers::fromVector(newIndices, { gpu::SCALAR, gpu::UINT32, gpu::INDEX })); - mesh->setVertexBuffer(buffer_helpers::fromVector(uniqueVerts, { gpu::VEC3, gpu::FLOAT, gpu::XYZ })); - - auto attributeViews = buffer_helpers::gatherBufferViews(mesh); - quint32 numUniqueVerts = uniqueVerts.size(); - for (const auto& a : attributeViews) { - auto& view = a.second; - auto slot = buffer_helpers::ATTRIBUTES[a.first]; - if (slot == gpu::Stream::POSITION) { - continue; - } - auto newView = buffer_helpers::resize(view, numUniqueVerts); -#ifdef SCRIPTABLE_MESH_DEBUG - qCInfo(graphics_scripting) << "ScriptableMeshPart::dedupeVertices" << a.first << slot << view.getNumElements(); - qCInfo(graphics_scripting) << a.first << "before: #" << view.getNumElements() << "after: #" << newView.getNumElements(); -#endif - quint32 numElements = (quint32)view.getNumElements(); - for (quint32 i = 0; i < numElements; i++) { - quint32 fromVertexIndex = i; - quint32 toVertexIndex = remapIndices.contains(fromVertexIndex) ? remapIndices[fromVertexIndex] : fromVertexIndex; - buffer_helpers::fromVariant( - newView, toVertexIndex, - buffer_helpers::toVariant(view, fromVertexIndex, false, "dedupe") - ); - } - mesh->addAttribute(slot, newView); } return true; } -scriptable::ScriptableMeshPointer scriptable::ScriptableMesh::cloneMesh(bool recalcNormals) { + +scriptable::ScriptableMeshPointer scriptable::ScriptableMesh::cloneMesh() { auto mesh = getMeshPointer(); if (!mesh) { qCInfo(graphics_scripting) << "ScriptableMesh::cloneMesh -- !meshPointer"; return nullptr; } - auto clone = buffer_helpers::cloneMesh(mesh); + auto clone = buffer_helpers::mesh::clone(mesh); - if (recalcNormals) { - buffer_helpers::recalculateNormals(clone); - } auto meshPointer = scriptable::make_scriptowned(provider, model, clone, nullptr); return scriptable::ScriptableMeshPointer(meshPointer); } @@ -505,114 +401,6 @@ scriptable::ScriptableMesh::~ScriptableMesh() { strongMesh.reset(); } -QString scriptable::ScriptableMeshPart::toOBJ() { - if (!getMeshPointer()) { - if (context()) { - context()->throwError(QString("null mesh")); - } else { - qCWarning(graphics_scripting) << "null mesh"; - } - return QString(); - } - return writeOBJToString({ getMeshPointer() }); -} - -namespace { - template - QScriptValue qObjectToScriptValue(QScriptEngine* engine, const T& object) { - if (!object) { - return QScriptValue::NullValue; - } - return engine->newQObject(object, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater); - } - - QScriptValue meshPointerToScriptValue(QScriptEngine* engine, const scriptable::ScriptableMeshPointer& in) { - return qObjectToScriptValue(engine, in); - } - QScriptValue meshPartPointerToScriptValue(QScriptEngine* engine, const scriptable::ScriptableMeshPartPointer& in) { - return qObjectToScriptValue(engine, in); - } - QScriptValue modelPointerToScriptValue(QScriptEngine* engine, const scriptable::ScriptableModelPointer& in) { - return qObjectToScriptValue(engine, in); - } - - void meshPointerFromScriptValue(const QScriptValue& value, scriptable::ScriptableMeshPointer &out) { - out = scriptable::qpointer_qobject_cast(value); - } - void modelPointerFromScriptValue(const QScriptValue& value, scriptable::ScriptableModelPointer &out) { - out = scriptable::qpointer_qobject_cast(value); - } - void meshPartPointerFromScriptValue(const QScriptValue& value, scriptable::ScriptableMeshPartPointer &out) { - out = scriptable::qpointer_qobject_cast(value); - } - - QScriptValue qVectorUInt32ToScriptValue(QScriptEngine* engine, const QVector& vector) { - return qScriptValueFromSequence(engine, vector); - } - - void qVectorUInt32FromScriptValue(const QScriptValue& array, QVector& result) { - qScriptValueToSequence(array, result); - } - - QVector metaTypeIds{ - qRegisterMetaType("uint32"), - qRegisterMetaType("scriptable::uint32"), - qRegisterMetaType>(), - qRegisterMetaType>("QVector"), - qRegisterMetaType(), - qRegisterMetaType(), - qRegisterMetaType(), - }; -} - -namespace scriptable { - bool registerMetaTypes(QScriptEngine* engine) { - qScriptRegisterSequenceMetaType>(engine); - qScriptRegisterSequenceMetaType>(engine); - qScriptRegisterSequenceMetaType>(engine); - - qScriptRegisterMetaType(engine, qVectorUInt32ToScriptValue, qVectorUInt32FromScriptValue); - qScriptRegisterMetaType(engine, modelPointerToScriptValue, modelPointerFromScriptValue); - qScriptRegisterMetaType(engine, meshPointerToScriptValue, meshPointerFromScriptValue); - qScriptRegisterMetaType(engine, meshPartPointerToScriptValue, meshPartPointerFromScriptValue); - - return metaTypeIds.size(); - } - - // callback helper that lets C++ method signatures remain simple (ie: taking a single callback argument) while - // still supporting extended Qt signal-like (scope, "methodName") and (scope, function(){}) "this" binding conventions - QScriptValue jsBindCallback(QScriptValue value) { - if (value.isObject() && value.property("callback").isFunction()) { - // value is already a bound callback - return value; - } - auto engine = value.engine(); - auto context = engine ? engine->currentContext() : nullptr; - auto length = context ? context->argumentCount() : 0; - QScriptValue scope = context ? context->thisObject() : QScriptValue::NullValue; - QScriptValue method; -#ifdef SCRIPTABLE_MESH_DEBUG - qCInfo(graphics_scripting) << "jsBindCallback" << engine << length << scope.toQObject() << method.toString(); -#endif - - // find position in the incoming JS Function.arguments array (so we can test for the two-argument case) - for (int i = 0; context && i < length; i++) { - if (context->argument(i).strictlyEquals(value)) { - method = context->argument(i+1); - } - } - if (method.isFunction() || method.isString()) { - // interpret as `API.func(..., scope, function callback(){})` or `API.func(..., scope, "methodName")` - scope = value; - } else { - // interpret as `API.func(..., function callback(){})` - method = value; - } -#ifdef SCRIPTABLE_MESH_DEBUG - qCInfo(graphics_scripting) << "scope:" << scope.toQObject() << "method:" << method.toString(); -#endif - return ::makeScopedHandlerObject(scope, method); - } -} #include "ScriptableMesh.moc" + diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h index 48ef6c3a81..16393de8c7 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h @@ -25,18 +25,23 @@ #include "GraphicsScriptingUtil.h" +#include + namespace scriptable { class ScriptableMesh : public ScriptableMeshBase, QScriptable { Q_OBJECT public: - Q_PROPERTY(uint32 numParts READ getNumParts) - Q_PROPERTY(uint32 numAttributes READ getNumAttributes) - Q_PROPERTY(uint32 numVertices READ getNumVertices) - Q_PROPERTY(uint32 numIndices READ getNumIndices) + Q_PROPERTY(glm::uint32 numParts READ getNumParts) + Q_PROPERTY(glm::uint32 numAttributes READ getNumAttributes) + Q_PROPERTY(glm::uint32 numVertices READ getNumVertices) + Q_PROPERTY(glm::uint32 numIndices READ getNumIndices) Q_PROPERTY(QVector attributeNames READ getAttributeNames) Q_PROPERTY(QVector parts READ getMeshParts) - Q_PROPERTY(bool valid READ hasValidMesh) + Q_PROPERTY(bool valid READ isValid) Q_PROPERTY(bool strong READ hasValidStrongMesh) + Q_PROPERTY(QVariantMap extents READ getMeshExtents) + Q_PROPERTY(QVariantMap bufferFormats READ getBufferFormats) + QVariantMap getBufferFormats() const; operator const ScriptableMeshBase*() const { return (qobject_cast(this)); } @@ -48,116 +53,49 @@ namespace scriptable { ScriptableMesh(const ScriptableMesh& other) : ScriptableMeshBase(other), QScriptable() {}; virtual ~ScriptableMesh(); - Q_INVOKABLE const scriptable::ScriptableModelPointer getParentModel() const { return qobject_cast(model); } - Q_INVOKABLE const scriptable::MeshPointer getOwnedMeshPointer() const { return strongMesh; } + const scriptable::MeshPointer getOwnedMeshPointer() const { return strongMesh; } scriptable::ScriptableMeshPointer getSelf() const { return const_cast(this); } - bool hasValidMesh() const { return !weakMesh.expired(); } + bool isValid() const { return !weakMesh.expired(); } bool hasValidStrongMesh() const { return (bool)strongMesh; } - public slots: - uint32 getNumParts() const; - uint32 getNumVertices() const; - uint32 getNumAttributes() const; - uint32 getNumIndices() const; + glm::uint32 getNumParts() const; + glm::uint32 getNumVertices() const; + glm::uint32 getNumAttributes() const; + glm::uint32 getNumIndices() const; QVector getAttributeNames() const; QVector getMeshParts() const; - - QVariantMap getVertexAttributes(uint32 vertexIndex) const; - QVariantMap getVertexAttributes(uint32 vertexIndex, QVector attributes) const; - - QVector getIndices() const; - QVector findNearbyIndices(const glm::vec3& origin, float epsilon = 1e-6) const; QVariantMap getMeshExtents() const; - bool setVertexAttributes(uint32 vertexIndex, QVariantMap attributes); - QVariantList getAttributeValues(const QString& attributeName) const; - - int _getSlotNumber(const QString& attributeName) const; - - scriptable::ScriptableMeshPointer cloneMesh(bool recalcNormals = false); - public: + // TODO: remove Q_INVOKABLE (curently exposed for debugging ) + Q_INVOKABLE int _getSlotNumber(const QString& attributeName) const; operator bool() const { return !weakMesh.expired(); } public slots: + const scriptable::ScriptableModelPointer getParentModel() const { return qobject_cast(model); } + QVector getIndices() const; + QVector findNearbyVertexIndices(const glm::vec3& origin, float epsilon = 1e-6) const; + + glm::uint32 addAttribute(const QString& attributeName, const QVariant& defaultValue = QVariant()); + glm::uint32 fillAttribute(const QString& attributeName, const QVariant& value); + bool removeAttribute(const QString& attributeName); + + QVariantList queryVertexAttributes(QVariant selector) const; + QVariantMap getVertexAttributes(glm::uint32 vertexIndex) const; + bool setVertexAttributes(glm::uint32 vertexIndex, const QVariantMap& attributeValues); + + QVariant getVertexProperty(glm::uint32 vertexIndex, const QString& attributeName) const; + bool setVertexProperty(glm::uint32 vertexIndex, const QString& attributeName, const QVariant& value); + + scriptable::ScriptableMeshPointer cloneMesh(); + // QScriptEngine-specific wrappers - uint32 mapAttributeValues(QScriptValue callback); + glm::uint32 updateVertexAttributes(QScriptValue callback); + glm::uint32 forEachVertex(QScriptValue callback); + bool isValidIndex(glm::uint32 vertexIndex, const QString& attributeName = QString()) const; }; - // TODO: part-specific wrapper for working with raw geometries - class ScriptableMeshPart : public QObject, QScriptable { - Q_OBJECT - public: - Q_PROPERTY(uint32 partIndex MEMBER partIndex CONSTANT) - Q_PROPERTY(int numElementsPerFace MEMBER _elementsPerFace CONSTANT) - Q_PROPERTY(QString topology MEMBER _topology CONSTANT) - - Q_PROPERTY(uint32 numFaces READ getNumFaces) - Q_PROPERTY(uint32 numAttributes READ getNumAttributes) - Q_PROPERTY(uint32 numVertices READ getNumVertices) - Q_PROPERTY(uint32 numIndices READ getNumIndices) - Q_PROPERTY(QVector attributeNames READ getAttributeNames) - - ScriptableMeshPart(scriptable::ScriptableMeshPointer parentMesh, int partIndex); - ScriptableMeshPart& operator=(const ScriptableMeshPart& view) { parentMesh=view.parentMesh; return *this; }; - ScriptableMeshPart(const ScriptableMeshPart& other) : QObject(other.parent()), QScriptable(), parentMesh(other.parentMesh), partIndex(other.partIndex) {} - - public slots: - scriptable::ScriptableMeshPointer getParentMesh() const { return parentMesh; } - uint32 getNumAttributes() const { return parentMesh ? parentMesh->getNumAttributes() : 0; } - uint32 getNumVertices() const { return parentMesh ? parentMesh->getNumVertices() : 0; } - uint32 getNumIndices() const { return parentMesh ? parentMesh->getNumIndices() : 0; } - uint32 getNumFaces() const { return parentMesh ? parentMesh->getNumIndices() / _elementsPerFace : 0; } - QVector getAttributeNames() const { return parentMesh ? parentMesh->getAttributeNames() : QVector(); } - QVector getFace(uint32 faceIndex) const { - if (parentMesh && faceIndex + 2 < parentMesh->getNumIndices()) { - return parentMesh->getIndices().mid(faceIndex*3, 3); - } - return QVector(); - } - QVariantMap scaleToFit(float unitScale); - QVariantMap translate(const glm::vec3& translation); - QVariantMap scale(const glm::vec3& scale, const glm::vec3& origin = glm::vec3(NAN)); - QVariantMap rotateDegrees(const glm::vec3& eulerAngles, const glm::vec3& origin = glm::vec3(NAN)); - QVariantMap rotate(const glm::quat& rotation, const glm::vec3& origin = glm::vec3(NAN)); - QVariantMap transform(const glm::mat4& transform); - - bool dedupeVertices(float epsilon = 1e-6); - bool recalculateNormals() { return buffer_helpers::recalculateNormals(getMeshPointer()); } - - bool replaceMeshData(scriptable::ScriptableMeshPartPointer source, const QVector& attributeNames = QVector()); - scriptable::ScriptableMeshPartPointer cloneMeshPart(bool recalcNormals = false) { - if (parentMesh) { - if (auto clone = parentMesh->cloneMesh(recalcNormals)) { - return clone->getMeshParts().value(partIndex); - } - } - return nullptr; - } - QString toOBJ(); - - public slots: - // QScriptEngine-specific wrappers - uint32 mapAttributeValues(QScriptValue callback); - - public: - scriptable::ScriptableMeshPointer parentMesh; - uint32 partIndex; - protected: - int _elementsPerFace{ 3 }; - QString _topology{ "triangles" }; - scriptable::MeshPointer getMeshPointer() const { return parentMesh ? parentMesh->getMeshPointer() : nullptr; } - }; - - // callback helper that lets C++ method signatures remain simple (ie: taking a single callback argument) while - // still supporting extended Qt signal-like (scope, "methodName") and (scope, function(){}) "this" binding conventions - QScriptValue jsBindCallback(QScriptValue callback); - - // derive a corresponding C++ class instance from the current script engine's thisObject - template T this_qobject_cast(QScriptEngine* engine); } Q_DECLARE_METATYPE(scriptable::ScriptableMeshPointer) Q_DECLARE_METATYPE(QVector) -Q_DECLARE_METATYPE(scriptable::ScriptableMeshPartPointer) -Q_DECLARE_METATYPE(QVector) -Q_DECLARE_METATYPE(scriptable::uint32) -Q_DECLARE_METATYPE(QVector) +Q_DECLARE_METATYPE(glm::uint32) +Q_DECLARE_METATYPE(QVector) diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp new file mode 100644 index 0000000000..0d4bb7bdc5 --- /dev/null +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp @@ -0,0 +1,438 @@ +// +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Forward.h" + +#include "ScriptableMeshPart.h" + +#include "BufferViewScripting.h" +#include "GraphicsScriptingUtil.h" +#include "OBJWriter.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QString scriptable::ScriptableMeshPart::toOBJ() { + if (!getMeshPointer()) { + if (context()) { + context()->throwError(QString("null mesh")); + } else { + qCWarning(graphics_scripting) << "null mesh"; + } + return QString(); + } + return writeOBJToString({ getMeshPointer() }); +} + + +bool scriptable::ScriptableMeshPart::isValidIndex(glm::uint32 vertexIndex, const QString& attributeName) const { + return isValid() && parentMesh->isValidIndex(vertexIndex, attributeName); +} + +bool scriptable::ScriptableMeshPart::setVertexAttributes(glm::uint32 vertexIndex, const QVariantMap& attributes) { + if (!isValidIndex(vertexIndex)) { + return false; + } + return buffer_helpers::mesh::setVertexAttributes(getMeshPointer(), vertexIndex, attributes); +} + +QVariantMap scriptable::ScriptableMeshPart::getVertexAttributes(glm::uint32 vertexIndex) const { + if (!isValidIndex(vertexIndex)) { + return QVariantMap(); + } + return parentMesh->getVertexAttributes(vertexIndex); +} + +bool scriptable::ScriptableMeshPart::setVertexProperty(glm::uint32 vertexIndex, const QString& attributeName, const QVariant& value) { + if (!isValidIndex(vertexIndex, attributeName)) { + return false; + } + auto slotNum = parentMesh->_getSlotNumber(attributeName); + const auto& bufferView = buffer_helpers::mesh::getBufferView(getMeshPointer(), static_cast(slotNum)); + return buffer_helpers::setValue(bufferView, vertexIndex, value); +} + +QVariant scriptable::ScriptableMeshPart::getVertexProperty(glm::uint32 vertexIndex, const QString& attributeName) const { + if (!isValidIndex(vertexIndex, attributeName)) { + return false; + } + return parentMesh->getVertexProperty(vertexIndex, attributeName); +} + +QVariantList scriptable::ScriptableMeshPart::queryVertexAttributes(QVariant selector) const { + QVariantList result; + if (!isValid()) { + return result; + } + return parentMesh->queryVertexAttributes(selector); +} + +glm::uint32 scriptable::ScriptableMeshPart::forEachVertex(QScriptValue _callback) { + // TODO: limit to vertices within the part's indexed range? + return isValid() ? parentMesh->forEachVertex(_callback) : 0; +} + +glm::uint32 scriptable::ScriptableMeshPart::updateVertexAttributes(QScriptValue _callback) { + // TODO: limit to vertices within the part's indexed range? + return isValid() ? parentMesh->updateVertexAttributes(_callback) : 0; +} + +bool scriptable::ScriptableMeshPart::replaceMeshPartData(scriptable::ScriptableMeshPartPointer src, const QVector& attributeNames) { + auto target = getMeshPointer(); + auto source = src ? src->getMeshPointer() : nullptr; + if (!target || !source) { + if (context()) { + context()->throwError("ScriptableMeshPart::replaceMeshData -- expected dest and src to be valid mesh proxy pointers"); + } else { + qCWarning(graphics_scripting) << "ScriptableMeshPart::replaceMeshData -- expected dest and src to be valid mesh proxy pointers"; + } + return false; + } + + QVector attributes = attributeNames.isEmpty() ? src->parentMesh->getAttributeNames() : attributeNames; + + qCInfo(graphics_scripting) << "ScriptableMeshPart::replaceMeshData -- " << + "source:" << QString::fromStdString(source->displayName) << + "target:" << QString::fromStdString(target->displayName) << + "attributes:" << attributes; + + // remove attributes only found on target mesh, unless user has explicitly specified the relevant attribute names + if (attributeNames.isEmpty()) { + auto attributeViews = buffer_helpers::mesh::getAllBufferViews(target); + for (const auto& a : attributeViews) { + auto slot = buffer_helpers::ATTRIBUTES[a.first]; + if (!attributes.contains(a.first)) { +#ifdef SCRIPTABLE_MESH_DEBUG + qCInfo(graphics_scripting) << "ScriptableMesh::replaceMeshData -- pruning target attribute" << a.first << slot; +#endif + target->removeAttribute(slot); + } + } + } + + target->setVertexBuffer(buffer_helpers::clone(source->getVertexBuffer())); + target->setIndexBuffer(buffer_helpers::clone(source->getIndexBuffer())); + target->setPartBuffer(buffer_helpers::clone(source->getPartBuffer())); + + for (const auto& a : attributes) { + auto slot = buffer_helpers::ATTRIBUTES[a]; + if (slot == gpu::Stream::POSITION) { + continue; + } +#ifdef SCRIPTABLE_MESH_DEBUG + auto& before = target->getAttributeBuffer(slot); +#endif + auto& input = source->getAttributeBuffer(slot); + if (input.getNumElements() == 0) { +#ifdef SCRIPTABLE_MESH_DEBUG + qCInfo(graphics_scripting) << "ScriptableMeshPart::replaceMeshData buffer is empty -- pruning" << a << slot; +#endif + target->removeAttribute(slot); + } else { +#ifdef SCRIPTABLE_MESH_DEBUG + if (before.getNumElements() == 0) { + qCInfo(graphics_scripting) << "ScriptableMeshPart::replaceMeshData target buffer is empty -- adding" << a << slot; + } else { + qCInfo(graphics_scripting) << "ScriptableMeshPart::replaceMeshData target buffer exists -- updating" << a << slot; + } +#endif + target->addAttribute(slot, buffer_helpers::clone(input)); + } +#ifdef SCRIPTABLE_MESH_DEBUG + auto& after = target->getAttributeBuffer(slot); + qCInfo(graphics_scripting) << "ScriptableMeshPart::replaceMeshData" << a << slot << before.getNumElements() << " -> " << after.getNumElements(); +#endif + } + + + return true; +} + +bool scriptable::ScriptableMeshPart::dedupeVertices(float epsilon) { + auto mesh = getMeshPointer(); + if (!mesh) { + return false; + } + auto positions = mesh->getVertexBuffer(); + auto numPositions = positions.getNumElements(); + const auto epsilon2 = epsilon*epsilon; + + QVector uniqueVerts; + uniqueVerts.reserve((int)numPositions); + QMap remapIndices; + + for (glm::uint32 i = 0; i < numPositions; i++) { + const glm::uint32 numUnique = uniqueVerts.size(); + const auto& position = positions.get(i); + bool unique = true; + for (glm::uint32 j = 0; j < numUnique; j++) { + if (glm::length2(uniqueVerts[j] - position) <= epsilon2) { + remapIndices[i] = j; + unique = false; + break; + } + } + if (unique) { + uniqueVerts << position; + remapIndices[i] = numUnique; + } + } + + qCInfo(graphics_scripting) << "//VERTS before" << numPositions << "after" << uniqueVerts.size(); + + auto indices = mesh->getIndexBuffer(); + auto numIndices = indices.getNumElements(); + auto esize = indices._element.getSize(); + QVector newIndices; + newIndices.reserve((int)numIndices); + for (glm::uint32 i = 0; i < numIndices; i++) { + glm::uint32 index = esize == 4 ? indices.get(i) : indices.get(i); + if (remapIndices.contains(index)) { + newIndices << remapIndices[index]; + } else { + qCInfo(graphics_scripting) << i << index << "!remapIndices[index]"; + } + } + + mesh->setIndexBuffer(buffer_helpers::newFromVector(newIndices, { gpu::SCALAR, gpu::UINT32, gpu::INDEX })); + mesh->setVertexBuffer(buffer_helpers::newFromVector(uniqueVerts, gpu::Element::VEC3F_XYZ)); + + auto attributeViews = buffer_helpers::mesh::getAllBufferViews(mesh); + glm::uint32 numUniqueVerts = uniqueVerts.size(); + for (const auto& a : attributeViews) { + auto& view = a.second; + auto slot = buffer_helpers::ATTRIBUTES[a.first]; + if (slot == gpu::Stream::POSITION) { + continue; + } + auto newView = buffer_helpers::resized(view, numUniqueVerts); +#ifdef SCRIPTABLE_MESH_DEBUG + qCInfo(graphics_scripting) << "ScriptableMeshPart::dedupeVertices" << a.first << slot << view.getNumElements(); + qCInfo(graphics_scripting) << a.first << "before: #" << view.getNumElements() << "after: #" << newView.getNumElements(); +#endif + glm::uint32 numElements = (glm::uint32)view.getNumElements(); + for (glm::uint32 i = 0; i < numElements; i++) { + glm::uint32 fromVertexIndex = i; + glm::uint32 toVertexIndex = remapIndices.contains(fromVertexIndex) ? remapIndices[fromVertexIndex] : fromVertexIndex; + buffer_helpers::setValue(newView, toVertexIndex, buffer_helpers::getValue(view, fromVertexIndex, "dedupe")); + } + mesh->addAttribute(slot, newView); + } + return true; +} + +bool scriptable::ScriptableMeshPart::removeAttribute(const QString& attributeName) { + return isValid() && parentMesh->removeAttribute(attributeName); +} + +glm::uint32 scriptable::ScriptableMeshPart::addAttribute(const QString& attributeName, const QVariant& defaultValue) { + return isValid() ? parentMesh->addAttribute(attributeName, defaultValue): 0; +} + +glm::uint32 scriptable::ScriptableMeshPart::fillAttribute(const QString& attributeName, const QVariant& value) { + return isValid() ? parentMesh->fillAttribute(attributeName, value) : 0; +} + +QVector scriptable::ScriptableMeshPart::findNearbyPartVertexIndices(const glm::vec3& origin, float epsilon) const { + QSet result; + if (!isValid()) { + return result.toList().toVector(); + } + auto mesh = getMeshPointer(); + auto offset = getFirstVertexIndex(); + auto numIndices = getNumIndices(); + auto vertexBuffer = mesh->getVertexBuffer(); + auto indexBuffer = mesh->getIndexBuffer(); + const auto epsilon2 = epsilon*epsilon; + + for (glm::uint32 i = 0; i < numIndices; i++) { + auto vertexIndex = buffer_helpers::getValue(indexBuffer, offset + i); + if (result.contains(vertexIndex)) { + continue; + } + const auto& position = buffer_helpers::getValue(vertexBuffer, vertexIndex); + if (glm::length2(position - origin) <= epsilon2) { + result << vertexIndex; + } + } + return result.toList().toVector(); +} + +scriptable::ScriptableMeshPartPointer scriptable::ScriptableMeshPart::cloneMeshPart() { + if (parentMesh) { + if (auto clone = parentMesh->cloneMesh()) { + return clone->getMeshParts().value(partIndex); + } + } + return nullptr; +} + +QVariantMap scriptable::ScriptableMeshPart::scaleToFit(float unitScale) { + if (auto mesh = getMeshPointer()) { + auto box = mesh->evalPartsBound(0, (int)mesh->getNumParts()); + auto center = box.calcCenter(); + float maxDimension = glm::distance(box.getMaximumPoint(), box.getMinimumPoint()); + return scale(glm::vec3(unitScale / maxDimension), center); + } + return {}; +} +QVariantMap scriptable::ScriptableMeshPart::translate(const glm::vec3& translation) { + return transform(glm::translate(translation)); +} +QVariantMap scriptable::ScriptableMeshPart::scale(const glm::vec3& scale, const glm::vec3& origin) { + if (auto mesh = getMeshPointer()) { + auto box = mesh->evalPartsBound(0, (int)mesh->getNumParts()); + glm::vec3 center = glm::isnan(origin.x) ? box.calcCenter() : origin; + return transform(glm::translate(center) * glm::scale(scale)); + } + return {}; +} +QVariantMap scriptable::ScriptableMeshPart::rotateDegrees(const glm::vec3& eulerAngles, const glm::vec3& origin) { + return rotate(glm::quat(glm::radians(eulerAngles)), origin); +} +QVariantMap scriptable::ScriptableMeshPart::rotate(const glm::quat& rotation, const glm::vec3& origin) { + if (auto mesh = getMeshPointer()) { + auto box = mesh->evalPartsBound(0, (int)mesh->getNumParts()); + glm::vec3 center = glm::isnan(origin.x) ? box.calcCenter() : origin; + return transform(glm::translate(center) * glm::toMat4(rotation)); + } + return {}; +} +QVariantMap scriptable::ScriptableMeshPart::transform(const glm::mat4& transform) { + if (auto mesh = getMeshPointer()) { + const auto& pos = buffer_helpers::mesh::getBufferView(mesh, gpu::Stream::POSITION); + const glm::uint32 num = (glm::uint32)pos.getNumElements(); + for (glm::uint32 i = 0; i < num; i++) { + auto& position = pos.edit(i); + position = transform * glm::vec4(position, 1.0f); + } + return parentMesh->getMeshExtents(); + } + return {}; +} + + +scriptable::ScriptableMeshPart::ScriptableMeshPart(scriptable::ScriptableMeshPointer parentMesh, int partIndex) + : QObject(), parentMesh(parentMesh), partIndex(partIndex) { + setObjectName(QString("%1.part[%2]").arg(parentMesh ? parentMesh->objectName() : "").arg(partIndex)); +} + +QVector scriptable::ScriptableMeshPart::getIndices() const { + if (auto mesh = getMeshPointer()) { +#ifdef SCRIPTABLE_MESH_DEBUG + qCDebug(graphics_scripting, "getIndices mesh %p", mesh.get()); +#endif + return buffer_helpers::bufferToVector(mesh->getIndexBuffer()); + } + return QVector(); +} + +bool scriptable::ScriptableMeshPart::setFirstVertexIndex( glm::uint32 vertexIndex) { + if (!isValidIndex(vertexIndex)) { + return false; + } + auto& part = getMeshPointer()->getPartBuffer().edit(partIndex); + part._startIndex = vertexIndex; + return true; +} + +bool scriptable::ScriptableMeshPart::setBaseVertexIndex( glm::uint32 vertexIndex) { + if (!isValidIndex(vertexIndex)) { + return false; + } + auto& part = getMeshPointer()->getPartBuffer().edit(partIndex); + part._baseVertex = vertexIndex; + return true; +} + +bool scriptable::ScriptableMeshPart::setLastVertexIndex( glm::uint32 vertexIndex) { + if (!isValidIndex(vertexIndex) || vertexIndex <= getFirstVertexIndex()) { + return false; + } + auto& part = getMeshPointer()->getPartBuffer().edit(partIndex); + part._numIndices = vertexIndex - part._startIndex; + return true; +} + +bool scriptable::ScriptableMeshPart::setIndices(const QVector& indices) { + if (!isValid()) { + return false; + } + glm::uint32 len = indices.size(); + if (len != getNumVertices()) { + context()->throwError(QString("setIndices: currently new indicies must be assign 1:1 across old indicies (indicies.size()=%1, numIndices=%2)") + .arg(len).arg(getNumIndices())); + } + auto mesh = getMeshPointer(); + auto indexBuffer = mesh->getIndexBuffer(); + + // first loop to validate all indices are valid + for (glm::uint32 i = 0; i < len; i++) { + if (!isValidIndex(indices.at(i))) { + return false; + } + } + const auto first = getFirstVertexIndex(); + // now actually apply them + for (glm::uint32 i = 0; i < len; i++) { + buffer_helpers::setValue(indexBuffer, first + i, indices.at(i)); + } + return true; +} + +const graphics::Mesh::Part& scriptable::ScriptableMeshPart::getMeshPart() const { + static const graphics::Mesh::Part invalidPart; + if (!isValid()) { + return invalidPart; + } + return getMeshPointer()->getPartBuffer().get(partIndex); +} + +bool scriptable::ScriptableMeshPart::setTopology(graphics::Mesh::Topology topology) { + if (!isValid()) { + return false; + } + auto& part = getMeshPointer()->getPartBuffer().edit(partIndex); + if (topology == graphics::Mesh::Topology::POINTS || + topology == graphics::Mesh::Topology::LINES || + topology == graphics::Mesh::Topology::TRIANGLES) { + part._topology = topology; + return true; + } + return false; +} + +glm::uint32 scriptable::ScriptableMeshPart::getTopologyLength() const { + switch(getTopology()) { + case graphics::Mesh::Topology::POINTS: return 1; + case graphics::Mesh::Topology::LINES: return 2; + case graphics::Mesh::Topology::TRIANGLES: return 3; + default: qCDebug(graphics_scripting) << "getTopologyLength -- unrecognized topology" << getTopology(); + } + return 0; +} + +QVector scriptable::ScriptableMeshPart::getFace(glm::uint32 faceIndex) const { + if (faceIndex < getNumFaces()) { + return getIndices().mid(faceIndex * getTopologyLength(), getTopologyLength()); + } + return QVector(); +} + +QVariantMap scriptable::ScriptableMeshPart::getPartExtents() const { + graphics::Box box; + if (auto mesh = getMeshPointer()) { + box = mesh->evalPartBound(partIndex); + } + return scriptable::toVariant(box).toMap(); +} diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h new file mode 100644 index 0000000000..4ef0465ca3 --- /dev/null +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h @@ -0,0 +1,106 @@ +// +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once + +#include "ScriptableMesh.h" + +namespace scriptable { + class ScriptableMeshPart : public QObject, QScriptable { + Q_OBJECT + Q_PROPERTY(bool valid READ isValid) + Q_PROPERTY(glm::uint32 partIndex MEMBER partIndex CONSTANT) + Q_PROPERTY(glm::uint32 firstVertexIndex READ getFirstVertexIndex WRITE setFirstVertexIndex) + Q_PROPERTY(glm::uint32 baseVertexIndex READ getBaseVertexIndex WRITE setBaseVertexIndex) + Q_PROPERTY(glm::uint32 lastVertexIndex READ getLastVertexIndex WRITE setLastVertexIndex) + Q_PROPERTY(int numVerticesPerFace READ getTopologyLength) + Q_PROPERTY(graphics::Mesh::Topology topology READ getTopology WRITE setTopology) + + Q_PROPERTY(glm::uint32 numFaces READ getNumFaces) + Q_PROPERTY(glm::uint32 numAttributes READ getNumAttributes) + Q_PROPERTY(glm::uint32 numVertices READ getNumVertices) + Q_PROPERTY(glm::uint32 numIndices READ getNumIndices WRITE setNumIndices) + + Q_PROPERTY(QVariantMap extents READ getPartExtents) + Q_PROPERTY(QVector attributeNames READ getAttributeNames) + Q_PROPERTY(QVariantMap bufferFormats READ getBufferFormats) + + public: + ScriptableMeshPart(scriptable::ScriptableMeshPointer parentMesh, int partIndex); + ScriptableMeshPart& operator=(const ScriptableMeshPart& view) { parentMesh=view.parentMesh; return *this; }; + ScriptableMeshPart(const ScriptableMeshPart& other) : QObject(other.parent()), QScriptable(), parentMesh(other.parentMesh), partIndex(other.partIndex) {} + bool isValid() const { auto mesh = getMeshPointer(); return mesh && partIndex < mesh->getNumParts(); } + + public slots: + QVector getIndices() const; + bool setIndices(const QVector& indices); + QVector findNearbyPartVertexIndices(const glm::vec3& origin, float epsilon = 1e-6) const; + QVariantList queryVertexAttributes(QVariant selector) const; + QVariantMap getVertexAttributes(glm::uint32 vertexIndex) const; + bool setVertexAttributes(glm::uint32 vertexIndex, const QVariantMap& attributeValues); + + QVariant getVertexProperty(glm::uint32 vertexIndex, const QString& attributeName) const; + bool setVertexProperty(glm::uint32 vertexIndex, const QString& attributeName, const QVariant& attributeValues); + + QVector getFace(glm::uint32 faceIndex) const; + + QVariantMap scaleToFit(float unitScale); + QVariantMap translate(const glm::vec3& translation); + QVariantMap scale(const glm::vec3& scale, const glm::vec3& origin = glm::vec3(NAN)); + QVariantMap rotateDegrees(const glm::vec3& eulerAngles, const glm::vec3& origin = glm::vec3(NAN)); + QVariantMap rotate(const glm::quat& rotation, const glm::vec3& origin = glm::vec3(NAN)); + QVariantMap transform(const glm::mat4& transform); + + glm::uint32 addAttribute(const QString& attributeName, const QVariant& defaultValue = QVariant()); + glm::uint32 fillAttribute(const QString& attributeName, const QVariant& value); + bool removeAttribute(const QString& attributeName); + bool dedupeVertices(float epsilon = 1e-6); + + scriptable::ScriptableMeshPointer getParentMesh() const { return parentMesh; } + + bool replaceMeshPartData(scriptable::ScriptableMeshPartPointer source, const QVector& attributeNames = QVector()); + scriptable::ScriptableMeshPartPointer cloneMeshPart(); + + QString toOBJ(); + + // QScriptEngine-specific wrappers + glm::uint32 updateVertexAttributes(QScriptValue callback); + glm::uint32 forEachVertex(QScriptValue callback); + + bool isValidIndex(glm::uint32 vertexIndex, const QString& attributeName = QString()) const; + public: + scriptable::ScriptableMeshPointer parentMesh; + glm::uint32 partIndex; + + protected: + const graphics::Mesh::Part& getMeshPart() const; + scriptable::MeshPointer getMeshPointer() const { return parentMesh ? parentMesh->getMeshPointer() : nullptr; } + QVariantMap getBufferFormats() { return isValid() ? parentMesh->getBufferFormats() : QVariantMap(); } + glm::uint32 getNumAttributes() const { return isValid() ? parentMesh->getNumAttributes() : 0; } + + bool setTopology(graphics::Mesh::Topology topology); + graphics::Mesh::Topology getTopology() const { return isValid() ? getMeshPart()._topology : graphics::Mesh::Topology(); } + glm::uint32 getTopologyLength() const; + glm::uint32 getNumIndices() const { return isValid() ? getMeshPart()._numIndices : 0; } + bool setNumIndices(glm::uint32 numIndices) { return setLastVertexIndex(getFirstVertexIndex() + numIndices); } + glm::uint32 getNumVertices() const { return isValid() ? parentMesh->getNumVertices() : 0; } + + bool setFirstVertexIndex(glm::uint32 vertexIndex); + glm::uint32 getFirstVertexIndex() const { return isValid() ? getMeshPart()._startIndex : 0; } + bool setLastVertexIndex(glm::uint32 vertexIndex); + glm::uint32 getLastVertexIndex() const { return isValid() ? getFirstVertexIndex() + getNumIndices() - 1 : 0; } + bool setBaseVertexIndex(glm::uint32 vertexIndex); + glm::uint32 getBaseVertexIndex() const { return isValid() ? getMeshPart()._baseVertex : 0; } + + glm::uint32 getNumFaces() const { return getNumIndices() / getTopologyLength(); } + QVector getAttributeNames() const { return isValid() ? parentMesh->getAttributeNames() : QVector(); } + QVariantMap getPartExtents() const; + }; +} + +Q_DECLARE_METATYPE(scriptable::ScriptableMeshPartPointer) +Q_DECLARE_METATYPE(QVector) diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp index 8ceb7de6a2..36322d170d 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.cpp @@ -58,7 +58,7 @@ scriptable::ScriptableModelPointer scriptable::ScriptableModel::cloneModel(const scriptable::ScriptableModelPointer clone = scriptable::ScriptableModelPointer(new scriptable::ScriptableModel(*this)); clone->meshes.clear(); for (const auto &mesh : getConstMeshes()) { - auto cloned = mesh->cloneMesh(options.value("recalculateNormals").toBool()); + auto cloned = mesh->cloneMesh(); if (auto tmp = qobject_cast(cloned)) { clone->meshes << *tmp; tmp->deleteLater(); // schedule our copy for cleanup @@ -70,8 +70,8 @@ scriptable::ScriptableModelPointer scriptable::ScriptableModel::cloneModel(const } -const QVector scriptable::ScriptableModel::getConstMeshes() const { - QVector out; +const scriptable::ScriptableMeshes scriptable::ScriptableModel::getConstMeshes() const { + scriptable::ScriptableMeshes out; for (const auto& mesh : meshes) { const scriptable::ScriptableMesh* m = qobject_cast(&mesh); if (!m) { @@ -85,8 +85,8 @@ const QVector scriptable::ScriptableModel::ge return out; } -QVector scriptable::ScriptableModel::getMeshes() { - QVector out; +scriptable::ScriptableMeshes scriptable::ScriptableModel::getMeshes() { + scriptable::ScriptableMeshes out; for (auto& mesh : meshes) { scriptable::ScriptableMesh* m = qobject_cast(&mesh); if (!m) { @@ -100,9 +100,10 @@ QVector scriptable::ScriptableModel::getMeshe return out; } -quint32 scriptable::ScriptableModel::mapAttributeValues(QScriptValue callback) { - quint32 result = 0; - QVector in = getMeshes(); +#if 0 +glm::uint32 scriptable::ScriptableModel::forEachVertexAttribute(QScriptValue callback) { + glm::uint32 result = 0; + scriptable::ScriptableMeshes in = getMeshes(); if (in.size()) { foreach (scriptable::ScriptableMeshPointer meshProxy, in) { result += meshProxy->mapAttributeValues(callback); @@ -110,5 +111,6 @@ quint32 scriptable::ScriptableModel::mapAttributeValues(QScriptValue callback) { } return result; } +#endif #include "ScriptableModel.moc" diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h index 4ed1cc9554..d821f1224d 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h @@ -13,35 +13,34 @@ class QScriptValue; namespace scriptable { + + using ScriptableMeshes = QVector; class ScriptableModel : public ScriptableModelBase { Q_OBJECT - public: Q_PROPERTY(QUuid objectID MEMBER objectID CONSTANT) - Q_PROPERTY(uint32 numMeshes READ getNumMeshes) - Q_PROPERTY(QVector meshes READ getMeshes) + Q_PROPERTY(glm::uint32 numMeshes READ getNumMeshes) + Q_PROPERTY(ScriptableMeshes meshes READ getMeshes) + public: ScriptableModel(QObject* parent = nullptr) : ScriptableModelBase(parent) {} ScriptableModel(const ScriptableModel& other) : ScriptableModelBase(other) {} ScriptableModel(const ScriptableModelBase& other) : ScriptableModelBase(other) {} ScriptableModel& operator=(const ScriptableModelBase& view) { ScriptableModelBase::operator=(view); return *this; } - - Q_INVOKABLE scriptable::ScriptableModelPointer cloneModel(const QVariantMap& options = QVariantMap()); - // TODO: in future accessors for these could go here - // QVariantMap shapes; - // QVariantMap materials; - // QVariantMap armature; - - QVector getMeshes(); - const QVector getConstMeshes() const; operator scriptable::ScriptableModelBasePointer() { return QPointer(qobject_cast(this)); } + ScriptableMeshes getMeshes(); + const ScriptableMeshes getConstMeshes() const; + public slots: + scriptable::ScriptableModelPointer cloneModel(const QVariantMap& options = QVariantMap()); + QString toString() const; // QScriptEngine-specific wrappers - Q_INVOKABLE uint32 mapAttributeValues(QScriptValue callback); - Q_INVOKABLE QString toString() const; - Q_INVOKABLE uint32 getNumMeshes() { return meshes.size(); } + //glm::uint32 forEachMeshVertexAttribute(QScriptValue callback); + protected: + glm::uint32 getNumMeshes() { return meshes.size(); } + }; } diff --git a/libraries/graphics/src/graphics/BufferViewHelpers.cpp b/libraries/graphics/src/graphics/BufferViewHelpers.cpp index 29dcbd58e3..46bd39bb45 100644 --- a/libraries/graphics/src/graphics/BufferViewHelpers.cpp +++ b/libraries/graphics/src/graphics/BufferViewHelpers.cpp @@ -15,11 +15,11 @@ #include #include "Geometry.h" +#include "GpuHelpers.h" -#include #include +#include -#include #include #include @@ -32,11 +32,10 @@ namespace { QLoggingCategory bufferhelper_logging{ "hifi.bufferview" }; } - const std::array buffer_helpers::XYZW = { { "x", "y", "z", "w" } }; const std::array buffer_helpers::ZERO123 = { { "0", "1", "2", "3" } }; -gpu::BufferView buffer_helpers::getBufferView(graphics::MeshPointer mesh, gpu::Stream::Slot slot) { +gpu::BufferView buffer_helpers::mesh::getBufferView(const graphics::MeshPointer& mesh, gpu::Stream::Slot slot) { return slot == gpu::Stream::POSITION ? mesh->getVertexBuffer() : mesh->getAttributeBuffer(slot); } @@ -56,21 +55,13 @@ QMap buffer_helpers::ATTRIBUTES{ namespace { - bool boundsCheck(const gpu::BufferView& view, quint32 index) { + bool boundsCheck(const gpu::BufferView& view, glm::uint32 index) { const auto byteLength = view._element.getSize(); return ( index < view.getNumElements() && index * byteLength < (view._size - 1) * byteLength ); } - - template QVariant getBufferViewElement(const gpu::BufferView& view, quint32 index, bool asArray = false) { - return buffer_helpers::glmVecToVariant(view.get(index), asArray); - } - - template void setBufferViewElement(const gpu::BufferView& view, quint32 index, const QVariant& v) { - view.edit(index) = buffer_helpers::glmVecFromVariant(v); - } } void buffer_helpers::packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint32& packedNormal, glm::uint32& packedTangent) { @@ -99,127 +90,23 @@ void buffer_helpers::packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, g packedTangent = tangentStruct.pack; } -bool buffer_helpers::fromVariant(const gpu::BufferView& view, quint32 index, const QVariant& v) { - const auto& element = view._element; - const auto vecN = element.getScalarCount(); - const auto dataType = element.getType(); - const auto byteLength = element.getSize(); - const auto BYTES_PER_ELEMENT = byteLength / vecN; - - if (BYTES_PER_ELEMENT == 1) { - switch(vecN) { - case 2: setBufferViewElement(view, index, v); return true; - case 3: setBufferViewElement(view, index, v); return true; - case 4: { - if (element == gpu::Element::COLOR_RGBA_32) { - glm::uint32 rawColor;// = glm::packUnorm4x8(glm::vec4(glmVecFromVariant(v), 0.0f)); - glm::uint32 unused; - packNormalAndTangent(glmVecFromVariant(v), glm::vec3(), rawColor, unused); - view.edit(index) = rawColor; - return true; - } else if (element == gpu::Element::VEC4F_NORMALIZED_XYZ10W2) { - glm::uint32 packedNormal;// = glm::packSnorm3x10_1x2(glm::vec4(glmVecFromVariant(v), 0.0f)); - glm::uint32 unused; - packNormalAndTangent(glm::vec3(), glmVecFromVariant(v), unused, packedNormal); - view.edit(index) = packedNormal; - return true; - } - setBufferViewElement(view, index, v); return true; - } - } - } else if (BYTES_PER_ELEMENT == 2) { - if (dataType == gpu::HALF) { - switch(vecN) { - case 2: view.edit(index) = glm::packSnorm2x8(glmVecFromVariant(v)); return true; - case 4: view.edit(index) = glm::packSnorm4x8(glmVecFromVariant(v)); return true; - default: return false; - } - } - switch(vecN) { - case 2: setBufferViewElement(view, index, v); return true; - case 3: setBufferViewElement(view, index, v); return true; - case 4: setBufferViewElement(view, index, v); return true; - } - } else if (BYTES_PER_ELEMENT == 4) { - if (dataType == gpu::FLOAT) { - switch(vecN) { - case 2: setBufferViewElement(view, index, v); return true; - case 3: setBufferViewElement(view, index, v); return true; - case 4: setBufferViewElement(view, index, v); return true; - } - } else { - switch(vecN) { - case 2: setBufferViewElement(view, index, v); return true; - case 3: setBufferViewElement(view, index, v); return true; - case 4: setBufferViewElement(view, index, v); return true; +namespace { + template + glm::uint32 forEachGlmVec(const gpu::BufferView& view, std::function func) { + QVector result; + const glm::uint32 num = (glm::uint32)view.getNumElements(); + glm::uint32 i = 0; + for (; i < num; i++) { + if (!func(i, view.get(i))) { + break; } } + return i; } - return false; } -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(); - const auto dataType = element.getType(); - const auto byteLength = element.getSize(); - const auto BYTES_PER_ELEMENT = byteLength / vecN; - Q_ASSERT(index < view.getNumElements()); - if (!boundsCheck(view, index)) { - // sanity checks - auto byteOffset = index * vecN * BYTES_PER_ELEMENT; - auto maxByteOffset = (view._size - 1) * vecN * BYTES_PER_ELEMENT; - if (byteOffset > maxByteOffset) { - qDebug() << "toVariant -- byteOffset out of range " << byteOffset << " < " << maxByteOffset; - qDebug() << "toVariant -- index: " << index << "numElements" << view.getNumElements(); - qDebug() << "toVariant -- vecN: " << vecN << "byteLength" << byteLength << "BYTES_PER_ELEMENT" << BYTES_PER_ELEMENT; - } - Q_ASSERT(byteOffset <= maxByteOffset); - } - if (BYTES_PER_ELEMENT == 1) { - switch(vecN) { - case 2: return getBufferViewElement(view, index, asArray); - case 3: return getBufferViewElement(view, index, asArray); - case 4: { - if (element == gpu::Element::COLOR_RGBA_32) { - auto rawColor = view.get(index); - return glmVecToVariant(glm::vec3(glm::unpackUnorm4x8(rawColor))); - } else if (element == gpu::Element::VEC4F_NORMALIZED_XYZ10W2) { - auto packedNormal = view.get(index); - return glmVecToVariant(glm::vec3(glm::unpackSnorm3x10_1x2(packedNormal))); - } - - return getBufferViewElement(view, index, asArray); - } - } - } else if (BYTES_PER_ELEMENT == 2) { - if (dataType == gpu::HALF) { - switch(vecN) { - case 2: return glmVecToVariant(glm::vec2(glm::unpackSnorm2x8(view.get(index)))); - case 4: return glmVecToVariant(glm::vec4(glm::unpackSnorm4x8(view.get(index)))); - } - } - switch(vecN) { - case 2: return getBufferViewElement(view, index, asArray); - case 3: return getBufferViewElement(view, index, asArray); - case 4: return getBufferViewElement(view, index, asArray); - } - } else if (BYTES_PER_ELEMENT == 4) { - if (dataType == gpu::FLOAT) { - switch(vecN) { - case 2: return getBufferViewElement(view, index, asArray); - case 3: return getBufferViewElement(view, index, asArray); - case 4: return getBufferViewElement(view, index, asArray); - } - } else { - switch(vecN) { - case 2: return getBufferViewElement(view, index, asArray); - case 3: return getBufferViewElement(view, index, asArray); - case 4: return getBufferViewElement(view, index, asArray); - } - } - } - return QVariant(); +template<> glm::uint32 buffer_helpers::forEach(const gpu::BufferView& view, std::function func) { + return forEachGlmVec(view, func); } template @@ -256,59 +143,63 @@ const T buffer_helpers::glmVecFromVariant(const QVariant& v) { } else { value = list.value(i).toFloat(); } -#ifdef DEBUG_BUFFERVIEW_SCRIPTING +#ifdef DEBUG_BUFFERVIEW_HELPERS if (value != value) { // NAN qWarning().nospace()<< "vec" << len << "." << components[i] << " NAN received from script.... " << v.toString(); } -#endif +#endif result[i] = value; } return result; } +// QVector => BufferView template -gpu::BufferView buffer_helpers::fromVector(const QVector& elements, const gpu::Element& elementType) { +gpu::BufferView buffer_helpers::newFromVector(const QVector& elements, const gpu::Element& elementType) { auto vertexBuffer = std::make_shared(elements.size() * sizeof(T), (gpu::Byte*)elements.data()); return { vertexBuffer, 0, vertexBuffer->getSize(),sizeof(T), elementType }; } namespace { template - gpu::BufferView _fromVector(const QVector& elements, const gpu::Element& elementType) { + gpu::BufferView bufferViewFromVector(const QVector& elements, const gpu::Element& elementType) { auto vertexBuffer = std::make_shared(elements.size() * sizeof(T), (gpu::Byte*)elements.data()); return { vertexBuffer, 0, vertexBuffer->getSize(),sizeof(T), elementType }; } } - -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 GpuVec4ToGlm; -template struct GpuScalarToGlm; +template<> gpu::BufferView buffer_helpers::newFromVector(const QVector& elements, const gpu::Element& elementType) { return bufferViewFromVector(elements, elementType); } +template<> gpu::BufferView buffer_helpers::newFromVector(const QVector& elements, const gpu::Element& elementType) { return bufferViewFromVector(elements, elementType); } +template<> gpu::BufferView buffer_helpers::newFromVector( const QVector& elements, const gpu::Element& elementType) { return bufferViewFromVector(elements, elementType); } +template<> gpu::BufferView buffer_helpers::newFromVector(const QVector& elements, const gpu::Element& elementType) { return bufferViewFromVector(elements, elementType); } +template<> gpu::BufferView buffer_helpers::newFromVector(const QVector& elements, const gpu::Element& elementType) { return bufferViewFromVector(elements, elementType); } struct GpuToGlmAdapter { - static float error(const QString& name, const gpu::BufferView& view, quint32 index, const char *hint) { - qDebug() << QString("GpuToGlmAdapter:: unhandled type=%1(element=%2) size=%3(per=%4) vec%5 hint=%6 #%7") + static float error(const QString& name, const gpu::BufferView& view, glm::uint32 index, const char *hint) { + qDebug() << QString("GpuToGlmAdapter:: unhandled type=%1(element=%2) size=%3(location=%4,per=%5) vec%6 hint=%7 #%8 %9 %10") .arg(name) - .arg(view._element.getType()) + .arg(gpu::toString(view._element.getType())) .arg(view._element.getSize()) + .arg(view._element.getLocationSize()) .arg(view._element.getSize() / view._element.getScalarCount()) .arg(view._element.getScalarCount()) .arg(hint) - .arg(view.getNumElements()); + .arg(view.getNumElements()) + .arg(gpu::toString(view._element.getSemantic())) + .arg(gpu::toString(view._element.getDimension())); Q_ASSERT(false); assert(false); return NAN; } }; +#define CHECK_SIZE(T) if (view._element.getSize() != sizeof(T)) { qDebug() << "invalid elementSize" << hint << view._element.getSize() << "expected:" << sizeof(T); break; } + template struct GpuScalarToGlm : GpuToGlmAdapter { - static T get(const gpu::BufferView& view, quint32 index, const char *hint) { switch(view._element.getType()) { + static T get(const gpu::BufferView& view, glm::uint32 index, const char *hint) { +#ifdef DEBUG_BUFFERVIEW_HELPERS + if(!boundsCheck(view, index))return T(error("GpuScalarToGlm::get::out of bounds", view, index, hint)); +#endif + 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); @@ -316,44 +207,101 @@ template struct GpuScalarToGlm : GpuToGlmAdapter { case gpu::INT16: return view.get(index); case gpu::INT8: return view.get(index); case gpu::FLOAT: return view.get(index); - case gpu::HALF: return T(glm::unpackSnorm1x8(view.get(index))); + case gpu::HALF: return T(glm::unpackHalf1x16(view.get(index))); + case gpu::NUINT8: return T(glm::unpackUnorm1x8(view.get(index))); default: break; - } return T(error("GpuScalarToGlm", view, index, hint)); + } return T(error("GpuScalarToGlm::get", view, index, hint)); + } + static bool set(const gpu::BufferView& view, glm::uint32 index, const T& value, const char *hint) { +#ifdef DEBUG_BUFFERVIEW_HELPERS + if(!boundsCheck(view, index))return T(error("GpuScalarToGlm::set::out of bounds", view, index, hint)); +#endif + switch(view._element.getType()) { + case gpu::UINT32: view.edit(index) = value; return true; + case gpu::UINT16: view.edit(index) = value; return true; + case gpu::UINT8: view.edit(index) = value; return true; + case gpu::INT32: view.edit(index) = value; return true; + case gpu::INT16: view.edit(index) = value; return true; + case gpu::INT8: view.edit(index) = value; return true; + case gpu::FLOAT: view.edit(index) = value; return true; + case gpu::HALF: view.edit(index) = glm::packHalf1x16(value); return true; + case gpu::NUINT8: view.edit(index) = glm::packUnorm1x8(value); return true; + default: break; + } error("GpuScalarToGlm::set", view, index, hint); return false; } }; -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); - case gpu::INT32: return view.get(index); - case gpu::INT16: return view.get(index); - case gpu::INT8: return view.get(index); - case gpu::FLOAT: return view.get(index); - case gpu::HALF: return glm::unpackSnorm2x8(view.get(index)); - default: break; - } return T(error("GpuVec2ToGlm", view, index, hint)); }}; +template struct GpuVec2ToGlm : GpuToGlmAdapter { static T get(const gpu::BufferView& view, glm::uint32 index, const char *hint) { +#ifdef DEBUG_BUFFERVIEW_HELPERS + if(!boundsCheck(view, index))return T(error("GpuVec2ToGlm::get::out of bounds", view, index, hint)); +#endif + 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); + case gpu::INT32: return view.get(index); + case gpu::INT16: return view.get(index); + case gpu::INT8: return view.get(index); + case gpu::FLOAT: return view.get(index); + case gpu::HALF: CHECK_SIZE(glm::uint32); return glm::unpackHalf2x16(view.get(index)); + case gpu::NUINT16: CHECK_SIZE(glm::uint32); return glm::unpackUnorm2x16(view.get(index)); + case gpu::NUINT8: CHECK_SIZE(glm::uint16); return glm::unpackUnorm2x8(view.get(index)); + default: break; + } return T(error("GpuVec2ToGlm::get", view, index, hint)); } + static bool set(const gpu::BufferView& view, glm::uint32 index, const T& value, const char *hint) { +#ifdef DEBUG_BUFFERVIEW_HELPERS + if(!boundsCheck(view, index))return T(error("GpuVec2ToGlm::set::out of bounds", view, index, hint)); +#endif + switch(view._element.getType()) { + // TODO: flush out GpuVec2ToGlm::set(value) + case gpu::FLOAT: view.edit(index) = value; return true; + case gpu::HALF: view.edit(index) = glm::packHalf2x16(value); return true; + case gpu::NUINT16: view.edit(index) = glm::packUnorm2x16(value); return true; + case gpu::NUINT8: view.edit(index) = glm::packUnorm2x8(value); return true; + default: break; + } error("GpuVec2ToGlm::set", view, index, hint); return false; + } +}; -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); - case gpu::INT32: return view.get(index); - case gpu::INT16: return view.get(index); - case gpu::INT8: return view.get(index); - case gpu::FLOAT: return view.get(index); - case gpu::HALF: - case gpu::NUINT8: - case gpu::NINT2_10_10_10: - if (view._element.getSize() == sizeof(glm::int32)) { - return GpuVec4ToGlm::get(view, index, hint); - } - default: break; - } return T(error("GpuVec3ToGlm", view, index, hint)); }}; +template struct GpuVec4ToGlm; -template struct GpuVec4ToGlm : GpuToGlmAdapter { static T get(const gpu::BufferView& view, quint32 index, const char *hint) { - assert(view._element.getSize() == sizeof(glm::int32)); +template struct GpuVec3ToGlm : GpuToGlmAdapter { static T get(const gpu::BufferView& view, glm::uint32 index, const char *hint) { +#ifdef DEBUG_BUFFERVIEW_HELPERS + if(!boundsCheck(view, index))return T(error("GpuVec3ToGlm::get::out of bounds", view, index, hint)); +#endif + 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); + case gpu::INT32: return view.get(index); + case gpu::INT16: return view.get(index); + case gpu::INT8: return view.get(index); + case gpu::FLOAT: return view.get(index); + case gpu::HALF: CHECK_SIZE(glm::uint64); return T(glm::unpackHalf4x16(view.get(index))); + case gpu::NUINT8: CHECK_SIZE(glm::uint32); return T(glm::unpackUnorm4x8(view.get(index))); + case gpu::NINT2_10_10_10: return T(glm::unpackSnorm3x10_1x2(view.get(index))); + default: break; + } return T(error("GpuVec3ToGlm::get", view, index, hint)); } + static bool set(const gpu::BufferView& view, glm::uint32 index, const T& value, const char *hint) { +#ifdef DEBUG_BUFFERVIEW_HELPERS + if(!boundsCheck(view, index))return T(error("GpuVec3ToGlm::set::out of bounds", view, index, hint)); +#endif + switch(view._element.getType()) { + // TODO: flush out GpuVec3ToGlm::set(value) + case gpu::FLOAT: view.edit(index) = value; return true; + case gpu::NUINT8: CHECK_SIZE(glm::uint32); view.edit(index) = glm::packUnorm4x8(glm::fvec4(value,0.0f)); return true; + case gpu::UINT8: view.edit(index) = value; return true; + case gpu::NINT2_10_10_10: view.edit(index) = glm::packSnorm3x10_1x2(glm::fvec4(value,0.0f)); return true; + default: break; + } error("GpuVec3ToGlm::set", view, index, hint); return false; + } +}; + +template struct GpuVec4ToGlm : GpuToGlmAdapter { static T get(const gpu::BufferView& view, glm::uint32 index, const char *hint) { +#ifdef DEBUG_BUFFERVIEW_HELPERS + if(!boundsCheck(view, index))return T(error("GpuVec4ToGlm::get::out of bounds", view, index, hint)); +#endif switch(view._element.getType()) { case gpu::UINT32: return view.get(index); case gpu::UINT16: return view.get(index); @@ -362,8 +310,8 @@ template struct GpuVec4ToGlm : GpuToGlmAdapter { static T get(const case gpu::INT16: return view.get(index); 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::NUINT16: CHECK_SIZE(glm::uint64); return glm::unpackUnorm4x16(view.get(index)); + case gpu::NUINT8: CHECK_SIZE(glm::uint32); return glm::unpackUnorm4x8(view.get(index)); case gpu::NUINT2: break; case gpu::NINT32: break; case gpu::NINT16: break; @@ -371,56 +319,221 @@ template struct GpuVec4ToGlm : GpuToGlmAdapter { static T get(const case gpu::COMPRESSED: break; case gpu::NUM_TYPES: break; case gpu::FLOAT: return view.get(index); - case gpu::HALF: return glm::unpackSnorm4x8(view.get(index)); + case gpu::HALF: CHECK_SIZE(glm::uint64); return glm::unpackHalf4x16(view.get(index)); case gpu::NINT2_10_10_10: return glm::unpackSnorm3x10_1x2(view.get(index)); - } return T(error("GpuVec4ToGlm", view, index, hint)); }}; - + } return T(error("GpuVec4ToGlm::get", view, index, hint)); } + static bool set(const gpu::BufferView& view, glm::uint32 index, const T& value, const char *hint) { +#ifdef DEBUG_BUFFERVIEW_HELPERS + if(!boundsCheck(view, index))return T(error("GpuVec4ToGlm::set::out of bounds", view, index, hint)); +#endif + switch(view._element.getType()) { + case gpu::FLOAT: view.edit(index) = value; return true; + case gpu::HALF: CHECK_SIZE(glm::uint64); view.edit(index) = glm::packHalf4x16(value); return true; + case gpu::UINT8: view.edit(index) = value; return true; + case gpu::NINT2_10_10_10: view.edit(index) = glm::packSnorm3x10_1x2(value); return true; + case gpu::NUINT16: CHECK_SIZE(glm::uint64); view.edit(index) = glm::packUnorm4x16(value); return true; + case gpu::NUINT8: CHECK_SIZE(glm::uint32); view.edit(index) = glm::packUnorm4x8(value); return true; + default: break; + } error("GpuVec4ToGlm::set", view, index, hint); return false; + } +}; +#undef CHECK_SIZE template -struct getVec { - static QVector __to_vector__(const gpu::BufferView& view, const char *hint) { +struct GpuValueResolver { + static QVector toVector(const gpu::BufferView& view, const char *hint) { QVector result; - const quint32 count = (quint32)view.getNumElements(); + const glm::uint32 count = (glm::uint32)view.getNumElements(); result.resize(count); - for (quint32 i = 0; i < count; i++) { + for (glm::uint32 i = 0; i < count; i++) { result[i] = FUNC::get(view, i, hint); } return result; } - static T __to_value__(const gpu::BufferView& view, quint32 index, const char *hint) { - assert(boundsCheck(view, index)); + static T toValue(const gpu::BufferView& view, glm::uint32 index, const char *hint) { return FUNC::get(view, index, hint); } }; // BufferView => QVector -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::bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,U>::toVector(view, hint); } + +template<> QVector buffer_helpers::bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,int>::toVector(view, hint); } +template<> QVector buffer_helpers::bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,glm::uint16>::toVector(view, hint); } +template<> QVector buffer_helpers::bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,glm::uint32>::toVector(view, hint); } +template<> QVector buffer_helpers::bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,glm::vec2>::toVector(view, hint); } +template<> QVector buffer_helpers::bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,glm::vec3>::toVector(view, hint); } +template<> QVector buffer_helpers::bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,glm::vec4>::toVector(view, hint); } + +// view.get with conversion between types +template<> int buffer_helpers::getValue(const gpu::BufferView& view, glm::uint32 index, const char *hint) { return GpuScalarToGlm::get(view, index, hint); } +template<> glm::uint32 buffer_helpers::getValue(const gpu::BufferView& view, glm::uint32 index, const char *hint) { return GpuScalarToGlm::get(view, index, hint); } +template<> glm::vec2 buffer_helpers::getValue(const gpu::BufferView& view, glm::uint32 index, const char *hint) { return GpuVec2ToGlm::get(view, index, hint); } +template<> glm::vec3 buffer_helpers::getValue(const gpu::BufferView& view, glm::uint32 index, const char *hint) { return GpuVec3ToGlm::get(view, index, hint); } +template<> glm::vec4 buffer_helpers::getValue(const gpu::BufferView& view, glm::uint32 index, const char *hint) { return GpuVec4ToGlm::get(view, index, hint); } + +// bufferView => QVariant +template<> QVariant buffer_helpers::getValue(const gpu::BufferView& view, glm::uint32 index, const char* hint) { + if (!boundsCheck(view, index)) { + qDebug() << "getValue -- out of bounds" << index << hint; + return false; + } + const auto dataType = view._element.getType(); + switch(view._element.getScalarCount()) { + case 1: + if (dataType == gpu::Type::FLOAT) { + return GpuScalarToGlm::get(view, index, hint); + } else { + switch(dataType) { + case gpu::INT8: case gpu::INT16: case gpu::INT32: + case gpu::NINT8: case gpu::NINT16: case gpu::NINT32: + case gpu::NINT2_10_10_10: + // signed + return GpuScalarToGlm::get(view, index, hint); + default: + // unsigned + return GpuScalarToGlm::get(view, index, hint); + } + } + case 2: return glmVecToVariant(GpuVec2ToGlm::get(view, index, hint)); + case 3: return glmVecToVariant(GpuVec3ToGlm::get(view, index, hint)); + case 4: return glmVecToVariant(GpuVec4ToGlm::get(view, index, hint)); + } + return QVariant(); } +glm::uint32 buffer_helpers::mesh::forEachVertex(const graphics::MeshPointer& mesh, std::function func) { + glm::uint32 i = 0; + auto attributeViews = getAllBufferViews(mesh); + auto nPositions = mesh->getNumVertices(); + for (; i < nPositions; i++) { + QVariantMap values; + for (const auto& a : attributeViews) { + values[a.first] = buffer_helpers::getValue(a.second, i, qUtf8Printable(a.first)); + } + if (!func(i, values)) { + break; + } + } + return i; +} -// indexed conversion accessors (like the hypothetical "view.convert(i)") -template <> int buffer_helpers::convert(const gpu::BufferView& view, quint32 index, const char *hint) { - return getVec,int>::__to_value__(view, index, hint); +// view.edit with conversion between types +template<> bool buffer_helpers::setValue(const gpu::BufferView& view, glm::uint32 index, const QVariant& v, const char* hint) { + if (!boundsCheck(view, index)) { + qDebug() << "setValue -- out of bounds" << index << hint; + return false; + } + const auto dataType = view._element.getType(); + + switch(view._element.getScalarCount()) { + case 1: + if (dataType == gpu::Type::FLOAT) { + return GpuScalarToGlm::set(view, index, v.toFloat(), hint); + } else { + switch(dataType) { + case gpu::INT8: case gpu::INT16: case gpu::INT32: + case gpu::NINT8: case gpu::NINT16: case gpu::NINT32: + case gpu::NINT2_10_10_10: + // signed + return GpuScalarToGlm::set(view, index, v.toInt(), hint); + default: + // unsigned + return GpuScalarToGlm::set(view, index, v.toUInt(), hint); + } + } + return false; + case 2: return GpuVec2ToGlm::set(view, index, glmVecFromVariant(v), hint); + case 3: return GpuVec3ToGlm::set(view, index, glmVecFromVariant(v), hint); + case 4: return GpuVec4ToGlm::set(view, index, glmVecFromVariant(v), hint); + } + return false; } -template <> glm::vec2 buffer_helpers::convert(const gpu::BufferView& view, quint32 index, const char *hint) { - return getVec,glm::vec2>::__to_value__(view, index, hint); + +template<> bool buffer_helpers::setValue(const gpu::BufferView& view, glm::uint32 index, const glm::uint32& value, const char* hint) { + return GpuScalarToGlm::set(view, index, value, hint); } -template <> glm::vec3 buffer_helpers::convert(const gpu::BufferView& view, quint32 index, const char *hint) { - return getVec,glm::vec3>::__to_value__(view, index, hint); +template<> bool buffer_helpers::setValue(const gpu::BufferView& view, glm::uint32 index, const glm::uint16& value, const char* hint) { + return GpuScalarToGlm::set(view, index, value, hint); } -template <> glm::vec4 buffer_helpers::convert(const gpu::BufferView& view, quint32 index, const char *hint) { - return getVec,glm::vec4>::__to_value__(view, index, hint); +template<> bool buffer_helpers::setValue(const gpu::BufferView& view, glm::uint32 index, const glm::vec2& value, const char* hint) { + return GpuVec2ToGlm::set(view, index, value, hint); } +template<> bool buffer_helpers::setValue(const gpu::BufferView& view, glm::uint32 index, const glm::vec3& value, const char* hint) { + return GpuVec3ToGlm::set(view, index, value, hint); +} +template<> bool buffer_helpers::setValue(const gpu::BufferView& view, glm::uint32 index, const glm::vec4& value, const char* hint) { + return GpuVec4ToGlm::set(view, index, value, hint); +} + +bool buffer_helpers::mesh::setVertexAttributes(const graphics::MeshPointer& mesh, glm::uint32 index, const QVariantMap& attributes) { + bool ok = true; + for (auto& a : getAllBufferViews(mesh)) { + const auto& name = a.first; + if (attributes.contains(name)) { + const auto& value = attributes.value(name); + if (value.isValid()) { + auto& view = a.second; + buffer_helpers::setValue(view, index, value); + } else { + ok = false; + //qCDebug(graphics_scripting) << "(skipping) setVertexAttributes" << vertexIndex << name; + } + } + } + return ok; +} + +QVariant buffer_helpers::mesh::getVertexAttributes(const graphics::MeshPointer& mesh, glm::uint32 vertexIndex) { + auto attributeViews = getAllBufferViews(mesh); + QVariantMap values; + for (const auto& a : attributeViews) { + values[a.first] = buffer_helpers::getValue(a.second, vertexIndex, qUtf8Printable(a.first)); + } + return values; +} + +// QVariantList => QVector +namespace { + template QVector qVariantListToGlmVector(const QVariantList& list) { + QVector output; + output.resize(list.size()); + int i = 0; + for (const auto& v : list) { + output[i++] = buffer_helpers::glmVecFromVariant(v); + } + return output; + } + template QVector qVariantListToScalarVector(const QVariantList& list) { + QVector output; + output.resize(list.size()); + int i = 0; + for (const auto& v : list) { + output[i++] = v.value(); + } + return output; + } +} + +template QVector buffer_helpers::variantToVector(const QVariant& value) { qDebug() << "variantToVector[class]"; return qVariantListToGlmVector(value.toList()); } +template<> QVector buffer_helpers::variantToVector(const QVariant& value) { return qVariantListToScalarVector(value.toList()); } +template<> QVector buffer_helpers::variantToVector(const QVariant& value) { return qVariantListToScalarVector(value.toList()); } +template<> QVector buffer_helpers::variantToVector(const QVariant& value) { return qVariantListToScalarVector(value.toList()); } +template<> QVector buffer_helpers::variantToVector(const QVariant& value) { return qVariantListToGlmVector(value.toList()); } +template<> QVector buffer_helpers::variantToVector(const QVariant& value) { return qVariantListToGlmVector(value.toList()); } +template<> QVector buffer_helpers::variantToVector(const QVariant& value) { return qVariantListToGlmVector(value.toList()); } + +template<> gpu::BufferView buffer_helpers::newFromVector(const QVector& _elements, const gpu::Element& elementType) { + glm::uint32 numElements = _elements.size(); + auto buffer = new gpu::Buffer(); + buffer->resize(elementType.getSize() * numElements); + auto bufferView = gpu::BufferView(buffer, elementType); + for (glm::uint32 i = 0; i < numElements; i++) { + setValue(bufferView, i, _elements[i]); + } + return bufferView; +} + gpu::BufferView buffer_helpers::clone(const gpu::BufferView& input) { return gpu::BufferView( @@ -429,25 +542,29 @@ gpu::BufferView buffer_helpers::clone(const gpu::BufferView& input) { ); } -// TODO: preserve existing data -gpu::BufferView buffer_helpers::resize(const gpu::BufferView& input, quint32 numElements) { +gpu::BufferView buffer_helpers::resized(const gpu::BufferView& input, glm::uint32 numElements) { +#ifdef DEBUG_BUFFERVIEW_HELPERS auto effectiveSize = input._buffer->getSize() / input.getNumElements(); qCDebug(bufferhelper_logging) << "resize input" << input.getNumElements() << input._buffer->getSize() << "effectiveSize" << effectiveSize; +#endif auto vsize = input._element.getSize() * numElements; std::unique_ptr data{ new gpu::Byte[vsize] }; memset(data.get(), 0, vsize); auto buffer = new gpu::Buffer(vsize, data.get()); + memcpy(data.get(), input._buffer->getData(), std::min(vsize, (glm::uint32)input._buffer->getSize())); auto output = gpu::BufferView(buffer, input._element); +#ifdef DEBUG_BUFFERVIEW_HELPERS qCDebug(bufferhelper_logging) << "resized output" << output.getNumElements() << output._buffer->getSize(); +#endif return output; } -graphics::MeshPointer buffer_helpers::cloneMesh(graphics::MeshPointer mesh) { +graphics::MeshPointer buffer_helpers::mesh::clone(const graphics::MeshPointer& mesh) { auto clone = std::make_shared(); clone->displayName = (QString::fromStdString(mesh->displayName) + "-clone").toStdString(); clone->setIndexBuffer(buffer_helpers::clone(mesh->getIndexBuffer())); clone->setPartBuffer(buffer_helpers::clone(mesh->getPartBuffer())); - auto attributeViews = buffer_helpers::gatherBufferViews(mesh); + auto attributeViews = buffer_helpers::mesh::getAllBufferViews(mesh); for (const auto& a : attributeViews) { auto& view = a.second; auto slot = buffer_helpers::ATTRIBUTES[a.first]; @@ -461,195 +578,17 @@ graphics::MeshPointer buffer_helpers::cloneMesh(graphics::MeshPointer mesh) { return clone; } -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); - const auto& elementType = bufferView._element; - gpu::Size elementSize = elementType.getSize(); - auto nPositions = mesh->getNumVertices(); - auto vsize = nPositions * elementSize; - auto diffTypes = (elementType.getType() != bufferView._element.getType() || - elementType.getSize() > bufferView._element.getSize() || - elementType.getScalarCount() > bufferView._element.getScalarCount() || - vsize > bufferView._size - ); - QString hint = QString("%1").arg(slot); -#ifdef DEV_BUILD - auto beforeCount = bufferView.getNumElements(); - auto beforeTotal = bufferView._size; -#endif - if (bufferView.getNumElements() < nPositions || diffTypes) { - if (!bufferView._buffer || bufferView.getNumElements() == 0) { - qCInfo(bufferhelper_logging).nospace() << "ScriptableMesh -- adding missing mesh attribute '" << hint << "' for BufferView"; - std::unique_ptr data{ new gpu::Byte[vsize] }; - memset(data.get(), 0, vsize); - auto buffer = new gpu::Buffer(vsize, data.get()); - bufferView = gpu::BufferView(buffer, elementType); - mesh->addAttribute(slot, bufferView); - } else { - qCInfo(bufferhelper_logging) << "ScriptableMesh -- resizing Buffer current:" << hint << bufferView._buffer->getSize() << "wanted:" << vsize; - bufferView._element = elementType; - bufferView._buffer->resize(vsize); - bufferView._size = bufferView._buffer->getSize(); - } - } -#ifdef DEV_BUILD - auto afterCount = bufferView.getNumElements(); - auto afterTotal = bufferView._size; - if (beforeTotal != afterTotal || beforeCount != afterCount) { - QString typeName = QString("%1").arg(bufferView._element.getType()); - 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); - } -#endif - return bufferView; - } - - gpu::BufferView expandAttributeToMatchPositions(graphics::MeshPointer mesh, gpu::Stream::Slot slot) { - if (slot == gpu::Stream::POSITION) { - return buffer_helpers::getBufferView(mesh, slot); - } - return _expandedAttributeBuffer(mesh, slot); - } -} - -std::map buffer_helpers::gatherBufferViews(graphics::MeshPointer mesh, const QStringList& expandToMatchPositions) { +std::map buffer_helpers::mesh::getAllBufferViews(const graphics::MeshPointer& mesh) { std::map attributeViews; if (!mesh) { return attributeViews; } for (const auto& a : buffer_helpers::ATTRIBUTES.toStdMap()) { - auto name = a.first; - auto slot = a.second; - auto view = getBufferView(mesh, slot); - auto beforeCount = view.getNumElements(); -#if DEV_BUILD - auto beforeTotal = view._size; -#endif - if (expandToMatchPositions.contains(name)) { - expandAttributeToMatchPositions(mesh, slot); - } - if (beforeCount > 0) { - auto element = view._element; - QString typeName = QString("%1").arg(element.getType()); - - attributeViews[name] = getBufferView(mesh, slot); - -#if DEV_BUILD - const auto vecN = element.getScalarCount(); - auto afterTotal = attributeViews[name]._size; - 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); - } -#endif + auto bufferView = getBufferView(mesh, a.second); + if (bufferView.getNumElements()) { + attributeViews[a.first] = bufferView; } } return attributeViews; } -bool buffer_helpers::recalculateNormals(graphics::MeshPointer mesh) { - qCInfo(bufferhelper_logging) << "Recalculating normals" << !!mesh; - if (!mesh) { - return false; - } - buffer_helpers::gatherBufferViews(mesh, { "normal", "color" }); // ensures #normals >= #positions - auto normals = mesh->getAttributeBuffer(gpu::Stream::NORMAL); - auto verts = mesh->getVertexBuffer(); - auto indices = mesh->getIndexBuffer(); - auto esize = indices._element.getSize(); - auto numPoints = indices.getNumElements(); - const auto TRIANGLE = 3; - quint32 numFaces = (quint32)numPoints / TRIANGLE; - QVector faceNormals; - QMap> vertexToFaces; - faceNormals.resize(numFaces); - auto numNormals = normals.getNumElements(); - qCInfo(bufferhelper_logging) << QString("numFaces: %1, numNormals: %2, numPoints: %3").arg(numFaces).arg(numNormals).arg(numPoints); - if (normals.getNumElements() != verts.getNumElements()) { - return false; - } - for (quint32 i = 0; i < numFaces; i++) { - quint32 I = TRIANGLE * i; - quint32 i0 = esize == 4 ? indices.get(I+0) : indices.get(I+0); - quint32 i1 = esize == 4 ? indices.get(I+1) : indices.get(I+1); - quint32 i2 = esize == 4 ? indices.get(I+2) : indices.get(I+2); - - Triangle face = { - verts.get(i1), - verts.get(i2), - verts.get(i0) - }; - faceNormals[i] = face.getNormal(); - if (glm::isnan(faceNormals[i].x)) { -#ifdef DEBUG_BUFFERVIEW_SCRIPTING - qCInfo(bufferhelper_logging) << i << i0 << i1 << i2 << glmVecToVariant(face.v0) << glmVecToVariant(face.v1) << glmVecToVariant(face.v2); -#endif - break; - } - vertexToFaces[glm::to_string(glm::dvec3(face.v0)).c_str()] << i; - vertexToFaces[glm::to_string(glm::dvec3(face.v1)).c_str()] << i; - vertexToFaces[glm::to_string(glm::dvec3(face.v2)).c_str()] << i; - } - for (quint32 j = 0; j < numNormals; j++) { - //auto v = verts.get(j); - glm::vec3 normal { 0.0f, 0.0f, 0.0f }; - QString key { glm::to_string(glm::dvec3(verts.get(j))).c_str() }; - const auto& faces = vertexToFaces.value(key); - if (faces.size()) { - for (const auto i : faces) { - normal += faceNormals[i]; - } - normal *= 1.0f / (float)faces.size(); - } else { - static int logged = 0; - if (logged++ < 10) { - qCInfo(bufferhelper_logging) << "no faces for key!?" << key; - } - normal = verts.get(j); - } - if (glm::isnan(normal.x)) { -#ifdef DEBUG_BUFFERVIEW_SCRIPTING - static int logged = 0; - if (logged++ < 10) { - qCInfo(bufferhelper_logging) << "isnan(normal.x)" << j << glmVecToVariant(normal); - } -#endif - break; - } - buffer_helpers::fromVariant(normals, j, glmVecToVariant(glm::normalize(normal))); - } - return true; -} - -QVariant buffer_helpers::toVariant(const glm::mat4& mat4) { - QVector floats; - floats.resize(16); - memcpy(floats.data(), &mat4, sizeof(glm::mat4)); - QVariant v; - v.setValue>(floats); - return v; -}; - -QVariant buffer_helpers::toVariant(const Extents& box) { - return QVariantMap{ - { "center", glmVecToVariant(box.minimum + (box.size() / 2.0f)) }, - { "minimum", glmVecToVariant(box.minimum) }, - { "maximum", glmVecToVariant(box.maximum) }, - { "dimensions", glmVecToVariant(box.size()) }, - }; -} - -QVariant buffer_helpers::toVariant(const AABox& box) { - return QVariantMap{ - { "brn", glmVecToVariant(box.getCorner()) }, - { "tfl", glmVecToVariant(box.calcTopFarLeft()) }, - { "center", glmVecToVariant(box.calcCenter()) }, - { "minimum", glmVecToVariant(box.getMinimumPoint()) }, - { "maximum", glmVecToVariant(box.getMaximumPoint()) }, - { "dimensions", glmVecToVariant(box.getDimensions()) }, - }; -} diff --git a/libraries/graphics/src/graphics/BufferViewHelpers.h b/libraries/graphics/src/graphics/BufferViewHelpers.h index 6d4908a2c7..f877341d50 100644 --- a/libraries/graphics/src/graphics/BufferViewHelpers.h +++ b/libraries/graphics/src/graphics/BufferViewHelpers.h @@ -10,10 +10,7 @@ #include #include -namespace gpu { - class BufferView; - class Element; -} +#include "GpuHelpers.h" namespace graphics { class Mesh; @@ -23,33 +20,41 @@ namespace graphics { class Extents; class AABox; -struct buffer_helpers { - template static QVariant glmVecToVariant(const T& v, bool asArray = false); - template static const T glmVecFromVariant(const QVariant& v); +namespace buffer_helpers { + extern QMap ATTRIBUTES; + extern const std::array XYZW; + extern const std::array ZERO123; - static graphics::MeshPointer cloneMesh(graphics::MeshPointer mesh); - static QMap ATTRIBUTES; - static std::map gatherBufferViews(graphics::MeshPointer mesh, const QStringList& expandToMatchPositions = QStringList()); - static bool recalculateNormals(graphics::MeshPointer meshProxy); - static gpu::BufferView getBufferView(graphics::MeshPointer mesh, quint8 slot); + template QVariant glmVecToVariant(const T& v, bool asArray = false); + template const T glmVecFromVariant(const QVariant& v); - static QVariant toVariant(const Extents& box); - static QVariant toVariant(const AABox& box); - static QVariant toVariant(const glm::mat4& mat4); - static QVariant toVariant(const gpu::BufferView& view, quint32 index, bool asArray = false, const char* hint = ""); + glm::uint32 forEachVariant(const gpu::BufferView& view, std::function func, const char* hint = ""); + template glm::uint32 forEach(const gpu::BufferView& view, std::function func); - static bool fromVariant(const gpu::BufferView& view, quint32 index, const QVariant& v); + template gpu::BufferView newFromVector(const QVector& elements, const gpu::Element& elementType); + template gpu::BufferView newFromVariantList(const QVariantList& list, const gpu::Element& elementType); - template static gpu::BufferView fromVector(const QVector& elements, const gpu::Element& elementType); + template QVector variantToVector(const QVariant& list); + template QVector bufferToVector(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 = ""); + // note: these do value conversions from the underlying buffer type into the template type + template T getValue(const gpu::BufferView& view, glm::uint32 index, const char* hint = ""); + template bool setValue(const gpu::BufferView& view, glm::uint32 index, const T& value, const char* hint = ""); - static gpu::BufferView clone(const gpu::BufferView& input); - static gpu::BufferView resize(const gpu::BufferView& input, quint32 numElements); + gpu::BufferView clone(const gpu::BufferView& input); + gpu::BufferView resized(const gpu::BufferView& input, glm::uint32 numElements); - static void packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint32& packedNormal, glm::uint32& packedTangent); + void packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint32& packedNormal, glm::uint32& packedTangent); - static const std::array XYZW; - static const std::array ZERO123; -}; + namespace mesh { + glm::uint32 forEachVertex(const graphics::MeshPointer& mesh, std::function func); + bool setVertexAttributes(const graphics::MeshPointer& mesh, glm::uint32 index, const QVariantMap& attributes); + QVariant getVertexAttributes(const graphics::MeshPointer& mesh, glm::uint32 index); + graphics::MeshPointer clone(const graphics::MeshPointer& mesh); + gpu::BufferView getBufferView(const graphics::MeshPointer& mesh, quint8 slot); + std::map getAllBufferViews(const graphics::MeshPointer& mesh); + template QVector attributeToVector(const graphics::MeshPointer& mesh, gpu::Stream::InputSlot slot) { + return bufferToVector(getBufferView(mesh, slot), qUtf8Printable(gpu::toString(slot))); + } + } +} diff --git a/libraries/graphics/src/graphics/GpuHelpers.cpp b/libraries/graphics/src/graphics/GpuHelpers.cpp new file mode 100644 index 0000000000..63393df5e1 --- /dev/null +++ b/libraries/graphics/src/graphics/GpuHelpers.cpp @@ -0,0 +1,122 @@ +// +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +// + +#include "GpuHelpers.h" + +namespace graphics { + DebugEnums TOPOLOGIES{ + { Mesh::Topology::POINTS, "points" }, + { Mesh::Topology::LINES, "lines" }, + { Mesh::Topology::LINE_STRIP, "line_strip" }, + { Mesh::Topology::TRIANGLES, "triangles" }, + { Mesh::Topology::TRIANGLE_STRIP, "triangle_strip" }, + { Mesh::Topology::QUADS, "quads" }, + { Mesh::Topology::QUAD_STRIP, "quad_strip" }, + { Mesh::Topology::NUM_TOPOLOGIES, "num_topologies" }, + }; +} +namespace gpu { + + DebugEnums TYPES{ + { Type::FLOAT, "float" }, + { Type::INT32, "int32" }, + { Type::UINT32, "uint32" }, + { Type::HALF, "half" }, + { Type::INT16, "int16" }, + { Type::UINT16, "uint16" }, + { Type::INT8, "int8" }, + { Type::UINT8, "uint8" }, + { Type::NINT32, "nint32" }, + { Type::NUINT32, "nuint32" }, + { Type::NINT16, "nint16" }, + { Type::NUINT16, "nuint16" }, + { Type::NINT8, "nint8" }, + { Type::NUINT8, "nuint8" }, + { Type::NUINT2, "nuint2" }, + { Type::NINT2_10_10_10, "nint2_10_10_10" }, + { Type::COMPRESSED, "compressed" }, + { Type::NUM_TYPES, "num_types" }, + }; + DebugEnums DIMENSIONS{ + { Dimension::SCALAR, "scalar" }, + { Dimension::VEC2, "vec2" }, + { Dimension::VEC3, "vec3" }, + { Dimension::VEC4, "vec4" }, + { Dimension::MAT2, "mat2" }, + { Dimension::MAT3, "mat3" }, + { Dimension::MAT4, "mat4" }, + { Dimension::TILE4x4, "tile4x4" }, + { Dimension::NUM_DIMENSIONS, "num_dimensions" }, + }; + DebugEnums SEMANTICS{ + { Semantic::RAW, "raw" }, + + { Semantic::RED, "red" }, + { Semantic::RGB, "rgb" }, + { Semantic::RGBA, "rgba" }, + { Semantic::BGRA, "bgra" }, + + { Semantic::XY, "xy" }, + { Semantic::XYZ, "xyz" }, + { Semantic::XYZW, "xyzw" }, + { Semantic::QUAT, "quat" }, + { Semantic::UV, "uv" }, + { Semantic::INDEX, "index" }, + { Semantic::PART, "part" }, + + { Semantic::DEPTH, "depth" }, + { Semantic::STENCIL, "stencil" }, + { Semantic::DEPTH_STENCIL, "depth_stencil" }, + + { Semantic::SRED, "sred" }, + { Semantic::SRGB, "srgb" }, + { Semantic::SRGBA, "srgba" }, + { Semantic::SBGRA, "sbgra" }, + + { Semantic::_FIRST_COMPRESSED, "_first_compressed" }, + + { Semantic::COMPRESSED_BC1_SRGB, "compressed_bc1_srgb" }, + { Semantic::COMPRESSED_BC1_SRGBA, "compressed_bc1_srgba" }, + { Semantic::COMPRESSED_BC3_SRGBA, "compressed_bc3_srgba" }, + { Semantic::COMPRESSED_BC4_RED, "compressed_bc4_red" }, + { Semantic::COMPRESSED_BC5_XY, "compressed_bc5_xy" }, + { Semantic::COMPRESSED_BC6_RGB, "compressed_bc6_rgb" }, + { Semantic::COMPRESSED_BC7_SRGBA, "compressed_bc7_srgba" }, + + { Semantic::_LAST_COMPRESSED, "_last_compressed" }, + + { Semantic::R11G11B10, "r11g11b10" }, + { Semantic::RGB9E5, "rgb9e5" }, + + { Semantic::UNIFORM, "uniform" }, + { Semantic::UNIFORM_BUFFER, "uniform_buffer" }, + { Semantic::RESOURCE_BUFFER, "resource_buffer" }, + { Semantic::SAMPLER, "sampler" }, + { Semantic::SAMPLER_MULTISAMPLE, "sampler_multisample" }, + { Semantic::SAMPLER_SHADOW, "sampler_shadow" }, + + + { Semantic::NUM_SEMANTICS, "num_semantics" }, + }; + DebugEnums SLOTS{ + { Stream::InputSlot::POSITION, "position" }, + { Stream::InputSlot::NORMAL, "normal" }, + { Stream::InputSlot::COLOR, "color" }, + { Stream::InputSlot::TEXCOORD0, "texcoord0" }, + { Stream::InputSlot::TEXCOORD, "texcoord" }, + { Stream::InputSlot::TANGENT, "tangent" }, + { Stream::InputSlot::SKIN_CLUSTER_INDEX, "skin_cluster_index" }, + { Stream::InputSlot::SKIN_CLUSTER_WEIGHT, "skin_cluster_weight" }, + { Stream::InputSlot::TEXCOORD1, "texcoord1" }, + { Stream::InputSlot::TEXCOORD2, "texcoord2" }, + { Stream::InputSlot::TEXCOORD3, "texcoord3" }, + { Stream::InputSlot::TEXCOORD4, "texcoord4" }, + { Stream::InputSlot::NUM_INPUT_SLOTS, "num_input_slots" }, + { Stream::InputSlot::DRAW_CALL_INFO, "draw_call_info" }, + }; +} diff --git a/libraries/graphics/src/graphics/GpuHelpers.h b/libraries/graphics/src/graphics/GpuHelpers.h new file mode 100644 index 0000000000..ceae823f83 --- /dev/null +++ b/libraries/graphics/src/graphics/GpuHelpers.h @@ -0,0 +1,47 @@ +// +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#pragma once + +#include +#include +#include +#include "Geometry.h" + +template +using DebugEnums = QMap; + +namespace graphics { + extern DebugEnums TOPOLOGIES; + inline QDebug operator<<(QDebug dbg, Mesh::Topology type) { return dbg << TOPOLOGIES.value(type);} + inline const QString toString(Mesh::Topology v) { return TOPOLOGIES.value(v); } +} + +namespace gpu { + extern DebugEnums TYPES; + extern DebugEnums DIMENSIONS; + extern DebugEnums SEMANTICS; + extern DebugEnums SLOTS; + inline QDebug operator<<(QDebug dbg, gpu::Type type) { return dbg << TYPES.value(type); } + inline QDebug operator<<(QDebug dbg, gpu::Dimension type) { return dbg << DIMENSIONS.value(type); } + inline QDebug operator<<(QDebug dbg, gpu::Semantic type) { return dbg << SEMANTICS.value(type); } + inline QDebug operator<<(QDebug dbg, gpu::Stream::InputSlot type) { return dbg << SLOTS.value(type); } + inline const QString toString(gpu::Type v) { return TYPES.value(v); } + inline const QString toString(gpu::Dimension v) { return DIMENSIONS.value(v); } + inline const QString toString(gpu::Semantic v) { return SEMANTICS.value(v); } + inline const QString toString(gpu::Stream::InputSlot v) { return SLOTS.value(v); } + inline const QString toString(gpu::Element v) { + return QString("[Element semantic=%1 type=%1 dimension=%2]") + .arg(toString(v.getSemantic())) + .arg(toString(v.getType())) + .arg(toString(v.getDimension())); + } +} + +Q_DECLARE_METATYPE(gpu::Type) +Q_DECLARE_METATYPE(gpu::Dimension) +Q_DECLARE_METATYPE(gpu::Semantic) +Q_DECLARE_METATYPE(graphics::Mesh::Topology) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 6d735497c0..0abeb70254 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -440,7 +440,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g const FBXGeometry& geometry = getFBXGeometry(); if (!_triangleSetsValid) { - calculateTriangleSets(); + calculateTriangleSets(geometry); } glm::mat4 meshToModelMatrix = glm::scale(_scale) * glm::translate(_offset); @@ -525,7 +525,7 @@ bool Model::convexHullContains(glm::vec3 point) { QMutexLocker locker(&_mutex); if (!_triangleSetsValid) { - calculateTriangleSets(); + calculateTriangleSets(getFBXGeometry()); } // If we are inside the models box, then consider the submeshes... @@ -587,9 +587,6 @@ MeshProxyList Model::getMeshes() const { return result; } -// FIXME: temporary workaround that updates the whole FBXGeometry (to keep findRayIntersection in sync) -#include "Model_temporary_hack.cpp.h" - bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer newModel, int meshIndex, int partIndex) { QMutexLocker lock(&_mutex); @@ -603,18 +600,60 @@ bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointe return false; } - auto resource = new MyGeometryResource(_url, _renderGeometry, newModel); - _needsReload = false; - _needsUpdateTextures = false; - _visualGeometryRequestFailed = false; - _needsFixupInScene = true; + const auto& meshes = newModel->meshes; + render::Transaction transaction; + const render::ScenePointer& scene = AbstractViewStateInterface::instance()->getMain3DScene(); - invalidCalculatedMeshBoxes(); - deleteGeometry(); - _renderGeometry.reset(resource); - updateGeometry(); - calculateTriangleSets(); - setRenderItemsNeedUpdate(); + meshIndex = meshIndex >= 0 ? meshIndex : 0; + partIndex = partIndex >= 0 ? partIndex : 0; + + if (meshIndex >= meshes.size()) { + qDebug() << meshIndex << "meshIndex >= newModel.meshes.size()" << meshes.size(); + return false; + } + + auto mesh = meshes[meshIndex].getMeshPointer(); + { + // update visual geometry + render::Transaction transaction; + for (int i = 0; i < (int) _modelMeshRenderItemIDs.size(); i++) { + auto itemID = _modelMeshRenderItemIDs[i]; + auto shape = _modelMeshRenderItemShapes[i]; + // TODO: check to see if .partIndex matches too + if (shape.meshIndex == meshIndex) { + transaction.updateItem(itemID, [=](ModelMeshPartPayload& data) { + data.updateMeshPart(mesh, partIndex); + }); + } + } + scene->enqueueTransaction(transaction); + } + // update triangles for ray picking + { + FBXGeometry geometry; + for (const auto& newMesh : meshes) { + FBXMesh mesh; + mesh._mesh = newMesh.getMeshPointer(); + mesh.vertices = buffer_helpers::mesh::attributeToVector(mesh._mesh, gpu::Stream::POSITION); + int numParts = newMesh.getMeshPointer()->getNumParts(); + for (int partID = 0; partID < numParts; partID++) { + FBXMeshPart part; + part.triangleIndices = buffer_helpers::bufferToVector(mesh._mesh->getIndexBuffer(), "part.triangleIndices"); + mesh.parts << part; + } + { + foreach (const glm::vec3& vertex, mesh.vertices) { + glm::vec3 transformedVertex = glm::vec3(mesh.modelTransform * glm::vec4(vertex, 1.0f)); + geometry.meshExtents.minimum = glm::min(geometry.meshExtents.minimum, transformedVertex); + geometry.meshExtents.maximum = glm::max(geometry.meshExtents.maximum, transformedVertex); + mesh.meshExtents.minimum = glm::min(mesh.meshExtents.minimum, transformedVertex); + mesh.meshExtents.maximum = glm::max(mesh.meshExtents.maximum, transformedVertex); + } + } + geometry.meshes << mesh; + } + calculateTriangleSets(geometry); + } return true; } @@ -638,10 +677,9 @@ scriptable::ScriptableModelBase Model::getScriptableModel() { return result; } -void Model::calculateTriangleSets() { +void Model::calculateTriangleSets(const FBXGeometry& geometry) { PROFILE_RANGE(render, __FUNCTION__); - const FBXGeometry& geometry = getFBXGeometry(); int numberOfMeshes = geometry.meshes.size(); _triangleSetsValid = true; @@ -664,7 +702,7 @@ void Model::calculateTriangleSets() { int totalTriangles = (numberOfQuads * TRIANGLES_PER_QUAD) + numberOfTris; _modelSpaceMeshTriangleSets[i].reserve(totalTriangles); - auto meshTransform = getFBXGeometry().offset * mesh.modelTransform; + auto meshTransform = geometry.offset * mesh.modelTransform; if (part.quadIndices.size() > 0) { int vIndex = 0; diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index bbb323d1e7..f6a9a64e83 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -419,7 +419,7 @@ protected: bool _overrideModelTransform { false }; bool _triangleSetsValid { false }; - void calculateTriangleSets(); + void calculateTriangleSets(const FBXGeometry& geometry); QVector _modelSpaceMeshTriangleSets; // model space triangles for all sub meshes diff --git a/libraries/render-utils/src/Model_temporary_hack.cpp.h b/libraries/render-utils/src/Model_temporary_hack.cpp.h deleted file mode 100644 index cfa6945571..0000000000 --- a/libraries/render-utils/src/Model_temporary_hack.cpp.h +++ /dev/null @@ -1,84 +0,0 @@ -// FIXME: temporary workaround for duplicating the FBXModel when dynamically replacing an underlying mesh part -#include -#include -class MyGeometryResource : public GeometryResource { -public: - shared_ptr fbxGeometry; - MyGeometryResource(const QUrl& url, Geometry::Pointer originalGeometry, scriptable::ScriptableModelBasePointer newModel) : GeometryResource(url) { - fbxGeometry = std::make_shared(); - FBXGeometry& geometry = *fbxGeometry.get(); - const FBXGeometry* original; - shared_ptr tmpGeometry; - if (originalGeometry) { - original = &originalGeometry->getFBXGeometry(); - } else { - tmpGeometry = std::make_shared(); - original = tmpGeometry.get(); - } - geometry.originalURL = original->originalURL; - geometry.bindExtents = original->bindExtents; - - for (const auto &j : original->joints) { - geometry.joints << j; - } - for (const FBXMaterial& material : original->materials) { - _materials.push_back(std::make_shared(material, _textureBaseUrl)); - } - std::shared_ptr meshes = std::make_shared(); - std::shared_ptr parts = std::make_shared(); - int meshID = 0; - if (newModel) { - geometry.meshExtents.reset(); - for (const auto& newMesh : newModel->meshes) { - // qDebug() << "newMesh #" << meshID; - FBXMesh mesh; - if (meshID < original->meshes.size()) { - mesh = original->meshes.at(meshID); // copy - } - mesh._mesh = newMesh.getMeshPointer(); - // duplicate the buffers - mesh.vertices = buffer_helpers::toVector(mesh._mesh->getVertexBuffer(), "mesh.vertices"); - mesh.normals = buffer_helpers::toVector(buffer_helpers::getBufferView(mesh._mesh, gpu::Stream::NORMAL), "mesh.normals"); - mesh.colors = buffer_helpers::toVector(buffer_helpers::getBufferView(mesh._mesh, gpu::Stream::COLOR), "mesh.colors"); - mesh.texCoords = buffer_helpers::toVector(buffer_helpers::getBufferView(mesh._mesh, gpu::Stream::TEXCOORD0), "mesh.texCoords"); - mesh.texCoords1 = buffer_helpers::toVector(buffer_helpers::getBufferView(mesh._mesh, gpu::Stream::TEXCOORD1), "mesh.texCoords1"); - mesh.createMeshTangents(true); - mesh.createBlendShapeTangents(false); - geometry.meshes << mesh; - // Copy mesh pointers - meshes->emplace_back(newMesh.getMeshPointer()); - int partID = 0; - const auto oldParts = mesh.parts; - mesh.parts.clear(); - for (const FBXMeshPart& fbxPart : oldParts) { - FBXMeshPart part; // new copy - part.materialID = fbxPart.materialID; - // Construct local parts - part.triangleIndices = buffer_helpers::toVector(mesh._mesh->getIndexBuffer(), "part.triangleIndices"); - mesh.parts << part; - auto p = std::make_shared(meshID, partID, 0); - parts->push_back(p); - partID++; - } - { - // accumulate local transforms - // compute the mesh extents from the transformed vertices - foreach (const glm::vec3& vertex, mesh.vertices) { - glm::vec3 transformedVertex = glm::vec3(mesh.modelTransform * glm::vec4(vertex, 1.0f)); - geometry.meshExtents.minimum = glm::min(geometry.meshExtents.minimum, transformedVertex); - geometry.meshExtents.maximum = glm::max(geometry.meshExtents.maximum, transformedVertex); - - mesh.meshExtents.minimum = glm::min(mesh.meshExtents.minimum, transformedVertex); - mesh.meshExtents.maximum = glm::max(mesh.meshExtents.maximum, transformedVertex); - } - } - meshID++; - } - } - _meshes = meshes; - _meshParts = parts; - _loaded = true; - _fbxGeometry = fbxGeometry; - }; -}; - From 54dc0240b0cb5c30fc4c7480391812e9930ae6b5 Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 26 Feb 2018 05:26:35 -0500 Subject: [PATCH 26/29] remove bufferviewscripting refs; fix compiler warnings --- .../GraphicsScriptingInterface.cpp | 1 - .../src/graphics-scripting/ScriptableMesh.cpp | 1 - .../graphics-scripting/ScriptableMeshPart.cpp | 1 - .../src/graphics/BufferViewHelpers.cpp | 300 +++++++++--------- 4 files changed, 149 insertions(+), 154 deletions(-) diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index 22ef346df6..ed8982b9c7 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -9,7 +9,6 @@ // #include "GraphicsScriptingInterface.h" -#include "BufferViewScripting.h" #include "GraphicsScriptingUtil.h" #include "OBJWriter.h" #include "RegisteredMetaTypes.h" diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp index 2cef4fef64..e5e58b847a 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp @@ -10,7 +10,6 @@ #include "ScriptableMesh.h" #include "ScriptableMeshPart.h" -#include "BufferViewScripting.h" #include "GraphicsScriptingUtil.h" #include "OBJWriter.h" #include diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp index 0d4bb7bdc5..9d6359bee3 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp @@ -9,7 +9,6 @@ #include "ScriptableMeshPart.h" -#include "BufferViewScripting.h" #include "GraphicsScriptingUtil.h" #include "OBJWriter.h" #include diff --git a/libraries/graphics/src/graphics/BufferViewHelpers.cpp b/libraries/graphics/src/graphics/BufferViewHelpers.cpp index 46bd39bb45..5c7e6ff892 100644 --- a/libraries/graphics/src/graphics/BufferViewHelpers.cpp +++ b/libraries/graphics/src/graphics/BufferViewHelpers.cpp @@ -32,14 +32,12 @@ namespace { QLoggingCategory bufferhelper_logging{ "hifi.bufferview" }; } -const std::array buffer_helpers::XYZW = { { "x", "y", "z", "w" } }; -const std::array buffer_helpers::ZERO123 = { { "0", "1", "2", "3" } }; +namespace buffer_helpers { -gpu::BufferView buffer_helpers::mesh::getBufferView(const graphics::MeshPointer& mesh, gpu::Stream::Slot slot) { - return slot == gpu::Stream::POSITION ? mesh->getVertexBuffer() : mesh->getAttributeBuffer(slot); -} +const std::array XYZW = { { "x", "y", "z", "w" } }; +const std::array ZERO123 = { { "0", "1", "2", "3" } }; -QMap buffer_helpers::ATTRIBUTES{ +QMap ATTRIBUTES{ {"position", gpu::Stream::POSITION }, {"normal", gpu::Stream::NORMAL }, {"color", gpu::Stream::COLOR }, @@ -64,7 +62,7 @@ namespace { } } -void buffer_helpers::packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint32& packedNormal, glm::uint32& packedTangent) { +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)); @@ -90,27 +88,25 @@ void buffer_helpers::packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, g packedTangent = tangentStruct.pack; } -namespace { - template - glm::uint32 forEachGlmVec(const gpu::BufferView& view, std::function func) { - QVector result; - const glm::uint32 num = (glm::uint32)view.getNumElements(); - glm::uint32 i = 0; - for (; i < num; i++) { - if (!func(i, view.get(i))) { - break; - } +template +glm::uint32 forEachGlmVec(const gpu::BufferView& view, std::function func) { + QVector result; + const glm::uint32 num = (glm::uint32)view.getNumElements(); + glm::uint32 i = 0; + for (; i < num; i++) { + if (!func(i, view.get(i))) { + break; } - return i; } + return i; } -template<> glm::uint32 buffer_helpers::forEach(const gpu::BufferView& view, std::function func) { +template<> glm::uint32 forEach(const gpu::BufferView& view, std::function func) { return forEachGlmVec(view, func); } template -QVariant buffer_helpers::glmVecToVariant(const T& v, bool asArray /*= false*/) { +QVariant glmVecToVariant(const T& v, bool asArray /*= false*/) { static const auto len = T().length(); if (asArray) { QVariantList list; @@ -128,7 +124,7 @@ QVariant buffer_helpers::glmVecToVariant(const T& v, bool asArray /*= false*/) { } template -const T buffer_helpers::glmVecFromVariant(const QVariant& v) { +const T glmVecFromVariant(const QVariant& v) { auto isMap = v.type() == (QVariant::Type)QMetaType::QVariantMap; static const auto len = T().length(); const auto& components = isMap ? XYZW : ZERO123; @@ -155,23 +151,21 @@ const T buffer_helpers::glmVecFromVariant(const QVariant& v) { // QVector => BufferView template -gpu::BufferView buffer_helpers::newFromVector(const QVector& elements, const gpu::Element& elementType) { +gpu::BufferView newFromVector(const QVector& elements, const gpu::Element& elementType) { auto vertexBuffer = std::make_shared(elements.size() * sizeof(T), (gpu::Byte*)elements.data()); return { vertexBuffer, 0, vertexBuffer->getSize(),sizeof(T), elementType }; } -namespace { - template - gpu::BufferView bufferViewFromVector(const QVector& elements, const gpu::Element& elementType) { - auto vertexBuffer = std::make_shared(elements.size() * sizeof(T), (gpu::Byte*)elements.data()); - return { vertexBuffer, 0, vertexBuffer->getSize(),sizeof(T), elementType }; - } +template +gpu::BufferView bufferViewFromVector(const QVector& elements, const gpu::Element& elementType) { + auto vertexBuffer = std::make_shared(elements.size() * sizeof(T), (gpu::Byte*)elements.data()); + return { vertexBuffer, 0, vertexBuffer->getSize(),sizeof(T), elementType }; } -template<> gpu::BufferView buffer_helpers::newFromVector(const QVector& elements, const gpu::Element& elementType) { return bufferViewFromVector(elements, elementType); } -template<> gpu::BufferView buffer_helpers::newFromVector(const QVector& elements, const gpu::Element& elementType) { return bufferViewFromVector(elements, elementType); } -template<> gpu::BufferView buffer_helpers::newFromVector( const QVector& elements, const gpu::Element& elementType) { return bufferViewFromVector(elements, elementType); } -template<> gpu::BufferView buffer_helpers::newFromVector(const QVector& elements, const gpu::Element& elementType) { return bufferViewFromVector(elements, elementType); } -template<> gpu::BufferView buffer_helpers::newFromVector(const QVector& elements, const gpu::Element& elementType) { return bufferViewFromVector(elements, elementType); } +template<> gpu::BufferView newFromVector(const QVector& elements, const gpu::Element& elementType) { return bufferViewFromVector(elements, elementType); } +template<> gpu::BufferView newFromVector(const QVector& elements, const gpu::Element& elementType) { return bufferViewFromVector(elements, elementType); } +template<> gpu::BufferView newFromVector( const QVector& elements, const gpu::Element& elementType) { return bufferViewFromVector(elements, elementType); } +template<> gpu::BufferView newFromVector(const QVector& elements, const gpu::Element& elementType) { return bufferViewFromVector(elements, elementType); } +template<> gpu::BufferView newFromVector(const QVector& elements, const gpu::Element& elementType) { return bufferViewFromVector(elements, elementType); } struct GpuToGlmAdapter { static float error(const QString& name, const gpu::BufferView& view, glm::uint32 index, const char *hint) { @@ -356,24 +350,24 @@ struct GpuValueResolver { }; // BufferView => QVector -template QVector buffer_helpers::bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,U>::toVector(view, hint); } +template QVector bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,U>::toVector(view, hint); } -template<> QVector buffer_helpers::bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,int>::toVector(view, hint); } -template<> QVector buffer_helpers::bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,glm::uint16>::toVector(view, hint); } -template<> QVector buffer_helpers::bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,glm::uint32>::toVector(view, hint); } -template<> QVector buffer_helpers::bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,glm::vec2>::toVector(view, hint); } -template<> QVector buffer_helpers::bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,glm::vec3>::toVector(view, hint); } -template<> QVector buffer_helpers::bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,glm::vec4>::toVector(view, hint); } +template<> QVector bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,int>::toVector(view, hint); } +template<> QVector bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,glm::uint16>::toVector(view, hint); } +template<> QVector bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,glm::uint32>::toVector(view, hint); } +template<> QVector bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,glm::vec2>::toVector(view, hint); } +template<> QVector bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,glm::vec3>::toVector(view, hint); } +template<> QVector bufferToVector(const gpu::BufferView& view, const char *hint) { return GpuValueResolver,glm::vec4>::toVector(view, hint); } // view.get with conversion between types -template<> int buffer_helpers::getValue(const gpu::BufferView& view, glm::uint32 index, const char *hint) { return GpuScalarToGlm::get(view, index, hint); } -template<> glm::uint32 buffer_helpers::getValue(const gpu::BufferView& view, glm::uint32 index, const char *hint) { return GpuScalarToGlm::get(view, index, hint); } -template<> glm::vec2 buffer_helpers::getValue(const gpu::BufferView& view, glm::uint32 index, const char *hint) { return GpuVec2ToGlm::get(view, index, hint); } -template<> glm::vec3 buffer_helpers::getValue(const gpu::BufferView& view, glm::uint32 index, const char *hint) { return GpuVec3ToGlm::get(view, index, hint); } -template<> glm::vec4 buffer_helpers::getValue(const gpu::BufferView& view, glm::uint32 index, const char *hint) { return GpuVec4ToGlm::get(view, index, hint); } +template<> int getValue(const gpu::BufferView& view, glm::uint32 index, const char *hint) { return GpuScalarToGlm::get(view, index, hint); } +template<> glm::uint32 getValue(const gpu::BufferView& view, glm::uint32 index, const char *hint) { return GpuScalarToGlm::get(view, index, hint); } +template<> glm::vec2 getValue(const gpu::BufferView& view, glm::uint32 index, const char *hint) { return GpuVec2ToGlm::get(view, index, hint); } +template<> glm::vec3 getValue(const gpu::BufferView& view, glm::uint32 index, const char *hint) { return GpuVec3ToGlm::get(view, index, hint); } +template<> glm::vec4 getValue(const gpu::BufferView& view, glm::uint32 index, const char *hint) { return GpuVec4ToGlm::get(view, index, hint); } // bufferView => QVariant -template<> QVariant buffer_helpers::getValue(const gpu::BufferView& view, glm::uint32 index, const char* hint) { +template<> QVariant getValue(const gpu::BufferView& view, glm::uint32 index, const char* hint) { if (!boundsCheck(view, index)) { qDebug() << "getValue -- out of bounds" << index << hint; return false; @@ -402,24 +396,8 @@ template<> QVariant buffer_helpers::getValue(const gpu::BufferView& vi return QVariant(); } -glm::uint32 buffer_helpers::mesh::forEachVertex(const graphics::MeshPointer& mesh, std::function func) { - glm::uint32 i = 0; - auto attributeViews = getAllBufferViews(mesh); - auto nPositions = mesh->getNumVertices(); - for (; i < nPositions; i++) { - QVariantMap values; - for (const auto& a : attributeViews) { - values[a.first] = buffer_helpers::getValue(a.second, i, qUtf8Printable(a.first)); - } - if (!func(i, values)) { - break; - } - } - return i; -} - // view.edit with conversion between types -template<> bool buffer_helpers::setValue(const gpu::BufferView& view, glm::uint32 index, const QVariant& v, const char* hint) { +template<> bool setValue(const gpu::BufferView& view, glm::uint32 index, const QVariant& v, const char* hint) { if (!boundsCheck(view, index)) { qDebug() << "setValue -- out of bounds" << index << hint; return false; @@ -450,80 +428,51 @@ template<> bool buffer_helpers::setValue(const gpu::BufferView& view, return false; } -template<> bool buffer_helpers::setValue(const gpu::BufferView& view, glm::uint32 index, const glm::uint32& value, const char* hint) { +template<> bool setValue(const gpu::BufferView& view, glm::uint32 index, const glm::uint32& value, const char* hint) { return GpuScalarToGlm::set(view, index, value, hint); } -template<> bool buffer_helpers::setValue(const gpu::BufferView& view, glm::uint32 index, const glm::uint16& value, const char* hint) { +template<> bool setValue(const gpu::BufferView& view, glm::uint32 index, const glm::uint16& value, const char* hint) { return GpuScalarToGlm::set(view, index, value, hint); } -template<> bool buffer_helpers::setValue(const gpu::BufferView& view, glm::uint32 index, const glm::vec2& value, const char* hint) { +template<> bool setValue(const gpu::BufferView& view, glm::uint32 index, const glm::vec2& value, const char* hint) { return GpuVec2ToGlm::set(view, index, value, hint); } -template<> bool buffer_helpers::setValue(const gpu::BufferView& view, glm::uint32 index, const glm::vec3& value, const char* hint) { +template<> bool setValue(const gpu::BufferView& view, glm::uint32 index, const glm::vec3& value, const char* hint) { return GpuVec3ToGlm::set(view, index, value, hint); } -template<> bool buffer_helpers::setValue(const gpu::BufferView& view, glm::uint32 index, const glm::vec4& value, const char* hint) { +template<> bool setValue(const gpu::BufferView& view, glm::uint32 index, const glm::vec4& value, const char* hint) { return GpuVec4ToGlm::set(view, index, value, hint); } -bool buffer_helpers::mesh::setVertexAttributes(const graphics::MeshPointer& mesh, glm::uint32 index, const QVariantMap& attributes) { - bool ok = true; - for (auto& a : getAllBufferViews(mesh)) { - const auto& name = a.first; - if (attributes.contains(name)) { - const auto& value = attributes.value(name); - if (value.isValid()) { - auto& view = a.second; - buffer_helpers::setValue(view, index, value); - } else { - ok = false; - //qCDebug(graphics_scripting) << "(skipping) setVertexAttributes" << vertexIndex << name; - } - } - } - return ok; -} - -QVariant buffer_helpers::mesh::getVertexAttributes(const graphics::MeshPointer& mesh, glm::uint32 vertexIndex) { - auto attributeViews = getAllBufferViews(mesh); - QVariantMap values; - for (const auto& a : attributeViews) { - values[a.first] = buffer_helpers::getValue(a.second, vertexIndex, qUtf8Printable(a.first)); - } - return values; -} - // QVariantList => QVector -namespace { - template QVector qVariantListToGlmVector(const QVariantList& list) { - QVector output; - output.resize(list.size()); - int i = 0; - for (const auto& v : list) { - output[i++] = buffer_helpers::glmVecFromVariant(v); - } - return output; +template QVector qVariantListToGlmVector(const QVariantList& list) { + QVector output; + output.resize(list.size()); + int i = 0; + for (const auto& v : list) { + output[i++] = glmVecFromVariant(v); } - template QVector qVariantListToScalarVector(const QVariantList& list) { - QVector output; - output.resize(list.size()); - int i = 0; - for (const auto& v : list) { - output[i++] = v.value(); - } - return output; + return output; +} +template QVector qVariantListToScalarVector(const QVariantList& list) { + QVector output; + output.resize(list.size()); + int i = 0; + for (const auto& v : list) { + output[i++] = v.value(); } + return output; } -template QVector buffer_helpers::variantToVector(const QVariant& value) { qDebug() << "variantToVector[class]"; return qVariantListToGlmVector(value.toList()); } -template<> QVector buffer_helpers::variantToVector(const QVariant& value) { return qVariantListToScalarVector(value.toList()); } -template<> QVector buffer_helpers::variantToVector(const QVariant& value) { return qVariantListToScalarVector(value.toList()); } -template<> QVector buffer_helpers::variantToVector(const QVariant& value) { return qVariantListToScalarVector(value.toList()); } -template<> QVector buffer_helpers::variantToVector(const QVariant& value) { return qVariantListToGlmVector(value.toList()); } -template<> QVector buffer_helpers::variantToVector(const QVariant& value) { return qVariantListToGlmVector(value.toList()); } -template<> QVector buffer_helpers::variantToVector(const QVariant& value) { return qVariantListToGlmVector(value.toList()); } +template QVector variantToVector(const QVariant& value) { qDebug() << "variantToVector[class]"; return qVariantListToGlmVector(value.toList()); } +template<> QVector variantToVector(const QVariant& value) { return qVariantListToScalarVector(value.toList()); } +template<> QVector variantToVector(const QVariant& value) { return qVariantListToScalarVector(value.toList()); } +template<> QVector variantToVector(const QVariant& value) { return qVariantListToScalarVector(value.toList()); } +template<> QVector variantToVector(const QVariant& value) { return qVariantListToGlmVector(value.toList()); } +template<> QVector variantToVector(const QVariant& value) { return qVariantListToGlmVector(value.toList()); } +template<> QVector variantToVector(const QVariant& value) { return qVariantListToGlmVector(value.toList()); } -template<> gpu::BufferView buffer_helpers::newFromVector(const QVector& _elements, const gpu::Element& elementType) { +template<> gpu::BufferView newFromVector(const QVector& _elements, const gpu::Element& elementType) { glm::uint32 numElements = _elements.size(); auto buffer = new gpu::Buffer(); buffer->resize(elementType.getSize() * numElements); @@ -535,14 +484,14 @@ template<> gpu::BufferView buffer_helpers::newFromVector(const QVector } -gpu::BufferView buffer_helpers::clone(const gpu::BufferView& input) { +gpu::BufferView clone(const gpu::BufferView& input) { return gpu::BufferView( std::make_shared(input._buffer->getSize(), input._buffer->getData()), input._offset, input._size, input._stride, input._element ); } -gpu::BufferView buffer_helpers::resized(const gpu::BufferView& input, glm::uint32 numElements) { +gpu::BufferView resized(const gpu::BufferView& input, glm::uint32 numElements) { #ifdef DEBUG_BUFFERVIEW_HELPERS auto effectiveSize = input._buffer->getSize() / input.getNumElements(); qCDebug(bufferhelper_logging) << "resize input" << input.getNumElements() << input._buffer->getSize() << "effectiveSize" << effectiveSize; @@ -559,36 +508,85 @@ gpu::BufferView buffer_helpers::resized(const gpu::BufferView& input, glm::uint3 return output; } -graphics::MeshPointer buffer_helpers::mesh::clone(const graphics::MeshPointer& mesh) { - auto clone = std::make_shared(); - clone->displayName = (QString::fromStdString(mesh->displayName) + "-clone").toStdString(); - clone->setIndexBuffer(buffer_helpers::clone(mesh->getIndexBuffer())); - clone->setPartBuffer(buffer_helpers::clone(mesh->getPartBuffer())); - auto attributeViews = buffer_helpers::mesh::getAllBufferViews(mesh); - for (const auto& a : attributeViews) { - auto& view = a.second; - auto slot = buffer_helpers::ATTRIBUTES[a.first]; - auto points = buffer_helpers::clone(view); - if (slot == gpu::Stream::POSITION) { - clone->setVertexBuffer(points); - } else { - clone->addAttribute(slot, points); - } +// mesh helpers +namespace mesh { + gpu::BufferView getBufferView(const graphics::MeshPointer& mesh, gpu::Stream::Slot slot) { + return slot == gpu::Stream::POSITION ? mesh->getVertexBuffer() : mesh->getAttributeBuffer(slot); } - return clone; -} -std::map buffer_helpers::mesh::getAllBufferViews(const graphics::MeshPointer& mesh) { - std::map attributeViews; - if (!mesh) { + glm::uint32 forEachVertex(const graphics::MeshPointer& mesh, std::function func) { + glm::uint32 i = 0; + auto attributeViews = getAllBufferViews(mesh); + auto nPositions = mesh->getNumVertices(); + for (; i < nPositions; i++) { + QVariantMap values; + for (const auto& a : attributeViews) { + values[a.first] = getValue(a.second, i, qUtf8Printable(a.first)); + } + if (!func(i, values)) { + break; + } + } + return i; + } + bool setVertexAttributes(const graphics::MeshPointer& mesh, glm::uint32 index, const QVariantMap& attributes) { + bool ok = true; + for (auto& a : getAllBufferViews(mesh)) { + const auto& name = a.first; + if (attributes.contains(name)) { + const auto& value = attributes.value(name); + if (value.isValid()) { + auto& view = a.second; + setValue(view, index, value); + } else { + ok = false; + //qCDebug(graphics_scripting) << "(skipping) setVertexAttributes" << vertexIndex << name; + } + } + } + return ok; + } + + QVariant getVertexAttributes(const graphics::MeshPointer& mesh, glm::uint32 vertexIndex) { + auto attributeViews = getAllBufferViews(mesh); + QVariantMap values; + for (const auto& a : attributeViews) { + values[a.first] = getValue(a.second, vertexIndex, qUtf8Printable(a.first)); + } + return values; + } + + graphics::MeshPointer clone(const graphics::MeshPointer& mesh) { + auto clonedMesh = std::make_shared(); + clonedMesh->displayName = (QString::fromStdString(mesh->displayName) + "-clone").toStdString(); + clonedMesh->setIndexBuffer(buffer_helpers::clone(mesh->getIndexBuffer())); + clonedMesh->setPartBuffer(buffer_helpers::clone(mesh->getPartBuffer())); + auto attributeViews = getAllBufferViews(mesh); + for (const auto& a : attributeViews) { + auto& view = a.second; + auto slot = ATTRIBUTES[a.first]; + auto points = buffer_helpers::clone(view); + if (slot == gpu::Stream::POSITION) { + clonedMesh->setVertexBuffer(points); + } else { + clonedMesh->addAttribute(slot, points); + } + } + return clonedMesh; + } + + std::map getAllBufferViews(const graphics::MeshPointer& mesh) { + std::map attributeViews; + if (!mesh) { + return attributeViews; + } + for (const auto& a : ATTRIBUTES.toStdMap()) { + auto bufferView = getBufferView(mesh, a.second); + if (bufferView.getNumElements()) { + attributeViews[a.first] = bufferView; + } + } return attributeViews; } - for (const auto& a : buffer_helpers::ATTRIBUTES.toStdMap()) { - auto bufferView = getBufferView(mesh, a.second); - if (bufferView.getNumElements()) { - attributeViews[a.first] = bufferView; - } - } - return attributeViews; -} - +} // mesh +} // buffer_helpers From 3dbe5d79bb9900f43dd8edfd6e1e8db164d3eb3d Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 26 Feb 2018 06:01:16 -0500 Subject: [PATCH 27/29] fix windows punctilious warnings --- .../src/graphics-scripting/ScriptableMesh.cpp | 14 +++++++------- libraries/render-utils/src/Model.cpp | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp index e5e58b847a..5ff916275e 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp @@ -131,10 +131,10 @@ QVariantMap scriptable::ScriptableMesh::getBufferFormats() const { auto bufferView = buffer_helpers::mesh::getBufferView(getMeshPointer(), a.second); result[a.first] = QVariantMap{ { "slot", a.second }, - { "length", (quint32)bufferView.getNumElements() }, - { "byteLength", (quint32)bufferView._size }, - { "offset", (quint32) bufferView._offset }, - { "stride", (quint32)bufferView._stride }, + { "length", (glm::uint32)bufferView.getNumElements() }, + { "byteLength", (glm::uint32)bufferView._size }, + { "offset", (glm::uint32) bufferView._offset }, + { "stride", (glm::uint32)bufferView._stride }, { "element", scriptable::toVariant(bufferView._element) }, }; } @@ -171,7 +171,7 @@ glm::uint32 scriptable::ScriptableMesh::addAttribute(const QString& attributeNam return values.size(); } else { auto bufferView = buffer_helpers::mesh::getBufferView(mesh, slot); - auto current = bufferView.getNumElements(); + auto current = (glm::uint32)bufferView.getNumElements(); if (current < numVertices) { bufferView = buffer_helpers::resized(bufferView, numVertices); for (glm::uint32 i = current; i < numVertices; i++) { @@ -220,7 +220,7 @@ QVariantList scriptable::ScriptableMesh::queryVertexAttributes(QVariant selector } auto slotNum = _getSlotNumber(attributeName); const auto& bufferView = buffer_helpers::mesh::getBufferView(getMeshPointer(), static_cast(slotNum)); - glm::uint32 numElements = bufferView.getNumElements(); + glm::uint32 numElements = (glm::uint32)bufferView.getNumElements(); for (glm::uint32 i = 0; i < numElements; i++) { result << buffer_helpers::getValue(bufferView, i, qUtf8Printable(attributeName)); } @@ -339,7 +339,7 @@ bool scriptable::ScriptableMesh::isValidIndex(glm::uint32 vertexIndex, const QSt return false; } auto view = buffer_helpers::mesh::getBufferView(getMeshPointer(), static_cast(slotNum)); - if (vertexIndex >= view.getNumElements()) { + if (vertexIndex >= (glm::uint32)view.getNumElements()) { if (context()) { context()->throwError(QString("vertexIndex=%1 out of range (attribute=%2, numElements=%3)").arg(vertexIndex).arg(attributeName).arg(view.getNumElements())); } diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 0abeb70254..f6e3ce20f0 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -635,7 +635,7 @@ bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointe FBXMesh mesh; mesh._mesh = newMesh.getMeshPointer(); mesh.vertices = buffer_helpers::mesh::attributeToVector(mesh._mesh, gpu::Stream::POSITION); - int numParts = newMesh.getMeshPointer()->getNumParts(); + int numParts = (int)newMesh.getMeshPointer()->getNumParts(); for (int partID = 0; partID < numParts; partID++) { FBXMeshPart part; part.triangleIndices = buffer_helpers::bufferToVector(mesh._mesh->getIndexBuffer(), "part.triangleIndices"); From 767569bc4015e61bb5211e8ffc6da77ca2d2b19d Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 26 Feb 2018 18:00:22 -0500 Subject: [PATCH 28/29] * changes per CR feedback * make MeshPart.topology read-only * JS Graphics API starter documentation * remove redundant qscriptvalue sequence registrations --- .../src/graphics-scripting/Forward.h | 7 +- .../GraphicsScriptingInterface.cpp | 74 +++++++------------ .../GraphicsScriptingInterface.h | 35 +++++++-- .../src/graphics-scripting/ScriptableMesh.h | 11 ++- .../graphics-scripting/ScriptableMeshPart.cpp | 29 ++++++-- .../graphics-scripting/ScriptableMeshPart.h | 15 +++- .../src/graphics-scripting/ScriptableModel.h | 10 ++- .../graphics/src/graphics/GpuHelpers.cpp | 3 +- 8 files changed, 116 insertions(+), 68 deletions(-) diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h index 2f2f191dce..38f45fbfd0 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/Forward.h +++ b/libraries/graphics-scripting/src/graphics-scripting/Forward.h @@ -94,7 +94,12 @@ namespace scriptable { using ScriptableMeshPointer = QPointer; class ScriptableMeshPart; using ScriptableMeshPartPointer = QPointer; - bool registerMetaTypes(QScriptEngine* engine); } +Q_DECLARE_METATYPE(glm::uint32) +Q_DECLARE_METATYPE(QVector) Q_DECLARE_METATYPE(NestableType) +Q_DECLARE_METATYPE(scriptable::MeshPointer) +Q_DECLARE_METATYPE(scriptable::WeakMeshPointer) +Q_DECLARE_METATYPE(scriptable::ModelProviderPointer) + diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp index ed8982b9c7..f3ba5e8406 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.cpp @@ -26,9 +26,6 @@ #include GraphicsScriptingInterface::GraphicsScriptingInterface(QObject* parent) : QObject(parent), QScriptable() { - if (auto scriptEngine = qobject_cast(parent)) { - this->registerMetaTypes(scriptEngine); - } } void GraphicsScriptingInterface::jsThrowError(const QString& error) { @@ -179,12 +176,17 @@ scriptable::ScriptableMeshPointer GraphicsScriptingInterface::newMesh(const QVar const auto numVertices = vertices.size(); const auto numIndices = indices.size(); - const auto topology = graphics::Mesh::TRIANGLES; + const auto topology = graphics::TOPOLOGIES.key(topologyName); + + // TODO: support additional topologies (POINTS and LINES ought to "just work" -- + // if MeshPartPayload::drawCall is updated to actually check the Mesh::Part::_topology value + // (TRIANGLE_STRIP, TRIANGLE_FAN, QUADS, QUAD_STRIP may need additional conversion code though) + static const QStringList acceptableTopologies{ "triangles" }; // sanity checks QString error; - if (!topologyName.isEmpty() && topologyName != "triangles") { - error = "expected 'triangles' or undefined for .topology"; + if (!topologyName.isEmpty() && !acceptableTopologies.contains(topologyName)) { + error = QString("expected .topology to be %1").arg(acceptableTopologies.join(" | ")); } else if (!numIndices) { error = QString("expected non-empty [uint32,...] array for .indices (got type=%1)").arg(ifsMeshData.value("indices").typeName()); } else if (numIndices % 3 != 0) { @@ -227,8 +229,8 @@ scriptable::ScriptableMeshPointer GraphicsScriptingInterface::newMesh(const QVar mesh->modelName = "graphics::newMesh"; mesh->displayName = meshName.toStdString(); - // TODO: newFromVector does no conversion -- later we could autodetect if fitting into gpu::INDEX_UINT16 - // and also pack other values (like NORMAL / TEXCOORD0 where relevant) + // TODO: newFromVector does inbound type conversion, but not compression or packing + // (later we should autodetect if fitting into gpu::INDEX_UINT16 and reduce / pack normals etc.) mesh->setIndexBuffer(buffer_helpers::newFromVector(indices, gpu::Format::INDEX_INT32)); mesh->setVertexBuffer(buffer_helpers::newFromVector(vertices, gpu::Format::VEC3F_XYZ)); if (normals.size()) { @@ -240,7 +242,7 @@ scriptable::ScriptableMeshPointer GraphicsScriptingInterface::newMesh(const QVar if (texCoords0.size()) { mesh->addAttribute(gpu::Stream::TEXCOORD0, buffer_helpers::newFromVector(texCoords0, gpu::Format::VEC2F_UV)); } - QVector parts = {{ 0, indices.size(), 0, topology}}; + QVector parts = {{ 0, indices.size(), 0, topology }}; mesh->setPartBuffer(buffer_helpers::newFromVector(parts, gpu::Element::PART_DRAWCALL)); return scriptable::make_scriptowned(mesh, nullptr); } @@ -262,10 +264,6 @@ QString GraphicsScriptingInterface::exportModelToOBJ(const scriptable::Scriptabl return QString(); } -void GraphicsScriptingInterface::registerMetaTypes(QScriptEngine* engine) { - scriptable::registerMetaTypes(engine); -} - MeshPointer GraphicsScriptingInterface::getMeshPointer(const scriptable::ScriptableMesh& meshProxy) { return meshProxy.getMeshPointer(); } @@ -287,14 +285,6 @@ MeshPointer GraphicsScriptingInterface::getMeshPointer(scriptable::ScriptableMes } namespace { - QScriptValue qVectorUInt32ToScriptValue(QScriptEngine* engine, const QVector& vector) { - return qScriptValueFromSequence(engine, vector); - } - - void qVectorUInt32FromScriptValue(const QScriptValue& array, QVector& result) { - qScriptValueToSequence(array, result); - } - QVector metaTypeIds{ qRegisterMetaType("uint32"), qRegisterMetaType("glm::uint32"), @@ -312,7 +302,7 @@ namespace { } namespace scriptable { - template int registerQPointerThing(QScriptEngine* engine) { + template int registerQPointerMetaType(QScriptEngine* engine) { qScriptRegisterSequenceMetaType>>(engine); return qScriptRegisterMetaType>( engine, @@ -346,44 +336,32 @@ namespace scriptable { } template int registerDebugEnum(QScriptEngine* engine, const DebugEnums& debugEnums) { - static const DebugEnums& poop = debugEnums; + static const DebugEnums& instance = debugEnums; return qScriptRegisterMetaType( engine, [](QScriptEngine* engine, const T& topology) -> QScriptValue { - return poop.value(topology); + return instance.value(topology); }, [](const QScriptValue& value, T& topology) { - topology = poop.key(value.toString()); + topology = instance.key(value.toString()); } ); } +} - bool registerMetaTypes(QScriptEngine* engine) { - qScriptRegisterSequenceMetaType>(engine); +void GraphicsScriptingInterface::registerMetaTypes(QScriptEngine* engine) { + qScriptRegisterSequenceMetaType>(engine); - qScriptRegisterMetaType(engine, qVectorUInt32ToScriptValue, qVectorUInt32FromScriptValue); + scriptable::registerQPointerMetaType(engine); + scriptable::registerQPointerMetaType(engine); + scriptable::registerQPointerMetaType(engine); - registerQPointerThing(engine); - registerQPointerThing(engine); - registerQPointerThing(engine); - qScriptRegisterMetaType>( - engine, - [](QScriptEngine* engine, const QVector& vector) -> QScriptValue { - return qScriptValueFromSequence(engine, vector); - }, - [](const QScriptValue& array, QVector& result) { - qScriptValueToSequence(array, result); - } - ); - - registerDebugEnum(engine, graphics::TOPOLOGIES); - registerDebugEnum(engine, gpu::TYPES); - registerDebugEnum(engine, gpu::SEMANTICS); - registerDebugEnum(engine, gpu::DIMENSIONS); - - return metaTypeIds.size(); - } + scriptable::registerDebugEnum(engine, graphics::TOPOLOGIES); + scriptable::registerDebugEnum(engine, gpu::TYPES); + scriptable::registerDebugEnum(engine, gpu::SEMANTICS); + scriptable::registerDebugEnum(engine, gpu::DIMENSIONS); + Q_UNUSED(metaTypeIds); } #include "GraphicsScriptingInterface.moc" diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h index a66e382bc7..2e86ab01e2 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h @@ -20,6 +20,12 @@ #include "ScriptableMesh.h" #include + +/**jsdoc + * The experimental Graphics API (experimental) lets you query and manage certain graphics-related structures (like underlying meshes and textures) from scripting. + * @namespace Graphics + */ + class GraphicsScriptingInterface : public QObject, public QScriptable, public Dependency { Q_OBJECT @@ -29,15 +35,36 @@ public: public slots: /**jsdoc - * Returns the model/meshes associated with a UUID (entityID, overlayID, or avatarID) + * Returns a model reference object associated with the specified UUID ({@link EntityID}, {@link OverlayID}, or {@link AvatarID}). * - * @function GraphicsScriptingInterface.getModel - * @param {UUID} The objectID of the model whose meshes are to be retrieve + * @function Graphics.getModel + * @param {UUID} The objectID of the model whose meshes are to be retrieved. + * @return {Graphics.Model} the resulting Model object */ scriptable::ScriptableModelPointer getModel(QUuid uuid); + bool updateModel(QUuid uuid, const scriptable::ScriptableModelPointer& model); + bool canUpdateModel(QUuid uuid, int meshIndex = -1, int partNumber = -1); + scriptable::ScriptableModelPointer newModel(const scriptable::ScriptableMeshes& meshes); + + /**jsdoc + * Create a new Mesh / Mesh Part with the specified data buffers. + * + * @function Graphics.newMesh + * @param {Graphics.IFSData} ifsMeshData Index-Faced Set (IFS) arrays used to create the new mesh. + * @return {Graphics.Mesh} the resulting Mesh / Mesh Part object + */ + /**jsdoc + * @typedef {object} Graphics.IFSData + * @property {string} [name] - mesh name (useful for debugging / debug prints). + * @property {number[]} indices - vertex indices to use for the mesh faces. + * @property {Vec3[]} vertices - vertex positions (model space) + * @property {Vec3[]} [normals] - vertex normals (normalized) + * @property {Vec3[]} [colors] - vertex colors (normalized) + * @property {Vec2[]} [texCoords0] - vertex texture coordinates (normalized) + */ scriptable::ScriptableMeshPointer newMesh(const QVariantMap& ifsMeshData); #ifdef SCRIPTABLE_MESH_TODO @@ -58,6 +85,4 @@ private: }; -Q_DECLARE_METATYPE(scriptable::ModelProviderPointer) - #endif // hifi_GraphicsScriptingInterface_h diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h index 16393de8c7..50299b8d20 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h @@ -28,6 +28,15 @@ #include namespace scriptable { + /**jsdoc + * @typedef {object} Graphics.Mesh + * @property {Graphics.MeshPart[]} parts - Array of submesh part references. + * @property {string[]} attributeNames - Vertex attribute names (color, normal, etc.) + * @property {number} numParts - The number of parts contained in the mesh. + * @property {number} numIndices - Total number of vertex indices in the mesh. + * @property {number} numVertices - Total number of vertices in the Mesh. + * @property {number} numAttributes - Number of currently defined vertex attributes. + */ class ScriptableMesh : public ScriptableMeshBase, QScriptable { Q_OBJECT public: @@ -97,5 +106,3 @@ namespace scriptable { Q_DECLARE_METATYPE(scriptable::ScriptableMeshPointer) Q_DECLARE_METATYPE(QVector) -Q_DECLARE_METATYPE(glm::uint32) -Q_DECLARE_METATYPE(QVector) diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp index 9d6359bee3..ac48b23323 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp @@ -21,6 +21,7 @@ #include #include + QString scriptable::ScriptableMeshPart::toOBJ() { if (!getMeshPointer()) { if (context()) { @@ -371,6 +372,7 @@ bool scriptable::ScriptableMeshPart::setIndices(const QVector& indi if (len != getNumVertices()) { context()->throwError(QString("setIndices: currently new indicies must be assign 1:1 across old indicies (indicies.size()=%1, numIndices=%2)") .arg(len).arg(getNumIndices())); + return false; } auto mesh = getMeshPointer(); auto indexBuffer = mesh->getIndexBuffer(); @@ -397,18 +399,24 @@ const graphics::Mesh::Part& scriptable::ScriptableMeshPart::getMeshPart() const return getMeshPointer()->getPartBuffer().get(partIndex); } +// FIXME: how we handle topology will need to be reworked if wanting to support TRIANGLE_STRIP, QUADS and QUAD_STRIP bool scriptable::ScriptableMeshPart::setTopology(graphics::Mesh::Topology topology) { if (!isValid()) { return false; } auto& part = getMeshPointer()->getPartBuffer().edit(partIndex); - if (topology == graphics::Mesh::Topology::POINTS || - topology == graphics::Mesh::Topology::LINES || - topology == graphics::Mesh::Topology::TRIANGLES) { + switch (topology) { +#ifdef DEV_BUILD + case graphics::Mesh::Topology::POINTS: + case graphics::Mesh::Topology::LINES: +#endif + case graphics::Mesh::Topology::TRIANGLES: part._topology = topology; return true; + default: + context()->throwError("changing topology to " + graphics::toString(topology) + " is not yet supported"); + return false; } - return false; } glm::uint32 scriptable::ScriptableMeshPart::getTopologyLength() const { @@ -416,16 +424,23 @@ glm::uint32 scriptable::ScriptableMeshPart::getTopologyLength() const { case graphics::Mesh::Topology::POINTS: return 1; case graphics::Mesh::Topology::LINES: return 2; case graphics::Mesh::Topology::TRIANGLES: return 3; + case graphics::Mesh::Topology::QUADS: return 4; default: qCDebug(graphics_scripting) << "getTopologyLength -- unrecognized topology" << getTopology(); } return 0; } QVector scriptable::ScriptableMeshPart::getFace(glm::uint32 faceIndex) const { - if (faceIndex < getNumFaces()) { - return getIndices().mid(faceIndex * getTopologyLength(), getTopologyLength()); + switch (getTopology()) { + case graphics::Mesh::Topology::POINTS: + case graphics::Mesh::Topology::LINES: + case graphics::Mesh::Topology::TRIANGLES: + case graphics::Mesh::Topology::QUADS: + if (faceIndex < getNumFaces()) { + return getIndices().mid(faceIndex * getTopologyLength(), getTopologyLength()); + } + default: return QVector(); } - return QVector(); } QVariantMap scriptable::ScriptableMeshPart::getPartExtents() const { diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h index 4ef0465ca3..dd71d9b998 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.h @@ -10,6 +10,18 @@ #include "ScriptableMesh.h" namespace scriptable { + /**jsdoc + * @typedef {object} Graphics.MeshPart + * @property {number} partIndex - The part index (within the containing Mesh). + * @property {Graphics.Topology} topology - element interpretation (currently only 'triangles' is supported). + * @property {string[]} attributeNames - Vertex attribute names (color, normal, etc.) + * @property {number} numIndices - Number of vertex indices that this mesh part refers to. + * @property {number} numVerticesPerFace - Number of vertices per face (eg: 3 when topology is 'triangles'). + * @property {number} numFaces - Number of faces represented by the mesh part (numIndices / numVerticesPerFace). + * @property {number} numVertices - Total number of vertices in the containing Mesh. + * @property {number} numAttributes - Number of currently defined vertex attributes. + */ + class ScriptableMeshPart : public QObject, QScriptable { Q_OBJECT Q_PROPERTY(bool valid READ isValid) @@ -18,7 +30,8 @@ namespace scriptable { Q_PROPERTY(glm::uint32 baseVertexIndex READ getBaseVertexIndex WRITE setBaseVertexIndex) Q_PROPERTY(glm::uint32 lastVertexIndex READ getLastVertexIndex WRITE setLastVertexIndex) Q_PROPERTY(int numVerticesPerFace READ getTopologyLength) - Q_PROPERTY(graphics::Mesh::Topology topology READ getTopology WRITE setTopology) + // NOTE: making read-only for now (see also GraphicsScriptingInterface::newMesh and MeshPartPayload::drawCall) + Q_PROPERTY(graphics::Mesh::Topology topology READ getTopology) Q_PROPERTY(glm::uint32 numFaces READ getNumFaces) Q_PROPERTY(glm::uint32 numAttributes READ getNumAttributes) diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h index d821f1224d..d496323c1c 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h @@ -15,6 +15,14 @@ class QScriptValue; namespace scriptable { using ScriptableMeshes = QVector; + + /**jsdoc + * @typedef {object} Graphics.Model + * @property {Uuid} objectID - UUID of corresponding inworld object (if model is associated) + * @property {number} numMeshes - The number of submeshes contained in the model. + * @property {Graphics.Mesh[]} meshes - Array of submesh references. + */ + class ScriptableModel : public ScriptableModelBase { Q_OBJECT Q_PROPERTY(QUuid objectID MEMBER objectID CONSTANT) @@ -45,8 +53,6 @@ namespace scriptable { } -Q_DECLARE_METATYPE(scriptable::MeshPointer) -Q_DECLARE_METATYPE(scriptable::WeakMeshPointer) Q_DECLARE_METATYPE(scriptable::ScriptableModelPointer) Q_DECLARE_METATYPE(scriptable::ScriptableModelBase) Q_DECLARE_METATYPE(scriptable::ScriptableModelBasePointer) diff --git a/libraries/graphics/src/graphics/GpuHelpers.cpp b/libraries/graphics/src/graphics/GpuHelpers.cpp index 63393df5e1..0c3bd945e1 100644 --- a/libraries/graphics/src/graphics/GpuHelpers.cpp +++ b/libraries/graphics/src/graphics/GpuHelpers.cpp @@ -21,8 +21,7 @@ namespace graphics { }; } namespace gpu { - - DebugEnums TYPES{ + DebugEnums TYPES{ { Type::FLOAT, "float" }, { Type::INT32, "int32" }, { Type::UINT32, "uint32" }, From d3bae870660a43c24e8b8738dfc6be51ac63e2a3 Mon Sep 17 00:00:00 2001 From: humbletim Date: Mon, 26 Feb 2018 19:07:55 -0500 Subject: [PATCH 29/29] * remove unneeded Q_DECLARE_METATYPEs * CR feedback --- .../src/graphics-scripting/Forward.h | 8 -------- .../GraphicsScriptingInterface.h | 4 ++++ .../src/graphics-scripting/ScriptableMesh.cpp | 18 +++++++++--------- .../src/graphics-scripting/ScriptableMesh.h | 3 +-- .../graphics-scripting/ScriptableMeshPart.cpp | 4 ++-- .../src/graphics-scripting/ScriptableModel.h | 5 +---- libraries/render-utils/src/Model.cpp | 11 ++++++++--- 7 files changed, 25 insertions(+), 28 deletions(-) diff --git a/libraries/graphics-scripting/src/graphics-scripting/Forward.h b/libraries/graphics-scripting/src/graphics-scripting/Forward.h index 38f45fbfd0..a0e186dda5 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/Forward.h +++ b/libraries/graphics-scripting/src/graphics-scripting/Forward.h @@ -95,11 +95,3 @@ namespace scriptable { class ScriptableMeshPart; using ScriptableMeshPartPointer = QPointer; } - -Q_DECLARE_METATYPE(glm::uint32) -Q_DECLARE_METATYPE(QVector) -Q_DECLARE_METATYPE(NestableType) -Q_DECLARE_METATYPE(scriptable::MeshPointer) -Q_DECLARE_METATYPE(scriptable::WeakMeshPointer) -Q_DECLARE_METATYPE(scriptable::ModelProviderPointer) - diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h index 2e86ab01e2..84c6cb6fa8 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h +++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h @@ -85,4 +85,8 @@ private: }; +Q_DECLARE_METATYPE(glm::uint32) +Q_DECLARE_METATYPE(QVector) +Q_DECLARE_METATYPE(NestableType) + #endif // hifi_GraphicsScriptingInterface_h diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp index 5ff916275e..bc31d45229 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.cpp @@ -118,7 +118,7 @@ bool scriptable::ScriptableMesh::setVertexAttributes(glm::uint32 vertexIndex, co return buffer_helpers::mesh::setVertexAttributes(getMeshPointer(), vertexIndex, attributes); } -int scriptable::ScriptableMesh::_getSlotNumber(const QString& attributeName) const { +int scriptable::ScriptableMesh::getSlotNumber(const QString& attributeName) const { if (auto mesh = getMeshPointer()) { return buffer_helpers::ATTRIBUTES.value(attributeName, -1); } @@ -142,9 +142,9 @@ QVariantMap scriptable::ScriptableMesh::getBufferFormats() const { } bool scriptable::ScriptableMesh::removeAttribute(const QString& attributeName) { - auto slot = isValid() ? _getSlotNumber(attributeName) : -1; + auto slot = isValid() ? getSlotNumber(attributeName) : -1; if (slot < 0) { - return 0; + return false; } if (slot == gpu::Stream::POSITION) { context()->throwError("cannot remove .position attribute"); @@ -158,7 +158,7 @@ bool scriptable::ScriptableMesh::removeAttribute(const QString& attributeName) { } glm::uint32 scriptable::ScriptableMesh::addAttribute(const QString& attributeName, const QVariant& defaultValue) { - auto slot = isValid() ? _getSlotNumber(attributeName) : -1; + auto slot = isValid() ? getSlotNumber(attributeName) : -1; if (slot < 0) { return 0; } @@ -187,7 +187,7 @@ glm::uint32 scriptable::ScriptableMesh::addAttribute(const QString& attributeNam } glm::uint32 scriptable::ScriptableMesh::fillAttribute(const QString& attributeName, const QVariant& value) { - auto slot = isValid() ? _getSlotNumber(attributeName) : -1; + auto slot = isValid() ? getSlotNumber(attributeName) : -1; if (slot < 0) { return 0; } @@ -218,7 +218,7 @@ QVariantList scriptable::ScriptableMesh::queryVertexAttributes(QVariant selector if (!isValidIndex(0, attributeName)) { return result; } - auto slotNum = _getSlotNumber(attributeName); + auto slotNum = getSlotNumber(attributeName); const auto& bufferView = buffer_helpers::mesh::getBufferView(getMeshPointer(), static_cast(slotNum)); glm::uint32 numElements = (glm::uint32)bufferView.getNumElements(); for (glm::uint32 i = 0; i < numElements; i++) { @@ -231,7 +231,7 @@ QVariant scriptable::ScriptableMesh::getVertexProperty(glm::uint32 vertexIndex, if (!isValidIndex(vertexIndex, attributeName)) { return QVariant(); } - auto slotNum = _getSlotNumber(attributeName); + auto slotNum = getSlotNumber(attributeName); const auto& bufferView = buffer_helpers::mesh::getBufferView(getMeshPointer(), static_cast(slotNum)); return buffer_helpers::getValue(bufferView, vertexIndex, qUtf8Printable(attributeName)); } @@ -240,7 +240,7 @@ bool scriptable::ScriptableMesh::setVertexProperty(glm::uint32 vertexIndex, cons if (!isValidIndex(vertexIndex, attributeName)) { return false; } - auto slotNum = _getSlotNumber(attributeName); + auto slotNum = getSlotNumber(attributeName); const auto& bufferView = buffer_helpers::mesh::getBufferView(getMeshPointer(), static_cast(slotNum)); return buffer_helpers::setValue(bufferView, vertexIndex, value); } @@ -331,7 +331,7 @@ bool scriptable::ScriptableMesh::isValidIndex(glm::uint32 vertexIndex, const QSt return false; } if (!attributeName.isEmpty()) { - auto slotNum = _getSlotNumber(attributeName); + auto slotNum = getSlotNumber(attributeName); if (slotNum < 0) { if (context()) { context()->throwError(QString("invalid attributeName=%1").arg(attributeName)); diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h index 50299b8d20..62a67aa5e6 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMesh.h @@ -74,9 +74,8 @@ namespace scriptable { QVector getMeshParts() const; QVariantMap getMeshExtents() const; - // TODO: remove Q_INVOKABLE (curently exposed for debugging ) - Q_INVOKABLE int _getSlotNumber(const QString& attributeName) const; operator bool() const { return !weakMesh.expired(); } + int getSlotNumber(const QString& attributeName) const; public slots: const scriptable::ScriptableModelPointer getParentModel() const { return qobject_cast(model); } diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp index ac48b23323..4414b0ad7e 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableMeshPart.cpp @@ -57,7 +57,7 @@ bool scriptable::ScriptableMeshPart::setVertexProperty(glm::uint32 vertexIndex, if (!isValidIndex(vertexIndex, attributeName)) { return false; } - auto slotNum = parentMesh->_getSlotNumber(attributeName); + auto slotNum = parentMesh->getSlotNumber(attributeName); const auto& bufferView = buffer_helpers::mesh::getBufferView(getMeshPointer(), static_cast(slotNum)); return buffer_helpers::setValue(bufferView, vertexIndex, value); } @@ -369,7 +369,7 @@ bool scriptable::ScriptableMeshPart::setIndices(const QVector& indi return false; } glm::uint32 len = indices.size(); - if (len != getNumVertices()) { + if (len != getNumIndices()) { context()->throwError(QString("setIndices: currently new indicies must be assign 1:1 across old indicies (indicies.size()=%1, numIndices=%2)") .arg(len).arg(getNumIndices())); return false; diff --git a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h index d496323c1c..9a4c4f695b 100644 --- a/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h +++ b/libraries/graphics-scripting/src/graphics-scripting/ScriptableModel.h @@ -44,8 +44,6 @@ namespace scriptable { scriptable::ScriptableModelPointer cloneModel(const QVariantMap& options = QVariantMap()); QString toString() const; - // QScriptEngine-specific wrappers - //glm::uint32 forEachMeshVertexAttribute(QScriptValue callback); protected: glm::uint32 getNumMeshes() { return meshes.size(); } @@ -54,5 +52,4 @@ namespace scriptable { } Q_DECLARE_METATYPE(scriptable::ScriptableModelPointer) -Q_DECLARE_METATYPE(scriptable::ScriptableModelBase) -Q_DECLARE_METATYPE(scriptable::ScriptableModelBasePointer) +Q_DECLARE_METATYPE(QVector) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index f6e3ce20f0..700ced1502 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -604,15 +604,20 @@ bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointe render::Transaction transaction; const render::ScenePointer& scene = AbstractViewStateInterface::instance()->getMain3DScene(); - meshIndex = meshIndex >= 0 ? meshIndex : 0; - partIndex = partIndex >= 0 ? partIndex : 0; + meshIndex = max(meshIndex, 0); + partIndex = max(partIndex, 0); - if (meshIndex >= meshes.size()) { + if (meshIndex >= (int)meshes.size()) { qDebug() << meshIndex << "meshIndex >= newModel.meshes.size()" << meshes.size(); return false; } auto mesh = meshes[meshIndex].getMeshPointer(); + + if (partIndex >= (int)mesh->getNumParts()) { + qDebug() << partIndex << "partIndex >= mesh->getNumParts()" << mesh->getNumParts(); + return false; + } { // update visual geometry render::Transaction transaction;