mirror of
https://github.com/overte-org/overte.git
synced 2025-04-14 04:07:11 +02:00
cleanup
This commit is contained in:
parent
06afaa7470
commit
a08770c816
9 changed files with 722 additions and 656 deletions
|
@ -15,6 +15,9 @@
|
|||
#include "OBJWriter.h"
|
||||
#include "ModelFormatLogging.h"
|
||||
|
||||
// FIXME: should this live in shared? (it depends on gpu/)
|
||||
#include <../graphics-scripting/src/graphics-scripting/BufferViewHelpers.h>
|
||||
|
||||
static QString formatFloat(double n) {
|
||||
// limit precision to 6, but don't output trailing zeros.
|
||||
QString s = QString::number(n, 'f', 6);
|
||||
|
@ -91,7 +94,8 @@ bool writeOBJToTextStream(QTextStream& out, QList<MeshPointer> meshes) {
|
|||
const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(gpu::Stream::InputSlot::NORMAL);
|
||||
gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements();
|
||||
for (gpu::BufferView::Index i = 0; i < numNormals; i++) {
|
||||
glm::vec3 normal = normalsBufferView.get<glm::vec3>(i);
|
||||
glm::vec3 normal = glmVecFromVariant<glm::vec3>(bufferViewElementToVariant(normalsBufferView, i));
|
||||
//glm::vec3 normal = normalsBufferView.get<glm::vec3>(i);
|
||||
out << "vn ";
|
||||
out << formatFloat(normal[0]) << " ";
|
||||
out << formatFloat(normal[1]) << " ";
|
||||
|
|
|
@ -8,9 +8,15 @@
|
|||
#include <gpu/Stream.h>
|
||||
|
||||
#include <glm/gtc/packing.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
|
||||
#include "DebugNames.h"
|
||||
QLoggingCategory bufferview_helpers{"hifi.bufferview"};
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
@ -61,6 +67,9 @@ bool bufferViewElementFromVariant(const gpu::BufferView& view, quint32 index, co
|
|||
const auto dataType = element.getType();
|
||||
const auto byteLength = element.getSize();
|
||||
const auto BYTES_PER_ELEMENT = byteLength / vecN;
|
||||
#ifdef DEBUG_BUFFERVIEW_SCRIPTING
|
||||
qCDebug(bufferview_helpers) << "bufferViewElementFromVariant" << index << DebugNames::stringFrom(dataType) << BYTES_PER_ELEMENT << vecN;
|
||||
#endif
|
||||
if (BYTES_PER_ELEMENT == 1) {
|
||||
switch(vecN) {
|
||||
case 2: setBufferViewElement<glm::u8vec2>(view, index, v); return true;
|
||||
|
@ -71,16 +80,25 @@ bool bufferViewElementFromVariant(const gpu::BufferView& view, quint32 index, co
|
|||
glm::uint32 unused;
|
||||
packNormalAndTangent(glmVecFromVariant<glm::vec3>(v), glm::vec3(), rawColor, unused);
|
||||
view.edit<glm::uint32>(index) = rawColor;
|
||||
return true;
|
||||
} else if (element == gpu::Element::VEC4F_NORMALIZED_XYZ10W2) {
|
||||
glm::uint32 packedNormal;// = glm::packSnorm3x10_1x2(glm::vec4(glmVecFromVariant<glm::vec3>(v), 0.0f));
|
||||
glm::uint32 unused;
|
||||
packNormalAndTangent(glm::vec3(), glmVecFromVariant<glm::vec3>(v), unused, packedNormal);
|
||||
view.edit<glm::uint32>(index) = packedNormal;
|
||||
return true;
|
||||
}
|
||||
setBufferViewElement<glm::u8vec4>(view, index, v); return true;
|
||||
}
|
||||
}
|
||||
} else if (BYTES_PER_ELEMENT == 2) {
|
||||
if (dataType == gpu::HALF) {
|
||||
switch(vecN) {
|
||||
case 2: view.edit<glm::int16>(index) = glm::packSnorm2x8(glmVecFromVariant<glm::vec2>(v)); return true;
|
||||
case 4: view.edit<glm::int32>(index) = glm::packSnorm4x8(glmVecFromVariant<glm::vec4>(v)); return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
switch(vecN) {
|
||||
case 2: setBufferViewElement<glm::u16vec2>(view, index, v); return true;
|
||||
case 3: setBufferViewElement<glm::u16vec3>(view, index, v); return true;
|
||||
|
@ -112,6 +130,9 @@ QVariant bufferViewElementToVariant(const gpu::BufferView& view, quint32 index,
|
|||
const auto BYTES_PER_ELEMENT = byteLength / vecN;
|
||||
Q_ASSERT(index < view.getNumElements());
|
||||
Q_ASSERT(index * vecN * BYTES_PER_ELEMENT < (view._size - vecN * BYTES_PER_ELEMENT));
|
||||
#ifdef DEBUG_BUFFERVIEW_SCRIPTING
|
||||
qCDebug(bufferview_helpers) << "bufferViewElementToVariant" << index << DebugNames::stringFrom(dataType) << BYTES_PER_ELEMENT << vecN;
|
||||
#endif
|
||||
if (BYTES_PER_ELEMENT == 1) {
|
||||
switch(vecN) {
|
||||
case 2: return getBufferViewElement<glm::u8vec2>(view, index, asArray);
|
||||
|
@ -129,6 +150,12 @@ QVariant bufferViewElementToVariant(const gpu::BufferView& view, quint32 index,
|
|||
}
|
||||
}
|
||||
} else if (BYTES_PER_ELEMENT == 2) {
|
||||
if (dataType == gpu::HALF) {
|
||||
switch(vecN) {
|
||||
case 2: return glmVecToVariant(glm::vec2(glm::unpackSnorm2x8(view.get<glm::int16>(index))));
|
||||
case 4: return glmVecToVariant(glm::vec4(glm::unpackSnorm4x8(view.get<glm::int32>(index))));
|
||||
}
|
||||
}
|
||||
switch(vecN) {
|
||||
case 2: return getBufferViewElement<glm::u16vec2>(view, index, asArray);
|
||||
case 3: return getBufferViewElement<glm::u16vec3>(view, index, asArray);
|
||||
|
@ -193,3 +220,31 @@ const T glmVecFromVariant(const QVariant& v) {
|
|||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
gpu::BufferView bufferViewFromVector(QVector<T> elements, gpu::Element elementType) {
|
||||
auto vertexBuffer = std::make_shared<gpu::Buffer>(elements.size() * sizeof(T), (gpu::Byte*)elements.data());
|
||||
return { vertexBuffer, 0, vertexBuffer->getSize(),sizeof(T), elementType };
|
||||
}
|
||||
|
||||
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); }
|
||||
|
||||
gpu::BufferView cloneBufferView(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) {
|
||||
auto effectiveSize = input._buffer->getSize() / input.getNumElements();
|
||||
qDebug() << "resize input" << input.getNumElements() << input._buffer->getSize() << "effectiveSize" << effectiveSize;
|
||||
auto vsize = input._element.getSize() * numElements;
|
||||
gpu::Byte *data = new gpu::Byte[vsize];
|
||||
memset(data, 0, vsize);
|
||||
auto buffer = new gpu::Buffer(vsize, (gpu::Byte*)data);
|
||||
delete[] data;
|
||||
auto output = gpu::BufferView(buffer, input._element);
|
||||
qDebug() << "resized output" << output.getNumElements() << output._buffer->getSize();
|
||||
return output;
|
||||
}
|
||||
|
|
|
@ -8,11 +8,18 @@
|
|||
|
||||
#include <QtCore>
|
||||
|
||||
namespace gpu { class BufferView; }
|
||||
namespace gpu {
|
||||
class BufferView;
|
||||
class Element;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
gpu::BufferView cloneBufferView(const gpu::BufferView& input);
|
||||
gpu::BufferView resizedBufferView(const gpu::BufferView& input, quint32 numElements);
|
||||
|
||||
|
|
|
@ -17,27 +17,16 @@
|
|||
#include "BaseScriptEngine.h"
|
||||
#include "ScriptEngineLogging.h"
|
||||
#include "OBJWriter.h"
|
||||
#include "OBJReader.h"
|
||||
//#include "ui/overlays/Base3DOverlay.h"
|
||||
//#include "EntityTreeRenderer.h"
|
||||
//#include "avatar/AvatarManager.h"
|
||||
//#include "RenderableEntityItem.h"
|
||||
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
#include <GeometryUtil.h>
|
||||
|
||||
#include <shared/QtHelpers.h>
|
||||
|
||||
|
||||
#include <graphics-scripting/DebugNames.h>
|
||||
|
||||
#include <graphics-scripting/BufferViewHelpers.h>
|
||||
|
||||
#include "BufferViewScripting.h"
|
||||
|
||||
#include "ScriptableMesh.h"
|
||||
|
||||
using ScriptableMesh = scriptable::ScriptableMesh;
|
||||
|
||||
#include "ModelScriptingInterface.moc"
|
||||
|
||||
namespace {
|
||||
|
@ -50,13 +39,56 @@ ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(pare
|
|||
}
|
||||
}
|
||||
|
||||
void ModelScriptingInterface::getMeshes(QUuid uuid, QScriptValue scopeOrCallback, QScriptValue methodOrName) {
|
||||
auto handler = makeScopedHandlerObject(scopeOrCallback, methodOrName);
|
||||
Q_ASSERT(handler.engine() == this->engine());
|
||||
QPointer<BaseScriptEngine> engine = dynamic_cast<BaseScriptEngine*>(handler.engine());
|
||||
|
||||
scriptable::ScriptableModel* meshes{ nullptr };
|
||||
bool success = false;
|
||||
QString error;
|
||||
|
||||
auto appProvider = DependencyManager::get<scriptable::ModelProviderFactory>();
|
||||
qDebug() << "appProvider" << appProvider.data();
|
||||
scriptable::ModelProviderPointer provider = appProvider ? appProvider->lookupModelProvider(uuid) : nullptr;
|
||||
QString providerType = provider ? provider->metadata.value("providerType").toString() : QString();
|
||||
if (providerType.isEmpty()) {
|
||||
providerType = "unknown";
|
||||
}
|
||||
if (provider) {
|
||||
qCDebug(model_scripting) << "fetching meshes from " << providerType << "...";
|
||||
auto scriptableMeshes = provider->getScriptableModel(&success);
|
||||
qCDebug(model_scripting) << "//fetched meshes from " << providerType << "success:" <<success << "#" << scriptableMeshes.meshes.size();
|
||||
if (success) {
|
||||
meshes = new scriptable::ScriptableModel(scriptableMeshes);//SimpleModelProxy::fromScriptableModel(scriptableMeshes);
|
||||
if (meshes->objectName().isEmpty()) {
|
||||
meshes->setObjectName(providerType+"::meshes");
|
||||
}
|
||||
if (meshes->objectID.isNull()) {
|
||||
meshes->objectID = uuid.toString();
|
||||
}
|
||||
meshes->metadata["provider"] = provider->metadata;
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
error = QString("failed to get meshes from %1 provider for uuid %2").arg(providerType).arg(uuid.toString());
|
||||
}
|
||||
|
||||
if (!error.isEmpty()) {
|
||||
qCWarning(model_scripting) << "ModelScriptingInterface::getMeshes ERROR" << error;
|
||||
callScopedHandlerObject(handler, engine->makeError(error), QScriptValue::NullValue);
|
||||
} else {
|
||||
callScopedHandlerObject(handler, QScriptValue::NullValue, engine->newQObject(meshes, QScriptEngine::ScriptOwnership));
|
||||
}
|
||||
}
|
||||
|
||||
QString ModelScriptingInterface::meshToOBJ(const scriptable::ScriptableModel& _in) {
|
||||
const auto& in = _in.getMeshes();
|
||||
const auto& in = _in.getConstMeshes();
|
||||
qCDebug(model_scripting) << "meshToOBJ" << in.size();
|
||||
if (in.size()) {
|
||||
QList<scriptable::MeshPointer> meshes;
|
||||
foreach (const auto meshProxy, in) {
|
||||
qCDebug(model_scripting) << "meshToOBJ" << meshProxy.get();
|
||||
foreach (auto meshProxy, in) {
|
||||
qCDebug(model_scripting) << "meshToOBJ" << meshProxy;
|
||||
if (meshProxy) {
|
||||
meshes.append(getMeshPointer(meshProxy));
|
||||
}
|
||||
|
@ -77,7 +109,7 @@ QScriptValue ModelScriptingInterface::appendMeshes(scriptable::ScriptableModel _
|
|||
size_t totalColorCount { 0 };
|
||||
size_t totalNormalCount { 0 };
|
||||
size_t totalIndexCount { 0 };
|
||||
foreach (const scriptable::ScriptableMeshPointer meshProxy, in) {
|
||||
foreach (auto& meshProxy, in) {
|
||||
scriptable::MeshPointer mesh = getMeshPointer(meshProxy);
|
||||
totalVertexCount += mesh->getNumVertices();
|
||||
|
||||
|
@ -113,7 +145,7 @@ QScriptValue ModelScriptingInterface::appendMeshes(scriptable::ScriptableModel _
|
|||
|
||||
uint32_t indexStartOffset { 0 };
|
||||
|
||||
foreach (const scriptable::ScriptableMeshPointer meshProxy, in) {
|
||||
foreach (const auto& meshProxy, in) {
|
||||
scriptable::MeshPointer mesh = getMeshPointer(meshProxy);
|
||||
mesh->forEach(
|
||||
[&](glm::vec3 position){
|
||||
|
@ -175,8 +207,7 @@ QScriptValue ModelScriptingInterface::appendMeshes(scriptable::ScriptableModel _
|
|||
(gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL));
|
||||
|
||||
|
||||
scriptable::ScriptableMeshPointer resultProxy = scriptable::ScriptableMeshPointer(new ScriptableMesh(nullptr, result));
|
||||
return engine()->toScriptValue(result);
|
||||
return engine()->toScriptValue(scriptable::ScriptableMeshPointer(new scriptable::ScriptableMesh(nullptr, result)));
|
||||
}
|
||||
|
||||
QScriptValue ModelScriptingInterface::transformMesh(scriptable::ScriptableMeshPointer meshProxy, glm::mat4 transform) {
|
||||
|
@ -189,7 +220,7 @@ QScriptValue ModelScriptingInterface::transformMesh(scriptable::ScriptableMeshPo
|
|||
[&](glm::vec3 color){ return color; },
|
||||
[&](glm::vec3 normal){ return glm::vec3(transform * glm::vec4(normal, 0.0f)); },
|
||||
[&](uint32_t index){ return index; });
|
||||
scriptable::ScriptableMeshPointer resultProxy = scriptable::ScriptableMeshPointer(new ScriptableMesh(nullptr, result));
|
||||
scriptable::ScriptableMeshPointer resultProxy = scriptable::ScriptableMeshPointer(new scriptable::ScriptableMesh(nullptr, result));
|
||||
return engine()->toScriptValue(resultProxy);
|
||||
}
|
||||
|
||||
|
@ -201,8 +232,11 @@ QScriptValue ModelScriptingInterface::getVertexCount(scriptable::ScriptableMeshP
|
|||
return (uint32_t)mesh->getNumVertices();
|
||||
}
|
||||
|
||||
QScriptValue ModelScriptingInterface::getVertex(scriptable::ScriptableMeshPointer meshProxy, mesh::uint32 vertexIndex) {
|
||||
QScriptValue ModelScriptingInterface::getVertex(scriptable::ScriptableMeshPointer meshProxy, quint32 vertexIndex) {
|
||||
auto mesh = getMeshPointer(meshProxy);
|
||||
if (!mesh) {
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
const gpu::BufferView& vertexBufferView = mesh->getVertexBuffer();
|
||||
auto numVertices = mesh->getNumVertices();
|
||||
|
@ -266,480 +300,46 @@ QScriptValue ModelScriptingInterface::newMesh(const QVector<glm::vec3>& vertices
|
|||
|
||||
|
||||
|
||||
scriptable::ScriptableMeshPointer meshProxy = scriptable::ScriptableMeshPointer(new ScriptableMesh(nullptr, mesh));
|
||||
scriptable::ScriptableMeshPointer meshProxy = scriptable::ScriptableMeshPointer(new scriptable::ScriptableMesh(nullptr, mesh));
|
||||
return engine()->toScriptValue(meshProxy);
|
||||
}
|
||||
|
||||
QScriptValue ModelScriptingInterface::mapAttributeValues(
|
||||
QScriptValue _in,
|
||||
QScriptValue scopeOrCallback,
|
||||
QScriptValue methodOrName
|
||||
) {
|
||||
qCInfo(model_scripting) << "mapAttributeValues" << _in.toVariant().typeName() << _in.toVariant().toString() << _in.toQObject();
|
||||
auto in = qscriptvalue_cast<scriptable::ScriptableModel>(_in).getMeshes();
|
||||
if (in.size()) {
|
||||
foreach (scriptable::ScriptableMeshPointer meshProxy, in) {
|
||||
mapMeshAttributeValues(meshProxy, scopeOrCallback, methodOrName);
|
||||
}
|
||||
return thisObject();
|
||||
} else if (auto meshProxy = qobject_cast<scriptable::ScriptableMesh*>(_in.toQObject())) {
|
||||
return mapMeshAttributeValues(meshProxy->shared_from_this(), scopeOrCallback, methodOrName);
|
||||
} else {
|
||||
context()->throwError("invalid ModelProxy || MeshProxyPointer");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
QScriptValue ModelScriptingInterface::unrollVertices(scriptable::ScriptableMeshPointer meshProxy, bool recalcNormals) {
|
||||
auto mesh = getMeshPointer(meshProxy);
|
||||
qCInfo(model_scripting) << "ModelScriptingInterface::unrollVertices" << !!mesh<< !!meshProxy;
|
||||
if (!mesh) {
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
auto positions = mesh->getVertexBuffer();
|
||||
auto indices = mesh->getIndexBuffer();
|
||||
quint32 numPoints = (quint32)indices.getNumElements();
|
||||
auto buffer = new gpu::Buffer();
|
||||
buffer->resize(numPoints * sizeof(uint32_t));
|
||||
auto newindices = gpu::BufferView(buffer, { gpu::SCALAR, gpu::UINT32, gpu::INDEX });
|
||||
qCInfo(model_scripting) << "ModelScriptingInterface::unrollVertices numPoints" << numPoints;
|
||||
auto attributeViews = ScriptableMesh::gatherBufferViews(mesh);
|
||||
for (const auto& a : attributeViews) {
|
||||
auto& view = a.second;
|
||||
auto sz = view._element.getSize();
|
||||
auto buffer = new gpu::Buffer();
|
||||
buffer->resize(numPoints * sz);
|
||||
auto points = gpu::BufferView(buffer, view._element);
|
||||
auto src = (uint8_t*)view._buffer->getData();
|
||||
auto dest = (uint8_t*)points._buffer->getData();
|
||||
auto slot = ScriptableMesh::ATTRIBUTES[a.first];
|
||||
qCInfo(model_scripting) << "ModelScriptingInterface::unrollVertices buffer" << a.first;
|
||||
qCInfo(model_scripting) << "ModelScriptingInterface::unrollVertices source" << view.getNumElements();
|
||||
qCInfo(model_scripting) << "ModelScriptingInterface::unrollVertices dest" << points.getNumElements();
|
||||
qCInfo(model_scripting) << "ModelScriptingInterface::unrollVertices sz" << sz << src << dest << slot;
|
||||
auto esize = indices._element.getSize();
|
||||
const char* hint= a.first.toStdString().c_str();
|
||||
for(quint32 i = 0; i < numPoints; i++) {
|
||||
quint32 index = esize == 4 ? indices.get<quint32>(i) : indices.get<quint16>(i);
|
||||
newindices.edit<uint32_t>(i) = i;
|
||||
bufferViewElementFromVariant(
|
||||
points, i,
|
||||
bufferViewElementToVariant(view, index, false, hint)
|
||||
);
|
||||
}
|
||||
if (slot == gpu::Stream::POSITION) {
|
||||
mesh->setVertexBuffer(points);
|
||||
} else {
|
||||
mesh->addAttribute(slot, points);
|
||||
}
|
||||
}
|
||||
mesh->setIndexBuffer(newindices);
|
||||
if (recalcNormals) {
|
||||
recalculateNormals(meshProxy);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
template <typename T>
|
||||
gpu::BufferView bufferViewFromVector(QVector<T> elements, gpu::Element elementType) {
|
||||
auto vertexBuffer = std::make_shared<gpu::Buffer>(
|
||||
elements.size() * sizeof(T),
|
||||
(gpu::Byte*)elements.data()
|
||||
);
|
||||
return { vertexBuffer, 0, vertexBuffer->getSize(),sizeof(T), elementType };
|
||||
}
|
||||
|
||||
gpu::BufferView cloneBufferView(const gpu::BufferView& input) {
|
||||
//qCInfo(model_scripting) << "input" << input.getNumElements() << input._buffer->getSize();
|
||||
auto output = gpu::BufferView(
|
||||
std::make_shared<gpu::Buffer>(input._buffer->getSize(), input._buffer->getData()),
|
||||
input._offset,
|
||||
input._size,
|
||||
input._stride,
|
||||
input._element
|
||||
);
|
||||
//qCInfo(model_scripting) << "after" << output.getNumElements() << output._buffer->getSize();
|
||||
return output;
|
||||
}
|
||||
|
||||
gpu::BufferView resizedBufferView(const gpu::BufferView& input, quint32 numElements) {
|
||||
auto effectiveSize = input._buffer->getSize() / input.getNumElements();
|
||||
qCInfo(model_scripting) << "resize input" << input.getNumElements() << input._buffer->getSize() << "effectiveSize" << effectiveSize;
|
||||
auto vsize = input._element.getSize() * numElements;
|
||||
gpu::Byte *data = new gpu::Byte[vsize];
|
||||
memset(data, 0, vsize);
|
||||
auto buffer = new gpu::Buffer(vsize, (gpu::Byte*)data);
|
||||
delete[] data;
|
||||
auto output = gpu::BufferView(buffer, input._element);
|
||||
qCInfo(model_scripting) << "resized output" << output.getNumElements() << output._buffer->getSize();
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
bool ModelScriptingInterface::replaceMeshData(scriptable::ScriptableMeshPointer dest, scriptable::ScriptableMeshPointer src, const QVector<QString>& attributeNames) {
|
||||
auto target = getMeshPointer(dest);
|
||||
auto source = getMeshPointer(src);
|
||||
if (!target || !source) {
|
||||
context()->throwError("ModelScriptingInterface::replaceMeshData -- expected dest and src to be valid mesh proxy pointers");
|
||||
return false;
|
||||
}
|
||||
|
||||
QVector<QString> attributes = attributeNames.isEmpty() ? src->getAttributeNames() : attributeNames;
|
||||
|
||||
//qCInfo(model_scripting) << "ModelScriptingInterface::replaceMeshData -- source:" << source->displayName << "target:" << target->displayName << "attributes:" << attributes;
|
||||
|
||||
// remove attributes only found on target mesh, unless user has explicitly specified the relevant attribute names
|
||||
if (attributeNames.isEmpty()) {
|
||||
auto attributeViews = ScriptableMesh::gatherBufferViews(target);
|
||||
for (const auto& a : attributeViews) {
|
||||
auto slot = ScriptableMesh::ATTRIBUTES[a.first];
|
||||
if (!attributes.contains(a.first)) {
|
||||
//qCInfo(model_scripting) << "ModelScriptingInterface::replaceMeshData -- pruning target attribute" << a.first << slot;
|
||||
target->removeAttribute(slot);
|
||||
}
|
||||
QScriptValue meshPointerToScriptValue(QScriptEngine* engine, scriptable::ScriptableMeshPointer const &in) {
|
||||
if (!in) {
|
||||
return QScriptValue::NullValue;
|
||||
}
|
||||
return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects);
|
||||
}
|
||||
|
||||
target->setVertexBuffer(cloneBufferView(source->getVertexBuffer()));
|
||||
target->setIndexBuffer(cloneBufferView(source->getIndexBuffer()));
|
||||
target->setPartBuffer(cloneBufferView(source->getPartBuffer()));
|
||||
|
||||
for (const auto& a : attributes) {
|
||||
auto slot = ScriptableMesh::ATTRIBUTES[a];
|
||||
if (slot == gpu::Stream::POSITION) {
|
||||
continue;
|
||||
}
|
||||
// auto& before = target->getAttributeBuffer(slot);
|
||||
auto& input = source->getAttributeBuffer(slot);
|
||||
if (input.getNumElements() == 0) {
|
||||
//qCInfo(model_scripting) << "ModelScriptingInterface::replaceMeshData buffer is empty -- pruning" << a << slot;
|
||||
target->removeAttribute(slot);
|
||||
} else {
|
||||
// if (before.getNumElements() == 0) {
|
||||
// qCInfo(model_scripting) << "ModelScriptingInterface::replaceMeshData target buffer is empty -- adding" << a << slot;
|
||||
// } else {
|
||||
// qCInfo(model_scripting) << "ModelScriptingInterface::replaceMeshData target buffer exists -- updating" << a << slot;
|
||||
// }
|
||||
target->addAttribute(slot, cloneBufferView(input));
|
||||
}
|
||||
// auto& after = target->getAttributeBuffer(slot);
|
||||
// qCInfo(model_scripting) << "ModelScriptingInterface::replaceMeshData" << a << slot << before.getNumElements() << " -> " << after.getNumElements();
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ModelScriptingInterface::dedupeVertices(scriptable::ScriptableMeshPointer meshProxy, float epsilon) {
|
||||
auto mesh = getMeshPointer(meshProxy);
|
||||
if (!mesh) {
|
||||
return false;
|
||||
}
|
||||
auto positions = mesh->getVertexBuffer();
|
||||
auto numPositions = positions.getNumElements();
|
||||
const auto epsilon2 = epsilon*epsilon;
|
||||
|
||||
QVector<glm::vec3> uniqueVerts;
|
||||
uniqueVerts.reserve((int)numPositions);
|
||||
QMap<quint32,quint32> remapIndices;
|
||||
|
||||
for (quint32 i = 0; i < numPositions; i++) {
|
||||
const quint32 numUnique = uniqueVerts.size();
|
||||
const auto& position = positions.get<glm::vec3>(i);
|
||||
bool unique = true;
|
||||
for (quint32 j = 0; j < numUnique; j++) {
|
||||
if (glm::length2(uniqueVerts[j] - position) <= epsilon2) {
|
||||
remapIndices[i] = j;
|
||||
unique = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (unique) {
|
||||
uniqueVerts << position;
|
||||
remapIndices[i] = numUnique;
|
||||
}
|
||||
}
|
||||
|
||||
qCInfo(model_scripting) << "//VERTS before" << numPositions << "after" << uniqueVerts.size();
|
||||
|
||||
auto indices = mesh->getIndexBuffer();
|
||||
auto numIndices = indices.getNumElements();
|
||||
auto esize = indices._element.getSize();
|
||||
QVector<quint32> newIndices;
|
||||
newIndices.reserve((int)numIndices);
|
||||
for (quint32 i = 0; i < numIndices; i++) {
|
||||
quint32 index = esize == 4 ? indices.get<quint32>(i) : indices.get<quint16>(i);
|
||||
if (remapIndices.contains(index)) {
|
||||
//qCInfo(model_scripting) << i << index << "->" << remapIndices[index];
|
||||
newIndices << remapIndices[index];
|
||||
} else {
|
||||
qCInfo(model_scripting) << i << index << "!remapIndices[index]";
|
||||
}
|
||||
}
|
||||
|
||||
mesh->setIndexBuffer(bufferViewFromVector(newIndices, { gpu::SCALAR, gpu::UINT32, gpu::INDEX }));
|
||||
mesh->setVertexBuffer(bufferViewFromVector(uniqueVerts, { gpu::VEC3, gpu::FLOAT, gpu::XYZ }));
|
||||
|
||||
auto attributeViews = ScriptableMesh::gatherBufferViews(mesh);
|
||||
quint32 numUniqueVerts = uniqueVerts.size();
|
||||
for (const auto& a : attributeViews) {
|
||||
auto& view = a.second;
|
||||
auto slot = ScriptableMesh::ATTRIBUTES[a.first];
|
||||
if (slot == gpu::Stream::POSITION) {
|
||||
continue;
|
||||
}
|
||||
qCInfo(model_scripting) << "ModelScriptingInterface::dedupeVertices" << a.first << slot << view.getNumElements();
|
||||
auto newView = resizedBufferView(view, numUniqueVerts);
|
||||
qCInfo(model_scripting) << a.first << "before: #" << view.getNumElements() << "after: #" << newView.getNumElements();
|
||||
quint32 numElements = (quint32)view.getNumElements();
|
||||
for (quint32 i = 0; i < numElements; i++) {
|
||||
quint32 fromVertexIndex = i;
|
||||
quint32 toVertexIndex = remapIndices.contains(fromVertexIndex) ? remapIndices[fromVertexIndex] : fromVertexIndex;
|
||||
bufferViewElementFromVariant(
|
||||
newView, toVertexIndex,
|
||||
bufferViewElementToVariant(view, fromVertexIndex, false, "dedupe")
|
||||
);
|
||||
}
|
||||
mesh->addAttribute(slot, newView);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QScriptValue ModelScriptingInterface::cloneMesh(scriptable::ScriptableMeshPointer meshProxy, bool recalcNormals) {
|
||||
auto mesh = getMeshPointer(meshProxy);
|
||||
if (!mesh) {
|
||||
return QScriptValue::NullValue;
|
||||
}
|
||||
graphics::MeshPointer clone(new graphics::Mesh());
|
||||
clone->displayName = mesh->displayName + "-clone";
|
||||
qCInfo(model_scripting) << "ModelScriptingInterface::cloneMesh" << !!mesh<< !!meshProxy;
|
||||
if (!mesh) {
|
||||
return QScriptValue::NullValue;
|
||||
}
|
||||
|
||||
clone->setIndexBuffer(cloneBufferView(mesh->getIndexBuffer()));
|
||||
clone->setPartBuffer(cloneBufferView(mesh->getPartBuffer()));
|
||||
auto attributeViews = ScriptableMesh::gatherBufferViews(mesh);
|
||||
for (const auto& a : attributeViews) {
|
||||
auto& view = a.second;
|
||||
auto slot = ScriptableMesh::ATTRIBUTES[a.first];
|
||||
qCInfo(model_scripting) << "ModelScriptingInterface::cloneVertices buffer" << a.first << slot;
|
||||
auto points = cloneBufferView(view);
|
||||
qCInfo(model_scripting) << "ModelScriptingInterface::cloneVertices source" << view.getNumElements();
|
||||
qCInfo(model_scripting) << "ModelScriptingInterface::cloneVertices dest" << points.getNumElements();
|
||||
if (slot == gpu::Stream::POSITION) {
|
||||
clone->setVertexBuffer(points);
|
||||
} else {
|
||||
clone->addAttribute(slot, points);
|
||||
}
|
||||
}
|
||||
|
||||
auto result = scriptable::ScriptableMeshPointer(new ScriptableMesh(nullptr, clone));
|
||||
if (recalcNormals) {
|
||||
recalculateNormals(result);
|
||||
}
|
||||
return engine()->toScriptValue(result);
|
||||
}
|
||||
|
||||
bool ModelScriptingInterface::recalculateNormals(scriptable::ScriptableMeshPointer meshProxy) {
|
||||
qCInfo(model_scripting) << "Recalculating normals" << !!meshProxy;
|
||||
auto mesh = getMeshPointer(meshProxy);
|
||||
if (!mesh) {
|
||||
return false;
|
||||
}
|
||||
ScriptableMesh::gatherBufferViews(mesh, { "normal", "color" }); // ensures #normals >= #positions
|
||||
auto normals = mesh->getAttributeBuffer(gpu::Stream::NORMAL);
|
||||
auto verts = mesh->getVertexBuffer();
|
||||
auto indices = mesh->getIndexBuffer();
|
||||
auto esize = indices._element.getSize();
|
||||
auto numPoints = indices.getNumElements();
|
||||
const auto TRIANGLE = 3;
|
||||
quint32 numFaces = (quint32)numPoints / TRIANGLE;
|
||||
//QVector<Triangle> faces;
|
||||
QVector<glm::vec3> faceNormals;
|
||||
QMap<QString,QVector<quint32>> vertexToFaces;
|
||||
//faces.resize(numFaces);
|
||||
faceNormals.resize(numFaces);
|
||||
auto numNormals = normals.getNumElements();
|
||||
qCInfo(model_scripting) << QString("numFaces: %1, numNormals: %2, numPoints: %3").arg(numFaces).arg(numNormals).arg(numPoints);
|
||||
if (normals.getNumElements() != verts.getNumElements()) {
|
||||
return false;
|
||||
}
|
||||
for (quint32 i = 0; i < numFaces; i++) {
|
||||
quint32 I = TRIANGLE * i;
|
||||
quint32 i0 = esize == 4 ? indices.get<quint32>(I+0) : indices.get<quint16>(I+0);
|
||||
quint32 i1 = esize == 4 ? indices.get<quint32>(I+1) : indices.get<quint16>(I+1);
|
||||
quint32 i2 = esize == 4 ? indices.get<quint32>(I+2) : indices.get<quint16>(I+2);
|
||||
|
||||
Triangle face = {
|
||||
verts.get<glm::vec3>(i1),
|
||||
verts.get<glm::vec3>(i2),
|
||||
verts.get<glm::vec3>(i0)
|
||||
};
|
||||
faceNormals[i] = face.getNormal();
|
||||
if (glm::isnan(faceNormals[i].x)) {
|
||||
qCInfo(model_scripting) << i << i0 << i1 << i2 << vec3toVariant(face.v0) << vec3toVariant(face.v1) << vec3toVariant(face.v2);
|
||||
break;
|
||||
}
|
||||
vertexToFaces[glm::to_string(face.v0).c_str()] << i;
|
||||
vertexToFaces[glm::to_string(face.v1).c_str()] << i;
|
||||
vertexToFaces[glm::to_string(face.v2).c_str()] << i;
|
||||
}
|
||||
for (quint32 j = 0; j < numNormals; j++) {
|
||||
//auto v = verts.get<glm::vec3>(j);
|
||||
glm::vec3 normal { 0.0f, 0.0f, 0.0f };
|
||||
QString key { glm::to_string(verts.get<glm::vec3>(j)).c_str() };
|
||||
const auto& faces = vertexToFaces.value(key);
|
||||
if (faces.size()) {
|
||||
for (const auto i : faces) {
|
||||
normal += faceNormals[i];
|
||||
}
|
||||
normal *= 1.0f / (float)faces.size();
|
||||
} else {
|
||||
static int logged = 0;
|
||||
if (logged++ < 10) {
|
||||
qCInfo(model_scripting) << "no faces for key!?" << key;
|
||||
}
|
||||
normal = verts.get<glm::vec3>(j);
|
||||
}
|
||||
if (glm::isnan(normal.x)) {
|
||||
static int logged = 0;
|
||||
if (logged++ < 10) {
|
||||
qCInfo(model_scripting) << "isnan(normal.x)" << j << vec3toVariant(normal);
|
||||
}
|
||||
break;
|
||||
}
|
||||
normals.edit<glm::vec3>(j) = glm::normalize(normal);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QScriptValue ModelScriptingInterface::mapMeshAttributeValues(
|
||||
scriptable::ScriptableMeshPointer meshProxy, QScriptValue scopeOrCallback, QScriptValue methodOrName
|
||||
) {
|
||||
auto mesh = getMeshPointer(meshProxy);
|
||||
if (!mesh) {
|
||||
return false;
|
||||
}
|
||||
auto scopedHandler = makeScopedHandlerObject(scopeOrCallback, methodOrName);
|
||||
|
||||
// input buffers
|
||||
gpu::BufferView positions = mesh->getVertexBuffer();
|
||||
|
||||
const auto nPositions = positions.getNumElements();
|
||||
|
||||
// destructure so we can still invoke callback scoped, but with a custom signature (obj, i, jsMesh)
|
||||
auto scope = scopedHandler.property("scope");
|
||||
auto callback = scopedHandler.property("callback");
|
||||
auto js = engine(); // cache value to avoid resolving each iteration
|
||||
auto meshPart = js->toScriptValue(meshProxy);
|
||||
|
||||
auto obj = js->newObject();
|
||||
auto attributeViews = ScriptableMesh::gatherBufferViews(mesh, { "normal", "color" });
|
||||
for (uint32_t i=0; i < nPositions; i++) {
|
||||
for (const auto& a : attributeViews) {
|
||||
bool asArray = a.second._element.getType() != gpu::FLOAT;
|
||||
obj.setProperty(a.first, bufferViewElementToScriptValue(js, a.second, i, asArray, a.first.toStdString().c_str()));
|
||||
}
|
||||
auto result = callback.call(scope, { obj, i, meshPart });
|
||||
if (js->hasUncaughtException()) {
|
||||
context()->throwValue(js->uncaughtException());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result.isBool() && !result.toBool()) {
|
||||
// bail without modifying data if user explicitly returns false
|
||||
continue;
|
||||
}
|
||||
if (result.isObject() && !result.strictlyEquals(obj)) {
|
||||
// user returned a new object (ie: instead of modifying input properties)
|
||||
obj = result;
|
||||
}
|
||||
|
||||
for (const auto& a : attributeViews) {
|
||||
const auto& attribute = obj.property(a.first);
|
||||
auto& view = a.second;
|
||||
if (attribute.isValid()) {
|
||||
bufferViewElementFromScriptValue(attribute, view, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return thisObject();
|
||||
}
|
||||
|
||||
void ModelScriptingInterface::getMeshes(QUuid uuid, QScriptValue scopeOrCallback, QScriptValue methodOrName) {
|
||||
auto handler = makeScopedHandlerObject(scopeOrCallback, methodOrName);
|
||||
Q_ASSERT(handler.engine() == this->engine());
|
||||
QPointer<BaseScriptEngine> engine = dynamic_cast<BaseScriptEngine*>(handler.engine());
|
||||
|
||||
scriptable::ScriptableModel meshes;
|
||||
bool success = false;
|
||||
QString error;
|
||||
|
||||
auto appProvider = DependencyManager::get<scriptable::ModelProviderFactory>();
|
||||
qDebug() << "appProvider" << appProvider.data();
|
||||
scriptable::ModelProviderPointer provider = appProvider ? appProvider->lookupModelProvider(uuid) : nullptr;
|
||||
QString providerType = provider ? provider->metadata.value("providerType").toString() : QString();
|
||||
if (providerType.isEmpty()) {
|
||||
providerType = "unknown";
|
||||
}
|
||||
if (provider) {
|
||||
qCDebug(model_scripting) << "fetching meshes from " << providerType << "...";
|
||||
auto scriptableMeshes = provider->getScriptableModel(&success);
|
||||
qCDebug(model_scripting) << "//fetched meshes from " << providerType << "success:" <<success << "#" << scriptableMeshes.meshes.size();
|
||||
if (success) {
|
||||
meshes = scriptableMeshes;//SimpleModelProxy::fromScriptableModel(scriptableMeshes);
|
||||
}
|
||||
}
|
||||
if (!success) {
|
||||
error = QString("failed to get meshes from %1 provider for uuid %2").arg(providerType).arg(uuid.toString());
|
||||
}
|
||||
|
||||
if (!error.isEmpty()) {
|
||||
qCWarning(model_scripting) << "ModelScriptingInterface::getMeshes ERROR" << error;
|
||||
callScopedHandlerObject(handler, engine->makeError(error), QScriptValue::NullValue);
|
||||
} else {
|
||||
callScopedHandlerObject(handler, QScriptValue::NullValue, engine->toScriptValue(meshes));
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
QScriptValue meshToScriptValue(QScriptEngine* engine, scriptable::ScriptableMeshPointer const &in) {
|
||||
return engine->newQObject(in.get(), QScriptEngine::QtOwnership,
|
||||
QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects
|
||||
);
|
||||
}
|
||||
|
||||
void meshFromScriptValue(const QScriptValue& value, scriptable::ScriptableMeshPointer &out) {
|
||||
void meshPointerFromScriptValue(const QScriptValue& value, scriptable::ScriptableMeshPointer &out) {
|
||||
auto obj = value.toQObject();
|
||||
//qDebug() << "meshFromScriptValue" << obj;
|
||||
qDebug() << "meshPointerFromScriptValue" << obj;
|
||||
if (auto tmp = qobject_cast<scriptable::ScriptableMesh*>(obj)) {
|
||||
out = tmp->shared_from_this();
|
||||
out = tmp;
|
||||
}
|
||||
// FIXME: Why does above cast not work on Win32!?
|
||||
if (!out) {
|
||||
auto smp = static_cast<scriptable::ScriptableMesh*>(obj);
|
||||
//qDebug() << "meshFromScriptValue2" << smp;
|
||||
out = smp->shared_from_this();
|
||||
if (auto smp = static_cast<scriptable::ScriptableMesh*>(obj)) {
|
||||
qDebug() << "meshPointerFromScriptValue2" << smp;
|
||||
out = smp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QScriptValue meshesToScriptValue(QScriptEngine* engine, const scriptable::ScriptableModelPointer &in) {
|
||||
// QScriptValueList result;
|
||||
QScriptValue result = engine->newArray();
|
||||
int i = 0;
|
||||
foreach(scriptable::ScriptableMeshPointer const meshProxy, in->getMeshes()) {
|
||||
result.setProperty(i++, meshToScriptValue(engine, meshProxy));
|
||||
}
|
||||
return result;
|
||||
QScriptValue modelPointerToScriptValue(QScriptEngine* engine, const scriptable::ScriptableModelPointer &in) {
|
||||
return engine->newQObject(in, QScriptEngine::QtOwnership, QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects);
|
||||
// QScriptValue result = engine->newArray();
|
||||
// int i = 0;
|
||||
// foreach(auto& mesh, in->getMeshes()) {
|
||||
// result.setProperty(i++, meshPointerToScriptValue(engine, mesh));
|
||||
// }
|
||||
// return result;
|
||||
}
|
||||
|
||||
void meshesFromScriptValue(const QScriptValue& value, scriptable::ScriptableModelPointer &out) {
|
||||
void modelPointerFromScriptValue(const QScriptValue& value, scriptable::ScriptableModelPointer &out) {
|
||||
const auto length = value.property("length").toInt32();
|
||||
qCDebug(model_scripting) << "in meshesFromScriptValue, length =" << length;
|
||||
qCDebug(model_scripting) << "in modelPointerFromScriptValue, length =" << length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
if (const auto meshProxy = qobject_cast<scriptable::ScriptableMesh*>(value.property(i).toQObject())) {
|
||||
out->meshes.append(meshProxy->getMeshPointer());
|
||||
|
@ -749,79 +349,64 @@ namespace {
|
|||
}
|
||||
}
|
||||
|
||||
void modelProxyFromScriptValue(const QScriptValue& object, scriptable::ScriptableModel &meshes) {
|
||||
auto meshesProperty = object.property("meshes");
|
||||
if (meshesProperty.property("length").toInt32() > 0) {
|
||||
//meshes._meshes = qobject_cast<scriptable::ScriptableModelPointer>(meshesProperty.toQObject());
|
||||
// qDebug() << "modelProxyFromScriptValue" << meshesProperty.property("length").toInt32() << meshesProperty.toVariant().typeName();
|
||||
qScriptValueToSequence(meshesProperty, meshes.meshes);
|
||||
} else if (auto mesh = qobject_cast<scriptable::ScriptableMesh*>(object.toQObject())) {
|
||||
meshes.meshes << mesh->getMeshPointer();
|
||||
} else {
|
||||
qDebug() << "modelProxyFromScriptValue -- unrecognized input" << object.toVariant().toString();
|
||||
}
|
||||
// FIXME: MESHFACES:
|
||||
// QScriptValue meshFaceToScriptValue(QScriptEngine* engine, const mesh::MeshFace &meshFace) {
|
||||
// QScriptValue obj = engine->newObject();
|
||||
// obj.setProperty("vertices", qVectorIntToScriptValue(engine, meshFace.vertexIndices));
|
||||
// return obj;
|
||||
// }
|
||||
// void meshFaceFromScriptValue(const QScriptValue &object, mesh::MeshFace& meshFaceResult) {
|
||||
// qScriptValueToSequence(object.property("vertices"), meshFaceResult.vertexIndices);
|
||||
// }
|
||||
// QScriptValue qVectorMeshFaceToScriptValue(QScriptEngine* engine, const QVector<mesh::MeshFace>& vector) {
|
||||
// return qScriptValueFromSequence(engine, vector);
|
||||
// }
|
||||
// void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector<mesh::MeshFace>& result) {
|
||||
// qScriptValueToSequence(array, result);
|
||||
// }
|
||||
|
||||
meshes.metadata = object.property("metadata").toVariant().toMap();
|
||||
}
|
||||
|
||||
QScriptValue modelProxyToScriptValue(QScriptEngine* engine, const scriptable::ScriptableModel &in) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("meshes", qScriptValueFromSequence(engine, in.meshes));
|
||||
obj.setProperty("metadata", engine->toScriptValue(in.metadata));
|
||||
return obj;
|
||||
}
|
||||
|
||||
QScriptValue meshFaceToScriptValue(QScriptEngine* engine, const mesh::MeshFace &meshFace) {
|
||||
QScriptValue obj = engine->newObject();
|
||||
obj.setProperty("vertices", qVectorIntToScriptValue(engine, meshFace.vertexIndices));
|
||||
return obj;
|
||||
}
|
||||
|
||||
void meshFaceFromScriptValue(const QScriptValue &object, mesh::MeshFace& meshFaceResult) {
|
||||
qScriptValueToSequence(object.property("vertices"), meshFaceResult.vertexIndices);
|
||||
}
|
||||
|
||||
QScriptValue qVectorMeshFaceToScriptValue(QScriptEngine* engine, const QVector<mesh::MeshFace>& vector) {
|
||||
QScriptValue qVectorUInt32ToScriptValue(QScriptEngine* engine, const QVector<quint32>& vector) {
|
||||
return qScriptValueFromSequence(engine, vector);
|
||||
}
|
||||
|
||||
void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector<mesh::MeshFace>& result) {
|
||||
qScriptValueToSequence(array, result);
|
||||
}
|
||||
|
||||
QScriptValue qVectorUInt32ToScriptValue(QScriptEngine* engine, const QVector<mesh::uint32>& vector) {
|
||||
return qScriptValueFromSequence(engine, vector);
|
||||
}
|
||||
|
||||
void qVectorUInt32FromScriptValue(const QScriptValue& array, QVector<mesh::uint32>& result) {
|
||||
void qVectorUInt32FromScriptValue(const QScriptValue& array, QVector<quint32>& result) {
|
||||
qScriptValueToSequence(array, result);
|
||||
}
|
||||
}
|
||||
|
||||
int meshUint32 = qRegisterMetaType<mesh::uint32>();
|
||||
int meshUint32 = qRegisterMetaType<quint32>();
|
||||
namespace mesh {
|
||||
int meshUint32 = qRegisterMetaType<uint32>();
|
||||
}
|
||||
int qVectorMeshUint32 = qRegisterMetaType<QVector<mesh::uint32>>();
|
||||
int qVectorMeshUint32 = qRegisterMetaType<QVector<quint32>>();
|
||||
|
||||
void ModelScriptingInterface::registerMetaTypes(QScriptEngine* engine) {
|
||||
qScriptRegisterSequenceMetaType<QVector<scriptable::ScriptableMeshPointer>>(engine);
|
||||
qScriptRegisterSequenceMetaType<mesh::MeshFaces>(engine);
|
||||
qScriptRegisterSequenceMetaType<QVector<mesh::uint32>>(engine);
|
||||
qScriptRegisterMetaType(engine, modelProxyToScriptValue, modelProxyFromScriptValue);
|
||||
qScriptRegisterSequenceMetaType<QVector<quint32>>(engine);
|
||||
|
||||
qScriptRegisterMetaType(engine, qVectorUInt32ToScriptValue, qVectorUInt32FromScriptValue);
|
||||
qScriptRegisterMetaType(engine, meshToScriptValue, meshFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, meshesToScriptValue, meshesFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, meshFaceToScriptValue, meshFaceFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, qVectorMeshFaceToScriptValue, qVectorMeshFaceFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, meshPointerToScriptValue, meshPointerFromScriptValue);
|
||||
qScriptRegisterMetaType(engine, modelPointerToScriptValue, modelPointerFromScriptValue);
|
||||
|
||||
// FIXME: MESHFACES: remove if MeshFace is not needed anywhere
|
||||
// qScriptRegisterSequenceMetaType<mesh::MeshFaces>(engine);
|
||||
// qScriptRegisterMetaType(engine, meshFaceToScriptValue, meshFaceFromScriptValue);
|
||||
// qScriptRegisterMetaType(engine, qVectorMeshFaceToScriptValue, qVectorMeshFaceFromScriptValue);
|
||||
}
|
||||
|
||||
MeshPointer ModelScriptingInterface::getMeshPointer(const scriptable::ScriptableMesh& meshProxy) {
|
||||
return meshProxy._mesh;//getMeshPointer(&meshProxy);
|
||||
}
|
||||
MeshPointer ModelScriptingInterface::getMeshPointer(scriptable::ScriptableMesh& meshProxy) {
|
||||
return getMeshPointer(&meshProxy);
|
||||
}
|
||||
MeshPointer ModelScriptingInterface::getMeshPointer(scriptable::ScriptableMeshPointer meshProxy) {
|
||||
MeshPointer result;
|
||||
if (!meshProxy) {
|
||||
if (context()){
|
||||
context()->throwError("expected meshProxy as first parameter");
|
||||
} else {
|
||||
qDebug() << "expected meshProxy as first parameter";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -829,6 +414,8 @@ MeshPointer ModelScriptingInterface::getMeshPointer(scriptable::ScriptableMeshPo
|
|||
if (!mesh) {
|
||||
if (context()) {
|
||||
context()->throwError("expected valid meshProxy as first parameter");
|
||||
} else {
|
||||
qDebug() << "expected valid meshProxy as first parameter";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -38,30 +38,20 @@ public slots:
|
|||
*/
|
||||
void getMeshes(QUuid uuid, QScriptValue scopeOrCallback, QScriptValue methodOrName = QScriptValue());
|
||||
|
||||
bool dedupeVertices(scriptable::ScriptableMeshPointer meshProxy, float epsilon = 1e-6);
|
||||
bool recalculateNormals(scriptable::ScriptableMeshPointer meshProxy);
|
||||
QScriptValue cloneMesh(scriptable::ScriptableMeshPointer meshProxy, bool recalcNormals = true);
|
||||
QScriptValue unrollVertices(scriptable::ScriptableMeshPointer meshProxy, bool recalcNormals = true);
|
||||
QScriptValue mapAttributeValues(QScriptValue in,
|
||||
QScriptValue scopeOrCallback,
|
||||
QScriptValue methodOrName = QScriptValue());
|
||||
QScriptValue mapMeshAttributeValues(scriptable::ScriptableMeshPointer meshProxy,
|
||||
QScriptValue scopeOrCallback,
|
||||
QScriptValue methodOrName = QScriptValue());
|
||||
|
||||
QString meshToOBJ(const scriptable::ScriptableModel& in);
|
||||
|
||||
bool replaceMeshData(scriptable::ScriptableMeshPointer dest, scriptable::ScriptableMeshPointer source, const QVector<QString>& attributeNames = QVector<QString>());
|
||||
QScriptValue appendMeshes(scriptable::ScriptableModel in);
|
||||
QScriptValue transformMesh(scriptable::ScriptableMeshPointer meshProxy, glm::mat4 transform);
|
||||
QScriptValue newMesh(const QVector<glm::vec3>& vertices,
|
||||
const QVector<glm::vec3>& normals,
|
||||
const QVector<mesh::MeshFace>& faces);
|
||||
QScriptValue getVertexCount(scriptable::ScriptableMeshPointer meshProxy);
|
||||
QScriptValue getVertex(scriptable::ScriptableMeshPointer meshProxy, mesh::uint32 vertexIndex);
|
||||
QScriptValue getVertex(scriptable::ScriptableMeshPointer meshProxy, quint32 vertexIndex);
|
||||
|
||||
private:
|
||||
scriptable::MeshPointer getMeshPointer(scriptable::ScriptableMeshPointer meshProxy);
|
||||
scriptable::MeshPointer getMeshPointer(scriptable::ScriptableMesh& meshProxy);
|
||||
scriptable::MeshPointer getMeshPointer(const scriptable::ScriptableMesh& meshProxy);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -14,14 +14,20 @@
|
|||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/transform.hpp>
|
||||
#include <glm/gtx/norm.hpp>
|
||||
#include <glm/gtx/string_cast.hpp>
|
||||
#include <graphics/Geometry.h>
|
||||
#include <graphics-scripting/DebugNames.h>
|
||||
#include <graphics-scripting/BufferViewHelpers.h>
|
||||
#include <graphics-scripting/BufferViewScripting.h>
|
||||
#include <Extents.h>
|
||||
|
||||
#include "ScriptableMesh.moc"
|
||||
|
||||
#include <RegisteredMetaTypes.h>
|
||||
#include <BaseScriptEngine.h>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
#include "OBJWriter.h"
|
||||
|
||||
QLoggingCategory mesh_logging { "hifi.scripting.mesh" };
|
||||
|
||||
|
@ -41,10 +47,26 @@ QMap<QString,int> ScriptableMesh::ATTRIBUTES{
|
|||
{"texcoord4", gpu::Stream::TEXCOORD4 },
|
||||
};
|
||||
|
||||
QVector<scriptable::ScriptableMeshPointer> scriptable::ScriptableModel::getMeshes() const {
|
||||
|
||||
QString scriptable::ScriptableModel::toString() const {
|
||||
return QString("[ScriptableModel%1%2]")
|
||||
.arg(objectID.isNull() ? "" : " objectID="+objectID.toString())
|
||||
.arg(objectName().isEmpty() ? "" : " name=" +objectName());
|
||||
}
|
||||
|
||||
const QVector<scriptable::ScriptableMeshPointer> scriptable::ScriptableModel::getConstMeshes() const {
|
||||
QVector<scriptable::ScriptableMeshPointer> out;
|
||||
for(const auto& mesh : meshes) {
|
||||
const scriptable::ScriptableMeshPointer m = scriptable::ScriptableMeshPointer(new scriptable::ScriptableMesh(const_cast<scriptable::ScriptableModel*>(this), mesh));
|
||||
out << m;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
QVector<scriptable::ScriptableMeshPointer> scriptable::ScriptableModel::getMeshes() {
|
||||
QVector<scriptable::ScriptableMeshPointer> out;
|
||||
for(auto& mesh : meshes) {
|
||||
out << scriptable::ScriptableMeshPointer(new ScriptableMesh(std::const_pointer_cast<scriptable::ScriptableModel>(this->shared_from_this()), mesh));
|
||||
scriptable::ScriptableMeshPointer m{new scriptable::ScriptableMesh(this, mesh)};
|
||||
out << m;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
@ -134,15 +156,16 @@ QVariantMap ScriptableMesh::getVertexAttributes(quint32 vertexIndex) const {
|
|||
}
|
||||
|
||||
bool ScriptableMesh::setVertexAttributes(quint32 vertexIndex, QVariantMap attributes) {
|
||||
qDebug() << "setVertexAttributes" << vertexIndex << attributes;
|
||||
//qDebug() << "setVertexAttributes" << vertexIndex << attributes;
|
||||
for (auto& a : gatherBufferViews(getMeshPointer())) {
|
||||
const auto& name = a.first;
|
||||
const auto& value = attributes.value(name);
|
||||
if (value.isValid()) {
|
||||
auto& view = a.second;
|
||||
//qCDebug(mesh_logging) << "setVertexAttributes" << vertexIndex << name;
|
||||
bufferViewElementFromVariant(view, vertexIndex, value);
|
||||
} else {
|
||||
qCDebug(mesh_logging) << "setVertexAttributes" << vertexIndex << name;
|
||||
//qCDebug(mesh_logging) << "(skipping) setVertexAttributes" << vertexIndex << name;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
@ -357,3 +380,375 @@ std::map<QString, gpu::BufferView> ScriptableMesh::gatherBufferViews(scriptable:
|
|||
}
|
||||
return attributeViews;
|
||||
}
|
||||
|
||||
QScriptValue ScriptableModel::mapAttributeValues(QScriptValue scopeOrCallback, QScriptValue methodOrName) {
|
||||
auto context = scopeOrCallback.engine()->currentContext();
|
||||
auto _in = context->thisObject();
|
||||
qCInfo(mesh_logging) << "mapAttributeValues" << _in.toVariant().typeName() << _in.toVariant().toString() << _in.toQObject();
|
||||
auto model = qscriptvalue_cast<scriptable::ScriptableModel>(_in);
|
||||
QVector<scriptable::ScriptableMeshPointer> in = model.getMeshes();
|
||||
if (in.size()) {
|
||||
foreach (scriptable::ScriptableMeshPointer meshProxy, in) {
|
||||
meshProxy->mapAttributeValues(scopeOrCallback, methodOrName);
|
||||
}
|
||||
return _in;
|
||||
} else if (auto meshProxy = qobject_cast<scriptable::ScriptableMesh*>(_in.toQObject())) {
|
||||
return meshProxy->mapAttributeValues(scopeOrCallback, methodOrName);
|
||||
} else {
|
||||
context->throwError("invalid ModelProxy || MeshProxyPointer");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
QScriptValue ScriptableMesh::mapAttributeValues(QScriptValue scopeOrCallback, QScriptValue methodOrName) {
|
||||
auto mesh = getMeshPointer();
|
||||
if (!mesh) {
|
||||
return false;
|
||||
}
|
||||
auto scopedHandler = makeScopedHandlerObject(scopeOrCallback, methodOrName);
|
||||
|
||||
// input buffers
|
||||
gpu::BufferView positions = mesh->getVertexBuffer();
|
||||
|
||||
const auto nPositions = positions.getNumElements();
|
||||
|
||||
// destructure so we can still invoke callback scoped, but with a custom signature (obj, i, jsMesh)
|
||||
auto scope = scopedHandler.property("scope");
|
||||
auto callback = scopedHandler.property("callback");
|
||||
auto js = engine(); // cache value to avoid resolving each iteration
|
||||
auto meshPart = thisObject();//js->toScriptValue(meshProxy);
|
||||
|
||||
auto obj = js->newObject();
|
||||
auto attributeViews = ScriptableMesh::gatherBufferViews(mesh, { "normal", "color" });
|
||||
for (uint32_t i=0; i < nPositions; i++) {
|
||||
for (const auto& a : attributeViews) {
|
||||
bool asArray = a.second._element.getType() != gpu::FLOAT;
|
||||
obj.setProperty(a.first, bufferViewElementToScriptValue(js, a.second, i, asArray, a.first.toStdString().c_str()));
|
||||
}
|
||||
auto result = callback.call(scope, { obj, i, meshPart });
|
||||
if (js->hasUncaughtException()) {
|
||||
context()->throwValue(js->uncaughtException());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result.isBool() && !result.toBool()) {
|
||||
// bail without modifying data if user explicitly returns false
|
||||
continue;
|
||||
}
|
||||
if (result.isObject() && !result.strictlyEquals(obj)) {
|
||||
// user returned a new object (ie: instead of modifying input properties)
|
||||
obj = result;
|
||||
}
|
||||
|
||||
for (const auto& a : attributeViews) {
|
||||
const auto& attribute = obj.property(a.first);
|
||||
auto& view = a.second;
|
||||
if (attribute.isValid()) {
|
||||
bufferViewElementFromScriptValue(attribute, view, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return thisObject();
|
||||
}
|
||||
|
||||
QScriptValue ScriptableMesh::unrollVertices(bool recalcNormals) {
|
||||
auto meshProxy = this;
|
||||
auto mesh = getMeshPointer();
|
||||
qCInfo(mesh_logging) << "ScriptableMesh::unrollVertices" << !!mesh<< !!meshProxy;
|
||||
if (!mesh) {
|
||||
return QScriptValue();
|
||||
}
|
||||
|
||||
auto positions = mesh->getVertexBuffer();
|
||||
auto indices = mesh->getIndexBuffer();
|
||||
quint32 numPoints = (quint32)indices.getNumElements();
|
||||
auto buffer = new gpu::Buffer();
|
||||
buffer->resize(numPoints * sizeof(uint32_t));
|
||||
auto newindices = gpu::BufferView(buffer, { gpu::SCALAR, gpu::UINT32, gpu::INDEX });
|
||||
qCInfo(mesh_logging) << "ScriptableMesh::unrollVertices numPoints" << numPoints;
|
||||
auto attributeViews = ScriptableMesh::gatherBufferViews(mesh);
|
||||
for (const auto& a : attributeViews) {
|
||||
auto& view = a.second;
|
||||
auto sz = view._element.getSize();
|
||||
auto buffer = new gpu::Buffer();
|
||||
buffer->resize(numPoints * sz);
|
||||
auto points = gpu::BufferView(buffer, view._element);
|
||||
auto src = (uint8_t*)view._buffer->getData();
|
||||
auto dest = (uint8_t*)points._buffer->getData();
|
||||
auto slot = ScriptableMesh::ATTRIBUTES[a.first];
|
||||
qCInfo(mesh_logging) << "ScriptableMesh::unrollVertices buffer" << a.first;
|
||||
qCInfo(mesh_logging) << "ScriptableMesh::unrollVertices source" << view.getNumElements();
|
||||
qCInfo(mesh_logging) << "ScriptableMesh::unrollVertices dest" << points.getNumElements();
|
||||
qCInfo(mesh_logging) << "ScriptableMesh::unrollVertices sz" << sz << src << dest << slot;
|
||||
auto esize = indices._element.getSize();
|
||||
const char* hint= a.first.toStdString().c_str();
|
||||
for(quint32 i = 0; i < numPoints; i++) {
|
||||
quint32 index = esize == 4 ? indices.get<quint32>(i) : indices.get<quint16>(i);
|
||||
newindices.edit<uint32_t>(i) = i;
|
||||
bufferViewElementFromVariant(
|
||||
points, i,
|
||||
bufferViewElementToVariant(view, index, false, hint)
|
||||
);
|
||||
}
|
||||
if (slot == gpu::Stream::POSITION) {
|
||||
mesh->setVertexBuffer(points);
|
||||
} else {
|
||||
mesh->addAttribute(slot, points);
|
||||
}
|
||||
}
|
||||
mesh->setIndexBuffer(newindices);
|
||||
if (recalcNormals) {
|
||||
recalculateNormals();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScriptableMesh::replaceMeshData(scriptable::ScriptableMeshPointer src, const QVector<QString>& attributeNames) {
|
||||
auto target = getMeshPointer();
|
||||
auto source = src ? src->getMeshPointer() : nullptr;
|
||||
if (!target || !source) {
|
||||
context()->throwError("ScriptableMesh::replaceMeshData -- expected dest and src to be valid mesh proxy pointers");
|
||||
return false;
|
||||
}
|
||||
|
||||
QVector<QString> attributes = attributeNames.isEmpty() ? src->getAttributeNames() : attributeNames;
|
||||
|
||||
//qCInfo(mesh_logging) << "ScriptableMesh::replaceMeshData -- source:" << source->displayName << "target:" << target->displayName << "attributes:" << attributes;
|
||||
|
||||
// remove attributes only found on target mesh, unless user has explicitly specified the relevant attribute names
|
||||
if (attributeNames.isEmpty()) {
|
||||
auto attributeViews = ScriptableMesh::gatherBufferViews(target);
|
||||
for (const auto& a : attributeViews) {
|
||||
auto slot = ScriptableMesh::ATTRIBUTES[a.first];
|
||||
if (!attributes.contains(a.first)) {
|
||||
//qCInfo(mesh_logging) << "ScriptableMesh::replaceMeshData -- pruning target attribute" << a.first << slot;
|
||||
target->removeAttribute(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
target->setVertexBuffer(cloneBufferView(source->getVertexBuffer()));
|
||||
target->setIndexBuffer(cloneBufferView(source->getIndexBuffer()));
|
||||
target->setPartBuffer(cloneBufferView(source->getPartBuffer()));
|
||||
|
||||
for (const auto& a : attributes) {
|
||||
auto slot = ScriptableMesh::ATTRIBUTES[a];
|
||||
if (slot == gpu::Stream::POSITION) {
|
||||
continue;
|
||||
}
|
||||
// auto& before = target->getAttributeBuffer(slot);
|
||||
auto& input = source->getAttributeBuffer(slot);
|
||||
if (input.getNumElements() == 0) {
|
||||
//qCInfo(mesh_logging) << "ScriptableMesh::replaceMeshData buffer is empty -- pruning" << a << slot;
|
||||
target->removeAttribute(slot);
|
||||
} else {
|
||||
// if (before.getNumElements() == 0) {
|
||||
// qCInfo(mesh_logging) << "ScriptableMesh::replaceMeshData target buffer is empty -- adding" << a << slot;
|
||||
// } else {
|
||||
// qCInfo(mesh_logging) << "ScriptableMesh::replaceMeshData target buffer exists -- updating" << a << slot;
|
||||
// }
|
||||
target->addAttribute(slot, cloneBufferView(input));
|
||||
}
|
||||
// auto& after = target->getAttributeBuffer(slot);
|
||||
// qCInfo(mesh_logging) << "ScriptableMesh::replaceMeshData" << a << slot << before.getNumElements() << " -> " << after.getNumElements();
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScriptableMesh::dedupeVertices(float epsilon) {
|
||||
scriptable::ScriptableMeshPointer meshProxy = this;
|
||||
auto mesh = getMeshPointer();
|
||||
if (!mesh) {
|
||||
return false;
|
||||
}
|
||||
auto positions = mesh->getVertexBuffer();
|
||||
auto numPositions = positions.getNumElements();
|
||||
const auto epsilon2 = epsilon*epsilon;
|
||||
|
||||
QVector<glm::vec3> uniqueVerts;
|
||||
uniqueVerts.reserve((int)numPositions);
|
||||
QMap<quint32,quint32> remapIndices;
|
||||
|
||||
for (quint32 i = 0; i < numPositions; i++) {
|
||||
const quint32 numUnique = uniqueVerts.size();
|
||||
const auto& position = positions.get<glm::vec3>(i);
|
||||
bool unique = true;
|
||||
for (quint32 j = 0; j < numUnique; j++) {
|
||||
if (glm::length2(uniqueVerts[j] - position) <= epsilon2) {
|
||||
remapIndices[i] = j;
|
||||
unique = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (unique) {
|
||||
uniqueVerts << position;
|
||||
remapIndices[i] = numUnique;
|
||||
}
|
||||
}
|
||||
|
||||
qCInfo(mesh_logging) << "//VERTS before" << numPositions << "after" << uniqueVerts.size();
|
||||
|
||||
auto indices = mesh->getIndexBuffer();
|
||||
auto numIndices = indices.getNumElements();
|
||||
auto esize = indices._element.getSize();
|
||||
QVector<quint32> newIndices;
|
||||
newIndices.reserve((int)numIndices);
|
||||
for (quint32 i = 0; i < numIndices; i++) {
|
||||
quint32 index = esize == 4 ? indices.get<quint32>(i) : indices.get<quint16>(i);
|
||||
if (remapIndices.contains(index)) {
|
||||
//qCInfo(mesh_logging) << i << index << "->" << remapIndices[index];
|
||||
newIndices << remapIndices[index];
|
||||
} else {
|
||||
qCInfo(mesh_logging) << i << index << "!remapIndices[index]";
|
||||
}
|
||||
}
|
||||
|
||||
mesh->setIndexBuffer(bufferViewFromVector(newIndices, { gpu::SCALAR, gpu::UINT32, gpu::INDEX }));
|
||||
mesh->setVertexBuffer(bufferViewFromVector(uniqueVerts, { gpu::VEC3, gpu::FLOAT, gpu::XYZ }));
|
||||
|
||||
auto attributeViews = ScriptableMesh::gatherBufferViews(mesh);
|
||||
quint32 numUniqueVerts = uniqueVerts.size();
|
||||
for (const auto& a : attributeViews) {
|
||||
auto& view = a.second;
|
||||
auto slot = ScriptableMesh::ATTRIBUTES[a.first];
|
||||
if (slot == gpu::Stream::POSITION) {
|
||||
continue;
|
||||
}
|
||||
qCInfo(mesh_logging) << "ScriptableMesh::dedupeVertices" << a.first << slot << view.getNumElements();
|
||||
auto newView = resizedBufferView(view, numUniqueVerts);
|
||||
qCInfo(mesh_logging) << a.first << "before: #" << view.getNumElements() << "after: #" << newView.getNumElements();
|
||||
quint32 numElements = (quint32)view.getNumElements();
|
||||
for (quint32 i = 0; i < numElements; i++) {
|
||||
quint32 fromVertexIndex = i;
|
||||
quint32 toVertexIndex = remapIndices.contains(fromVertexIndex) ? remapIndices[fromVertexIndex] : fromVertexIndex;
|
||||
bufferViewElementFromVariant(
|
||||
newView, toVertexIndex,
|
||||
bufferViewElementToVariant(view, fromVertexIndex, false, "dedupe")
|
||||
);
|
||||
}
|
||||
mesh->addAttribute(slot, newView);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QScriptValue ScriptableMesh::cloneMesh(bool recalcNormals) {
|
||||
auto mesh = getMeshPointer();
|
||||
if (!mesh) {
|
||||
return QScriptValue::NullValue;
|
||||
}
|
||||
graphics::MeshPointer clone(new graphics::Mesh());
|
||||
clone->displayName = mesh->displayName + "-clone";
|
||||
qCInfo(mesh_logging) << "ScriptableMesh::cloneMesh" << !!mesh;
|
||||
if (!mesh) {
|
||||
return QScriptValue::NullValue;
|
||||
}
|
||||
|
||||
clone->setIndexBuffer(cloneBufferView(mesh->getIndexBuffer()));
|
||||
clone->setPartBuffer(cloneBufferView(mesh->getPartBuffer()));
|
||||
auto attributeViews = ScriptableMesh::gatherBufferViews(mesh);
|
||||
for (const auto& a : attributeViews) {
|
||||
auto& view = a.second;
|
||||
auto slot = ScriptableMesh::ATTRIBUTES[a.first];
|
||||
qCInfo(mesh_logging) << "ScriptableMesh::cloneVertices buffer" << a.first << slot;
|
||||
auto points = cloneBufferView(view);
|
||||
qCInfo(mesh_logging) << "ScriptableMesh::cloneVertices source" << view.getNumElements();
|
||||
qCInfo(mesh_logging) << "ScriptableMesh::cloneVertices dest" << points.getNumElements();
|
||||
if (slot == gpu::Stream::POSITION) {
|
||||
clone->setVertexBuffer(points);
|
||||
} else {
|
||||
clone->addAttribute(slot, points);
|
||||
}
|
||||
}
|
||||
|
||||
auto result = scriptable::ScriptableMeshPointer(new ScriptableMesh(nullptr, clone));
|
||||
if (recalcNormals) {
|
||||
result->recalculateNormals();
|
||||
}
|
||||
return engine()->toScriptValue(result);
|
||||
}
|
||||
|
||||
bool ScriptableMesh::recalculateNormals() {
|
||||
scriptable::ScriptableMeshPointer meshProxy = this;
|
||||
qCInfo(mesh_logging) << "Recalculating normals" << !!meshProxy;
|
||||
auto mesh = getMeshPointer();
|
||||
if (!mesh) {
|
||||
return false;
|
||||
}
|
||||
ScriptableMesh::gatherBufferViews(mesh, { "normal", "color" }); // ensures #normals >= #positions
|
||||
auto normals = mesh->getAttributeBuffer(gpu::Stream::NORMAL);
|
||||
auto verts = mesh->getVertexBuffer();
|
||||
auto indices = mesh->getIndexBuffer();
|
||||
auto esize = indices._element.getSize();
|
||||
auto numPoints = indices.getNumElements();
|
||||
const auto TRIANGLE = 3;
|
||||
quint32 numFaces = (quint32)numPoints / TRIANGLE;
|
||||
//QVector<Triangle> faces;
|
||||
QVector<glm::vec3> faceNormals;
|
||||
QMap<QString,QVector<quint32>> vertexToFaces;
|
||||
//faces.resize(numFaces);
|
||||
faceNormals.resize(numFaces);
|
||||
auto numNormals = normals.getNumElements();
|
||||
qCInfo(mesh_logging) << QString("numFaces: %1, numNormals: %2, numPoints: %3").arg(numFaces).arg(numNormals).arg(numPoints);
|
||||
if (normals.getNumElements() != verts.getNumElements()) {
|
||||
return false;
|
||||
}
|
||||
for (quint32 i = 0; i < numFaces; i++) {
|
||||
quint32 I = TRIANGLE * i;
|
||||
quint32 i0 = esize == 4 ? indices.get<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(mesh_logging) << i << i0 << i1 << i2 << vec3toVariant(face.v0) << vec3toVariant(face.v1) << vec3toVariant(face.v2);
|
||||
break;
|
||||
}
|
||||
vertexToFaces[glm::to_string(face.v0).c_str()] << i;
|
||||
vertexToFaces[glm::to_string(face.v1).c_str()] << i;
|
||||
vertexToFaces[glm::to_string(face.v2).c_str()] << i;
|
||||
}
|
||||
for (quint32 j = 0; j < numNormals; j++) {
|
||||
//auto v = verts.get<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(mesh_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(mesh_logging) << "isnan(normal.x)" << j << vec3toVariant(normal);
|
||||
}
|
||||
break;
|
||||
}
|
||||
normals.edit<glm::vec3>(j) = glm::normalize(normal);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
QString ScriptableMesh::toOBJ() {
|
||||
if (!getMeshPointer()) {
|
||||
context()->throwError(QString("null mesh"));
|
||||
}
|
||||
return writeOBJToString({ getMeshPointer() });
|
||||
}
|
||||
|
||||
|
|
|
@ -6,12 +6,16 @@
|
|||
#include <QtCore/QList>
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtCore/QUuid>
|
||||
#include <QPointer>
|
||||
#include <memory>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
||||
#include <graphics-scripting/ScriptableModel.h>
|
||||
|
||||
#include <QtScript/QScriptable>
|
||||
#include <QtScript/QScriptValue>
|
||||
|
||||
namespace graphics {
|
||||
class Mesh;
|
||||
}
|
||||
|
@ -19,91 +23,112 @@ namespace gpu {
|
|||
class BufferView;
|
||||
}
|
||||
namespace scriptable {
|
||||
class ScriptableMesh : public QObject, public std::enable_shared_from_this<ScriptableMesh> {
|
||||
class ScriptableMeshPart;
|
||||
using ScriptableMeshPartPointer = QPointer<ScriptableMeshPart>;
|
||||
class ScriptableMesh : public QObject, QScriptable {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ScriptableModelPointer _model;
|
||||
scriptable::MeshPointer _mesh;
|
||||
QVariantMap _metadata;
|
||||
ScriptableMesh() : QObject() {}
|
||||
ScriptableMesh(ScriptableModelPointer parent, scriptable::MeshPointer mesh) : QObject(), _model(parent), _mesh(mesh) {}
|
||||
ScriptableMesh(const ScriptableMesh& other) : QObject(), _model(other._model), _mesh(other._mesh), _metadata(other._metadata) {}
|
||||
~ScriptableMesh() { qDebug() << "~ScriptableMesh" << this; }
|
||||
Q_PROPERTY(quint32 numParts READ getNumParts)
|
||||
Q_PROPERTY(quint32 numAttributes READ getNumAttributes)
|
||||
Q_PROPERTY(quint32 numVertices READ getNumVertices)
|
||||
Q_PROPERTY(quint32 numIndices READ getNumIndices)
|
||||
Q_PROPERTY(QVariantMap metadata MEMBER _metadata)
|
||||
Q_PROPERTY(QVector<QString> attributeNames READ getAttributeNames)
|
||||
|
||||
virtual scriptable::MeshPointer getMeshPointer() const { return _mesh; }
|
||||
Q_INVOKABLE virtual quint32 getNumParts() const;
|
||||
Q_INVOKABLE virtual quint32 getNumVertices() const;
|
||||
Q_INVOKABLE virtual quint32 getNumAttributes() const;
|
||||
Q_INVOKABLE virtual quint32 getNumIndices() const { return 0; }
|
||||
Q_INVOKABLE virtual QVector<QString> getAttributeNames() const;
|
||||
Q_INVOKABLE virtual QVariantMap getVertexAttributes(quint32 vertexIndex) const;
|
||||
Q_INVOKABLE virtual QVariantMap getVertexAttributes(quint32 vertexIndex, QVector<QString> attributes) const;
|
||||
|
||||
Q_INVOKABLE virtual QVector<quint32> getIndices() const;
|
||||
Q_INVOKABLE virtual QVector<quint32> findNearbyIndices(const glm::vec3& origin, float epsilon = 1e-6) const;
|
||||
Q_INVOKABLE virtual QVariantMap getMeshExtents() const;
|
||||
Q_INVOKABLE virtual bool setVertexAttributes(quint32 vertexIndex, QVariantMap attributes);
|
||||
Q_INVOKABLE virtual QVariantMap scaleToFit(float unitScale);
|
||||
static QMap<QString,int> ATTRIBUTES;
|
||||
static std::map<QString, gpu::BufferView> gatherBufferViews(MeshPointer mesh, const QStringList& expandToMatchPositions = QStringList());
|
||||
|
||||
static QMap<QString,int> ATTRIBUTES;
|
||||
static std::map<QString, gpu::BufferView> gatherBufferViews(MeshPointer mesh, const QStringList& expandToMatchPositions = QStringList());
|
||||
ScriptableMesh& operator=(const ScriptableMesh& other) { _model=other._model; _mesh=other._mesh; _metadata=other._metadata; return *this; };
|
||||
ScriptableMesh() : QObject(), _model(nullptr) {}
|
||||
ScriptableMesh(ScriptableModelPointer parent, scriptable::MeshPointer mesh) : QObject(), _model(parent), _mesh(mesh) {}
|
||||
ScriptableMesh(const ScriptableMesh& other) : QObject(), _model(other._model), _mesh(other._mesh), _metadata(other._metadata) {}
|
||||
~ScriptableMesh() { qDebug() << "~ScriptableMesh" << this; }
|
||||
|
||||
Q_INVOKABLE QVariantList getAttributeValues(const QString& attributeName) const;
|
||||
scriptable::MeshPointer getMeshPointer() const { return _mesh; }
|
||||
public slots:
|
||||
quint32 getNumParts() const;
|
||||
quint32 getNumVertices() const;
|
||||
quint32 getNumAttributes() const;
|
||||
quint32 getNumIndices() const { return 0; }
|
||||
QVector<QString> getAttributeNames() const;
|
||||
|
||||
Q_INVOKABLE int _getSlotNumber(const QString& attributeName) const;
|
||||
QVariantMap getVertexAttributes(quint32 vertexIndex) const;
|
||||
QVariantMap getVertexAttributes(quint32 vertexIndex, QVector<QString> attributes) const;
|
||||
|
||||
QVariantMap translate(const glm::vec3& translation);
|
||||
QVariantMap scale(const glm::vec3& scale, const glm::vec3& origin = glm::vec3(NAN));
|
||||
QVariantMap rotateDegrees(const glm::vec3& eulerAngles, const glm::vec3& origin = glm::vec3(NAN));
|
||||
QVariantMap rotate(const glm::quat& rotation, const glm::vec3& origin = glm::vec3(NAN));
|
||||
Q_INVOKABLE QVariantMap transform(const glm::mat4& transform);
|
||||
QVector<quint32> getIndices() const;
|
||||
QVector<quint32> findNearbyIndices(const glm::vec3& origin, float epsilon = 1e-6) const;
|
||||
QVariantMap getMeshExtents() const;
|
||||
bool setVertexAttributes(quint32 vertexIndex, QVariantMap attributes);
|
||||
QVariantMap scaleToFit(float unitScale);
|
||||
|
||||
QVariantList getAttributeValues(const QString& attributeName) const;
|
||||
|
||||
int _getSlotNumber(const QString& attributeName) const;
|
||||
|
||||
QVariantMap translate(const glm::vec3& translation);
|
||||
QVariantMap scale(const glm::vec3& scale, const glm::vec3& origin = glm::vec3(NAN));
|
||||
QVariantMap rotateDegrees(const glm::vec3& eulerAngles, const glm::vec3& origin = glm::vec3(NAN));
|
||||
QVariantMap rotate(const glm::quat& rotation, const glm::vec3& origin = glm::vec3(NAN));
|
||||
QVariantMap transform(const glm::mat4& transform);
|
||||
|
||||
public:
|
||||
operator bool() const { return _mesh != nullptr; }
|
||||
ScriptableModelPointer _model;
|
||||
scriptable::MeshPointer _mesh;
|
||||
QVariantMap _metadata;
|
||||
|
||||
public slots:
|
||||
// QScriptEngine-specific wrappers
|
||||
QScriptValue mapAttributeValues(QScriptValue scopeOrCallback, QScriptValue methodOrName = QScriptValue());
|
||||
bool dedupeVertices(float epsilon = 1e-6);
|
||||
bool recalculateNormals();
|
||||
QScriptValue cloneMesh(bool recalcNormals = true);
|
||||
QScriptValue unrollVertices(bool recalcNormals = true);
|
||||
bool replaceMeshData(scriptable::ScriptableMeshPointer source, const QVector<QString>& attributeNames = QVector<QString>());
|
||||
QString toOBJ();
|
||||
};
|
||||
|
||||
// TODO: for now this is a part-specific wrapper around ScriptableMesh
|
||||
class ScriptableMeshPart : public ScriptableMesh {
|
||||
// TODO: part-specific wrapper for working with raw geometries
|
||||
class ScriptableMeshPart : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ScriptableMeshPart& operator=(const ScriptableMeshPart& view) { _model=view._model; _mesh=view._mesh; return *this; };
|
||||
ScriptableMeshPart(const ScriptableMeshPart& other) : ScriptableMesh(other._model, other._mesh) {}
|
||||
ScriptableMeshPart() : ScriptableMesh(nullptr, nullptr) {}
|
||||
~ScriptableMeshPart() { qDebug() << "~ScriptableMeshPart" << this; }
|
||||
ScriptableMeshPart(ScriptableMeshPointer mesh) : ScriptableMesh(mesh->_model, mesh->_mesh) {}
|
||||
Q_PROPERTY(QString topology READ getTopology)
|
||||
Q_PROPERTY(quint32 numFaces READ getNumFaces)
|
||||
|
||||
scriptable::MeshPointer parentMesh;
|
||||
int partIndex;
|
||||
QString getTopology() const { return "triangles"; }
|
||||
Q_INVOKABLE virtual quint32 getNumFaces() const { return getIndices().size() / 3; }
|
||||
Q_INVOKABLE virtual QVector<quint32> getFace(quint32 faceIndex) const {
|
||||
auto inds = getIndices();
|
||||
ScriptableMeshPart& operator=(const ScriptableMeshPart& view) { parentMesh=view.parentMesh; return *this; };
|
||||
ScriptableMeshPart(const ScriptableMeshPart& other) : parentMesh(other.parentMesh) {}
|
||||
ScriptableMeshPart() {}
|
||||
~ScriptableMeshPart() { qDebug() << "~ScriptableMeshPart" << this; }
|
||||
|
||||
public slots:
|
||||
QString getTopology() const { return "triangles"; }
|
||||
quint32 getNumFaces() const { return parentMesh.getIndices().size() / 3; }
|
||||
QVector<quint32> getFace(quint32 faceIndex) const {
|
||||
auto inds = parentMesh.getIndices();
|
||||
return faceIndex+2 < (quint32)inds.size() ? inds.mid(faceIndex*3, 3) : QVector<quint32>();
|
||||
}
|
||||
|
||||
public:
|
||||
scriptable::ScriptableMesh parentMesh;
|
||||
int partIndex;
|
||||
};
|
||||
|
||||
class GraphicsScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
GraphicsScriptingInterface(QObject* parent = nullptr) : QObject(parent) {}
|
||||
GraphicsScriptingInterface(const GraphicsScriptingInterface& other) {}
|
||||
GraphicsScriptingInterface(const GraphicsScriptingInterface& other) {}
|
||||
public slots:
|
||||
ScriptableMeshPart exportMeshPart(ScriptableMesh mesh, int part) { return {}; }
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(scriptable::ScriptableMesh)
|
||||
Q_DECLARE_METATYPE(scriptable::ScriptableMeshPointer)
|
||||
Q_DECLARE_METATYPE(QVector<scriptable::ScriptableMeshPointer>)
|
||||
Q_DECLARE_METATYPE(scriptable::ScriptableMeshPart)
|
||||
Q_DECLARE_METATYPE(scriptable::ScriptableMeshPartPointer)
|
||||
Q_DECLARE_METATYPE(scriptable::GraphicsScriptingInterface)
|
||||
|
||||
// FIXME: faces were supported in the original Model.* API -- are they still needed/used/useful for anything yet?
|
||||
// FIXME: MESHFACES: faces were supported in the original Model.* API -- are they still needed/used/useful for anything yet?
|
||||
#include <memory>
|
||||
|
||||
namespace mesh {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <QtCore/QList>
|
||||
#include <QtCore/QVariant>
|
||||
#include <QtCore/QUuid>
|
||||
#include <QPointer>
|
||||
#include <memory>
|
||||
|
||||
#include <DependencyManager.h>
|
||||
|
@ -16,50 +17,53 @@ namespace graphics {
|
|||
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;
|
||||
class ScriptableMeshPart;
|
||||
using ScriptableModelPointer = std::shared_ptr<scriptable::ScriptableModel>;
|
||||
using ScriptableMeshPointer = std::shared_ptr<scriptable::ScriptableMesh>;
|
||||
using ScriptableMeshPartPointer = std::shared_ptr<scriptable::ScriptableMeshPart>;
|
||||
class ScriptableModel : public QObject, public std::enable_shared_from_this<ScriptableModel> {
|
||||
using ScriptableMeshPointer = QPointer<ScriptableMesh>;
|
||||
|
||||
// abstract container for holding one or more scriptable meshes
|
||||
class ScriptableModel : public QObject {
|
||||
Q_OBJECT
|
||||
public:
|
||||
Q_PROPERTY(QVector<scriptable::ScriptableMeshPointer> meshes READ getMeshes)
|
||||
|
||||
Q_INVOKABLE QString toString() { return "[ScriptableModel " + objectName()+"]"; }
|
||||
ScriptableModel(QObject* parent = nullptr) : QObject(parent) {}
|
||||
ScriptableModel(const ScriptableModel& other) : objectID(other.objectID), metadata(other.metadata), meshes(other.meshes) {}
|
||||
ScriptableModel& operator=(const ScriptableModel& view) {
|
||||
objectID = view.objectID;
|
||||
metadata = view.metadata;
|
||||
meshes = view.meshes;
|
||||
return *this;
|
||||
}
|
||||
~ScriptableModel() { qDebug() << "~ScriptableModel" << this; }
|
||||
void mixin(const ScriptableModel& other) {
|
||||
for (const auto& key : other.metadata.keys()) {
|
||||
metadata[key] = other.metadata[key];
|
||||
}
|
||||
for(const auto&mesh : other.meshes) {
|
||||
meshes << mesh;
|
||||
}
|
||||
}
|
||||
QUuid objectID;
|
||||
QVariantMap metadata;
|
||||
QVector<scriptable::MeshPointer> meshes;
|
||||
// TODO: in future accessors for these could go here
|
||||
QVariantMap shapes;
|
||||
QVariantMap materials;
|
||||
QVariantMap armature;
|
||||
|
||||
QVector<scriptable::ScriptableMeshPointer> getMeshes() const;
|
||||
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;
|
||||
|
||||
ScriptableModel(QObject* parent = nullptr) : QObject(parent) {}
|
||||
ScriptableModel(const ScriptableModel& other) : objectID(other.objectID), metadata(other.metadata), meshes(other.meshes) {}
|
||||
ScriptableModel& operator=(const ScriptableModel& view) { objectID = view.objectID; metadata = view.metadata; meshes = view.meshes; return *this; }
|
||||
~ScriptableModel() { qDebug() << "~ScriptableModel" << this; }
|
||||
|
||||
void mixin(const ScriptableModel& other) {
|
||||
for (const auto& key : other.metadata.keys()) { metadata[key] = other.metadata[key]; }
|
||||
for (const auto& mesh : other.meshes) { meshes << mesh; }
|
||||
}
|
||||
|
||||
// TODO: in future accessors for these could go here
|
||||
// QVariantMap shapes;
|
||||
// QVariantMap materials;
|
||||
// QVariantMap armature;
|
||||
|
||||
QVector<scriptable::ScriptableMeshPointer> getMeshes();
|
||||
const QVector<scriptable::ScriptableMeshPointer> getConstMeshes() const;
|
||||
|
||||
// QScriptEngine-specific wrappers
|
||||
Q_INVOKABLE QScriptValue mapAttributeValues(QScriptValue scopeOrCallback, QScriptValue methodOrName);
|
||||
};
|
||||
|
||||
// mixin class for Avatar/Entity/Overlay Rendering that expose their in-memory graphics::Meshes
|
||||
class ModelProvider {
|
||||
public:
|
||||
QVariantMap metadata;
|
||||
|
@ -67,11 +71,12 @@ namespace scriptable {
|
|||
virtual scriptable::ScriptableModel getScriptableModel(bool* ok = nullptr) = 0;
|
||||
};
|
||||
using ModelProviderPointer = std::shared_ptr<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)
|
||||
|
|
|
@ -579,38 +579,9 @@ scriptable::ScriptableModel Model::getScriptableModel(bool* ok) {
|
|||
|
||||
if (!isLoaded()) {
|
||||
qDebug() << "Model::getScriptableModel -- !isLoaded";
|
||||
if (ok) {
|
||||
*ok = false;
|
||||
}
|
||||
return result;
|
||||
return scriptable::ModelProvider::modelUnavailableError(ok);
|
||||
}
|
||||
|
||||
// TODO: remove -- this was an earlier approach using renderGeometry instead of FBXGeometry
|
||||
#if 0 // renderGeometry approach
|
||||
const Geometry::GeometryMeshes& meshes = renderGeometry->getMeshes();
|
||||
Transform offset;
|
||||
offset.setScale(_scale);
|
||||
offset.postTranslate(_offset);
|
||||
glm::mat4 offsetMat = offset.getMatrix();
|
||||
|
||||
for (std::shared_ptr<const graphics::Mesh> mesh : meshes) {
|
||||
if (!mesh) {
|
||||
continue;
|
||||
}
|
||||
qDebug() << "Model::getScriptableModel #" << i++ << mesh->displayName;
|
||||
auto newmesh = mesh->map(
|
||||
[=](glm::vec3 position) {
|
||||
return glm::vec3(offsetMat * glm::vec4(position, 1.0f));
|
||||
},
|
||||
[=](glm::vec3 color) { return color; },
|
||||
[=](glm::vec3 normal) {
|
||||
return glm::normalize(glm::vec3(offsetMat * glm::vec4(normal, 0.0f)));
|
||||
},
|
||||
[&](uint32_t index) { return index; });
|
||||
newmesh->displayName = mesh->displayName;
|
||||
result << newmesh;
|
||||
}
|
||||
#endif
|
||||
const FBXGeometry& geometry = getFBXGeometry();
|
||||
auto mat4toVariant = [](const glm::mat4& mat4) -> QVariant {
|
||||
QVector<float> floats;
|
||||
|
@ -659,6 +630,33 @@ scriptable::ScriptableModel Model::getScriptableModel(bool* ok) {
|
|||
qDebug() << "//Model::getScriptableModel -- #" << result.meshes.size();
|
||||
result.metadata["submeshes"] = submeshes;
|
||||
return result;
|
||||
|
||||
// TODO: remove -- this was an earlier approach using renderGeometry instead of FBXGeometry
|
||||
#if 0 // renderGeometry approach
|
||||
const Geometry::GeometryMeshes& meshes = renderGeometry->getMeshes();
|
||||
Transform offset;
|
||||
offset.setScale(_scale);
|
||||
offset.postTranslate(_offset);
|
||||
glm::mat4 offsetMat = offset.getMatrix();
|
||||
|
||||
for (std::shared_ptr<const graphics::Mesh> mesh : meshes) {
|
||||
if (!mesh) {
|
||||
continue;
|
||||
}
|
||||
qDebug() << "Model::getScriptableModel #" << i++ << mesh->displayName;
|
||||
auto newmesh = mesh->map(
|
||||
[=](glm::vec3 position) {
|
||||
return glm::vec3(offsetMat * glm::vec4(position, 1.0f));
|
||||
},
|
||||
[=](glm::vec3 color) { return color; },
|
||||
[=](glm::vec3 normal) {
|
||||
return glm::normalize(glm::vec3(offsetMat * glm::vec4(normal, 0.0f)));
|
||||
},
|
||||
[&](uint32_t index) { return index; });
|
||||
newmesh->displayName = mesh->displayName;
|
||||
result << newmesh;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Model::calculateTriangleSets() {
|
||||
|
|
Loading…
Reference in a new issue