// // RenderablePolyVoxEntityItem.cpp // libraries/entities-renderer/src/ // // Created by Seth Alves on 5/19/15. // Copyright 2015 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 #include #include #include #include #include #include #include #include #include #include #include #include "model/Geometry.h" #include "gpu/GLBackend.h" #include "EntityTreeRenderer.h" #include "RenderablePolyVoxEntityItem.h" EntityItem* RenderablePolyVoxEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) { return new RenderablePolyVoxEntityItem(entityID, properties); } RenderablePolyVoxEntityItem::~RenderablePolyVoxEntityItem() { delete _volData; } void RenderablePolyVoxEntityItem::setVoxelVolumeSize(glm::vec3 voxelVolumeSize) { PolyVoxEntityItem::setVoxelVolumeSize(voxelVolumeSize); if (_volData) { delete _volData; } PolyVox::Vector3DInt32 lowCorner(0, 0, 0); PolyVox::Vector3DInt32 highCorner(_voxelVolumeSize[0] - 1, // -1 because these corners are inclusive _voxelVolumeSize[1] - 1, _voxelVolumeSize[2] - 1); _volData = new PolyVox::SimpleVolume(PolyVox::Region(lowCorner, highCorner)); } glm::mat4 RenderablePolyVoxEntityItem::voxelToWorldMatrix() const { glm::vec3 scale = _dimensions / _voxelVolumeSize; // meters / voxel-units glm::mat4 scaled = glm::scale(glm::mat4(), scale); glm::mat4 centerToCorner = glm::translate(scaled, _voxelVolumeSize / -2.0f); glm::mat4 rotation = glm::mat4_cast(_rotation); glm::mat4 translation = glm::translate(getCenter()); return translation * rotation * centerToCorner; } glm::mat4 RenderablePolyVoxEntityItem::worldToVoxelMatrix() const { glm::mat4 worldToModelMatrix = glm::inverse(voxelToWorldMatrix()); return worldToModelMatrix; } void RenderablePolyVoxEntityItem::setSphereInVolume(glm::vec3 center, float radius, uint8_t toValue) { // This three-level for loop iterates over every voxel in the volume for (int z = 0; z < _volData->getDepth(); z++) { for (int y = 0; y < _volData->getHeight(); y++) { for (int x = 0; x < _volData->getWidth(); x++) { // Store our current position as a vector... glm::vec3 pos(x, y, z); // And compute how far the current position is from the center of the volume float fDistToCenter = glm::distance(pos, center); uint8_t uVoxelValue = _volData->getVoxelAt(x, y, z); // If the current voxel is less than 'radius' units from the center then we make it solid. if (fDistToCenter <= radius) { // Our new voxel value uVoxelValue = toValue; } // Wrte the voxel value into the volume _volData->setVoxelAt(x, y, z, uVoxelValue); } } } compressVolumeData(); } void RenderablePolyVoxEntityItem::createSphereInVolume(glm::vec3 centerVoxelCoords, float radiusVoxelCoords) { setSphereInVolume(centerVoxelCoords, radiusVoxelCoords, 255); } void RenderablePolyVoxEntityItem::eraseSphereInVolume(glm::vec3 centerVoxelCoords, float radiusVoxelCoords) { setSphereInVolume(centerVoxelCoords, radiusVoxelCoords, 0); } void RenderablePolyVoxEntityItem::setSphere(glm::vec3 centerWorldCoords, float radiusWorldCoords, uint8_t toValue) { // glm::vec3 centerVoxelCoords = worldToVoxelCoordinates(centerWorldCoords); glm::vec4 centerVoxelCoords = worldToVoxelMatrix() * glm::vec4(centerWorldCoords, 1.0f); glm::vec3 scale = _dimensions / _voxelVolumeSize; // meters / voxel-units float scaleY = scale[0]; float radiusVoxelCoords = radiusWorldCoords / scaleY; setSphereInVolume(glm::vec3(centerVoxelCoords), radiusVoxelCoords, toValue); } void RenderablePolyVoxEntityItem::createSphere(glm::vec3 centerWorldCoords, float radiusWorldCoords) { setSphere(centerWorldCoords, radiusWorldCoords, 255); } void RenderablePolyVoxEntityItem::eraseSphere(glm::vec3 centerWorldCoords, float radiusWorldCoords) { setSphere(centerWorldCoords, radiusWorldCoords, 0); } void RenderablePolyVoxEntityItem::getModel() { if (!_volData) { // this will cause the allocation of _volData setVoxelVolumeSize(_voxelVolumeSize); } glm::vec3 center = getCenter(); createSphere(center, 4); createSphere(center + glm::vec3(6.0f, 0.0f, 0.0f), 2); // A mesh object to hold the result of surface extraction PolyVox::SurfaceMesh polyVoxMesh; //Create a surface extractor. Comment out one of the following two lines to decide which type gets created. PolyVox::CubicSurfaceExtractorWithNormals> surfaceExtractor (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); // PolyVox::MarchingCubesSurfaceExtractor> surfaceExtractor // (_volData, _volData->getEnclosingRegion(), &polyVoxMesh); //Execute the surface extractor. surfaceExtractor.execute(); // convert PolyVox mesh to a Sam mesh model::Mesh* mesh = new model::Mesh(); model::MeshPointer meshPtr(mesh); const std::vector& vecIndices = polyVoxMesh.getIndices(); auto indexBuffer = new gpu::Buffer(vecIndices.size() * sizeof(uint32_t), (gpu::Byte*)vecIndices.data()); auto indexBufferPtr = gpu::BufferPointer(indexBuffer); mesh->setIndexBuffer(gpu::BufferView(indexBufferPtr, gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::RAW))); const std::vector& vecVertices = polyVoxMesh.getVertices(); auto vertexBuffer = new gpu::Buffer(vecVertices.size() * sizeof(PolyVox::PositionMaterialNormal), (gpu::Byte*)vecVertices.data()); auto vertexBufferPtr = gpu::BufferPointer(vertexBuffer); mesh->setVertexBuffer(gpu::BufferView(vertexBufferPtr, 0, vertexBufferPtr->getSize() - sizeof(float) * 3, sizeof(PolyVox::PositionMaterialNormal), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); mesh->addAttribute(gpu::Stream::NORMAL, gpu::BufferView(vertexBufferPtr, sizeof(float) * 3, vertexBufferPtr->getSize() - sizeof(float) * 3, sizeof(PolyVox::PositionMaterialNormal), gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::RAW))); qDebug() << "-------------XXXXXXXXXXXXXXXXXXXX-------------------" << usecTimestampNow(); qDebug() << "---- vecIndices.size() =" << vecIndices.size(); qDebug() << "---- sizeof(vecIndices[0]) =" << sizeof(vecIndices[0]); qDebug() << "---- vecVertices.size() =" << vecVertices.size(); qDebug() << "---- sizeof(vecVertices[0]) =" << sizeof(vecVertices[0]); qDebug() << "---- sizeof(uint32_t) =" << sizeof(uint32_t); qDebug() << "---- sizeof(float) =" << sizeof(float); qDebug() << "---- sizeof(PolyVox::PositionMaterialNormal) =" << sizeof(PolyVox::PositionMaterialNormal); // -------------XXXXXXXXXXXXXXXXXXXX------------------- // ---- vecIndices.size() = 25524 // ---- sizeof(vecIndices[0]) = 4 // ---- vecVertices.size() = 17016 // ---- sizeof(vecVertices[0]) = 28 // ---- sizeof(uint32_t) = 4 // ---- sizeof(float) = 4 // ---- sizeof(PolyVox::PositionMaterialNormal) = 28 _modelGeometry.setMesh(meshPtr); _needsModelReload = false; } void RenderablePolyVoxEntityItem::render(RenderArgs* args) { PerformanceTimer perfTimer("RenderablePolyVoxEntityItem::render"); assert(getType() == EntityTypes::PolyVox); if (_needsModelReload) { getModel(); } glm::vec3 position = getPosition(); glm::vec3 dimensions = getDimensions(); glm::vec3 scale = dimensions / _voxelVolumeSize; glm::vec3 center = getCenter(); glm::quat rotation = getRotation(); glPushMatrix(); glTranslatef(position.x, position.y, position.z); glm::vec3 axis = glm::axis(rotation); glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); glm::vec3 positionToCenter = center - position; // make the rendered voxel volume be centered on the entity's position positionToCenter -= _dimensions * glm::vec3(0.5f,0.5f,0.5f); glTranslatef(positionToCenter.x, positionToCenter.y, positionToCenter.z); glScalef(scale.x, scale.y, scale.z); auto mesh = _modelGeometry.getMesh(); gpu::Batch batch; batch.setInputFormat(mesh->getVertexFormat()); batch.setInputBuffer(gpu::Stream::POSITION, mesh->getVertexBuffer()); batch.setInputBuffer(gpu::Stream::NORMAL, mesh->getVertexBuffer()._buffer, sizeof(float) * 3, mesh->getVertexBuffer()._stride); batch.setIndexBuffer(gpu::UINT32, mesh->getIndexBuffer()._buffer, 0); batch.drawIndexed(gpu::TRIANGLES, mesh->getNumIndices(), 0); gpu::GLBackend::renderBatch(batch); glPopMatrix(); RenderableDebugableEntityItem::render(this, args); } class RaycastFunctor { public: RaycastFunctor() : _result(glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)) { } bool operator()(PolyVox::SimpleVolume::Sampler& sampler) { if (sampler.getVoxel() == 0) { return true; // keep raycasting } PolyVox::Vector3DInt32 positionIndex = sampler.getPosition(); _result = glm::vec4(positionIndex.getX(), positionIndex.getY(), positionIndex.getZ(), 1.0f); return false; } glm::vec4 _result; }; bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face, void** intersectedObject, bool precisionPicking) const { if (_needsModelReload || !precisionPicking) { // just intersect with bounding box return true; } glm::mat4 wtvMatrix = worldToVoxelMatrix(); glm::vec3 farPoint = origin + direction; glm::vec4 originInVoxel = wtvMatrix * glm::vec4(origin, 1.0f); glm::vec4 farInVoxel = wtvMatrix * glm::vec4(farPoint, 1.0f); glm::vec4 directionInVoxel = farInVoxel - originInVoxel; PolyVox::Vector3DFloat start(originInVoxel[0], originInVoxel[1], originInVoxel[2]); PolyVox::Vector3DFloat pvDirection(directionInVoxel[0], directionInVoxel[1], directionInVoxel[2]); pvDirection.normalise(); // pvDirection *= 1000.0f; // Casts ray of length 1000 pvDirection *= 64.0f; // Casts ray of length 1000 PolyVox::RaycastResult raycastResult; RaycastFunctor callback; raycastResult = PolyVox::raycastWithDirection(_volData, start, pvDirection, callback); if (raycastResult == PolyVox::RaycastResults::Completed) // the ray completed its path -- nothing was hit. return false; glm::vec4 intersectedWorldPosition = voxelToWorldMatrix() * callback._result; distance = glm::distance(glm::vec3(intersectedWorldPosition), origin); face = BoxFace::MIN_X_FACE; // XXX return true; } void RenderablePolyVoxEntityItem::compressVolumeData() { int rawSize = _volData->getDepth() * _volData->getHeight() * _volData->getWidth(); QByteArray uncompressedData = QByteArray(rawSize, '\0'); for (int z = 0; z < _volData->getDepth(); z++) { for (int y = 0; y < _volData->getHeight(); y++) { for (int x = 0; x < _volData->getWidth(); x++) { uint8_t uVoxelValue = _volData->getVoxelAt(x, y, z); int uncompressedIndex = z * _volData->getHeight() * _volData->getWidth() + y * _volData->getWidth() + x; uncompressedData[uncompressedIndex] = uVoxelValue; } } } QByteArray compressedData = qCompress(uncompressedData, 9); qDebug() << "-----------------------------------------------------------------------------"; qDebug() << "raw-size =" << rawSize << " compressed-size =" << compressedData.size(); }