Merge pull request #9946 from sethalves/model-scripting-1

Model scripting
This commit is contained in:
Brad Hefta-Gaub 2017-03-21 09:26:32 -07:00 committed by GitHub
commit ac309df3d1
11 changed files with 310 additions and 20 deletions

View file

@ -666,11 +666,8 @@ void RenderablePolyVoxEntityItem::setZTextureURL(QString zTextureURL) {
}
}
void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render");
assert(getType() == EntityTypes::PolyVox);
Q_ASSERT(args->_batch);
bool RenderablePolyVoxEntityItem::updateDependents() {
bool voxelDataDirty;
bool volDataDirty;
withWriteLock([&] {
@ -688,6 +685,17 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
recomputeMesh();
}
return !volDataDirty;
}
void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render");
assert(getType() == EntityTypes::PolyVox);
Q_ASSERT(args->_batch);
updateDependents();
model::MeshPointer mesh;
glm::vec3 voxelVolumeSize;
withReadLock([&] {
@ -1584,14 +1592,22 @@ void RenderablePolyVoxEntityItem::locationChanged(bool tellPhysics) {
scene->enqueuePendingChanges(pendingChanges);
}
bool RenderablePolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) const {
bool RenderablePolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) {
if (!updateDependents()) {
return false;
}
bool success = false;
MeshProxy* meshProxy = nullptr;
model::MeshPointer mesh = nullptr;
glm::mat4 transform = voxelToLocalMatrix();
withReadLock([&] {
if (_meshInitialized) {
success = true;
meshProxy = new MeshProxy(_mesh);
// the mesh will be in voxel-space. transform it into object-space
meshProxy = new MeshProxy(
_mesh->map([=](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); },
[=](glm::vec3 normal){ return glm::vec3(transform * glm::vec4(normal, 0.0f)); },
[](uint32_t index){ return index; }));
}
});
result = meshToScriptValue(engine, meshProxy);

View file

@ -61,6 +61,8 @@ public:
virtual uint8_t getVoxel(int x, int y, int z) override;
virtual bool setVoxel(int x, int y, int z, uint8_t toValue) override;
int getOnCount() const override { return _onCount; }
void render(RenderArgs* args) override;
virtual bool supportsDetailedRayIntersection() const override { return true; }
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
@ -133,7 +135,7 @@ public:
QByteArray volDataToArray(quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize) const;
void setMesh(model::MeshPointer mesh);
bool getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) const override;
bool getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) override;
void setCollisionPoints(ShapeInfo::PointCollection points, AABox box);
PolyVox::SimpleVolume<uint8_t>* getVolData() { return _volData; }
@ -193,6 +195,7 @@ private:
void cacheNeighbors();
void copyUpperEdgesFromNeighbors();
void bonkNeighbors();
bool updateDependents();
};
bool inUserBounds(const PolyVox::SimpleVolume<uint8_t>* vol, PolyVoxEntityItem::PolyVoxSurfaceStyle surfaceStyle,

View file

@ -8,6 +8,10 @@
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/transform.hpp>
#include "EntityScriptingInterface.h"
#include <QFutureWatcher>
@ -1039,13 +1043,20 @@ bool EntityScriptingInterface::setVoxelsInCuboid(QUuid entityID, const glm::vec3
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);
bool success { false };
QScriptValue mesh { false };
polyVoxWorker(entityID, [&](PolyVoxEntityItem& polyVoxEntity) mutable {
if (polyVoxEntity.getOnCount() == 0) {
success = true;
} else {
success = polyVoxEntity.getMeshAsScriptValue(callback.engine(), mesh);
}
return true;
});
QScriptValueList args { mesh, success };
callback.call(QScriptValue(), args);
}
bool EntityScriptingInterface::setAllPoints(QUuid entityID, const QVector<glm::vec3>& points) {
@ -1663,3 +1674,20 @@ bool EntityScriptingInterface::AABoxIntersectsCapsule(const glm::vec3& low, cons
AABox aaBox(low, dimensions);
return aaBox.findCapsulePenetration(start, end, radius, penetration);
}
glm::mat4 EntityScriptingInterface::getEntityTransform(const QUuid& entityID) {
glm::mat4 result;
if (_entityTree) {
_entityTree->withReadLock([&] {
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(entityID));
if (entity) {
glm::mat4 translation = glm::translate(entity->getPosition());
glm::mat4 rotation = glm::mat4_cast(entity->getRotation());
glm::mat4 registration = glm::translate(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT -
entity->getRegistrationPoint());
result = translation * rotation * registration;
}
});
}
return result;
}

View file

@ -331,6 +331,15 @@ public slots:
const glm::vec3& start, const glm::vec3& end, float radius);
/**jsdoc
* Returns object to world transform, excluding scale
*
* @function Entities.getEntityTransform
* @param {EntityID} entityID The ID of the entity whose transform is to be returned
* @return {Mat4} Entity's object to world transform, excluding scale
*/
Q_INVOKABLE glm::mat4 getEntityTransform(const QUuid& entityID);
signals:
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);

View file

@ -243,6 +243,6 @@ const QByteArray PolyVoxEntityItem::getVoxelData() const {
return voxelDataCopy;
}
bool PolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) const {
bool PolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) {
return false;
}

View file

@ -57,6 +57,8 @@ class PolyVoxEntityItem : public EntityItem {
virtual void setVoxelData(QByteArray voxelData);
virtual const QByteArray getVoxelData() const;
virtual int getOnCount() const { return 0; }
enum PolyVoxSurfaceStyle {
SURFACE_MARCHING_CUBES,
SURFACE_CUBIC,
@ -133,7 +135,7 @@ class PolyVoxEntityItem : public EntityItem {
void setVoxelDataDirty(bool value) { withWriteLock([&] { _voxelDataDirty = value; }); }
virtual void recomputeMesh() {};
virtual bool getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) const;
virtual bool getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result);
protected:
glm::vec3 _voxelVolumeSize; // this is always 3 bytes

View file

@ -198,7 +198,7 @@ public:
BufferView(const BufferPointer& buffer, Size offset, Size size, const Element& element = DEFAULT_ELEMENT);
BufferView(const BufferPointer& buffer, Size offset, Size size, uint16 stride, const Element& element = DEFAULT_ELEMENT);
Size getNumElements() const { return _size / _element.getSize(); }
Size getNumElements() const { return (_size - _offset) / _stride; }
//Template iterator with random access on the buffer sysmem
template<typename T>

View file

@ -117,7 +117,7 @@ Box Mesh::evalPartsBound(int partStart, int partEnd) const {
auto partItEnd = _partBuffer.cbegin<Part>() + partEnd;
for (;part != partItEnd; part++) {
Box partBound;
auto index = _indexBuffer.cbegin<uint>() + (*part)._startIndex;
auto endIndex = index + (*part)._numIndices;
@ -134,6 +134,115 @@ Box Mesh::evalPartsBound(int partStart, int partEnd) const {
return totalBound;
}
model::MeshPointer Mesh::map(std::function<glm::vec3(glm::vec3)> vertexFunc,
std::function<glm::vec3(glm::vec3)> normalFunc,
std::function<uint32_t(uint32_t)> indexFunc) {
// vertex data
const gpu::BufferView& vertexBufferView = getVertexBuffer();
gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices();
gpu::Resource::Size vertexSize = numVertices * sizeof(glm::vec3);
unsigned char* resultVertexData = new unsigned char[vertexSize];
unsigned char* vertexDataCursor = resultVertexData;
for (gpu::BufferView::Index i = 0; i < numVertices; i ++) {
glm::vec3 pos = vertexFunc(vertexBufferView.get<glm::vec3>(i));
memcpy(vertexDataCursor, &pos, sizeof(pos));
vertexDataCursor += sizeof(pos);
}
// normal data
int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h
const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal);
gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements();
gpu::Resource::Size normalSize = numNormals * sizeof(glm::vec3);
unsigned char* resultNormalData = new unsigned char[normalSize];
unsigned char* normalDataCursor = resultNormalData;
for (gpu::BufferView::Index i = 0; i < numNormals; i ++) {
glm::vec3 normal = normalFunc(normalsBufferView.get<glm::vec3>(i));
memcpy(normalDataCursor, &normal, sizeof(normal));
normalDataCursor += sizeof(normal);
}
// TODO -- other attributes
// face data
const gpu::BufferView& indexBufferView = getIndexBuffer();
gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)getNumIndices();
gpu::Resource::Size indexSize = numIndexes * sizeof(uint32_t);
unsigned char* resultIndexData = new unsigned char[indexSize];
unsigned char* indexDataCursor = resultIndexData;
for (gpu::BufferView::Index i = 0; i < numIndexes; i ++) {
uint32_t index = indexFunc(indexBufferView.get<uint32_t>(i));
memcpy(indexDataCursor, &index, sizeof(index));
indexDataCursor += sizeof(index);
}
model::MeshPointer result(new model::Mesh());
gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* resultVertexBuffer = new gpu::Buffer(vertexSize, resultVertexData);
gpu::BufferPointer resultVertexBufferPointer(resultVertexBuffer);
gpu::BufferView resultVertexBufferView(resultVertexBufferPointer, vertexElement);
result->setVertexBuffer(resultVertexBufferView);
gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* resultNormalsBuffer = new gpu::Buffer(normalSize, resultNormalData);
gpu::BufferPointer resultNormalsBufferPointer(resultNormalsBuffer);
gpu::BufferView resultNormalsBufferView(resultNormalsBufferPointer, normalElement);
result->addAttribute(attributeTypeNormal, resultNormalsBufferView);
gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW);
gpu::Buffer* resultIndexesBuffer = new gpu::Buffer(indexSize, resultIndexData);
gpu::BufferPointer resultIndexesBufferPointer(resultIndexesBuffer);
gpu::BufferView resultIndexesBufferView(resultIndexesBufferPointer, indexElement);
result->setIndexBuffer(resultIndexesBufferView);
// TODO -- shouldn't assume just one part
std::vector<model::Mesh::Part> parts;
parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex
(model::Index)result->getNumIndices(), // numIndices
(model::Index)0, // baseVertex
model::Mesh::TRIANGLES)); // topology
result->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part),
(gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL));
return result;
}
void Mesh::forEach(std::function<void(glm::vec3)> vertexFunc,
std::function<void(glm::vec3)> normalFunc,
std::function<void(uint32_t)> indexFunc) {
int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h
// vertex data
const gpu::BufferView& vertexBufferView = getVertexBuffer();
gpu::BufferView::Index numVertices = (gpu::BufferView::Index)getNumVertices();
for (gpu::BufferView::Index i = 0; i < numVertices; i ++) {
vertexFunc(vertexBufferView.get<glm::vec3>(i));
}
// normal data
const gpu::BufferView& normalsBufferView = getAttributeBuffer(attributeTypeNormal);
gpu::BufferView::Index numNormals = (gpu::BufferView::Index) normalsBufferView.getNumElements();
for (gpu::BufferView::Index i = 0; i < numNormals; i ++) {
normalFunc(normalsBufferView.get<glm::vec3>(i));
}
// TODO -- other attributes
// face data
const gpu::BufferView& indexBufferView = getIndexBuffer();
gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)getNumIndices();
for (gpu::BufferView::Index i = 0; i < numIndexes; i ++) {
indexFunc(indexBufferView.get<uint32_t>(i));
}
}
Geometry::Geometry() {
}
@ -148,4 +257,3 @@ Geometry::~Geometry() {
void Geometry::setMesh(const MeshPointer& mesh) {
_mesh = mesh;
}

View file

@ -25,6 +25,10 @@ typedef AABox Box;
typedef std::vector< Box > Boxes;
typedef glm::vec3 Vec3;
class Mesh;
using MeshPointer = std::shared_ptr< Mesh >;
class Mesh {
public:
const static Index PRIMITIVE_RESTART_INDEX = -1;
@ -114,6 +118,15 @@ public:
static gpu::Primitive topologyToPrimitive(Topology topo) { return static_cast<gpu::Primitive>(topo); }
// create a copy of this mesh after passing its vertices, normals, and indexes though the provided functions
MeshPointer map(std::function<glm::vec3(glm::vec3)> vertexFunc,
std::function<glm::vec3(glm::vec3)> normalFunc,
std::function<uint32_t(uint32_t)> indexFunc);
void forEach(std::function<void(glm::vec3)> vertexFunc,
std::function<void(glm::vec3)> normalFunc,
std::function<void(uint32_t)> indexFunc);
protected:
gpu::Stream::FormatPointer _vertexFormat;
@ -130,7 +143,6 @@ protected:
void evalVertexStream();
};
using MeshPointer = std::shared_ptr< Mesh >;
class Geometry {

View file

@ -12,11 +12,12 @@
#include <QScriptEngine>
#include <QScriptValueIterator>
#include <QtScript/QScriptValue>
#include "ScriptEngine.h"
#include "ModelScriptingInterface.h"
#include "OBJWriter.h"
ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(parent) {
_modelScriptEngine = qobject_cast<ScriptEngine*>(parent);
}
QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in) {
@ -51,3 +52,108 @@ QString ModelScriptingInterface::meshToOBJ(MeshProxyList in) {
return writeOBJToString(meshes);
}
QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) {
// figure out the size of the resulting mesh
size_t totalVertexCount { 0 };
size_t totalAttributeCount { 0 };
size_t totalIndexCount { 0 };
foreach (const MeshProxy* meshProxy, in) {
MeshPointer mesh = meshProxy->getMeshPointer();
totalVertexCount += mesh->getNumVertices();
int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h
const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(attributeTypeNormal);
gpu::BufferView::Index numNormals = (gpu::BufferView::Index)normalsBufferView.getNumElements();
totalAttributeCount += numNormals;
totalIndexCount += mesh->getNumIndices();
}
// alloc the resulting mesh
gpu::Resource::Size combinedVertexSize = totalVertexCount * sizeof(glm::vec3);
unsigned char* combinedVertexData = new unsigned char[combinedVertexSize];
unsigned char* combinedVertexDataCursor = combinedVertexData;
gpu::Resource::Size combinedNormalSize = totalAttributeCount * sizeof(glm::vec3);
unsigned char* combinedNormalData = new unsigned char[combinedNormalSize];
unsigned char* combinedNormalDataCursor = combinedNormalData;
gpu::Resource::Size combinedIndexSize = totalIndexCount * sizeof(uint32_t);
unsigned char* combinedIndexData = new unsigned char[combinedIndexSize];
unsigned char* combinedIndexDataCursor = combinedIndexData;
uint32_t indexStartOffset { 0 };
foreach (const MeshProxy* meshProxy, in) {
MeshPointer mesh = meshProxy->getMeshPointer();
mesh->forEach(
[&](glm::vec3 position){
memcpy(combinedVertexDataCursor, &position, sizeof(position));
combinedVertexDataCursor += sizeof(position);
},
[&](glm::vec3 normal){
memcpy(combinedNormalDataCursor, &normal, sizeof(normal));
combinedNormalDataCursor += sizeof(normal);
},
[&](uint32_t index){
index += indexStartOffset;
memcpy(combinedIndexDataCursor, &index, sizeof(index));
combinedIndexDataCursor += sizeof(index);
});
gpu::BufferView::Index numVertices = (gpu::BufferView::Index)mesh->getNumVertices();
indexStartOffset += numVertices;
}
model::MeshPointer result(new model::Mesh());
gpu::Element vertexElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* combinedVertexBuffer = new gpu::Buffer(combinedVertexSize, combinedVertexData);
gpu::BufferPointer combinedVertexBufferPointer(combinedVertexBuffer);
gpu::BufferView combinedVertexBufferView(combinedVertexBufferPointer, vertexElement);
result->setVertexBuffer(combinedVertexBufferView);
int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h
gpu::Element normalElement = gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ);
gpu::Buffer* combinedNormalsBuffer = new gpu::Buffer(combinedNormalSize, combinedNormalData);
gpu::BufferPointer combinedNormalsBufferPointer(combinedNormalsBuffer);
gpu::BufferView combinedNormalsBufferView(combinedNormalsBufferPointer, normalElement);
result->addAttribute(attributeTypeNormal, combinedNormalsBufferView);
gpu::Element indexElement = gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW);
gpu::Buffer* combinedIndexesBuffer = new gpu::Buffer(combinedIndexSize, combinedIndexData);
gpu::BufferPointer combinedIndexesBufferPointer(combinedIndexesBuffer);
gpu::BufferView combinedIndexesBufferView(combinedIndexesBufferPointer, indexElement);
result->setIndexBuffer(combinedIndexesBufferView);
std::vector<model::Mesh::Part> parts;
parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex
(model::Index)result->getNumIndices(), // numIndices
(model::Index)0, // baseVertex
model::Mesh::TRIANGLES)); // topology
result->setPartBuffer(gpu::BufferView(new gpu::Buffer(parts.size() * sizeof(model::Mesh::Part),
(gpu::Byte*) parts.data()), gpu::Element::PART_DRAWCALL));
MeshProxy* resultProxy = new MeshProxy(result);
return meshToScriptValue(_modelScriptEngine, resultProxy);
}
QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshProxy* meshProxy) {
if (!meshProxy) {
return QScriptValue(false);
}
MeshPointer mesh = meshProxy->getMeshPointer();
if (!mesh) {
return QScriptValue(false);
}
model::MeshPointer result = mesh->map([&](glm::vec3 position){ return glm::vec3(transform * glm::vec4(position, 1.0f)); },
[&](glm::vec3 normal){ return glm::vec3(transform * glm::vec4(normal, 0.0f)); },
[&](uint32_t index){ return index; });
MeshProxy* resultProxy = new MeshProxy(result);
return meshToScriptValue(_modelScriptEngine, resultProxy);
}

View file

@ -20,6 +20,7 @@
#include "MeshProxy.h"
using MeshPointer = std::shared_ptr<model::Mesh>;
class ScriptEngine;
class ModelScriptingInterface : public QObject {
Q_OBJECT
@ -28,6 +29,11 @@ public:
ModelScriptingInterface(QObject* parent);
Q_INVOKABLE QString meshToOBJ(MeshProxyList in);
Q_INVOKABLE QScriptValue appendMeshes(MeshProxyList in);
Q_INVOKABLE QScriptValue transformMesh(glm::mat4 transform, MeshProxy* meshProxy);
private:
ScriptEngine* _modelScriptEngine { nullptr };
};
QScriptValue meshToScriptValue(QScriptEngine* engine, MeshProxy* const &in);