Volume Builder Type Fog Should it be solid?
-
Hello, I'm using a Volume Builder, with Volume Type Fog and I'm getting some results I didn't expect with the location of the voxels. Would it be expected that the entire Volume would be filled and 'solid' with Fog mode?
It's not the most elegant test case, but it's the one I can trim down.
//Get the volume from the volume builder object. maxon::Volume volume = volumeObject->GetVolume(); maxon::GridIteratorRef<maxon::Float32, maxon::ITERATORTYPE::ON> iterator = maxon::GridIteratorRef<maxon::Float32, maxon::ITERATORTYPE::ON>::Create() iferr_return; iterator.Init(volume)iferr_return; maxon::Matrix transform = volume.GetGridTransform(); BaseDocument* doc = GetActiveDocument(); BaseObject* sphere = BaseObject::Alloc(Osphere); BaseContainer* con = sphere->GetDataInstance(); con->SetFloat(PRIM_SPHERE_RAD, 5); for (; iterator.IsNotAtEnd(); iterator.StepNext()) { const maxon::IntVector32 coord = iterator66.GetCoords(); BaseObject* clone = static_cast<BaseObject*>( sphere->GetClone(COPYFLAGS::NONE, nullptr)); clone->SetAbsPos(transform* Vector(coord)); doc->InsertObject(clone, nullptr, nullptr); }
This code will create a sphere at the location of each active voxel. This was the easiest way I could think of showing the active voxels.
If I use a cube underneath a Volume Builder and delete away some of the spheres then the hollow inside, which I didn't think would happen with Fog mode.
Screenshot with the spheres deleted to show the hollow interior.Dan
-
Hey @d_schmidt yes this is expected, openvdb is sparse with 4 level tile options. Since our volume tool use openVDB we inherit from that.
In the context of OpenVDB and similar data structures, "sparse" refers to a way of efficiently representing and storing data in which only the non-empty or non-default parts are explicitly kept in memory. This is in contrast to dense representations, where every element is stored regardless of whether it holds meaningful information or not.
In a sparse representation like OpenVDB, the data structure is designed to allocate memory only for the parts of the voxel grid that contain relevant data (i.e., active voxels), while ignoring the regions that are empty or hold similar values. This allows for significant memory savings and performance improvements, especially when dealing with large grids where most of the space is empty/similar.So with that's said the way to retrieve what you expected is with the next code. Note that I've used Multi-Instance for speed reason and not polluting too much the viewport.
constexpr inline Int ConvertVoxelLevelToAmount(TREEVOXELLEVEL level) { switch (level) { case TREEVOXELLEVEL::ROOT: { return 32; } case TREEVOXELLEVEL::TWO: { return 16; } case TREEVOXELLEVEL::THREE: { return 8; } case TREEVOXELLEVEL::LEAF: { return 1; } } static_assert(true, "Unsupported voxel depth"); return 0; } class Command : public CommandData { public: virtual Bool Execute(BaseDocument* doc, GeDialog* parentManager) { iferr_scope_handler { return true; }; BaseObject* object = doc->GetFirstObject(); if (!object) return true; if (!object->IsInstanceOf(Ovolumebuilder)) return true; VolumeBuilder* const volumeBuilder = static_cast<VolumeBuilder*>(object); // get cache BaseObject* const cache = volumeBuilder->GetCache(); if (cache == nullptr) return true; // check for volume object if (!cache->IsInstanceOf(Ovolume)) return true; const VolumeObject* const volumeObject = static_cast<VolumeObject*>(cache); maxon::Volume volume = volumeObject->GetVolume(); maxon::GridIteratorRef<maxon::Float32, maxon::ITERATORTYPE::ON> iterator = maxon::GridIteratorRef<maxon::Float32, maxon::ITERATORTYPE::ON>::Create() iferr_return; iterator.Init(volume) iferr_return; maxon::Matrix transform = volume.GetGridTransform(); BaseObject* sphere = BaseObject::Alloc(Osphere); BaseContainer* con = sphere->GetDataInstance(); con->SetFloat(PRIM_SPHERE_RAD, 5); doc->InsertObject(sphere, nullptr, nullptr); // create instance object InstanceObject* const instance = InstanceObject::Alloc(); if (instance == nullptr) return true; doc->InsertObject(instance, nullptr, nullptr); instance->SetReferenceObject(sphere) iferr_return; if (!instance->SetParameter(ConstDescID(DescLevel(INSTANCEOBJECT_RENDERINSTANCE_MODE)), INSTANCEOBJECT_RENDERINSTANCE_MODE_MULTIINSTANCE, DESCFLAGS_SET::NONE)) return true; maxon::BaseArray<Matrix> matrices; for (; iterator.IsNotAtEnd(); iterator.StepNext()) { if (iterator.GetValue() < 0.0) continue; const Int voxelAmount = ConvertVoxelLevelToAmount(iterator.GetVoxelLevel()); for (Int32 voxelIndexX = 0; voxelIndexX < voxelAmount; voxelIndexX++) { for (Int32 voxelIndexY = 0; voxelIndexY < voxelAmount; voxelIndexY++) { for (Int32 voxelIndexZ = 0; voxelIndexZ < voxelAmount; voxelIndexZ++) { const maxon::IntVector32 voxelCoord = iterator.GetCoords(); Matrix m; m.off = transform * Vector(voxelCoord.x + voxelIndexX, voxelCoord.y + voxelIndexY, voxelCoord.z + voxelIndexZ); matrices.Append(std::move(m)) iferr_return; } } } } instance->SetInstanceMatrices(matrices) iferr_return; EventAdd(); return dlg.Open(DLG_TYPE::ASYNC, ID_ACTIVEOBJECT, -1, -1, 500, 300); } virtual Int32 GetState(BaseDocument* doc, GeDialog* parentManager) { return CMD_ENABLED; } };
Cheers,
Maxime.