Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions library/private/scene_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class scene_impl : public scene
scene& add(const std::vector<std::filesystem::path>& filePath) override;
scene& add(const std::vector<std::string>& filePathStrings) override;
scene& add(const mesh_t& mesh) override;
scene& add(const double startTime, const double endTime, MeshCallback&& callback) override;
scene& clear() override;
int addLight(const light_state_t& lightState) const override;
int getLightCount() const override;
Expand Down
12 changes: 12 additions & 0 deletions library/public/scene.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

/// @cond
#include <filesystem>
#include <functional>
#include <string>
#include <vector>
/// @endcond
Expand Down Expand Up @@ -45,6 +46,12 @@ class F3D_EXPORT scene
: exception(what) {};
};

/**
* Callback type for animated meshes.
* Called with the current time value, should return the mesh for that time.
*/
using MeshCallback = std::function<mesh_t(double time)>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do you think of this name @Meakk ?


///@{
/**
* Add and load provided files into the scene
Expand All @@ -62,6 +69,11 @@ class F3D_EXPORT scene
*/
virtual scene& add(const mesh_t& mesh) = 0;

/**
* Add and load provided mesh into the scene
*/
virtual scene& add(const double startTime, const double endTime, MeshCallback&& callback) = 0;

///@{
/**
* Convenience initializer list signature for add method
Expand Down
38 changes: 34 additions & 4 deletions library/src/scene_impl.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,35 @@ scene& scene_impl::add(const std::vector<fs::path>& filePaths)
}

//----------------------------------------------------------------------------
scene& scene_impl::add(const double startTime, const double endTime, MeshCallback&& callback)
{
if (startTime > endTime)
{
throw scene::load_failure_exception("startTime must be less than or equal to endTime");
}

vtkNew<vtkF3DMemoryMesh> vtkSource;

auto wrappedCallback = [cb = std::move(callback)](double time, vtkF3DMemoryMesh* vtkMesh)
{
mesh_t mesh = cb(time);
vtkMesh->SetPoints(mesh.points);
vtkMesh->SetNormals(mesh.normals);
vtkMesh->SetTCoords(mesh.texture_coordinates);
vtkMesh->SetFaces(mesh.face_sides, mesh.face_indices);
};

vtkSource->SetAnimatedMesh(startTime, endTime, std::move(wrappedCallback));

auto importer = vtkSmartPointer<vtkF3DGenericImporter>::New();
importer->SetInternalReader(vtkSource);

log::debug("Loading animated 3D scene from memory");

this->Internals->Load({ importer });
return *this;
}

scene& scene_impl::add(const mesh_t& mesh)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you should be able to call the other method here ?

{
// sanity checks
Expand All @@ -306,16 +335,18 @@ scene& scene_impl::add(const mesh_t& mesh)
}

vtkNew<vtkF3DMemoryMesh> vtkSource;
auto importer = vtkSmartPointer<vtkF3DGenericImporter>::New();

log::debug("Loading 3D scene from memory");

vtkSource->SetPoints(mesh.points);
vtkSource->SetNormals(mesh.normals);
vtkSource->SetTCoords(mesh.texture_coordinates);
vtkSource->SetFaces(mesh.face_sides, mesh.face_indices);

vtkSmartPointer<vtkF3DGenericImporter> importer = vtkSmartPointer<vtkF3DGenericImporter>::New();
importer->SetInternalReader(vtkSource);
Internals->Load({ importer });

log::debug("Loading 3D scene from memory");
this->Internals->Load({ importer });
return *this;
}

Expand All @@ -327,7 +358,6 @@ scene& scene_impl::clear()

// Clear the window of all actors
this->Internals->Window.Initialize();

return *this;
}

Expand Down
1 change: 1 addition & 0 deletions library/testing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ list(APPEND libf3dSDKTests_list
TestSDKInteractorDropFullScene.cxx
TestSDKInteractorCommand.cxx
TestSDKSceneFromMemory.cxx
TestSDKSceneTemporalFromMemory.cxx
TestSDKScene.cxx
TestSDKLog.cxx
TestSDKMultiColoring.cxx
Expand Down
62 changes: 62 additions & 0 deletions library/testing/TestSDKSceneTemporalFromMemory.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#include "PseudoUnitTest.h"

#include <engine.h>
#include <image.h>
#include <log.h>
#include <scene.h>
#include <window.h>

#include <cmath>
#include <sstream>
#include <string>
#include <vector>

int TestSDKSceneTemporalFromMemory([[maybe_unused]] int argc, [[maybe_unused]] char* argv[])
{
PseudoUnitTest test;

f3d::log::setVerboseLevel(f3d::log::VerboseLevel::DEBUG);
f3d::engine eng = f3d::engine::create(true);
f3d::scene& sce = eng.getScene();
f3d::window& win = eng.getWindow().setSize(300, 300);

// Define mesh data for two frames
const std::vector<float> basePoints{ 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 1.f, 0.f, 0.f, 1.f, 1.f, 0.f };
const std::vector<float> translatedPoints{ 0.25f, 0.f, 0.f, 0.25f, 1.f, 0.f, 1.25f, 0.f, 0.f,
1.25f, 1.f, 0.f };
const std::vector<float> normals{ 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f, -1.f, 0.f, 0.f,
-1.f };
const std::vector<float> tcoords{ 0.f, 0.f, 0.f, 1.f, 1.f, 0.f, 1.f, 1.f };
const std::vector<unsigned int> faceSides{ 3, 3 };
const std::vector<unsigned int> faceIndices{ 0, 1, 2, 1, 3, 2 };

auto meshCallback = [&](double time) -> f3d::mesh_t
{
if (time < 0.5)
{
return f3d::mesh_t{ basePoints, normals, tcoords, faceSides, faceIndices };
}
else
{
return f3d::mesh_t{ translatedPoints, normals, tcoords, faceSides, faceIndices };
}
};

test("add animated mesh with callback", [&]() { sce.add(0.0, 1.0, meshCallback); });

test("animation time range", [&]() {
auto range = sce.animationTimeRange();
return std::abs(range.first - 0.0) < 1e-6 && std::abs(range.second - 1.0) < 1e-6;
});

test("quantitative temporal difference", [&]() {
sce.loadAnimationTime(0.0);
f3d::image frame0 = win.renderToImage();
sce.loadAnimationTime(1.0);
f3d::image frame1 = win.renderToImage();
double error = frame0.compare(frame1);
return error > 0.01;
});

return test.result();
}
65 changes: 55 additions & 10 deletions vtkext/private/module/vtkF3DMemoryMesh.cxx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
#include "vtkF3DMemoryMesh.h"

#include "vtkDataArrayRange.h"
#include "vtkCellArray.h"
#include "vtkFloatArray.h"
#include "vtkIdTypeArray.h"
#include "vtkInformation.h"
#include "vtkInformationVector.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkPoints.h"
#include "vtkPolyData.h"
#include "vtkSMPTools.h"

#include <numeric>
#include "vtkStreamingDemandDrivenPipeline.h"

vtkStandardNewMacro(vtkF3DMemoryMesh);

Expand Down Expand Up @@ -51,20 +53,28 @@ void vtkF3DMemoryMesh::SetPoints(const std::vector<float>& positions)
vtkNew<vtkPoints> points;
points->SetDataTypeToFloat();
points->SetData(ConvertToFloatArray<3>(positions));

this->Mesh->SetPoints(points);
this->StaticMesh->SetPoints(points);
this->Modified();
}

//------------------------------------------------------------------------------
void vtkF3DMemoryMesh::SetNormals(const std::vector<float>& normals)
{
this->Mesh->GetPointData()->SetNormals(ConvertToFloatArray<3>(normals));
if (!normals.empty())
{
this->StaticMesh->GetPointData()->SetNormals(ConvertToFloatArray<3>(normals));
this->Modified();
}
}

//------------------------------------------------------------------------------
void vtkF3DMemoryMesh::SetTCoords(const std::vector<float>& tcoords)
{
this->Mesh->GetPointData()->SetTCoords(ConvertToFloatArray<2>(tcoords));
if (!tcoords.empty())
{
this->StaticMesh->GetPointData()->SetTCoords(ConvertToFloatArray<2>(tcoords));
this->Modified();
}
}

//------------------------------------------------------------------------------
Expand Down Expand Up @@ -96,17 +106,52 @@ void vtkF3DMemoryMesh::SetFaces(

vtkNew<vtkCellArray> polys;
polys->SetData(offsets, connectivity);
this->StaticMesh->SetPolys(polys);
this->Modified();
}

this->Mesh->SetPolys(polys);
//------------------------------------------------------------------------------
void vtkF3DMemoryMesh::SetAnimatedMesh(double startTime, double endTime, MeshCallback callback)
{
this->AnimatedCallback = std::move(callback);
this->TimeRange[0] = startTime;
this->TimeRange[1] = endTime;
this->IsAnimated = true;
this->Modified();
}

//------------------------------------------------------------------------------
int vtkF3DMemoryMesh::RequestInformation(vtkInformation* vtkNotUsed(request),
vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* outputVector)
{
vtkInformation* outInfo = outputVector->GetInformationObject(0);

if (this->IsAnimated)
{
outInfo->Set(vtkStreamingDemandDrivenPipeline::TIME_RANGE(), this->TimeRange, 2);
}

return 1;
}

//------------------------------------------------------------------------------
int vtkF3DMemoryMesh::RequestData(vtkInformation* vtkNotUsed(request),
vtkInformationVector** vtkNotUsed(inputVector), vtkInformationVector* outputVector)
{
vtkPolyData* output = vtkPolyData::GetData(outputVector->GetInformationObject(0));
vtkInformation* outInfo = outputVector->GetInformationObject(0);
vtkPolyData* output = vtkPolyData::GetData(outInfo);

if (this->IsAnimated && this->AnimatedCallback)
{
double requestedTime = this->TimeRange[0];
if (outInfo->Has(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP()))
{
requestedTime = outInfo->Get(vtkStreamingDemandDrivenPipeline::UPDATE_TIME_STEP());
}
this->AnimatedCallback(requestedTime, this);
}

output->ShallowCopy(this->Mesh);
output->ShallowCopy(this->StaticMesh);

return 1;
}
38 changes: 29 additions & 9 deletions vtkext/private/module/vtkF3DMemoryMesh.h
Original file line number Diff line number Diff line change
@@ -1,31 +1,38 @@
/**
* @class vtkF3DMemoryMesh
* @brief create vtkPolyData from vectors.
* @brief create vtkPolyData from vectors or a callback.
*
* Simple source which convert and copy vectors provided by the user
* to internal structure of vtkPolyData.
* Does not support point data (normals, tcoords...) nor cell data yet.
* Simple source which converts vectors provided by the user
* to internal structure of vtkPolyData, or uses a callback for animated mesh generation.
*/
#ifndef vtkF3DMemoryMesh_h
#define vtkF3DMemoryMesh_h

#include "vtkPolyDataAlgorithm.h"

#include <functional>

class vtkF3DMemoryMesh : public vtkPolyDataAlgorithm
{
public:
static vtkF3DMemoryMesh* New();
vtkTypeMacro(vtkF3DMemoryMesh, vtkPolyDataAlgorithm);

/**
* Set contiguous list of positions.
* Callback type for animated meshes.
* Called with the current time value, should populate the mesh using the Set* methods.
*/
using MeshCallback = std::function<void(double time, vtkF3DMemoryMesh* mesh)>;

/**
* Set contiguous list of positions for a static mesh.
* Length of the list must be a multiple of 3.
* The list is copied internally.
*/
void SetPoints(const std::vector<float>& positions);

/**
* Set contiguous list of normals.
* Set contiguous list of normals for a static mesh.
* Length of the list must be a multiple of 3 (or left empty).
* Must match the number of points specified in SetPoints.
* The list is copied internally.
Expand All @@ -34,7 +41,7 @@ class vtkF3DMemoryMesh : public vtkPolyDataAlgorithm
void SetNormals(const std::vector<float>& normals);

/**
* Set contiguous list of texture coordinates.
* Set contiguous list of texture coordinates for a static mesh.
* Length of the list must be a multiple of 2 (or left empty).
* Must match the number of points specified in SetPoints.
* The list is copied internally.
Expand All @@ -43,7 +50,7 @@ class vtkF3DMemoryMesh : public vtkPolyDataAlgorithm
void SetTCoords(const std::vector<float>& tcoords);

/**
* Set faces by vertex indices.
* Set faces by vertex indices for a static mesh.
* faceSizes contains the size of each face (3 is triangle, 4 is quad, etc...)
* cellIndices is a contiguous array of all face indices
* The length of faceIndices should be the sum of all values in faceSizes
Expand All @@ -53,17 +60,30 @@ class vtkF3DMemoryMesh : public vtkPolyDataAlgorithm
void SetFaces(
const std::vector<unsigned int>& faceSizes, const std::vector<unsigned int>& faceIndices);

/**
* Set a callback for animated mesh generation.
* The callback is invoked at each requested time and should populate the mesh
* using SetPoints, SetNormals, SetTCoords, and SetFaces methods.
* This switches the mesh to animated mode with the given time range.
*/
void SetAnimatedMesh(double startTime, double endTime, MeshCallback callback);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use std::vector here too


protected:
vtkF3DMemoryMesh();
~vtkF3DMemoryMesh() override;

int RequestInformation(vtkInformation*, vtkInformationVector**, vtkInformationVector*) override;
int RequestData(vtkInformation*, vtkInformationVector**, vtkInformationVector*) override;

private:
vtkF3DMemoryMesh(const vtkF3DMemoryMesh&) = delete;
void operator=(const vtkF3DMemoryMesh&) = delete;

vtkNew<vtkPolyData> Mesh;
vtkNew<vtkPolyData> StaticMesh;

MeshCallback AnimatedCallback;
double TimeRange[2] = { 0.0, 0.0 };
bool IsAnimated = false;
};

#endif