Merge pull request #10015 from sethalves/model-scripting-2

model/mesh scripting
This commit is contained in:
Andrew Meadows 2017-03-28 12:53:16 -07:00 committed by GitHub
commit 49b3fc8674
20 changed files with 430 additions and 100 deletions

View file

@ -404,6 +404,9 @@ bool RenderablePolyVoxEntityItem::setSphere(glm::vec3 centerWorldCoords, float r
float smallestDimensionSize = voxelSize.x;
smallestDimensionSize = glm::min(smallestDimensionSize, voxelSize.y);
smallestDimensionSize = glm::min(smallestDimensionSize, voxelSize.z);
if (smallestDimensionSize <= 0.0f) {
return false;
}
glm::vec3 maxRadiusInVoxelCoords = glm::vec3(radiusWorldCoords / smallestDimensionSize);
glm::vec3 centerInVoxelCoords = wtvMatrix * glm::vec4(centerWorldCoords, 1.0f);
@ -414,21 +417,33 @@ bool RenderablePolyVoxEntityItem::setSphere(glm::vec3 centerWorldCoords, float r
glm::ivec3 lowI = glm::clamp(low, glm::vec3(0.0f), _voxelVolumeSize);
glm::ivec3 highI = glm::clamp(high, glm::vec3(0.0f), _voxelVolumeSize);
glm::vec3 radials(radiusWorldCoords / voxelSize.x,
radiusWorldCoords / voxelSize.y,
radiusWorldCoords / voxelSize.z);
// This three-level for loop iterates over every voxel in the volume that might be in the sphere
withWriteLock([&] {
for (int z = lowI.z; z < highI.z; z++) {
for (int y = lowI.y; y < highI.y; y++) {
for (int x = lowI.x; x < highI.x; x++) {
// Store our current position as a vector...
glm::vec4 pos(x + 0.5f, y + 0.5f, z + 0.5f, 1.0); // consider voxels cenetered on their coordinates
// convert to world coordinates
glm::vec3 worldPos = glm::vec3(vtwMatrix * pos);
// compute how far the current position is from the center of the volume
float fDistToCenter = glm::distance(worldPos, centerWorldCoords);
// If the current voxel is less than 'radius' units from the center then we set its value
if (fDistToCenter <= radiusWorldCoords) {
// set voxels whose bounding-box touches the sphere
AABox voxelBox(glm::vec3(x - 0.5f, y - 0.5f, z - 0.5f), glm::vec3(1.0f, 1.0f, 1.0f));
if (voxelBox.touchesAAEllipsoid(centerInVoxelCoords, radials)) {
result |= setVoxelInternal(x, y, z, toValue);
}
// TODO -- this version only sets voxels which have centers inside the sphere. which is best?
// // Store our current position as a vector...
// glm::vec4 pos(x + 0.5f, y + 0.5f, z + 0.5f, 1.0); // consider voxels cenetered on their coordinates
// // convert to world coordinates
// glm::vec3 worldPos = glm::vec3(vtwMatrix * pos);
// // compute how far the current position is from the center of the volume
// float fDistToCenter = glm::distance(worldPos, centerWorldCoords);
// // If the current voxel is less than 'radius' units from the center then we set its value
// if (fDistToCenter <= radiusWorldCoords) {
// result |= setVoxelInternal(x, y, z, toValue);
// }
}
}
}
@ -677,6 +692,8 @@ bool RenderablePolyVoxEntityItem::updateDependents() {
_voxelDataDirty = false;
} else if (_volDataDirty) {
_volDataDirty = false;
} else {
_meshReady = true;
}
});
if (voxelDataDirty) {
@ -694,7 +711,9 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
assert(getType() == EntityTypes::PolyVox);
Q_ASSERT(args->_batch);
updateDependents();
if (_voxelDataDirty || _volDataDirty) {
updateDependents();
}
model::MeshPointer mesh;
glm::vec3 voxelVolumeSize;
@ -756,6 +775,12 @@ void RenderablePolyVoxEntityItem::render(RenderArgs* args) {
0,
sizeof(PolyVox::PositionMaterialNormal));
// TODO -- should we be setting this?
// batch.setInputBuffer(gpu::Stream::NORMAL, mesh->getVertexBuffer()._buffer,
// 12,
// sizeof(PolyVox::PositionMaterialNormal));
batch.setIndexBuffer(gpu::UINT32, mesh->getIndexBuffer()._buffer, 0);
if (!_xTextureURL.isEmpty() && !_xTexture) {
@ -1274,23 +1299,27 @@ void RenderablePolyVoxEntityItem::recomputeMesh() {
auto indexBuffer = std::make_shared<gpu::Buffer>(vecIndices.size() * sizeof(uint32_t),
(gpu::Byte*)vecIndices.data());
auto indexBufferPtr = gpu::BufferPointer(indexBuffer);
gpu::BufferView indexBufferView(indexBufferPtr, gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW));
gpu::BufferView indexBufferView(indexBufferPtr, gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::INDEX));
mesh->setIndexBuffer(indexBufferView);
const std::vector<PolyVox::PositionMaterialNormal>& vecVertices = polyVoxMesh.getVertices();
const std::vector<PolyVox::PositionMaterialNormal>& vecVertices = polyVoxMesh.getRawVertexData();
auto vertexBuffer = std::make_shared<gpu::Buffer>(vecVertices.size() * sizeof(PolyVox::PositionMaterialNormal),
(gpu::Byte*)vecVertices.data());
auto vertexBufferPtr = gpu::BufferPointer(vertexBuffer);
gpu::BufferView vertexBufferView(vertexBufferPtr, 0,
vertexBufferPtr->getSize(),
sizeof(PolyVox::PositionMaterialNormal),
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW));
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
mesh->setVertexBuffer(vertexBufferView);
// TODO -- use 3-byte normals rather than 3-float normals
mesh->addAttribute(gpu::Stream::NORMAL,
gpu::BufferView(vertexBufferPtr, sizeof(float) * 3,
vertexBufferPtr->getSize() ,
gpu::BufferView(vertexBufferPtr,
sizeof(float) * 3, // polyvox mesh is packed: position, normal, material
vertexBufferPtr->getSize(),
sizeof(PolyVox::PositionMaterialNormal),
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW)));
gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)));
std::vector<model::Mesh::Part> parts;
parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex
@ -1312,7 +1341,7 @@ void RenderablePolyVoxEntityItem::setMesh(model::MeshPointer mesh) {
}
_mesh = mesh;
_meshDirty = true;
_meshInitialized = true;
_meshReady = true;
neighborsNeedUpdate = _neighborsNeedUpdate;
_neighborsNeedUpdate = false;
});
@ -1324,7 +1353,7 @@ void RenderablePolyVoxEntityItem::setMesh(model::MeshPointer mesh) {
void RenderablePolyVoxEntityItem::computeShapeInfoWorker() {
// this creates a collision-shape for the physics engine. The shape comes from
// _volData for cubic extractors and from _mesh for marching-cube extractors
if (!_meshInitialized) {
if (!_meshReady) {
return;
}
@ -1592,7 +1621,7 @@ void RenderablePolyVoxEntityItem::locationChanged(bool tellPhysics) {
scene->enqueuePendingChanges(pendingChanges);
}
bool RenderablePolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) {
bool RenderablePolyVoxEntityItem::getMeshes(MeshProxyList& result) {
if (!updateDependents()) {
return false;
}
@ -1601,15 +1630,22 @@ bool RenderablePolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QS
MeshProxy* meshProxy = nullptr;
glm::mat4 transform = voxelToLocalMatrix();
withReadLock([&] {
if (_meshInitialized) {
gpu::BufferView::Index numVertices = (gpu::BufferView::Index)_mesh->getNumVertices();
if (!_meshReady) {
// we aren't ready to return a mesh. the caller will have to try again later.
success = false;
} else if (numVertices == 0) {
// we are ready, but there are no triangles in the mesh.
success = true;
} else {
success = true;
// 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; }));
[=](glm::vec3 normal){ return glm::normalize(glm::vec3(transform * glm::vec4(normal, 0.0f))); },
[&](uint32_t index){ return index; }));
result << meshProxy;
}
});
result = meshToScriptValue(engine, meshProxy);
return success;
}

View file

@ -66,7 +66,7 @@ public:
void render(RenderArgs* args) override;
virtual bool supportsDetailedRayIntersection() const override { return true; }
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElementPointer& element, float& distance,
bool& keepSearching, OctreeElementPointer& element, float& distance,
BoxFace& face, glm::vec3& surfaceNormal,
void** intersectedObject, bool precisionPicking) const override;
@ -135,18 +135,19 @@ public:
QByteArray volDataToArray(quint16 voxelXSize, quint16 voxelYSize, quint16 voxelZSize) const;
void setMesh(model::MeshPointer mesh);
bool getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) override;
void setCollisionPoints(ShapeInfo::PointCollection points, AABox box);
PolyVox::SimpleVolume<uint8_t>* getVolData() { return _volData; }
uint8_t getVoxelInternal(int x, int y, int z) const;
bool setVoxelInternal(int x, int y, int z, uint8_t toValue);
void setVolDataDirty() { withWriteLock([&] { _volDataDirty = true; }); }
void setVolDataDirty() { withWriteLock([&] { _volDataDirty = true; _meshReady = false; }); }
// Transparent polyvox didn't seem to be working so disable for now
bool isTransparent() override { return false; }
bool getMeshes(MeshProxyList& result) override;
protected:
virtual void locationChanged(bool tellPhysics = true) override;
@ -157,7 +158,7 @@ private:
model::MeshPointer _mesh;
gpu::Stream::FormatPointer _vertexFormat;
bool _meshDirty { true }; // does collision-shape need to be recomputed?
bool _meshInitialized { false };
bool _meshReady { false };
NetworkTexturePointer _xTexture;
NetworkTexturePointer _yTexture;

View file

@ -1,6 +1,6 @@
set(TARGET_NAME entities)
setup_hifi_library(Network Script)
link_hifi_libraries(avatars shared audio octree model fbx networking animation)
link_hifi_libraries(avatars shared audio octree model model-networking fbx networking animation)
target_bullet()

View file

@ -63,6 +63,8 @@ namespace render {
#define debugTimeOnly(T) qPrintable(QString("%1").arg(T, 16, 10))
#define debugTreeVector(V) V << "[" << V << " in meters ]"
class MeshProxyList;
/// EntityItem class this is the base class for all entity types. It handles the basic properties and functionality available
/// to all other entity types. In particular: postion, size, rotation, age, lifetime, velocity, gravity. You can not instantiate
@ -470,9 +472,11 @@ public:
QUuid getLastEditedBy() const { return _lastEditedBy; }
void setLastEditedBy(QUuid value) { _lastEditedBy = value; }
bool matchesJSONFilters(const QJsonObject& jsonFilters) const;
virtual bool getMeshes(MeshProxyList& result) { return true; }
protected:
void setSimulated(bool simulated) { _simulated = simulated; }

View file

@ -21,6 +21,7 @@
#include <VariantMapToScriptValue.h>
#include <SharedUtil.h>
#include <SpatialParentFinder.h>
#include <model-networking/MeshProxy.h>
#include "EntitiesLogging.h"
#include "EntityActionFactoryInterface.h"
@ -1031,25 +1032,6 @@ bool EntityScriptingInterface::setVoxelsInCuboid(QUuid entityID, const glm::vec3
});
}
void EntityScriptingInterface::voxelsToMesh(QUuid entityID, QScriptValue callback) {
PROFILE_RANGE(script_entities, __FUNCTION__);
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) {
PROFILE_RANGE(script_entities, __FUNCTION__);
@ -1666,6 +1648,30 @@ bool EntityScriptingInterface::AABoxIntersectsCapsule(const glm::vec3& low, cons
return aaBox.findCapsulePenetration(start, end, radius, penetration);
}
void EntityScriptingInterface::getMeshes(QUuid entityID, QScriptValue callback) {
PROFILE_RANGE(script_entities, __FUNCTION__);
EntityItemPointer entity = static_cast<EntityItemPointer>(_entityTree->findEntityByEntityItemID(entityID));
if (!entity) {
qCDebug(entities) << "EntityScriptingInterface::getMeshes no entity with ID" << entityID;
QScriptValueList args { callback.engine()->undefinedValue(), false };
callback.call(QScriptValue(), args);
return;
}
MeshProxyList result;
bool success = entity->getMeshes(result);
if (success) {
QScriptValue resultAsScriptValue = meshesToScriptValue(callback.engine(), result);
QScriptValueList args { resultAsScriptValue, true };
callback.call(QScriptValue(), args);
} else {
QScriptValueList args { callback.engine()->undefinedValue(), false };
callback.call(QScriptValue(), args);
}
}
glm::mat4 EntityScriptingInterface::getEntityTransform(const QUuid& entityID) {
glm::mat4 result;
if (_entityTree) {
@ -1682,3 +1688,20 @@ glm::mat4 EntityScriptingInterface::getEntityTransform(const QUuid& entityID) {
}
return result;
}
glm::mat4 EntityScriptingInterface::getEntityLocalTransform(const QUuid& entityID) {
glm::mat4 result;
if (_entityTree) {
_entityTree->withReadLock([&] {
EntityItemPointer entity = _entityTree->findEntityByEntityItemID(EntityItemID(entityID));
if (entity) {
glm::mat4 translation = glm::translate(entity->getLocalPosition());
glm::mat4 rotation = glm::mat4_cast(entity->getLocalOrientation());
glm::mat4 registration = glm::translate(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT -
entity->getRegistrationPoint());
result = translation * rotation * registration;
}
});
}
return result;
}

View file

@ -265,7 +265,6 @@ 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);
@ -330,6 +329,8 @@ public slots:
const glm::vec3& start, const glm::vec3& end, float radius);
Q_INVOKABLE void getMeshes(QUuid entityID, QScriptValue callback);
/**jsdoc
* Returns object to world transform, excluding scale
*
@ -339,6 +340,16 @@ public slots:
*/
Q_INVOKABLE glm::mat4 getEntityTransform(const QUuid& entityID);
/**jsdoc
* Returns object to world transform, excluding scale
*
* @function Entities.getEntityLocalTransform
* @param {EntityID} entityID The ID of the entity whose local transform is to be returned
* @return {Mat4} Entity's object to parent transform, excluding scale
*/
Q_INVOKABLE glm::mat4 getEntityLocalTransform(const QUuid& entityID);
signals:
void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision);

View file

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

View file

@ -135,8 +135,6 @@ class PolyVoxEntityItem : public EntityItem {
void setVoxelDataDirty(bool value) { withWriteLock([&] { _voxelDataDirty = value; }); }
virtual void recomputeMesh() {};
virtual bool getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result);
protected:
glm::vec3 _voxelVolumeSize; // this is always 3 bytes

View file

@ -40,12 +40,16 @@ static QString formatFloat(double n) {
}
bool writeOBJToTextStream(QTextStream& out, QList<MeshPointer> meshes) {
int attributeTypeNormal = gpu::Stream::InputSlot::NORMAL; // libraries/gpu/src/gpu/Stream.h
// 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;
QList<int> meshNormalStartOffset;
int currentVertexStartOffset = 0;
int currentNormalStartOffset = 0;
// write out all vertices
// write out vertices
foreach (const MeshPointer& mesh, meshes) {
meshVertexStartOffset.append(currentVertexStartOffset);
const gpu::BufferView& vertexBuffer = mesh->getVertexBuffer();
@ -64,10 +68,28 @@ bool writeOBJToTextStream(QTextStream& out, QList<MeshPointer> meshes) {
}
out << "\n";
// write out normals
bool haveNormals = true;
foreach (const MeshPointer& mesh, meshes) {
meshNormalStartOffset.append(currentNormalStartOffset);
const gpu::BufferView& normalsBufferView = mesh->getAttributeBuffer(attributeTypeNormal);
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);
out << "vn ";
out << formatFloat(normal[0]) << " ";
out << formatFloat(normal[1]) << " ";
out << formatFloat(normal[2]) << "\n";
}
currentNormalStartOffset += numNormals;
}
out << "\n";
// write out faces
int nth = 0;
foreach (const MeshPointer& mesh, meshes) {
currentVertexStartOffset = meshVertexStartOffset.takeFirst();
currentNormalStartOffset = meshNormalStartOffset.takeFirst();
const gpu::BufferView& partBuffer = mesh->getPartBuffer();
const gpu::BufferView& indexBuffer = mesh->getIndexBuffer();
@ -104,9 +126,15 @@ bool writeOBJToTextStream(QTextStream& out, QList<MeshPointer> meshes) {
indexCount++;
out << "f ";
out << currentVertexStartOffset + index0 + 1 << " ";
out << currentVertexStartOffset + index1 + 1 << " ";
out << currentVertexStartOffset + index2 + 1 << "\n";
if (haveNormals) {
out << currentVertexStartOffset + index0 + 1 << "//" << currentVertexStartOffset + index0 + 1 << " ";
out << currentVertexStartOffset + index1 + 1 << "//" << currentVertexStartOffset + index1 + 1 << " ";
out << currentVertexStartOffset + index2 + 1 << "//" << currentVertexStartOffset + index2 + 1 << "\n";
} else {
out << currentVertexStartOffset + index0 + 1 << " ";
out << currentVertexStartOffset + index1 + 1 << " ";
out << currentVertexStartOffset + index2 + 1 << "\n";
}
}
out << "\n";
}

View file

@ -0,0 +1,44 @@
//
// MeshFace.cpp
// libraries/model/src/model/
//
// Created by Seth Alves on 2017-3-23
// 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 <RegisteredMetaTypes.h>
#include "MeshFace.h"
QScriptValue meshFaceToScriptValue(QScriptEngine* engine, const MeshFace &meshFace) {
QScriptValue obj = engine->newObject();
obj.setProperty("vertices", qVectorIntToScriptValue(engine, meshFace.vertexIndices));
return obj;
}
void meshFaceFromScriptValue(const QScriptValue &object, MeshFace& meshFaceResult) {
qVectorIntFromScriptValue(object.property("vertices"), meshFaceResult.vertexIndices);
}
QScriptValue qVectorMeshFaceToScriptValue(QScriptEngine* engine, const QVector<MeshFace>& vector) {
QScriptValue array = engine->newArray();
for (int i = 0; i < vector.size(); i++) {
array.setProperty(i, meshFaceToScriptValue(engine, vector.at(i)));
}
return array;
}
void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector<MeshFace>& result) {
int length = array.property("length").toInteger();
result.clear();
for (int i = 0; i < length; i++) {
MeshFace meshFace = MeshFace();
meshFaceFromScriptValue(array.property(i), meshFace);
result << meshFace;
}
}

View file

@ -0,0 +1,43 @@
//
// MeshFace.h
// libraries/model/src/model/
//
// Created by Seth Alves on 2017-3-23
// 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_MeshFace_h
#define hifi_MeshFace_h
#include <QScriptEngine>
#include <QScriptValueIterator>
#include <QtScript/QScriptValue>
#include <model/Geometry.h>
using MeshPointer = std::shared_ptr<model::Mesh>;
class MeshFace {
public:
MeshFace() {}
~MeshFace() {}
QVector<uint32_t> vertexIndices;
// TODO -- material...
};
Q_DECLARE_METATYPE(MeshFace)
Q_DECLARE_METATYPE(QVector<MeshFace>)
QScriptValue meshFaceToScriptValue(QScriptEngine* engine, const MeshFace &meshFace);
void meshFaceFromScriptValue(const QScriptValue &object, MeshFace& meshFaceResult);
QScriptValue qVectorMeshFaceToScriptValue(QScriptEngine* engine, const QVector<MeshFace>& vector);
void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector<MeshFace>& result);
#endif // hifi_MeshFace_h

View file

@ -0,0 +1,48 @@
//
// MeshProxy.cpp
// libraries/model/src/model/
//
// Created by Seth Alves on 2017-3-22.
// 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 "MeshProxy.h"
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) {
// QScriptValueList result;
QScriptValue result = engine->newArray();
int i = 0;
foreach (MeshProxy* const meshProxy, in) {
result.setProperty(i++, meshToScriptValue(engine, meshProxy));
}
return result;
}
void meshesFromScriptValue(const QScriptValue& value, MeshProxyList &out) {
QScriptValueIterator itr(value);
qDebug() << "in meshesFromScriptValue, value.length =" << value.property("length").toInt32();
while(itr.hasNext()) {
itr.next();
MeshProxy* meshProxy = qscriptvalue_cast<MeshProxyList::value_type>(itr.value());
if (meshProxy) {
out.append(meshProxy);
} else {
qDebug() << "null meshProxy";
}
}
}

View file

@ -1,6 +1,6 @@
//
// MeshProxy.h
// libraries/script-engine/src
// libraries/model/src/model/
//
// Created by Seth Alves on 2017-1-27.
// Copyright 2017 High Fidelity, Inc.
@ -12,6 +12,10 @@
#ifndef hifi_MeshProxy_h
#define hifi_MeshProxy_h
#include <QScriptEngine>
#include <QScriptValueIterator>
#include <QtScript/QScriptValue>
#include <model/Geometry.h>
using MeshPointer = std::shared_ptr<model::Mesh>;
@ -38,4 +42,11 @@ 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);
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_MeshProxy_h

View file

@ -145,7 +145,7 @@ model::MeshPointer Mesh::map(std::function<glm::vec3(glm::vec3)> vertexFunc,
unsigned char* resultVertexData = new unsigned char[vertexSize];
unsigned char* vertexDataCursor = resultVertexData;
for (gpu::BufferView::Index i = 0; i < numVertices; i ++) {
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);
@ -159,7 +159,7 @@ model::MeshPointer Mesh::map(std::function<glm::vec3(glm::vec3)> vertexFunc,
unsigned char* resultNormalData = new unsigned char[normalSize];
unsigned char* normalDataCursor = resultNormalData;
for (gpu::BufferView::Index i = 0; i < numNormals; i ++) {
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);
@ -173,7 +173,7 @@ model::MeshPointer Mesh::map(std::function<glm::vec3(glm::vec3)> vertexFunc,
unsigned char* resultIndexData = new unsigned char[indexSize];
unsigned char* indexDataCursor = resultIndexData;
for (gpu::BufferView::Index i = 0; i < numIndexes; i ++) {
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);
@ -217,19 +217,18 @@ model::MeshPointer Mesh::map(std::function<glm::vec3(glm::vec3)> vertexFunc,
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 ++) {
for (gpu::BufferView::Index i = 0; i < numVertices; i++) {
vertexFunc(vertexBufferView.get<glm::vec3>(i));
}
// 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();
for (gpu::BufferView::Index i = 0; i < numNormals; i ++) {
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
@ -237,7 +236,7 @@ void Mesh::forEach(std::function<void(glm::vec3)> vertexFunc,
// face data
const gpu::BufferView& indexBufferView = getIndexBuffer();
gpu::BufferView::Index numIndexes = (gpu::BufferView::Index)getNumIndices();
for (gpu::BufferView::Index i = 0; i < numIndexes; i ++) {
for (gpu::BufferView::Index i = 0; i < numIndexes; i++) {
indexFunc(indexBufferView.get<uint32_t>(i));
}
}

View file

@ -12,36 +12,18 @@
#include <QScriptEngine>
#include <QScriptValueIterator>
#include <QtScript/QScriptValue>
#include <model-networking/MeshFace.h>
#include "ScriptEngine.h"
#include "ScriptEngineLogging.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) {
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);
}
}
qScriptRegisterSequenceMetaType<QList<MeshProxy*>>(_modelScriptEngine);
qScriptRegisterMetaType(_modelScriptEngine, meshFaceToScriptValue, meshFaceFromScriptValue);
qScriptRegisterMetaType(_modelScriptEngine, qVectorMeshFaceToScriptValue, qVectorMeshFaceFromScriptValue);
}
QString ModelScriptingInterface::meshToOBJ(MeshProxyList in) {
@ -140,8 +122,6 @@ QScriptValue ModelScriptingInterface::appendMeshes(MeshProxyList in) {
return meshToScriptValue(_modelScriptEngine, resultProxy);
}
QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshProxy* meshProxy) {
if (!meshProxy) {
return QScriptValue(false);
@ -157,3 +137,57 @@ QScriptValue ModelScriptingInterface::transformMesh(glm::mat4 transform, MeshPro
MeshProxy* resultProxy = new MeshProxy(result);
return meshToScriptValue(_modelScriptEngine, resultProxy);
}
QScriptValue ModelScriptingInterface::newMesh(const QVector<glm::vec3>& vertices,
const QVector<glm::vec3>& normals,
const QVector<MeshFace>& faces) {
model::MeshPointer mesh(new model::Mesh());
// vertices
auto vertexBuffer = std::make_shared<gpu::Buffer>(vertices.size() * sizeof(glm::vec3), (gpu::Byte*)vertices.data());
auto vertexBufferPtr = gpu::BufferPointer(vertexBuffer);
gpu::BufferView vertexBufferView(vertexBufferPtr, 0, vertexBufferPtr->getSize(),
sizeof(glm::vec3), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
mesh->setVertexBuffer(vertexBufferView);
if (vertices.size() == normals.size()) {
// normals
auto normalBuffer = std::make_shared<gpu::Buffer>(normals.size() * sizeof(glm::vec3), (gpu::Byte*)normals.data());
auto normalBufferPtr = gpu::BufferPointer(normalBuffer);
gpu::BufferView normalBufferView(normalBufferPtr, 0, normalBufferPtr->getSize(),
sizeof(glm::vec3), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ));
mesh->addAttribute(gpu::Stream::NORMAL, normalBufferView);
} else {
qCDebug(scriptengine) << "ModelScriptingInterface::newMesh normals must be same length as vertices";
}
// indices (faces)
int VERTICES_PER_TRIANGLE = 3;
int indexBufferSize = faces.size() * sizeof(uint32_t) * VERTICES_PER_TRIANGLE;
unsigned char* indexData = new unsigned char[indexBufferSize];
unsigned char* indexDataCursor = indexData;
foreach(const MeshFace& meshFace, faces) {
for (int i = 0; i < VERTICES_PER_TRIANGLE; i++) {
memcpy(indexDataCursor, &meshFace.vertexIndices[i], sizeof(uint32_t));
indexDataCursor += sizeof(uint32_t);
}
}
auto indexBuffer = std::make_shared<gpu::Buffer>(indexBufferSize, (gpu::Byte*)indexData);
auto indexBufferPtr = gpu::BufferPointer(indexBuffer);
gpu::BufferView indexBufferView(indexBufferPtr, gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW));
mesh->setIndexBuffer(indexBufferView);
// parts
std::vector<model::Mesh::Part> parts;
parts.emplace_back(model::Mesh::Part((model::Index)0, // startIndex
(model::Index)faces.size() * 3, // 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));
MeshProxy* meshProxy = new MeshProxy(mesh);
return meshToScriptValue(_modelScriptEngine, meshProxy);
}

View file

@ -17,7 +17,8 @@
#include <QScriptValue>
#include <OBJWriter.h>
#include <model/Geometry.h>
#include "MeshProxy.h"
#include <model-networking/MeshProxy.h>
#include <model-networking/MeshFace.h>
using MeshPointer = std::shared_ptr<model::Mesh>;
class ScriptEngine;
@ -31,15 +32,12 @@ public:
Q_INVOKABLE QString meshToOBJ(MeshProxyList in);
Q_INVOKABLE QScriptValue appendMeshes(MeshProxyList in);
Q_INVOKABLE QScriptValue transformMesh(glm::mat4 transform, MeshProxy* meshProxy);
Q_INVOKABLE QScriptValue newMesh(const QVector<glm::vec3>& vertices,
const QVector<glm::vec3>& normals,
const QVector<MeshFace>& faces);
private:
ScriptEngine* _modelScriptEngine { nullptr };
};
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

View file

@ -436,6 +436,38 @@ glm::vec3 AABox::getClosestPointOnFace(const glm::vec4& origin, const glm::vec4&
return getClosestPointOnFace(glm::vec3(origin), face);
}
bool AABox::touchesAAEllipsoid(const glm::vec3& center, const glm::vec3& radials) const {
// handle case where ellipsoid's alix-aligned box doesn't touch this AABox
if (_corner.x - radials.x > center.x ||
_corner.y - radials.y > center.y ||
_corner.z - radials.z > center.z ||
_corner.x + _scale.x + radials.x < center.x ||
_corner.y + _scale.y + radials.y < center.y ||
_corner.z + _scale.z + radials.z < center.z) {
return false;
}
// handle case where ellipsoid is entirely inside this AABox
if (contains(center)) {
return true;
}
for (int i = 0; i < FACE_COUNT; i++) {
glm::vec3 closest = getClosestPointOnFace(center, (BoxFace)i) - center;
float x = closest.x;
float y = closest.y;
float z = closest.z;
float a = radials.x;
float b = radials.y;
float c = radials.z;
if (x*x/(a*a) + y*y/(b*b) + z*z/(c*c) < 1.0f) {
return true;
}
}
return false;
}
glm::vec4 AABox::getPlane(BoxFace face) const {
switch (face) {
case MIN_X_FACE: return glm::vec4(-1.0f, 0.0f, 0.0f, _corner.x);

View file

@ -70,6 +70,7 @@ public:
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance,
BoxFace& face, glm::vec3& surfaceNormal) const;
bool touchesSphere(const glm::vec3& center, float radius) const; // fast but may generate false positives
bool touchesAAEllipsoid(const glm::vec3& center, const glm::vec3& radials) const;
bool findSpherePenetration(const glm::vec3& center, float radius, glm::vec3& penetration) const;
bool findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius, glm::vec3& penetration) const;

View file

@ -43,6 +43,7 @@ void registerMetaTypes(QScriptEngine* engine) {
qScriptRegisterMetaType(engine, qVectorQuatToScriptValue, qVectorQuatFromScriptValue);
qScriptRegisterMetaType(engine, qVectorBoolToScriptValue, qVectorBoolFromScriptValue);
qScriptRegisterMetaType(engine, qVectorFloatToScriptValue, qVectorFloatFromScriptValue);
qScriptRegisterMetaType(engine, qVectorIntToScriptValue, qVectorIntFromScriptValue);
qScriptRegisterMetaType(engine, vec2toScriptValue, vec2FromScriptValue);
qScriptRegisterMetaType(engine, quatToScriptValue, quatFromScriptValue);
qScriptRegisterMetaType(engine, qRectToScriptValue, qRectFromScriptValue);
@ -386,6 +387,15 @@ QScriptValue qVectorFloatToScriptValue(QScriptEngine* engine, const QVector<floa
return array;
}
QScriptValue qVectorIntToScriptValue(QScriptEngine* engine, const QVector<uint32_t>& vector) {
QScriptValue array = engine->newArray();
for (int i = 0; i < vector.size(); i++) {
int num = vector.at(i);
array.setProperty(i, QScriptValue(num));
}
return array;
}
void qVectorFloatFromScriptValue(const QScriptValue& array, QVector<float>& vector) {
int length = array.property("length").toInteger();
@ -393,6 +403,15 @@ void qVectorFloatFromScriptValue(const QScriptValue& array, QVector<float>& vect
vector << array.property(i).toVariant().toFloat();
}
}
void qVectorIntFromScriptValue(const QScriptValue& array, QVector<uint32_t>& vector) {
int length = array.property("length").toInteger();
for (int i = 0; i < length; i++) {
vector << array.property(i).toVariant().toInt();
}
}
//
QVector<glm::vec3> qVectorVec3FromScriptValue(const QScriptValue& array){
QVector<glm::vec3> newVector;

View file

@ -113,6 +113,10 @@ QScriptValue qVectorFloatToScriptValue(QScriptEngine* engine, const QVector<floa
void qVectorFloatFromScriptValue(const QScriptValue& array, QVector<float>& vector);
QVector<float> qVectorFloatFromScriptValue(const QScriptValue& array);
// vector<uint32_t>
QScriptValue qVectorIntToScriptValue(QScriptEngine* engine, const QVector<uint32_t>& vector);
void qVectorIntFromScriptValue(const QScriptValue& array, QVector<uint32_t>& vector);
QVector<QUuid> qVectorQUuidFromScriptValue(const QScriptValue& array);
QScriptValue aaCubeToScriptValue(QScriptEngine* engine, const AACube& aaCube);