Example Scripts¶
Simulation graph in Python¶
The following Python script runs a simple smoke simulation and caches the result to disk. It demonstrates how to create a node graph and evaluate it from within Python.
import eddy
from eddy import *
if __name__ == '__main__':
eddy.startup()
# Create all the nodes and set properties.
sphere = create_node(type="SphereField")
sphere.set_property_value(name="radius", value=0.5)
iso_mask = create_node(type="IsoMaskField")
iso_mask.set_property_value(name="scalar_inside_value", value=5.0)
emitter = create_node(type="Emitter")
emitter.set_property_value(name="mode", value="Add")
element = create_node(type="SparseSmokeElement")
element.set_property_value(name="dx", value=0.02)
cache_writer = create_node(type="CacheWriter")
cache_writer.set_property_value(name="overwrite", value=True)
channel_list = ChannelList()
channel_list.add_scalar_channel(channel_name="density")
cache_writer.set_property_value(name="channel_list", value=channel_list)
# Connect the nodes into a graph.
connect(source_node=sphere, target_node=iso_mask)
connect(source_node=iso_mask, target_node=emitter)
connect(source_node=emitter, target_node=element, target_plug="density")
connect(source_node=element, target_node=cache_writer)
# Evaluate the graph for a range of frames.
evaluator = Evaluator()
prev_ticks = 0.0
for frame in range(1, 101):
print "Evaluating frame %d" % (frame)
current_ticks = seconds_to_ticks(frame/24.0)
context = EvaluationContext(start_ticks=prev_ticks, end_ticks=current_ticks, fps=24.0)
cache_writer.set_property_value(name="file", value="cache."+format(frame, '04d')+".vdb")
evaluator.evaluate(node=cache_writer, context=context)
prev_ticks = current_ticks
Rendering graph in Python¶
The following Python script builds a node graph to create a pyroclastic sphere field, rasterize it to a DenseVolume, and then render it. It is also possible to do this using more direct API functions instead of building a node graph. See below for an equivalent example that doesn’t use the node graph.
import eddy
import math
from eddy import *
if __name__ == '__main__':
eddy.startup()
# Create a pyroclastic sphere field.
sphere = create_node(type="SphereField")
sphere.set_property_value(name="radius", value=0.9)
iso_mask = create_node(type="IsoMaskField")
iso_mask.set_property_value(name="scalar_inside_value", value=2.0)
vector_noise = create_node(type="PerlinNoiseField")
vector_noise.set_property_value(name="mode", value="vector")
vector_noise.set_property_value(name="seed", value=4)
vector_noise.set_property_value(name="frequency", value=V3f(3.0))
vector_noise.set_property_value(name="vector_amplitude", value=V3f(1.4))
vector_noise.set_property_value(name="octaves", value=5)
vector_noise.set_property_value(name="lacunarity", value=2.0)
vector_noise.set_property_value(name="gain", value=0.5)
offset_lookup = create_node(type="OffsetLookupField")
offset_lookup.set_property_value(name="mode", value="trace")
offset_lookup.set_property_value(name="trace_steps", value=4)
offset_lookup.set_property_value(name="scale", value=0.2)
connect(source_node=sphere, target_node=iso_mask)
connect(source_node=iso_mask, target_node=offset_lookup, target_plug="field")
connect(source_node=vector_noise, target_node=offset_lookup, target_plug="offset_field")
# Rasterize the field. Not strictly necessary, as we could render the implicit field directly, but it is usually faster
# to render a rasterized version.
rasterize = create_node(type="RasterizeField", name="my_rasterizer")
rasterize.set_property_value(name="voxel_size", value=0.01)
connect(source_node=offset_lookup, target_node=rasterize)
# Create RenderVolume.
render_volume = create_node(type="SmokeRenderVolume")
render_volume.set_property_value(name="absorption_colour", value=C3f(0.36, 0.36, 0.4))
render_volume.set_property_value(name="scattering_colour", value=C3f(0.45, 0.45, 0.5))
connect(source_node=rasterize, target_node=render_volume, target_plug="density")
# Create PointLight.
light_transform = M44f()
light_transform.set_translation(V3f(5.0, 5.0, 5.0))
point_light = create_node(type="PointLight")
point_light.set_property_value(name="transform", value=light_transform)
point_light.set_property_value(name="intensity", value=V3f(20.0))
# Create RenderScene.
render_scene = create_node(type="RenderScene")
connect(source_node=render_volume, target_node=render_scene)
connect(source_node=point_light, target_node=render_scene)
# Create camera.
camera_transform = M44f()
camera_transform.set_translation(V3f(0.0, 0.0, 4.0))
horizontal_fov = math.pi / 3.0
vertical_fov = 2.0 * math.atan(math.tan(0.5 * math.pi / 3.0) * 9.0 / 16.0)
camera = create_node(type="Camera")
camera.set_property_value(name="transform", value=camera_transform)
camera.set_property_value(name="vertical_FOV", value=vertical_fov)
camera.set_property_value(name="horizontal_FOV", value=horizontal_fov)
# Create RenderData.
render_data = create_node(type="RenderData")
render_data.set_property_value(name="max_progressions", value=2000)
connect(source_node=camera, target_node=render_data)
connect(source_node=render_scene, target_node=render_data)
# Create renderer node.
render = create_node(type="Render")
render.set_property_value(name="width", value=1280)
render.set_property_value(name="height", value=720)
connect(source_node=render_data, target_node=render)
# Create ImageWriter to save the result.
image_writer = create_node(type="ImageWriter")
image_writer.set_property_value(name="filename", value="render_example.exr")
connect(source_node=render, source_plug="image", target_node=image_writer)
# Evaluate graph.
evaluator = Evaluator()
evaluator.evaluate(node=image_writer, context=EvaluationContext())
Rendering in Python¶
The following Python script creates a pyroclastic sphere field, rasterizes it to a DenseVolume, then renders it. It uses the direct functions of the API instead of creating a node graph. See above for a rendering example that uses the node graph.
import eddy
import math
from eddy import *
if __name__ == '__main__':
eddy.startup()
# Create a pyroclastic sphere field.
sphere = SphereField(radius=0.9)
iso_mask = IsoMaskField(field=sphere, inside_value=2.0)
vector_noise = PerlinNoiseField(seed=4, frequency=V3f(3.0), amplitude=V3f(1.4), octaves=5, lacunarity=2.0, gain=0.5)
offset_lookup = OffsetLookupField(field=iso_mask, offset_field=vector_noise, mode="trace", trace_steps=4, scale=0.2)
# Rasterize the field. Not strictly necessary, as we could render the implicit field directly, but it is usually faster
# to render a rasterized version.
dense_volume = DenseVolume(dimensions=V3f(2.56), resolution=256)
dense_volume.add_float_channel("density")
rasterize_field(volume=dense_volume, channel_name="density", field=offset_lookup)
density_field = DenseVolumeScalarField(volume=dense_volume, channel_name="density")
# Load shader.
shader_python_file = eddy.find_shader("smoke.py")
with open(shader_python_file) as f:
shader_python = f.read()
# Create RenderVolume.
render_volume = RenderVolume()
render_volume.set_shader(language="Python", shader=shader_python)
render_volume.set_parameter(name="density", value=density_field)
render_volume.set_parameter(name="absorption_colour", value=V3f(0.36, 0.36, 0.4))
render_volume.set_parameter(name="scattering_colour", value=V3f(0.45, 0.45, 0.5))
# Create RenderScene with a PointLight.
scene = RenderScene()
scene.render_volume = render_volume
scene.add_point_light(PointLight(position=V3f(5.0, 5.0, 5.0), intensity=V3f(20.0)))
# Create RenderSettings.
render_settings = RenderSettings()
render_settings.min_progressions = 400;
render_settings.max_progressions = 2000;
render_settings.output_settings.append(RenderOutputSettings(error_bound=0.01))
# Create camera.
camera = CameraMotion()
camera.position = V3f(0.0, 0.0, 4.0)
camera.set_aspect_ratio_and_horizontal_fov(16.0/9.0, math.pi/3.0)
# Create RenderData.
render_data = RenderData(camera=camera, scene=scene, settings=render_settings)
# Create RenderTarget.
render_target = RenderTarget(1280, 720)
# Create RenderState and execute the render.
render_state = RenderState(render_data=render_data, render_target=render_target)
render(render_state)
# Save the result.
render_target.write_exr_file(filename="render_example.exr")
Simulation and rendering in C¶
The following C++ program creates a node graph to both run a simulation and render the result, and save both the cache and the rendered images to disk.
#include <iostream>
#include <math.h>
#include "Eddy/eddy.h"
using eddy_api::ResourceHandle;
#define CHECK(result) do { checkEddyResult((result), __FILE__, __LINE__); } while (0,0)
void checkEddyResult(eddy_api::Result result, const char* file, int line)
{
if (result != eddy_api::EDDY_RESULT_SUCCESS)
{
std::cerr << "Eddy function returned error: " << result << std::endl;
std::cerr << file << ":" << line << std::endl;
char errorStr[1024];
eddy_api::GetLastErrorString(errorStr, sizeof(errorStr));
std::cerr << errorStr << std::endl;
exit(1);
}
}
eddy_api::M44f createTranslationMatrix(float x, float y, float z)
{
eddy_api::M44f result;
result.v[0][0] = 1.0f; result.v[0][1] = 0.0f; result.v[0][2] = 0.0f; result.v[0][3] = 0.0f;
result.v[1][0] = 0.0f; result.v[1][1] = 1.0f; result.v[1][2] = 0.0f; result.v[1][3] = 0.0f;
result.v[2][0] = 0.0f; result.v[2][1] = 0.0f; result.v[2][2] = 1.0f; result.v[2][3] = 0.0f;
result.v[3][0] = x; result.v[3][1] = y; result.v[3][2] = z; result.v[3][3] = 1.0f;
return result;
}
#define M_PI 3.14159265358979323846
int main(int argc, char *argv[])
{
CHECK(eddy_api::Startup());
// Create the simulation nodes.
ResourceHandle sphereField;
CHECK(eddy_api::CreateNode(&sphereField, "SphereField", "sphere"));
CHECK(eddy_api::NodeSetPropertyFloat(sphereField, "radius", 0.5f));
ResourceHandle isoMaskField;
CHECK(eddy_api::CreateNode(&isoMaskField, "IsoMaskField", "isomask"));
CHECK(eddy_api::NodeSetPropertyFloat(isoMaskField, "scalar_inside_value", 5.0f));
ResourceHandle emitter;
CHECK(eddy_api::CreateNode(&emitter, "Emitter", "emitter"));
CHECK(eddy_api::NodeSetPropertyString(emitter, "mode", "Add"));
ResourceHandle element;
CHECK(eddy_api::CreateNode(&element, "SparseSmokeElement", "element"));
CHECK(eddy_api::NodeSetPropertyFloat(element, "dx", 0.02f));
ResourceHandle cacheWriter;
CHECK(eddy_api::CreateNode(&cacheWriter, "CacheWriter", "cacheWriter"));
CHECK(eddy_api::NodeSetPropertyBool(cacheWriter, "overwrite", true));
ResourceHandle channelList;
CHECK(eddy_api::CreateChannelList(&channelList));
CHECK(eddy_api::ChannelListAddScalarChannel(channelList, "density", true, 0.0f, false, nullptr));
CHECK(eddy_api::NodeSetProperty(cacheWriter, "channel_list", channelList));
// Connect the simulation nodes.
CHECK(eddy_api::NodeConnect(sphereField, nullptr, isoMaskField, nullptr));
CHECK(eddy_api::NodeConnect(isoMaskField, nullptr, emitter, nullptr));
CHECK(eddy_api::NodeConnect(emitter, nullptr, element, "density"));
CHECK(eddy_api::NodeConnect(element, nullptr, cacheWriter, nullptr));
// Create the rendering nodes.
ResourceHandle densityChannel;
CHECK(eddy_api::CreateNode(&densityChannel, "ChannelSetField", "densityChannel"));
CHECK(eddy_api::NodeSetPropertyString(densityChannel, "channel", "density"));
ResourceHandle renderVolume;
CHECK(eddy_api::CreateNode(&renderVolume, "SmokeRenderVolume", "renderVolume"));
CHECK(eddy_api::NodeSetPropertyColor3f(renderVolume, "absorption_colour", eddy_api::MakeV3f(0.36f, 0.36f, 0.4f)));
CHECK(eddy_api::NodeSetPropertyColor3f(renderVolume, "scattering_colour", eddy_api::MakeV3f(0.45f, 0.45f, 0.5f)));
ResourceHandle pointLight;
CHECK(eddy_api::CreateNode(&pointLight, "PointLight", "pointLight"));
CHECK(eddy_api::NodeSetPropertyM44f(pointLight, "transform", createTranslationMatrix(5.0f, 5.0f, 5.0f)));
CHECK(eddy_api::NodeSetPropertyV3f(pointLight, "intensity", eddy_api::MakeV3f(20.0f, 20.0f, 20.0f)));
ResourceHandle renderScene;
CHECK(eddy_api::CreateNode(&renderScene, "RenderScene", "renderScene"));
ResourceHandle camera;
CHECK(eddy_api::CreateNode(&camera, "Camera", "camera"));
CHECK(eddy_api::NodeSetPropertyM44f(camera, "transform", createTranslationMatrix(0.0f, 1.5f, 6.0f)));
CHECK(eddy_api::NodeSetPropertyFloat(camera, "vertical_FOV", 2.0f * atanf(tanf(0.5f * M_PI / 3.0f) * 9.0f / 16.0f)));
CHECK(eddy_api::NodeSetPropertyFloat(camera, "horizontal_FOV", M_PI / 3.0f));
ResourceHandle renderData;
CHECK(eddy_api::CreateNode(&renderData, "RenderData", "renderData"));
CHECK(eddy_api::NodeSetPropertyInt(renderData, "max_progressions", 200));
ResourceHandle renderer;
CHECK(eddy_api::CreateNode(&renderer, "Render", "renderer"));
CHECK(eddy_api::NodeSetPropertyInt(renderer, "width", 1280));
CHECK(eddy_api::NodeSetPropertyInt(renderer, "height", 720));
ResourceHandle imageWriter;
CHECK(eddy_api::CreateNode(&imageWriter, "ImageWriter", "imageWriter"));
// Connect the rendering nodes.
CHECK(eddy_api::NodeConnect(cacheWriter, nullptr, densityChannel, nullptr));
CHECK(eddy_api::NodeConnect(densityChannel, nullptr, renderVolume, "density"));
CHECK(eddy_api::NodeConnect(renderVolume, nullptr, renderScene, nullptr));
CHECK(eddy_api::NodeConnect(pointLight, nullptr, renderScene, nullptr));
CHECK(eddy_api::NodeConnect(camera, nullptr, renderData, nullptr));
CHECK(eddy_api::NodeConnect(renderScene, nullptr, renderData, nullptr));
CHECK(eddy_api::NodeConnect(renderData, nullptr, renderer, nullptr));
CHECK(eddy_api::NodeConnect(renderer, "image", imageWriter, nullptr));
// Create the evaluator.
ResourceHandle evaluator;
eddy_api::CreateEvaluator(&evaluator);
// Frame loop.
double prevTicks = 0.0;
for (int frame = 1; frame <= 100; ++frame)
{
std::cout << "Evaluating frame " << frame << std::endl;
// Set the output filenames.
char filename[256];
sprintf(filename, "cache.%04d.vdb", frame);
CHECK(eddy_api::NodeSetPropertyString(cacheWriter, "file", filename));
sprintf(filename, "render.%04d.exr", frame);
CHECK(eddy_api::NodeSetPropertyString(imageWriter, "filename", filename));
// Evaluate graph for this frame.
double currentTicks = frame / 24.0f * eddy_api::EDDY_TICKS_PER_SECOND;
CHECK(eddy_api::Evaluate(evaluator, imageWriter, nullptr, prevTicks, currentTicks, 24.0f, nullptr));
prevTicks = currentTicks;
}
// Release all resource handles.
CHECK(eddy_api::ReleaseResource(sphereField));
CHECK(eddy_api::ReleaseResource(isoMaskField));
CHECK(eddy_api::ReleaseResource(emitter));
CHECK(eddy_api::ReleaseResource(element));
CHECK(eddy_api::ReleaseResource(cacheWriter));
CHECK(eddy_api::ReleaseResource(channelList));
CHECK(eddy_api::ReleaseResource(densityChannel));
CHECK(eddy_api::ReleaseResource(renderVolume));
CHECK(eddy_api::ReleaseResource(pointLight));
CHECK(eddy_api::ReleaseResource(renderScene));
CHECK(eddy_api::ReleaseResource(camera));
CHECK(eddy_api::ReleaseResource(renderData));
CHECK(eddy_api::ReleaseResource(renderer));
CHECK(eddy_api::ReleaseResource(imageWriter));
CHECK(eddy_api::ReleaseResource(evaluator));
CHECK(eddy_api::Shutdown());
return 0;
}