diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 1d58527427..419f32f897 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -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(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& vecVertices = polyVoxMesh.getVertices(); + const std::vector& vecVertices = polyVoxMesh.getRawVertexData(); auto vertexBuffer = std::make_shared(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 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; } diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h index cf4672f068..cdfe2e38fe 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.h @@ -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* 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; diff --git a/libraries/entities/CMakeLists.txt b/libraries/entities/CMakeLists.txt index 1230fe8146..b2ae0f0ab7 100644 --- a/libraries/entities/CMakeLists.txt +++ b/libraries/entities/CMakeLists.txt @@ -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() diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 163b4d9e45..b973d916e6 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -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; } diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 1dded10302..2c332e8d05 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #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& 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(_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; +} diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h index 630c55e4e2..b25764790e 100644 --- a/libraries/entities/src/EntityScriptingInterface.h +++ b/libraries/entities/src/EntityScriptingInterface.h @@ -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& 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); diff --git a/libraries/entities/src/PolyVoxEntityItem.cpp b/libraries/entities/src/PolyVoxEntityItem.cpp index 90344d6c4b..2a374c1d17 100644 --- a/libraries/entities/src/PolyVoxEntityItem.cpp +++ b/libraries/entities/src/PolyVoxEntityItem.cpp @@ -242,7 +242,3 @@ const QByteArray PolyVoxEntityItem::getVoxelData() const { }); return voxelDataCopy; } - -bool PolyVoxEntityItem::getMeshAsScriptValue(QScriptEngine *engine, QScriptValue& result) { - return false; -} diff --git a/libraries/entities/src/PolyVoxEntityItem.h b/libraries/entities/src/PolyVoxEntityItem.h index 311a002a4a..cf7531fc9e 100644 --- a/libraries/entities/src/PolyVoxEntityItem.h +++ b/libraries/entities/src/PolyVoxEntityItem.h @@ -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 diff --git a/libraries/fbx/src/OBJWriter.cpp b/libraries/fbx/src/OBJWriter.cpp index 5ee04c5718..034263eb53 100644 --- a/libraries/fbx/src/OBJWriter.cpp +++ b/libraries/fbx/src/OBJWriter.cpp @@ -40,12 +40,16 @@ static QString formatFloat(double n) { } bool writeOBJToTextStream(QTextStream& out, QList 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 meshVertexStartOffset; + QList 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 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(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 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"; } diff --git a/libraries/model-networking/src/model-networking/MeshFace.cpp b/libraries/model-networking/src/model-networking/MeshFace.cpp new file mode 100644 index 0000000000..8092d36aa3 --- /dev/null +++ b/libraries/model-networking/src/model-networking/MeshFace.cpp @@ -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 + +#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& 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& 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; + } +} diff --git a/libraries/model-networking/src/model-networking/MeshFace.h b/libraries/model-networking/src/model-networking/MeshFace.h new file mode 100644 index 0000000000..3b81b372c3 --- /dev/null +++ b/libraries/model-networking/src/model-networking/MeshFace.h @@ -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 +#include +#include + +#include + +using MeshPointer = std::shared_ptr; + +class MeshFace { + +public: + MeshFace() {} + ~MeshFace() {} + + QVector vertexIndices; + // TODO -- material... +}; + +Q_DECLARE_METATYPE(MeshFace) +Q_DECLARE_METATYPE(QVector) + +QScriptValue meshFaceToScriptValue(QScriptEngine* engine, const MeshFace &meshFace); +void meshFaceFromScriptValue(const QScriptValue &object, MeshFace& meshFaceResult); +QScriptValue qVectorMeshFaceToScriptValue(QScriptEngine* engine, const QVector& vector); +void qVectorMeshFaceFromScriptValue(const QScriptValue& array, QVector& result); + + + +#endif // hifi_MeshFace_h diff --git a/libraries/model-networking/src/model-networking/MeshProxy.cpp b/libraries/model-networking/src/model-networking/MeshProxy.cpp new file mode 100644 index 0000000000..1b6fa43c82 --- /dev/null +++ b/libraries/model-networking/src/model-networking/MeshProxy.cpp @@ -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(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(itr.value()); + if (meshProxy) { + out.append(meshProxy); + } else { + qDebug() << "null meshProxy"; + } + } +} diff --git a/libraries/script-engine/src/MeshProxy.h b/libraries/model-networking/src/model-networking/MeshProxy.h similarity index 69% rename from libraries/script-engine/src/MeshProxy.h rename to libraries/model-networking/src/model-networking/MeshProxy.h index 82f5038348..c5b25b7895 100644 --- a/libraries/script-engine/src/MeshProxy.h +++ b/libraries/model-networking/src/model-networking/MeshProxy.h @@ -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 +#include +#include + #include using MeshPointer = std::shared_ptr; @@ -38,4 +42,11 @@ Q_DECLARE_METATYPE(MeshProxy*); class MeshProxyList : public QList {}; // 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 diff --git a/libraries/model/src/model/Geometry.cpp b/libraries/model/src/model/Geometry.cpp index 04b0db92d3..16608ab63e 100755 --- a/libraries/model/src/model/Geometry.cpp +++ b/libraries/model/src/model/Geometry.cpp @@ -145,7 +145,7 @@ model::MeshPointer Mesh::map(std::function 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(i)); memcpy(vertexDataCursor, &pos, sizeof(pos)); vertexDataCursor += sizeof(pos); @@ -159,7 +159,7 @@ model::MeshPointer Mesh::map(std::function 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(i)); memcpy(normalDataCursor, &normal, sizeof(normal)); normalDataCursor += sizeof(normal); @@ -173,7 +173,7 @@ model::MeshPointer Mesh::map(std::function 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(i)); memcpy(indexDataCursor, &index, sizeof(index)); indexDataCursor += sizeof(index); @@ -217,19 +217,18 @@ model::MeshPointer Mesh::map(std::function vertexFunc, void Mesh::forEach(std::function vertexFunc, std::function normalFunc, std::function 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(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(i)); } // TODO -- other attributes @@ -237,7 +236,7 @@ void Mesh::forEach(std::function 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(i)); } } diff --git a/libraries/script-engine/src/ModelScriptingInterface.cpp b/libraries/script-engine/src/ModelScriptingInterface.cpp index 833ac5b64d..f56312568e 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.cpp +++ b/libraries/script-engine/src/ModelScriptingInterface.cpp @@ -12,36 +12,18 @@ #include #include #include +#include #include "ScriptEngine.h" +#include "ScriptEngineLogging.h" #include "ModelScriptingInterface.h" #include "OBJWriter.h" ModelScriptingInterface::ModelScriptingInterface(QObject* parent) : QObject(parent) { _modelScriptEngine = qobject_cast(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(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(itr.value()); - if (meshProxy) { - out.append(meshProxy); - } - } + qScriptRegisterSequenceMetaType>(_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& vertices, + const QVector& normals, + const QVector& faces) { + model::MeshPointer mesh(new model::Mesh()); + + // vertices + auto vertexBuffer = std::make_shared(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(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(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 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); +} diff --git a/libraries/script-engine/src/ModelScriptingInterface.h b/libraries/script-engine/src/ModelScriptingInterface.h index 14789943e3..d899f532d8 100644 --- a/libraries/script-engine/src/ModelScriptingInterface.h +++ b/libraries/script-engine/src/ModelScriptingInterface.h @@ -17,7 +17,8 @@ #include #include #include -#include "MeshProxy.h" +#include +#include using MeshPointer = std::shared_ptr; 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& vertices, + const QVector& normals, + const QVector& 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 diff --git a/libraries/shared/src/AABox.cpp b/libraries/shared/src/AABox.cpp index 89d5ce709d..3f3146cc04 100644 --- a/libraries/shared/src/AABox.cpp +++ b/libraries/shared/src/AABox.cpp @@ -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); diff --git a/libraries/shared/src/AABox.h b/libraries/shared/src/AABox.h index ccc7b6e302..a53cc26163 100644 --- a/libraries/shared/src/AABox.h +++ b/libraries/shared/src/AABox.h @@ -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; diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index 7f12d6cc00..70067b93f3 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -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& 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& vector) { int length = array.property("length").toInteger(); @@ -393,6 +403,15 @@ void qVectorFloatFromScriptValue(const QScriptValue& array, QVector& vect vector << array.property(i).toVariant().toFloat(); } } + +void qVectorIntFromScriptValue(const QScriptValue& array, QVector& vector) { + int length = array.property("length").toInteger(); + + for (int i = 0; i < length; i++) { + vector << array.property(i).toVariant().toInt(); + } +} + // QVector qVectorVec3FromScriptValue(const QScriptValue& array){ QVector newVector; diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index 498a8b3b3a..8a15f62eed 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -113,6 +113,10 @@ QScriptValue qVectorFloatToScriptValue(QScriptEngine* engine, const QVector& vector); QVector qVectorFloatFromScriptValue(const QScriptValue& array); +// vector +QScriptValue qVectorIntToScriptValue(QScriptEngine* engine, const QVector& vector); +void qVectorIntFromScriptValue(const QScriptValue& array, QVector& vector); + QVector qVectorQUuidFromScriptValue(const QScriptValue& array); QScriptValue aaCubeToScriptValue(QScriptEngine* engine, const AACube& aaCube);