mirror of
https://github.com/AleziaKurdis/overte.git
synced 2025-04-18 00:47:30 +02:00
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
This commit is contained in:
parent
bc819c698e
commit
06afaa7470
51 changed files with 2242 additions and 600 deletions
|
@ -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
|
||||
|
|
|
@ -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 <src/scripting/LimitlessVoiceRecognitionScriptingInterface.h>
|
||||
#include <src/scripting/GooglePolyScriptingInterface.h>
|
||||
#include <EntityScriptClient.h>
|
||||
#include <ModelScriptingInterface.h>
|
||||
|
||||
#include <PickManager.h>
|
||||
#include <PointerManager.h>
|
||||
|
@ -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<EntityItemID>(uuid))) {
|
||||
provider = entityInterface;
|
||||
} else if (auto overlayInterface = getOverlayModelProvider(static_cast<OverlayID>(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<scriptable::ModelProvider>(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<Base3DOverlay>(overlay)) {
|
||||
provider = std::dynamic_pointer_cast<scriptable::ModelProvider>(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<AvatarManager>();
|
||||
if (auto avatar = avatarManager->getAvatarBySessionID(sessionUUID)) {
|
||||
if (avatar->getSessionUUID() == sessionUUID) {
|
||||
provider = std::dynamic_pointer_cast<scriptable::ModelProvider>(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<ResourceCacheSharedItems>();
|
||||
DependencyManager::set<DesktopScriptingInterface>();
|
||||
DependencyManager::set<EntityScriptingInterface>(true);
|
||||
DependencyManager::set<ModelScriptingInterface>();
|
||||
DependencyManager::registerInheritance<scriptable::ModelProviderFactory, ApplicationMeshProvider>();
|
||||
DependencyManager::set<ApplicationMeshProvider>();
|
||||
DependencyManager::set<RecordingScriptingInterface>();
|
||||
DependencyManager::set<WindowScriptingInterface>();
|
||||
DependencyManager::set<HMDScriptingInterface>();
|
||||
|
@ -5915,6 +5978,9 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEnginePointe
|
|||
scriptEngine->registerGlobalObject("Scene", DependencyManager::get<SceneScriptingInterface>().data());
|
||||
scriptEngine->registerGlobalObject("Render", _renderEngine->getConfiguration().get());
|
||||
|
||||
ModelScriptingInterface::registerMetaTypes(scriptEngine.data());
|
||||
scriptEngine->registerGlobalObject("Model", DependencyManager::get<ModelScriptingInterface>().data());
|
||||
|
||||
scriptEngine->registerGlobalObject("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
|
||||
scriptEngine->registerGlobalObject("Reticle", getApplicationCompositor().getReticleInterface());
|
||||
|
||||
|
|
|
@ -72,7 +72,6 @@
|
|||
|
||||
#include <procedural/ProceduralSkybox.h>
|
||||
#include <graphics/Skybox.h>
|
||||
#include <ModelScriptingInterface.h>
|
||||
#include "FrameTimingsScriptingInterface.h"
|
||||
|
||||
#include "Sound.h"
|
||||
|
|
|
@ -13,10 +13,11 @@
|
|||
|
||||
#include <Transform.h>
|
||||
#include <SpatiallyNestable.h>
|
||||
|
||||
#include <graphics-scripting/ScriptableModel.h>
|
||||
#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(); }
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -179,3 +179,18 @@ Transform Shape3DOverlay::evalRenderTransform() {
|
|||
transform.setRotation(rotation);
|
||||
return transform;
|
||||
}
|
||||
|
||||
scriptable::ScriptableModel Shape3DOverlay::getScriptableModel(bool* ok) {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
#include "ModelEntityItem.h"
|
||||
#include "RenderableModelEntityItem.h"
|
||||
|
||||
#include <graphics-scripting/ScriptableModel.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <AvatarData.h>
|
||||
#include <ShapeInfo.h>
|
||||
#include <render/Scene.h>
|
||||
#include <graphics-scripting/ScriptableModel.h>
|
||||
#include <GLMHelpers.h>
|
||||
|
||||
|
||||
|
@ -53,7 +54,7 @@ class Texture;
|
|||
|
||||
using AvatarPhysicsCallback = std::function<void(uint32_t)>;
|
||||
|
||||
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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -17,13 +17,14 @@
|
|||
#include <Sound.h>
|
||||
#include "AbstractViewStateInterface.h"
|
||||
#include "EntitiesRendererLogging.h"
|
||||
#include <graphics-scripting/ScriptableModel.h>
|
||||
|
||||
class EntityTreeRenderer;
|
||||
|
||||
namespace render { namespace entities {
|
||||
|
||||
// Base class for all renderable entities
|
||||
class EntityRenderer : public QObject, public std::enable_shared_from_this<EntityRenderer>, public PayloadProxyInterface, protected ReadWriteLockable {
|
||||
class EntityRenderer : public QObject, public std::enable_shared_from_this<EntityRenderer>, public PayloadProxyInterface, protected ReadWriteLockable, public scriptable::ModelProvider {
|
||||
Q_OBJECT
|
||||
|
||||
using Pointer = std::shared_ptr<EntityRenderer>;
|
||||
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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<RenderableModelEntityItem
|
|||
|
||||
public:
|
||||
ModelEntityRenderer(const EntityItemPointer& entity);
|
||||
virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override;
|
||||
|
||||
protected:
|
||||
virtual void removeFromScene(const ScenePointer& scene, Transaction& transaction) override;
|
||||
|
|
|
@ -281,6 +281,10 @@ std::vector<PolyLineEntityRenderer::Vertex> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ class PolyLineEntityRenderer : public TypedEntityRenderer<PolyLineEntityItem> {
|
|||
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,
|
||||
|
|
|
@ -20,8 +20,6 @@
|
|||
#include <QByteArray>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
|
||||
#include <model-networking/SimpleMeshProxy.h>
|
||||
#include <ModelScriptingInterface.h>
|
||||
#include <EntityEditPacketSender.h>
|
||||
#include <PhysicalEntitySimulation.h>
|
||||
#include <StencilMaskPass.h>
|
||||
|
@ -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;
|
||||
|
|
|
@ -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<RenderablePolyVoxEntity
|
|||
|
||||
public:
|
||||
PolyVoxEntityRenderer(const EntityItemPointer& entity);
|
||||
virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override {
|
||||
return asTypedEntity<RenderablePolyVoxEntityItem>()->getScriptableModel(ok);
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual ItemKey getKey() override { return ItemKey::Builder::opaqueShape(); }
|
||||
|
|
|
@ -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<GeometryCache>();
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ class ShapeEntityRenderer : public TypedEntityRenderer<ShapeEntityItem> {
|
|||
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;
|
||||
|
|
|
@ -57,8 +57,6 @@ using EntityTreeElementExtraEncodeDataPointer = std::shared_ptr<EntityTreeElemen
|
|||
#define debugTimeOnly(T) qPrintable(QString("%1").arg(T, 16, 10))
|
||||
#define debugTreeVector(V) V << "[" << V << " in meters ]"
|
||||
|
||||
class MeshProxyList;
|
||||
|
||||
/// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available
|
||||
/// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate
|
||||
/// one directly, instead you must only construct one of it's derived classes with additional features.
|
||||
|
@ -456,8 +454,6 @@ public:
|
|||
|
||||
bool matchesJSONFilters(const QJsonObject& jsonFilters) const;
|
||||
|
||||
virtual bool getMeshes(MeshProxyList& result) { return true; }
|
||||
|
||||
virtual void locationChanged(bool tellPhysics = true) override;
|
||||
|
||||
virtual bool getScalesWithParent() const override;
|
||||
|
|
|
@ -1805,30 +1805,6 @@ 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<EntityItemPointer>(_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) {
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -46,9 +46,12 @@ bool writeOBJToTextStream(QTextStream& out, QList<MeshPointer> meshes) {
|
|||
QList<int> 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<MeshPointer> 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<MeshPointer> 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<MeshPointer> 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<graphics::Mesh::Part>(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<const uint32_t> indexItr = indexBuffer.cbegin<uint32_t>();
|
||||
indexItr += part._startIndex;
|
||||
|
||||
int indexCount = 0;
|
||||
while (indexItr != indexBuffer.cend<uint32_t>() && indexCount < part._numIndices) {
|
||||
uint32_t index0 = *indexItr;
|
||||
indexItr++;
|
||||
indexCount++;
|
||||
if (indexItr == indexBuffer.cend<uint32_t>() || 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<uint16_t>(i0);
|
||||
index1 = indexBuffer.get<uint16_t>(i1);
|
||||
index2 = indexBuffer.get<uint16_t>(i2);
|
||||
} else {
|
||||
index0 = indexBuffer.get<uint32_t>(i0);
|
||||
index1 = indexBuffer.get<uint32_t>(i1);
|
||||
index2 = indexBuffer.get<uint32_t>(i2);
|
||||
}
|
||||
uint32_t index1 = *indexItr;
|
||||
indexItr++;
|
||||
indexCount++;
|
||||
if (indexItr == indexBuffer.cend<uint32_t>() || 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<MeshPointer> 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";
|
||||
}
|
||||
|
|
5
libraries/graphics-scripting/CMakeLists.txt
Normal file
5
libraries/graphics-scripting/CMakeLists.txt
Normal file
|
@ -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)
|
|
@ -0,0 +1,195 @@
|
|||
#include "./graphics-scripting/BufferViewHelpers.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QVariant>
|
||||
|
||||
#include <gpu/Buffer.h>
|
||||
#include <gpu/Format.h>
|
||||
#include <gpu/Stream.h>
|
||||
|
||||
#include <glm/gtc/packing.hpp>
|
||||
|
||||
#ifdef DEBUG_BUFFERVIEW_SCRIPTING
|
||||
#include "DebugNames.h"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
const std::array<const char*, 4> XYZW = {{ "x", "y", "z", "w" }};
|
||||
const std::array<const char*, 4> ZERO123 = {{ "0", "1", "2", "3" }};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QVariant getBufferViewElement(const gpu::BufferView& view, quint32 index, bool asArray = false) {
|
||||
return glmVecToVariant(view.get<T>(index), asArray);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void setBufferViewElement(const gpu::BufferView& view, quint32 index, const QVariant& v) {
|
||||
view.edit<T>(index) = glmVecFromVariant<T>(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<glm::u8vec2>(view, index, v); return true;
|
||||
case 3: setBufferViewElement<glm::u8vec3>(view, index, v); return true;
|
||||
case 4: {
|
||||
if (element == gpu::Element::COLOR_RGBA_32) {
|
||||
glm::uint32 rawColor;// = glm::packUnorm4x8(glm::vec4(glmVecFromVariant<glm::vec3>(v), 0.0f));
|
||||
glm::uint32 unused;
|
||||
packNormalAndTangent(glmVecFromVariant<glm::vec3>(v), glm::vec3(), rawColor, unused);
|
||||
view.edit<glm::uint32>(index) = rawColor;
|
||||
} else if (element == gpu::Element::VEC4F_NORMALIZED_XYZ10W2) {
|
||||
glm::uint32 packedNormal;// = glm::packSnorm3x10_1x2(glm::vec4(glmVecFromVariant<glm::vec3>(v), 0.0f));
|
||||
glm::uint32 unused;
|
||||
packNormalAndTangent(glm::vec3(), glmVecFromVariant<glm::vec3>(v), unused, packedNormal);
|
||||
view.edit<glm::uint32>(index) = packedNormal;
|
||||
}
|
||||
setBufferViewElement<glm::u8vec4>(view, index, v); return true;
|
||||
}
|
||||
}
|
||||
} else if (BYTES_PER_ELEMENT == 2) {
|
||||
switch(vecN) {
|
||||
case 2: setBufferViewElement<glm::u16vec2>(view, index, v); return true;
|
||||
case 3: setBufferViewElement<glm::u16vec3>(view, index, v); return true;
|
||||
case 4: setBufferViewElement<glm::u16vec4>(view, index, v); return true;
|
||||
}
|
||||
} else if (BYTES_PER_ELEMENT == 4) {
|
||||
if (dataType == gpu::FLOAT) {
|
||||
switch(vecN) {
|
||||
case 2: setBufferViewElement<glm::vec2>(view, index, v); return true;
|
||||
case 3: setBufferViewElement<glm::vec3>(view, index, v); return true;
|
||||
case 4: setBufferViewElement<glm::vec4>(view, index, v); return true;
|
||||
}
|
||||
} else {
|
||||
switch(vecN) {
|
||||
case 2: setBufferViewElement<glm::u32vec2>(view, index, v); return true;
|
||||
case 3: setBufferViewElement<glm::u32vec3>(view, index, v); return true;
|
||||
case 4: setBufferViewElement<glm::u32vec4>(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<glm::u8vec2>(view, index, asArray);
|
||||
case 3: return getBufferViewElement<glm::u8vec3>(view, index, asArray);
|
||||
case 4: {
|
||||
if (element == gpu::Element::COLOR_RGBA_32) {
|
||||
auto rawColor = view.get<glm::uint32>(index);
|
||||
return glmVecToVariant(glm::vec3(glm::unpackUnorm4x8(rawColor)));
|
||||
} else if (element == gpu::Element::VEC4F_NORMALIZED_XYZ10W2) {
|
||||
auto packedNormal = view.get<glm::uint32>(index);
|
||||
return glmVecToVariant(glm::vec3(glm::unpackSnorm3x10_1x2(packedNormal)));
|
||||
}
|
||||
|
||||
return getBufferViewElement<glm::u8vec4>(view, index, asArray);
|
||||
}
|
||||
}
|
||||
} else if (BYTES_PER_ELEMENT == 2) {
|
||||
switch(vecN) {
|
||||
case 2: return getBufferViewElement<glm::u16vec2>(view, index, asArray);
|
||||
case 3: return getBufferViewElement<glm::u16vec3>(view, index, asArray);
|
||||
case 4: return getBufferViewElement<glm::u16vec4>(view, index, asArray);
|
||||
}
|
||||
} else if (BYTES_PER_ELEMENT == 4) {
|
||||
if (dataType == gpu::FLOAT) {
|
||||
switch(vecN) {
|
||||
case 2: return getBufferViewElement<glm::vec2>(view, index, asArray);
|
||||
case 3: return getBufferViewElement<glm::vec3>(view, index, asArray);
|
||||
case 4: return getBufferViewElement<glm::vec4>(view, index, asArray);
|
||||
}
|
||||
} else {
|
||||
switch(vecN) {
|
||||
case 2: return getBufferViewElement<glm::u32vec2>(view, index, asArray);
|
||||
case 3: return getBufferViewElement<glm::u32vec3>(view, index, asArray);
|
||||
case 4: return getBufferViewElement<glm::u32vec4>(view, index, asArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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 <typename T>
|
||||
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;
|
||||
}
|
||||
|
|
@ -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 <QtCore>
|
||||
|
||||
namespace gpu { class BufferView; }
|
||||
|
||||
template <typename T> QVariant glmVecToVariant(const T& v, bool asArray = false);
|
||||
template <typename T> 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);
|
||||
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
#include "BufferViewScripting.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QVariant>
|
||||
#include <QtScript/QScriptValue>
|
||||
#include <QtScript/QScriptEngine>
|
||||
|
||||
#include <gpu/Buffer.h>
|
||||
#include <gpu/Format.h>
|
||||
#include <gpu/Stream.h>
|
||||
|
||||
#include <graphics-scripting/BufferViewHelpers.h>
|
||||
|
||||
#ifdef DEBUG_BUFFERVIEW_SCRIPTING
|
||||
#include <graphics/DebugNames.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
const std::array<const char*, 4> XYZW = {{ "x", "y", "z", "w" }};
|
||||
const std::array<const char*, 4> ZERO123 = {{ "0", "1", "2", "3" }};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
QScriptValue getBufferViewElement(QScriptEngine* js, const gpu::BufferView& view, quint32 index, bool asArray = false) {
|
||||
return glmVecToScriptValue(js, view.get<T>(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 <typename T>
|
||||
void setBufferViewElement(const gpu::BufferView& view, quint32 index, const QScriptValue& v) {
|
||||
view.edit<T>(index) = glmVecFromScriptValue<T>(v);
|
||||
}
|
||||
|
||||
bool bufferViewElementFromScriptValue(const QScriptValue& v, const gpu::BufferView& view, quint32 index) {
|
||||
return bufferViewElementFromVariant(view, index, v.toVariant());
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
template <typename T>
|
||||
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 <typename T>
|
||||
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;
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtCore>
|
||||
|
||||
namespace gpu { class BufferView; }
|
||||
class QScriptValue;
|
||||
class QScriptEngine;
|
||||
template <typename T> QScriptValue glmVecToScriptValue(QScriptEngine *js, const T& v, bool asArray = false);
|
||||
template <typename T> 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);
|
|
@ -0,0 +1,72 @@
|
|||
#pragma once
|
||||
#include <QtCore>
|
||||
#include <QVariant>
|
||||
#include <QObject>
|
||||
#include <gpu/Format.h>
|
||||
#include <gpu/Stream.h>
|
||||
//#include <gpu/Buffer.h>
|
||||
|
||||
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;
|
||||
};
|
|
@ -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 <QtScript/QScriptEngine>
|
||||
#include <QtScript/QScriptValueIterator>
|
||||
#include <QtScript/QScriptValue>
|
||||
#include <QUuid>
|
||||
#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 <glm/gtx/string_cast.hpp>
|
||||
#include <GeometryUtil.h>
|
||||
|
||||
#include <shared/QtHelpers.h>
|
||||
|
||||
|
||||
#include <graphics-scripting/DebugNames.h>
|
||||
|
||||
#include <graphics-scripting/BufferViewHelpers.h>
|
||||
#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<QScriptEngine*>(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<scriptable::MeshPointer> 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<graphics::Mesh::Part> 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<glm::vec3>(vertexIndex);
|
||||
return engine()->toScriptValue(pos);
|
||||
}
|
||||
|
||||
QScriptValue ModelScriptingInterface::newMesh(const QVector<glm::vec3>& vertices,
|
||||
const QVector<glm::vec3>& normals,
|
||||
const QVector<mesh::MeshFace>& faces) {
|
||||
graphics::MeshPointer mesh(new graphics::Mesh());
|
||||
|
||||
// vertices
|
||||
auto vertexBuffer = std::make_shared<gpu::Buffer>(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<gpu::Buffer>(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<gpu::Buffer>(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<graphics::Mesh::Part> 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<scriptable::ScriptableModel>(_in).getMeshes();
|
||||
if (in.size()) {
|
||||
foreach (scriptable::ScriptableMeshPointer meshProxy, in) {
|
||||
mapMeshAttributeValues(meshProxy, scopeOrCallback, methodOrName);
|
||||
}
|
||||
return thisObject();
|
||||
} else if (auto meshProxy = qobject_cast<scriptable::ScriptableMesh*>(_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<quint32>(i) : indices.get<quint16>(i);
|
||||
newindices.edit<uint32_t>(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 <typename T>
|
||||
gpu::BufferView bufferViewFromVector(QVector<T> elements, gpu::Element elementType) {
|
||||
auto vertexBuffer = std::make_shared<gpu::Buffer>(
|
||||
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<gpu::Buffer>(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<QString>& 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<QString> 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<glm::vec3> uniqueVerts;
|
||||
uniqueVerts.reserve((int)numPositions);
|
||||
QMap<quint32,quint32> remapIndices;
|
||||
|
||||
for (quint32 i = 0; i < numPositions; i++) {
|
||||
const quint32 numUnique = uniqueVerts.size();
|
||||
const auto& position = positions.get<glm::vec3>(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<quint32> newIndices;
|
||||
newIndices.reserve((int)numIndices);
|
||||
for (quint32 i = 0; i < numIndices; i++) {
|
||||
quint32 index = esize == 4 ? indices.get<quint32>(i) : indices.get<quint16>(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<Triangle> faces;
|
||||
QVector<glm::vec3> faceNormals;
|
||||
QMap<QString,QVector<quint32>> 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<quint32>(I+0) : indices.get<quint16>(I+0);
|
||||
quint32 i1 = esize == 4 ? indices.get<quint32>(I+1) : indices.get<quint16>(I+1);
|
||||
quint32 i2 = esize == 4 ? indices.get<quint32>(I+2) : indices.get<quint16>(I+2);
|
||||
|
||||
Triangle face = {
|
||||
verts.get<glm::vec3>(i1),
|
||||
verts.get<glm::vec3>(i2),
|
||||
verts.get<glm::vec3>(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<glm::vec3>(j);
|
||||
glm::vec3 normal { 0.0f, 0.0f, 0.0f };
|
||||
QString key { glm::to_string(verts.get<glm::vec3>(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<glm::vec3>(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<glm::vec3>(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<BaseScriptEngine> engine = dynamic_cast<BaseScriptEngine*>(handler.engine());
|
||||
|
||||
scriptable::ScriptableModel meshes;
|
||||
bool success = false;
|
||||
QString error;
|
||||
|
||||
auto appProvider = DependencyManager::get<scriptable::ModelProviderFactory>();
|
||||
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:" <<success << "#" << scriptableMeshes.meshes.size();
|
||||
if (success) {
|
||||
meshes = scriptableMeshes;//SimpleModelProxy::fromScriptableModel(scriptableMeshes);
|
||||
}
|
||||
}
|
||||
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->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<scriptable::ScriptableMesh*>(obj)) {
|
||||
out = tmp->shared_from_this();
|
||||
}
|
||||
// FIXME: Why does above cast not work on Win32!?
|
||||
if (!out) {
|
||||
auto smp = static_cast<scriptable::ScriptableMesh*>(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<scriptable::ScriptableMesh*>(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<scriptable::ScriptableModelPointer>(meshesProperty.toQObject());
|
||||
// qDebug() << "modelProxyFromScriptValue" << meshesProperty.property("length").toInt32() << meshesProperty.toVariant().typeName();
|
||||
qScriptValueToSequence(meshesProperty, meshes.meshes);
|
||||
} else if (auto mesh = qobject_cast<scriptable::ScriptableMesh*>(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<mesh::MeshFace>& vector) {
|
||||
return qScriptValueFromSequence(engine, vector);
|
||||
}
|
||||
|
||||
void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector<mesh::MeshFace>& result) {
|
||||
qScriptValueToSequence(array, result);
|
||||
}
|
||||
|
||||
QScriptValue qVectorUInt32ToScriptValue(QScriptEngine* engine, const QVector<mesh::uint32>& vector) {
|
||||
return qScriptValueFromSequence(engine, vector);
|
||||
}
|
||||
|
||||
void qVectorUInt32FromScriptValue(const QScriptValue& array, QVector<mesh::uint32>& result) {
|
||||
qScriptValueToSequence(array, result);
|
||||
}
|
||||
}
|
||||
|
||||
int meshUint32 = qRegisterMetaType<mesh::uint32>();
|
||||
namespace mesh {
|
||||
int meshUint32 = qRegisterMetaType<uint32>();
|
||||
}
|
||||
int qVectorMeshUint32 = qRegisterMetaType<QVector<mesh::uint32>>();
|
||||
|
||||
void ModelScriptingInterface::registerMetaTypes(QScriptEngine* engine) {
|
||||
qScriptRegisterSequenceMetaType<QVector<scriptable::ScriptableMeshPointer>>(engine);
|
||||
qScriptRegisterSequenceMetaType<mesh::MeshFaces>(engine);
|
||||
qScriptRegisterSequenceMetaType<QVector<mesh::uint32>>(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;
|
||||
}
|
|
@ -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 <QtCore/QObject>
|
||||
#include <QUrl>
|
||||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include <QtScript/QScriptable>
|
||||
|
||||
#include "ScriptableMesh.h"
|
||||
#include <DependencyManager.h>
|
||||
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<QString>& attributeNames = QVector<QString>());
|
||||
QScriptValue appendMeshes(scriptable::ScriptableModel in);
|
||||
QScriptValue transformMesh(scriptable::ScriptableMeshPointer meshProxy, glm::mat4 transform);
|
||||
QScriptValue newMesh(const QVector<glm::vec3>& vertices,
|
||||
const QVector<glm::vec3>& normals,
|
||||
const QVector<mesh::MeshFace>& 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
|
|
@ -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 <glm/glm.hpp>
|
||||
#include <glm/gtx/transform.hpp>
|
||||
#include <glm/gtx/norm.hpp>
|
||||
#include <graphics/Geometry.h>
|
||||
#include <graphics-scripting/DebugNames.h>
|
||||
#include <graphics-scripting/BufferViewHelpers.h>
|
||||
#include <Extents.h>
|
||||
|
||||
#include "ScriptableMesh.moc"
|
||||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
QLoggingCategory mesh_logging { "hifi.scripting.mesh" };
|
||||
|
||||
// FIXME: unroll/resolve before PR
|
||||
using namespace scriptable;
|
||||
QMap<QString,int> 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::ScriptableMeshPointer> scriptable::ScriptableModel::getMeshes() const {
|
||||
QVector<scriptable::ScriptableMeshPointer> out;
|
||||
for(auto& mesh : meshes) {
|
||||
out << scriptable::ScriptableMeshPointer(new ScriptableMesh(std::const_pointer_cast<scriptable::ScriptableModel>(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<quint32> ScriptableMesh::findNearbyIndices(const glm::vec3& origin, float epsilon) const {
|
||||
QVector<quint32> 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<glm::vec3>(i);
|
||||
if (glm::distance(position, origin) <= epsilon) {
|
||||
result << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QVector<quint32> ScriptableMesh::getIndices() const {
|
||||
QVector<quint32> 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<quint32>(i);
|
||||
}
|
||||
} else {
|
||||
for (quint32 i = 0; i < count; i++) {
|
||||
result[i] = indexBufferView.get<quint16>(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
quint32 ScriptableMesh::getNumAttributes() const {
|
||||
if (auto mesh = getMeshPointer()) {
|
||||
return (quint32)mesh->getNumAttributes();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
QVector<QString> ScriptableMesh::getAttributeNames() const {
|
||||
QVector<QString> 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<glm::vec3>(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<QString> 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<QString, gpu::BufferView> ScriptableMesh::gatherBufferViews(scriptable::MeshPointer mesh, const QStringList& expandToMatchPositions) {
|
||||
std::map<QString, gpu::BufferView> 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;
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtCore/QUuid>
|
||||
#include <memory>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include <graphics-scripting/ScriptableModel.h>
|
||||
|
||||
namespace graphics {
|
||||
class Mesh;
|
||||
}
|
||||
namespace gpu {
|
||||
class BufferView;
|
||||
}
|
||||
namespace scriptable {
|
||||
class ScriptableMesh : public QObject, public std::enable_shared_from_this<ScriptableMesh> {
|
||||
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<QString> 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<QString> getAttributeNames() const;
|
||||
Q_INVOKABLE virtual QVariantMap getVertexAttributes(quint32 vertexIndex) const;
|
||||
Q_INVOKABLE virtual QVariantMap getVertexAttributes(quint32 vertexIndex, QVector<QString> attributes) const;
|
||||
|
||||
Q_INVOKABLE virtual QVector<quint32> getIndices() const;
|
||||
Q_INVOKABLE virtual QVector<quint32> 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<QString,int> ATTRIBUTES;
|
||||
static std::map<QString, gpu::BufferView> 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<quint32> getFace(quint32 faceIndex) const {
|
||||
auto inds = getIndices();
|
||||
return faceIndex+2 < (quint32)inds.size() ? inds.mid(faceIndex*3, 3) : QVector<quint32>();
|
||||
}
|
||||
};
|
||||
|
||||
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<scriptable::ScriptableMeshPointer>)
|
||||
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 <memory>
|
||||
|
||||
namespace mesh {
|
||||
using uint32 = quint32;
|
||||
class MeshFace;
|
||||
using MeshFaces = QVector<mesh::MeshFace>;
|
||||
class MeshFace {
|
||||
public:
|
||||
MeshFace() {}
|
||||
MeshFace(QVector<mesh::uint32> vertexIndices) : vertexIndices(vertexIndices) {}
|
||||
~MeshFace() {}
|
||||
|
||||
QVector<mesh::uint32> vertexIndices;
|
||||
// TODO -- material...
|
||||
};
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(mesh::MeshFace)
|
||||
Q_DECLARE_METATYPE(QVector<mesh::MeshFace>)
|
||||
Q_DECLARE_METATYPE(mesh::uint32)
|
||||
Q_DECLARE_METATYPE(QVector<mesh::uint32>)
|
|
@ -0,0 +1,80 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtCore/QUuid>
|
||||
#include <memory>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
namespace graphics {
|
||||
class Mesh;
|
||||
}
|
||||
namespace gpu {
|
||||
class BufferView;
|
||||
}
|
||||
namespace scriptable {
|
||||
using Mesh = graphics::Mesh;
|
||||
using MeshPointer = std::shared_ptr<scriptable::Mesh>;
|
||||
|
||||
class ScriptableModel;
|
||||
class ScriptableMesh;
|
||||
class ScriptableMeshPart;
|
||||
using ScriptableModelPointer = std::shared_ptr<scriptable::ScriptableModel>;
|
||||
using ScriptableMeshPointer = std::shared_ptr<scriptable::ScriptableMesh>;
|
||||
using ScriptableMeshPartPointer = std::shared_ptr<scriptable::ScriptableMeshPart>;
|
||||
class ScriptableModel : public QObject, public std::enable_shared_from_this<ScriptableModel> {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Q_PROPERTY(QVector<scriptable::ScriptableMeshPointer> 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<scriptable::MeshPointer> meshes;
|
||||
// TODO: in future accessors for these could go here
|
||||
QVariantMap shapes;
|
||||
QVariantMap materials;
|
||||
QVariantMap armature;
|
||||
|
||||
QVector<scriptable::ScriptableMeshPointer> 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<scriptable::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::ScriptableModelPointer)
|
||||
|
|
@ -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<glm::vec3(glm::vec3)> 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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <graphics/Geometry.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -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 <QScriptEngine>
|
||||
#include <QScriptValueIterator>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
|
||||
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
|
|
@ -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()
|
||||
|
|
|
@ -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<gpu::Buffer>(*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<glm::vec3>((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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
#include <GLMHelpers.h>
|
||||
#include <TBBHelpers.h>
|
||||
|
||||
#include <model-networking/SimpleMeshProxy.h>
|
||||
#include <graphics-scripting/ScriptableModel.h>
|
||||
#include <DualQuaternion.h>
|
||||
|
||||
#include <glm/gtc/packing.hpp>
|
||||
|
@ -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<float> floats;
|
||||
floats.resize(16);
|
||||
memcpy(floats.data(), &mat4, sizeof(glm::mat4));
|
||||
QVariant v;
|
||||
v.setValue<QVector<float>>(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<graphics::Mesh>(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;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <gpu/Batch.h>
|
||||
#include <render/Forward.h>
|
||||
#include <render/Scene.h>
|
||||
#include <graphics-scripting/ScriptableModel.h>
|
||||
#include <Transform.h>
|
||||
#include <SpatiallyNestable.h>
|
||||
#include <TriangleSet.h>
|
||||
|
@ -64,7 +65,7 @@ using ModelWeakPointer = std::weak_ptr<Model>;
|
|||
|
||||
|
||||
/// A generic 3D model displaying geometry loaded from a URL.
|
||||
class Model : public QObject, public std::enable_shared_from_this<Model> {
|
||||
class Model : public QObject, public std::enable_shared_from_this<Model>, 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();
|
||||
|
||||
|
|
|
@ -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 <QScriptEngine>
|
||||
#include <QScriptValueIterator>
|
||||
#include <QtScript/QScriptValue>
|
||||
#include <model-networking/SimpleMeshProxy.h>
|
||||
#include "ScriptEngine.h"
|
||||
#include "ScriptEngineLogging.h"
|
||||
#include "OBJWriter.h"
|
||||
|
||||
ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(parent) {
|
||||
_modelScriptEngine = qobject_cast<QScriptEngine*>(parent);
|
||||
|
||||
qScriptRegisterSequenceMetaType<QList<MeshProxy*>>(_modelScriptEngine);
|
||||
qScriptRegisterMetaType(_modelScriptEngine, meshFaceToScriptValue, meshFaceFromScriptValue);
|
||||
qScriptRegisterMetaType(_modelScriptEngine, qVectorMeshFaceToScriptValue, qVectorMeshFaceFromScriptValue);
|
||||
}
|
||||
|
||||
QString ModelScriptingInterface::meshToOBJ(MeshProxyList in) {
|
||||
QList<MeshPointer> 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<unsigned char> combinedVertexData{ new unsigned char[combinedVertexSize] };
|
||||
unsigned char* combinedVertexDataCursor = combinedVertexData.get();
|
||||
|
||||
gpu::Resource::Size combinedColorSize = totalColorCount * sizeof(glm::vec3);
|
||||
std::unique_ptr<unsigned char> combinedColorData{ new unsigned char[combinedColorSize] };
|
||||
unsigned char* combinedColorDataCursor = combinedColorData.get();
|
||||
|
||||
gpu::Resource::Size combinedNormalSize = totalNormalCount * sizeof(glm::vec3);
|
||||
std::unique_ptr<unsigned char> combinedNormalData{ new unsigned char[combinedNormalSize] };
|
||||
unsigned char* combinedNormalDataCursor = combinedNormalData.get();
|
||||
|
||||
gpu::Resource::Size combinedIndexSize = totalIndexCount * sizeof(uint32_t);
|
||||
std::unique_ptr<unsigned char> 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<graphics::Mesh::Part> 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<glm::vec3>(vertexIndex);
|
||||
return vec3toScriptValue(_modelScriptEngine, pos);
|
||||
}
|
||||
|
||||
|
||||
QScriptValue ModelScriptingInterface::newMesh(const QVector<glm::vec3>& vertices,
|
||||
const QVector<glm::vec3>& normals,
|
||||
const QVector<MeshFace>& faces) {
|
||||
graphics::MeshPointer mesh(new graphics::Mesh());
|
||||
|
||||
// vertices
|
||||
auto vertexBuffer = std::make_shared<gpu::Buffer>(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<gpu::Buffer>(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<gpu::Buffer>(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<graphics::Mesh::Part> 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);
|
||||
}
|
|
@ -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 <QtCore/QObject>
|
||||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
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<glm::vec3>& vertices,
|
||||
const QVector<glm::vec3>& normals,
|
||||
const QVector<MeshFace>& faces);
|
||||
Q_INVOKABLE QScriptValue getVertexCount(MeshProxy* meshProxy);
|
||||
Q_INVOKABLE QScriptValue getVertex(MeshProxy* meshProxy, int vertexIndex);
|
||||
|
||||
private:
|
||||
QScriptEngine* _modelScriptEngine { nullptr };
|
||||
};
|
||||
|
||||
#endif // hifi_ModelScriptingInterface_h
|
|
@ -73,8 +73,6 @@
|
|||
#include "WebSocketClass.h"
|
||||
#include "RecordingScriptingInterface.h"
|
||||
#include "ScriptEngines.h"
|
||||
#include "ModelScriptingInterface.h"
|
||||
|
||||
|
||||
#include <Profile.h>
|
||||
|
||||
|
@ -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<UserActivityLoggerScriptingInterface>().data());
|
||||
}
|
||||
|
||||
|
|
|
@ -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<MeshProxy*>(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<MeshProxyList::value_type>(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<MeshFace>& 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<MeshFace>& 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<graphics::Mesh>;
|
||||
|
||||
|
||||
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<MeshProxy*> {}; // 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<uint32_t> vertexIndices;
|
||||
// TODO -- material...
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(MeshFace)
|
||||
Q_DECLARE_METATYPE(QVector<MeshFace>)
|
||||
|
||||
QScriptValue meshFaceToScriptValue(QScriptEngine* engine, const MeshFace &meshFace);
|
||||
void meshFaceFromScriptValue(const QScriptValue &object, MeshFace& meshFaceResult);
|
||||
QScriptValue qVectorMeshFaceToScriptValue(QScriptEngine* engine, const QVector<MeshFace>& vector);
|
||||
void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector<MeshFace>& result);
|
||||
|
||||
|
||||
#endif // hifi_RegisteredMetaTypes_h
|
||||
|
|
Loading…
Reference in a new issue