mirror of
https://github.com/overte-org/overte.git
synced 2025-04-12 03:45:08 +02:00
Merge pull request #9885 from sethalves/model-scripting
Model scripting
This commit is contained in:
commit
a75acd139b
15 changed files with 379 additions and 22 deletions
|
@ -177,6 +177,8 @@
|
|||
#include "FrameTimingsScriptingInterface.h"
|
||||
#include <GPUIdent.h>
|
||||
#include <gl/GLHelpers.h>
|
||||
#include <EntityScriptClient.h>
|
||||
#include <ModelScriptingInterface.h>
|
||||
|
||||
// On Windows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
|
||||
// FIXME seems to be broken.
|
||||
|
|
|
@ -72,6 +72,8 @@
|
|||
|
||||
#include <procedural/ProceduralSkybox.h>
|
||||
#include <model/Skybox.h>
|
||||
#include <ModelScriptingInterface.h>
|
||||
|
||||
|
||||
class OffscreenGLCanvas;
|
||||
class GLCanvas;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <QByteArray>
|
||||
#include <QtConcurrent/QtConcurrentRun>
|
||||
#include <glm/gtx/transform.hpp>
|
||||
#include "ModelScriptingInterface.h"
|
||||
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
#pragma GCC diagnostic push
|
||||
|
@ -73,7 +74,7 @@ const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5;
|
|||
_meshDirty
|
||||
|
||||
In RenderablePolyVoxEntityItem::render, these flags are checked and changes are propagated along the chain.
|
||||
decompressVolumeData() is called to decompress _voxelData into _volData. getMesh() is called to invoke the
|
||||
decompressVolumeData() is called to decompress _voxelData into _volData. recomputeMesh() is called to invoke the
|
||||
polyVox surface extractor to create _mesh (as well as set Simulation _dirtyFlags). Because Simulation::DIRTY_SHAPE
|
||||
is set, isReadyToComputeShape() gets called and _shape is created either from _volData or _shape, depending on
|
||||
the surface style.
|
||||
|
@ -81,7 +82,7 @@ const float MARCHING_CUBE_COLLISION_HULL_OFFSET = 0.5;
|
|||
When a script changes _volData, compressVolumeDataAndSendEditPacket is called to update _voxelData and to
|
||||
send a packet to the entity-server.
|
||||
|
||||
decompressVolumeData, getMesh, computeShapeInfoWorker, and compressVolumeDataAndSendEditPacket are too expensive
|
||||
decompressVolumeData, recomputeMesh, computeShapeInfoWorker, and compressVolumeDataAndSendEditPacket are too expensive
|
||||
to run on a thread that has other things to do. These use QtConcurrent::run to spawn a thread. As each thread
|
||||
finishes, it adjusts the dirty flags so that the next call to render() will kick off the next step.
|
||||
|
||||
|
@ -682,7 +683,7 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
|
|||
if (voxelDataDirty) {
|
||||
decompressVolumeData();
|
||||
} else if (volDataDirty) {
|
||||
getMesh();
|
||||
recomputeMesh();
|
||||
}
|
||||
|
||||
model::MeshPointer mesh;
|
||||
|
@ -1199,7 +1200,7 @@ void RenderablePolyVoxEntityItem::copyUpperEdgesFromNeighbors() {
|
|||
}
|
||||
}
|
||||
|
||||
void RenderablePolyVoxEntityItem::getMesh() {
|
||||
void RenderablePolyVoxEntityItem::recomputeMesh() {
|
||||
// use _volData to make a renderable mesh
|
||||
PolyVoxSurfaceStyle voxelSurfaceStyle;
|
||||
withReadLock([&] {
|
||||
|
@ -1269,12 +1270,20 @@ void RenderablePolyVoxEntityItem::getMesh() {
|
|||
vertexBufferPtr->getSize() ,
|
||||
sizeof(PolyVox::PositionMaterialNormal),
|
||||
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW)));
|
||||
|
||||
std::vector<model::Mesh::Part> parts;
|
||||
parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex
|
||||
(model::Index)vecIndices.size(), // numIndices
|
||||
(model::Index)0, // baseVertex
|
||||
model::Mesh::TRIANGLES)); // topology
|
||||
mesh->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part),
|
||||
(gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL));
|
||||
entity->setMesh(mesh);
|
||||
});
|
||||
}
|
||||
|
||||
void RenderablePolyVoxEntityItem::setMesh(model::MeshPointer mesh) {
|
||||
// this catches the payload from getMesh
|
||||
// this catches the payload from recomputeMesh
|
||||
bool neighborsNeedUpdate;
|
||||
withWriteLock([&] {
|
||||
if (!_collisionless) {
|
||||
|
@ -1531,7 +1540,6 @@ std::shared_ptr<RenderablePolyVoxEntityItem> RenderablePolyVoxEntityItem::getZPN
|
|||
return std::dynamic_pointer_cast<RenderablePolyVoxEntityItem>(_zPNeighbor.lock());
|
||||
}
|
||||
|
||||
|
||||
void RenderablePolyVoxEntityItem::bonkNeighbors() {
|
||||
// flag neighbors to the negative of this entity as needing to rebake their meshes.
|
||||
cacheNeighbors();
|
||||
|
@ -1551,7 +1559,6 @@ void RenderablePolyVoxEntityItem::bonkNeighbors() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void RenderablePolyVoxEntityItem::locationChanged(bool tellPhysics) {
|
||||
EntityItem::locationChanged(tellPhysics);
|
||||
if (!_pipeline || !render::Item::isValidID(_myItem)) {
|
||||
|
@ -1563,3 +1570,17 @@ void RenderablePolyVoxEntityItem::locationChanged(bool tellPhysics) {
|
|||
|
||||
scene->enqueuePendingChanges(pendingChanges);
|
||||
}
|
||||
|
||||
bool RenderablePolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) const {
|
||||
bool success = false;
|
||||
MeshProxy* meshProxy = nullptr;
|
||||
model::MeshPointer mesh = nullptr;
|
||||
withReadLock([&] {
|
||||
if (_meshInitialized) {
|
||||
success = true;
|
||||
meshProxy = new MeshProxy(_mesh);
|
||||
}
|
||||
});
|
||||
result = meshToScriptValue(engine, meshProxy);
|
||||
return success;
|
||||
}
|
||||
|
|
|
@ -133,6 +133,7 @@ public:
|
|||
QByteArray volDataToArray(quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize) const;
|
||||
|
||||
void setMesh(model::MeshPointer mesh);
|
||||
bool getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) const override;
|
||||
void setCollisionPoints(ShapeInfo::PointCollection points, AABox box);
|
||||
PolyVox::SimpleVolume<uint8_t>* getVolData() { return _volData; }
|
||||
|
||||
|
@ -167,7 +168,7 @@ private:
|
|||
ShapeInfo _shapeInfo;
|
||||
|
||||
PolyVox::SimpleVolume<uint8_t>* _volData = nullptr;
|
||||
bool _volDataDirty = false; // does getMesh need to be called?
|
||||
bool _volDataDirty = false; // does recomputeMesh need to be called?
|
||||
int _onCount; // how many non-zero voxels are in _volData
|
||||
|
||||
bool _neighborsNeedUpdate { false };
|
||||
|
@ -178,7 +179,7 @@ private:
|
|||
// these are run off the main thread
|
||||
void decompressVolumeData();
|
||||
void compressVolumeDataAndSendEditPacket();
|
||||
virtual void getMesh() override; // recompute mesh
|
||||
virtual void recomputeMesh() override; // recompute mesh
|
||||
void computeShapeInfoWorker();
|
||||
|
||||
// these are cached lookups of _xNNeighborID, _yNNeighborID, _zNNeighborID, _xPNeighborID, _yPNeighborID, _zPNeighborID
|
||||
|
|
|
@ -815,8 +815,7 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra
|
|||
}
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::setVoxels(QUuid entityID,
|
||||
std::function<bool(PolyVoxEntityItem&)> actor) {
|
||||
bool EntityScriptingInterface::polyVoxWorker(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
if (!_entityTree) {
|
||||
|
@ -882,11 +881,9 @@ bool EntityScriptingInterface::setPoints(QUuid entityID, std::function<bool(Line
|
|||
return success;
|
||||
}
|
||||
|
||||
|
||||
bool EntityScriptingInterface::setVoxelSphere(QUuid entityID, const glm::vec3& center, float radius, int value) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
return setVoxels(entityID, [center, radius, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return polyVoxWorker(entityID, [center, radius, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return polyVoxEntity.setSphere(center, radius, value);
|
||||
});
|
||||
}
|
||||
|
@ -896,7 +893,7 @@ bool EntityScriptingInterface::setVoxelCapsule(QUuid entityID,
|
|||
float radius, int value) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
return setVoxels(entityID, [start, end, radius, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return polyVoxWorker(entityID, [start, end, radius, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return polyVoxEntity.setCapsule(start, end, radius, value);
|
||||
});
|
||||
}
|
||||
|
@ -904,7 +901,7 @@ bool EntityScriptingInterface::setVoxelCapsule(QUuid entityID,
|
|||
bool EntityScriptingInterface::setVoxel(QUuid entityID, const glm::vec3& position, int value) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
return setVoxels(entityID, [position, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return polyVoxWorker(entityID, [position, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return polyVoxEntity.setVoxelInVolume(position, value);
|
||||
});
|
||||
}
|
||||
|
@ -912,7 +909,7 @@ bool EntityScriptingInterface::setVoxel(QUuid entityID, const glm::vec3& positio
|
|||
bool EntityScriptingInterface::setAllVoxels(QUuid entityID, int value) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
return setVoxels(entityID, [value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return polyVoxWorker(entityID, [value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return polyVoxEntity.setAll(value);
|
||||
});
|
||||
}
|
||||
|
@ -921,11 +918,23 @@ bool EntityScriptingInterface::setVoxelsInCuboid(QUuid entityID, const glm::vec3
|
|||
const glm::vec3& cuboidSize, int value) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
return setVoxels(entityID, [lowPosition, cuboidSize, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return polyVoxWorker(entityID, [lowPosition, cuboidSize, value](PolyVoxEntityItem& polyVoxEntity) {
|
||||
return polyVoxEntity.setCuboid(lowPosition, cuboidSize, value);
|
||||
});
|
||||
}
|
||||
|
||||
void EntityScriptingInterface::voxelsToMesh(QUuid entityID, QScriptValue callback) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
polyVoxWorker(entityID, [callback](PolyVoxEntityItem& polyVoxEntity) mutable {
|
||||
QScriptValue mesh;
|
||||
polyVoxEntity.getMeshAsScriptValue(callback.engine(), mesh);
|
||||
QScriptValueList args { mesh };
|
||||
callback.call(QScriptValue(), args);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
bool EntityScriptingInterface::setAllPoints(QUuid entityID, const QVector<glm::vec3>& points) {
|
||||
PROFILE_RANGE(script_entities, __FUNCTION__);
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "EntityItemProperties.h"
|
||||
|
||||
class EntityTree;
|
||||
class MeshProxy;
|
||||
|
||||
class RayToEntityIntersectionResult {
|
||||
public:
|
||||
|
@ -229,6 +230,7 @@ public slots:
|
|||
Q_INVOKABLE bool setAllVoxels(QUuid entityID, int value);
|
||||
Q_INVOKABLE bool setVoxelsInCuboid(QUuid entityID, const glm::vec3& lowPosition,
|
||||
const glm::vec3& cuboidSize, int value);
|
||||
Q_INVOKABLE void voxelsToMesh(QUuid entityID, QScriptValue callback);
|
||||
|
||||
Q_INVOKABLE bool setAllPoints(QUuid entityID, const QVector<glm::vec3>& points);
|
||||
Q_INVOKABLE bool appendPoint(QUuid entityID, const glm::vec3& point);
|
||||
|
@ -325,7 +327,7 @@ signals:
|
|||
|
||||
private:
|
||||
bool actionWorker(const QUuid& entityID, std::function<bool(EntitySimulationPointer, EntityItemPointer)> actor);
|
||||
bool setVoxels(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor);
|
||||
bool polyVoxWorker(QUuid entityID, std::function<bool(PolyVoxEntityItem&)> actor);
|
||||
bool setPoints(QUuid entityID, std::function<bool(LineEntityItem&)> actor);
|
||||
void queueEntityMessage(PacketType packetType, EntityItemID entityID, const EntityItemProperties& properties);
|
||||
|
||||
|
|
|
@ -242,3 +242,7 @@ const QByteArray PolyVoxEntityItem::getVoxelData() const {
|
|||
});
|
||||
return voxelDataCopy;
|
||||
}
|
||||
|
||||
bool PolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) const {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -131,7 +131,9 @@ class PolyVoxEntityItem : public EntityItem {
|
|||
virtual void rebakeMesh() {};
|
||||
|
||||
void setVoxelDataDirty(bool value) { withWriteLock([&] { _voxelDataDirty = value; }); }
|
||||
virtual void getMesh() {}; // recompute mesh
|
||||
virtual void recomputeMesh() {};
|
||||
|
||||
virtual bool getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) const;
|
||||
|
||||
protected:
|
||||
glm::vec3 _voxelVolumeSize; // this is always 3 bytes
|
||||
|
|
|
@ -54,7 +54,8 @@ template<class T> QVariant readBinaryArray(QDataStream& in, int& position) {
|
|||
in.readRawData(compressed.data() + sizeof(quint32), compressedLength);
|
||||
position += compressedLength;
|
||||
arrayData = qUncompress(compressed);
|
||||
if (arrayData.isEmpty() || arrayData.size() != (sizeof(T) * arrayLength)) { // answers empty byte array if corrupt
|
||||
if (arrayData.isEmpty() ||
|
||||
(unsigned int)arrayData.size() != (sizeof(T) * arrayLength)) { // answers empty byte array if corrupt
|
||||
throw QString("corrupt fbx file");
|
||||
}
|
||||
} else {
|
||||
|
|
148
libraries/fbx/src/OBJWriter.cpp
Normal file
148
libraries/fbx/src/OBJWriter.cpp
Normal file
|
@ -0,0 +1,148 @@
|
|||
//
|
||||
// OBJWriter.cpp
|
||||
// libraries/fbx/src/
|
||||
//
|
||||
// Created by Seth Alves on 2017-1-27.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include "model/Geometry.h"
|
||||
#include "OBJWriter.h"
|
||||
#include "ModelFormatLogging.h"
|
||||
|
||||
static QString formatFloat(double n) {
|
||||
// limit precision to 6, but don't output trailing zeros.
|
||||
QString s = QString::number(n, 'f', 6);
|
||||
while (s.endsWith("0")) {
|
||||
s.remove(s.size() - 1, 1);
|
||||
}
|
||||
if (s.endsWith(".")) {
|
||||
s.remove(s.size() - 1, 1);
|
||||
}
|
||||
|
||||
// check for non-numbers. if we get NaN or inf or scientific notation, just return 0
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
auto c = s.at(i).toLatin1();
|
||||
if (c != '-' &&
|
||||
c != '.' &&
|
||||
(c < '0' || c > '9')) {
|
||||
qCDebug(modelformat) << "OBJWriter zeroing bad vertex coordinate:" << s << "because of" << c;
|
||||
return QString("0");
|
||||
}
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
bool writeOBJToTextStream(QTextStream& out, QList<MeshPointer> meshes) {
|
||||
// each mesh's vertices are numbered from zero. We're combining all their vertices into one list here,
|
||||
// so keep track of the start index for each mesh.
|
||||
QList<int> meshVertexStartOffset;
|
||||
int currentVertexStartOffset = 0;
|
||||
|
||||
// write out all vertices
|
||||
foreach (const MeshPointer& mesh, meshes) {
|
||||
meshVertexStartOffset.append(currentVertexStartOffset);
|
||||
const gpu::BufferView& vertexBuffer = mesh->getVertexBuffer();
|
||||
int vertexCount = 0;
|
||||
gpu::BufferView::Iterator<const glm::vec3> vertexItr = vertexBuffer.cbegin<const glm::vec3>();
|
||||
while (vertexItr != vertexBuffer.cend<const glm::vec3>()) {
|
||||
glm::vec3 v = *vertexItr;
|
||||
out << "v ";
|
||||
out << formatFloat(v[0]) << " ";
|
||||
out << formatFloat(v[1]) << " ";
|
||||
out << formatFloat(v[2]) << "\n";
|
||||
vertexItr++;
|
||||
vertexCount++;
|
||||
}
|
||||
currentVertexStartOffset += vertexCount;
|
||||
}
|
||||
out << "\n";
|
||||
|
||||
// write out faces
|
||||
int nth = 0;
|
||||
foreach (const MeshPointer& mesh, meshes) {
|
||||
currentVertexStartOffset = meshVertexStartOffset.takeFirst();
|
||||
|
||||
const gpu::BufferView& partBuffer = mesh->getPartBuffer();
|
||||
const gpu::BufferView& indexBuffer = mesh->getIndexBuffer();
|
||||
|
||||
model::Index partCount = (model::Index)mesh->getNumParts();
|
||||
for (int partIndex = 0; partIndex < partCount; partIndex++) {
|
||||
const model::Mesh::Part& part = partBuffer.get<model::Mesh::Part>(partIndex);
|
||||
|
||||
out << "g part-" << nth++ << "\n";
|
||||
|
||||
// model::Mesh::TRIANGLES
|
||||
// TODO -- handle other formats
|
||||
gpu::BufferView::Iterator<const uint32_t> indexItr = indexBuffer.cbegin<uint32_t>();
|
||||
indexItr += part._startIndex;
|
||||
|
||||
int indexCount = 0;
|
||||
while (indexItr != indexBuffer.cend<uint32_t>() && indexCount < part._numIndices) {
|
||||
uint32_t index0 = *indexItr;
|
||||
indexItr++;
|
||||
indexCount++;
|
||||
if (indexItr == indexBuffer.cend<uint32_t>() || indexCount >= part._numIndices) {
|
||||
qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3";
|
||||
break;
|
||||
}
|
||||
uint32_t index1 = *indexItr;
|
||||
indexItr++;
|
||||
indexCount++;
|
||||
if (indexItr == indexBuffer.cend<uint32_t>() || indexCount >= part._numIndices) {
|
||||
qCDebug(modelformat) << "OBJWriter -- index buffer length isn't multiple of 3";
|
||||
break;
|
||||
}
|
||||
uint32_t index2 = *indexItr;
|
||||
indexItr++;
|
||||
indexCount++;
|
||||
|
||||
out << "f ";
|
||||
out << currentVertexStartOffset + index0 + 1 << " ";
|
||||
out << currentVertexStartOffset + index1 + 1 << " ";
|
||||
out << currentVertexStartOffset + index2 + 1 << "\n";
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool writeOBJToFile(QString path, QList<MeshPointer> meshes) {
|
||||
if (QFileInfo(path).exists() && !QFile::remove(path)) {
|
||||
qCDebug(modelformat) << "OBJ writer failed, file exists:" << path;
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
qCDebug(modelformat) << "OBJ writer failed to open output file:" << path;
|
||||
return false;
|
||||
}
|
||||
|
||||
QTextStream outStream(&file);
|
||||
|
||||
bool success;
|
||||
success = writeOBJToTextStream(outStream, meshes);
|
||||
|
||||
file.close();
|
||||
return success;
|
||||
}
|
||||
|
||||
QString writeOBJToString(QList<MeshPointer> meshes) {
|
||||
QString result;
|
||||
QTextStream outStream(&result, QIODevice::ReadWrite);
|
||||
bool success;
|
||||
success = writeOBJToTextStream(outStream, meshes);
|
||||
if (success) {
|
||||
return result;
|
||||
}
|
||||
return QString("");
|
||||
}
|
26
libraries/fbx/src/OBJWriter.h
Normal file
26
libraries/fbx/src/OBJWriter.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// OBJWriter.h
|
||||
// libraries/fbx/src/
|
||||
//
|
||||
// Created by Seth Alves on 2017-1-27.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_objwriter_h
|
||||
#define hifi_objwriter_h
|
||||
|
||||
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
#include <model/Geometry.h>
|
||||
|
||||
using MeshPointer = std::shared_ptr<model::Mesh>;
|
||||
|
||||
bool writeOBJToTextStream(QTextStream& out, QList<MeshPointer> meshes);
|
||||
bool writeOBJToFile(QString path, QList<MeshPointer> meshes);
|
||||
QString writeOBJToString(QList<MeshPointer> meshes);
|
||||
|
||||
#endif // hifi_objwriter_h
|
41
libraries/script-engine/src/MeshProxy.h
Normal file
41
libraries/script-engine/src/MeshProxy.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
//
|
||||
// MeshProxy.h
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Seth Alves on 2017-1-27.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#ifndef hifi_MeshProxy_h
|
||||
#define hifi_MeshProxy_h
|
||||
|
||||
#include <model/Geometry.h>
|
||||
|
||||
using MeshPointer = std::shared_ptr<model::Mesh>;
|
||||
|
||||
class MeshProxy : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MeshProxy(MeshPointer mesh) : _mesh(mesh) {}
|
||||
~MeshProxy() {}
|
||||
|
||||
MeshPointer getMeshPointer() const { return _mesh; }
|
||||
|
||||
Q_INVOKABLE int getNumVertices() const { return (int)_mesh->getNumVertices(); }
|
||||
Q_INVOKABLE glm::vec3 getPos3(int index) const { return _mesh->getPos3(index); }
|
||||
|
||||
|
||||
protected:
|
||||
MeshPointer _mesh;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(MeshProxy*);
|
||||
|
||||
class MeshProxyList : public QList<MeshProxy*> {}; // typedef and using fight with the Qt macros/templates, do this instead
|
||||
Q_DECLARE_METATYPE(MeshProxyList);
|
||||
|
||||
#endif // hifi_MeshProxy_h
|
53
libraries/script-engine/src/ModelScriptingInterface.cpp
Normal file
53
libraries/script-engine/src/ModelScriptingInterface.cpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
//
|
||||
// ModelScriptingInterface.cpp
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Seth Alves on 2017-1-27.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
#include <QScriptEngine>
|
||||
#include <QScriptValueIterator>
|
||||
#include <QtScript/QScriptValue>
|
||||
#include "ModelScriptingInterface.h"
|
||||
#include "OBJWriter.h"
|
||||
|
||||
|
||||
ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(parent) {
|
||||
}
|
||||
|
||||
QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in) {
|
||||
return engine->newQObject(in, QScriptEngine::QtOwnership,
|
||||
QScriptEngine::ExcludeDeleteLater | QScriptEngine::ExcludeChildObjects);
|
||||
}
|
||||
|
||||
void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out) {
|
||||
out = qobject_cast<MeshProxy*>(value.toQObject());
|
||||
}
|
||||
|
||||
QScriptValue meshesToScriptValue(QScriptEngine* engine, const MeshProxyList &in) {
|
||||
return engine->toScriptValue(in);
|
||||
}
|
||||
|
||||
void meshesFromScriptValue(const QScriptValue& value, MeshProxyList &out) {
|
||||
QScriptValueIterator itr(value);
|
||||
while(itr.hasNext()) {
|
||||
itr.next();
|
||||
MeshProxy* meshProxy = qscriptvalue_cast<MeshProxyList::value_type>(itr.value());
|
||||
if (meshProxy) {
|
||||
out.append(meshProxy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString ModelScriptingInterface::meshToOBJ(MeshProxyList in) {
|
||||
QList<MeshPointer> meshes;
|
||||
foreach (const MeshProxy* meshProxy, in) {
|
||||
meshes.append(meshProxy->getMeshPointer());
|
||||
}
|
||||
|
||||
return writeOBJToString(meshes);
|
||||
}
|
39
libraries/script-engine/src/ModelScriptingInterface.h
Normal file
39
libraries/script-engine/src/ModelScriptingInterface.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// ModelScriptingInterface.h
|
||||
// libraries/script-engine/src
|
||||
//
|
||||
// Created by Seth Alves on 2017-1-27.
|
||||
// Copyright 2017 High Fidelity, Inc.
|
||||
//
|
||||
// Distributed under the Apache License, Version 2.0.
|
||||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
|
||||
#ifndef hifi_ModelScriptingInterface_h
|
||||
#define hifi_ModelScriptingInterface_h
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QScriptValue>
|
||||
#include <OBJWriter.h>
|
||||
#include <model/Geometry.h>
|
||||
#include "MeshProxy.h"
|
||||
|
||||
using MeshPointer = std::shared_ptr<model::Mesh>;
|
||||
|
||||
class ModelScriptingInterface : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ModelScriptingInterface(QObject* parent);
|
||||
|
||||
Q_INVOKABLE QString meshToOBJ(MeshProxyList in);
|
||||
};
|
||||
|
||||
QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in);
|
||||
void meshFromScriptValue(const QScriptValue& value, MeshProxy* &out);
|
||||
|
||||
QScriptValue meshesToScriptValue(QScriptEngine* engine, const MeshProxyList &in);
|
||||
void meshesFromScriptValue(const QScriptValue& value, MeshProxyList &out);
|
||||
|
||||
#endif // hifi_ModelScriptingInterface_h
|
|
@ -65,6 +65,8 @@
|
|||
#include "RecordingScriptingInterface.h"
|
||||
#include "ScriptEngines.h"
|
||||
#include "TabletScriptingInterface.h"
|
||||
#include "ModelScriptingInterface.h"
|
||||
|
||||
|
||||
#include <Profile.h>
|
||||
|
||||
|
@ -594,7 +596,7 @@ void ScriptEngine::init() {
|
|||
registerGlobalObject("Messages", DependencyManager::get<MessagesClient>().data());
|
||||
|
||||
registerGlobalObject("File", new FileScriptingInterface(this));
|
||||
|
||||
|
||||
qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue);
|
||||
qScriptRegisterMetaType(this, resultHandlerToScriptValue, resultHandlerFromScriptValue);
|
||||
|
||||
|
@ -612,6 +614,10 @@ void ScriptEngine::init() {
|
|||
registerGlobalObject("Resources", DependencyManager::get<ResourceScriptingInterface>().data());
|
||||
|
||||
registerGlobalObject("DebugDraw", &DebugDraw::getInstance());
|
||||
|
||||
registerGlobalObject("Model", new ModelScriptingInterface(this));
|
||||
qScriptRegisterMetaType(this, meshToScriptValue, meshFromScriptValue);
|
||||
qScriptRegisterMetaType(this, meshesToScriptValue, meshesFromScriptValue);
|
||||
}
|
||||
|
||||
void ScriptEngine::registerValue(const QString& valueName, QScriptValue value) {
|
||||
|
|
Loading…
Reference in a new issue