mirror of
https://github.com/overte-org/overte.git
synced 2025-04-14 04:07:11 +02:00
interim checkin
This commit is contained in:
parent
a08770c816
commit
145a0df082
21 changed files with 1219 additions and 634 deletions
|
@ -600,7 +600,9 @@ public:
|
|||
QString error;
|
||||
|
||||
scriptable::ModelProviderPointer provider;
|
||||
if (auto entityInterface = getEntityModelProvider(static_cast<EntityItemID>(uuid))) {
|
||||
if (uuid.isNull()) {
|
||||
provider = nullptr;
|
||||
} else if (auto entityInterface = getEntityModelProvider(static_cast<EntityItemID>(uuid))) {
|
||||
provider = entityInterface;
|
||||
} else if (auto overlayInterface = getOverlayModelProvider(static_cast<OverlayID>(uuid))) {
|
||||
provider = overlayInterface;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
#include <Transform.h>
|
||||
#include <SpatiallyNestable.h>
|
||||
#include <graphics-scripting/ScriptableModel.h>
|
||||
#include <graphics-scripting/Forward.h>
|
||||
#include "Overlay.h"
|
||||
|
||||
namespace model { class Mesh; }
|
||||
|
@ -37,7 +37,7 @@ public:
|
|||
virtual bool is3D() const override { return true; }
|
||||
|
||||
virtual uint32_t fetchMetaSubItems(render::ItemIDs& subItems) const override { subItems.push_back(getRenderItemID()); return (uint32_t) subItems.size(); }
|
||||
virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override { return scriptable::ModelProvider::modelUnavailableError(ok); }
|
||||
virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override { return scriptable::ModelProvider::modelUnavailableError(ok); }
|
||||
|
||||
// TODO: consider implementing registration points in this class
|
||||
glm::vec3 getCenter() const { return getWorldPosition(); }
|
||||
|
|
|
@ -630,7 +630,7 @@ uint32_t ModelOverlay::fetchMetaSubItems(render::ItemIDs& subItems) const {
|
|||
return 0;
|
||||
}
|
||||
|
||||
scriptable::ScriptableModel ModelOverlay::getScriptableModel(bool* ok) {
|
||||
scriptable::ScriptableModelBase ModelOverlay::getScriptableModel(bool* ok) {
|
||||
if (!_model || !_model->isLoaded()) {
|
||||
return Base3DOverlay::getScriptableModel(ok);
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ public:
|
|||
void setDrawInFront(bool drawInFront) override;
|
||||
void setDrawHUDLayer(bool drawHUDLayer) override;
|
||||
|
||||
virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override;
|
||||
virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override;
|
||||
protected:
|
||||
Transform evalRenderTransform() override;
|
||||
|
||||
|
|
|
@ -180,15 +180,15 @@ Transform Shape3DOverlay::evalRenderTransform() {
|
|||
return transform;
|
||||
}
|
||||
|
||||
scriptable::ScriptableModel Shape3DOverlay::getScriptableModel(bool* ok) {
|
||||
scriptable::ScriptableModelBase Shape3DOverlay::getScriptableModel(bool* ok) {
|
||||
auto geometryCache = DependencyManager::get<GeometryCache>();
|
||||
auto vertexColor = ColorUtils::toVec3(_color);
|
||||
scriptable::ScriptableModel result;
|
||||
scriptable::ScriptableModelBase result;
|
||||
result.metadata = {
|
||||
{ "origin", "Shape3DOverlay::"+shapeStrings[_shape] },
|
||||
{ "overlayID", getID() },
|
||||
};
|
||||
result.meshes << geometryCache->meshFromShape(_shape, vertexColor);
|
||||
result.append(geometryCache->meshFromShape(_shape, vertexColor), {{ "shape", shapeStrings[_shape] }});
|
||||
if (ok) {
|
||||
*ok = true;
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ public:
|
|||
void setProperties(const QVariantMap& properties) override;
|
||||
QVariant getProperty(const QString& property) override;
|
||||
|
||||
virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override;
|
||||
virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override;
|
||||
protected:
|
||||
Transform evalRenderTransform() override;
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ include_hifi_library_headers(fbx)
|
|||
include_hifi_library_headers(entities)
|
||||
include_hifi_library_headers(avatars)
|
||||
include_hifi_library_headers(controllers)
|
||||
include_hifi_library_headers(graphics-scripting) # for ScriptableModel.h
|
||||
include_hifi_library_headers(graphics-scripting) # for Forward.h
|
||||
|
||||
target_bullet()
|
||||
target_polyvox()
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
#include <Sound.h>
|
||||
#include "AbstractViewStateInterface.h"
|
||||
#include "EntitiesRendererLogging.h"
|
||||
#include <graphics-scripting/ScriptableModel.h>
|
||||
#include <graphics-scripting/Forward.h>
|
||||
|
||||
class EntityTreeRenderer;
|
||||
|
||||
|
@ -55,7 +55,7 @@ public:
|
|||
|
||||
const uint64_t& getUpdateTime() const { return _updateTime; }
|
||||
|
||||
virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override { return scriptable::ModelProvider::modelUnavailableError(ok); }
|
||||
virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override { return scriptable::ModelProvider::modelUnavailableError(ok); }
|
||||
protected:
|
||||
virtual bool needsRenderUpdateFromEntity() const final { return needsRenderUpdateFromEntity(_entity); }
|
||||
virtual void onAddToScene(const EntityItemPointer& entity);
|
||||
|
|
|
@ -950,12 +950,9 @@ QStringList RenderableModelEntityItem::getJointNames() const {
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
scriptable::ScriptableModel render::entities::ModelEntityRenderer::getScriptableModel(bool* ok) {
|
||||
scriptable::ScriptableModelBase render::entities::ModelEntityRenderer::getScriptableModel(bool* ok) {
|
||||
ModelPointer model;
|
||||
withReadLock([&] {
|
||||
model = _model;
|
||||
});
|
||||
withReadLock([&] { model = _model; });
|
||||
|
||||
if (!model || !model->isLoaded()) {
|
||||
return scriptable::ModelProvider::modelUnavailableError(ok);
|
||||
|
@ -964,6 +961,18 @@ scriptable::ScriptableModel render::entities::ModelEntityRenderer::getScriptable
|
|||
return _model->getScriptableModel(ok);
|
||||
}
|
||||
|
||||
bool render::entities::ModelEntityRenderer::replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer newModel, int meshIndex, int partIndex) {
|
||||
qCDebug(entitiesrenderer) << "REPLACING RenderableModelEntityItem" << newModel->objectName();
|
||||
ModelPointer model;
|
||||
withReadLock([&] { model = _model; });
|
||||
|
||||
if (!model || !model->isLoaded()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _model->replaceScriptableModelMeshPart(newModel, meshIndex, partIndex);
|
||||
}
|
||||
|
||||
void RenderableModelEntityItem::simulateRelayedJoints() {
|
||||
ModelPointer model = getModel();
|
||||
if (model && model->isLoaded()) {
|
||||
|
|
|
@ -140,7 +140,8 @@ class ModelEntityRenderer : public TypedEntityRenderer<RenderableModelEntityItem
|
|||
|
||||
public:
|
||||
ModelEntityRenderer(const EntityItemPointer& entity);
|
||||
virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) override;
|
||||
virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) override;
|
||||
virtual bool replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer model, int meshIndex, int partIndex) override;
|
||||
|
||||
protected:
|
||||
virtual void removeFromScene(const ScenePointer& scene, Transaction& transaction) override;
|
||||
|
|
|
@ -7,23 +7,48 @@
|
|||
#include <gpu/Format.h>
|
||||
#include <gpu/Stream.h>
|
||||
|
||||
#include <graphics/Geometry.h>
|
||||
|
||||
#include <Extents.h>
|
||||
#include <AABox.h>
|
||||
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
#include <glm/gtc/packing.hpp>
|
||||
#include <glm/detail/type_vec.hpp>
|
||||
namespace glm {
|
||||
using hvec2 = glm::tvec2<glm::detail::hdata>;
|
||||
using hvec4 = glm::tvec4<glm::detail::hdata>;
|
||||
}
|
||||
|
||||
//#define DEBUG_BUFFERVIEW_SCRIPTING
|
||||
#ifdef DEBUG_BUFFERVIEW_SCRIPTING
|
||||
//#ifdef DEBUG_BUFFERVIEW_SCRIPTING
|
||||
#include "DebugNames.h"
|
||||
QLoggingCategory bufferview_helpers{"hifi.bufferview"};
|
||||
#endif
|
||||
//#endif
|
||||
|
||||
namespace {
|
||||
QLoggingCategory bufferhelper_logging{"hifi.bufferview"};
|
||||
const std::array<const char*, 4> XYZW = {{ "x", "y", "z", "w" }};
|
||||
const std::array<const char*, 4> ZERO123 = {{ "0", "1", "2", "3" }};
|
||||
}
|
||||
|
||||
gpu::BufferView buffer_helpers::getBufferView(graphics::MeshPointer mesh, gpu::Stream::Slot slot) {
|
||||
return slot == gpu::Stream::POSITION ? mesh->getVertexBuffer() : mesh->getAttributeBuffer(slot);
|
||||
}
|
||||
QMap<QString,int> buffer_helpers::ATTRIBUTES{
|
||||
{"position", gpu::Stream::POSITION },
|
||||
{"normal", gpu::Stream::NORMAL },
|
||||
{"color", gpu::Stream::COLOR },
|
||||
{"tangent", gpu::Stream::TEXCOORD0 },
|
||||
{"skin_cluster_index", gpu::Stream::SKIN_CLUSTER_INDEX },
|
||||
{"skin_cluster_weight", gpu::Stream::SKIN_CLUSTER_WEIGHT },
|
||||
{"texcoord0", gpu::Stream::TEXCOORD0 },
|
||||
{"texcoord1", gpu::Stream::TEXCOORD1 },
|
||||
{"texcoord2", gpu::Stream::TEXCOORD2 },
|
||||
{"texcoord3", gpu::Stream::TEXCOORD3 },
|
||||
{"texcoord4", gpu::Stream::TEXCOORD4 },
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
QVariant getBufferViewElement(const gpu::BufferView& view, quint32 index, bool asArray = false) {
|
||||
return glmVecToVariant(view.get<T>(index), asArray);
|
||||
|
@ -61,14 +86,14 @@ static void packNormalAndTangent(glm::vec3 normal, glm::vec3 tangent, glm::uint3
|
|||
packedTangent = tangentStruct.pack;
|
||||
}
|
||||
|
||||
bool bufferViewElementFromVariant(const gpu::BufferView& view, quint32 index, const QVariant& v) {
|
||||
bool buffer_helpers::fromVariant(const gpu::BufferView& view, quint32 index, const QVariant& v) {
|
||||
const auto& element = view._element;
|
||||
const auto vecN = element.getScalarCount();
|
||||
const auto dataType = element.getType();
|
||||
const auto byteLength = element.getSize();
|
||||
const auto BYTES_PER_ELEMENT = byteLength / vecN;
|
||||
#ifdef DEBUG_BUFFERVIEW_SCRIPTING
|
||||
qCDebug(bufferview_helpers) << "bufferViewElementFromVariant" << index << DebugNames::stringFrom(dataType) << BYTES_PER_ELEMENT << vecN;
|
||||
qCDebug(bufferhelper_logging) << "bufferViewElementFromVariant" << index << DebugNames::stringFrom(dataType) << BYTES_PER_ELEMENT << vecN;
|
||||
#endif
|
||||
if (BYTES_PER_ELEMENT == 1) {
|
||||
switch(vecN) {
|
||||
|
@ -122,16 +147,34 @@ bool bufferViewElementFromVariant(const gpu::BufferView& view, quint32 index, co
|
|||
return false;
|
||||
}
|
||||
|
||||
QVariant bufferViewElementToVariant(const gpu::BufferView& view, quint32 index, bool asArray, const char* hint) {
|
||||
bool boundsCheck(const gpu::BufferView& view, quint32 index) {
|
||||
const auto byteLength = view._element.getSize();
|
||||
return (
|
||||
index < view.getNumElements() &&
|
||||
index * byteLength < (view._size - 1) * byteLength
|
||||
);
|
||||
}
|
||||
|
||||
QVariant buffer_helpers::toVariant(const gpu::BufferView& view, quint32 index, bool asArray, const char* hint) {
|
||||
const auto& element = view._element;
|
||||
const auto vecN = element.getScalarCount();
|
||||
const auto dataType = element.getType();
|
||||
const auto byteLength = element.getSize();
|
||||
const auto BYTES_PER_ELEMENT = byteLength / vecN;
|
||||
Q_ASSERT(index < view.getNumElements());
|
||||
Q_ASSERT(index * vecN * BYTES_PER_ELEMENT < (view._size - vecN * BYTES_PER_ELEMENT));
|
||||
if (!boundsCheck(view, index)) {
|
||||
// sanity checks
|
||||
auto byteOffset = index * vecN * BYTES_PER_ELEMENT;
|
||||
auto maxByteOffset = (view._size - 1) * vecN * BYTES_PER_ELEMENT;
|
||||
if (byteOffset > maxByteOffset) {
|
||||
qDebug() << "bufferViewElementToVariant -- byteOffset out of range " << byteOffset << " < " << maxByteOffset << DebugNames::stringFrom(dataType);
|
||||
qDebug() << "bufferViewElementToVariant -- index: " << index << "numElements" << view.getNumElements();
|
||||
qDebug() << "bufferViewElementToVariant -- vecN: " << vecN << "byteLength" << byteLength << "BYTES_PER_ELEMENT" << BYTES_PER_ELEMENT;
|
||||
}
|
||||
Q_ASSERT(byteOffset <= maxByteOffset);
|
||||
}
|
||||
#ifdef DEBUG_BUFFERVIEW_SCRIPTING
|
||||
qCDebug(bufferview_helpers) << "bufferViewElementToVariant" << index << DebugNames::stringFrom(dataType) << BYTES_PER_ELEMENT << vecN;
|
||||
qCDebug(bufferhelper_logging) << "bufferViewElementToVariant" << index << DebugNames::stringFrom(dataType) << BYTES_PER_ELEMENT << vecN;
|
||||
#endif
|
||||
if (BYTES_PER_ELEMENT == 1) {
|
||||
switch(vecN) {
|
||||
|
@ -221,22 +264,137 @@ const T glmVecFromVariant(const QVariant& v) {
|
|||
}
|
||||
|
||||
template <typename T>
|
||||
gpu::BufferView bufferViewFromVector(QVector<T> elements, gpu::Element elementType) {
|
||||
gpu::BufferView buffer_helpers::fromVector(const QVector<T>& elements, const 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 };
|
||||
}
|
||||
template<> gpu::BufferView buffer_helpers::fromVector<unsigned int>(const QVector<unsigned int>& elements, const gpu::Element& elementType) { return fromVector(elements, elementType); }
|
||||
template<> gpu::BufferView buffer_helpers::fromVector<glm::vec3>(const QVector<glm::vec3>& elements, const gpu::Element& elementType) { return fromVector(elements, elementType); }
|
||||
|
||||
template<> gpu::BufferView bufferViewFromVector<unsigned int>(QVector<unsigned int> elements, gpu::Element elementType) { return bufferViewFromVector(elements, elementType); }
|
||||
template<> gpu::BufferView bufferViewFromVector<glm::vec3>(QVector<glm::vec3> elements, gpu::Element elementType) { return bufferViewFromVector(elements, elementType); }
|
||||
template <typename T> struct getVec4;// { static T get(const gpu::BufferView& view, quint32 index, const char *hint); };
|
||||
template <typename T> struct getScalar;// { static T get(const gpu::BufferView& view, quint32 index, const char *hint); };
|
||||
|
||||
gpu::BufferView cloneBufferView(const gpu::BufferView& input) {
|
||||
struct gotter {
|
||||
static float error(const QString& name, const gpu::BufferView& view, quint32 index, const char *hint) {
|
||||
qDebug() << QString("gotter:: unhandled type=%1(element=%2(%3)) size=%4(per=%5) vec%6 hint=%7 #%8")
|
||||
.arg(name)
|
||||
.arg(DebugNames::stringFrom(view._element.getType()))
|
||||
.arg(view._element.getType())
|
||||
.arg(view._element.getSize())
|
||||
.arg(view._element.getSize() / view._element.getScalarCount())
|
||||
.arg(view._element.getScalarCount())
|
||||
.arg(hint)
|
||||
.arg(view.getNumElements());
|
||||
Q_ASSERT(false);
|
||||
assert(false);
|
||||
return NAN;
|
||||
}
|
||||
};
|
||||
template <typename T> struct getScalar : gotter {
|
||||
static T get(const gpu::BufferView& view, quint32 index, const char *hint) { switch(view._element.getType()) {
|
||||
case gpu::UINT32: return view.get<glm::uint32>(index);
|
||||
case gpu::UINT16: return view.get<glm::uint16>(index);
|
||||
case gpu::UINT8: return view.get<glm::uint8>(index);
|
||||
case gpu::INT32: return view.get<glm::int32>(index);
|
||||
case gpu::INT16: return view.get<glm::int16>(index);
|
||||
case gpu::INT8: return view.get<glm::int8>(index);
|
||||
case gpu::FLOAT: return view.get<glm::float32>(index);
|
||||
case gpu::HALF: return T(glm::unpackSnorm1x8(view.get<glm::int8>(index)));
|
||||
default: break;
|
||||
} return T(error("getScalar", view, index, hint));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct getVec2 : gotter { static T get(const gpu::BufferView& view, quint32 index, const char *hint) { switch(view._element.getType()) {
|
||||
case gpu::UINT32: return view.get<glm::u32vec2>(index);
|
||||
case gpu::UINT16: return view.get<glm::u16vec2>(index);
|
||||
case gpu::UINT8: return view.get<glm::u8vec2>(index);
|
||||
case gpu::INT32: return view.get<glm::i32vec2>(index);
|
||||
case gpu::INT16: return view.get<glm::i16vec2>(index);
|
||||
case gpu::INT8: return view.get<glm::i8vec2>(index);
|
||||
case gpu::FLOAT: return view.get<glm::fvec2>(index);
|
||||
case gpu::HALF: return glm::unpackSnorm2x8(view.get<glm::int16>(index));
|
||||
default: break;
|
||||
} return T(error("getVec2", view, index, hint)); }};
|
||||
|
||||
|
||||
template <typename T> struct getVec3 : gotter { static T get(const gpu::BufferView& view, quint32 index, const char *hint) { switch(view._element.getType()) {
|
||||
case gpu::UINT32: return view.get<glm::u32vec3>(index);
|
||||
case gpu::UINT16: return view.get<glm::u16vec3>(index);
|
||||
case gpu::UINT8: return view.get<glm::u8vec3>(index);
|
||||
case gpu::INT32: return view.get<glm::i32vec3>(index);
|
||||
case gpu::INT16: return view.get<glm::i16vec3>(index);
|
||||
case gpu::INT8: return view.get<glm::i8vec3>(index);
|
||||
case gpu::FLOAT: return view.get<glm::fvec3>(index);
|
||||
case gpu::HALF:
|
||||
case gpu::NUINT8:
|
||||
case gpu::NINT2_10_10_10:
|
||||
if (view._element.getSize() == sizeof(glm::int32)) {
|
||||
return getVec4<T>::get(view, index, hint);
|
||||
}
|
||||
default: break;
|
||||
} return T(error("getVec3", view, index, hint)); }};
|
||||
|
||||
template <typename T> struct getVec4 : gotter { static T get(const gpu::BufferView& view, quint32 index, const char *hint) {
|
||||
assert(view._element.getSize() == sizeof(glm::int32));
|
||||
switch(view._element.getType()) {
|
||||
case gpu::UINT32: return view.get<glm::u32vec4>(index);
|
||||
case gpu::UINT16: return view.get<glm::u16vec4>(index);
|
||||
case gpu::UINT8: return view.get<glm::u8vec4>(index);
|
||||
case gpu::INT32: return view.get<glm::i32vec4>(index);
|
||||
case gpu::INT16: return view.get<glm::i16vec4>(index);
|
||||
case gpu::INT8: return view.get<glm::i8vec4>(index);
|
||||
case gpu::NUINT32: break;
|
||||
case gpu::NUINT16: break;
|
||||
case gpu::NUINT8: return glm::unpackUnorm4x8(view.get<glm::uint32>(index));
|
||||
case gpu::NUINT2: break;
|
||||
case gpu::NINT32: break;
|
||||
case gpu::NINT16: break;
|
||||
case gpu::NINT8: break;
|
||||
case gpu::COMPRESSED: break;
|
||||
case gpu::NUM_TYPES: break;
|
||||
case gpu::FLOAT: return view.get<glm::fvec4>(index);
|
||||
case gpu::HALF: return glm::unpackSnorm4x8(view.get<glm::int32>(index));
|
||||
case gpu::NINT2_10_10_10: return glm::unpackSnorm3x10_1x2(view.get<glm::uint32>(index));
|
||||
} return T(error("getVec4", view, index, hint)); }};
|
||||
|
||||
|
||||
template <typename FUNC, typename T>
|
||||
struct getVec {
|
||||
static QVector<T> __to_vector__(const gpu::BufferView& view, const char *hint) {
|
||||
QVector<T> result;
|
||||
const quint32 count = (quint32)view.getNumElements();
|
||||
result.resize(count);
|
||||
for (quint32 i = 0; i < count; i++) {
|
||||
result[i] = FUNC::get(view, i, hint);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static T __to_scalar__(const gpu::BufferView& view, quint32 index, const char *hint) {
|
||||
assert(boundsCheck(view, index));
|
||||
return FUNC::get(view, index, hint);
|
||||
}
|
||||
};
|
||||
|
||||
template <> QVector<int> buffer_helpers::toVector<int>(const gpu::BufferView& view, const char *hint) { return getVec<getScalar<int>,int>::__to_vector__(view, hint); }
|
||||
template <> QVector<glm::vec2> buffer_helpers::toVector<glm::vec2>(const gpu::BufferView& view, const char *hint) { return getVec<getVec2<glm::vec2>,glm::vec2>::__to_vector__(view, hint); }
|
||||
template <> QVector<glm::vec3> buffer_helpers::toVector<glm::vec3>(const gpu::BufferView& view, const char *hint) { return getVec<getVec3<glm::vec3>,glm::vec3>::__to_vector__(view, hint); }
|
||||
template <> QVector<glm::vec4> buffer_helpers::toVector<glm::vec4>(const gpu::BufferView& view, const char *hint) { return getVec<getVec4<glm::vec4>,glm::vec4>::__to_vector__(view, hint); }
|
||||
|
||||
|
||||
template <> int buffer_helpers::convert<int>(const gpu::BufferView& view, quint32 index, const char *hint) { return getVec<getScalar<int>,int>::__to_scalar__(view, index, hint); }
|
||||
template <> glm::vec2 buffer_helpers::convert<glm::vec2>(const gpu::BufferView& view, quint32 index, const char *hint) { return getVec<getVec2<glm::vec2>,glm::vec2>::__to_scalar__(view, index, hint); }
|
||||
template <> glm::vec3 buffer_helpers::convert<glm::vec3>(const gpu::BufferView& view, quint32 index, const char *hint) { return getVec<getVec3<glm::vec3>,glm::vec3>::__to_scalar__(view, index, hint); }
|
||||
template <> glm::vec4 buffer_helpers::convert<glm::vec4>(const gpu::BufferView& view, quint32 index, const char *hint) { return getVec<getVec4<glm::vec4>,glm::vec4>::__to_scalar__(view, index, hint); }
|
||||
|
||||
gpu::BufferView buffer_helpers::clone(const gpu::BufferView& input) {
|
||||
return gpu::BufferView(
|
||||
std::make_shared<gpu::Buffer>(input._buffer->getSize(), input._buffer->getData()),
|
||||
input._offset, input._size, input._stride, input._element
|
||||
);
|
||||
}
|
||||
|
||||
gpu::BufferView resizedBufferView(const gpu::BufferView& input, quint32 numElements) {
|
||||
gpu::BufferView buffer_helpers::resize(const gpu::BufferView& input, quint32 numElements) {
|
||||
auto effectiveSize = input._buffer->getSize() / input.getNumElements();
|
||||
qDebug() << "resize input" << input.getNumElements() << input._buffer->getSize() << "effectiveSize" << effectiveSize;
|
||||
auto vsize = input._element.getSize() * numElements;
|
||||
|
@ -248,3 +406,235 @@ gpu::BufferView resizedBufferView(const gpu::BufferView& input, quint32 numEleme
|
|||
qDebug() << "resized output" << output.getNumElements() << output._buffer->getSize();
|
||||
return output;
|
||||
}
|
||||
|
||||
graphics::MeshPointer buffer_helpers::cloneMesh(graphics::MeshPointer mesh) {
|
||||
auto clone = std::make_shared<graphics::Mesh>();
|
||||
//[](graphics::Mesh* blah) {
|
||||
//qCDebug(bufferhelper_logging) << "--- DELETING MESH POINTER" << blah;
|
||||
// delete blah;
|
||||
//});
|
||||
clone->displayName = (QString::fromStdString(mesh->displayName) + "-clone").toStdString();
|
||||
//qCInfo(bufferhelper_logging) << "+++ ALLOCATED MESH POINTER ScriptableMesh::cloneMesh" << clone->displayName << clone.get() << !!mesh;
|
||||
clone->setIndexBuffer(buffer_helpers::clone(mesh->getIndexBuffer()));
|
||||
clone->setPartBuffer(buffer_helpers::clone(mesh->getPartBuffer()));
|
||||
auto attributeViews = buffer_helpers::gatherBufferViews(mesh);
|
||||
for (const auto& a : attributeViews) {
|
||||
auto& view = a.second;
|
||||
auto slot = buffer_helpers::ATTRIBUTES[a.first];
|
||||
auto points = buffer_helpers::clone(view);
|
||||
if (slot == gpu::Stream::POSITION) {
|
||||
clone->setVertexBuffer(points);
|
||||
} else {
|
||||
clone->addAttribute(slot, points);
|
||||
}
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
|
||||
/// --- buffer view <-> variant helpers
|
||||
|
||||
namespace {
|
||||
// expand the corresponding attribute buffer (creating it if needed) so that it matches POSITIONS size and specified element type
|
||||
gpu::BufferView _expandedAttributeBuffer(const graphics::MeshPointer mesh, gpu::Stream::Slot slot) {
|
||||
gpu::BufferView bufferView = buffer_helpers::getBufferView(mesh, slot);
|
||||
const auto& elementType = bufferView._element;
|
||||
//auto vecN = element.getScalarCount();
|
||||
//auto type = element.getType();
|
||||
//gpu::Element elementType = getVecNElement(type, vecN);
|
||||
|
||||
gpu::Size elementSize = elementType.getSize();
|
||||
auto nPositions = mesh->getNumVertices();
|
||||
auto vsize = nPositions * elementSize;
|
||||
auto diffTypes = (elementType.getType() != bufferView._element.getType() ||
|
||||
elementType.getSize() > bufferView._element.getSize() ||
|
||||
elementType.getScalarCount() > bufferView._element.getScalarCount() ||
|
||||
vsize > bufferView._size
|
||||
);
|
||||
QString hint = QString("%1").arg(slot);
|
||||
#ifdef DEBUG_BUFFERVIEW_SCRIPTING
|
||||
hint = DebugNames::stringFrom(slot);
|
||||
#endif
|
||||
#ifdef DEV_BUILD
|
||||
auto beforeCount = bufferView.getNumElements();
|
||||
auto beforeTotal = bufferView._size;
|
||||
#endif
|
||||
if (bufferView.getNumElements() < nPositions || diffTypes) {
|
||||
if (!bufferView._buffer || bufferView.getNumElements() == 0) {
|
||||
qCInfo(bufferhelper_logging).nospace() << "ScriptableMesh -- adding missing mesh attribute '" << hint << "' for BufferView";
|
||||
gpu::Byte *data = new gpu::Byte[vsize];
|
||||
memset(data, 0, vsize);
|
||||
auto buffer = new gpu::Buffer(vsize, (gpu::Byte*)data);
|
||||
delete[] data;
|
||||
bufferView = gpu::BufferView(buffer, elementType);
|
||||
mesh->addAttribute(slot, bufferView);
|
||||
} else {
|
||||
qCInfo(bufferhelper_logging) << "ScriptableMesh -- resizing Buffer current:" << hint << bufferView._buffer->getSize() << "wanted:" << vsize;
|
||||
bufferView._element = elementType;
|
||||
bufferView._buffer->resize(vsize);
|
||||
bufferView._size = bufferView._buffer->getSize();
|
||||
}
|
||||
}
|
||||
#ifdef DEV_BUILD
|
||||
auto afterCount = bufferView.getNumElements();
|
||||
auto afterTotal = bufferView._size;
|
||||
if (beforeTotal != afterTotal || beforeCount != afterCount) {
|
||||
QString typeName = QString("%1").arg(bufferView._element.getType());
|
||||
#ifdef DEBUG_BUFFERVIEW_SCRIPTING
|
||||
typeName = DebugNames::stringFrom(bufferView._element.getType());
|
||||
#endif
|
||||
qCDebug(bufferhelper_logging, "NOTE:: _expandedAttributeBuffer.%s vec%d %s (before count=%lu bytes=%lu // after count=%lu bytes=%lu)",
|
||||
hint.toStdString().c_str(), bufferView._element.getScalarCount(),
|
||||
typeName.toStdString().c_str(), beforeCount, beforeTotal, afterCount, afterTotal);
|
||||
}
|
||||
#endif
|
||||
return bufferView;
|
||||
}
|
||||
|
||||
gpu::BufferView expandAttributeToMatchPositions(graphics::MeshPointer mesh, gpu::Stream::Slot slot) {
|
||||
if (slot == gpu::Stream::POSITION) {
|
||||
return buffer_helpers::getBufferView(mesh, slot);
|
||||
}
|
||||
return _expandedAttributeBuffer(mesh, slot);
|
||||
}
|
||||
}
|
||||
|
||||
std::map<QString, gpu::BufferView> buffer_helpers::gatherBufferViews(graphics::MeshPointer mesh, const QStringList& expandToMatchPositions) {
|
||||
std::map<QString, gpu::BufferView> attributeViews;
|
||||
if (!mesh) {
|
||||
return attributeViews;
|
||||
}
|
||||
for (const auto& a : buffer_helpers::ATTRIBUTES.toStdMap()) {
|
||||
auto name = a.first;
|
||||
auto slot = a.second;
|
||||
auto view = getBufferView(mesh, slot);
|
||||
auto beforeCount = view.getNumElements();
|
||||
auto beforeTotal = view._size;
|
||||
if (expandToMatchPositions.contains(name)) {
|
||||
expandAttributeToMatchPositions(mesh, slot);
|
||||
}
|
||||
if (beforeCount > 0) {
|
||||
auto element = view._element;
|
||||
auto vecN = element.getScalarCount();
|
||||
//auto type = element.getType();
|
||||
QString typeName = QString("%1").arg(element.getType());
|
||||
#ifdef DEBUG_BUFFERVIEW_SCRIPTING
|
||||
typeName = DebugNames::stringFrom(element.getType());
|
||||
#endif
|
||||
|
||||
attributeViews[name] = getBufferView(mesh, slot);
|
||||
|
||||
#if DEV_BUILD
|
||||
auto afterTotal = attributeViews[name]._size;
|
||||
auto afterCount = attributeViews[name].getNumElements();
|
||||
if (beforeTotal != afterTotal || beforeCount != afterCount) {
|
||||
qCDebug(bufferhelper_logging, "NOTE:: gatherBufferViews.%s vec%d %s (before count=%lu bytes=%lu // after count=%lu bytes=%lu)",
|
||||
name.toStdString().c_str(), vecN, typeName.toStdString().c_str(), beforeCount, beforeTotal, afterCount, afterTotal);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return attributeViews;
|
||||
}
|
||||
|
||||
|
||||
bool buffer_helpers::recalculateNormals(graphics::MeshPointer mesh) {
|
||||
qCInfo(bufferhelper_logging) << "Recalculating normals" << !!mesh;
|
||||
if (!mesh) {
|
||||
return false;
|
||||
}
|
||||
buffer_helpers::gatherBufferViews(mesh, { "normal", "color" }); // ensures #normals >= #positions
|
||||
auto normals = mesh->getAttributeBuffer(gpu::Stream::NORMAL);
|
||||
auto verts = mesh->getVertexBuffer();
|
||||
auto indices = mesh->getIndexBuffer();
|
||||
auto esize = indices._element.getSize();
|
||||
auto numPoints = indices.getNumElements();
|
||||
const auto TRIANGLE = 3;
|
||||
quint32 numFaces = (quint32)numPoints / TRIANGLE;
|
||||
//QVector<Triangle> faces;
|
||||
QVector<glm::vec3> faceNormals;
|
||||
QMap<QString,QVector<quint32>> vertexToFaces;
|
||||
//faces.resize(numFaces);
|
||||
faceNormals.resize(numFaces);
|
||||
auto numNormals = normals.getNumElements();
|
||||
qCInfo(bufferhelper_logging) << QString("numFaces: %1, numNormals: %2, numPoints: %3").arg(numFaces).arg(numNormals).arg(numPoints);
|
||||
if (normals.getNumElements() != verts.getNumElements()) {
|
||||
return false;
|
||||
}
|
||||
for (quint32 i = 0; i < numFaces; i++) {
|
||||
quint32 I = TRIANGLE * i;
|
||||
quint32 i0 = esize == 4 ? indices.get<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(bufferhelper_logging) << i << i0 << i1 << i2 << glmVecToVariant(face.v0) << glmVecToVariant(face.v1) << glmVecToVariant(face.v2);
|
||||
break;
|
||||
}
|
||||
vertexToFaces[glm::to_string(face.v0).c_str()] << i;
|
||||
vertexToFaces[glm::to_string(face.v1).c_str()] << i;
|
||||
vertexToFaces[glm::to_string(face.v2).c_str()] << i;
|
||||
}
|
||||
for (quint32 j = 0; j < numNormals; j++) {
|
||||
//auto v = verts.get<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(bufferhelper_logging) << "no faces for key!?" << key;
|
||||
}
|
||||
normal = verts.get<glm::vec3>(j);
|
||||
}
|
||||
if (glm::isnan(normal.x)) {
|
||||
static int logged = 0;
|
||||
if (logged++ < 10) {
|
||||
qCInfo(bufferhelper_logging) << "isnan(normal.x)" << j << glmVecToVariant(normal);
|
||||
}
|
||||
break;
|
||||
}
|
||||
normals.edit<glm::vec3>(j) = glm::normalize(normal);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariant buffer_helpers::toVariant(const glm::mat4& mat4) {
|
||||
QVector<float> floats;
|
||||
floats.resize(16);
|
||||
memcpy(floats.data(), &mat4, sizeof(glm::mat4));
|
||||
QVariant v;
|
||||
v.setValue<QVector<float>>(floats);
|
||||
return v;
|
||||
};
|
||||
|
||||
QVariant buffer_helpers::toVariant(const Extents& box) {
|
||||
return QVariantMap{
|
||||
{ "center", glmVecToVariant(box.minimum + (box.size() / 2.0f)) },
|
||||
{ "minimum", glmVecToVariant(box.minimum) },
|
||||
{ "maximum", glmVecToVariant(box.maximum) },
|
||||
{ "dimensions", glmVecToVariant(box.size()) },
|
||||
};
|
||||
}
|
||||
|
||||
QVariant buffer_helpers::toVariant(const AABox& box) {
|
||||
return QVariantMap{
|
||||
{ "brn", glmVecToVariant(box.getCorner()) },
|
||||
{ "tfl", glmVecToVariant(box.calcTopFarLeft()) },
|
||||
{ "center", glmVecToVariant(box.calcCenter()) },
|
||||
{ "minimum", glmVecToVariant(box.getMinimumPoint()) },
|
||||
{ "maximum", glmVecToVariant(box.getMaximumPoint()) },
|
||||
{ "dimensions", glmVecToVariant(box.getDimensions()) },
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtCore>
|
||||
#include <memory>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
namespace gpu {
|
||||
class BufferView;
|
||||
|
@ -15,11 +17,34 @@ namespace gpu {
|
|||
|
||||
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);
|
||||
|
||||
template <typename T> gpu::BufferView bufferViewFromVector(QVector<T> elements, gpu::Element elementType);
|
||||
namespace graphics {
|
||||
class Mesh;
|
||||
using MeshPointer = std::shared_ptr<Mesh>;
|
||||
}
|
||||
|
||||
gpu::BufferView cloneBufferView(const gpu::BufferView& input);
|
||||
gpu::BufferView resizedBufferView(const gpu::BufferView& input, quint32 numElements);
|
||||
class Extents;
|
||||
class AABox;
|
||||
|
||||
struct buffer_helpers {
|
||||
static graphics::MeshPointer cloneMesh(graphics::MeshPointer mesh);
|
||||
static QMap<QString,int> ATTRIBUTES;
|
||||
static std::map<QString, gpu::BufferView> gatherBufferViews(graphics::MeshPointer mesh, const QStringList& expandToMatchPositions = QStringList());
|
||||
static bool recalculateNormals(graphics::MeshPointer meshProxy);
|
||||
static gpu::BufferView getBufferView(graphics::MeshPointer mesh, quint8 slot);
|
||||
|
||||
static QVariant toVariant(const Extents& box);
|
||||
static QVariant toVariant(const AABox& box);
|
||||
static QVariant toVariant(const glm::mat4& mat4);
|
||||
static QVariant toVariant(const gpu::BufferView& view, quint32 index, bool asArray = false, const char* hint = "");
|
||||
|
||||
static bool fromVariant(const gpu::BufferView& view, quint32 index, const QVariant& v);
|
||||
|
||||
template <typename T> static gpu::BufferView fromVector(const QVector<T>& elements, const gpu::Element& elementType);
|
||||
|
||||
template <typename T> static QVector<T> toVector(const gpu::BufferView& view, const char *hint = "");
|
||||
template <typename T> static T convert(const gpu::BufferView& view, quint32 index, const char* hint = "");
|
||||
|
||||
static gpu::BufferView clone(const gpu::BufferView& input);
|
||||
static gpu::BufferView resize(const gpu::BufferView& input, quint32 numElements);
|
||||
};
|
||||
|
|
|
@ -26,7 +26,7 @@ QScriptValue getBufferViewElement(QScriptEngine* js, const gpu::BufferView& view
|
|||
}
|
||||
|
||||
QScriptValue bufferViewElementToScriptValue(QScriptEngine* engine, const gpu::BufferView& view, quint32 index, bool asArray, const char* hint) {
|
||||
QVariant result = bufferViewElementToVariant(view, index, asArray, hint);
|
||||
QVariant result = buffer_helpers::toVariant(view, index, asArray, hint);
|
||||
if (!result.isValid()) {
|
||||
return QScriptValue::NullValue;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ void setBufferViewElement(const gpu::BufferView& view, quint32 index, const QScr
|
|||
}
|
||||
|
||||
bool bufferViewElementFromScriptValue(const QScriptValue& v, const gpu::BufferView& view, quint32 index) {
|
||||
return bufferViewElementFromVariant(view, index, v.toVariant());
|
||||
return buffer_helpers::fromVariant(view, index, v.toVariant());
|
||||
}
|
||||
|
||||
//
|
||||
|
|
107
libraries/graphics-scripting/src/graphics-scripting/Forward.h
Normal file
107
libraries/graphics-scripting/src/graphics-scripting/Forward.h
Normal file
|
@ -0,0 +1,107 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtCore/QUuid>
|
||||
#include <QPointer>
|
||||
#include <memory>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
namespace graphics {
|
||||
class Mesh;
|
||||
}
|
||||
namespace gpu {
|
||||
class BufferView;
|
||||
}
|
||||
class QScriptEngine;
|
||||
|
||||
namespace scriptable {
|
||||
using Mesh = graphics::Mesh;
|
||||
using MeshPointer = std::shared_ptr<scriptable::Mesh>;
|
||||
using WeakMeshPointer = std::weak_ptr<scriptable::Mesh>;
|
||||
|
||||
class ScriptableModelBase;
|
||||
using ScriptableModelBasePointer = QPointer<ScriptableModelBase>;
|
||||
|
||||
class ModelProvider;
|
||||
using ModelProviderPointer = std::shared_ptr<scriptable::ModelProvider>;
|
||||
using WeakModelProviderPointer = std::weak_ptr<scriptable::ModelProvider>;
|
||||
|
||||
class ScriptableMeshBase : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
WeakModelProviderPointer provider;
|
||||
ScriptableModelBasePointer model;
|
||||
WeakMeshPointer mesh;
|
||||
MeshPointer ownedMesh;
|
||||
QVariantMap metadata;
|
||||
ScriptableMeshBase(WeakModelProviderPointer provider, ScriptableModelBasePointer model, WeakMeshPointer mesh, const QVariantMap& metadata);
|
||||
ScriptableMeshBase(WeakMeshPointer mesh = WeakMeshPointer());
|
||||
ScriptableMeshBase(MeshPointer mesh, const QVariantMap& metadata);
|
||||
ScriptableMeshBase(const ScriptableMeshBase& other) { *this = other; }
|
||||
ScriptableMeshBase& operator=(const ScriptableMeshBase& view);
|
||||
virtual ~ScriptableMeshBase();
|
||||
Q_INVOKABLE const scriptable::MeshPointer getMeshPointer() const { return mesh.lock(); }
|
||||
Q_INVOKABLE const scriptable::ModelProviderPointer getModelProviderPointer() const { return provider.lock(); }
|
||||
Q_INVOKABLE const scriptable::ScriptableModelBasePointer getModelBasePointer() const { return model; }
|
||||
};
|
||||
|
||||
// abstract container for holding one or more references to mesh pointers
|
||||
class ScriptableModelBase : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
WeakModelProviderPointer provider;
|
||||
QUuid objectID; // spatially nestable ID
|
||||
QVariantMap metadata;
|
||||
QVector<scriptable::ScriptableMeshBase> meshes;
|
||||
|
||||
ScriptableModelBase(QObject* parent = nullptr) : QObject(parent) {}
|
||||
ScriptableModelBase(const ScriptableModelBase& other) { *this = other; }
|
||||
ScriptableModelBase& operator=(const ScriptableModelBase& other) {
|
||||
provider = other.provider;
|
||||
objectID = other.objectID;
|
||||
metadata = other.metadata;
|
||||
for (auto& mesh : other.meshes) {
|
||||
append(mesh);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
virtual ~ScriptableModelBase();
|
||||
|
||||
void mixin(const QVariantMap& other);
|
||||
void append(const ScriptableModelBase& other, const QVariantMap& modelMetadata = QVariantMap());
|
||||
void append(scriptable::WeakMeshPointer mesh, const QVariantMap& metadata = QVariantMap());
|
||||
void append(const ScriptableMeshBase& mesh, const QVariantMap& metadata = QVariantMap());
|
||||
// TODO: in future containers for these could go here
|
||||
// QVariantMap shapes;
|
||||
// QVariantMap materials;
|
||||
// QVariantMap armature;
|
||||
};
|
||||
|
||||
// mixin class for Avatar/Entity/Overlay Rendering that expose their in-memory graphics::Meshes
|
||||
class ModelProvider {
|
||||
public:
|
||||
QVariantMap metadata{ { "providerType", "unknown" } };
|
||||
static scriptable::ScriptableModelBase modelUnavailableError(bool* ok) { if (ok) { *ok = false; } return {}; }
|
||||
virtual scriptable::ScriptableModelBase getScriptableModel(bool* ok = nullptr) = 0;
|
||||
|
||||
virtual bool replaceScriptableModelMeshPart(scriptable::ScriptableModelBasePointer model, int meshIndex, int partIndex) { return false; }
|
||||
};
|
||||
|
||||
// mixin class for resolving UUIDs into a corresponding ModelProvider
|
||||
class ModelProviderFactory : public Dependency {
|
||||
public:
|
||||
virtual scriptable::ModelProviderPointer lookupModelProvider(QUuid uuid) = 0;
|
||||
};
|
||||
|
||||
using uint32 = quint32;
|
||||
class ScriptableModel;
|
||||
using ScriptableModelPointer = QPointer<ScriptableModel>;
|
||||
class ScriptableMesh;
|
||||
using ScriptableMeshPointer = QPointer<ScriptableMesh>;
|
||||
class ScriptableMeshPart;
|
||||
using ScriptableMeshPartPointer = QPointer<ScriptableMeshPart>;
|
||||
bool registerMetaTypes(QScriptEngine* engine);
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
#include "GraphicsScriptingUtil.h"
|
||||
|
||||
Q_LOGGING_CATEGORY(graphics_scripting, "hifi.scripting.graphics")
|
|
@ -0,0 +1,85 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtScript/QScriptEngine>
|
||||
#include <QtScript/QScriptValue>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QLoggingCategory>
|
||||
#include <QDebug>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
Q_DECLARE_LOGGING_CATEGORY(graphics_scripting)
|
||||
|
||||
namespace scriptable {
|
||||
// derive current context's C++ QObject (based on current JS "this" value)
|
||||
template <typename T> T this_qobject_cast(QScriptEngine* engine) {
|
||||
auto context = engine ? engine->currentContext() : nullptr;
|
||||
return qscriptvalue_cast<T>(context ? context->thisObject() : QScriptValue::NullValue);
|
||||
}
|
||||
// JS => QPointer<QObject>
|
||||
template <typename T> QPointer<T> qpointer_qobject_cast(const QScriptValue& value) {
|
||||
auto obj = value.toQObject();
|
||||
qCInfo(graphics_scripting) << "qpointer_qobject_cast" << obj << value.toString();
|
||||
if (auto tmp = qobject_cast<T*>(obj)) {
|
||||
return QPointer<T>(tmp);
|
||||
}
|
||||
if (auto tmp = static_cast<T*>(obj)) {
|
||||
return QPointer<T>(tmp);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
inline QString toDebugString(QObject* tmp) {
|
||||
return QString("%0 (0x%1%2)")
|
||||
.arg(tmp ? tmp->metaObject()->className() : "QObject")
|
||||
.arg(qulonglong(tmp), 16, 16, QChar('0'))
|
||||
.arg(tmp && tmp->objectName().size() ? " name=" + tmp->objectName() : "");
|
||||
}
|
||||
template <typename T> QString toDebugString(std::shared_ptr<T> tmp) {
|
||||
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...);
|
||||
qCInfo(graphics_scripting) << "scriptable::make_qtowned" << toDebugString(tmp);
|
||||
QString debug = toDebugString(tmp);
|
||||
if (tmp) {
|
||||
tmp->metadata["__ownership__"] = QScriptEngine::QtOwnership;
|
||||
QObject::connect(tmp, &QObject::destroyed, [=]() { qCInfo(graphics_scripting) << "-------- ~scriptable::make_qtowned" << debug; });
|
||||
auto ptr = std::shared_ptr<T>(tmp, [debug](T* tmp) {
|
||||
//qDebug() << "~std::shared_ptr<T>" << debug;
|
||||
delete tmp;
|
||||
});
|
||||
return ptr;
|
||||
} else {
|
||||
return std::shared_ptr<T>(tmp);
|
||||
}
|
||||
}
|
||||
// C++ > ScriptOwned JS instance
|
||||
template <typename T, class... Rest> QPointer<T> make_scriptowned(Rest... rest) {
|
||||
T* tmp = new T(rest...);
|
||||
qCInfo(graphics_scripting) << "scriptable::make_scriptowned" << toDebugString(tmp);
|
||||
if (tmp) {
|
||||
tmp->metadata["__ownership__"] = QScriptEngine::ScriptOwnership;
|
||||
//auto blah = (DeleterFunction)[](void* delme) { };
|
||||
return add_scriptowned_destructor(tmp);
|
||||
} else {
|
||||
return QPointer<T>(tmp);
|
||||
}
|
||||
}
|
||||
// C++ > ScriptOwned JS instance
|
||||
template <typename T> QPointer<T> add_scriptowned_destructor(T* tmp) {
|
||||
QString debug = toDebugString(tmp);
|
||||
if (tmp) {
|
||||
QObject::connect(tmp, &QObject::destroyed, [=]() {
|
||||
qCInfo(graphics_scripting) << "-------- ~scriptable::make_scriptowned" << debug;// << !!customDeleter;
|
||||
//if (customDeleter) {
|
||||
// customDeleter(tmp);
|
||||
//}
|
||||
});
|
||||
} else {
|
||||
qCInfo(graphics_scripting) << "add_scriptowned_destructor -- not connecting to null value" << debug;
|
||||
}
|
||||
return QPointer<T>(tmp);
|
||||
}
|
||||
}
|
|
@ -26,12 +26,11 @@
|
|||
|
||||
#include "BufferViewScripting.h"
|
||||
#include "ScriptableMesh.h"
|
||||
#include "GraphicsScriptingUtil.h"
|
||||
|
||||
#include "ModelScriptingInterface.moc"
|
||||
|
||||
namespace {
|
||||
QLoggingCategory model_scripting { "hifi.model.scripting" };
|
||||
}
|
||||
#include "RegisteredMetaTypes.h"
|
||||
|
||||
ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(parent) {
|
||||
if (auto scriptEngine = qobject_cast<QScriptEngine*>(parent)) {
|
||||
|
@ -39,8 +38,51 @@ ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(pare
|
|||
}
|
||||
}
|
||||
|
||||
void ModelScriptingInterface::getMeshes(QUuid uuid, QScriptValue scopeOrCallback, QScriptValue methodOrName) {
|
||||
auto handler = makeScopedHandlerObject(scopeOrCallback, methodOrName);
|
||||
bool ModelScriptingInterface::updateMeshes(QUuid uuid, const scriptable::ScriptableMeshPointer mesh, int meshIndex, int partIndex) {
|
||||
auto model = scriptable::make_qtowned<scriptable::ScriptableModel>();
|
||||
if (mesh) {
|
||||
model->append(*mesh);
|
||||
}
|
||||
return updateMeshes(uuid, model.get());
|
||||
}
|
||||
|
||||
bool ModelScriptingInterface::updateMeshes(QUuid uuid, const scriptable::ScriptableModelPointer model) {
|
||||
auto appProvider = DependencyManager::get<scriptable::ModelProviderFactory>();
|
||||
qCDebug(graphics_scripting) << "appProvider" << appProvider.data();
|
||||
scriptable::ModelProviderPointer provider = appProvider ? appProvider->lookupModelProvider(uuid) : nullptr;
|
||||
QString providerType = provider ? provider->metadata.value("providerType").toString() : QString();
|
||||
if (providerType.isEmpty()) {
|
||||
providerType = "unknown";
|
||||
}
|
||||
bool success = false;
|
||||
if (provider) {
|
||||
qCDebug(graphics_scripting) << "fetching meshes from " << providerType << "...";
|
||||
auto scriptableMeshes = provider->getScriptableModel(&success);
|
||||
qCDebug(graphics_scripting) << "//fetched meshes from " << providerType << "success:" <<success << "#" << scriptableMeshes.meshes.size();
|
||||
if (success) {
|
||||
const scriptable::ScriptableModelBasePointer base = model->operator scriptable::ScriptableModelBasePointer();
|
||||
qCDebug(graphics_scripting) << "as base" << base;
|
||||
if (base) {
|
||||
//auto meshes = model->getConstMeshes();
|
||||
success = provider->replaceScriptableModelMeshPart(base, -1, -1);
|
||||
|
||||
// for (uint32_t m = 0; success && m < meshes.size(); m++) {
|
||||
// const auto& mesh = meshes.at(m);
|
||||
// for (int p = 0; success && p < mesh->getNumParts(); p++) {
|
||||
// qCDebug(graphics_scripting) << "provider->replaceScriptableModelMeshPart" << "meshIndex" << m << "partIndex" << p;
|
||||
// success = provider->replaceScriptableModelMeshPart(base, m, p);
|
||||
// //if (!success) {
|
||||
// qCDebug(graphics_scripting) << "//provider->replaceScriptableModelMeshPart" << "meshIndex" << m << "partIndex" << p << success;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
void ModelScriptingInterface::getMeshes(QUuid uuid, QScriptValue callback) {
|
||||
auto handler = scriptable::jsBindCallback(callback);
|
||||
Q_ASSERT(handler.engine() == this->engine());
|
||||
QPointer<BaseScriptEngine> engine = dynamic_cast<BaseScriptEngine*>(handler.engine());
|
||||
|
||||
|
@ -49,18 +91,23 @@ void ModelScriptingInterface::getMeshes(QUuid uuid, QScriptValue scopeOrCallback
|
|||
QString error;
|
||||
|
||||
auto appProvider = DependencyManager::get<scriptable::ModelProviderFactory>();
|
||||
qDebug() << "appProvider" << appProvider.data();
|
||||
qCDebug(graphics_scripting) << "appProvider" << appProvider.data();
|
||||
scriptable::ModelProviderPointer provider = appProvider ? appProvider->lookupModelProvider(uuid) : nullptr;
|
||||
QString providerType = provider ? provider->metadata.value("providerType").toString() : QString();
|
||||
if (providerType.isEmpty()) {
|
||||
providerType = "unknown";
|
||||
}
|
||||
if (provider) {
|
||||
qCDebug(model_scripting) << "fetching meshes from " << providerType << "...";
|
||||
qCDebug(graphics_scripting) << "fetching meshes from " << providerType << "...";
|
||||
auto scriptableMeshes = provider->getScriptableModel(&success);
|
||||
qCDebug(model_scripting) << "//fetched meshes from " << providerType << "success:" <<success << "#" << scriptableMeshes.meshes.size();
|
||||
qCDebug(graphics_scripting) << "//fetched meshes from " << providerType << "success:" <<success << "#" << scriptableMeshes.meshes.size();
|
||||
if (success) {
|
||||
meshes = new scriptable::ScriptableModel(scriptableMeshes);//SimpleModelProxy::fromScriptableModel(scriptableMeshes);
|
||||
meshes = scriptable::make_scriptowned<scriptable::ScriptableModel>(scriptableMeshes);
|
||||
QString debugString = scriptable::toDebugString(meshes);
|
||||
QObject::connect(meshes, &QObject::destroyed, this, [=]() {
|
||||
qCDebug(graphics_scripting) << "///fetched meshes" << debugString;
|
||||
});
|
||||
|
||||
if (meshes->objectName().isEmpty()) {
|
||||
meshes->setObjectName(providerType+"::meshes");
|
||||
}
|
||||
|
@ -75,20 +122,20 @@ void ModelScriptingInterface::getMeshes(QUuid uuid, QScriptValue scopeOrCallback
|
|||
}
|
||||
|
||||
if (!error.isEmpty()) {
|
||||
qCWarning(model_scripting) << "ModelScriptingInterface::getMeshes ERROR" << error;
|
||||
qCWarning(graphics_scripting) << "ModelScriptingInterface::getMeshes ERROR" << error;
|
||||
callScopedHandlerObject(handler, engine->makeError(error), QScriptValue::NullValue);
|
||||
} else {
|
||||
callScopedHandlerObject(handler, QScriptValue::NullValue, engine->newQObject(meshes, QScriptEngine::ScriptOwnership));
|
||||
callScopedHandlerObject(handler, QScriptValue::NullValue, engine->toScriptValue(meshes));
|
||||
}
|
||||
}
|
||||
|
||||
QString ModelScriptingInterface::meshToOBJ(const scriptable::ScriptableModel& _in) {
|
||||
const auto& in = _in.getConstMeshes();
|
||||
qCDebug(model_scripting) << "meshToOBJ" << in.size();
|
||||
qCDebug(graphics_scripting) << "meshToOBJ" << in.size();
|
||||
if (in.size()) {
|
||||
QList<scriptable::MeshPointer> meshes;
|
||||
foreach (auto meshProxy, in) {
|
||||
qCDebug(model_scripting) << "meshToOBJ" << meshProxy;
|
||||
qCDebug(graphics_scripting) << "meshToOBJ" << meshProxy;
|
||||
if (meshProxy) {
|
||||
meshes.append(getMeshPointer(meshProxy));
|
||||
}
|
||||
|
@ -207,7 +254,7 @@ QScriptValue ModelScriptingInterface::appendMeshes(scriptable::ScriptableModel _
|
|||
(gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL));
|
||||
|
||||
|
||||
return engine()->toScriptValue(scriptable::ScriptableMeshPointer(new scriptable::ScriptableMesh(nullptr, result)));
|
||||
return engine()->toScriptValue(scriptable::make_scriptowned<scriptable::ScriptableMesh>(result));
|
||||
}
|
||||
|
||||
QScriptValue ModelScriptingInterface::transformMesh(scriptable::ScriptableMeshPointer meshProxy, glm::mat4 transform) {
|
||||
|
@ -220,8 +267,7 @@ QScriptValue ModelScriptingInterface::transformMesh(scriptable::ScriptableMeshPo
|
|||
[&](glm::vec3 color){ return color; },
|
||||
[&](glm::vec3 normal){ return glm::vec3(transform * glm::vec4(normal, 0.0f)); },
|
||||
[&](uint32_t index){ return index; });
|
||||
scriptable::ScriptableMeshPointer resultProxy = scriptable::ScriptableMeshPointer(new scriptable::ScriptableMesh(nullptr, result));
|
||||
return engine()->toScriptValue(resultProxy);
|
||||
return engine()->toScriptValue(scriptable::make_scriptowned<scriptable::ScriptableMesh>(result));
|
||||
}
|
||||
|
||||
QScriptValue ModelScriptingInterface::getVertexCount(scriptable::ScriptableMeshPointer meshProxy) {
|
||||
|
@ -270,7 +316,7 @@ QScriptValue ModelScriptingInterface::newMesh(const QVector<glm::vec3>& vertices
|
|||
sizeof(glm::vec3), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
|
||||
mesh->addAttribute(gpu::Stream::NORMAL, normalBufferView);
|
||||
} else {
|
||||
qCWarning(model_scripting, "ModelScriptingInterface::newMesh normals must be same length as vertices");
|
||||
qCWarning(graphics_scripting, "ModelScriptingInterface::newMesh normals must be same length as vertices");
|
||||
}
|
||||
|
||||
// indices (faces)
|
||||
|
@ -300,54 +346,10 @@ QScriptValue ModelScriptingInterface::newMesh(const QVector<glm::vec3>& vertices
|
|||
|
||||
|
||||
|
||||
scriptable::ScriptableMeshPointer meshProxy = scriptable::ScriptableMeshPointer(new scriptable::ScriptableMesh(nullptr, mesh));
|
||||
return engine()->toScriptValue(meshProxy);
|
||||
return engine()->toScriptValue(scriptable::make_scriptowned<scriptable::ScriptableMesh>(mesh));
|
||||
}
|
||||
|
||||
namespace {
|
||||
QScriptValue meshPointerToScriptValue(QScriptEngine* engine, scriptable::ScriptableMeshPointer const &in) {
|
||||
if (!in) {
|
||||
return QScriptValue::NullValue;
|
||||
}
|
||||
return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects);
|
||||
}
|
||||
|
||||
void meshPointerFromScriptValue(const QScriptValue& value, scriptable::ScriptableMeshPointer &out) {
|
||||
auto obj = value.toQObject();
|
||||
qDebug() << "meshPointerFromScriptValue" << obj;
|
||||
if (auto tmp = qobject_cast<scriptable::ScriptableMesh*>(obj)) {
|
||||
out = tmp;
|
||||
}
|
||||
// FIXME: Why does above cast not work on Win32!?
|
||||
if (!out) {
|
||||
if (auto smp = static_cast<scriptable::ScriptableMesh*>(obj)) {
|
||||
qDebug() << "meshPointerFromScriptValue2" << smp;
|
||||
out = smp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue modelPointerToScriptValue(QScriptEngine* engine, const scriptable::ScriptableModelPointer &in) {
|
||||
return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects);
|
||||
// QScriptValue result = engine->newArray();
|
||||
// int i = 0;
|
||||
// foreach(auto& mesh, in->getMeshes()) {
|
||||
// result.setProperty(i++, meshPointerToScriptValue(engine, mesh));
|
||||
// }
|
||||
// return result;
|
||||
}
|
||||
|
||||
void modelPointerFromScriptValue(const QScriptValue& value, scriptable::ScriptableModelPointer &out) {
|
||||
const auto length = value.property("length").toInt32();
|
||||
qCDebug(model_scripting) << "in modelPointerFromScriptValue, length =" << length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (const auto meshProxy = qobject_cast<scriptable::ScriptableMesh*>(value.property(i).toQObject())) {
|
||||
out->meshes.append(meshProxy->getMeshPointer());
|
||||
} else {
|
||||
qCDebug(model_scripting) << "null meshProxy" << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: MESHFACES:
|
||||
// QScriptValue meshFaceToScriptValue(QScriptEngine* engine, const mesh::MeshFace &meshFace) {
|
||||
|
@ -365,29 +367,11 @@ namespace {
|
|||
// qScriptValueToSequence(array, result);
|
||||
// }
|
||||
|
||||
QScriptValue qVectorUInt32ToScriptValue(QScriptEngine* engine, const QVector<quint32>& vector) {
|
||||
return qScriptValueFromSequence(engine, vector);
|
||||
}
|
||||
|
||||
void qVectorUInt32FromScriptValue(const QScriptValue& array, QVector<quint32>& result) {
|
||||
qScriptValueToSequence(array, result);
|
||||
}
|
||||
}
|
||||
|
||||
int meshUint32 = qRegisterMetaType<quint32>();
|
||||
namespace mesh {
|
||||
int meshUint32 = qRegisterMetaType<uint32>();
|
||||
}
|
||||
int qVectorMeshUint32 = qRegisterMetaType<QVector<quint32>>();
|
||||
|
||||
void ModelScriptingInterface::registerMetaTypes(QScriptEngine* engine) {
|
||||
qScriptRegisterSequenceMetaType<QVector<scriptable::ScriptableMeshPointer>>(engine);
|
||||
qScriptRegisterSequenceMetaType<QVector<quint32>>(engine);
|
||||
|
||||
qScriptRegisterMetaType(engine, qVectorUInt32ToScriptValue, qVectorUInt32FromScriptValue);
|
||||
qScriptRegisterMetaType(engine, meshPointerToScriptValue, meshPointerFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, modelPointerToScriptValue, modelPointerFromScriptValue);
|
||||
|
||||
scriptable::registerMetaTypes(engine);
|
||||
// FIXME: MESHFACES: remove if MeshFace is not needed anywhere
|
||||
// qScriptRegisterSequenceMetaType<mesh::MeshFaces>(engine);
|
||||
// qScriptRegisterMetaType(engine, meshFaceToScriptValue, meshFaceFromScriptValue);
|
||||
|
@ -395,7 +379,7 @@ void ModelScriptingInterface::registerMetaTypes(QScriptEngine* engine) {
|
|||
}
|
||||
|
||||
MeshPointer ModelScriptingInterface::getMeshPointer(const scriptable::ScriptableMesh& meshProxy) {
|
||||
return meshProxy._mesh;//getMeshPointer(&meshProxy);
|
||||
return meshProxy.getMeshPointer();
|
||||
}
|
||||
MeshPointer ModelScriptingInterface::getMeshPointer(scriptable::ScriptableMesh& meshProxy) {
|
||||
return getMeshPointer(&meshProxy);
|
||||
|
@ -406,7 +390,7 @@ MeshPointer ModelScriptingInterface::getMeshPointer(scriptable::ScriptableMeshPo
|
|||
if (context()){
|
||||
context()->throwError("expected meshProxy as first parameter");
|
||||
} else {
|
||||
qDebug() << "expected meshProxy as first parameter";
|
||||
qCDebug(graphics_scripting) << "expected meshProxy as first parameter";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -415,7 +399,7 @@ MeshPointer ModelScriptingInterface::getMeshPointer(scriptable::ScriptableMeshPo
|
|||
if (context()) {
|
||||
context()->throwError("expected valid meshProxy as first parameter");
|
||||
} else {
|
||||
qDebug() << "expected valid meshProxy as first parameter";
|
||||
qCDebug(graphics_scripting) << "expected valid meshProxy as first parameter";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -15,19 +15,17 @@
|
|||
#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
|
||||
|
@ -36,7 +34,9 @@ public slots:
|
|||
* @function ModelScriptingInterface.getMeshes
|
||||
* @param {EntityID} entityID The ID of the entity whose meshes are to be retrieve
|
||||
*/
|
||||
void getMeshes(QUuid uuid, QScriptValue scopeOrCallback, QScriptValue methodOrName = QScriptValue());
|
||||
void getMeshes(QUuid uuid, QScriptValue callback);
|
||||
bool updateMeshes(QUuid uuid, const scriptable::ScriptableModelPointer model);
|
||||
bool updateMeshes(QUuid uuid, const scriptable::ScriptableMeshPointer mesh, int meshIndex=0, int partIndex=0);
|
||||
|
||||
QString meshToOBJ(const scriptable::ScriptableModel& in);
|
||||
|
||||
|
@ -48,6 +48,8 @@ public slots:
|
|||
QScriptValue getVertexCount(scriptable::ScriptableMeshPointer meshProxy);
|
||||
QScriptValue getVertex(scriptable::ScriptableMeshPointer meshProxy, quint32 vertexIndex);
|
||||
|
||||
static void registerMetaTypes(QScriptEngine* engine);
|
||||
|
||||
private:
|
||||
scriptable::MeshPointer getMeshPointer(scriptable::ScriptableMeshPointer meshProxy);
|
||||
scriptable::MeshPointer getMeshPointer(scriptable::ScriptableMesh& meshProxy);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -11,142 +11,188 @@
|
|||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
//#include <graphics-scriping/Forward.h>
|
||||
#include <graphics-scripting/ScriptableModel.h>
|
||||
#include <graphics-scripting/BufferViewHelpers.h>
|
||||
|
||||
#include <QtScript/QScriptable>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
namespace graphics {
|
||||
class Mesh;
|
||||
}
|
||||
namespace gpu {
|
||||
class BufferView;
|
||||
}
|
||||
namespace scriptable {
|
||||
class ScriptableMeshPart;
|
||||
using ScriptableMeshPartPointer = QPointer<ScriptableMeshPart>;
|
||||
class ScriptableMesh : public QObject, QScriptable {
|
||||
|
||||
QScriptValue jsBindCallback(QScriptValue callback);
|
||||
class ScriptableMesh : public ScriptableMeshBase, QScriptable {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Q_PROPERTY(quint32 numParts READ getNumParts)
|
||||
Q_PROPERTY(quint32 numAttributes READ getNumAttributes)
|
||||
Q_PROPERTY(quint32 numVertices READ getNumVertices)
|
||||
Q_PROPERTY(quint32 numIndices READ getNumIndices)
|
||||
Q_PROPERTY(QVariantMap metadata MEMBER _metadata)
|
||||
Q_PROPERTY(uint32 numParts READ getNumParts)
|
||||
Q_PROPERTY(uint32 numAttributes READ getNumAttributes)
|
||||
Q_PROPERTY(uint32 numVertices READ getNumVertices)
|
||||
Q_PROPERTY(uint32 numIndices READ getNumIndices)
|
||||
Q_PROPERTY(QVariantMap metadata MEMBER metadata)
|
||||
Q_PROPERTY(QVector<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(); }
|
||||
|
||||
static QMap<QString,int> ATTRIBUTES;
|
||||
static std::map<QString, gpu::BufferView> gatherBufferViews(MeshPointer mesh, const QStringList& expandToMatchPositions = QStringList());
|
||||
operator const ScriptableMeshBase*() const { return (qobject_cast<const scriptable::ScriptableMeshBase*>(this)); }
|
||||
ScriptableMesh(scriptable::MeshPointer mesh) : ScriptableMeshBase(mesh) { ownedMesh = mesh; }
|
||||
ScriptableMesh(WeakModelProviderPointer provider, ScriptableModelBasePointer model, MeshPointer mesh, const QVariantMap& metadata)
|
||||
: ScriptableMeshBase(provider, model, mesh, metadata) { ownedMesh = mesh; }
|
||||
//ScriptableMesh& operator=(const ScriptableMesh& other) { model=other.model; mesh=other.mesh; metadata=other.metadata; return *this; };
|
||||
//ScriptableMesh() : QObject(), model(nullptr) {}
|
||||
//ScriptableMesh(const ScriptableMesh& other) : QObject(), model(other.model), mesh(other.mesh), metadata(other.metadata) {}
|
||||
ScriptableMesh(const ScriptableMeshBase& other);
|
||||
ScriptableMesh(const ScriptableMesh& other) : ScriptableMeshBase(other) {};
|
||||
virtual ~ScriptableMesh();
|
||||
|
||||
ScriptableMesh& operator=(const ScriptableMesh& other) { _model=other._model; _mesh=other._mesh; _metadata=other._metadata; return *this; };
|
||||
ScriptableMesh() : QObject(), _model(nullptr) {}
|
||||
ScriptableMesh(ScriptableModelPointer parent, scriptable::MeshPointer mesh) : QObject(), _model(parent), _mesh(mesh) {}
|
||||
ScriptableMesh(const ScriptableMesh& other) : QObject(), _model(other._model), _mesh(other._mesh), _metadata(other._metadata) {}
|
||||
~ScriptableMesh() { qDebug() << "~ScriptableMesh" << this; }
|
||||
|
||||
scriptable::MeshPointer getMeshPointer() const { return _mesh; }
|
||||
Q_INVOKABLE const scriptable::ScriptableModelPointer getParentModel() const { return qobject_cast<scriptable::ScriptableModel*>(model); }
|
||||
Q_INVOKABLE const scriptable::MeshPointer getOwnedMeshPointer() const { return ownedMesh; }
|
||||
scriptable::ScriptableMeshPointer getSelf() const { return const_cast<scriptable::ScriptableMesh*>(this); }
|
||||
public slots:
|
||||
quint32 getNumParts() const;
|
||||
quint32 getNumVertices() const;
|
||||
quint32 getNumAttributes() const;
|
||||
quint32 getNumIndices() const { return 0; }
|
||||
uint32 getNumParts() const;
|
||||
uint32 getNumVertices() const;
|
||||
uint32 getNumAttributes() const;
|
||||
uint32 getNumIndices() const;
|
||||
QVector<QString> getAttributeNames() const;
|
||||
QVector<scriptable::ScriptableMeshPartPointer> getMeshParts() const;
|
||||
|
||||
QVariantMap getVertexAttributes(quint32 vertexIndex) const;
|
||||
QVariantMap getVertexAttributes(quint32 vertexIndex, QVector<QString> attributes) const;
|
||||
QVariantMap getVertexAttributes(uint32 vertexIndex) const;
|
||||
QVariantMap getVertexAttributes(uint32 vertexIndex, QVector<QString> attributes) const;
|
||||
|
||||
QVector<quint32> getIndices() const;
|
||||
QVector<quint32> findNearbyIndices(const glm::vec3& origin, float epsilon = 1e-6) const;
|
||||
QVector<uint32> getIndices() const;
|
||||
QVector<uint32> findNearbyIndices(const glm::vec3& origin, float epsilon = 1e-6) const;
|
||||
QVariantMap getMeshExtents() const;
|
||||
bool setVertexAttributes(quint32 vertexIndex, QVariantMap attributes);
|
||||
QVariantMap scaleToFit(float unitScale);
|
||||
bool setVertexAttributes(uint32 vertexIndex, QVariantMap attributes);
|
||||
|
||||
QVariantList getAttributeValues(const QString& attributeName) const;
|
||||
|
||||
int _getSlotNumber(const QString& attributeName) const;
|
||||
|
||||
scriptable::ScriptableMeshPointer cloneMesh(bool recalcNormals = false);
|
||||
public:
|
||||
operator bool() const { return !mesh.expired(); }
|
||||
|
||||
public slots:
|
||||
// QScriptEngine-specific wrappers
|
||||
uint32 mapAttributeValues(QScriptValue callback);
|
||||
};
|
||||
|
||||
// TODO: part-specific wrapper for working with raw geometries
|
||||
class ScriptableMeshPart : public QObject, QScriptable {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Q_PROPERTY(uint32 partIndex MEMBER partIndex CONSTANT)
|
||||
Q_PROPERTY(int numElementsPerFace MEMBER _elementsPerFace CONSTANT)
|
||||
Q_PROPERTY(QString topology MEMBER _topology CONSTANT)
|
||||
|
||||
Q_PROPERTY(uint32 numFaces READ getNumFaces)
|
||||
Q_PROPERTY(uint32 numAttributes READ getNumAttributes)
|
||||
Q_PROPERTY(uint32 numVertices READ getNumVertices)
|
||||
Q_PROPERTY(uint32 numIndices READ getNumIndices)
|
||||
Q_PROPERTY(QVector<QString> attributeNames READ getAttributeNames)
|
||||
|
||||
Q_PROPERTY(QVariantMap metadata MEMBER metadata)
|
||||
|
||||
//Q_PROPERTY(scriptable::ScriptableMeshPointer parentMesh MEMBER parentMesh CONSTANT HIDE)
|
||||
|
||||
ScriptableMeshPart(scriptable::ScriptableMeshPointer parentMesh, int partIndex);
|
||||
ScriptableMeshPart& operator=(const ScriptableMeshPart& view) { parentMesh=view.parentMesh; return *this; };
|
||||
ScriptableMeshPart(const ScriptableMeshPart& other) : parentMesh(other.parentMesh), partIndex(other.partIndex) {}
|
||||
~ScriptableMeshPart() { qDebug() << "~ScriptableMeshPart" << this; }
|
||||
|
||||
public slots:
|
||||
scriptable::ScriptableMeshPointer getParentMesh() const { return parentMesh; }
|
||||
uint32 getNumAttributes() const { return parentMesh ? parentMesh->getNumAttributes() : 0; }
|
||||
uint32 getNumVertices() const { return parentMesh ? parentMesh->getNumVertices() : 0; }
|
||||
uint32 getNumIndices() const { return parentMesh ? parentMesh->getNumIndices() : 0; }
|
||||
uint32 getNumFaces() const { return parentMesh ? parentMesh->getNumIndices() / _elementsPerFace : 0; }
|
||||
QVector<QString> getAttributeNames() const { return parentMesh ? parentMesh->getAttributeNames() : QVector<QString>(); }
|
||||
QVector<uint32> getFace(uint32 faceIndex) const {
|
||||
auto inds = parentMesh ? parentMesh->getIndices() : QVector<uint32>();
|
||||
return faceIndex+2 < (uint32)inds.size() ? inds.mid(faceIndex*3, 3) : QVector<uint32>();
|
||||
}
|
||||
QVariantMap scaleToFit(float unitScale);
|
||||
QVariantMap translate(const glm::vec3& translation);
|
||||
QVariantMap scale(const glm::vec3& scale, const glm::vec3& origin = glm::vec3(NAN));
|
||||
QVariantMap rotateDegrees(const glm::vec3& eulerAngles, const glm::vec3& origin = glm::vec3(NAN));
|
||||
QVariantMap rotate(const glm::quat& rotation, const glm::vec3& origin = glm::vec3(NAN));
|
||||
QVariantMap transform(const glm::mat4& transform);
|
||||
|
||||
public:
|
||||
operator bool() const { return _mesh != nullptr; }
|
||||
ScriptableModelPointer _model;
|
||||
scriptable::MeshPointer _mesh;
|
||||
QVariantMap _metadata;
|
||||
bool unrollVertices(bool recalcNormals = false);
|
||||
bool dedupeVertices(float epsilon = 1e-6);
|
||||
bool recalculateNormals() { return buffer_helpers::recalculateNormals(getMeshPointer()); }
|
||||
|
||||
bool replaceMeshData(scriptable::ScriptableMeshPartPointer source, const QVector<QString>& attributeNames = QVector<QString>());
|
||||
scriptable::ScriptableMeshPartPointer cloneMeshPart(bool recalcNormals = false) {
|
||||
if (parentMesh) {
|
||||
if (auto clone = parentMesh->cloneMesh(recalcNormals)) {
|
||||
return clone->getMeshParts().value(partIndex);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
QString toOBJ();
|
||||
|
||||
public slots:
|
||||
// QScriptEngine-specific wrappers
|
||||
QScriptValue mapAttributeValues(QScriptValue scopeOrCallback, QScriptValue methodOrName = QScriptValue());
|
||||
bool dedupeVertices(float epsilon = 1e-6);
|
||||
bool recalculateNormals();
|
||||
QScriptValue cloneMesh(bool recalcNormals = true);
|
||||
QScriptValue unrollVertices(bool recalcNormals = true);
|
||||
bool replaceMeshData(scriptable::ScriptableMeshPointer source, const QVector<QString>& attributeNames = QVector<QString>());
|
||||
QString toOBJ();
|
||||
};
|
||||
|
||||
// TODO: part-specific wrapper for working with raw geometries
|
||||
class ScriptableMeshPart : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Q_PROPERTY(QString topology READ getTopology)
|
||||
Q_PROPERTY(quint32 numFaces READ getNumFaces)
|
||||
|
||||
ScriptableMeshPart& operator=(const ScriptableMeshPart& view) { parentMesh=view.parentMesh; return *this; };
|
||||
ScriptableMeshPart(const ScriptableMeshPart& other) : parentMesh(other.parentMesh) {}
|
||||
ScriptableMeshPart() {}
|
||||
~ScriptableMeshPart() { qDebug() << "~ScriptableMeshPart" << this; }
|
||||
|
||||
public slots:
|
||||
QString getTopology() const { return "triangles"; }
|
||||
quint32 getNumFaces() const { return parentMesh.getIndices().size() / 3; }
|
||||
QVector<quint32> getFace(quint32 faceIndex) const {
|
||||
auto inds = parentMesh.getIndices();
|
||||
return faceIndex+2 < (quint32)inds.size() ? inds.mid(faceIndex*3, 3) : QVector<quint32>();
|
||||
}
|
||||
uint32 mapAttributeValues(QScriptValue callback);
|
||||
|
||||
public:
|
||||
scriptable::ScriptableMesh parentMesh;
|
||||
int partIndex;
|
||||
scriptable::ScriptableMeshPointer parentMesh;
|
||||
uint32 partIndex;
|
||||
QVariantMap metadata;
|
||||
protected:
|
||||
int _elementsPerFace{ 3 };
|
||||
QString _topology{ "triangles" };
|
||||
scriptable::MeshPointer getMeshPointer() const { return parentMesh ? parentMesh->getMeshPointer() : nullptr; }
|
||||
};
|
||||
|
||||
class GraphicsScriptingInterface : public QObject {
|
||||
class GraphicsScriptingInterface : public QObject, QScriptable {
|
||||
Q_OBJECT
|
||||
public:
|
||||
GraphicsScriptingInterface(QObject* parent = nullptr) : QObject(parent) {}
|
||||
GraphicsScriptingInterface(const GraphicsScriptingInterface& other) {}
|
||||
public slots:
|
||||
ScriptableMeshPart exportMeshPart(ScriptableMesh mesh, int part) { return {}; }
|
||||
public slots:
|
||||
ScriptableMeshPartPointer exportMeshPart(ScriptableMeshPointer mesh, int part=0) {
|
||||
return ScriptableMeshPartPointer(new ScriptableMeshPart(mesh, part));
|
||||
}
|
||||
bool updateMeshPart(ScriptableMeshPointer mesh, ScriptableMeshPartPointer part);
|
||||
};
|
||||
|
||||
// callback helper that lets C++ method signatures remain simple (ie: taking a single callback argument) while
|
||||
// still supporting extended Qt signal-like (scope, "methodName") and (scope, function(){}) "this" binding conventions
|
||||
QScriptValue jsBindCallback(QScriptValue callback);
|
||||
|
||||
// derive a corresponding C++ class instance from the current script engine's thisObject
|
||||
template <typename T> T this_qobject_cast(QScriptEngine* engine);
|
||||
}
|
||||
|
||||
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 {
|
||||
using uint32 = quint32;
|
||||
class MeshFace;
|
||||
using MeshFaces = QVector<mesh::MeshFace>;
|
||||
class MeshFace {
|
||||
public:
|
||||
MeshFace() {}
|
||||
MeshFace(QVector<mesh::uint32> vertexIndices) : vertexIndices(vertexIndices) {}
|
||||
MeshFace(QVector<scriptable::uint32> vertexIndices) : vertexIndices(vertexIndices) {}
|
||||
~MeshFace() {}
|
||||
|
||||
QVector<mesh::uint32> vertexIndices;
|
||||
QVector<scriptable::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>)
|
||||
Q_DECLARE_METATYPE(scriptable::uint32)
|
||||
Q_DECLARE_METATYPE(QVector<scriptable::uint32>)
|
||||
|
|
|
@ -1,56 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <QtCore/QObject>
|
||||
#include <QtCore/QVector>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtCore/QUuid>
|
||||
#include <QPointer>
|
||||
#include <memory>
|
||||
#include "Forward.h"
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
namespace graphics {
|
||||
class Mesh;
|
||||
}
|
||||
namespace gpu {
|
||||
class BufferView;
|
||||
}
|
||||
class QScriptValue;
|
||||
|
||||
namespace scriptable {
|
||||
using Mesh = graphics::Mesh;
|
||||
using MeshPointer = std::shared_ptr<scriptable::Mesh>;
|
||||
|
||||
class ScriptableModel;
|
||||
using ScriptableModelPointer = QPointer<ScriptableModel>;
|
||||
class ScriptableMesh;
|
||||
using ScriptableMeshPointer = QPointer<ScriptableMesh>;
|
||||
|
||||
// abstract container for holding one or more scriptable meshes
|
||||
class ScriptableModel : public QObject {
|
||||
class ScriptableModel : public ScriptableModelBase {
|
||||
Q_OBJECT
|
||||
public:
|
||||
QUuid objectID;
|
||||
QVariantMap metadata;
|
||||
QVector<scriptable::MeshPointer> meshes;
|
||||
|
||||
Q_PROPERTY(QVector<scriptable::ScriptableMeshPointer> meshes READ getMeshes)
|
||||
Q_PROPERTY(QUuid objectID MEMBER objectID CONSTANT)
|
||||
Q_PROPERTY(QVariantMap metadata MEMBER metadata CONSTANT)
|
||||
Q_INVOKABLE QString toString() const;
|
||||
Q_PROPERTY(uint32 numMeshes READ getNumMeshes)
|
||||
Q_PROPERTY(QVector<scriptable::ScriptableMeshPointer> meshes READ getMeshes)
|
||||
|
||||
ScriptableModel(QObject* parent = nullptr) : QObject(parent) {}
|
||||
ScriptableModel(const ScriptableModel& other) : objectID(other.objectID), metadata(other.metadata), meshes(other.meshes) {}
|
||||
ScriptableModel& operator=(const ScriptableModel& view) { objectID = view.objectID; metadata = view.metadata; meshes = view.meshes; return *this; }
|
||||
~ScriptableModel() { qDebug() << "~ScriptableModel" << this; }
|
||||
|
||||
void mixin(const ScriptableModel& other) {
|
||||
for (const auto& key : other.metadata.keys()) { metadata[key] = other.metadata[key]; }
|
||||
for (const auto& mesh : other.meshes) { meshes << mesh; }
|
||||
}
|
||||
ScriptableModel(QObject* parent = nullptr) : ScriptableModelBase(parent) {}
|
||||
ScriptableModel(const ScriptableModel& other) : ScriptableModelBase(other) {}
|
||||
ScriptableModel(const ScriptableModelBase& other) : ScriptableModelBase(other) {}
|
||||
ScriptableModel& operator=(const ScriptableModelBase& view) { ScriptableModelBase::operator=(view); return *this; }
|
||||
virtual ~ScriptableModel() { qDebug() << "~ScriptableModel" << this; }
|
||||
|
||||
Q_INVOKABLE scriptable::ScriptableModelPointer cloneModel(const QVariantMap& options = QVariantMap());
|
||||
// TODO: in future accessors for these could go here
|
||||
// QVariantMap shapes;
|
||||
// QVariantMap materials;
|
||||
|
@ -58,28 +26,23 @@ namespace scriptable {
|
|||
|
||||
QVector<scriptable::ScriptableMeshPointer> getMeshes();
|
||||
const QVector<scriptable::ScriptableMeshPointer> getConstMeshes() const;
|
||||
operator scriptable::ScriptableModelBasePointer() {
|
||||
QPointer<scriptable::ScriptableModelBase> p;
|
||||
p = qobject_cast<scriptable::ScriptableModelBase*>(this);
|
||||
return p;
|
||||
}
|
||||
|
||||
|
||||
// QScriptEngine-specific wrappers
|
||||
Q_INVOKABLE QScriptValue mapAttributeValues(QScriptValue scopeOrCallback, QScriptValue methodOrName);
|
||||
Q_INVOKABLE uint32 mapAttributeValues(QScriptValue callback);
|
||||
Q_INVOKABLE QString toString() const;
|
||||
Q_INVOKABLE uint32 getNumMeshes() { return meshes.size(); }
|
||||
};
|
||||
|
||||
// mixin class for Avatar/Entity/Overlay Rendering that expose their in-memory graphics::Meshes
|
||||
class ModelProvider {
|
||||
public:
|
||||
QVariantMap metadata;
|
||||
static scriptable::ScriptableModel modelUnavailableError(bool* ok) { if (ok) { *ok = false; } return {}; }
|
||||
virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) = 0;
|
||||
};
|
||||
using ModelProviderPointer = std::shared_ptr<scriptable::ModelProvider>;
|
||||
|
||||
// mixin class for Application to resolve UUIDs into a corresponding ModelProvider
|
||||
class ModelProviderFactory : public Dependency {
|
||||
public:
|
||||
virtual scriptable::ModelProviderPointer lookupModelProvider(QUuid uuid) = 0;
|
||||
};
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(scriptable::MeshPointer)
|
||||
Q_DECLARE_METATYPE(scriptable::ScriptableModel)
|
||||
Q_DECLARE_METATYPE(scriptable::WeakMeshPointer)
|
||||
Q_DECLARE_METATYPE(scriptable::ScriptableModelPointer)
|
||||
|
||||
Q_DECLARE_METATYPE(scriptable::ScriptableModelBase)
|
||||
Q_DECLARE_METATYPE(scriptable::ScriptableModelBasePointer)
|
||||
|
|
Loading…
Reference in a new issue