CR feedback; remove ok bool pointer and all metadata; cleanup

This commit is contained in:
humbletim 2018-02-23 07:47:39 -05:00
parent 0dd3672162
commit 6ea4b660b7
29 changed files with 307 additions and 575 deletions

View file

@ -36,7 +36,7 @@ public:
virtual bool is3D() const override { return true; }
virtual uint32_t fetchMetaSubItems(render::ItemIDs& subItems) const override { subItems.push_back(getRenderItemID()); return (uint32_t) subItems.size(); }
virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override { return scriptable::ModelProvider::modelUnavailableError(ok); }
virtual scriptable::ScriptableModelBase getScriptableModel() override { return scriptable::ScriptableModelBase(); }
// TODO: consider implementing registration points in this class
glm::vec3 getCenter() const { return getWorldPosition(); }

View file

@ -665,11 +665,11 @@ void ModelOverlay::processMaterials() {
}
}
scriptable::ScriptableModelBase ModelOverlay::getScriptableModel(bool* ok) {
scriptable::ScriptableModelBase ModelOverlay::getScriptableModel() {
if (!_model || !_model->isLoaded()) {
return Base3DOverlay::getScriptableModel(ok);
return Base3DOverlay::getScriptableModel();
}
auto result = _model->getScriptableModel(ok);
auto result = _model->getScriptableModel();
result.objectID = getID();
return result;
}
}

View file

@ -62,7 +62,7 @@ public:
void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) override;
void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) override;
virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override;
virtual scriptable::ScriptableModelBase getScriptableModel() override;
protected:
Transform evalRenderTransform() override;

View file

@ -180,14 +180,13 @@ Transform Shape3DOverlay::evalRenderTransform() {
return transform;
}
scriptable::ScriptableModelBase Shape3DOverlay::getScriptableModel(bool* ok) {
scriptable::ScriptableModelBase Shape3DOverlay::getScriptableModel() {
auto geometryCache = DependencyManager::get<GeometryCache>();
auto vertexColor = ColorUtils::toVec3(_color);
scriptable::ScriptableModelBase result;
result.objectID = getID();
result.append(geometryCache->meshFromShape(_shape, vertexColor), {{ "shape", shapeStrings[_shape] }});
if (ok) {
*ok = true;
if (auto mesh = geometryCache->meshFromShape(_shape, vertexColor)) {
result.append(mesh);
}
return result;
}

View file

@ -37,7 +37,7 @@ public:
void setProperties(const QVariantMap& properties) override;
QVariant getProperty(const QString& property) override;
virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override;
virtual scriptable::ScriptableModelBase getScriptableModel() override;
protected:
Transform evalRenderTransform() override;

View file

@ -1795,22 +1795,11 @@ void Avatar::processMaterials() {
}
}
scriptable::ScriptableModelBase Avatar::getScriptableModel(bool* ok) {
scriptable::ScriptableModelBase Avatar::getScriptableModel() {
if (!_skeletonModel || !_skeletonModel->isLoaded()) {
return scriptable::ModelProvider::modelUnavailableError(ok);
return scriptable::ScriptableModelBase();
}
scriptable::ScriptableModelBase result = _skeletonModel->getScriptableModel(ok);
auto result = _skeletonModel->getScriptableModel();
result.objectID = getSessionUUID();
result.mixin({{ "textures", _skeletonModel->getTextures() }});
// FIXME: for now access to attachment models are merged into the main avatar ScriptableModel set
for (int i = 0; i < (int)_attachmentModels.size(); i++) {
auto& model = _attachmentModels.at(i);
if (model->isLoaded()) {
result.append(model->getScriptableModel(ok), _attachmentData.at(i).toVariant().toMap());
}
}
if (ok) {
*ok = true;
}
return result;
}

View file

@ -276,7 +276,7 @@ public:
void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) override;
void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) override;
virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override;
virtual scriptable::ScriptableModelBase getScriptableModel() override;
public slots:

View file

@ -58,7 +58,7 @@ public:
virtual void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName);
virtual void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName);
virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override { return scriptable::ModelProvider::modelUnavailableError(ok); }
virtual scriptable::ScriptableModelBase getScriptableModel() override { return scriptable::ScriptableModelBase(); }
protected:
virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); }

View file

@ -960,15 +960,15 @@ bool RenderableModelEntityItem::getMeshes(MeshProxyList& result) {
return !result.isEmpty();
}
scriptable::ScriptableModelBase render::entities::ModelEntityRenderer::getScriptableModel(bool* ok) {
scriptable::ScriptableModelBase render::entities::ModelEntityRenderer::getScriptableModel() {
ModelPointer model;
withReadLock([&] { model = _model; });
if (!model || !model->isLoaded()) {
return scriptable::ModelProvider::modelUnavailableError(ok);
return scriptable::ScriptableModelBase();
}
auto result = _model->getScriptableModel(ok);
auto result = _model->getScriptableModel();
result.objectID = getEntity()->getID();
return result;
}
@ -981,7 +981,7 @@ bool render::entities::ModelEntityRenderer::replaceScriptableModelMeshPart(scrip
return false;
}
return model->replaceScriptableModelMeshPart(newModel, meshIndex, partIndex);
return model->replaceScriptableModelMeshPart(newModel, meshIndex, partIndex);
}
void RenderableModelEntityItem::simulateRelayedJoints() {

View file

@ -142,7 +142,7 @@ class ModelEntityRenderer : public TypedEntityRenderer<RenderableModelEntityItem
public:
ModelEntityRenderer(const EntityItemPointer& entity);
virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override;
virtual scriptable::ScriptableModelBase getScriptableModel() override;
virtual bool replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer model, int meshIndex, int partIndex) override;
void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) override;

View file

@ -281,9 +281,9 @@ std::vector<PolyLineEntityRenderer::Vertex> PolyLineEntityRenderer::updateVertic
return vertices;
}
scriptable::ScriptableModelBase PolyLineEntityRenderer::getScriptableModel(bool *ok) {
scriptable::ScriptableModelBase PolyLineEntityRenderer::getScriptableModel() {
// TODO: adapt polyline into a triangles mesh...
return EntityRenderer::getScriptableModel(ok);
return EntityRenderer::getScriptableModel();
}
void PolyLineEntityRenderer::doRender(RenderArgs* args) {

View file

@ -25,7 +25,7 @@ class PolyLineEntityRenderer : public TypedEntityRenderer<PolyLineEntityItem> {
public:
PolyLineEntityRenderer(const EntityItemPointer& entity);
virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override;
virtual scriptable::ScriptableModelBase getScriptableModel() override;
protected:
virtual bool needsRenderUpdateFromTypedEntity(const TypedEntityPointer& entity) const override;
virtual void doRenderUpdateSynchronousTyped(const ScenePointer& scene,

View file

@ -1451,9 +1451,9 @@ bool RenderablePolyVoxEntityItem::getMeshes(MeshProxyList& result) {
return success;
}
scriptable::ScriptableModelBase RenderablePolyVoxEntityItem::getScriptableModel(bool * ok) {
scriptable::ScriptableModelBase RenderablePolyVoxEntityItem::getScriptableModel() {
if (!updateDependents() || !_mesh) {
return scriptable::ModelProvider::modelUnavailableError(ok);
return scriptable::ScriptableModelBase();
}
bool success = false;
@ -1479,9 +1479,6 @@ scriptable::ScriptableModelBase RenderablePolyVoxEntityItem::getScriptableModel(
));
}
});
if (ok) {
*ok = success;
}
return result;
}

View file

@ -114,7 +114,7 @@ public:
void setVolDataDirty() { withWriteLock([&] { _volDataDirty = true; _meshReady = false; }); }
bool getMeshes(MeshProxyList& result) override; // deprecated
virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override;
virtual scriptable::ScriptableModelBase getScriptableModel() override;
private:
bool updateOnCount(const ivec3& v, uint8_t toValue);
@ -164,8 +164,8 @@ class PolyVoxEntityRenderer : public TypedEntityRenderer<RenderablePolyVoxEntity
public:
PolyVoxEntityRenderer(const EntityItemPointer& entity);
virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override {
return asTypedEntity<RenderablePolyVoxEntityItem>()->getScriptableModel(ok);
virtual scriptable::ScriptableModelBase getScriptableModel() override {
return asTypedEntity<RenderablePolyVoxEntityItem>()->getScriptableModel();
}
protected:

View file

@ -164,22 +164,17 @@ void ShapeEntityRenderer::doRender(RenderArgs* args) {
args->_details._trianglesRendered += (int)triCount;
}
scriptable::ScriptableModelBase ShapeEntityRenderer::getScriptableModel(bool* ok) {
scriptable::ScriptableModelBase ShapeEntityRenderer::getScriptableModel() {
scriptable::ScriptableModelBase result;
result.objectID = getEntity()->getID();
auto geometryCache = DependencyManager::get<GeometryCache>();
auto geometryShape = geometryCache->getShapeForEntityShape(_shape);
glm::vec3 vertexColor;
if (_materials["0"].top().material) {
vertexColor = _materials["0"].top().material->getAlbedo();
}
auto success = false;
if (auto mesh = geometryCache->meshFromShape(geometryShape, vertexColor)) {
result.append({ mesh, {{ "shape", entity::stringFromShape(_shape) }}});
success = true;
}
if (ok) {
*ok = success;
result.objectID = getEntity()->getID();
result.append(mesh);
}
return result;
}

View file

@ -22,7 +22,7 @@ class ShapeEntityRenderer : public TypedEntityRenderer<ShapeEntityItem> {
public:
ShapeEntityRenderer(const EntityItemPointer& entity);
virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override;
virtual scriptable::ScriptableModelBase getScriptableModel() override;
private:
virtual bool needsRenderUpdate() const override;

View file

@ -36,16 +36,14 @@ namespace scriptable {
public:
WeakModelProviderPointer provider;
ScriptableModelBasePointer model;
WeakMeshPointer mesh;
MeshPointer ownedMesh;
QVariantMap metadata;
ScriptableMeshBase(WeakModelProviderPointer provider, ScriptableModelBasePointer model, WeakMeshPointer mesh, const QVariantMap& metadata);
ScriptableMeshBase(WeakMeshPointer mesh = WeakMeshPointer());
ScriptableMeshBase(MeshPointer mesh, const QVariantMap& metadata);
ScriptableMeshBase(const ScriptableMeshBase& other) : QObject() { *this = other; }
WeakMeshPointer weakMesh;
MeshPointer strongMesh;
ScriptableMeshBase(WeakModelProviderPointer provider, ScriptableModelBasePointer model, WeakMeshPointer weakMesh, QObject* parent);
ScriptableMeshBase(WeakMeshPointer weakMesh = WeakMeshPointer(), QObject* parent = nullptr);
ScriptableMeshBase(const ScriptableMeshBase& other, QObject* parent = nullptr) : QObject(parent) { *this = other; }
ScriptableMeshBase& operator=(const ScriptableMeshBase& view);
virtual ~ScriptableMeshBase();
Q_INVOKABLE const scriptable::MeshPointer getMeshPointer() const { return mesh.lock(); }
Q_INVOKABLE const scriptable::MeshPointer getMeshPointer() const { return weakMesh.lock(); }
Q_INVOKABLE const scriptable::ModelProviderPointer getModelProviderPointer() const { return provider.lock(); }
Q_INVOKABLE const scriptable::ScriptableModelBasePointer getModelBasePointer() const { return model; }
};
@ -56,18 +54,15 @@ namespace scriptable {
public:
WeakModelProviderPointer provider;
QUuid objectID; // spatially nestable ID
QVariantMap metadata;
QVector<scriptable::ScriptableMeshBase> meshes;
ScriptableModelBase(QObject* parent = nullptr) : QObject(parent) {}
ScriptableModelBase(const ScriptableModelBase& other) : QObject() { *this = other; }
ScriptableModelBase(const ScriptableModelBase& other) : QObject(other.parent()) { *this = other; }
ScriptableModelBase& operator=(const ScriptableModelBase& other);
virtual ~ScriptableModelBase();
void mixin(const QVariantMap& other);
void append(const ScriptableModelBase& other, const QVariantMap& modelMetadata = QVariantMap());
void append(scriptable::WeakMeshPointer mesh, const QVariantMap& metadata = QVariantMap());
void append(const ScriptableMeshBase& mesh, const QVariantMap& metadata = QVariantMap());
void append(const ScriptableMeshBase& mesh);
void append(scriptable::WeakMeshPointer mesh);
// TODO: in future containers for these could go here
// QVariantMap shapes;
// QVariantMap materials;
@ -78,8 +73,7 @@ namespace scriptable {
class ModelProvider {
public:
NestableType modelProviderType;
static scriptable::ScriptableModelBase modelUnavailableError(bool* ok) { if (ok) { *ok = false; } return {}; }
virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) = 0;
virtual scriptable::ScriptableModelBase getScriptableModel() = 0;
virtual bool replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer model, int meshIndex, int partIndex) { return false; }
};

View file

@ -9,7 +9,6 @@
//
#include "GraphicsScriptingInterface.h"
#include "BaseScriptEngine.h"
#include "BufferViewScripting.h"
#include "GraphicsScriptingUtil.h"
#include "OBJWriter.h"
@ -24,86 +23,119 @@
#include <graphics/BufferViewHelpers.h>
#include <shared/QtHelpers.h>
#include "GraphicsScriptingInterface.moc"
GraphicsScriptingInterface::GraphicsScriptingInterface(QObject* parent) : QObject(parent), QScriptable() {
if (auto scriptEngine = qobject_cast<QScriptEngine*>(parent)) {
this->registerMetaTypes(scriptEngine);
}
}
bool GraphicsScriptingInterface::updateMeshes(QUuid uuid, const scriptable::ScriptableMeshPointer mesh, int meshIndex, int partIndex) {
auto model = scriptable::make_qtowned<scriptable::ScriptableModel>();
if (mesh) {
model->append(*mesh);
}
return updateMeshes(uuid, model.get());
}
bool GraphicsScriptingInterface::updateMeshes(QUuid uuid, const scriptable::ScriptableModelPointer model) {
auto appProvider = DependencyManager::get<scriptable::ModelProviderFactory>();
scriptable::ModelProviderPointer provider = appProvider ? appProvider->lookupModelProvider(uuid) : nullptr;
QString providerType = provider ? SpatiallyNestable::nestableTypeToString(provider->modelProviderType) : QString();
if (providerType.isEmpty()) {
providerType = "unknown";
}
bool success = false;
if (provider) {
auto scriptableMeshes = provider->getScriptableModel(&success);
if (success) {
const scriptable::ScriptableModelBasePointer base = model->operator scriptable::ScriptableModelBasePointer();
if (base) {
success = provider->replaceScriptableModelMeshPart(base, -1, -1);
}
}
}
return success;
}
QScriptValue GraphicsScriptingInterface::getMeshes(QUuid uuid) {
scriptable::ScriptableModel* meshes{ nullptr };
bool success = false;
QString error;
auto appProvider = DependencyManager::get<scriptable::ModelProviderFactory>();
qCDebug(graphics_scripting) << "appProvider" << appProvider.data();
scriptable::ModelProviderPointer provider = appProvider ? appProvider->lookupModelProvider(uuid) : nullptr;
QString providerType = provider ? SpatiallyNestable::nestableTypeToString(provider->modelProviderType) : QString();
if (providerType.isEmpty()) {
providerType = "unknown";
}
if (provider) {
auto scriptableMeshes = provider->getScriptableModel(&success);
if (success) {
meshes = scriptable::make_scriptowned<scriptable::ScriptableModel>(scriptableMeshes);
if (meshes->objectName().isEmpty()) {
meshes->setObjectName(providerType+"::meshes");
}
if (meshes->objectID.isNull()) {
meshes->objectID = uuid.toString();
}
meshes->metadata["provider"] = SpatiallyNestable::nestableTypeToString(provider->modelProviderType);
}
}
if (!success) {
error = QString("failed to get meshes from %1 provider for uuid %2").arg(providerType).arg(uuid.toString());
}
QPointer<BaseScriptEngine> scriptEngine = dynamic_cast<BaseScriptEngine*>(engine());
QScriptValue result = error.isEmpty() ? scriptEngine->toScriptValue(meshes) : scriptEngine->makeError(error);
if (result.isError()) {
qCWarning(graphics_scripting) << "GraphicsScriptingInterface::getMeshes ERROR" << result.toString();
if (context()) {
context()->throwValue(error);
bool GraphicsScriptingInterface::updateModelObject(QUuid uuid, const scriptable::ScriptableModelPointer model) {
if (auto provider = getModelProvider(uuid)) {
if (auto base = model->operator scriptable::ScriptableModelBasePointer()) {
#ifdef SCRIPTABLE_MESH_DEBUG
qDebug() << "replaceScriptableModelMeshPart" << model->toString() << -1 << -1;
#endif
return provider->replaceScriptableModelMeshPart(base, -1, -1);
} else {
qCWarning(graphics_scripting) << "GraphicsScriptingInterface::getMeshes ERROR" << result.toString();
qDebug() << "replaceScriptableModelMeshPart -- !base" << model << base << -1 << -1;
}
return QScriptValue::NullValue;
} else {
qDebug() << "replaceScriptableModelMeshPart -- !provider";
}
return scriptEngine->toScriptValue(meshes);
return false;
}
QString GraphicsScriptingInterface::meshToOBJ(const scriptable::ScriptableModel& _in) {
scriptable::ModelProviderPointer GraphicsScriptingInterface::getModelProvider(QUuid uuid) {
QString error;
if (auto appProvider = DependencyManager::get<scriptable::ModelProviderFactory>()) {
if (auto provider = appProvider->lookupModelProvider(uuid)) {
return provider;
} else {
error = "provider unavailable for " + uuid.toString();
}
} else {
error = "appProvider unavailable";
}
if (context()) {
context()->throwError(error);
} else {
qCWarning(graphics_scripting) << "GraphicsScriptingInterface::getModelProvider ERROR" << error;
}
return nullptr;
}
scriptable::ScriptableModelPointer GraphicsScriptingInterface::newModelObject(QVector<scriptable::ScriptableMeshPointer> meshes) {
auto modelWrapper = scriptable::make_scriptowned<scriptable::ScriptableModel>();
modelWrapper->setObjectName("js::model");
if (meshes.isEmpty()) {
if (context()) {
context()->throwError("expected [meshes] array as first argument");
}
} else {
int i = 0;
for (const auto& mesh : meshes) {
if (mesh) {
modelWrapper->append(*mesh);
} else if (context()) {
context()->throwError(QString("invalid mesh at index: %1").arg(i));
}
i++;
}
}
return modelWrapper;
}
scriptable::ScriptableModelPointer GraphicsScriptingInterface::getModelObject(QUuid uuid) {
QString error, providerType = "unknown";
if (auto provider = getModelProvider(uuid)) {
providerType = SpatiallyNestable::nestableTypeToString(provider->modelProviderType);
auto modelObject = provider->getScriptableModel();
if (modelObject.objectID == uuid) {
if (modelObject.meshes.size()) {
auto modelWrapper = scriptable::make_scriptowned<scriptable::ScriptableModel>(modelObject);
modelWrapper->setObjectName(providerType+"::"+uuid.toString()+"::model");
return modelWrapper;
} else {
error = "no meshes available: " + modelObject.objectID.toString();
}
} else {
error = "objectID mismatch: " + modelObject.objectID.toString();
}
} else {
error = "provider unavailable";
}
auto errorMessage = QString("failed to get meshes from %1 provider for uuid %2 (%3)").arg(providerType).arg(uuid.toString()).arg(error);
qCWarning(graphics_scripting) << "GraphicsScriptingInterface::getModelObject ERROR" << errorMessage;
if (context()) {
context()->throwError(errorMessage);
}
return nullptr;
}
#ifdef SCRIPTABLE_MESH_TODO
bool GraphicsScriptingInterface::updateMeshPart(scriptable::ScriptableMeshPointer mesh, scriptable::ScriptableMeshPartPointer part) {
Q_ASSERT(mesh);
Q_ASSERT(part);
Q_ASSERT(part->parentMesh);
auto tmp = exportMeshPart(mesh, part->partIndex);
if (part->parentMesh == mesh) {
#ifdef SCRIPTABLE_MESH_DEBUG
qCInfo(graphics_scripting) << "updateMeshPart -- update via clone" << mesh << part;
#endif
tmp->replaceMeshData(part->cloneMeshPart());
return false;
} else {
#ifdef SCRIPTABLE_MESH_DEBUG
qCInfo(graphics_scripting) << "updateMeshPart -- update via inplace" << mesh << part;
#endif
tmp->replaceMeshData(part);
return true;
}
}
#endif
QString GraphicsScriptingInterface::exportModelToOBJ(const scriptable::ScriptableModel& _in) {
const auto& in = _in.getConstMeshes();
if (in.size()) {
QList<scriptable::MeshPointer> meshes;
@ -152,3 +184,5 @@ MeshPointer GraphicsScriptingInterface::getMeshPointer(scriptable::ScriptableMes
}
return mesh;
}
#include "GraphicsScriptingInterface.moc"

View file

@ -29,16 +29,24 @@ public:
public slots:
/**jsdoc
* Returns the meshes associated with a UUID (entityID, overlayID, or avatarID)
* Returns the model/meshes associated with a UUID (entityID, overlayID, or avatarID)
*
* @function GraphicsScriptingInterface.getMeshes
* @param {EntityID} entityID The ID of the entity whose meshes are to be retrieve
* @function GraphicsScriptingInterface.getModel
* @param {UUID} The objectID of the model whose meshes are to be retrieve
*/
QScriptValue getMeshes(QUuid uuid);
bool updateMeshes(QUuid uuid, const scriptable::ScriptableModelPointer model);
bool updateMeshes(QUuid uuid, const scriptable::ScriptableMeshPointer mesh, int meshIndex=0, int partIndex=0);
scriptable::ModelProviderPointer getModelProvider(QUuid uuid);
scriptable::ScriptableModelPointer getModelObject(QUuid uuid);
bool updateModelObject(QUuid uuid, const scriptable::ScriptableModelPointer model);
scriptable::ScriptableModelPointer newModelObject(QVector<scriptable::ScriptableMeshPointer> meshes);
QString meshToOBJ(const scriptable::ScriptableModel& in);
#ifdef SCRIPTABLE_MESH_TODO
scriptable::ScriptableMeshPartPointer exportMeshPart(scriptable::ScriptableMeshPointer mesh, int part=0) {
return scriptable::make_scriptowned<scriptable::ScriptableMeshPart>(mesh, part);
}
bool updateMeshPart(scriptable::ScriptableMeshPointer mesh, scriptable::ScriptableMeshPartPointer part);
#endif
QString exportModelToOBJ(const scriptable::ScriptableModel& in);
private:
scriptable::MeshPointer getMeshPointer(scriptable::ScriptableMeshPointer meshProxy);

View file

@ -43,23 +43,13 @@ namespace scriptable {
return toDebugString(qobject_cast<QObject*>(tmp.get()));
}
// C++ > QtOwned instance
template <typename T, class... Rest>
std::shared_ptr<T> make_qtowned(Rest... rest) {
T* tmp = new T(rest...);
if (tmp) {
tmp->metadata["__ownership__"] = QScriptEngine::QtOwnership;
}
return std::shared_ptr<T>(tmp);
}
// C++ > ScriptOwned JS instance
// Helper for creating C++ > ScriptOwned JS instances
// (NOTE: this also helps track in the code where we need to update later if switching to
// std::shared_ptr's -- something currently non-trivial given mixed JS/C++ object ownership)
template <typename T, class... Rest>
QPointer<T> make_scriptowned(Rest... rest) {
T* tmp = new T(rest...);
if (tmp) {
tmp->metadata["__ownership__"] = QScriptEngine::ScriptOwnership;
}
return QPointer<T>(tmp);
auto instance = QPointer<T>(new T(rest...));
Q_ASSERT(instance && instance->parent());
return instance;
}
}

View file

@ -5,6 +5,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "Forward.h"
#include "ScriptableMesh.h"
#include "BufferViewScripting.h"
@ -19,7 +21,7 @@
#include <graphics/BufferViewHelpers.h>
#include <graphics/Geometry.h>
#include "ScriptableMesh.moc"
// #define SCRIPTABLE_MESH_DEBUG 1
scriptable::ScriptableMeshPart::ScriptableMeshPart(scriptable::ScriptableMeshPointer parentMesh, int partIndex)
: QObject(), parentMesh(parentMesh), partIndex(partIndex) {
@ -77,7 +79,9 @@ QVector<quint32> scriptable::ScriptableMesh::findNearbyIndices(const glm::vec3&
QVector<quint32> scriptable::ScriptableMesh::getIndices() const {
QVector<quint32> result;
if (auto mesh = getMeshPointer()) {
#ifdef SCRIPTABLE_MESH_DEBUG
qCDebug(graphics_scripting, "getTriangleIndices mesh %p", mesh.get());
#endif
gpu::BufferView indexBufferView = mesh->getIndexBuffer();
if (quint32 count = (quint32)indexBufferView.getNumElements()) {
result.resize(count);
@ -121,20 +125,16 @@ QVector<QString> scriptable::ScriptableMesh::getAttributeNames() const {
return result;
}
// override
QVariantMap scriptable::ScriptableMesh::getVertexAttributes(quint32 vertexIndex) const {
return getVertexAttributes(vertexIndex, getAttributeNames());
}
bool scriptable::ScriptableMesh::setVertexAttributes(quint32 vertexIndex, QVariantMap attributes) {
//qCInfo(graphics_scripting) << "setVertexAttributes" << vertexIndex << attributes;
metadata["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate);
for (auto& a : buffer_helpers::gatherBufferViews(getMeshPointer())) {
const auto& name = a.first;
const auto& value = attributes.value(name);
if (value.isValid()) {
auto& view = a.second;
//qCDebug(graphics_scripting) << "setVertexAttributes" << vertexIndex << name;
buffer_helpers::fromVariant(view, vertexIndex, value);
} else {
//qCDebug(graphics_scripting) << "(skipping) setVertexAttributes" << vertexIndex << name;
@ -269,7 +269,6 @@ quint32 scriptable::ScriptableMesh::mapAttributeValues(QScriptValue _callback) {
#endif
auto obj = js->newObject();
auto attributeViews = buffer_helpers::gatherBufferViews(mesh, { "normal", "color" });
metadata["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate);
uint32_t i = 0;
for (; i < nPositions; i++) {
for (const auto& a : attributeViews) {
@ -303,13 +302,13 @@ quint32 scriptable::ScriptableMesh::mapAttributeValues(QScriptValue _callback) {
}
quint32 scriptable::ScriptableMeshPart::mapAttributeValues(QScriptValue callback) {
return parentMesh ? parentMesh->mapAttributeValues(callback) : 0;
return parentMesh ? parentMesh->mapAttributeValues(callback) : 0;
}
bool scriptable::ScriptableMeshPart::unrollVertices(bool recalcNormals) {
auto mesh = getMeshPointer();
#ifdef SCRIPTABLE_MESH_DEBUG
qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices" << !!mesh<< !!meshProxy;
qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices" << !!mesh<< !!parentMesh;
#endif
if (!mesh) {
return false;
@ -321,8 +320,9 @@ bool scriptable::ScriptableMeshPart::unrollVertices(bool recalcNormals) {
auto buffer = new gpu::Buffer();
buffer->resize(numPoints * sizeof(uint32_t));
auto newindices = gpu::BufferView(buffer, { gpu::SCALAR, gpu::UINT32, gpu::INDEX });
metadata["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate);
#ifdef SCRIPTABLE_MESH_DEBUG
qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices numPoints" << numPoints;
#endif
auto attributeViews = buffer_helpers::gatherBufferViews(mesh);
for (const auto& a : attributeViews) {
auto& view = a.second;
@ -330,15 +330,17 @@ bool scriptable::ScriptableMeshPart::unrollVertices(bool recalcNormals) {
auto buffer = new gpu::Buffer();
buffer->resize(numPoints * sz);
auto points = gpu::BufferView(buffer, view._element);
auto src = (uint8_t*)view._buffer->getData();
auto dest = (uint8_t*)points._buffer->getData();
auto slot = buffer_helpers::ATTRIBUTES[a.first];
#ifdef SCRIPTABLE_MESH_DEBUG
if (0) {
auto src = (uint8_t*)view._buffer->getData();
auto dest = (uint8_t*)points._buffer->getData();
qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices buffer" << a.first;
qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices source" << view.getNumElements();
qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices dest" << points.getNumElements();
qCInfo(graphics_scripting) << "ScriptableMeshPart::unrollVertices sz" << sz << src << dest << slot;
}
#endif
auto esize = indices._element.getSize();
const char* hint= a.first.toStdString().c_str();
for(quint32 i = 0; i < numPoints; i++) {
@ -381,8 +383,6 @@ bool scriptable::ScriptableMeshPart::replaceMeshData(scriptable::ScriptableMeshP
"target:" << QString::fromStdString(target->displayName) <<
"attributes:" << attributes;
metadata["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate);
// remove attributes only found on target mesh, unless user has explicitly specified the relevant attribute names
if (attributeNames.isEmpty()) {
auto attributeViews = buffer_helpers::gatherBufferViews(target);
@ -438,7 +438,6 @@ bool scriptable::ScriptableMeshPart::dedupeVertices(float epsilon) {
uniqueVerts.reserve((int)numPositions);
QMap<quint32,quint32> remapIndices;
metadata["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate);
for (quint32 i = 0; i < numPositions; i++) {
const quint32 numUnique = uniqueVerts.size();
const auto& position = positions.get<glm::vec3>(i);
@ -508,63 +507,51 @@ scriptable::ScriptableMeshPointer scriptable::ScriptableMesh::cloneMesh(bool rec
return nullptr;
}
auto clone = buffer_helpers::cloneMesh(mesh);
if (recalcNormals) {
buffer_helpers::recalculateNormals(clone);
}
auto meshPointer = scriptable::make_scriptowned<scriptable::ScriptableMesh>(provider, model, clone, metadata);
clone.reset(); // free local reference
// qCInfo(graphics_scripting) << "========= ScriptableMesh::cloneMesh..." << meshPointer << meshPointer->ownedMesh.use_count();
//scriptable::MeshPointer* ppMesh = new scriptable::MeshPointer();
//*ppMesh = clone;
if (0 && meshPointer) {
scriptable::WeakMeshPointer delme = meshPointer->mesh;
QString debugString = scriptable::toDebugString(meshPointer);
QObject::connect(meshPointer, &QObject::destroyed, meshPointer, [=]() {
// qCWarning(graphics_scripting) << "*************** cloneMesh/Destroy";
// qCWarning(graphics_scripting) << "*************** " << debugString << delme.lock().get();
if (!delme.expired()) {
QTimer::singleShot(250, this, [=]{
if (!delme.expired()) {
qCWarning(graphics_scripting) << "cloneMesh -- potential memory leak..." << debugString << delme.use_count();
}
});
}
});
}
meshPointer->metadata["last-modified"] = QDateTime::currentDateTime().toTimeSpec(Qt::OffsetFromUTC).toString(Qt::ISODate);
auto meshPointer = scriptable::make_scriptowned<scriptable::ScriptableMesh>(provider, model, clone, nullptr);
return scriptable::ScriptableMeshPointer(meshPointer);
}
scriptable::ScriptableMeshBase::ScriptableMeshBase(scriptable::WeakModelProviderPointer provider, scriptable::ScriptableModelBasePointer model, scriptable::WeakMeshPointer mesh, const QVariantMap& metadata)
: provider(provider), model(model), mesh(mesh), metadata(metadata) {}
scriptable::ScriptableMeshBase::ScriptableMeshBase(scriptable::WeakMeshPointer mesh) : scriptable::ScriptableMeshBase(scriptable::WeakModelProviderPointer(), nullptr, mesh, QVariantMap()) { }
scriptable::ScriptableMeshBase::ScriptableMeshBase(scriptable::MeshPointer mesh, const QVariantMap& metadata)
: ScriptableMeshBase(WeakModelProviderPointer(), nullptr, mesh, metadata) {
ownedMesh = mesh;
// note: we don't always want the JS side to prevent mesh data from being freed
scriptable::ScriptableMeshBase::ScriptableMeshBase(
scriptable::WeakModelProviderPointer provider, scriptable::ScriptableModelBasePointer model, scriptable::WeakMeshPointer weakMesh, QObject* parent
) : QObject(parent), provider(provider), model(model), weakMesh(weakMesh) {
if (parent) {
qCDebug(graphics_scripting) << "ScriptableMeshBase -- have parent QObject, creating strong neshref" << weakMesh.lock().get() << parent;
strongMesh = weakMesh.lock();
}
}
scriptable::ScriptableMeshBase::ScriptableMeshBase(scriptable::WeakMeshPointer weakMesh, QObject* parent) :
scriptable::ScriptableMeshBase(scriptable::WeakModelProviderPointer(), nullptr, weakMesh, parent) {
}
scriptable::ScriptableMeshBase& scriptable::ScriptableMeshBase::operator=(const scriptable::ScriptableMeshBase& view) {
provider = view.provider;
model = view.model;
mesh = view.mesh;
ownedMesh = view.ownedMesh;
metadata = view.metadata;
weakMesh = view.weakMesh;
strongMesh = view.strongMesh;
return *this;
}
scriptable::ScriptableMeshBase::~ScriptableMeshBase() {
ownedMesh.reset();
scriptable::ScriptableMeshBase::~ScriptableMeshBase() {
#ifdef SCRIPTABLE_MESH_DEBUG
qCInfo(graphics_scripting) << "//~ScriptableMeshBase" << this << "ownedMesh:" << ownedMesh.use_count() << "mesh:" << mesh.use_count();
qCInfo(graphics_scripting) << "//~ScriptableMeshBase" << this << "strongMesh:" << strongMesh.use_count() << "weakMesh:" << weakMesh.use_count();
#endif
strongMesh.reset();
}
scriptable::ScriptableMesh::~ScriptableMesh() {
ownedMesh.reset();
#ifdef SCRIPTABLE_MESH_DEBUG
qCInfo(graphics_scripting) << "//~ScriptableMesh" << this << "ownedMesh:" << ownedMesh.use_count() << "mesh:" << mesh.use_count();
qCInfo(graphics_scripting) << "//~ScriptableMesh" << this << "strongMesh:" << strongMesh.use_count() << "weakMesh:" << weakMesh.use_count();
#endif
strongMesh.reset();
}
QString scriptable::ScriptableMeshPart::toOBJ() {
@ -573,7 +560,7 @@ QString scriptable::ScriptableMeshPart::toOBJ() {
context()->throwError(QString("null mesh"));
} else {
qCWarning(graphics_scripting) << "null mesh";
}
}
return QString();
}
return writeOBJToString({ getMeshPointer() });
@ -585,12 +572,7 @@ namespace {
if (!object) {
return QScriptValue::NullValue;
}
auto ownership = object->metadata.value("__ownership__");
return engine->newQObject(
object,
ownership.isValid() ? static_cast<QScriptEngine::ValueOwnership>(ownership.toInt()) : QScriptEngine::QtOwnership
//, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects
);
return engine->newQObject(object, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater);
}
QScriptValue meshPointerToScriptValue(QScriptEngine* engine, const scriptable::ScriptableMeshPointer& in) {
@ -637,7 +619,7 @@ namespace scriptable {
qScriptRegisterSequenceMetaType<QVector<scriptable::ScriptableMeshPartPointer>>(engine);
qScriptRegisterSequenceMetaType<QVector<scriptable::ScriptableMeshPointer>>(engine);
qScriptRegisterSequenceMetaType<QVector<scriptable::uint32>>(engine);
qScriptRegisterMetaType(engine, qVectorUInt32ToScriptValue, qVectorUInt32FromScriptValue);
qScriptRegisterMetaType(engine, modelPointerToScriptValue, modelPointerFromScriptValue);
qScriptRegisterMetaType(engine, meshPointerToScriptValue, meshPointerFromScriptValue);
@ -645,50 +627,41 @@ namespace scriptable {
return metaTypeIds.size();
}
// callback helper that lets C++ method signatures remain simple (ie: taking a single callback argument) while
// still supporting extended Qt signal-like (scope, "methodName") and (scope, function(){}) "this" binding conventions
QScriptValue jsBindCallback(QScriptValue callback) {
if (callback.isObject() && callback.property("callback").isFunction()) {
return callback;
QScriptValue jsBindCallback(QScriptValue value) {
if (value.isObject() && value.property("callback").isFunction()) {
// value is already a bound callback
return value;
}
auto engine = callback.engine();
auto engine = value.engine();
auto context = engine ? engine->currentContext() : nullptr;
auto length = context ? context->argumentCount() : 0;
QScriptValue scope = context ? context->thisObject() : QScriptValue::NullValue;
QScriptValue method;
#ifdef SCRIPTABLE_MESH_DEBUG
qCInfo(graphics_scripting) << "jsBindCallback" << engine << length << scope.toQObject() << method.toString();
int i = 0;
for (; context && i < length; i++) {
if (context->argument(i).strictlyEquals(callback)) {
#endif
// find position in the incoming JS Function.arguments array (so we can test for the two-argument case)
for (int i = 0; context && i < length; i++) {
if (context->argument(i).strictlyEquals(value)) {
method = context->argument(i+1);
}
}
if (method.isFunction() || method.isString()) {
scope = callback;
// interpret as `API.func(..., scope, function callback(){})` or `API.func(..., scope, "methodName")`
scope = value;
} else {
method = callback;
// interpret as `API.func(..., function callback(){})`
method = value;
}
#ifdef SCRIPTABLE_MESH_DEBUG
qCInfo(graphics_scripting) << "scope:" << scope.toQObject() << "method:" << method.toString();
#endif
return ::makeScopedHandlerObject(scope, method);
}
}
bool scriptable::GraphicsScriptingInterface::updateMeshPart(ScriptableMeshPointer mesh, ScriptableMeshPartPointer part) {
Q_ASSERT(mesh);
Q_ASSERT(part);
Q_ASSERT(part->parentMesh);
auto tmp = exportMeshPart(mesh, part->partIndex);
if (part->parentMesh == mesh) {
#ifdef SCRIPTABLE_MESH_DEBUG
qCInfo(graphics_scripting) << "updateMeshPart -- update via clone" << mesh << part;
#endif
tmp->replaceMeshData(part->cloneMeshPart());
return false;
} else {
#ifdef SCRIPTABLE_MESH_DEBUG
qCInfo(graphics_scripting) << "updateMeshPart -- update via inplace" << mesh << part;
#endif
tmp->replaceMeshData(part);
return true;
}
}
#include "ScriptableMesh.moc"

View file

@ -1,3 +1,10 @@
//
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#include "ScriptableModel.h"
@ -26,25 +33,26 @@ namespace scriptable {
Q_PROPERTY(uint32 numAttributes READ getNumAttributes)
Q_PROPERTY(uint32 numVertices READ getNumVertices)
Q_PROPERTY(uint32 numIndices READ getNumIndices)
Q_PROPERTY(QVariantMap metadata MEMBER metadata)
Q_PROPERTY(QVector<QString> attributeNames READ getAttributeNames)
Q_PROPERTY(QVector<scriptable::ScriptableMeshPartPointer> parts READ getMeshParts)
Q_PROPERTY(bool valid READ hasValidMesh)
bool hasValidMesh() const { return (bool)getMeshPointer(); }
Q_PROPERTY(bool validOwned READ hasValidOwnedMesh)
bool hasValidOwnedMesh() const { return (bool)getOwnedMeshPointer(); }
Q_PROPERTY(bool strong READ hasValidStrongMesh)
operator const ScriptableMeshBase*() const { return (qobject_cast<const scriptable::ScriptableMeshBase*>(this)); }
ScriptableMesh(scriptable::MeshPointer mesh) : ScriptableMeshBase(mesh), QScriptable() { ownedMesh = mesh; }
ScriptableMesh(WeakModelProviderPointer provider, ScriptableModelBasePointer model, MeshPointer mesh, const QVariantMap& metadata)
: ScriptableMeshBase(provider, model, mesh, metadata), QScriptable() { ownedMesh = mesh; }
ScriptableMesh(WeakModelProviderPointer provider, ScriptableModelBasePointer model, MeshPointer mesh, QObject* parent)
: ScriptableMeshBase(provider, model, mesh, parent), QScriptable() { strongMesh = mesh; }
ScriptableMesh(MeshPointer mesh, QObject* parent)
: ScriptableMeshBase(WeakModelProviderPointer(), nullptr, mesh, parent), QScriptable() { strongMesh = mesh; }
ScriptableMesh(const ScriptableMeshBase& other);
ScriptableMesh(const ScriptableMesh& other) : ScriptableMeshBase(other), QScriptable() {};
virtual ~ScriptableMesh();
Q_INVOKABLE const scriptable::ScriptableModelPointer getParentModel() const { return qobject_cast<scriptable::ScriptableModel*>(model); }
Q_INVOKABLE const scriptable::MeshPointer getOwnedMeshPointer() const { return ownedMesh; }
Q_INVOKABLE const scriptable::MeshPointer getOwnedMeshPointer() const { return strongMesh; }
scriptable::ScriptableMeshPointer getSelf() const { return const_cast<scriptable::ScriptableMesh*>(this); }
bool hasValidMesh() const { return !weakMesh.expired(); }
bool hasValidStrongMesh() const { return (bool)strongMesh; }
public slots:
uint32 getNumParts() const;
uint32 getNumVertices() const;
@ -65,9 +73,9 @@ namespace scriptable {
int _getSlotNumber(const QString& attributeName) const;
scriptable::ScriptableMeshPointer cloneMesh(bool recalcNormals = false);
scriptable::ScriptableMeshPointer cloneMesh(bool recalcNormals = false);
public:
operator bool() const { return !mesh.expired(); }
operator bool() const { return !weakMesh.expired(); }
public slots:
// QScriptEngine-specific wrappers
@ -88,11 +96,9 @@ namespace scriptable {
Q_PROPERTY(uint32 numIndices READ getNumIndices)
Q_PROPERTY(QVector<QString> attributeNames READ getAttributeNames)
Q_PROPERTY(QVariantMap metadata MEMBER metadata)
ScriptableMeshPart(scriptable::ScriptableMeshPointer parentMesh, int partIndex);
ScriptableMeshPart& operator=(const ScriptableMeshPart& view) { parentMesh=view.parentMesh; return *this; };
ScriptableMeshPart(const ScriptableMeshPart& other) : QObject(), QScriptable(), parentMesh(other.parentMesh), partIndex(other.partIndex) {}
ScriptableMeshPart(const ScriptableMeshPart& other) : QObject(other.parent()), QScriptable(), parentMesh(other.parentMesh), partIndex(other.partIndex) {}
public slots:
scriptable::ScriptableMeshPointer getParentMesh() const { return parentMesh; }
@ -134,23 +140,10 @@ namespace scriptable {
public:
scriptable::ScriptableMeshPointer parentMesh;
uint32 partIndex;
QVariantMap metadata;
protected:
int _elementsPerFace{ 3 };
QString _topology{ "triangles" };
scriptable::MeshPointer getMeshPointer() const { return parentMesh ? parentMesh->getMeshPointer() : nullptr; }
};
class GraphicsScriptingInterface : public QObject, QScriptable {
Q_OBJECT
public:
GraphicsScriptingInterface(QObject* parent = nullptr) : QObject(parent), QScriptable() {}
GraphicsScriptingInterface(const GraphicsScriptingInterface& other) : QObject(), QScriptable() {}
public slots:
ScriptableMeshPartPointer exportMeshPart(ScriptableMeshPointer mesh, int part=0) {
return ScriptableMeshPartPointer(new ScriptableMeshPart(mesh, part));
}
bool updateMeshPart(ScriptableMeshPointer mesh, ScriptableMeshPartPointer part);
scriptable::MeshPointer getMeshPointer() const { return parentMesh ? parentMesh->getMeshPointer() : nullptr; }
};
// callback helper that lets C++ method signatures remain simple (ie: taking a single callback argument) while
@ -165,26 +158,5 @@ Q_DECLARE_METATYPE(scriptable::ScriptableMeshPointer)
Q_DECLARE_METATYPE(QVector<scriptable::ScriptableMeshPointer>)
Q_DECLARE_METATYPE(scriptable::ScriptableMeshPartPointer)
Q_DECLARE_METATYPE(QVector<scriptable::ScriptableMeshPartPointer>)
Q_DECLARE_METATYPE(scriptable::GraphicsScriptingInterface)
// FIXME: MESHFACES: faces were supported in the original Model.* API -- are they still needed/used/useful for anything yet?
#include <memory>
namespace mesh {
class MeshFace;
using MeshFaces = QVector<mesh::MeshFace>;
class MeshFace {
public:
MeshFace() {}
MeshFace(QVector<scriptable::uint32> vertexIndices) : vertexIndices(vertexIndices) {}
~MeshFace() {}
QVector<scriptable::uint32> vertexIndices;
// TODO -- material...
};
};
Q_DECLARE_METATYPE(mesh::MeshFace)
Q_DECLARE_METATYPE(QVector<mesh::MeshFace>)
Q_DECLARE_METATYPE(scriptable::uint32)
Q_DECLARE_METATYPE(QVector<scriptable::uint32>)

View file

@ -13,25 +13,13 @@
#include "ScriptableMesh.h"
#include <QtScript/QScriptEngine>
//#include "ScriptableModel.moc"
void scriptable::ScriptableModelBase::mixin(const QVariantMap& modelMetaData) {
for (const auto& key : modelMetaData.keys()) {
const auto& value = modelMetaData[key];
if (metadata.contains(key) && metadata[key].type() == (QVariant::Type)QMetaType::QVariantList) {
qCDebug(graphics_scripting) << "CONCATENATING" << key << metadata[key].toList().size() << "+" << value.toList().size();
metadata[key] = metadata[key].toList() + value.toList();
} else {
metadata[key] = modelMetaData[key];
}
}
}
// #define SCRIPTABLE_MESH_DEBUG 1
scriptable::ScriptableModelBase& scriptable::ScriptableModelBase::operator=(const scriptable::ScriptableModelBase& other) {
provider = other.provider;
objectID = other.objectID;
metadata = other.metadata;
for (auto& mesh : other.meshes) {
for (const auto& mesh : other.meshes) {
append(mesh);
}
return *this;
@ -43,36 +31,27 @@ scriptable::ScriptableModelBase::~ScriptableModelBase() {
#endif
// makes cleanup order more deterministic to help with debugging
for (auto& m : meshes) {
m.ownedMesh.reset();
m.strongMesh.reset();
}
meshes.clear();
}
void scriptable::ScriptableModelBase::append(scriptable::WeakMeshPointer mesh, const QVariantMap& metadata) {
meshes << ScriptableMeshBase{ provider, this, mesh, metadata };
void scriptable::ScriptableModelBase::append(scriptable::WeakMeshPointer mesh) {
meshes << ScriptableMeshBase{ provider, this, mesh, this /*parent*/ };
}
void scriptable::ScriptableModelBase::append(const ScriptableMeshBase& mesh, const QVariantMap& modelMetaData) {
void scriptable::ScriptableModelBase::append(const ScriptableMeshBase& mesh) {
if (mesh.provider.lock().get() != provider.lock().get()) {
qCDebug(graphics_scripting) << "warning: appending mesh from different provider..." << mesh.provider.lock().get() << " != " << provider.lock().get();
}
meshes << mesh;
mixin(modelMetaData);
}
void scriptable::ScriptableModelBase::append(const ScriptableModelBase& other, const QVariantMap& modelMetaData) {
for (const auto& mesh : other.meshes) {
append(mesh);
}
mixin(other.metadata);
mixin(modelMetaData);
}
QString scriptable::ScriptableModel::toString() const {
return QString("[ScriptableModel%1%2]")
return QString("[ScriptableModel%1%2 numMeshes=%3]")
.arg(objectID.isNull() ? "" : " objectID="+objectID.toString())
.arg(objectName().isEmpty() ? "" : " name=" +objectName());
.arg(objectName().isEmpty() ? "" : " name=" +objectName())
.arg(meshes.size());
}
scriptable::ScriptableModelPointer scriptable::ScriptableModel::cloneModel(const QVariantMap& options) {
@ -131,3 +110,5 @@ quint32 scriptable::ScriptableModel::mapAttributeValues(QScriptValue callback) {
}
return result;
}
#include "ScriptableModel.moc"

View file

@ -1,15 +1,22 @@
//
// Copyright 2018 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#pragma once
#include "Forward.h"
#include "GraphicsScriptingUtil.h"
class QScriptValue;
namespace scriptable {
class ScriptableModel : public ScriptableModelBase {
Q_OBJECT
public:
Q_PROPERTY(QUuid objectID MEMBER objectID CONSTANT)
Q_PROPERTY(QVariantMap metadata MEMBER metadata CONSTANT)
Q_PROPERTY(uint32 numMeshes READ getNumMeshes)
Q_PROPERTY(QVector<scriptable::ScriptableMeshPointer> meshes READ getMeshes)
@ -27,11 +34,9 @@ namespace scriptable {
QVector<scriptable::ScriptableMeshPointer> getMeshes();
const QVector<scriptable::ScriptableMeshPointer> getConstMeshes() const;
operator scriptable::ScriptableModelBasePointer() {
QPointer<scriptable::ScriptableModelBase> p;
p = qobject_cast<scriptable::ScriptableModelBase*>(this);
return p;
return QPointer<scriptable::ScriptableModelBase>(qobject_cast<scriptable::ScriptableModelBase*>(this));
}
// QScriptEngine-specific wrappers
Q_INVOKABLE uint32 mapAttributeValues(QScriptValue callback);

View file

@ -28,10 +28,6 @@ namespace glm {
using hvec4 = glm::tvec4<glm::detail::hdata>;
}
#ifdef DEBUG_BUFFERVIEW_SCRIPTING
#include "../../graphics-scripting/src/graphics-scripting/DebugNames.h"
#endif
namespace {
QLoggingCategory bufferhelper_logging{ "hifi.bufferview" };
}
@ -109,9 +105,7 @@ bool buffer_helpers::fromVariant(const gpu::BufferView& view, quint32 index, con
const auto dataType = element.getType();
const auto byteLength = element.getSize();
const auto BYTES_PER_ELEMENT = byteLength / vecN;
#ifdef DEBUG_BUFFERVIEW_SCRIPTING
qCDebug(bufferhelper_logging) << "bufferViewElementFromVariant" << index << DebugNames::stringFrom(dataType) << BYTES_PER_ELEMENT << vecN;
#endif
if (BYTES_PER_ELEMENT == 1) {
switch(vecN) {
case 2: setBufferViewElement<glm::u8vec2>(view, index, v); return true;
@ -176,18 +170,12 @@ QVariant buffer_helpers::toVariant(const gpu::BufferView& view, quint32 index, b
auto byteOffset = index * vecN * BYTES_PER_ELEMENT;
auto maxByteOffset = (view._size - 1) * vecN * BYTES_PER_ELEMENT;
if (byteOffset > maxByteOffset) {
#ifdef DEBUG_BUFFERVIEW_SCRIPTING
qDebug() << "toVariant -- " << DebugNames::stringFrom(dataType)
#endif
qDebug() << "toVariant -- byteOffset out of range " << byteOffset << " < " << maxByteOffset;
qDebug() << "toVariant -- index: " << index << "numElements" << view.getNumElements();
qDebug() << "toVariant -- vecN: " << vecN << "byteLength" << byteLength << "BYTES_PER_ELEMENT" << BYTES_PER_ELEMENT;
}
Q_ASSERT(byteOffset <= maxByteOffset);
}
#ifdef DEBUG_BUFFERVIEW_SCRIPTING
qCDebug(bufferhelper_logging) << "toVariant -- " << index << DebugNames::stringFrom(dataType) << BYTES_PER_ELEMENT << vecN;
#endif
if (BYTES_PER_ELEMENT == 1) {
switch(vecN) {
case 2: return getBufferViewElement<glm::u8vec2>(view, index, asArray);
@ -305,13 +293,8 @@ template <typename T> struct GpuScalarToGlm;
struct GpuToGlmAdapter {
static float error(const QString& name, const gpu::BufferView& view, quint32 index, const char *hint) {
QString debugName;
#ifdef DEBUG_BUFFERVIEW_SCRIPTING
debugName = DebugNames::stringFrom(view._element.getType())
#endif
qDebug() << QString("GpuToGlmAdapter:: unhandled type=%1(element=%2(%3)) size=%4(per=%5) vec%6 hint=%7 #%8")
qDebug() << QString("GpuToGlmAdapter:: unhandled type=%1(element=%2) size=%3(per=%4) vec%5 hint=%6 #%7")
.arg(name)
.arg(debugName)
.arg(view._element.getType())
.arg(view._element.getSize())
.arg(view._element.getSize() / view._element.getScalarCount())
@ -404,7 +387,7 @@ struct getVec {
}
return result;
}
static T __to_scalar__(const gpu::BufferView& view, quint32 index, const char *hint) {
static T __to_value__(const gpu::BufferView& view, quint32 index, const char *hint) {
assert(boundsCheck(view, index));
return FUNC::get(view, index, hint);
}
@ -425,18 +408,18 @@ template <> QVector<glm::vec4> buffer_helpers::toVector<glm::vec4>(const gpu::Bu
}
// indexed conversion accessors (similar to "view.convert<T>(i)" existed)
// indexed conversion accessors (like the hypothetical "view.convert<T>(i)")
template <> int buffer_helpers::convert<int>(const gpu::BufferView& view, quint32 index, const char *hint) {
return getVec<GpuScalarToGlm<int>,int>::__to_scalar__(view, index, hint);
return getVec<GpuScalarToGlm<int>,int>::__to_value__(view, index, hint);
}
template <> glm::vec2 buffer_helpers::convert<glm::vec2>(const gpu::BufferView& view, quint32 index, const char *hint) {
return getVec<GpuVec2ToGlm<glm::vec2>,glm::vec2>::__to_scalar__(view, index, hint);
return getVec<GpuVec2ToGlm<glm::vec2>,glm::vec2>::__to_value__(view, index, hint);
}
template <> glm::vec3 buffer_helpers::convert<glm::vec3>(const gpu::BufferView& view, quint32 index, const char *hint) {
return getVec<GpuVec3ToGlm<glm::vec3>,glm::vec3>::__to_scalar__(view, index, hint);
return getVec<GpuVec3ToGlm<glm::vec3>,glm::vec3>::__to_value__(view, index, hint);
}
template <> glm::vec4 buffer_helpers::convert<glm::vec4>(const gpu::BufferView& view, quint32 index, const char *hint) {
return getVec<GpuVec4ToGlm<glm::vec4>,glm::vec4>::__to_scalar__(view, index, hint);
return getVec<GpuVec4ToGlm<glm::vec4>,glm::vec4>::__to_value__(view, index, hint);
}
gpu::BufferView buffer_helpers::clone(const gpu::BufferView& input) {
@ -492,9 +475,6 @@ namespace {
vsize > bufferView._size
);
QString hint = QString("%1").arg(slot);
#ifdef DEBUG_BUFFERVIEW_SCRIPTING
hint = DebugNames::stringFrom(slot);
#endif
#ifdef DEV_BUILD
auto beforeCount = bufferView.getNumElements();
auto beforeTotal = bufferView._size;
@ -519,9 +499,6 @@ namespace {
auto afterTotal = bufferView._size;
if (beforeTotal != afterTotal || beforeCount != afterCount) {
QString typeName = QString("%1").arg(bufferView._element.getType());
#ifdef DEBUG_BUFFERVIEW_SCRIPTING
typeName = DebugNames::stringFrom(bufferView._element.getType());
#endif
qCDebug(bufferhelper_logging, "NOTE:: _expandedAttributeBuffer.%s vec%d %s (before count=%lu bytes=%lu // after count=%lu bytes=%lu)",
hint.toStdString().c_str(), bufferView._element.getScalarCount(),
typeName.toStdString().c_str(), beforeCount, beforeTotal, afterCount, afterTotal);
@ -557,9 +534,6 @@ std::map<QString, gpu::BufferView> buffer_helpers::gatherBufferViews(graphics::M
if (beforeCount > 0) {
auto element = view._element;
QString typeName = QString("%1").arg(element.getType());
#ifdef DEBUG_BUFFERVIEW_SCRIPTING
typeName = DebugNames::stringFrom(element.getType());
#endif
attributeViews[name] = getBufferView(mesh, slot);

View file

@ -492,7 +492,6 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g
{ "v2", vec3toVariant(bestModelTriangle.v2) },
};
}
}
}
@ -591,38 +590,41 @@ MeshProxyList Model::getMeshes() const {
// FIXME: temporary workaround that updates the whole FBXGeometry (to keep findRayIntersection in sync)
#include "Model_temporary_hack.cpp.h"
bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer _newModel, int meshIndex, int partIndex) {
bool Model::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer newModel, int meshIndex, int partIndex) {
QMutexLocker lock(&_mutex);
if (!isLoaded()) {
qDebug() << "!isLoaded" << this;
return false;
}
{
// FIXME: temporary workaround for updating the whole FBXGeometry (to keep findRayIntersection in sync)
auto newRenderGeometry = new MyGeometryMappingResource(
_url, _renderGeometry, _newModel ? scriptable::make_qtowned<scriptable::ScriptableModelBase>(*_newModel) : nullptr
);
_visualGeometryRequestFailed = false;
deleteGeometry();
_renderGeometry.reset(newRenderGeometry);
_rig.destroyAnimGraph();
updateGeometry();
calculateTriangleSets();
_needsReload = false;
_needsFixupInScene = true;
setRenderItemsNeedUpdate();
if (!newModel || !newModel->meshes.size()) {
qDebug() << "!newModel.meshes.size()" << this;
return false;
}
auto resource = new MyGeometryResource(_url, _renderGeometry, newModel);
_needsReload = false;
_needsUpdateTextures = false;
_visualGeometryRequestFailed = false;
_needsFixupInScene = true;
invalidCalculatedMeshBoxes();
deleteGeometry();
_renderGeometry.reset(resource);
updateGeometry();
calculateTriangleSets();
setRenderItemsNeedUpdate();
return true;
}
scriptable::ScriptableModelBase Model::getScriptableModel(bool* ok) {
scriptable::ScriptableModelBase Model::getScriptableModel() {
QMutexLocker lock(&_mutex);
scriptable::ScriptableModelBase result;
if (!isLoaded()) {
qCDebug(renderutils) << "Model::getScriptableModel -- !isLoaded";
return scriptable::ModelProvider::modelUnavailableError(ok);
return result;
}
const FBXGeometry& geometry = getFBXGeometry();
@ -630,22 +632,9 @@ scriptable::ScriptableModelBase Model::getScriptableModel(bool* ok) {
for (int i = 0; i < numberOfMeshes; i++) {
const FBXMesh& fbxMesh = geometry.meshes.at(i);
if (auto mesh = fbxMesh._mesh) {
auto name = geometry.getModelNameOfMesh(i);
result.append(std::const_pointer_cast<graphics::Mesh>(mesh), {
{ "index", i },
{ "name", name },
{ "meshIndex", fbxMesh.meshIndex },
{ "displayName", QString::fromStdString(mesh->displayName) },
{ "modelName", QString::fromStdString(mesh->modelName) },
{ "modelTransform", buffer_helpers::toVariant(fbxMesh.modelTransform) },
{ "transform", buffer_helpers::toVariant(geometry.offset * fbxMesh.modelTransform) },
{ "extents", buffer_helpers::toVariant(fbxMesh.meshExtents) },
});
result.append(mesh);
}
}
if (ok) {
*ok = true;
}
return result;
}

View file

@ -317,7 +317,7 @@ public:
int getResourceDownloadAttempts() { return _renderWatcher.getResourceDownloadAttempts(); }
int getResourceDownloadAttemptsRemaining() { return _renderWatcher.getResourceDownloadAttemptsRemaining(); }
virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override;
virtual scriptable::ScriptableModelBase getScriptableModel() override;
virtual bool replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer model, int meshIndex, int partIndex) override;
void scaleToFit();

View file

@ -1,9 +1,9 @@
#include <graphics/BufferViewHelpers.h>
#include <graphics-scripting/GraphicsScriptingUtil.h>
class MyGeometryMappingResource : public GeometryResource {
class MyGeometryResource : public GeometryResource {
public:
shared_ptr<FBXGeometry> fbxGeometry;
MyGeometryMappingResource(const QUrl& url, Geometry::Pointer originalGeometry, std::shared_ptr<scriptable::ScriptableModelBase> newModel) : GeometryResource(url) {
MyGeometryResource(const QUrl& url, Geometry::Pointer originalGeometry, scriptable::ScriptableModelBasePointer newModel) : GeometryResource(url) {
fbxGeometry = std::make_shared<FBXGeometry>();
FBXGeometry& geometry = *fbxGeometry.get();
const FBXGeometry* original;
@ -15,41 +15,12 @@ public:
original = tmpGeometry.get();
}
geometry.originalURL = original->originalURL;
geometry.bindExtents = original->bindExtents;
geometry.author = original->author;
geometry.applicationName = original->applicationName;
for (const auto &j : original->joints) {
geometry.joints << j;
}
geometry.jointIndices = QHash<QString,int>{ original->jointIndices };
geometry.animationFrames = QVector<FBXAnimationFrame>{ original->animationFrames };
geometry.meshIndicesToModelNames = QHash<int, QString>{ original->meshIndicesToModelNames };
geometry.blendshapeChannelNames = QList<QString>{ original->blendshapeChannelNames };
geometry.hasSkeletonJoints = original->hasSkeletonJoints;
geometry.offset = original->offset;
geometry.leftEyeJointIndex = original->leftEyeJointIndex;
geometry.rightEyeJointIndex = original->rightEyeJointIndex;
geometry.neckJointIndex = original->neckJointIndex;
geometry.rootJointIndex = original->rootJointIndex;
geometry.leanJointIndex = original->leanJointIndex;
geometry.headJointIndex = original->headJointIndex;
geometry.leftHandJointIndex = original->leftHandJointIndex;
geometry.rightHandJointIndex = original->rightHandJointIndex;
geometry.leftToeJointIndex = original->leftToeJointIndex;
geometry.rightToeJointIndex = original->rightToeJointIndex;
geometry.leftEyeSize = original->leftEyeSize;
geometry.rightEyeSize = original->rightEyeSize;
geometry.humanIKJointIndices = original->humanIKJointIndices;
geometry.palmDirection = original->palmDirection;
geometry.neckPivot = original->neckPivot;
geometry.bindExtents = original->bindExtents;
// Copy materials
QHash<QString, size_t> materialIDAtlas;
for (const FBXMaterial& material : original->materials) {
materialIDAtlas[material.materialID] = _materials.size();
_materials.push_back(std::make_shared<NetworkMaterial>(material, _textureBaseUrl));
}
std::shared_ptr<GeometryMeshes> meshes = std::make_shared<GeometryMeshes>();
@ -58,6 +29,7 @@ public:
if (newModel) {
geometry.meshExtents.reset();
for (const auto& newMesh : newModel->meshes) {
// qDebug() << "newMesh #" << meshID;
FBXMesh mesh;
if (meshID < original->meshes.size()) {
mesh = original->meshes.at(meshID); // copy
@ -73,7 +45,7 @@ public:
mesh.createBlendShapeTangents(false);
geometry.meshes << mesh;
// Copy mesh pointers
meshes->emplace_back(newMesh.getMeshPointer());//buffer_helpers::cloneMesh(ptr));
meshes->emplace_back(newMesh.getMeshPointer());
int partID = 0;
const auto oldParts = mesh.parts;
mesh.parts.clear();
@ -83,7 +55,7 @@ public:
// Construct local parts
part.triangleIndices = buffer_helpers::toVector<int>(mesh._mesh->getIndexBuffer(), "part.triangleIndices");
mesh.parts << part;
auto p = std::make_shared<MeshPart>(meshID, partID, (int)materialIDAtlas[part.materialID]);
auto p = std::make_shared<MeshPart>(meshID, partID, 0);
parts->push_back(p);
partID++;
}
@ -94,20 +66,18 @@ public:
glm::vec3 transformedVertex = glm::vec3(mesh.modelTransform * glm::vec4(vertex, 1.0f));
geometry.meshExtents.minimum = glm::min(geometry.meshExtents.minimum, transformedVertex);
geometry.meshExtents.maximum = glm::max(geometry.meshExtents.maximum, transformedVertex);
mesh.meshExtents.minimum = glm::min(mesh.meshExtents.minimum, transformedVertex);
mesh.meshExtents.maximum = glm::max(mesh.meshExtents.maximum, transformedVertex);
}
}
meshID++;
}
}
_meshes = meshes;
_meshParts = parts;
_animGraphOverrideUrl = originalGeometry ? originalGeometry->getAnimGraphOverrideUrl() : QUrl();
_loaded = true;
_fbxGeometry = fbxGeometry;
};
};
};

View file

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