Compare commits
10 Commits
6b625ea251
...
375a98b664
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
375a98b664 | ||
|
|
21323d092c | ||
|
|
acf459bc5a | ||
|
|
e8a1427c0a | ||
|
|
0e5a929408 | ||
| 4eb59a59a6 | |||
| e41db66cca | |||
| c9461b4347 | |||
| 45c2e757e6 | |||
| b8f8dfad43 |
19
README.md
19
README.md
@@ -3,15 +3,18 @@
|
||||
</div>
|
||||
<h1 align="center">Enzo 3D</h1>
|
||||
|
||||
<img align="left" src="https://parkerbritt.com/jenkins_badge?job=enzo">
|
||||
<img align="left" src="https://cards.parkerbritt.com/jenkins_badge?job=enzo">
|
||||
<div align="right">
|
||||
<img src="https://parkerbritt.com/badge?label=C%2B%2B&icon=cplusplus&color=00599C">
|
||||
<img src="https://parkerbritt.com/badge?label=opengl&icon=opengl&color=5586A4">
|
||||
<img src="https://parkerbritt.com/badge?label=qt&icon=qt&color=41CD52">
|
||||
<img src="https://cards.parkerbritt.com/badge?label=C%2B%2B&icon=cplusplus&color=00599C">
|
||||
<img src="https://cards.parkerbritt.com/badge?label=opengl&icon=opengl&color=5586A4">
|
||||
<img src="https://cards.parkerbritt.com/badge?label=qt&icon=qt&color=41CD52">
|
||||
</div>
|
||||
|
||||
<div align="center">
|
||||
Enzo is a 3D animation software application focused on procedural geometry editing.
|
||||
<div align="justify">
|
||||
Enzo is a node-based procedural 3D modeling framework for Visual Effects and Animation.<br><br>
|
||||
|
||||
It was created as an accessible alternative to existing industry tools, which are often prohibitively expensive, closed source, and resistant to user driven innovation.
|
||||
Enzo is currently designed as a proof of concept and hobby project. The goal is to build a lean functional foundation for further development, experimentation, and community contribution.
|
||||
</div>
|
||||
<br><br>
|
||||
<img src="https://github.com/user-attachments/assets/e919e41b-f41f-41b9-8aec-082d53fed470">
|
||||
@@ -33,5 +36,5 @@ Enzo is a 3D animation software application focused on procedural geometry editi
|
||||
|
||||
|
||||
|
||||
## Docs
|
||||
Check out the api [here!](https://parkerbritt.github.io/enzo/)
|
||||
## Documentation
|
||||
Doxygen documentation is generated automatically and can be accessed [here.](https://parkerbritt.github.io/enzo/)
|
||||
|
||||
@@ -7,30 +7,95 @@
|
||||
#include <QObject>
|
||||
|
||||
namespace enzo::nt {
|
||||
/**
|
||||
* @brief The central coordinator of the engine's node system.
|
||||
*
|
||||
* The network manager is the central coordinator of the engine’s node system.
|
||||
* It manages the lifecycle of operators, including their creation, storage,
|
||||
* and validation, while also tracking dependencies between them. Acting
|
||||
* as a singleton, it ensures that all parts of the engine work with a single
|
||||
* consistent view of the network, providing global access. Beyond just storing
|
||||
* operators, it also controls cooking and traversing dependency graphs,
|
||||
* ensuring that updates flow correctly through the network when nodes change.
|
||||
*
|
||||
* @todo remove Qobject inheritance, this is no longer needed since switching to boost signals.
|
||||
*
|
||||
*/
|
||||
class NetworkManager
|
||||
: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
// delete copy constructor
|
||||
/// @brief Deleted the copy constructor for singleton.
|
||||
NetworkManager(const NetworkManager& obj) = delete;
|
||||
|
||||
/// @brief Returns a reference to the singleton instance.
|
||||
static NetworkManager& getInstance();
|
||||
|
||||
// functions
|
||||
/**
|
||||
* @brief Creates a new node in the network
|
||||
*
|
||||
* @param OpInfo Data designating the properties of the node.
|
||||
*
|
||||
* @returns The operator ID of the newly created node
|
||||
*/
|
||||
OpId addOperator(op::OpInfo opInfo);
|
||||
|
||||
/** @brief Returns the operator ID for the node with its display flag set.
|
||||
* There can only be only be one operator displayed at a time.
|
||||
* Return value is nullopt if no node is set to display
|
||||
*/
|
||||
std::optional<OpId> getDisplayOp();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Returns whether the node exists in the network and is valid.
|
||||
* @param opId Operator ID of the node to check the validity of.
|
||||
*/
|
||||
bool isValidOp(nt::OpId opId);
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the GeometryOperator with the given OpId
|
||||
*/
|
||||
GeometryOperator& getGeoOperator(nt::OpId opId);
|
||||
|
||||
/**
|
||||
* @brief Sets given OpId to be displayed, releasing previous display Node
|
||||
*/
|
||||
void setDisplayOp(OpId opId);
|
||||
|
||||
/**
|
||||
* @brief Set the selection state for the given node.
|
||||
*
|
||||
* @param opId The node to set the state on.
|
||||
* @param selected The selection state, true selects the node, false unselects it.
|
||||
* @param add By default all other nodes are unselected, this parameter
|
||||
* allows adding a selected node without deslecting any others.
|
||||
*/
|
||||
void setSelectedNode(OpId opId, bool selected, bool add=false);
|
||||
|
||||
/**
|
||||
* @brief Returns the OpIds for all selected nodes.
|
||||
*/
|
||||
const std::vector<enzo::nt::OpId>& getSelectedNodes();
|
||||
|
||||
/** @name Signals
|
||||
* @{
|
||||
*/
|
||||
// @brief A signal emitted when the display node is changed
|
||||
boost::signals2::signal<void (nt::OpId)> displayNodeChanged;
|
||||
boost::signals2::signal<void (std::vector<nt::OpId> selectedNodeIds)> selectedNodesChanged;
|
||||
|
||||
// @brief A signal emitted when the geometry to be displayed is changed
|
||||
// This is different to #displayNodeChanged because the state of geometry
|
||||
// in a node can change based on parameters or other factors.
|
||||
boost::signals2::signal<void (enzo::geo::Geometry& geometry)> displayGeoChanged;
|
||||
|
||||
// @brief A signal emitted when the selection of nodes changes
|
||||
boost::signals2::signal<void (std::vector<nt::OpId> selectedNodeIds)> selectedNodesChanged;
|
||||
/** @} */
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
/// @brief For use in unit tests, resets the state of the operator.
|
||||
void _reset();
|
||||
#endif
|
||||
|
||||
|
||||
@@ -10,6 +10,12 @@ namespace enzo::nt
|
||||
|
||||
namespace enzo::op
|
||||
{
|
||||
/**
|
||||
* @class Context
|
||||
* @brief Provides network context for the cookOp function.
|
||||
*
|
||||
* The context class is an interface for the cookOp function that provides important runtime context about the network. It allows querying parameters, reading input geometry, and in the future provides values like time.
|
||||
*/
|
||||
class Context
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -53,6 +53,9 @@ using attributeIterator = std::vector<std::shared_ptr<ga::Attribute>>::iterator;
|
||||
class Geometry
|
||||
{
|
||||
public:
|
||||
Geometry();
|
||||
Geometry(const Geometry& other);
|
||||
|
||||
/**
|
||||
* @brief Assignment operator. Performs a deep copy of another Geometry.
|
||||
* @param rhs The Geometry object to copy from.
|
||||
|
||||
@@ -6,24 +6,56 @@
|
||||
namespace enzo::nt
|
||||
{
|
||||
class GeometryOperator;
|
||||
|
||||
/**
|
||||
* @class GeometryConnection
|
||||
* @brief Directional edges to connect nodes
|
||||
*
|
||||
*
|
||||
* @todo Currently geometry connections are stored on nodes. It might
|
||||
* make more sense to move them to a container within the network manager
|
||||
* so they're all in the same place
|
||||
*
|
||||
* Input and output are in relation to data flow.
|
||||
*
|
||||
* @todo Currently geometry connections are stored on nodes. It might
|
||||
* make more sense to move them to a container within the network manager
|
||||
* so they're all in the same place
|
||||
*/
|
||||
|
||||
class GeometryConnection
|
||||
{
|
||||
public:
|
||||
// input and output are in relation to data flow
|
||||
// the input node is the node the data flows from
|
||||
// the output node is the node the data flows to
|
||||
GeometryConnection(enzo::nt::OpId inputOpId, unsigned int inputIndex, enzo::nt::OpId outputOpId, unsigned int outputIndex);
|
||||
/**
|
||||
* @brief Constructs a connection between two nodes
|
||||
*
|
||||
* Input and output are in relation to data flow.
|
||||
*
|
||||
* @param inputOpId The [Operator ID](@ref OpId) in which data flows from.
|
||||
* @param outputOpId The [Operator ID](@ref OpId) in which data flows to.
|
||||
* @param inputOpIndex The output socket number of @p inputOpId in which data flows from.
|
||||
* @param outputOpIndex The input socket number of @p outputOpId in which data flows to.
|
||||
*/
|
||||
GeometryConnection(enzo::nt::OpId inputOpId, unsigned int inputOpIndex, enzo::nt::OpId outputOpId, unsigned int outputOpIndex);
|
||||
|
||||
/// @brief Returns the [Operator ID](@ref OpId) of the connection input (where data flows from).
|
||||
enzo::nt::OpId getInputOpId() const;
|
||||
/// @brief Returns the [Operator ID](@ref OpId) of the connection output (where data flows to).
|
||||
enzo::nt::OpId getOutputOpId() const;
|
||||
/// @brief Returns the socket number of #getInputOpId in which data flows from.
|
||||
unsigned int getInputIndex() const;
|
||||
/// @brief Returns the socket number of #getOutputOpId in which data flows to.
|
||||
unsigned int getOutputIndex() const;
|
||||
|
||||
/// @brief Provides an ostream representation of the connection, useful for debugging.
|
||||
friend std::ostream& operator<<(std::ostream& os, const GeometryConnection& p)
|
||||
{
|
||||
return os << p.inputOperatorId_ << ":" << p.inputIndex_ << " -> " << p.outputOperatorId_ << ":" << p.outputIndex_ << "\n";
|
||||
}
|
||||
|
||||
/// @brief A signal emitted when the connection is removed
|
||||
boost::signals2::signal<void ()> removed;
|
||||
/// @brief Removes the connection from it's associated nodes. Does not delete the object.
|
||||
void remove();
|
||||
// bool isValid();
|
||||
private:
|
||||
|
||||
@@ -13,17 +13,58 @@ namespace enzo::nt
|
||||
{
|
||||
class NetworkManager;
|
||||
|
||||
/**
|
||||
* @brief Abstract class used to create new operators.
|
||||
*
|
||||
* The operator definition is a base class from which new geometry operators
|
||||
* are inherited from. It provides and abstracted interface, to read and
|
||||
* write data about itself and the context it is being computed.
|
||||
*
|
||||
* The class exposes utility functions for setting outputs and reading information
|
||||
* about itself like the number of inputs.
|
||||
*
|
||||
* The most important part of this node is the virtual cookOp member function.
|
||||
* This must be overridden to implement the node's logic when being cooked.
|
||||
* When a node is cooked it takes the optional input geometry from the context
|
||||
* class and outputs new geometry based on the purpose of that operator.
|
||||
*/
|
||||
class BOOST_SYMBOL_EXPORT GeometryOpDef
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Sets up internal state
|
||||
*/
|
||||
GeometryOpDef(nt::NetworkManager* network, op::OpInfo opInfo);
|
||||
/**
|
||||
* @brief This function is called at runtime to create the output geometry
|
||||
*
|
||||
* @post When overriding, this function must call setOutputGeo(n) at
|
||||
* the end of a successful cook. Any outputs that are not set will output
|
||||
* an emtpy geometry object.
|
||||
*/
|
||||
virtual void cookOp(op::Context context) = 0;
|
||||
|
||||
/**
|
||||
* @brief Returns the current output geometry.
|
||||
*
|
||||
* For use by the runtime Node
|
||||
* Does not force a cook.
|
||||
*
|
||||
* @todo move to friend class Node
|
||||
*/
|
||||
geo::Geometry& getOutputGeo(unsigned outputIndex);
|
||||
|
||||
/**
|
||||
* @brief Stops the cook and displays an error. Use inside the #cookOp function.
|
||||
* @todo Add visual error to GUI
|
||||
*/
|
||||
void throwError(std::string error);
|
||||
|
||||
/// @brief Returns the minimum number of input connections required for the node to function. Set by op::OpInfo when registering the operator.
|
||||
unsigned int getMinInputs() const;
|
||||
/// @brief Returns the maximum number of input connections accepted by the node. Set by op::OpInfo when registering the operator.
|
||||
unsigned int getMaxInputs() const;
|
||||
/// @brief Returns the number of available outputs the node provides. Set by op::OpInfo when registering the operator.
|
||||
unsigned int getMaxOutputs() const;
|
||||
private:
|
||||
std::vector<enzo::geo::Geometry> outputGeometry_;
|
||||
|
||||
@@ -11,46 +11,149 @@
|
||||
namespace enzo::nt {
|
||||
std::weak_ptr<GeometryConnection> connectOperators(enzo::nt::OpId inputOpId, unsigned int inputIndex, enzo::nt::OpId outputOpId, unsigned int outputIndex);
|
||||
|
||||
/**
|
||||
* @class GeometryOperator
|
||||
* @brief The unique runtime representation of a node
|
||||
*/
|
||||
class GeometryOperator
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a new node
|
||||
*
|
||||
* @param opId the operator id assigned to this node. For most situations
|
||||
* this should be set by the nt::NetworkManager
|
||||
* @param opInfo The data class informing the node what its properties
|
||||
* are that set it apart from other nodes. This is what makes a grid
|
||||
* node different to a transform node.
|
||||
*/
|
||||
GeometryOperator(enzo::nt::OpId opId, op::OpInfo opInfo);
|
||||
|
||||
// disable copying
|
||||
/// @brief Deleted copy constructor to avoid accidental copies.
|
||||
GeometryOperator(const GeometryOperator&) = delete;
|
||||
/// @brief Deleted copy assignment operator to avoid accidental copies.
|
||||
GeometryOperator& operator=(const GeometryOperator&) = delete;
|
||||
|
||||
/// @brief Computes the output geometry based on the [cookOp](@ref nt::GeometryOpDef::cookOp)
|
||||
/// definition in nt::GeometryOpDef. This is set by the @p opInfo constructor parameter
|
||||
void cookOp(op::Context context);
|
||||
|
||||
/**
|
||||
* @brief Returns the current output geometry.
|
||||
*
|
||||
* Does not trigger a cook so if the geometry may be outdated if not cooked first.
|
||||
*
|
||||
* @todo Add option to force cook or cook if dirty.
|
||||
*/
|
||||
geo::Geometry& getOutputGeo(unsigned int outputIndex) const;
|
||||
|
||||
/** @brief Adds a GeometryConnection to one of the inputs. Replacing old connections if needed.
|
||||
*
|
||||
* Which input is decided and stored on the connection.
|
||||
*
|
||||
* Nodes can only have one connection so it will automatically remove existing connections
|
||||
* with the same index, prioritizing the new one.
|
||||
*/
|
||||
void addInputConnection(std::shared_ptr<nt::GeometryConnection> connection);
|
||||
|
||||
/** @brief Adds a GeometryConnection to one of the outputs. Replacing old connections if needed.
|
||||
*
|
||||
* Which output is decided and stored on the connection.
|
||||
*
|
||||
* Nodes can only have one connection so it will automatically remove existing connections
|
||||
* with the same index, prioritizing the new one.
|
||||
*/
|
||||
void addOutputConnection(std::shared_ptr<nt::GeometryConnection> connection);
|
||||
|
||||
/** @brief Removes an input from the node's container.
|
||||
*
|
||||
* Does not remove the connection from any other node it's connected
|
||||
* to, likely causing undefined behavior if called incorrectly.
|
||||
*
|
||||
* @todo remove in favor of the rewrite suggested in GeometryConnection
|
||||
* todo in which connections are handled by the network manager rather than individual nodes.
|
||||
*/
|
||||
void removeInputConnection(unsigned int inputIndex);
|
||||
|
||||
/** @brief Removes an output from the node's container.
|
||||
*
|
||||
* Does not remove the connection from any other node it's connected
|
||||
* to, likely causing undefined behavior if called incorrectly.
|
||||
*
|
||||
* @todo remove in favor of the rewrite suggested in GeometryConnection
|
||||
* todo in which connections are handled by the network manager rather than individual nodes.
|
||||
*/
|
||||
void removeOutputConnection(const nt::GeometryConnection* connection);
|
||||
|
||||
/**
|
||||
* @brief Returns a vector containing weak pointers for all input connections.
|
||||
*
|
||||
* Connections returned by this function are weak pointers to indicate
|
||||
* ownership belongs to the node/network and can be modified or deleted at any time.
|
||||
*/
|
||||
std::vector<std::weak_ptr<const GeometryConnection>> getInputConnections() const;
|
||||
|
||||
/**
|
||||
* @brief Returns a vector containing weak pointers for all output connections.
|
||||
*
|
||||
* Connections returned by this function are weak pointers to indicate
|
||||
* ownership belongs to the node/network and can be modified or deleted at any time.
|
||||
*/
|
||||
std::vector<std::weak_ptr<const GeometryConnection>> getOutputConnections() const;
|
||||
|
||||
/**
|
||||
* @brief Returns an optional connection from a specific input index.
|
||||
*
|
||||
* @returns Nullopt if the connection doesn't exist.
|
||||
*/
|
||||
std::optional<std::reference_wrapper<const GeometryConnection>> getInputConnection(size_t index) const;
|
||||
|
||||
/// @brief Returns all parameters belonging to this node.
|
||||
std::vector<std::weak_ptr<prm::Parameter>> getParameters();
|
||||
|
||||
/// @brief Returns a parameter with the given name belonging to this node.
|
||||
/// @returns Empty default constructed std::weak_ptr<prm::Parameter>() if no parameter of that name exists.
|
||||
std::weak_ptr<prm::Parameter> getParameter(std::string parameterName);
|
||||
|
||||
/**
|
||||
* @brief NOT YET IMPLEMENTED. Returns the runtime label given to this node as a unique identifier within it's scope.
|
||||
* @todo implement
|
||||
*/
|
||||
std::string getLabel(); // TODO: implement node labels
|
||||
|
||||
/**
|
||||
* @brief Returns the name belonging to this type of node (eg. grid or transform). Not to be confused with the label.
|
||||
*
|
||||
* The type name decided at compile time and is shared across all nodes
|
||||
* of the given type. All grids nodes share the same type name. Labels
|
||||
* on the other hand are unique identifiers for a given runtime node (eg. myGrid1, groundGrid, wall).
|
||||
*/
|
||||
std::string getTypeName();
|
||||
|
||||
/**
|
||||
* @brief Marks the outputed geometry as outdated and notifies the network
|
||||
*
|
||||
* @param dirtyDescendents Sets whether all descendents (nodes connected
|
||||
* directly or indirectly to the output of this node) are also dirtied.
|
||||
* This is usually what you want.
|
||||
*/
|
||||
void dirtyNode(bool dirtyDescendents=true);
|
||||
|
||||
/// @brief Returns true if the node is dirty and false if the node is clean (does not need cooking).
|
||||
bool isDirty();
|
||||
|
||||
|
||||
/// @brief Returns the minimum number of input connections required
|
||||
/// for the node to function. These are in order so 3 would mean the
|
||||
/// first three inputs must have a connection.
|
||||
unsigned int getMinInputs() const;
|
||||
/// @brief Returns the maximum number of input connections accepted by the node.
|
||||
unsigned int getMaxInputs() const;
|
||||
/// @brief Returns the number of available outputs the node provides.
|
||||
unsigned int getMaxOutputs() const;
|
||||
|
||||
// signals
|
||||
/// @brief A signal emitted when the node is dirtied. This will usually notify the NetworkManager
|
||||
boost::signals2::signal<void (nt::OpId opId, bool dirtyDescendents)> nodeDirtied;
|
||||
|
||||
|
||||
|
||||
private:
|
||||
void initParameters();
|
||||
|
||||
|
||||
@@ -74,6 +74,9 @@ namespace enzo
|
||||
}
|
||||
namespace nt
|
||||
{
|
||||
/**
|
||||
* @brief The unique ID assigned to each node in the network.
|
||||
*/
|
||||
using OpId = uint64_t;
|
||||
|
||||
enum class SocketIOType {
|
||||
|
||||
Reference in New Issue
Block a user