#include <type_traits>
#include <utility>
#include <cassert>
#include <limits>
#include <cmath>
#include <thread>
#include <maya/MPxNode.h>
#include <maya/MFnNumericAttribute.h>
#include <maya/MFnUnitAttribute.h>
#include <maya/MFnCompoundAttribute.h>
#include <maya/MString.h>
#include <maya/MTypeId.h>
#include <maya/MPlug.h>
#include <maya/MDataBlock.h>
#include <maya/MDataHandle.h>
#include <maya/MFnPlugin.h>
#include <maya/MNodeCacheDisablingInfoHelper.h>
#include <maya/MDGMessage.h>
#include <maya/MItDependencyNodes.h>
#include <maya/MFn.h>
#include <maya/MDGModifier.h>
namespace {
struct MDouble3 { double x, y, z; };
struct MDouble4 { double x, y, z, w; };
MDouble3 operator+ (MDouble3 lhs, MDouble3 rhs) noexcept { return { lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z }; }
MDouble3 operator- (MDouble3 lhs, MDouble3 rhs) noexcept { return { lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z }; }
MDouble3 operator* (MDouble3 lhs, MDouble3 rhs) noexcept { return { lhs.x * rhs.x, lhs.y * rhs.y, lhs.z * rhs.z }; }
MDouble3 operator* (MDouble3 lhs, double rhs) noexcept { return { lhs.x * rhs, lhs.y * rhs, lhs.z * rhs }; }
MDouble3 operator/ (MDouble3 lhs, MDouble3 rhs) noexcept { return { lhs.x / rhs.x, lhs.y / rhs.y, lhs.z / rhs.z }; }
MDouble3 operator/ (MDouble3 lhs, double rhs) noexcept { return { lhs.x / rhs, lhs.y / rhs, lhs.z / rhs }; }
MDouble4 to4(MDouble3 v, double w) { return { v.x,v.y,v.z,w }; }
MDouble3 xyz(MDouble4 v) { return { v.x,v.y,v.z }; }
double dot(MDouble4 lhs, MDouble4 rhs) noexcept { return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z + lhs.w * rhs.w; }
double dot(MDouble3 lhs, MDouble3 rhs) noexcept { return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z; }
MDouble3 normalized(MDouble3 v) { return v / sqrt(dot(v,v)); }
MDouble3 point_on_plane(MDouble4 p) {
if (p.x != .0) {
return { -p.w / p.x, .0, .0 };
} else if (p.y != .0) {
return { .0, -p.w / p.y, .0 };
} else if (p.z != .0) {
return { .0, .0, -p.w / p.z };
}
return { .0,.0,.0 };
};
}
namespace{
template <typename T>
using type = T;
template <typename U>
};
namespace detail {
struct error_type {};
template <typename T>
decltype(
auto) as(const
MDataHandle& handle) {
return error_type{}; }
template <> decltype(
auto) as<
bool>(const
MDataHandle& handle) {
return handle.asBool(); }
template <> decltype(
auto) as<
double>(const
MDataHandle& handle) {
return handle.asDouble(); }
template <> decltype(
auto) as<
MTime>(const
MDataHandle& handle) {
return handle.asTime(); }
template <>
decltype(
auto) as<MDouble3>(const
MDataHandle& handle) {
return reinterpret_cast<MDouble3&>(handle.asDouble3());
}
template <>
decltype(
auto) as<MDouble4>(const
MDataHandle& handle) {
return reinterpret_cast<MDouble4&>(handle.asDouble4());
}
}
template <typename T>
decltype(
auto) get_input(
MDataBlock& data, const MAttributeOf<T>& attr) {
MDataHandle handle = data.inputValue(static_cast<const MObject&>(attr), &status);
return ::detail::as<T>(handle);
}
template <typename T>
decltype(
auto) get_as_is(
MDataBlock& data, const MAttributeOf<T>& attr) {
MDataHandle handle = data.outputValue(static_cast<const MObject&>(attr), &status);
return ::detail::as<T>(handle);
}
template <typename T>
void set(
MDataBlock& data,
const MAttributeOf<T>& attr,
const T& value) {
::detail::as<T>(handle) = value;
}
}
namespace physics {
struct ObjectStatus {
MDouble3 Position;
MDouble3 Velocity;
};
ObjectStatus apply_velocity(
const ObjectStatus& prev,
MTime dt, MDouble3 acceleration) {
ObjectStatus now;
now.Time = prev.Time + dt;
now.Velocity = prev.Velocity + acceleration * dts;
now.Position = prev.Position + (now.Velocity + prev.Velocity) * (0.5 * dts);
return now;
}
ObjectStatus resolve_collision(ObjectStatus particle, MDouble4 ground, double elasticity) {
assert(elasticity >= 0);
MDouble4 p = to4(particle.Position, 1.0);
double v = dot(p, ground);
if (v > .0) {
} else {
MDouble3 gp = point_on_plane(ground);
MDouble3 n = normalized(xyz(ground));
MDouble3 dp = particle.Position - gp;
dp = n * (dot(dp, n) * (1 + elasticity));
particle.Position = particle.Position - dp;
dp = n * (dot(particle.Velocity, n) * (1 + elasticity));
particle.Velocity = particle.Velocity - dp;
}
return particle;
}
}
class physicsEngine final :
public MPxNode
{
public:
static void* creator();
public:
static MAttributeOf<bool> aSimulationEnabled;
static MAttributeOf<MTime> aComputeLatency;
static MAttributeOf<MTime> aInputTime;
static MAttributeOf<MTime> aCurrentTime;
static MAttributeOf<MDouble3> aCurrentPosition;
static MAttributeOf<MDouble3> aCurrentVelocity;
static MAttributeOf<MTime> aInitialTime;
static MAttributeOf<MDouble3> aInitialPosition;
static MAttributeOf<MDouble3> aInitialVelocity;
static MAttributeOf<double> aMass;
static MAttributeOf<MDouble3> aForce;
static MAttributeOf<MDouble4> aCollisionPlane;
static MAttributeOf<double> aCollisionElasticity;
public:
};
MTypeId physicsEngine::id = { 0x00081165 };
MString physicsEngine::nodeName = {
"physicsEngine" };
MAttributeOf<MTime> physicsEngine::aInputTime;
MObject physicsEngine::aCurrentStatus;
MAttributeOf<MTime> physicsEngine::aCurrentTime;
MAttributeOf<MDouble3> physicsEngine::aCurrentPosition;
MAttributeOf<MDouble3> physicsEngine::aCurrentVelocity;
MObject physicsEngine::aInitialStatus;
MAttributeOf<MTime> physicsEngine::aInitialTime;
MAttributeOf<MDouble3> physicsEngine::aInitialPosition;
MAttributeOf<MDouble3> physicsEngine::aInitialVelocity;
MAttributeOf<MDouble3> physicsEngine::aForce;
MAttributeOf<double> physicsEngine::aMass;
MAttributeOf<MDouble4> physicsEngine::aCollisionPlane;
MAttributeOf<double> physicsEngine::aCollisionElasticity;
MAttributeOf<bool> physicsEngine::aSimulationEnabled;
MAttributeOf<MTime> physicsEngine::aComputeLatency;
static constexpr MTime::MTick kMaximumTimeTick = std::numeric_limits<MTime::MTick>::max() / 2;
static constexpr MTime::MTick kMinimumTimeTick = std::numeric_limits<MTime::MTick>::min() / 2 + 1;
{
using namespace physics;
if (plug == aCurrentStatus || plug.
parent() == aCurrentStatus)
{
MTime time = get_input(data, aInputTime);
MTime start = get_input(data, aInitialTime);
ObjectStatus status;
status.Time = get_as_is(data, aCurrentTime);
status.Position = get_as_is(data, aCurrentPosition);
status.Velocity = get_as_is(data, aCurrentVelocity);
if (status.Time <= start || time <= start) {
status.Time = start;
status.Position = get_input(data, aInitialPosition);
status.Velocity = get_input(data, aInitialVelocity);
}
assert(status.Time >= start);
bool simulation = get_input(data, aSimulationEnabled);
if (simulation && time > status.Time) {
MTime dt = time - status.Time;
MDouble3 force = get_input(data, aForce);
double mass = get_input(data, aMass);
MDouble4 ground = get_input(data, aCollisionPlane);
double damp = get_input(data, aCollisionElasticity);
status = apply_velocity(status, dt, force / mass);
status = resolve_collision(status, ground, damp);
MTime latency = get_input(data, aComputeLatency);
if (latency >
MTime{ 0.0 }) {
std::this_thread::sleep_for(std::chrono::duration<double>{ latency.
as(
MTime::kSeconds) });
}
}
set(data, aCurrentTime, status.Time);
set(data, aCurrentPosition, status.Position);
set(data, aCurrentVelocity, status.Velocity);
return MS::kSuccess;
}
return MS::kUnknownParameter;
}
{
if (evalType == kEvaluatedDirectly && context.
isNormal())
{
}
}
{
bool simulation;
assert(!"'physicsEngine.simulation' cannot not be animated");
simulation = true;
} else {
auto data = const_cast<physicsEngine*>(this)->forceCache();
simulation = get_input(data, aSimulationEnabled);
monitoredAttributes.
append(aSimulationEnabled);
}
}
}
{
{
schema.
add(aCurrentStatus);
}
}
{
auto data = const_cast<physicsEngine*>(this)->forceCache();
auto start = get_as_is(data, aInitialTime);
auto simulation = get_as_is(data, aSimulationEnabled) || !data.
isClean(aSimulationEnabled);
if (simulation && input.
intersects(start, kMaximumTime)) {
}
return input;
}
void* physicsEngine::creator()
{
return new physicsEngine();
}
MStatus physicsEngine::initialize()
{
aCurrentStatus = cAttr.
create(
"status",
"s");
aInitialStatus = cAttr.
create(
"initialStatus",
"is");
status = addAttribute(aSimulationEnabled);
status = addAttribute(aComputeLatency);
status = addAttribute(aInputTime);
status = addAttribute(aCurrentStatus);
status = addAttribute(aInitialStatus);
status = addAttribute(aForce);
status = addAttribute(aMass);
status = addAttribute(aCollisionPlane);
status = addAttribute(aCollisionElasticity);
status = attributeAffects(aSimulationEnabled, aCurrentStatus);
status = attributeAffects(aInputTime, aCurrentStatus);
status = attributeAffects(aInitialStatus, aCurrentStatus);
status = attributeAffects(aForce, aCurrentStatus);
status = attributeAffects(aMass, aCurrentStatus);
status = attributeAffects(aCollisionPlane, aCurrentStatus);
status = attributeAffects(aCollisionElasticity, aCurrentStatus);
return MS::kSuccess;
}
MCallbackId gNodeAddedCallbackId;
{
MFnPlugin plugin(obj, PLUGIN_COMPANY,
"1.0",
"Any");
status = plugin.registerNode(
physicsEngine::nodeName,
physicsEngine::id,
physicsEngine::creator,
physicsEngine::initialize
);
if (!status) {
status.
perror(
"registerNode");
return status;
}
static MPlug timeOutPlug;
{
timeOutPlug = { timeNode, fDN.attribute("outTime") };
}
MPlug timeInPlug { node, physicsEngine::aInputTime };
if (!timeOutPlug.
isNull() && !timeInPlug.isNull()) {
modifier.
connect(timeOutPlug, timeInPlug);
}
}, physicsEngine::nodeName);
return status;
}
{
status = plugin.deregisterNode(physicsEngine::id);
if (!status) {
status.
perror(
"deregisterNode");
return status;
}
return status;
}