diff options
Diffstat (limited to 'intern')
77 files changed, 4876 insertions, 470 deletions
diff --git a/intern/CMakeLists.txt b/intern/CMakeLists.txt index 74048c2a4cc..cf7f913d675 100644 --- a/intern/CMakeLists.txt +++ b/intern/CMakeLists.txt @@ -82,3 +82,7 @@ if(WIN32) add_subdirectory(utfconv) endif() +if(WITH_OPENVDB) + add_subdirectory(openvdb) +endif() + diff --git a/intern/SConscript b/intern/SConscript index c0dafe37855..d416cab8aa2 100644 --- a/intern/SConscript +++ b/intern/SConscript @@ -63,3 +63,5 @@ if env['WITH_BF_BULLET']: if env['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'win64-mingw', 'linuxcross', 'win64-vc'): SConscript(['utfconv/SConscript']) +if env['WITH_BF_OPENVDB']: + SConscript (['openvdb/SConscript']) diff --git a/intern/audaspace/intern/AUD_Sequencer.cpp b/intern/audaspace/intern/AUD_Sequencer.cpp index ddcf97e2ea1..a5b70232068 100644 --- a/intern/audaspace/intern/AUD_Sequencer.cpp +++ b/intern/audaspace/intern/AUD_Sequencer.cpp @@ -44,7 +44,8 @@ AUD_Sequencer::AUD_Sequencer(AUD_Specs specs, float fps, bool muted) : m_distance_model(AUD_DISTANCE_MODEL_INVERSE_CLAMPED), m_volume(1, 1.0f), m_location(3), - m_orientation(4) + m_orientation(4), + m_recursive(false) { AUD_Quaternion q; m_orientation.write(q.get()); diff --git a/intern/audaspace/intern/AUD_Sequencer.h b/intern/audaspace/intern/AUD_Sequencer.h index 1066eeae8e3..ef68efbbafc 100644 --- a/intern/audaspace/intern/AUD_Sequencer.h +++ b/intern/audaspace/intern/AUD_Sequencer.h @@ -201,6 +201,8 @@ public: * \param entry The entry to remove. */ void remove(boost::shared_ptr<AUD_SequencerEntry> entry); + + bool m_recursive; }; #endif //__AUD_SEQUENCER_H__ diff --git a/intern/audaspace/intern/AUD_SequencerReader.cpp b/intern/audaspace/intern/AUD_SequencerReader.cpp index aef93cd3896..b893b132fa3 100644 --- a/intern/audaspace/intern/AUD_SequencerReader.cpp +++ b/intern/audaspace/intern/AUD_SequencerReader.cpp @@ -78,6 +78,9 @@ AUD_Specs AUD_SequencerReader::getSpecs() const void AUD_SequencerReader::read(int& length, bool& eos, sample_t* buffer) { + if (m_sequence->m_recursive) + return; + AUD_MutexLock lock(*m_sequence); if(m_sequence->m_status != m_status) @@ -192,7 +195,9 @@ void AUD_SequencerReader::read(int& length, bool& eos, sample_t* buffer) v2 -= v; m_device.setListenerVelocity(v2 * m_sequence->m_fps); + m_sequence->m_recursive = true; m_device.read(reinterpret_cast<data_t*>(buffer + specs.channels * pos), len); + m_sequence->m_recursive = false; pos += len; time += float(len) / float(specs.rate); diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt index ed6961f49e0..6c1264ff594 100644 --- a/intern/cycles/CMakeLists.txt +++ b/intern/cycles/CMakeLists.txt @@ -146,6 +146,15 @@ if(WITH_CYCLES_OSL) ) endif() +if(WITH_OPENVDB) + add_definitions(-DWITH_OPENVDB) + add_definitions(-DOPENVDB_USE_BLOSC) + add_definitions(-DDWREAL_IS_DOUBLE=0) + include_directories( + ${OPENVDB_INCLUDE_DIRS} + ) +endif() + add_definitions( -DWITH_OPENCL -DWITH_CUDA diff --git a/intern/cycles/SConscript b/intern/cycles/SConscript index 99df8c299fc..68eccf96a45 100644 --- a/intern/cycles/SConscript +++ b/intern/cycles/SConscript @@ -67,6 +67,10 @@ if env['WITH_BF_CYCLES_OSL']: defs.append('OSL_STATIC_LIBRARY') incs.append(cycles['BF_OSL_INC']) +if env['WITH_BF_OPENVDB']: + defs.append('WITH_OPENVDB') + incs.append(env['BF_OPENVDB_INC']) + if env['WITH_BF_CYCLES_DEBUG']: defs.append('WITH_CYCLES_DEBUG') diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt index b000266cac2..b9a2abf1c9a 100644 --- a/intern/cycles/app/CMakeLists.txt +++ b/intern/cycles/app/CMakeLists.txt @@ -96,6 +96,12 @@ macro(cycles_target_link_libraries target) ${CMAKE_DL_LIBS} ${PLATFORM_LINKLIBS} ) + if(WITH_ALEMBIC) + target_link_libraries( + ${target} + ${ALEMBIC_LIBRARIES} + ) + endif() endmacro() # Application build targets @@ -106,6 +112,23 @@ if(WITH_CYCLES_STANDALONE) cycles_xml.cpp cycles_xml.h ) + + if(WITH_ALEMBIC) + list(APPEND SRC + cycles_alembic.cpp + cycles_alembic.h + ) + add_definitions(-DWITH_ALEMBIC) + include_directories( + SYSTEM + ${ALEMBIC_INCLUDE_DIRS} + ) + endif() + + if(WITH_HDF5) + add_definitions(-DWITH_HDF5) + endif() + add_executable(cycles ${SRC}) cycles_target_link_libraries(cycles) diff --git a/intern/cycles/app/cycles_alembic.cpp b/intern/cycles/app/cycles_alembic.cpp new file mode 100644 index 00000000000..3a1e6a6bcaa --- /dev/null +++ b/intern/cycles/app/cycles_alembic.cpp @@ -0,0 +1,426 @@ +/* + * Copyright 2015 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> + +#include <sstream> +#include <algorithm> +#include <iterator> + +#include <Alembic/AbcCoreOgawa/ReadWrite.h> +#ifdef WITH_HDF5 +#include <Alembic/AbcCoreHDF5/ReadWrite.h> +#endif +#include <Alembic/Abc/IArchive.h> +#include <Alembic/Abc/IObject.h> +#include <Alembic/Abc/ISampleSelector.h> +#include <Alembic/Abc/ICompoundProperty.h> +#include <Alembic/Abc/IScalarProperty.h> +#include <Alembic/Abc/IArrayProperty.h> +#include <Alembic/Abc/ArchiveInfo.h> +#include <Alembic/AbcGeom/IPolyMesh.h> + +#include "camera.h" +#include "film.h" +#include "graph.h" +#include "integrator.h" +#include "light.h" +#include "mesh.h" +#include "nodes.h" +#include "object.h" +#include "shader.h" +#include "scene.h" + +#include "subd_mesh.h" +#include "subd_patch.h" +#include "subd_split.h" + +#include "util_debug.h" +#include "util_foreach.h" +#include "util_path.h" +#include "util_transform.h" +#include "util_xml.h" + +#include "cycles_alembic.h" + +CCL_NAMESPACE_BEGIN + +using namespace Alembic; +using namespace Abc; +using namespace AbcGeom; + +#define ABC_SAFE_CALL_BEGIN \ + try { + +#define ABC_SAFE_CALL_END \ + } \ + catch (Alembic::Util::Exception e) { \ + printf("%s", e.what()); \ + } + + +/* File */ + +static const std::string g_sep(";"); + +static void visitProperties(std::stringstream &ss, ICompoundProperty, std::string &); + +template <class PROP> +static void visitSimpleArrayProperty(std::stringstream &ss, PROP iProp, const std::string &iIndent) +{ + std::string ptype = "ArrayProperty "; + size_t asize = 0; + + AbcA::ArraySamplePtr samp; + index_t maxSamples = iProp.getNumSamples(); + for (index_t i = 0 ; i < maxSamples; ++i) { + iProp.get(samp, ISampleSelector( i )); + asize = samp->size(); + }; + + std::string mdstring = "interpretation="; + mdstring += iProp.getMetaData().get("interpretation"); + + std::stringstream dtype; + dtype << "datatype="; + dtype << iProp.getDataType(); + + std::stringstream asizestr; + asizestr << ";arraysize="; + asizestr << asize; + + mdstring += g_sep; + + mdstring += dtype.str(); + + mdstring += asizestr.str(); + + ss << iIndent << " " << ptype << "name=" << iProp.getName() + << g_sep << mdstring << g_sep << "numsamps=" + << iProp.getNumSamples() << std::endl; +} + +template <class PROP> +static void visitSimpleScalarProperty(std::stringstream &ss, PROP iProp, const std::string &iIndent) +{ + std::string ptype = "ScalarProperty "; + size_t asize = 0; + + const AbcA::DataType &dt = iProp.getDataType(); + const Alembic::Util ::uint8_t extent = dt.getExtent(); + Alembic::Util::Dimensions dims(extent); + AbcA::ArraySamplePtr samp = AbcA::AllocateArraySample( dt, dims ); + index_t maxSamples = iProp.getNumSamples(); + for (index_t i = 0 ; i < maxSamples; ++i) { + iProp.get(const_cast<void*>(samp->getData()), ISampleSelector( i )); + asize = samp->size(); + }; + + std::string mdstring = "interpretation="; + mdstring += iProp.getMetaData().get("interpretation"); + + std::stringstream dtype; + dtype << "datatype="; + dtype << dt; + + std::stringstream asizestr; + asizestr << ";arraysize="; + asizestr << asize; + + mdstring += g_sep; + + mdstring += dtype.str(); + + mdstring += asizestr.str(); + + ss << iIndent << " " << ptype << "name=" << iProp.getName() + << g_sep << mdstring << g_sep << "numsamps=" + << iProp.getNumSamples() << std::endl; +} + +static void visitCompoundProperty(std::stringstream &ss, ICompoundProperty iProp, std::string &ioIndent) +{ + std::string oldIndent = ioIndent; + ioIndent += " "; + + std::string interp = "schema="; + interp += iProp.getMetaData().get("schema"); + + ss << ioIndent << "CompoundProperty " << "name=" << iProp.getName() + << g_sep << interp << std::endl; + + visitProperties(ss, iProp, ioIndent); + + ioIndent = oldIndent; +} + +static void visitProperties(std::stringstream &ss, ICompoundProperty iParent, std::string &ioIndent ) +{ + std::string oldIndent = ioIndent; + for (size_t i = 0 ; i < iParent.getNumProperties() ; i++) { + PropertyHeader header = iParent.getPropertyHeader(i); + + if (header.isCompound()) { + visitCompoundProperty(ss, ICompoundProperty(iParent, header.getName()), ioIndent); + } + else if (header.isScalar()) { + visitSimpleScalarProperty(ss, IScalarProperty(iParent, header.getName()), ioIndent); + } + else { + assert(header.isArray()); + visitSimpleArrayProperty(ss, IArrayProperty(iParent, header.getName()), ioIndent); + } + } + + ioIndent = oldIndent; +} + +static void visitObject(std::stringstream &ss, IObject iObj, std::string iIndent, AbcArchiveInfoLevel info_level) +{ + // Object has a name, a full name, some meta data, + // and then it has a compound property full of properties. + std::string path = iObj.getFullName(); + + if (iObj.isInstanceRoot()) { + if (path != "/") { + ss << "Object " << "name=" << path + << " [Instance " << iObj.instanceSourcePath() << "]" + << std::endl; + } + } + else if (iObj.isInstanceDescendant()) { + /* skip non-root instances to avoid repetition */ + return; + } + else { + if (path != "/") { + ss << "Object " << "name=" << path << std::endl; + } + + if (info_level >= ABC_INFO_PROPERTIES) { + // Get the properties. + ICompoundProperty props = iObj.getProperties(); + visitProperties(ss, props, iIndent); + } + + // now the child objects + for (size_t i = 0 ; i < iObj.getNumChildren() ; i++) { + visitObject(ss, IObject(iObj, iObj.getChildHeader(i).getName()), iIndent, info_level); + } + } +} + +static std::string abc_archive_info(IArchive &archive, AbcArchiveInfoLevel info_level) +{ + std::stringstream ss; + + ss << "Alembic Archive Info for " + << Alembic::AbcCoreAbstract::GetLibraryVersion() + << std::endl;; + + std::string appName; + std::string libraryVersionString; + Alembic::Util::uint32_t libraryVersion; + std::string whenWritten; + std::string userDescription; + GetArchiveInfo(archive, + appName, + libraryVersionString, + libraryVersion, + whenWritten, + userDescription); + + if (appName != "") { + ss << " file written by: " << appName << std::endl; + ss << " using Alembic : " << libraryVersionString << std::endl; + ss << " written on : " << whenWritten << std::endl; + ss << " user description : " << userDescription << std::endl; + ss << std::endl; + } + else { +// ss << argv[1] << std::endl; + ss << " (file doesn't have any ArchiveInfo)" + << std::endl; + ss << std::endl; + } + + if (info_level >= ABC_INFO_OBJECTS) + visitObject(ss, archive.getTop(), "", info_level); + + return ss.str(); +} + +/* ========================================================================= */ + +struct AbcReadState { + Scene *scene; /* scene pointer */ + float time; + Transform tfm; /* current transform state */ + bool smooth; /* smooth normal state */ + int shader; /* current shader */ + string base; /* base path to current file*/ + float dicing_rate; /* current dicing rate */ + Mesh::DisplacementMethod displacement_method; +}; + +static ISampleSelector get_sample_selector(const AbcReadState &state) +{ + return ISampleSelector(state.time, ISampleSelector::kFloorIndex); +} + +static Mesh *add_mesh(Scene *scene, const Transform& tfm) +{ + /* create mesh */ + Mesh *mesh = new Mesh(); + scene->meshes.push_back(mesh); + + /* create object*/ + Object *object = new Object(); + object->mesh = mesh; + object->tfm = tfm; + scene->objects.push_back(object); + + return mesh; +} + +static void read_mesh(const AbcReadState &state, IPolyMesh object) +{ + /* add mesh */ + Mesh *mesh = add_mesh(state.scene, state.tfm); + mesh->used_shaders.push_back(state.shader); + + /* read state */ + int shader = state.shader; + bool smooth = state.smooth; + + mesh->displacement_method = state.displacement_method; + + ISampleSelector ss = get_sample_selector(state); + IPolyMeshSchema schema = object.getSchema(); + + IPolyMeshSchema::Sample sample; + schema.get(sample, ss); + + int totverts = sample.getPositions()->size(); + int totfaces = sample.getFaceCounts()->size(); + const V3f *P = sample.getPositions()->get(); + const int32_t *verts = sample.getFaceIndices()->get(); + const int32_t *nverts = sample.getFaceCounts()->get(); + + /* create vertices */ + mesh->verts.reserve(totverts); + for(int i = 0; i < totverts; i++) { + mesh->verts.push_back(make_float3(P[i].x, P[i].y, P[i].z)); + } + + /* create triangles */ + int index_offset = 0; + + for(int i = 0; i < totfaces; i++) { + int n = nverts[i]; + /* XXX TODO only supports tris and quads atm, + * need a proper tessellation algorithm in cycles. + */ + if (n > 4) { + printf("%d-sided face found, only triangles and quads are supported currently", n); + n = 4; + } + + for(int j = 0; j < n-2; j++) { + int v0 = verts[index_offset]; + int v1 = verts[index_offset + j + 1]; + int v2 = verts[index_offset + j + 2]; + + assert(v0 < (int)totverts); + assert(v1 < (int)totverts); + assert(v2 < (int)totverts); + + mesh->add_triangle(v0, v1, v2, shader, smooth); + } + + index_offset += n; + } + + /* temporary for test compatibility */ + mesh->attributes.remove(ATTR_STD_VERTEX_NORMAL); +} + +static void read_object(const AbcReadState &state, IObject object) +{ + for (int i = 0; i < object.getNumChildren(); ++i) { + IObject child = object.getChild(i); + const MetaData &metadata = child.getMetaData(); + + if (IPolyMeshSchema::matches(metadata)) { + read_mesh(state, IPolyMesh(child, kWrapExisting)); + } + else { + read_object(state, child); + } + } +} + +static void read_archive(Scene *scene, IArchive archive, const char *filepath) +{ + AbcReadState state; + + state.scene = scene; + state.time = 0.0f; // TODO + state.tfm = transform_identity(); + state.shader = scene->default_surface; + state.smooth = false; + state.dicing_rate = 0.1f; + state.base = path_dirname(filepath); + + read_object(state, archive.getTop()); + + scene->params.bvh_type = SceneParams::BVH_STATIC; +} + +void abc_read_ogawa_file(Scene *scene, const char *filepath, AbcArchiveInfoLevel info_level) +{ + IArchive archive; + ABC_SAFE_CALL_BEGIN + archive = IArchive(AbcCoreOgawa::ReadArchive(), filepath, ErrorHandler::kThrowPolicy); + ABC_SAFE_CALL_END + + if (archive) { + if (info_level >= ABC_INFO_BASIC) + printf("%s", abc_archive_info(archive, info_level).c_str()); + + read_archive(scene, archive, filepath); + } +} + +void abc_read_hdf5_file(Scene *scene, const char *filepath, AbcArchiveInfoLevel info_level) +{ +#ifdef WITH_HDF5 + IArchive archive; + ABC_SAFE_CALL_BEGIN + archive = IArchive(AbcCoreHDF5::ReadArchive(), filepath, ErrorHandler::kThrowPolicy); + ABC_SAFE_CALL_END + + if (archive) { + if (info_level >= ABC_INFO_BASIC) + printf("%s", abc_archive_info(archive, info_level).c_str()); + + read_archive(scene, archive, filepath); + } +#endif +} + +CCL_NAMESPACE_END + diff --git a/intern/cycles/app/cycles_alembic.h b/intern/cycles/app/cycles_alembic.h new file mode 100644 index 00000000000..df17f8b8fbe --- /dev/null +++ b/intern/cycles/app/cycles_alembic.h @@ -0,0 +1,36 @@ +/* + * Copyright 2015 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __CYCLES_ALEMBIC_H__ +#define __CYCLES_ALEMBIC_H__ + +CCL_NAMESPACE_BEGIN + +class Scene; + +enum AbcArchiveInfoLevel { + ABC_INFO_NONE = 0, + ABC_INFO_BASIC, + ABC_INFO_OBJECTS, + ABC_INFO_PROPERTIES, +}; + +void abc_read_ogawa_file(Scene *scene, const char *filepath, AbcArchiveInfoLevel info_level = ABC_INFO_NONE); +void abc_read_hdf5_file(Scene *scene, const char *filepath, AbcArchiveInfoLevel info_level = ABC_INFO_NONE); + +CCL_NAMESPACE_END + +#endif /* __CYCLES_XML_H__ */ diff --git a/intern/cycles/app/cycles_standalone.cpp b/intern/cycles/app/cycles_standalone.cpp index b0d49d6ee72..3980ca547c2 100644 --- a/intern/cycles/app/cycles_standalone.cpp +++ b/intern/cycles/app/cycles_standalone.cpp @@ -38,13 +38,23 @@ #endif #include "cycles_xml.h" +#ifdef WITH_ALEMBIC +#include "cycles_alembic.h" +#endif CCL_NAMESPACE_BEGIN +enum FileType { + FILETYPE_XML = 0, + FILETYPE_ABC_HDF5, + FILETYPE_ABC_OGAWA, +}; + struct Options { Session *session; Scene *scene; string filepath; + FileType filetype; int width, height; SceneParams scene_params; SessionParams session_params; @@ -121,8 +131,22 @@ static void scene_init() { options.scene = new Scene(options.scene_params, options.session_params.device); - /* Read XML */ - xml_read_file(options.scene, options.filepath.c_str()); + /* Read file */ + switch (options.filetype) { + case FILETYPE_XML: + xml_read_file(options.scene, options.filepath.c_str()); + break; +#ifdef WITH_ALEMBIC + case FILETYPE_ABC_OGAWA: + abc_read_ogawa_file(options.scene, options.filepath.c_str()); + break; + case FILETYPE_ABC_HDF5: + abc_read_hdf5_file(options.scene, options.filepath.c_str()); + break; +#endif + default: + return; + } /* Camera width/height override? */ if(!(options.width == 0 || options.height == 0)) { @@ -356,6 +380,16 @@ static void options_parse(int argc, const char **argv) /* shading system */ string ssname = "svm"; + /* input file type */ + string filetypes = "auto, xml"; +#ifdef WITH_ALEMBIC + filetypes += ", alembic_ogawa"; +#ifdef WITH_HDF5 + filetypes += ", alembic_hdf5"; +#endif +#endif + string filetype = "auto"; + /* parse options */ ArgParse ap; bool help = false, debug = false; @@ -371,6 +405,7 @@ static void options_parse(int argc, const char **argv) "--quiet", &options.quiet, "In background mode, don't print progress messages", "--samples %d", &options.session_params.samples, "Number of samples to render", "--output %s", &options.session_params.output_path, "File path to write output image", + "--filetype %s", &filetype, ("File type: " + filetypes).c_str(), "--threads %d", &options.session_params.threads, "CPU Rendering Threads", "--width %d", &options.width, "Window width in pixel", "--height %d", &options.height, "Window height in pixel", @@ -416,6 +451,21 @@ static void options_parse(int argc, const char **argv) else if(ssname == "svm") options.scene_params.shadingsystem = SHADINGSYSTEM_SVM; + if(filetype == "auto") { + string extension = options.filepath.substr(options.filepath.find_last_of(".") + 1); + + if (extension == "xml") + options.filetype = FILETYPE_XML; + else if (extension == "abc") + options.filetype = FILETYPE_ABC_OGAWA; + } + else if(filetype == "xml") + options.filetype = FILETYPE_XML; + else if(filetype == "alembic_ogawa") + options.filetype = FILETYPE_ABC_OGAWA; + else if(filetype == "alembic_hdf5") + options.filetype = FILETYPE_ABC_HDF5; + #ifndef WITH_CYCLES_STANDALONE_GUI options.session_params.background = true; #endif diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp index edea8cd0ec4..77b0a6454ce 100644 --- a/intern/cycles/app/cycles_xml.cpp +++ b/intern/cycles/app/cycles_xml.cpp @@ -43,6 +43,9 @@ #include "util_xml.h" #include "cycles_xml.h" +#ifdef WITH_ALEMBIC +#include "cycles_alembic.h" +#endif CCL_NAMESPACE_BEGIN @@ -735,6 +738,13 @@ static void xml_read_shader_graph(const XMLReadState& state, Shader *shader, pug xml_read_enum(&vtransform->convert_to, VectorTransformNode::convert_space_enum, node, "convert_to"); snode = vtransform; } + else if(string_iequals(node.name(), "openvdb")) { + OpenVDBNode *vdbnode = new OpenVDBNode(); + xml_read_string(&vdbnode->filename, node, "src"); + vdbnode->filename = path_join(state.base, vdbnode->filename); + + snode = vdbnode; + } else if(string_iequals(node.name(), "connect")) { /* connect nodes */ vector<string> from_tokens, to_tokens; @@ -1188,6 +1198,25 @@ static void xml_read_state(XMLReadState& state, pugi::xml_node node) state.displacement_method = Mesh::DISPLACE_BOTH; } +/* Alembic */ +static void xml_read_alembic(const XMLReadState& state, pugi::xml_node node) +{ +#ifdef WITH_ALEMBIC + string filepath; + + if(xml_read_string(&filepath, node, "file")) { + filepath = path_join(state.base, filepath); + + if(xml_equal_string(node, "type", "hdf5")) + abc_read_hdf5_file(state.scene, filepath.c_str(), ABC_INFO_BASIC); + else if(xml_equal_string(node, "type", "ogawa")) + abc_read_ogawa_file(state.scene, filepath.c_str(), ABC_INFO_BASIC); + else + abc_read_ogawa_file(state.scene, filepath.c_str(), ABC_INFO_BASIC); /* default */ + } +#endif +} + /* Scene */ static void xml_read_include(const XMLReadState& state, const string& src); @@ -1237,6 +1266,9 @@ static void xml_read_scene(const XMLReadState& state, pugi::xml_node scene_node) if(xml_read_string(&src, node, "src")) xml_read_include(state, src); } + else if(string_iequals(node.name(), "alembic")) { + xml_read_alembic(state, node); + } else fprintf(stderr, "Unknown node \"%s\".\n", node.name()); } diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt index fff9ed20bba..c6a2b919486 100644 --- a/intern/cycles/blender/CMakeLists.txt +++ b/intern/cycles/blender/CMakeLists.txt @@ -31,10 +31,12 @@ set(SRC blender_session.cpp blender_shader.cpp blender_sync.cpp + blender_texture.cpp CCL_api.h blender_sync.h blender_session.h + blender_texture.h blender_util.h ) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 16a807b3af5..0daf6784798 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -463,6 +463,12 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): description="Use BVH spatial splits: longer builder time, faster render", default=False, ) + cls.debug_use_triangle_storage = BoolProperty( + name="Use Triangle Storage", + description="use special storage with aligned triangle coordinates for faster " + "intesection check in expense of higher memory usage", + default=True, + ) cls.use_cache = BoolProperty( name="Cache BVH", description="Cache last built BVH to disk for faster re-render if no geometry changed", @@ -511,6 +517,19 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): ), ) + cls.use_camera_cull = BoolProperty( + name="Use Camera Cull", + description="Allow objects to be culled based on the camera frustum", + default=False, + ) + + cls.camera_cull_margin = FloatProperty( + name="Camera Cull Margin", + description="Margin for the camera space culling", + default=0.1, + min=0.0, max=5.0 + ) + @classmethod def unregister(cls): del bpy.types.Scene.cycles @@ -890,6 +909,12 @@ class CyclesObjectBlurSettings(bpy.types.PropertyGroup): default=1, ) + cls.use_camera_cull = BoolProperty( + name="Use Camera Cull", + description="Allow this object and it's duplicators to be culled by camera space culling", + default=False, + ) + @classmethod def unregister(cls): del bpy.types.Object.cycles diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index f6ff86ac30e..7fcb99d9e15 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -322,6 +322,11 @@ class CyclesRender_PT_performance(CyclesButtonsPanel, Panel): subsub.enabled = not rd.use_border subsub.prop(rd, "use_save_buffers") + sub.prop(cscene, "use_camera_cull") + subsub = col.column() + subsub.active = cscene.use_camera_cull + subsub.prop(cscene, "camera_cull_margin") + col = split.column(align=True) col.label(text="Viewport:") @@ -339,6 +344,7 @@ class CyclesRender_PT_performance(CyclesButtonsPanel, Panel): col.label(text="Acceleration structure:") col.prop(cscene, "debug_use_spatial_splits") + col.prop(cscene, "debug_use_triangle_storage") class CyclesRender_PT_layer_options(CyclesButtonsPanel, Panel): @@ -687,8 +693,8 @@ class CyclesObject_PT_motion_blur(CyclesButtonsPanel, Panel): sub.prop(cob, "motion_steps", text="Steps") -class CyclesObject_PT_ray_visibility(CyclesButtonsPanel, Panel): - bl_label = "Ray Visibility" +class CyclesObject_PT_cycles_settings(CyclesButtonsPanel, Panel): + bl_label = "Cycles Settings" bl_context = "object" bl_options = {'DEFAULT_CLOSED'} @@ -703,8 +709,10 @@ class CyclesObject_PT_ray_visibility(CyclesButtonsPanel, Panel): layout = self.layout ob = context.object + cob = ob.cycles visibility = ob.cycles_visibility + layout.label(text="Ray Visibility:") flow = layout.column_flow() flow.prop(visibility, "camera") @@ -716,6 +724,9 @@ class CyclesObject_PT_ray_visibility(CyclesButtonsPanel, Panel): if ob.type != 'LAMP': flow.prop(visibility, "shadow") + layout.label(text="Performance:") + layout.prop(cob, "use_camera_cull") + class CYCLES_OT_use_shading_nodes(Operator): """Enable nodes on a material, world or lamp""" @@ -1225,7 +1236,8 @@ class CyclesTexture_PT_mapping(CyclesButtonsPanel, Panel): @classmethod def poll(cls, context): node = context.texture_node - return node and CyclesButtonsPanel.poll(context) + # TODO(sergey): perform a faster/nicer check? + return node and hasattr(node, 'texture_mapping') and CyclesButtonsPanel.poll(context) def draw(self, context): layout = self.layout @@ -1449,7 +1461,8 @@ class CyclesScene_PT_simplify(CyclesButtonsPanel, Panel): def draw(self, context): layout = self.layout - rd = context.scene.render + scene = context.scene + rd = scene.render layout.active = rd.use_simplify split = layout.split() @@ -1529,6 +1542,7 @@ def get_panels(): "DATA_PT_curve_texture_space", "DATA_PT_mball_texture_space", "DATA_PT_vertex_groups", + "DATA_PT_face_maps", "DATA_PT_shape_keys", "DATA_PT_uv_texture", "DATA_PT_vertex_colors", diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/blender_curves.cpp index dba801fc4df..4ed0b910f35 100644 --- a/intern/cycles/blender/blender_curves.cpp +++ b/intern/cycles/blender/blender_curves.cpp @@ -37,15 +37,14 @@ void curveinterp_v3_v3v3v3v3(float3 *p, float3 *v1, float3 *v2, float3 *v3, floa void interp_weights(float t, float data[4]); float shaperadius(float shape, float root, float tip, float time); void InterpolateKeySegments(int seg, int segno, int key, int curve, float3 *keyloc, float *time, ParticleCurveData *CData); -bool ObtainCacheParticleUV(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background, int uv_num); -bool ObtainCacheParticleVcol(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background, int vcol_num); -bool ObtainCacheParticleData(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background); void ExportCurveSegments(Scene *scene, Mesh *mesh, ParticleCurveData *CData); void ExportCurveTrianglePlanes(Mesh *mesh, ParticleCurveData *CData, float3 RotCam, bool is_ortho); void ExportCurveTriangleGeometry(Mesh *mesh, ParticleCurveData *CData, int resolution); void ExportCurveTriangleUV(ParticleCurveData *CData, int vert_offset, int resol, float3 *uvdata); +void ExportCurveUV(Mesh *mesh, ParticleCurveData *CData, ustring name, bool active_render, int primitive, int vert_offset, int resol); void ExportCurveTriangleVcol(ParticleCurveData *CData, int vert_offset, int resol, uchar4 *cdata); +void ExportCurveVcol(Mesh *mesh, ParticleCurveData *CData, ustring name, int primitive, int vert_offset, int resol); ParticleCurveData::ParticleCurveData() { @@ -119,206 +118,330 @@ void InterpolateKeySegments(int seg, int segno, int key, int curve, float3 *keyl curveinterp_v3_v3v3v3v3(keyloc, &ckey_loc1, &ckey_loc2, &ckey_loc3, &ckey_loc4, t); } -bool ObtainCacheParticleData(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background) +static void ObtainCacheParticleData(Mesh *mesh, BL::Object b_ob, BL::ParticleSystem b_psys, const Transform &itfm, + ParticleCurveData *CData, bool background) { - int curvenum = 0; - int keyno = 0; + BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr); + int mi = clamp(b_part.material()-1, 0, mesh->used_shaders.size()-1); + int shader = mesh->used_shaders[mi]; + int draw_step = background ? b_part.render_step() : b_part.draw_step(); + int totparts = b_psys.particles.length(); + int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f); + int totcurves = totchild; + + if(b_part.child_type() == 0) + totcurves += totparts; - if(!(mesh && b_mesh && b_ob && CData)) - return false; + if(totcurves == 0) + return; - Transform tfm = get_transform(b_ob->matrix_world()); - Transform itfm = transform_quick_inverse(tfm); + int ren_step = (1 << draw_step) + 1; + if(b_part.kink() == BL::ParticleSettings::kink_SPIRAL) + ren_step += b_part.kink_extra_steps(); - BL::Object::modifiers_iterator b_mod; - for(b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) { - if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && (background ? b_mod->show_render() : b_mod->show_viewport())) { - BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr); - BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr); - BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr); + PointerRNA cpsys = RNA_pointer_get(&b_part.ptr, "cycles"); + + int keyno = CData->curvekey_co.size(); + int curvenum = CData->curve_keynum.size(); + + CData->psys_firstcurve.push_back(curvenum); + CData->psys_curvenum.push_back(totcurves); + CData->psys_shader.push_back(shader); + + float radius = get_float(cpsys, "radius_scale") * 0.5f; + + CData->psys_rootradius.push_back(radius * get_float(cpsys, "root_width")); + CData->psys_tipradius.push_back(radius * get_float(cpsys, "tip_width")); + CData->psys_shape.push_back(get_float(cpsys, "shape")); + CData->psys_closetip.push_back(get_boolean(cpsys, "use_closetip")); + + int pa_no = 0; + if(!(b_part.child_type() == 0)) + pa_no = totparts; + + int num_add = (totparts+totchild - pa_no); + CData->curve_firstkey.reserve(CData->curve_firstkey.size() + num_add); + CData->curve_keynum.reserve(CData->curve_keynum.size() + num_add); + CData->curve_length.reserve(CData->curve_length.size() + num_add); + CData->curvekey_co.reserve(CData->curvekey_co.size() + num_add*ren_step); + CData->curvekey_time.reserve(CData->curvekey_time.size() + num_add*ren_step); + + for(; pa_no < totparts+totchild; pa_no++) { + int keynum = 0; + CData->curve_firstkey.push_back(keyno); + + float curve_length = 0.0f; + float3 pcKey; + for(int step_no = 0; step_no < ren_step; step_no++) { + float nco[3]; + b_psys.co_hair(b_ob, pa_no, step_no, nco); + float3 cKey = make_float3(nco[0], nco[1], nco[2]); + cKey = transform_point(&itfm, cKey); + if(step_no > 0) { + float step_length = len(cKey - pcKey); + if(step_length == 0.0f) + continue; + curve_length += step_length; + } + CData->curvekey_co.push_back(cKey); + CData->curvekey_time.push_back(curve_length); + pcKey = cKey; + keynum++; + } + keyno += keynum; - if((b_part.render_type() == BL::ParticleSettings::render_type_PATH) && (b_part.type() == BL::ParticleSettings::type_HAIR)) { - int mi = clamp(b_part.material()-1, 0, mesh->used_shaders.size()-1); - int shader = mesh->used_shaders[mi]; - int draw_step = background ? b_part.render_step() : b_part.draw_step(); - int totparts = b_psys.particles.length(); - int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f); - int totcurves = totchild; - - if(b_part.child_type() == 0) - totcurves += totparts; + CData->curve_keynum.push_back(keynum); + CData->curve_length.push_back(curve_length); + curvenum++; + } +} - if(totcurves == 0) - continue; +static void ObtainCacheParticleUV(Mesh * /*mesh*/, BL::Object /*b_ob*/, BL::Mesh b_mesh, BL::ParticleSystem b_psys, BL::ParticleSystemModifier b_psmd, + ParticleCurveData *CData, bool background, int uv_num) +{ + BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr); + int totparts = b_psys.particles.length(); + int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f); + int totcurves = totchild; + + if(b_part.child_type() == 0) + totcurves += totparts; - int ren_step = (1 << draw_step) + 1; - if(b_part.kink() == BL::ParticleSettings::kink_SPIRAL) - ren_step += b_part.kink_extra_steps(); + if(totcurves == 0) + return; - PointerRNA cpsys = RNA_pointer_get(&b_part.ptr, "cycles"); + int pa_no = 0; + if(!(b_part.child_type() == 0)) + pa_no = totparts; - CData->psys_firstcurve.push_back(curvenum); - CData->psys_curvenum.push_back(totcurves); - CData->psys_shader.push_back(shader); + int num_add = (totparts+totchild - pa_no); + CData->curve_uv.reserve(CData->curve_uv.size() + num_add); - float radius = get_float(cpsys, "radius_scale") * 0.5f; - - CData->psys_rootradius.push_back(radius * get_float(cpsys, "root_width")); - CData->psys_tipradius.push_back(radius * get_float(cpsys, "tip_width")); - CData->psys_shape.push_back(get_float(cpsys, "shape")); - CData->psys_closetip.push_back(get_boolean(cpsys, "use_closetip")); - - int pa_no = 0; - if(!(b_part.child_type() == 0)) - pa_no = totparts; - - int num_add = (totparts+totchild - pa_no); - CData->curve_firstkey.reserve(CData->curve_firstkey.size() + num_add); - CData->curve_keynum.reserve(CData->curve_keynum.size() + num_add); - CData->curve_length.reserve(CData->curve_length.size() + num_add); - CData->curvekey_co.reserve(CData->curvekey_co.size() + num_add*ren_step); - CData->curvekey_time.reserve(CData->curvekey_time.size() + num_add*ren_step); - - for(; pa_no < totparts+totchild; pa_no++) { - int keynum = 0; - CData->curve_firstkey.push_back(keyno); - - float curve_length = 0.0f; - float3 pcKey; - for(int step_no = 0; step_no < ren_step; step_no++) { - float nco[3]; - b_psys.co_hair(*b_ob, pa_no, step_no, nco); - float3 cKey = make_float3(nco[0], nco[1], nco[2]); - cKey = transform_point(&itfm, cKey); - if(step_no > 0) { - float step_length = len(cKey - pcKey); - if(step_length == 0.0f) - continue; - curve_length += step_length; - } - CData->curvekey_co.push_back(cKey); - CData->curvekey_time.push_back(curve_length); - pcKey = cKey; - keynum++; - } - keyno += keynum; + BL::ParticleSystem::particles_iterator b_pa; + b_psys.particles.begin(b_pa); + for(; pa_no < totparts+totchild; pa_no++) { + /* Add UVs */ + BL::Mesh::tessface_uv_textures_iterator l; + b_mesh.tessface_uv_textures.begin(l); - CData->curve_keynum.push_back(keynum); - CData->curve_length.push_back(curve_length); - curvenum++; - } - } - } - } + float3 uv = make_float3(0.0f, 0.0f, 0.0f); + if(b_mesh.tessface_uv_textures.length()) + b_psys.uv_on_emitter(b_psmd, *b_pa, pa_no, uv_num, &uv.x); + CData->curve_uv.push_back(uv); - return true; + if(pa_no < totparts && b_pa != b_psys.particles.end()) + ++b_pa; + } } -bool ObtainCacheParticleUV(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background, int uv_num) +static void ObtainCacheParticleVcol(Mesh * /*mesh*/, BL::Object /*b_ob*/, BL::Mesh b_mesh, BL::ParticleSystem b_psys, BL::ParticleSystemModifier b_psmd, + ParticleCurveData *CData, bool background, int vcol_num) { - if(!(mesh && b_mesh && b_ob && CData)) - return false; + BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr); + int totparts = b_psys.particles.length(); + int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f); + int totcurves = totchild; + + if(b_part.child_type() == 0) + totcurves += totparts; - CData->curve_uv.clear(); + if(totcurves == 0) + return; - BL::Object::modifiers_iterator b_mod; - for(b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) { - if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && (background ? b_mod->show_render() : b_mod->show_viewport())) { - BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr); - BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr); - BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr); + int pa_no = 0; + if(!(b_part.child_type() == 0)) + pa_no = totparts; - if((b_part.render_type() == BL::ParticleSettings::render_type_PATH) && (b_part.type() == BL::ParticleSettings::type_HAIR)) { - int totparts = b_psys.particles.length(); - int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f); - int totcurves = totchild; - - if(b_part.child_type() == 0) - totcurves += totparts; + int num_add = (totparts+totchild - pa_no); + CData->curve_vcol.reserve(CData->curve_vcol.size() + num_add); - if(totcurves == 0) - continue; + BL::ParticleSystem::particles_iterator b_pa; + b_psys.particles.begin(b_pa); + for(; pa_no < totparts+totchild; pa_no++) { + /* Add vertex colors */ + BL::Mesh::tessface_vertex_colors_iterator l; + b_mesh.tessface_vertex_colors.begin(l); - int pa_no = 0; - if(!(b_part.child_type() == 0)) - pa_no = totparts; + float3 vcol = make_float3(0.0f, 0.0f, 0.0f); + if(b_mesh.tessface_vertex_colors.length()) + b_psys.mcol_on_emitter(b_psmd, *b_pa, pa_no, vcol_num, &vcol.x); + CData->curve_vcol.push_back(vcol); - int num_add = (totparts+totchild - pa_no); - CData->curve_uv.reserve(CData->curve_uv.size() + num_add); + if(pa_no < totparts && b_pa != b_psys.particles.end()) + ++b_pa; + } +} + +/* A little bit of templated code here to avoid much duplication for parent/child strands: + * Most attributes are the same for both types, but some are handled slightly differently. + * These attributes are handled by templated utility functions that automatically get selected by type. + */ +template <typename StrandsT> +struct StrandsTraits; - BL::ParticleSystem::particles_iterator b_pa; - b_psys.particles.begin(b_pa); - for(; pa_no < totparts+totchild; pa_no++) { - /* Add UVs */ - BL::Mesh::tessface_uv_textures_iterator l; - b_mesh->tessface_uv_textures.begin(l); +template<> +struct StrandsTraits<BL::Strands> +{ + typedef BL::StrandsCurve curve_t; + typedef BL::StrandsVertex vertex_t; + + static float3 get_location(BL::Strands b_strands, int index) + { + float *co = (b_strands.has_motion_state())? b_strands.motion_state[index].location() : b_strands.vertices[index].location(); + return make_float3(co[0], co[1], co[2]); + } + static float3 get_uv(BL::Strands /*b_strands*/, int /*index*/, int /*uv_num*/) + { + return make_float3(0.0f, 0.0f, 0.0f); + } + static float3 get_vcol(BL::Strands /*b_strands*/, int /*index*/, int /*vcol_num*/) + { + return make_float3(0.0f, 0.0f, 0.0f); + } +}; - float3 uv = make_float3(0.0f, 0.0f, 0.0f); - if(b_mesh->tessface_uv_textures.length()) - b_psys.uv_on_emitter(psmd, *b_pa, pa_no, uv_num, &uv.x); - CData->curve_uv.push_back(uv); +template<> +struct StrandsTraits<BL::StrandsChildren> +{ + typedef BL::StrandsChildCurve curve_t; + typedef BL::StrandsChildVertex vertex_t; + + static float3 get_location(BL::StrandsChildren b_strands, int index) + { + float *co = b_strands.vertices[index].location(); + return make_float3(co[0], co[1], co[2]); + } + static float3 get_uv(BL::StrandsChildren b_strands, int index, int uv_num) + { + if (uv_num < b_strands.num_curve_uv_layers()) { + size_t offset = uv_num * b_strands.curves.length(); + float *uv = b_strands.curve_uvs[offset + index].uv(); + return make_float3(uv[0], uv[1], 0.0f); + } + else + return make_float3(0.0f, 0.0f, 0.0f); + } + static float3 get_vcol(BL::StrandsChildren b_strands, int index, int vcol_num) + { + if (vcol_num < b_strands.num_curve_vcol_layers()) { + size_t offset = vcol_num * b_strands.curves.length(); + float *vcol = b_strands.curve_vcols[offset + index].vcol(); + return make_float3(vcol[0], vcol[1], vcol[2]); + } + else + return make_float3(0.0f, 0.0f, 0.0f); + } +}; - if(pa_no < totparts && b_pa != b_psys.particles.end()) - ++b_pa; - } +template <typename StrandsT> +static bool ObtainCacheStrandsData(Mesh *mesh, BL::Scene /*b_scene*/, BL::Object /*b_parent*/, BL::DupliObject /*b_dupli_ob*/, BL::ParticleSystem b_psys, StrandsT b_strands, const Transform &/*itfm*/, + ParticleCurveData *CData, bool /*background*/) +{ + typedef StrandsTraits<StrandsT> traits; + typedef typename traits::curve_t CurveT; + + BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr); + PointerRNA cpsys = RNA_pointer_get(&b_part.ptr, "cycles"); + + int mi = clamp(b_part.material()-1, 0, mesh->used_shaders.size()-1); + int shader = mesh->used_shaders[mi]; + + int totcurves = b_strands.curves.length(); + int totvert = b_strands.vertices.length(); + + int keyno = CData->curvekey_co.size(); + int curvenum = CData->curve_keynum.size(); + + CData->psys_firstcurve.push_back(curvenum); + CData->psys_curvenum.push_back(totcurves); + CData->psys_shader.push_back(shader); + + float radius = get_float(cpsys, "radius_scale") * 0.5f; + + CData->psys_rootradius.push_back(radius * get_float(cpsys, "root_width")); + CData->psys_tipradius.push_back(radius * get_float(cpsys, "tip_width")); + CData->psys_shape.push_back(get_float(cpsys, "shape")); + CData->psys_closetip.push_back(get_boolean(cpsys, "use_closetip")); + + CData->curve_firstkey.reserve(CData->curve_firstkey.size() + totcurves); + CData->curve_keynum.reserve(CData->curve_keynum.size() + totcurves); + CData->curve_length.reserve(CData->curve_length.size() + totcurves); + CData->curvekey_co.reserve(CData->curvekey_co.size() + totvert); + CData->curvekey_time.reserve(CData->curvekey_time.size() + totvert); + + int icurve = 0; + int ivert = 0; + for(; icurve < totcurves; ++icurve) { + CurveT b_curve = b_strands.curves[icurve]; + int numverts = b_curve.size(); + int showverts = b_curve.render_size(); + int usedverts = 0; + CData->curve_firstkey.push_back(keyno); + + float curve_length = 0.0f; + float3 pcKey; + for(int cvert = 0; cvert < showverts; ++cvert) { + float3 cKey = traits::get_location(b_strands, ivert + cvert); + + if(cvert > 0) { + float step_length = len(cKey - pcKey); + if(step_length == 0.0f) + continue; + curve_length += step_length; } + CData->curvekey_co.push_back(cKey); + CData->curvekey_time.push_back(curve_length); + pcKey = cKey; + usedverts++; } - } + keyno += usedverts; + ivert += numverts; + CData->curve_keynum.push_back(usedverts); + CData->curve_length.push_back(curve_length); + curvenum++; + } + return true; } -bool ObtainCacheParticleVcol(Mesh *mesh, BL::Mesh *b_mesh, BL::Object *b_ob, ParticleCurveData *CData, bool background, int vcol_num) +template <typename StrandsT> +static bool ObtainCacheStrandsUV(Mesh * /*mesh*/, BL::Scene /*b_scene*/, BL::Object /*b_parent*/, BL::DupliObject /*b_dupli_ob*/, BL::ParticleSystem /*b_psys*/, StrandsT b_strands, + ParticleCurveData *CData, bool /*background*/, int uv_num) { - if(!(mesh && b_mesh && b_ob && CData)) - return false; - - CData->curve_vcol.clear(); - - BL::Object::modifiers_iterator b_mod; - for(b_ob->modifiers.begin(b_mod); b_mod != b_ob->modifiers.end(); ++b_mod) { - if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && (background ? b_mod->show_render() : b_mod->show_viewport())) { - BL::ParticleSystemModifier psmd((const PointerRNA)b_mod->ptr); - BL::ParticleSystem b_psys((const PointerRNA)psmd.particle_system().ptr); - BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr); - - if((b_part.render_type() == BL::ParticleSettings::render_type_PATH) && (b_part.type() == BL::ParticleSettings::type_HAIR)) { - int totparts = b_psys.particles.length(); - int totchild = background ? b_psys.child_particles.length() : (int)((float)b_psys.child_particles.length() * (float)b_part.draw_percentage() / 100.0f); - int totcurves = totchild; - - if(b_part.child_type() == 0) - totcurves += totparts; - - if(totcurves == 0) - continue; - - int pa_no = 0; - if(!(b_part.child_type() == 0)) - pa_no = totparts; - - int num_add = (totparts+totchild - pa_no); - CData->curve_vcol.reserve(CData->curve_vcol.size() + num_add); + typedef StrandsTraits<StrandsT> traits; + +// BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr); - BL::ParticleSystem::particles_iterator b_pa; - b_psys.particles.begin(b_pa); - for(; pa_no < totparts+totchild; pa_no++) { - /* Add vertex colors */ - BL::Mesh::tessface_vertex_colors_iterator l; - b_mesh->tessface_vertex_colors.begin(l); + int totcurves = b_strands.curves.length(); - float3 vcol = make_float3(0.0f, 0.0f, 0.0f); - if(b_mesh->tessface_vertex_colors.length()) - b_psys.mcol_on_emitter(psmd, *b_pa, pa_no, vcol_num, &vcol.x); - CData->curve_vcol.push_back(vcol); + CData->curve_uv.reserve(CData->curve_uv.size() + totcurves); - if(pa_no < totparts && b_pa != b_psys.particles.end()) - ++b_pa; - } - } - } + int icurve = 0; + for(; icurve < totcurves; ++icurve) { + CData->curve_uv.push_back(traits::get_uv(b_strands, icurve, uv_num)); } + + return true; +} +template <typename StrandsT> +static bool ObtainCacheStrandsVcol(Mesh * /*mesh*/, BL::Scene /*b_scene*/, BL::Object /*b_parent*/, BL::DupliObject /*b_dupli_ob*/, BL::ParticleSystem /*b_psys*/, StrandsT b_strands, + ParticleCurveData *CData, bool /*background*/, int vcol_num) +{ + typedef StrandsTraits<StrandsT> traits; + +// BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr); + + int totcurves = b_strands.curves.length(); + + CData->curve_vcol.reserve(CData->curve_vcol.size() + totcurves); + + int icurve = 0; + for(; icurve < totcurves; ++icurve) { + CData->curve_vcol.push_back(traits::get_vcol(b_strands, icurve, vcol_num)); + } + return true; } @@ -750,6 +873,39 @@ void ExportCurveTriangleUV(ParticleCurveData *CData, int vert_offset, int resol, } } +void ExportCurveUV(Mesh *mesh, ParticleCurveData *CData, ustring name, bool active_render, int primitive, int vert_offset, int resol) +{ + AttributeStandard std = (active_render)? ATTR_STD_UV: ATTR_STD_NONE; + Attribute *attr_uv; + + if(primitive == CURVE_TRIANGLES) { + if(active_render) + attr_uv = mesh->attributes.add(std, name); + else + attr_uv = mesh->attributes.add(name, TypeDesc::TypePoint, ATTR_ELEMENT_CORNER); + + float3 *uv = attr_uv->data_float3(); + + ExportCurveTriangleUV(CData, vert_offset, resol, uv); + } + else { + if(active_render) + attr_uv = mesh->curve_attributes.add(std, name); + else + attr_uv = mesh->curve_attributes.add(name, TypeDesc::TypePoint, ATTR_ELEMENT_CURVE); + + float3 *uv = attr_uv->data_float3(); + + if(uv) { + size_t i = 0; + + for(size_t curve = 0; curve < CData->curve_uv.size(); curve++) + if(!(CData->curve_keynum[curve] <= 1 || CData->curve_length[curve] == 0.0f)) + uv[i++] = CData->curve_uv[curve]; + } + } +} + void ExportCurveTriangleVcol(ParticleCurveData *CData, int vert_offset, int resol, uchar4 *cdata) { if(cdata == NULL) @@ -782,6 +938,31 @@ void ExportCurveTriangleVcol(ParticleCurveData *CData, int vert_offset, int reso } } +void ExportCurveVcol(Mesh *mesh, ParticleCurveData *CData, ustring name, int primitive, int vert_offset, int resol) +{ + if(primitive == CURVE_TRIANGLES) { + Attribute *attr_vcol = mesh->attributes.add(name, TypeDesc::TypeColor, ATTR_ELEMENT_CORNER_BYTE); + + uchar4 *cdata = attr_vcol->data_uchar4(); + + ExportCurveTriangleVcol(CData, vert_offset, resol, cdata); + } + else { + Attribute *attr_vcol = mesh->curve_attributes.add(name, TypeDesc::TypeColor, ATTR_ELEMENT_CURVE); + + float3 *fdata = attr_vcol->data_float3(); + + if(fdata) { + size_t i = 0; + + for(size_t curve = 0; curve < CData->curve_vcol.size(); curve++) + if(!(CData->curve_keynum[curve] <= 1 || CData->curve_length[curve] == 0.0f)) + fdata[i++] = color_srgb_to_scene_linear(CData->curve_vcol[curve]); + } + } +} + + /* Hair Curve Sync */ void BlenderSync::sync_curve_settings() @@ -856,8 +1037,61 @@ void BlenderSync::sync_curve_settings() curve_system_manager->tag_update(scene); } -void BlenderSync::sync_curves(Mesh *mesh, BL::Mesh b_mesh, BL::Object b_ob, bool motion, int time_index) +struct CurvesPSysData { + CurvesPSysData(BL::ParticleSystemModifier b_psmd=PointerRNA_NULL, BL::ParticleSystem b_psys=PointerRNA_NULL, + BL::Strands b_strands=PointerRNA_NULL, BL::StrandsChildren b_strands_children=PointerRNA_NULL) : + b_psmd(b_psmd), b_psys(b_psys), b_strands(b_strands), b_strands_children(b_strands_children) + {} + + BL::ParticleSystemModifier b_psmd; + BL::ParticleSystem b_psys; + BL::Strands b_strands; + BL::StrandsChildren b_strands_children; +}; + +static void curves_get_psys_data(std::vector<CurvesPSysData> &b_psys_list, BL::Scene b_scene, BL::Object b_ob, BL::Object b_parent, BL::DupliObject b_dupli_ob, bool preview) +{ + BL::Object::modifiers_iterator b_mod; + for(b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) { + if((b_mod->type() == b_mod->type_PARTICLE_SYSTEM) && (preview ? b_mod->show_viewport() : b_mod->show_render())) { + BL::ParticleSystemModifier b_psmd((const PointerRNA)b_mod->ptr); + BL::ParticleSystem b_psys((const PointerRNA)b_psmd.particle_system().ptr); + BL::ParticleSettings b_part((const PointerRNA)b_psys.settings().ptr); + + if((b_part.render_type() == BL::ParticleSettings::render_type_PATH) && (b_part.type() == BL::ParticleSettings::type_HAIR)) { + int settings = preview ? 1 : 2; + + BL::StrandsChildren b_strands_children = PointerRNA_NULL; + BL::Strands b_strands = PointerRNA_NULL; + + if (b_dupli_ob && b_parent) { + b_strands_children = b_dupli_ob.strands_children_new(b_scene, b_parent, b_psys, settings); + if (!b_strands_children) + b_strands = b_dupli_ob.strands_new(b_scene, b_parent, b_psys, settings); + } + + b_psys_list.push_back(CurvesPSysData(b_psmd, b_psys, b_strands, b_strands_children)); + } + } + } +} + +static void curves_free_psys_data(std::vector<CurvesPSysData> &b_psys_list, BL::DupliObject b_dupli_ob) +{ + /* free temporary strands data */ + for (int i = 0; i < b_psys_list.size(); ++i) { + CurvesPSysData &psys_data = b_psys_list[i]; + if (psys_data.b_strands) + b_dupli_ob.strands_free(psys_data.b_strands); + if (psys_data.b_strands_children) + b_dupli_ob.strands_children_free(psys_data.b_strands_children); + } +} + +void BlenderSync::sync_curves(Mesh *mesh, BL::Mesh b_mesh, BL::Object b_parent, bool motion, int time_index, BL::DupliObject b_dupli_ob) { + BL::Object b_ob = (b_dupli_ob ? b_dupli_ob.object() : b_parent); + if(!motion) { /* Clear stored curve data */ mesh->curve_keys.clear(); @@ -888,27 +1122,46 @@ void BlenderSync::sync_curves(Mesh *mesh, BL::Mesh b_mesh, BL::Object b_ob, bool if(!preview) set_resolution(&b_ob, &b_scene, true); - ObtainCacheParticleData(mesh, &b_mesh, &b_ob, &CData, !preview); + Transform tfm = get_transform(b_ob.matrix_world()); + Transform itfm = transform_quick_inverse(tfm); + + /* obtain camera parameters */ + float3 RotCam; + Camera *camera = scene->camera; + if(camera->type == CAMERA_ORTHOGRAPHIC) { + Transform &ctfm = camera->matrix; + RotCam = -make_float3(ctfm.x.z, ctfm.y.z, ctfm.z.z); + } + else { + Transform &ctfm = camera->matrix; + RotCam = transform_point(&itfm, make_float3(ctfm.x.w, ctfm.y.w, ctfm.z.w)); + } + bool is_ortho_camera = camera->type == CAMERA_ORTHOGRAPHIC; + + std::vector<CurvesPSysData> b_psys_list; + curves_get_psys_data(b_psys_list, b_scene, b_ob, b_parent, b_dupli_ob, preview); + + for (int i = 0; i < b_psys_list.size(); ++i) { + CurvesPSysData &psys_data = b_psys_list[i]; + if (psys_data.b_strands_children) + /* use child strands cache */ + ObtainCacheStrandsData(mesh, b_scene, b_parent, b_dupli_ob, psys_data.b_psys, psys_data.b_strands_children, + itfm, &CData, !preview); + else if (psys_data.b_strands) + /* use parent strands cache */ + ObtainCacheStrandsData(mesh, b_scene, b_parent, b_dupli_ob, psys_data.b_psys, psys_data.b_strands, + itfm, &CData, !preview); + else { + /* use object data */ + ObtainCacheParticleData(mesh, b_ob, psys_data.b_psys, + itfm, &CData, !preview); + } + } /* add hair geometry to mesh */ if(primitive == CURVE_TRIANGLES) { if(triangle_method == CURVE_CAMERA_TRIANGLES) { - /* obtain camera parameters */ - float3 RotCam; - Camera *camera = scene->camera; - Transform &ctfm = camera->matrix; - if(camera->type == CAMERA_ORTHOGRAPHIC) { - RotCam = -make_float3(ctfm.x.z, ctfm.y.z, ctfm.z.z); - } - else { - Transform tfm = get_transform(b_ob.matrix_world()); - Transform itfm = transform_quick_inverse(tfm); - RotCam = transform_point(&itfm, make_float3(ctfm.x.w, - ctfm.y.w, - ctfm.z.w)); - } - bool is_ortho = camera->type == CAMERA_ORTHOGRAPHIC; - ExportCurveTrianglePlanes(mesh, &CData, RotCam, is_ortho); + ExportCurveTrianglePlanes(mesh, &CData, RotCam, is_ortho_camera); } else { ExportCurveTriangleGeometry(mesh, &CData, resolution); @@ -955,33 +1208,31 @@ void BlenderSync::sync_curves(Mesh *mesh, BL::Mesh b_mesh, BL::Object b_ob, bool int vcol_num = 0; for(b_mesh.tessface_vertex_colors.begin(l); l != b_mesh.tessface_vertex_colors.end(); ++l, vcol_num++) { + ustring name = ustring(l->name().c_str()); + if(!mesh->need_attribute(scene, ustring(l->name().c_str()))) continue; - ObtainCacheParticleVcol(mesh, &b_mesh, &b_ob, &CData, !preview, vcol_num); - - if(primitive == CURVE_TRIANGLES) { - Attribute *attr_vcol = mesh->attributes.add( - ustring(l->name().c_str()), TypeDesc::TypeColor, ATTR_ELEMENT_CORNER_BYTE); - - uchar4 *cdata = attr_vcol->data_uchar4(); - - ExportCurveTriangleVcol(&CData, tri_num * 3, used_res, cdata); - } - else { - Attribute *attr_vcol = mesh->curve_attributes.add( - ustring(l->name().c_str()), TypeDesc::TypeColor, ATTR_ELEMENT_CURVE); - - float3 *fdata = attr_vcol->data_float3(); - - if(fdata) { - size_t i = 0; - - for(size_t curve = 0; curve < CData.curve_vcol.size(); curve++) - if(!(CData.curve_keynum[curve] <= 1 || CData.curve_length[curve] == 0.0f)) - fdata[i++] = color_srgb_to_scene_linear(CData.curve_vcol[curve]); + CData.curve_vcol.clear(); + + for (int i = 0; i < b_psys_list.size(); ++i) { + CurvesPSysData &psys_data = b_psys_list[i]; + if (psys_data.b_strands_children) + /* use child strands cache */ + ObtainCacheStrandsVcol(mesh, b_scene, b_parent, b_dupli_ob, psys_data.b_psys, psys_data.b_strands_children, + &CData, !preview, vcol_num); + else if (psys_data.b_strands) + /* use parent strands cache */ + ObtainCacheStrandsVcol(mesh, b_scene, b_parent, b_dupli_ob, psys_data.b_psys, psys_data.b_strands, + &CData, !preview, vcol_num); + else { + /* use object data */ + ObtainCacheParticleVcol(mesh, b_ob, b_mesh, psys_data.b_psys, psys_data.b_psmd, + &CData, !preview, vcol_num); } } + + ExportCurveVcol(mesh, &CData, name, primitive, tri_num * 3, used_res); } } @@ -992,45 +1243,37 @@ void BlenderSync::sync_curves(Mesh *mesh, BL::Mesh b_mesh, BL::Object b_ob, bool for(b_mesh.tessface_uv_textures.begin(l); l != b_mesh.tessface_uv_textures.end(); ++l, uv_num++) { bool active_render = l->active_render(); - AttributeStandard std = (active_render)? ATTR_STD_UV: ATTR_STD_NONE; ustring name = ustring(l->name().c_str()); /* UV map */ - if(mesh->need_attribute(scene, name) || mesh->need_attribute(scene, std)) { - Attribute *attr_uv; - - ObtainCacheParticleUV(mesh, &b_mesh, &b_ob, &CData, !preview, uv_num); - - if(primitive == CURVE_TRIANGLES) { - if(active_render) - attr_uv = mesh->attributes.add(std, name); - else - attr_uv = mesh->attributes.add(name, TypeDesc::TypePoint, ATTR_ELEMENT_CORNER); - - float3 *uv = attr_uv->data_float3(); - - ExportCurveTriangleUV(&CData, tri_num * 3, used_res, uv); - } + if(!(mesh->need_attribute(scene, name) || (active_render && mesh->need_attribute(scene, ATTR_STD_UV)))) + continue; + + CData.curve_uv.clear(); + + for (int i = 0; i < b_psys_list.size(); ++i) { + CurvesPSysData &psys_data = b_psys_list[i]; + if (psys_data.b_strands_children) + /* use child strands cache */ + ObtainCacheStrandsUV(mesh, b_scene, b_parent, b_dupli_ob, psys_data.b_psys, psys_data.b_strands_children, + &CData, !preview, uv_num); + else if (psys_data.b_strands) + /* use parent strands cache */ + ObtainCacheStrandsUV(mesh, b_scene, b_parent, b_dupli_ob, psys_data.b_psys, psys_data.b_strands, + &CData, !preview, uv_num); else { - if(active_render) - attr_uv = mesh->curve_attributes.add(std, name); - else - attr_uv = mesh->curve_attributes.add(name, TypeDesc::TypePoint, ATTR_ELEMENT_CURVE); - - float3 *uv = attr_uv->data_float3(); - - if(uv) { - size_t i = 0; - - for(size_t curve = 0; curve < CData.curve_uv.size(); curve++) - if(!(CData.curve_keynum[curve] <= 1 || CData.curve_length[curve] == 0.0f)) - uv[i++] = CData.curve_uv[curve]; - } + /* use object data */ + ObtainCacheParticleUV(mesh, b_ob, b_mesh, psys_data.b_psys, psys_data.b_psmd, + &CData, !preview, uv_num); } } + + ExportCurveUV(mesh, &CData, name, active_render, primitive, tri_num * 3, used_res); } } + curves_free_psys_data(b_psys_list, b_dupli_ob); + if(!preview) set_resolution(&b_ob, &b_scene, false); diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index d88ebb854d2..3fa80a89947 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -229,7 +229,13 @@ static void mikk_compute_tangents(BL::Mesh b_mesh, BL::MeshTextureFaceLayer *b_l /* Create Volume Attribute */ -static void create_mesh_volume_attribute(BL::Object b_ob, Mesh *mesh, ImageManager *image_manager, AttributeStandard std, float frame) +static void create_mesh_volume_attribute(BL::Object b_ob, + Mesh *mesh, + ImageManager *image_manager, + AttributeStandard std, + float frame, + AttributeStandard special_std = ATTR_STD_NONE, + bool from_alpha = false) { BL::SmokeDomainSettings b_domain = object_smoke_domain_find(b_ob); @@ -240,17 +246,35 @@ static void create_mesh_volume_attribute(BL::Object b_ob, Mesh *mesh, ImageManag VoxelAttribute *volume_data = attr->data_voxel(); bool is_float, is_linear; bool animated = false; + AttributeStandard data_attr = (special_std == ATTR_STD_NONE) ? std : special_std; volume_data->manager = image_manager; - volume_data->slot = image_manager->add_image(Attribute::standard_name(std), + volume_data->slot = image_manager->add_image(Attribute::standard_name(data_attr), b_ob.ptr.data, animated, frame, is_float, is_linear, INTERPOLATION_LINEAR, true); + volume_data->from_alpha = from_alpha; } static void create_mesh_volume_attributes(Scene *scene, BL::Object b_ob, Mesh *mesh, float frame) { /* for smoke volume rendering */ - if(mesh->need_attribute(scene, ATTR_STD_VOLUME_DENSITY)) - create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_DENSITY, frame); + if(mesh->need_attribute(scene, ATTR_STD_VOLUME_DENSITY)) { + /* Special case: we re-map density to A channel of Color texture + * if both attributes are requested. + */ + AttributeStandard special_std = ATTR_STD_VOLUME_DENSITY; + bool from_alpha = false; + if(mesh->need_attribute(scene, ATTR_STD_VOLUME_COLOR)) { + special_std = ATTR_STD_VOLUME_COLOR; + from_alpha = true; + } + create_mesh_volume_attribute(b_ob, + mesh, + scene->image_manager, + ATTR_STD_VOLUME_DENSITY, + frame, + special_std, + from_alpha); + } if(mesh->need_attribute(scene, ATTR_STD_VOLUME_COLOR)) create_mesh_volume_attribute(b_ob, mesh, scene->image_manager, ATTR_STD_VOLUME_COLOR, frame); if(mesh->need_attribute(scene, ATTR_STD_VOLUME_FLAME)) @@ -422,6 +446,52 @@ static void attr_create_pointiness(Scene *scene, } } +/* Create vertex custom attributes. */ +static void attr_create_vertex_properties(Scene *scene, + Mesh *mesh, + BL::Mesh b_mesh) +{ + { + BL::Mesh::vertex_layers_float_iterator l; + for(b_mesh.vertex_layers_float.begin(l); l != b_mesh.vertex_layers_float.end(); ++l) { + if(!mesh->need_attribute(scene, ustring(l->name().c_str()))) + continue; + + Attribute *attr = mesh->attributes.add( + ustring(l->name().c_str()), TypeDesc::TypeFloat, ATTR_ELEMENT_VERTEX); + + BL::MeshVertexFloatPropertyLayer::data_iterator d; + float *data = attr->data_float(); + size_t i = 0; + + for(l->data.begin(d); d != l->data.end(); ++d, ++i) { + *data = d->value(); + data += 1; + } + } + } + + { + BL::Mesh::vertex_layers_int_iterator l; + for(b_mesh.vertex_layers_int.begin(l); l != b_mesh.vertex_layers_int.end(); ++l) { + if(!mesh->need_attribute(scene, ustring(l->name().c_str()))) + continue; + + Attribute *attr = mesh->attributes.add( + ustring(l->name().c_str()), TypeDesc::TypeFloat, ATTR_ELEMENT_VERTEX); + + BL::MeshVertexIntPropertyLayer::data_iterator d; + float *data = attr->data_float(); + size_t i = 0; + + for(l->data.begin(d); d != l->data.end(); ++d, ++i) { + *data = (float)d->value(); + data += 1; + } + } + } +} + /* Create Mesh */ static void create_mesh(Scene *scene, Mesh *mesh, BL::Mesh b_mesh, const vector<uint>& used_shaders) @@ -528,6 +598,7 @@ static void create_mesh(Scene *scene, Mesh *mesh, BL::Mesh b_mesh, const vector< */ attr_create_vertex_color(scene, mesh, b_mesh, nverts); attr_create_uv_map(scene, mesh, b_mesh, nverts); + attr_create_vertex_properties(scene, mesh, b_mesh); /* for volume objects, create a matrix to transform from object space to * mesh texture space. this does not work with deformations but that can @@ -587,8 +658,10 @@ static void create_subd_mesh(Scene *scene, Mesh *mesh, BL::Mesh b_mesh, PointerR /* Sync */ -Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tris) +Mesh *BlenderSync::sync_mesh(BL::Object b_parent, bool object_updated, bool hide_tris, BL::DupliObject b_dupli_ob) { + BL::Object b_ob = (b_dupli_ob ? b_dupli_ob.object() : b_parent); + /* When viewport display is not needed during render we can force some * caches to be releases from blender side in order to reduce peak memory * footprint during synchronization process. @@ -599,7 +672,6 @@ Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tri /* test if we can instance or if the object is modified */ BL::ID b_ob_data = b_ob.data(); - BL::ID key = (BKE_object_is_modified(b_ob))? b_ob: b_ob_data; BL::Material material_override = render_layer.material_override; /* find shader indices */ @@ -630,7 +702,24 @@ Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tri } Mesh *mesh; - if(!mesh_map.sync(&mesh, key)) { + bool need_update; + BL::CacheLibrary b_cachelib = b_parent.cache_library(); + bool use_dupli_override = b_dupli_ob && b_cachelib && + (b_cachelib.source_mode() == BL::CacheLibrary::source_mode_CACHE || + b_cachelib.display_mode() == BL::CacheLibrary::display_mode_RESULT); + if (use_dupli_override) { + /* if a dupli override (cached data) is used, identify the mesh by object and parent together, + * so that individual per-dupli overrides are possible. + */ + MeshKey key = MeshKey(b_parent, b_ob); + need_update = mesh_map.sync(&mesh, b_ob, b_parent, key); + } + else { + BL::ID key = (BKE_object_is_modified(b_ob))? b_ob: b_ob_data; + need_update = mesh_map.sync(&mesh, key); + } + + if(!need_update) { /* if transform was applied to mesh, need full update */ if(object_updated && mesh->transform_applied); /* test if shaders changed, these can be object level so mesh @@ -681,7 +770,9 @@ Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tri b_ob.update_from_editmode(); bool need_undeformed = mesh->need_attribute(scene, ATTR_STD_GENERATED); - BL::Mesh b_mesh = object_to_mesh(b_data, b_ob, b_scene, true, !preview, need_undeformed); + BL::Mesh b_mesh = (use_dupli_override)? + dupli_to_mesh(b_data, b_scene, b_parent, b_dupli_ob, !preview, need_undeformed): + object_to_mesh(b_data, b_ob, b_scene, true, !preview, need_undeformed); if(b_mesh) { if(render_layer.use_surfaces && !hide_tris) { @@ -694,10 +785,10 @@ Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tri } if(render_layer.use_hair) - sync_curves(mesh, b_mesh, b_ob, false); + sync_curves(mesh, b_mesh, b_parent, false, 0, b_dupli_ob); if(can_free_caches) { - b_ob.cache_release(); + b_ob.cache_release(false); } /* free derived mesh */ @@ -740,8 +831,9 @@ Mesh *BlenderSync::sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tri return mesh; } -void BlenderSync::sync_mesh_motion(BL::Object b_ob, Object *object, float motion_time) +void BlenderSync::sync_mesh_motion(BL::Object b_parent, Object *object, float motion_time, BL::DupliObject b_dupli_ob) { + BL::Object b_ob = (b_dupli_ob ? b_dupli_ob.object() : b_parent); /* ensure we only sync instanced meshes once */ Mesh *mesh = object->mesh; @@ -800,7 +892,9 @@ void BlenderSync::sync_mesh_motion(BL::Object b_ob, Object *object, float motion if(ccl::BKE_object_is_deform_modified(b_ob, b_scene, preview)) { /* get derived mesh */ - b_mesh = object_to_mesh(b_data, b_ob, b_scene, true, !preview, false); + b_mesh = (b_dupli_ob && b_parent)? + dupli_to_mesh(b_data, b_scene, b_parent, b_dupli_ob, !preview, false): + object_to_mesh(b_data, b_ob, b_scene, true, !preview, false); } if(!b_mesh) { @@ -890,7 +984,7 @@ void BlenderSync::sync_mesh_motion(BL::Object b_ob, Object *object, float motion /* hair motion */ if(numkeys) - sync_curves(mesh, b_mesh, b_ob, true, time_index); + sync_curves(mesh, b_mesh, b_parent, true, time_index, b_dupli_ob); /* free derived mesh */ b_data.meshes.remove(b_mesh); diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/blender_object.cpp index 1e17d306fd0..6afcc2231aa 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/blender_object.cpp @@ -234,8 +234,53 @@ void BlenderSync::sync_background_light(bool use_portal) /* Object */ -Object *BlenderSync::sync_object(BL::Object b_parent, int persistent_id[OBJECT_PERSISTENT_ID_SIZE], BL::DupliObject b_dupli_ob, - Transform& tfm, uint layer_flag, float motion_time, bool hide_tris, bool *use_portal) +static bool object_boundbox_clip(Scene *scene, + BL::Object b_ob, + Transform& tfm, + float margin) +{ + Camera *cam = scene->camera; + Transform& worldtondc = cam->worldtondc; + BL::Array<float, 24> boundbox = b_ob.bound_box(); + float3 bb_min = make_float3(FLT_MAX, FLT_MAX, FLT_MAX), + bb_max = make_float3(-FLT_MAX, -FLT_MAX, -FLT_MAX); + bool all_behind = true; + for(int i = 0; i < 8; ++i) { + float3 p = make_float3(boundbox[3 * i + 0], + boundbox[3 * i + 1], + boundbox[3 * i + 2]); + p = transform_point(&tfm, p); + p = transform_point(&worldtondc, p); + if(p.z >= -margin) { + all_behind = false; + } + p /= p.z; + bb_min = min(bb_min, p); + bb_max = max(bb_max, p); + } + if(!all_behind) { + if(bb_min.x >= 1.0f + margin || + bb_min.y >= 1.0f + margin || + bb_max.x <= -margin || + bb_max.y <= -margin) + { + return true; + } + return false; + } + return true; +} + +Object *BlenderSync::sync_object(BL::Object b_parent, + int persistent_id[OBJECT_PERSISTENT_ID_SIZE], + BL::DupliObject b_dupli_ob, + Transform& tfm, + uint layer_flag, + float motion_time, + bool hide_tris, + bool use_camera_cull, + float camera_cull_margin, + bool *use_portal) { BL::Object b_ob = (b_dupli_ob ? b_dupli_ob.object() : b_parent); bool motion = motion_time != 0.0f; @@ -253,6 +298,11 @@ Object *BlenderSync::sync_object(BL::Object b_parent, int persistent_id[OBJECT_P if(!object_is_mesh(b_ob)) return NULL; + /* Perform camera space culling. */ + if(use_camera_cull && object_boundbox_clip(scene, b_ob, tfm, camera_cull_margin)) { + return NULL; + } + /* key to lookup object */ ObjectKey key(b_parent, persistent_id, b_ob); Object *object; @@ -279,7 +329,7 @@ Object *BlenderSync::sync_object(BL::Object b_parent, int persistent_id[OBJECT_P /* mesh deformation */ if(object->mesh) - sync_mesh_motion(b_ob, object, motion_time); + sync_mesh_motion(b_parent, object, motion_time, b_dupli_ob); } return object; @@ -294,7 +344,10 @@ Object *BlenderSync::sync_object(BL::Object b_parent, int persistent_id[OBJECT_P bool use_holdout = (layer_flag & render_layer.holdout_layer) != 0; /* mesh sync */ - object->mesh = sync_mesh(b_ob, object_updated, hide_tris); + if (b_dupli_ob) + object->mesh = sync_mesh(b_parent, object_updated, hide_tris, b_dupli_ob); + else + object->mesh = sync_mesh(b_ob, object_updated, hide_tris); /* special case not tracked by object update flags */ @@ -480,6 +533,15 @@ void BlenderSync::sync_objects(BL::SpaceView3D b_v3d, float motion_time) mesh_motion_synced.clear(); } + PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); + bool allow_camera_cull = scene->camera->type != CAMERA_PANORAMA && + !b_scene.render().use_multiview() && + get_boolean(cscene, "use_camera_cull"); + float camera_cull_margin = 0.0f; + if(allow_camera_cull) { + camera_cull_margin = get_float(cscene, "camera_cull_margin"); + } + /* object loop */ BL::Scene::object_bases_iterator b_base; BL::Scene b_sce = b_scene; @@ -502,6 +564,12 @@ void BlenderSync::sync_objects(BL::SpaceView3D b_v3d, float motion_time) if(!hide) { progress.set_sync_status("Synchronizing object", b_ob.name()); + PointerRNA cobject = RNA_pointer_get(&b_ob.ptr, "cycles"); + bool use_camera_cull = allow_camera_cull && get_boolean(cobject, "use_camera_cull"); + if(use_camera_cull) { + /* Need to have proper projection matrix. */ + scene->camera->update(); + } if(b_ob.is_duplicator() && !object_render_hide_duplis(b_ob)) { /* dupli objects */ b_ob.dupli_list_create(b_scene, dupli_settings); @@ -521,7 +589,16 @@ void BlenderSync::sync_objects(BL::SpaceView3D b_v3d, float motion_time) BL::Array<int, OBJECT_PERSISTENT_ID_SIZE> persistent_id = b_dup->persistent_id(); /* sync object and mesh or light data */ - Object *object = sync_object(b_ob, persistent_id.data, *b_dup, tfm, ob_layer, motion_time, hide_tris, &use_portal); + Object *object = sync_object(b_ob, + persistent_id.data, + *b_dup, + tfm, + ob_layer, + motion_time, + hide_tris, + use_camera_cull, + camera_cull_margin, + &use_portal); /* sync possible particle data, note particle_id * starts counting at 1, first is dummy particle */ @@ -541,7 +618,16 @@ void BlenderSync::sync_objects(BL::SpaceView3D b_v3d, float motion_time) if(!object_render_hide(b_ob, true, true, hide_tris)) { /* object itself */ Transform tfm = get_transform(b_ob.matrix_world()); - sync_object(b_ob, NULL, PointerRNA_NULL, tfm, ob_layer, motion_time, hide_tris, &use_portal); + sync_object(b_ob, + NULL, + PointerRNA_NULL, + tfm, + ob_layer, + motion_time, + hide_tris, + use_camera_cull, + camera_cull_margin, + &use_portal); } } diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index cdc44748923..9a1db703836 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -101,7 +101,8 @@ void BlenderSession::create_session() start_resize_time = 0.0; /* create scene */ - scene = new Scene(scene_params, session_params.device); + bool free_data_after_update = background && !scene_params.persistent_data; + scene = new Scene(scene_params, session_params.device, free_data_after_update); /* setup callbacks for builtin image support */ scene->image_manager->builtin_image_info_cb = function_bind(&BlenderSession::builtin_image_info, this, _1, _2, _3, _4, _5, _6, _7); @@ -413,6 +414,7 @@ void BlenderSession::render() /* set callback to write out render results */ session->write_render_tile_cb = function_bind(&BlenderSession::write_render_tile, this, _1); session->update_render_tile_cb = function_bind(&BlenderSession::update_render_tile, this, _1); + session->clear_database_cb = function_bind(&BlenderSession::clear_blender_database, this); /* get buffer parameters */ SessionParams session_params = BlenderSync::get_session_params(b_engine, b_userpref, b_scene, background); @@ -511,6 +513,7 @@ void BlenderSession::render() /* clear callback */ session->write_render_tile_cb = function_null; session->update_render_tile_cb = function_null; + session->clear_database_cb = function_null; /* free all memory used (host and device), so we wouldn't leave render * engine with extra memory allocated @@ -1012,6 +1015,18 @@ void BlenderSession::builtin_image_info(const string &builtin_name, void *builti is_float = true; } + else { + /* TODO(sergey): Check we're indeed in shader node tree. */ + PointerRNA ptr; + RNA_pointer_create(NULL, &RNA_Node, builtin_data, &ptr); + BL::Node b_node(ptr); + if(b_node.is_a(&RNA_ShaderNodeTexPointDensity)) { + BL::ShaderNodeTexPointDensity b_point_density_node(b_node); + channels = 4; + width = height = depth = b_point_density_node.resolution(); + is_float = true; + } + } } bool BlenderSession::builtin_image_pixels(const string &builtin_name, void *builtin_data, unsigned char *pixels) @@ -1154,9 +1169,41 @@ bool BlenderSession::builtin_image_float_pixels(const string &builtin_name, void fprintf(stderr, "Cycles error: unexpected smoke volume resolution, skipping\n"); } + else { + /* TODO(sergey): Check we're indeed in shader node tree. */ + PointerRNA ptr; + RNA_pointer_create(NULL, &RNA_Node, builtin_data, &ptr); + BL::Node b_node(ptr); + if(b_node.is_a(&RNA_ShaderNodeTexPointDensity)) { + BL::ShaderNodeTexPointDensity b_point_density_node(b_node); + int length; + b_point_density_node.calc_point_density(b_scene, &length, &pixels); + } + } return false; } +void BlenderSession::clear_blender_database() +{ + const bool is_interface_locked = b_engine.render() && + b_engine.render().use_lock_interface(); + const bool can_free_database = BlenderSession::headless || is_interface_locked; + if(!can_free_database) { + /* Database might be used by the interface, can not free it at all. */ + return; + } + for(BL::Scene b_sce = b_scene; b_sce; b_sce = b_sce.background_set()) { + BL::Scene::object_bases_iterator b_base; + for(b_sce.object_bases.begin(b_base); + b_base != b_sce.object_bases.end(); + ++b_base) + { + BL::Object b_ob = b_base->object(); + b_ob.cache_release(true); + } + } +} + CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h index 708776dc8ca..5498548eb74 100644 --- a/intern/cycles/blender/blender_session.h +++ b/intern/cycles/blender/blender_session.h @@ -109,6 +109,8 @@ protected: void builtin_image_info(const string &builtin_name, void *builtin_data, bool &is_float, int &width, int &height, int &depth, int &channels); bool builtin_image_pixels(const string &builtin_name, void *builtin_data, unsigned char *pixels); bool builtin_image_float_pixels(const string &builtin_name, void *builtin_data, float *pixels); + + void clear_blender_database(); }; CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 2b0e8acae38..140560bed0f 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -22,6 +22,7 @@ #include "scene.h" #include "shader.h" +#include "blender_texture.h" #include "blender_sync.h" #include "blender_util.h" @@ -736,6 +737,62 @@ static ShaderNode *add_node(Scene *scene, uvm->from_dupli = b_uvmap_node.from_dupli(); node = uvm; } + else if(b_node.is_a(&RNA_ShaderNodeTexPointDensity)) { + BL::ShaderNodeTexPointDensity b_point_density_node(b_node); + PointDensityTextureNode *point_density = new PointDensityTextureNode(); + point_density->filename = b_point_density_node.name(); + point_density->space = + PointDensityTextureNode::space_enum[(int)b_point_density_node.space()]; + point_density->interpolation = + (InterpolationType)b_point_density_node.interpolation(); + point_density->builtin_data = b_point_density_node.ptr.data; + + /* Transformation form world space to texture space. */ + BL::Object b_ob(b_point_density_node.object()); + if(b_ob) { + float3 loc, size; + point_density_texture_space(b_point_density_node, loc, size); + point_density->tfm = + transform_translate(-loc) * transform_scale(size) * + transform_inverse(get_transform(b_ob.matrix_world())); + } + + /* TODO(sergey): Use more proper update flag. */ + if(true) { + scene->image_manager->tag_reload_image(point_density->filename, + point_density->builtin_data, + point_density->interpolation); + } + node = point_density; + } + else if(b_node.is_a(&RNA_ShaderNodeOpenVDB)) { + BL::ShaderNodeOpenVDB b_vdb_node(b_node); + OpenVDBNode *vdb_node = new OpenVDBNode(); + vdb_node->filename = b_vdb_node.filename(); + vdb_node->sampling = b_vdb_node.sampling(); + + /* TODO(kevin) */ + if(b_vdb_node.source() == 1) { + string filename = b_vdb_node.filename(); + string basename = filename.substr(0, filename.size() - 8); + stringstream ss; + ss << b_scene.frame_current(); + string frame = ss.str(); + frame.insert(frame.begin(), 4 - frame.size(), '0'); + + vdb_node->filename = ustring::format("%s%s.vdb", basename, frame); + } + + BL::Node::outputs_iterator b_output; + + for(b_vdb_node.outputs.begin(b_output); b_output != b_vdb_node.outputs.end(); ++b_output) { + vdb_node->output_names.push_back(ustring(b_output->name())); + vdb_node->add_output(vdb_node->output_names.back().c_str(), + convert_socket_type(*b_output)); + } + + node = vdb_node; + } if(node) graph->add(node); diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index c5fb7925306..87e2932d6b1 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -435,6 +435,8 @@ SceneParams BlenderSync::get_scene_params(BL::Scene b_scene, bool background, bo params.use_qbvh = false; } + params.use_bvh_triangle_storage = RNA_boolean_get(&cscene, "debug_use_triangle_storage"); + return params; } diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/blender_sync.h index 89d93e19e9f..ef279a4fe63 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/blender_sync.h @@ -83,13 +83,22 @@ private: void sync_curve_settings(); void sync_nodes(Shader *shader, BL::ShaderNodeTree b_ntree); - Mesh *sync_mesh(BL::Object b_ob, bool object_updated, bool hide_tris); - void sync_curves(Mesh *mesh, BL::Mesh b_mesh, BL::Object b_ob, bool motion, int time_index = 0); - Object *sync_object(BL::Object b_parent, int persistent_id[OBJECT_PERSISTENT_ID_SIZE], BL::DupliObject b_dupli_ob, - Transform& tfm, uint layer_flag, float motion_time, bool hide_tris, bool *use_portal); + Mesh *sync_mesh(BL::Object b_parent, bool object_updated, bool hide_tris, BL::DupliObject b_dupli_ob = PointerRNA_NULL); + void sync_curves(Mesh *mesh, BL::Mesh b_mesh, BL::Object b_parent, bool motion, int time_index = 0, BL::DupliObject b_dupli_ob = PointerRNA_NULL); + Object *sync_object(BL::Object b_parent, + int persistent_id[OBJECT_PERSISTENT_ID_SIZE], + BL::DupliObject b_dupli_ob, + Transform& tfm, + uint layer_flag, + float motion_time, + bool hide_tris, + bool use_camera_cull, + float camera_cull_margin, + bool *use_portal); void sync_light(BL::Object b_parent, int persistent_id[OBJECT_PERSISTENT_ID_SIZE], BL::Object b_ob, Transform& tfm, bool *use_portal); void sync_background_light(bool use_portal); - void sync_mesh_motion(BL::Object b_ob, Object *object, float motion_time); + void sync_mesh_motion(BL::Object b_parent, Object *object, float motion_time, BL::DupliObject b_dupli_ob = PointerRNA_NULL); + void sync_camera_motion(BL::Object b_ob, float motion_time); /* particles */ @@ -111,7 +120,7 @@ private: id_map<void*, Shader> shader_map; id_map<ObjectKey, Object> object_map; - id_map<void*, Mesh> mesh_map; + id_map<MeshKey, Mesh> mesh_map; id_map<ObjectKey, Light> light_map; id_map<ParticleSystemKey, ParticleSystem> particle_system_map; set<Mesh*> mesh_synced; diff --git a/intern/cycles/blender/blender_texture.cpp b/intern/cycles/blender/blender_texture.cpp new file mode 100644 index 00000000000..cb4dd1792d0 --- /dev/null +++ b/intern/cycles/blender/blender_texture.cpp @@ -0,0 +1,116 @@ +/* + * Copyright 2011-2015 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "blender_texture.h" + +CCL_NAMESPACE_BEGIN + +namespace { + +/* Point density helpers. */ + +static void density_texture_space_invert(float3& loc, + float3& size) +{ + if(size.x != 0.0f) size.x = 0.5f/size.x; + if(size.y != 0.0f) size.y = 0.5f/size.y; + if(size.z != 0.0f) size.z = 0.5f/size.z; + + loc = loc*size - make_float3(0.5f, 0.5f, 0.5f); +} + +static void density_object_texture_space(BL::Object b_ob, + float radius, + float3& loc, + float3& size) +{ + if(b_ob.type() == BL::Object::type_MESH) { + BL::Mesh b_mesh(b_ob.data()); + loc = get_float3(b_mesh.texspace_location()); + size = get_float3(b_mesh.texspace_size()); + } + else { + /* TODO(sergey): Not supported currently. */ + } + /* Adjust texture space to include density points on the boundaries. */ + size = size + make_float3(radius, radius, radius); + density_texture_space_invert(loc, size); +} + +static void density_particle_system_texture_space( + BL::Object b_ob, + BL::ParticleSystem b_particle_system, + float radius, + float3& loc, + float3& size) +{ + if(b_particle_system.settings().type() == BL::ParticleSettings::type_HAIR) { + /* TODO(sergey): Not supported currently. */ + return; + } + Transform tfm = get_transform(b_ob.matrix_world()); + Transform itfm = transform_inverse(tfm); + float3 min = make_float3(FLT_MAX, FLT_MAX, FLT_MAX), + max = make_float3(-FLT_MAX, -FLT_MAX, -FLT_MAX); + float3 particle_size = make_float3(radius, radius, radius); + for(int i = 0; i < b_particle_system.particles.length(); ++i) { + BL::Particle particle = b_particle_system.particles[i]; + float3 location = get_float3(particle.location()); + location = transform_point(&itfm, location); + min = ccl::min(min, location - particle_size); + max = ccl::max(max, location + particle_size); + } + /* Calculate texture space from the particle bounds. */ + loc = (min + max) * 0.5f; + size = (max - min) * 0.5f; + density_texture_space_invert(loc, size); +} + +} /* namespace */ + +void point_density_texture_space(BL::ShaderNodeTexPointDensity b_point_density_node, + float3& loc, + float3& size) +{ + /* Fallback values. */ + loc = make_float3(0.0f, 0.0f, 0.0f); + size = make_float3(0.0f, 0.0f, 0.0f); + BL::Object b_ob(b_point_density_node.object()); + if(!b_ob) { + return; + } + if(b_point_density_node.point_source() == + BL::ShaderNodeTexPointDensity::point_source_PARTICLE_SYSTEM) + { + BL::ParticleSystem b_particle_system( + b_point_density_node.particle_system()); + if(b_particle_system) { + density_particle_system_texture_space(b_ob, + b_particle_system, + b_point_density_node.radius(), + loc, + size); + } + } + else { + density_object_texture_space(b_ob, + b_point_density_node.radius(), + loc, + size); + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_texture.h b/intern/cycles/blender/blender_texture.h new file mode 100644 index 00000000000..74fbca02a9e --- /dev/null +++ b/intern/cycles/blender/blender_texture.h @@ -0,0 +1,31 @@ +/* + * Copyright 2011-2015 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __BLENDER_TEXTURE_H__ +#define __BLENDER_TEXTURE_H__ + +#include <stdlib.h> +#include "blender_sync.h" + +CCL_NAMESPACE_BEGIN + +void point_density_texture_space(BL::ShaderNodeTexPointDensity b_point_density_node, + float3& loc, + float3& size); + +CCL_NAMESPACE_END + +#endif /* __BLENDER_TEXTURE_H__ */ diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index 165242d0dff..b7a5e0f3c3a 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -52,6 +52,18 @@ static inline BL::Mesh object_to_mesh(BL::BlendData data, BL::Object object, BL: return me; } +static inline BL::Mesh dupli_to_mesh(BL::BlendData data, BL::Scene scene, BL::Object parent, BL::DupliObject dob, bool render, bool calc_undeformed) +{ + BL::Mesh me = data.meshes.new_from_dupli(scene, parent, dob, (render)? 2: 1, false, calc_undeformed); + if ((bool)me) { + if (me.use_auto_smooth()) { + me.calc_normals_split(); + } + me.calc_tessface(true); + } + return me; +} + static inline void colorramp_to_array(BL::ColorRamp ramp, float4 *data, int size) { for(int i = 0; i < size; i++) { @@ -586,6 +598,36 @@ struct ObjectKey { } }; +/* Mesh Key */ + +struct MeshKey { + void *parent; + void *mesh; + + MeshKey(void *mesh_) + : parent(NULL), mesh(mesh_) + { + } + + MeshKey(void *parent_, void *mesh_) + : parent(parent_), mesh(mesh_) + { + } + + bool operator<(const MeshKey& k) const + { + if(mesh < k.mesh) { + return true; + } + else if(mesh == k.mesh) { + return parent < k.parent; + return true; + } + + return false; + } +}; + /* Particle System Key */ struct ParticleSystemKey { diff --git a/intern/cycles/bvh/bvh.cpp b/intern/cycles/bvh/bvh.cpp index 350ca16f6e2..9b808203694 100644 --- a/intern/cycles/bvh/bvh.cpp +++ b/intern/cycles/bvh/bvh.cpp @@ -278,28 +278,34 @@ void BVH::pack_triangle(int idx, float4 woop[3]) void BVH::pack_primitives() { - int nsize = TRI_NODE_SIZE; + const int nsize = TRI_NODE_SIZE; + const bool use_triangle_storage = params.use_triangle_storage; size_t tidx_size = pack.prim_index.size(); pack.tri_woop.clear(); - pack.tri_woop.resize(tidx_size * nsize); + if (use_triangle_storage) { + pack.tri_woop.resize(tidx_size * nsize); + } + else { + pack.tri_woop.resize(0); + } pack.prim_visibility.clear(); pack.prim_visibility.resize(tidx_size); for(unsigned int i = 0; i < tidx_size; i++) { if(pack.prim_index[i] != -1) { - float4 woop[3]; - - if(pack.prim_type[i] & PRIMITIVE_TRIANGLE) { - pack_triangle(i, woop); - } - else { - /* Avoid use of uninitialized memory. */ - memset(&woop, 0, sizeof(woop)); + if(use_triangle_storage) { + float4 woop[3]; + if(pack.prim_type[i] & PRIMITIVE_TRIANGLE) { + pack_triangle(i, woop); + } + else { + /* Avoid use of uninitialized memory. */ + memset(&woop, 0, sizeof(woop)); + } + memcpy(&pack.tri_woop[i * nsize], woop, sizeof(float4)*3); } - memcpy(&pack.tri_woop[i * nsize], woop, sizeof(float4)*3); - int tob = pack.prim_object[i]; Object *ob = objects[tob]; pack.prim_visibility[i] = ob->visibility; @@ -308,7 +314,9 @@ void BVH::pack_primitives() pack.prim_visibility[i] |= PATH_RAY_CURVE; } else { - memset(&pack.tri_woop[i * nsize], 0, sizeof(float4)*3); + if(use_triangle_storage) { + memset(&pack.tri_woop[i * nsize], 0, sizeof(float4)*3); + } pack.prim_visibility[i] = 0; } } diff --git a/intern/cycles/bvh/bvh_params.h b/intern/cycles/bvh/bvh_params.h index af8d8eeb3ee..892fd906e77 100644 --- a/intern/cycles/bvh/bvh_params.h +++ b/intern/cycles/bvh/bvh_params.h @@ -49,6 +49,9 @@ public: /* QBVH */ bool use_qbvh; + /* Use pre-aligned tringle storage for faster lookup. */ + bool use_triangle_storage; + /* fixed parameters */ enum { MAX_DEPTH = 64, @@ -73,6 +76,7 @@ public: top_level = false; use_cache = false; use_qbvh = false; + use_triangle_storage = true; } /* SAH costs */ diff --git a/intern/cycles/cmake/external_libs.cmake b/intern/cycles/cmake/external_libs.cmake index d7c59f42a5e..13196e2ba6b 100644 --- a/intern/cycles/cmake/external_libs.cmake +++ b/intern/cycles/cmake/external_libs.cmake @@ -30,7 +30,7 @@ if(NOT CYCLES_STANDALONE_REPOSITORY) set(GLEW_INCLUDE_DIR "${GLEW_INCLUDE_PATH}") endif() -if(WITH_CYCLES_STANDALONE AND WITH_CYCLES_STANDALONE_GUI) +if(WITH_CYCLES_STANDALONE) set(CYCLES_APP_GLEW_LIBRARY ${BLENDER_GLEW_LIBRARIES}) endif() diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index d0279b27046..872d5ab4af5 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -115,6 +115,7 @@ set(SRC_SVM_HEADERS svm/svm_noise.h svm/svm_noisetex.h svm/svm_normal.h + svm/svm_openvdb.h svm/svm_ramp.h svm/svm_sepcomb_hsv.h svm/svm_sepcomb_vector.h @@ -125,6 +126,7 @@ set(SRC_SVM_HEADERS svm/svm_value.h svm/svm_vector_transform.h svm/svm_voronoi.h + svm/svm_voxel.h svm/svm_wave.h ) diff --git a/intern/cycles/kernel/geom/geom_triangle_intersect.h b/intern/cycles/kernel/geom/geom_triangle_intersect.h index 3ef918dc842..ee5d4df2889 100644 --- a/intern/cycles/kernel/geom/geom_triangle_intersect.h +++ b/intern/cycles/kernel/geom/geom_triangle_intersect.h @@ -119,9 +119,19 @@ ccl_device_inline bool triangle_intersect(KernelGlobals *kg, const float Sz = isect_precalc->Sz; /* Calculate vertices relative to ray origin. */ - const float4 tri_a = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+0), - tri_b = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+1), - tri_c = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+2); + float4 tri_a, tri_b, tri_c; + if (kernel_data.bvh.use_tri_storage) { + tri_a = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+0); + tri_b = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+1); + tri_c = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+2); + } + else { + const int prim = kernel_tex_fetch(__prim_index, triAddr); + const float4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); + tri_a = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.x)); + tri_b = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.y)); + tri_c = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.z)); + } const float3 A = make_float3(tri_a.x - P.x, tri_a.y - P.y, tri_a.z - P.z); const float3 B = make_float3(tri_b.x - P.x, tri_b.y - P.y, tri_b.z - P.z); const float3 C = make_float3(tri_c.x - P.x, tri_c.y - P.y, tri_c.z - P.z); @@ -212,9 +222,19 @@ ccl_device_inline void triangle_intersect_subsurface( const float Sz = isect_precalc->Sz; /* Calculate vertices relative to ray origin. */ - const float4 tri_a = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+0), - tri_b = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+1), - tri_c = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+2); + float4 tri_a, tri_b, tri_c; + if (kernel_data.bvh.use_tri_storage) { + tri_a = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+0); + tri_b = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+1); + tri_c = kernel_tex_fetch(__tri_woop, triAddr*TRI_NODE_SIZE+2); + } + else { + const int prim = kernel_tex_fetch(__prim_index, triAddr); + const float4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); + tri_a = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.x)); + tri_b = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.y)); + tri_c = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.z)); + } const float3 A = make_float3(tri_a.x - P.x, tri_a.y - P.y, tri_a.z - P.z); const float3 B = make_float3(tri_b.x - P.x, tri_b.y - P.y, tri_b.z - P.z); const float3 C = make_float3(tri_c.x - P.x, tri_c.y - P.y, tri_c.z - P.z); @@ -327,9 +347,19 @@ ccl_device_inline float3 triangle_refine(KernelGlobals *kg, P = P + D*t; - const float4 tri_a = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+0), - tri_b = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+1), - tri_c = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+2); + float4 tri_a, tri_b, tri_c; + if (kernel_data.bvh.use_tri_storage) { + tri_a = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+0); + tri_b = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+1); + tri_c = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+2); + } + else { + const int prim = kernel_tex_fetch(__prim_index, isect->prim); + const float4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); + tri_a = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.x)); + tri_b = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.y)); + tri_c = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.z)); + } float3 edge1 = make_float3(tri_a.x - tri_c.x, tri_a.y - tri_c.y, tri_a.z - tri_c.z); float3 edge2 = make_float3(tri_b.x - tri_c.x, tri_b.y - tri_c.y, tri_b.z - tri_c.z); float3 tvec = make_float3(P.x - tri_c.x, P.y - tri_c.y, P.z - tri_c.z); @@ -384,9 +414,19 @@ ccl_device_inline float3 triangle_refine_subsurface(KernelGlobals *kg, P = P + D*t; - const float4 tri_a = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+0), - tri_b = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+1), - tri_c = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+2); + float4 tri_a, tri_b, tri_c; + if (kernel_data.bvh.use_tri_storage) { + tri_a = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+0); + tri_b = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+1); + tri_c = kernel_tex_fetch(__tri_woop, isect->prim*TRI_NODE_SIZE+2); + } + else { + const int prim = kernel_tex_fetch(__prim_index, isect->prim); + const float4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); + tri_a = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.x)); + tri_b = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.y)); + tri_c = kernel_tex_fetch(__tri_verts, __float_as_int(tri_vindex.z)); + } float3 edge1 = make_float3(tri_a.x - tri_c.x, tri_a.y - tri_c.y, tri_a.z - tri_c.z); float3 edge2 = make_float3(tri_b.x - tri_c.x, tri_b.y - tri_c.y, tri_b.z - tri_c.z); float3 tvec = make_float3(P.x - tri_c.x, P.y - tri_c.y, P.z - tri_c.z); diff --git a/intern/cycles/kernel/geom/geom_volume.h b/intern/cycles/kernel/geom/geom_volume.h index c72afa2a3a4..24430dd223b 100644 --- a/intern/cycles/kernel/geom/geom_volume.h +++ b/intern/cycles/kernel/geom/geom_volume.h @@ -53,17 +53,21 @@ ccl_device float volume_attribute_float(KernelGlobals *kg, const ShaderData *sd, float4 r = make_float4(0.0f, 0.0f, 0.0f, 0.0f); #else float4 r; + int slot = id >> 1; if(sd->flag & SD_VOLUME_CUBIC) - r = kernel_tex_image_interp_3d_ex(id, P.x, P.y, P.z, INTERPOLATION_CUBIC); + r = kernel_tex_image_interp_3d_ex(slot, P.x, P.y, P.z, INTERPOLATION_CUBIC); else - r = kernel_tex_image_interp_3d(id, P.x, P.y, P.z); + r = kernel_tex_image_interp_3d(slot, P.x, P.y, P.z); #endif if(dx) *dx = 0.0f; if(dy) *dy = 0.0f; /* todo: support float textures to lower memory usage for single floats */ - return average(float4_to_float3(r)); + if(id & 1) + return r.w; + else + return average(float4_to_float3(r)); } ccl_device float3 volume_attribute_float3(KernelGlobals *kg, const ShaderData *sd, AttributeElement elem, int id, float3 *dx, float3 *dy) @@ -73,16 +77,20 @@ ccl_device float3 volume_attribute_float3(KernelGlobals *kg, const ShaderData *s float4 r = make_float4(0.0f, 0.0f, 0.0f, 0.0f); #else float4 r; + int slot = id >> 1; if(sd->flag & SD_VOLUME_CUBIC) - r = kernel_tex_image_interp_3d_ex(id, P.x, P.y, P.z, INTERPOLATION_CUBIC); + r = kernel_tex_image_interp_3d_ex(slot, P.x, P.y, P.z, INTERPOLATION_CUBIC); else - r = kernel_tex_image_interp_3d(id, P.x, P.y, P.z); + r = kernel_tex_image_interp_3d(slot, P.x, P.y, P.z); #endif if(dx) *dx = make_float3(0.0f, 0.0f, 0.0f); if(dy) *dy = make_float3(0.0f, 0.0f, 0.0f); - return float4_to_float3(r); + if(id & 1) + return make_float3(r.w, r.w, r.w); + else + return float4_to_float3(r); } #endif diff --git a/intern/cycles/kernel/kernel_compat_cpu.h b/intern/cycles/kernel/kernel_compat_cpu.h index 0bf1ed36d1e..8a1ad7052f8 100644 --- a/intern/cycles/kernel/kernel_compat_cpu.h +++ b/intern/cycles/kernel/kernel_compat_cpu.h @@ -40,6 +40,8 @@ #include "util_simd.h" #include "util_half.h" #include "util_types.h" +#include "util_openvdb.h" +#include "util_vector.h" #define ccl_addr_space @@ -416,6 +418,8 @@ typedef texture_image<uchar4> texture_image_uchar4; #define kernel_tex_image_interp(tex, x, y) ((tex < MAX_FLOAT_IMAGES) ? kg->texture_float_images[tex].interp(x, y) : kg->texture_byte_images[tex - MAX_FLOAT_IMAGES].interp(x, y)) #define kernel_tex_image_interp_3d(tex, x, y, z) ((tex < MAX_FLOAT_IMAGES) ? kg->texture_float_images[tex].interp_3d(x, y, z) : kg->texture_byte_images[tex - MAX_FLOAT_IMAGES].interp_3d(x, y, z)) #define kernel_tex_image_interp_3d_ex(tex, x, y, z, interpolation) ((tex < MAX_FLOAT_IMAGES) ? kg->texture_float_images[tex].interp_3d_ex(x, y, z, interpolation) : kg->texture_byte_images[tex - MAX_FLOAT_IMAGES].interp_3d_ex(x, y, z, interpolation)) +#define kernel_tex_voxel_float(tex, x, y, z, sampling) (kg->float_volumes[tex]->sample(x, y, z, sampling)) +#define kernel_tex_voxel_float3(tex, x, y, z, sampling) (kg->float3_volumes[tex]->sample(x, y, z, sampling)) #define kernel_data (kg->__data) diff --git a/intern/cycles/kernel/kernel_globals.h b/intern/cycles/kernel/kernel_globals.h index 17fa18909c4..157720d6679 100644 --- a/intern/cycles/kernel/kernel_globals.h +++ b/intern/cycles/kernel/kernel_globals.h @@ -33,11 +33,15 @@ struct OSLShadingSystem; #define MAX_BYTE_IMAGES 1024 #define MAX_FLOAT_IMAGES 1024 +#define MAX_VOLUME 1024 typedef struct KernelGlobals { texture_image_uchar4 texture_byte_images[MAX_BYTE_IMAGES]; texture_image_float4 texture_float_images[MAX_FLOAT_IMAGES]; + float_volume *float_volumes[MAX_VOLUME]; + float3_volume *float3_volumes[MAX_VOLUME]; + #define KERNEL_TEX(type, ttype, name) ttype name; #define KERNEL_IMAGE_TEX(type, ttype, name) #include "kernel_textures.h" diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 2a70bfcb8f0..c071be5419a 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -67,6 +67,9 @@ CCL_NAMESPACE_BEGIN #ifdef WITH_OSL #define __OSL__ #endif +#ifdef WITH_OPENVDB +#define __OPENVDB__ +#endif #define __SUBSURFACE__ #define __CMJ__ #define __VOLUME__ @@ -952,7 +955,8 @@ typedef struct KernelBVH { int have_curves; int have_instancing; int use_qbvh; - int pad1, pad2; + int use_tri_storage; + int pad1; } KernelBVH; typedef enum CurveFlag { @@ -976,7 +980,8 @@ typedef struct KernelCurves { typedef struct KernelTables { int beckmann_offset; - int pad1, pad2, pad3; + int num_volumes; + int density_index, pad2; } KernelTables; typedef struct KernelData { diff --git a/intern/cycles/kernel/kernel_volume.h b/intern/cycles/kernel/kernel_volume.h index e06568457c6..cc958a09a46 100644 --- a/intern/cycles/kernel/kernel_volume.h +++ b/intern/cycles/kernel/kernel_volume.h @@ -21,7 +21,8 @@ CCL_NAMESPACE_BEGIN typedef enum VolumeIntegrateResult { VOLUME_PATH_SCATTERED = 0, VOLUME_PATH_ATTENUATED = 1, - VOLUME_PATH_MISSED = 2 + VOLUME_PATH_MISSED = 2, + VOLUME_PATH_CONTINUE = 3, } VolumeIntegrateResult; /* Volume shader properties @@ -161,6 +162,38 @@ ccl_device void kernel_volume_shadow_homogeneous(KernelGlobals *kg, PathState *s *throughput *= volume_color_transmittance(sigma_t, ray->t); } +ccl_device_inline bool kernel_volume_integrate_shadow_ray( + KernelGlobals *kg, PathState *state, Ray *ray, ShaderData *sd, + float3 *tp, float t, float new_t, float random_jitter_offset, + float3 *sum, float tp_eps, int i) +{ + float dt = new_t - t; + + /* use random position inside this segment to sample shader */ + if(new_t == ray->t) + random_jitter_offset = lcg_step_float(&state->rng_congruential) * dt; + + float3 new_P = ray->P + ray->D * (t + random_jitter_offset); + float3 sigma_t; + + /* compute attenuation over segment */ + if(volume_shader_extinction_sample(kg, sd, state, new_P, &sigma_t)) { + /* Compute expf() only for every Nth step, to save some calculations + * because exp(a)*exp(b) = exp(a+b), also do a quick tp_eps check then. */ + + *sum += (-sigma_t * (new_t - t)); + if((i & 0x07) == 0) { /* ToDo: Other interval? */ + *tp = *tp * make_float3(expf(sum->x), expf(sum->y), expf(sum->z)); + + /* stop if nearly all light is blocked */ + if(tp->x < tp_eps && tp->y < tp_eps && tp->z < tp_eps) + return true; + } + } + + return false; +} + /* heterogeneous volume: integrate stepping through the volume until we * reach the end, get absorbed entirely, or run out of iterations */ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg, PathState *state, Ray *ray, ShaderData *sd, float3 *throughput) @@ -178,42 +211,66 @@ ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg, PathState float3 sum = make_float3(0.0f, 0.0f, 0.0f); - for(int i = 0; i < max_steps; i++) { - /* advance to new position */ - float new_t = min(ray->t, (i+1) * step); - float dt = new_t - t; - - /* use random position inside this segment to sample shader */ - if(new_t == ray->t) - random_jitter_offset = lcg_step_float(&state->rng_congruential) * dt; - - float3 new_P = ray->P + ray->D * (t + random_jitter_offset); - float3 sigma_t; - - /* compute attenuation over segment */ - if(volume_shader_extinction_sample(kg, sd, state, new_P, &sigma_t)) { - /* Compute expf() only for every Nth step, to save some calculations - * because exp(a)*exp(b) = exp(a+b), also do a quick tp_eps check then. */ +#ifdef __OPENVDB__ + int vdb_index = kernel_data.tables.density_index; + bool has_vdb_volume = kernel_data.tables.num_volumes > 0; + float t1 = 0.0f; + + if(has_vdb_volume && vdb_index >= 0 && kg->float_volumes[vdb_index]->has_uniform_voxels()) { + /* TODO(kevin): this call should be moved out of here, all it does is + * checking if we have an intersection with the boundbox of the volumue + * which in most cases corresponds to the boundbox of the object that has + * this volume. Also it initializes the rays for the ray marching. */ + if(!kg->float_volumes[vdb_index]->intersect(ray, NULL)) { + return; + } - sum += (-sigma_t * (new_t - t)); - if((i & 0x07) == 0) { /* ToDo: Other interval? */ - tp = *throughput * make_float3(expf(sum.x), expf(sum.y), expf(sum.z)); + /* t and t1 represent the entry and exit points for each leaf node or tile + * containing active voxels. If we don't have any active node in the current + * ray path (i.e. empty space) the ray march loop is not executed, + * otherwise we loop through all leaves until the end of the volume. */ + while(kg->float_volumes[vdb_index]->march(&t, &t1)) { + int i = 0; + + /* Perform small steps through the current leaf or tile. */ + for(float new_t = step * ceilf(t / step); new_t <= t1; new_t += step) { + bool ok = kernel_volume_integrate_shadow_ray( + kg, state, ray, sd, &tp, t, new_t, random_jitter_offset, + &sum, tp_eps, i); + + if (ok) { + *throughput = tp; + return; + } - /* stop if nearly all light is blocked */ - if(tp.x < tp_eps && tp.y < tp_eps && tp.z < tp_eps) - break; + /* stop if at the end of the volume */ + t = new_t; + i++; } } - - /* stop if at the end of the volume */ - t = new_t; - if(t == ray->t) { - /* Update throughput in case we haven't done it above */ - tp = *throughput * make_float3(expf(sum.x), expf(sum.y), expf(sum.z)); - break; + } + else +#endif + { + for(int i = 0; i < max_steps; i++) { + /* advance to new position */ + float new_t = min(ray->t, (i+1) * step); + + bool ok = kernel_volume_integrate_shadow_ray( + kg, state, ray, sd, &tp, t, new_t, random_jitter_offset, + &sum, tp_eps, i); + + /* stop if at the end of the volume */ + t = new_t; + if(ok || t == ray->t) { + break; + } } } + /* Update throughput in case we haven't done it above */ + tp = *throughput * make_float3(expf(sum.x), expf(sum.y), expf(sum.z)); + *throughput = tp; } @@ -420,6 +477,112 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_homogeneous(KernelGloba return VOLUME_PATH_ATTENUATED; } +ccl_device_inline VolumeIntegrateResult kernel_volume_integrate_ray( + KernelGlobals *kg, PathState *state, Ray *ray, ShaderData *sd, + PathRadiance *L, float3 *throughput, float t, float new_t, + float random_jitter_offset, bool has_scatter, float3 *accum_transmittance, + int channel, const float tp_eps, float *xi) +{ + float dt = new_t - t; + float3 tp = *throughput; + + /* use random position inside this segment to sample shader */ + if(new_t == ray->t) + random_jitter_offset = lcg_step_float(&state->rng_congruential) * dt; + + float3 new_P = ray->P + ray->D * (t + random_jitter_offset); + VolumeShaderCoefficients coeff; + + /* compute segment */ + if(volume_shader_sample(kg, sd, state, new_P, &coeff)) { + int closure_flag = sd->flag; + float3 new_tp; + float3 transmittance; + bool scatter = false; + + /* distance sampling */ +#ifdef __VOLUME_SCATTER__ + if((closure_flag & SD_SCATTER) || (has_scatter && (closure_flag & SD_ABSORPTION))) { + has_scatter = true; + + float3 sigma_t = coeff.sigma_a + coeff.sigma_s; + float3 sigma_s = coeff.sigma_s; + + /* compute transmittance over full step */ + transmittance = volume_color_transmittance(sigma_t, dt); + + /* decide if we will scatter or continue */ + float sample_transmittance = kernel_volume_channel_get(transmittance, channel); + + if(1.0f - *xi >= sample_transmittance) { + /* compute sampling distance */ + float sample_sigma_t = kernel_volume_channel_get(sigma_t, channel); + float new_dt = -logf(1.0f - *xi)/sample_sigma_t; + new_t = t + new_dt; + + /* transmittance and pdf */ + float3 new_transmittance = volume_color_transmittance(sigma_t, new_dt); + float3 pdf = sigma_t * new_transmittance; + + /* throughput */ + new_tp = tp * sigma_s * new_transmittance / average(pdf); + scatter = true; + } + else { + /* throughput */ + float pdf = average(transmittance); + new_tp = tp * transmittance / pdf; + + /* remap xi so we can reuse it and keep thing stratified */ + *xi = 1.0f - (1.0f - *xi)/sample_transmittance; + } + } + else +#endif + if(closure_flag & SD_ABSORPTION) { + /* absorption only, no sampling needed */ + float3 sigma_a = coeff.sigma_a; + + transmittance = volume_color_transmittance(sigma_a, dt); + new_tp = tp * transmittance; + } + + /* integrate emission attenuated by absorption */ + if(L && (closure_flag & SD_EMISSION)) { + float3 emission = kernel_volume_emission_integrate(&coeff, closure_flag, transmittance, dt); + path_radiance_accum_emission(L, tp, emission, state->bounce); + } + + /* modify throughput */ + if(closure_flag & (SD_ABSORPTION|SD_SCATTER)) { + tp = new_tp; + + /* stop if nearly all light blocked */ + if(tp.x < tp_eps && tp.y < tp_eps && tp.z < tp_eps) { + tp = make_float3(0.0f, 0.0f, 0.0f); + *throughput = tp; + return VOLUME_PATH_ATTENUATED; + } + } + + /* prepare to scatter to new direction */ + if(scatter) { + /* adjust throughput and move to new location */ + sd->P = ray->P + new_t*ray->D; + *throughput = tp; + + return VOLUME_PATH_SCATTERED; + } + else { + /* accumulate transmittance */ + *accum_transmittance *= transmittance; + } + } + + *throughput = tp; + return VOLUME_PATH_CONTINUE; +} + /* heterogeneous volume distance sampling: integrate stepping through the * volume until we reach the end, get absorbed entirely, or run out of * iterations. this does probalistically scatter or get transmitted through @@ -427,7 +590,7 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_homogeneous(KernelGloba ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous_distance(KernelGlobals *kg, PathState *state, Ray *ray, ShaderData *sd, PathRadiance *L, float3 *throughput, RNG *rng) { - float3 tp = *throughput; + VolumeIntegrateResult result = VOLUME_PATH_MISSED; const float tp_eps = 1e-6f; /* todo: this is likely not the right value */ /* prepare for stepping */ @@ -436,7 +599,7 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous_distance( float random_jitter_offset = lcg_step_float(&state->rng_congruential) * step_size; /* compute coefficients at the start */ - float t = 0.0f; + float t = 0.0f, t1 = 0.0f; float3 accum_transmittance = make_float3(1.0f, 1.0f, 1.0f); /* pick random color channel, we use the Veach one-sample @@ -446,113 +609,65 @@ ccl_device VolumeIntegrateResult kernel_volume_integrate_heterogeneous_distance( int channel = (int)(rphase*3.0f); sd->randb_closure = rphase*3.0f - channel; bool has_scatter = false; + bool path_missed = true; + +#ifdef __OPENVDB__ + int vdb_index = kernel_data.tables.density_index; + bool has_vdb_volume = kernel_data.tables.num_volumes > 0; + + if(has_vdb_volume && vdb_index >= 0 && kg->float_volumes[vdb_index]->has_uniform_voxels()) { + /* TODO(kevin): this call should be moved out of here, all it does is + * checking if we have an intersection with the boundbox of the volumue + * which in most cases corresponds to the boundbox of the object that has + * this volume. Also it initializes the rays for the ray marching. */ + if(!kg->float_volumes[vdb_index]->intersect(ray, NULL)) { + return VOLUME_PATH_MISSED; + } - for(int i = 0; i < max_steps; i++) { - /* advance to new position */ - float new_t = min(ray->t, (i+1) * step_size); - float dt = new_t - t; - - /* use random position inside this segment to sample shader */ - if(new_t == ray->t) - random_jitter_offset = lcg_step_float(&state->rng_congruential) * dt; - - float3 new_P = ray->P + ray->D * (t + random_jitter_offset); - VolumeShaderCoefficients coeff; - - /* compute segment */ - if(volume_shader_sample(kg, sd, state, new_P, &coeff)) { - int closure_flag = sd->flag; - float3 new_tp; - float3 transmittance; - bool scatter = false; - - /* distance sampling */ -#ifdef __VOLUME_SCATTER__ - if((closure_flag & SD_SCATTER) || (has_scatter && (closure_flag & SD_ABSORPTION))) { - has_scatter = true; - - float3 sigma_t = coeff.sigma_a + coeff.sigma_s; - float3 sigma_s = coeff.sigma_s; - - /* compute transmittance over full step */ - transmittance = volume_color_transmittance(sigma_t, dt); - - /* decide if we will scatter or continue */ - float sample_transmittance = kernel_volume_channel_get(transmittance, channel); - - if(1.0f - xi >= sample_transmittance) { - /* compute sampling distance */ - float sample_sigma_t = kernel_volume_channel_get(sigma_t, channel); - float new_dt = -logf(1.0f - xi)/sample_sigma_t; - new_t = t + new_dt; + /* t and t1 represent the entry and exit points for each leaf node or tile + * containing active voxels. If we don't have any active node in the current + * ray path (i.e. empty space) the ray march loop is not executed, + * otherwise we loop through all leaves until the end of the volume. */ + while(kg->float_volumes[vdb_index]->march(&t, &t1)) { + path_missed = false; - /* transmittance and pdf */ - float3 new_transmittance = volume_color_transmittance(sigma_t, new_dt); - float3 pdf = sigma_t * new_transmittance; + /* Perform small steps through the current leaf or tile. */ + for(float new_t = step_size * ceilf(t / step_size); new_t <= t1; new_t += step_size) { + result = kernel_volume_integrate_ray(kg, state, ray, sd, L, throughput, t, new_t, + random_jitter_offset, has_scatter, + &accum_transmittance, channel, tp_eps, &xi); - /* throughput */ - new_tp = tp * sigma_s * new_transmittance / average(pdf); - scatter = true; - } - else { - /* throughput */ - float pdf = average(transmittance); - new_tp = tp * transmittance / pdf; + if(result != VOLUME_PATH_CONTINUE) + return result; - /* remap xi so we can reuse it and keep thing stratified */ - xi = 1.0f - (1.0f - xi)/sample_transmittance; - } + t = new_t; } - else + } + } + else #endif - if(closure_flag & SD_ABSORPTION) { - /* absorption only, no sampling needed */ - float3 sigma_a = coeff.sigma_a; - - transmittance = volume_color_transmittance(sigma_a, dt); - new_tp = tp * transmittance; - } - - /* integrate emission attenuated by absorption */ - if(L && (closure_flag & SD_EMISSION)) { - float3 emission = kernel_volume_emission_integrate(&coeff, closure_flag, transmittance, dt); - path_radiance_accum_emission(L, tp, emission, state->bounce); - } + { + path_missed = false; - /* modify throughput */ - if(closure_flag & (SD_ABSORPTION|SD_SCATTER)) { - tp = new_tp; + for(int i = 0; i < max_steps; i++) { + /* advance to new position */ + float new_t = min(ray->t, (i+1) * step_size); - /* stop if nearly all light blocked */ - if(tp.x < tp_eps && tp.y < tp_eps && tp.z < tp_eps) { - tp = make_float3(0.0f, 0.0f, 0.0f); - break; - } - } + result = kernel_volume_integrate_ray(kg, state, ray, sd, L, throughput, t, new_t, + random_jitter_offset, has_scatter, + &accum_transmittance, channel, tp_eps, &xi); - /* prepare to scatter to new direction */ - if(scatter) { - /* adjust throughput and move to new location */ - sd->P = ray->P + new_t*ray->D; - *throughput = tp; + if(result != VOLUME_PATH_CONTINUE) + return result; - return VOLUME_PATH_SCATTERED; - } - else { - /* accumulate transmittance */ - accum_transmittance *= transmittance; - } + /* stop if at the end of the volume */ + t = new_t; + if(t == ray->t) + break; } - - /* stop if at the end of the volume */ - t = new_t; - if(t == ray->t) - break; } - *throughput = tp; - - return VOLUME_PATH_ATTENUATED; + return (path_missed) ? VOLUME_PATH_MISSED : VOLUME_PATH_ATTENUATED; } /* get the volume attenuation and emission over line segment defined by diff --git a/intern/cycles/kernel/kernels/cpu/kernel.cpp b/intern/cycles/kernel/kernels/cpu/kernel.cpp index 37a73ab2f04..500aef3fe19 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel.cpp +++ b/intern/cycles/kernel/kernels/cpu/kernel.cpp @@ -34,6 +34,10 @@ void kernel_const_copy(KernelGlobals *kg, const char *name, void *host, size_t s { if(strcmp(name, "__data") == 0) memcpy(&kg->__data, host, size); + else if(strcmp(name, "__float_volume") == 0) + kg->float_volumes[size] = (float_volume *)host; + else if(strcmp(name, "__float3_volume") == 0) + kg->float3_volumes[size] = (float3_volume *)host; else assert(0); } diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index 15ac6519780..21604e78f7d 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -168,6 +168,7 @@ CCL_NAMESPACE_END #include "svm_wave.h" #include "svm_math.h" #include "svm_mix.h" +#include "svm_openvdb.h" #include "svm_ramp.h" #include "svm_sepcomb_hsv.h" #include "svm_sepcomb_vector.h" @@ -179,6 +180,7 @@ CCL_NAMESPACE_END #include "svm_checker.h" #include "svm_brick.h" #include "svm_vector_transform.h" +#include "svm_voxel.h" CCL_NAMESPACE_BEGIN @@ -390,6 +392,9 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, Shade case NODE_TEX_BRICK: svm_node_tex_brick(kg, sd, stack, node, &offset); break; + case NODE_TEX_VOXEL: + svm_node_tex_voxel(kg, sd, stack, node, &offset); + break; # endif /* __TEXTURES__ */ # ifdef __EXTRA_NODES__ case NODE_NORMAL: @@ -447,6 +452,11 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, Shade break; # endif /* __EXTRA_NODES__ */ #endif /* NODES_GROUP(NODE_GROUP_LEVEL_3) */ +#ifdef __OPENVDB__ + case NODE_OPENVDB: + svm_node_openvdb(kg, sd, stack, node, &offset); + break; +#endif case NODE_END: return; default: diff --git a/intern/cycles/kernel/svm/svm_openvdb.h b/intern/cycles/kernel/svm/svm_openvdb.h new file mode 100644 index 00000000000..8820dae42c4 --- /dev/null +++ b/intern/cycles/kernel/svm/svm_openvdb.h @@ -0,0 +1,55 @@ +/* + * Copyright 2015 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +CCL_NAMESPACE_BEGIN + +#ifdef __OPENVDB__ + +ccl_device void svm_node_openvdb(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +{ + uint slot = node.y; + uint type, out_offset, sampling, co_offset; + decode_node_uchar4(node.z, &type, &co_offset, &out_offset, &sampling); + + float3 co = stack_load_float3(stack, co_offset); + + Transform tfm; + tfm.x = read_node_float(kg, offset); + tfm.y = read_node_float(kg, offset); + tfm.z = read_node_float(kg, offset); + tfm.w = read_node_float(kg, offset); + co = transform_point(&tfm, co); + + if(type == NODE_VDB_FLOAT) { + float out = kernel_tex_voxel_float(slot, co.x, co.y, co.z, sampling); + + if(stack_valid(out_offset)) { + stack_store_float(stack, out_offset, out); + } + } + else if(type == NODE_VDB_FLOAT3) { + float3 out = kernel_tex_voxel_float3(slot, co.x, co.y, co.z, sampling); + + if(stack_valid(out_offset)) { + stack_store_float3(stack, out_offset, out); + } + } +} + +#endif + +CCL_NAMESPACE_END + diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index 009e91192eb..28e55a1e0d5 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -125,7 +125,9 @@ typedef enum NodeType { NODE_TANGENT, NODE_NORMAL_MAP, NODE_HAIR_INFO, - NODE_UVMAP + NODE_UVMAP, + NODE_TEX_VOXEL, + NODE_OPENVDB, } NodeType; typedef enum NodeAttributeType { @@ -349,6 +351,16 @@ typedef enum NodeBumpOffset { NODE_BUMP_OFFSET_DY, } NodeBumpOffset; +typedef enum NodeTexVoxelSpace { + NODE_TEX_VOXEL_SPACE_OBJECT = 0, + NODE_TEX_VOXEL_SPACE_WORLD = 1, +} NodeTexVoxelSpace; + +typedef enum NodeOpenVDBType { + NODE_VDB_FLOAT = 0, + NODE_VDB_FLOAT3 = 1, +} NodeOpenVDBType; + typedef enum ShaderType { SHADER_TYPE_SURFACE, SHADER_TYPE_VOLUME, diff --git a/intern/cycles/kernel/svm/svm_voxel.h b/intern/cycles/kernel/svm/svm_voxel.h new file mode 100644 index 00000000000..4838926503e --- /dev/null +++ b/intern/cycles/kernel/svm/svm_voxel.h @@ -0,0 +1,61 @@ +/* + * Copyright 2011-2015 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +CCL_NAMESPACE_BEGIN + +ccl_device void svm_node_tex_voxel(KernelGlobals *kg, + ShaderData *sd, + float *stack, + uint4 node, + int *offset) +{ + int id = node.y; + uint co_offset, density_out_offset, color_out_offset, space; + decode_node_uchar4(node.z, &co_offset, &density_out_offset, &color_out_offset, &space); + float3 co = stack_load_float3(stack, co_offset); + if(space == NODE_TEX_VOXEL_SPACE_OBJECT) { + co = volume_normalized_position(kg, sd, co); + } + else { + kernel_assert(space == NODE_TEX_VOXEL_SPACE_WORLD); + Transform tfm; + tfm.x = read_node_float(kg, offset); + tfm.y = read_node_float(kg, offset); + tfm.z = read_node_float(kg, offset); + tfm.w = read_node_float(kg, offset); + co = transform_point(&tfm, co); + } + if(co.x < 0.0f || co.y < 0.0f || co.z < 0.0f || + co.x > 1.0f || co.y > 1.0f || co.z > 1.0f) + { + if (stack_valid(density_out_offset)) + stack_store_float(stack, density_out_offset, 0.0f); + if (stack_valid(color_out_offset)) + stack_store_float3(stack, color_out_offset, make_float3(0.0f, 0.0f, 0.0f)); + return; + } +#ifdef __KERNEL_GPU__ + float4 r = make_float4(0.0f, 0.0f, 0.0f, 0.0f); +#else + float4 r = kernel_tex_image_interp_3d(id, co.x, co.y, co.z); +#endif + if (stack_valid(density_out_offset)) + stack_store_float(stack, density_out_offset, r.w); + if (stack_valid(color_out_offset)) + stack_store_float3(stack, color_out_offset, make_float3(r.x, r.y, r.z)); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/render/CMakeLists.txt index 4e8a1794813..e85b7e67f6a 100644 --- a/intern/cycles/render/CMakeLists.txt +++ b/intern/cycles/render/CMakeLists.txt @@ -14,6 +14,12 @@ set(INC_SYS ${GLEW_INCLUDE_DIR} ) +if(WITH_OPENVDB) + list(APPEND INC_SYS + ${OPENVDB_INCLUDE_DIRS} + ) +endif() + set(SRC attribute.cpp background.cpp @@ -29,6 +35,7 @@ set(SRC mesh_displace.cpp nodes.cpp object.cpp + openvdb.cpp osl.cpp particles.cpp curves.cpp @@ -56,6 +63,7 @@ set(SRC_HEADERS mesh.h nodes.h object.h + openvdb.h osl.h particles.h curves.h diff --git a/intern/cycles/render/attribute.h b/intern/cycles/render/attribute.h index bbc6cf7f65f..b49f0ffe387 100644 --- a/intern/cycles/render/attribute.h +++ b/intern/cycles/render/attribute.h @@ -39,6 +39,7 @@ struct Transform; struct VoxelAttribute { ImageManager *manager; int slot; + int from_alpha; }; /* Attribute diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index 45685fe5927..fdd1e3ea32c 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -515,6 +515,7 @@ void Mesh::compute_bvh(SceneParams *params, Progress *progress, int n, int total BVHParams bparams; bparams.use_cache = params->use_bvh_cache; bparams.use_spatial_split = params->use_bvh_spatial_split; + bparams.use_triangle_storage = params->use_bvh_triangle_storage; bparams.use_qbvh = params->use_qbvh; delete bvh; @@ -554,9 +555,10 @@ bool Mesh::has_motion_blur() const /* Mesh Manager */ -MeshManager::MeshManager() +MeshManager::MeshManager(const bool free_data_after_update_) { bvh = NULL; + free_data_after_update = free_data_after_update_; need_update = true; need_flags_update = true; } @@ -816,7 +818,10 @@ static void update_attribute_element_offset(Mesh *mesh, if(mattr->element == ATTR_ELEMENT_VOXEL) { /* store slot in offset value */ VoxelAttribute *voxel_data = mattr->data_voxel(); - offset = voxel_data->slot; + offset = voxel_data->slot << 1; + if(voxel_data->from_alpha) { + ++offset; + } } else if(mattr->element == ATTR_ELEMENT_CORNER_BYTE) { uchar4 *data = mattr->data_uchar4(); @@ -1085,6 +1090,7 @@ void MeshManager::device_update_bvh(Device *device, DeviceScene *dscene, Scene * bparams.use_qbvh = scene->params.use_qbvh; bparams.use_spatial_split = scene->params.use_bvh_spatial_split; bparams.use_cache = scene->params.use_bvh_cache; + bparams.use_triangle_storage = scene->params.use_bvh_triangle_storage; delete bvh; bvh = BVH::create(bparams, scene->objects); @@ -1132,6 +1138,7 @@ void MeshManager::device_update_bvh(Device *device, DeviceScene *dscene, Scene * dscene->data.bvh.root = pack.root_index; dscene->data.bvh.use_qbvh = scene->params.use_qbvh; + dscene->data.bvh.use_tri_storage = scene->params.use_bvh_triangle_storage? 1: 0; } void MeshManager::device_update_flags(Device * /*device*/, @@ -1320,6 +1327,15 @@ void MeshManager::device_update(Device *device, DeviceScene *dscene, Scene *scen device_update_bvh(device, dscene, scene, progress); + if(free_data_after_update) { + foreach(Object *object, scene->objects) { + if(object->mesh->bvh != NULL) { + delete object->mesh->bvh; + object->mesh->bvh = NULL; + } + } + } + need_update = false; if(need_displacement_images) { diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h index 76c186a3feb..887ef65004e 100644 --- a/intern/cycles/render/mesh.h +++ b/intern/cycles/render/mesh.h @@ -152,7 +152,10 @@ public: bool need_update; bool need_flags_update; - MeshManager(); + /* Free memory used by BVH after device update. */ + bool free_data_after_update; + + MeshManager(const bool free_data_after_update); ~MeshManager(); bool displace(Device *device, DeviceScene *dscene, Scene *scene, Mesh *mesh, Progress& progress); diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index 69ae2078216..7a9ed1006ce 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -16,6 +16,7 @@ #include "image.h" #include "nodes.h" +#include "openvdb.h" #include "svm.h" #include "svm_math_util.h" #include "osl.h" @@ -1297,6 +1298,118 @@ void BrickTextureNode::compile(OSLCompiler& compiler) compiler.add(this, "node_brick_texture"); } +/* Point Density Texture */ + +static ShaderEnum point_density_space_init() +{ + ShaderEnum enm; + + enm.insert("Object", NODE_TEX_VOXEL_SPACE_OBJECT); + enm.insert("World", NODE_TEX_VOXEL_SPACE_WORLD); + + return enm; +} + +ShaderEnum PointDensityTextureNode::space_enum = point_density_space_init(); + +PointDensityTextureNode::PointDensityTextureNode() +: ShaderNode("point_density") +{ + image_manager = NULL; + slot = -1; + filename = ""; + space = ustring("Object"); + builtin_data = NULL; + interpolation = INTERPOLATION_LINEAR; + + tfm = transform_identity(); + + add_input("Vector", SHADER_SOCKET_POINT, ShaderInput::POSITION); + add_output("Density", SHADER_SOCKET_FLOAT); + add_output("Color", SHADER_SOCKET_COLOR); +} + +PointDensityTextureNode::~PointDensityTextureNode() +{ + if(image_manager) + image_manager->remove_image(filename, builtin_data, interpolation); +} + +ShaderNode *PointDensityTextureNode::clone() const +{ + PointDensityTextureNode *node = new PointDensityTextureNode(*this); + node->image_manager = NULL; + node->slot = -1; + return node; +} + +void PointDensityTextureNode::attributes(Shader *shader, + AttributeRequestSet *attributes) +{ + if(shader->has_volume) + attributes->add(ATTR_STD_GENERATED_TRANSFORM); + + ShaderNode::attributes(shader, attributes); +} + +void PointDensityTextureNode::compile(SVMCompiler& compiler) +{ + ShaderInput *vector_in = input("Vector"); + ShaderOutput *density_out = output("Density"); + ShaderOutput *color_out = output("Color"); + + bool use_density = !density_out->links.empty(); + bool use_color = !color_out->links.empty(); + + image_manager = compiler.image_manager; + + if (use_density || use_color) { + if (use_density) + compiler.stack_assign(density_out); + if (use_color) + compiler.stack_assign(color_out); + + if(slot == -1) { + bool is_float, is_linear; + slot = image_manager->add_image(filename, builtin_data, + false, 0, + is_float, is_linear, + interpolation, + true); + } + + if(slot != -1) { + compiler.stack_assign(vector_in); + compiler.add_node(NODE_TEX_VOXEL, + slot, + compiler.encode_uchar4(vector_in->stack_offset, + density_out->stack_offset, + color_out->stack_offset, + space_enum[space])); + if(space == "World") { + compiler.add_node(tfm.x); + compiler.add_node(tfm.y); + compiler.add_node(tfm.z); + compiler.add_node(tfm.w); + } + } + else { + compiler.add_node(NODE_VALUE_F, + __float_as_int(0.0f), + density_out->stack_offset); + compiler.add_node(NODE_VALUE_V, color_out->stack_offset); + compiler.add_node(NODE_VALUE_V, make_float3(TEX_IMAGE_MISSING_R, + TEX_IMAGE_MISSING_G, + TEX_IMAGE_MISSING_B)); + } + } +} + +void PointDensityTextureNode::compile(OSLCompiler& /*compiler*/) +{ + /* TODO(sergey): To be supported. */ +} + /* Normal */ NormalNode::NormalNode() @@ -4367,4 +4480,71 @@ void TangentNode::compile(OSLCompiler& compiler) compiler.add(this, "node_tangent"); } +OpenVDBNode::OpenVDBNode() +: ShaderNode("openvdb") +{ + filename = ""; + volume_manager = NULL; + sampling = OPENVDB_SAMPLE_POINT; + tfm = transform_identity(); + + add_input("Vector", SHADER_SOCKET_POINT, ShaderInput::POSITION); +} + +void OpenVDBNode::attributes(Shader *shader, AttributeRequestSet *attributes) +{ + if(shader->has_volume) + attributes->add(ATTR_STD_GENERATED_TRANSFORM); + + ShaderNode::attributes(shader, attributes); +} + +void OpenVDBNode::compile(SVMCompiler& compiler) +{ + ShaderInput *vector_in = input("Vector"); + volume_manager = compiler.volume_manager; + + compiler.stack_assign(vector_in); + + for(size_t i = 0; i < outputs.size(); ++i) { + ShaderOutput *out = outputs[i]; + + if(out->links.empty()) { + continue; + } + + int type = NODE_VDB_FLOAT; + + if(out->type == SHADER_SOCKET_VECTOR || out->type == SHADER_SOCKET_COLOR) { + type = NODE_VDB_FLOAT3; + } + + grid_slot = volume_manager->add_volume(filename.string(), + output_names[i].string(), + sampling, type); + + if(grid_slot == -1) { + continue; + } + + compiler.stack_assign(out); + + compiler.add_node(NODE_OPENVDB, + grid_slot, + compiler.encode_uchar4(type, + vector_in->stack_offset, + out->stack_offset, + sampling)); + + compiler.add_node(tfm.x); + compiler.add_node(tfm.y); + compiler.add_node(tfm.z); + compiler.add_node(tfm.w); + } +} + +void OpenVDBNode::compile(OSLCompiler& /*compiler*/) +{ +} + CCL_NAMESPACE_END diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index 7ec20f0879b..c839152ec2f 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -25,6 +25,7 @@ CCL_NAMESPACE_BEGIN class ImageManager; class Shader; +class VolumeManager; /* Texture Mapping */ @@ -213,6 +214,29 @@ public: virtual int get_group() { return NODE_GROUP_LEVEL_2; } }; +class PointDensityTextureNode : public ShaderNode { +public: + SHADER_NODE_NO_CLONE_CLASS(PointDensityTextureNode) + + ~PointDensityTextureNode(); + ShaderNode *clone() const; + void attributes(Shader *shader, AttributeRequestSet *attributes); + + bool has_spatial_varying() { return true; } + bool has_object_dependency() { return true; } + + ImageManager *image_manager; + int slot; + string filename; + ustring space; + void *builtin_data; + InterpolationType interpolation; + + Transform tfm; + + static ShaderEnum space_enum; +}; + class MappingNode : public ShaderNode { public: SHADER_NODE_CLASS(MappingNode) @@ -734,6 +758,22 @@ public: ustring attribute; }; +class OpenVDBNode : public ShaderNode { +public: + SHADER_NODE_CLASS(OpenVDBNode) + void attributes(Shader *shader, AttributeRequestSet *attributes); + bool has_spatial_varying() { return true; } + + ustring filename; + VolumeManager *volume_manager; + + int grid_slot; + int sampling; + vector<ustring> output_names; + + Transform tfm; +}; + CCL_NAMESPACE_END #endif /* __NODES_H__ */ diff --git a/intern/cycles/render/openvdb.cpp b/intern/cycles/render/openvdb.cpp new file mode 100644 index 00000000000..44468f4876e --- /dev/null +++ b/intern/cycles/render/openvdb.cpp @@ -0,0 +1,276 @@ +/* + * Copyright 2015 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "openvdb.h" +#include "scene.h" +#include "util_logging.h" +#include "util_progress.h" + +CCL_NAMESPACE_BEGIN + +#define MAX_VOLUME 1024 + +VolumeManager::VolumeManager() +{ +#ifdef WITH_OPENVDB + openvdb::initialize(); + + scalar_grids.reserve(64); + vector_grids.reserve(64); + current_grids.reserve(64); + float_volumes.reserve(64); + float3_volumes.reserve(64); +#endif + + need_update = true; +} + +VolumeManager::~VolumeManager() +{ +#ifdef WITH_OPENVDB + scalar_grids.clear(); + vector_grids.clear(); + current_grids.clear(); + float_volumes.clear(); + float3_volumes.clear(); +#endif +} + +static inline void catch_exceptions() +{ +#ifdef WITH_OPENVDB + try { + throw; + } + catch (const openvdb::IoError& e) { + std::cerr << e.what() << "\n"; + } +#endif +} + +int VolumeManager::add_volume(const string& filename, const string& name, int sampling, int grid_type) +{ + size_t slot = -1; + + if((slot = find_existing_slot(filename, name, sampling, grid_type)) != -1) { + return slot; + } + + try { + if(is_openvdb_file(filename)) { + slot = add_openvdb_volume(filename, name, sampling, grid_type); + } + + add_grid_description(filename, name, sampling, slot); + + need_update = true; + } + catch (...) { + catch_exceptions(); + need_update = false; + slot = -1; + } + + return slot; +} + +int VolumeManager::find_existing_slot(const string& filename, const string& name, int sampling, int grid_type) +{ + for(size_t i = 0; i < current_grids.size(); ++i) { + GridDescription grid = current_grids[i]; + + if(grid.filename == filename && grid.name == name) { + if(grid.sampling == sampling) { + return grid.slot; + } + else { + /* sampling was changed, remove the sampler */ + if(grid_type == NODE_VDB_FLOAT) { + delete float_volumes[grid.slot]; + float_volumes[grid.slot] = NULL; + } + else { + delete float3_volumes[grid.slot]; + float3_volumes[grid.slot] = NULL; + } + + /* remove the grid description too */ + std::swap(current_grids[i], current_grids.back()); + current_grids.pop_back(); + break; + } + } + } + + return -1; +} + +int VolumeManager::find_density_slot() +{ + /* first try finding a matching grid name */ + for (size_t i = 0; i < current_grids.size(); ++i) { + GridDescription grid = current_grids[i]; + + if (string_iequals(grid.name, "density") || string_iequals(grid.name, "density high")) + return grid.slot; + } + + /* try using the first scalar float grid instead */ + if (!float_volumes.empty()) { + return 0; + } + + return -1; +} + +bool VolumeManager::is_openvdb_file(const string& filename) const +{ + return string_endswith(filename, ".vdb"); +} + +template <typename Container> +size_t find_empty_slot(Container container) +{ + size_t slot = 0; + + for(; slot < container.size(); ++slot) { + if(!container[slot]) { + break; + } + } + + if(slot == container.size()) { + if(slot == MAX_VOLUME) { + printf("VolumeManager::add_volume: volume sampler limit reached %d!\n", + MAX_VOLUME); + return -1; + } + + container.resize(slot + 1); + } + + return slot; +} + +size_t VolumeManager::add_openvdb_volume(const std::string& filename, const std::string& name, int /*sampling*/, int grid_type) +{ + size_t slot = -1; + +#ifdef WITH_OPENVDB + using namespace openvdb; + + io::File file(filename); + file.open(); + + if (!file.hasGrid(name)) return -1; + + if(grid_type == NODE_VDB_FLOAT) { + slot = find_empty_slot(float_volumes); + + if(slot == -1) return -1; + + if (!file.hasGrid(name)) return -1; + FloatGrid::Ptr grid = gridPtrCast<FloatGrid>(file.readGrid(name)); + vdb_float_volume *sampler = new vdb_float_volume(grid); + + float_volumes.insert(float_volumes.begin() + slot, sampler); + scalar_grids.push_back(grid); + } + else if(grid_type == NODE_VDB_FLOAT3) { + slot = find_empty_slot(float3_volumes); + + if(slot == -1) return -1; + + if (!file.hasGrid(name)) return -1; + Vec3SGrid::Ptr grid = gridPtrCast<Vec3SGrid>(file.readGrid(name)); + vdb_float3_volume *sampler = new vdb_float3_volume(grid); + + float3_volumes.insert(float3_volumes.begin() + slot, sampler); + vector_grids.push_back(grid); + } +#else + (void)filename; + (void)name; + (void)grid_type; +#endif + + return slot; +} + +void VolumeManager::add_grid_description(const string& filename, const string& name, int sampling, int slot) +{ + GridDescription descr; + descr.filename = filename; + descr.name = name; + descr.sampling = sampling; + descr.slot = slot; + + current_grids.push_back(descr); +} + +void VolumeManager::device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress) +{ + (void)scene; + + if(!need_update) { + return; + } + + device_free(device, dscene); + progress.set_status("Updating OpenVDB volumes", "Sending samplers to device."); + + for(size_t i = 0; i < float_volumes.size(); ++i) { + if(!float_volumes[i]) { + continue; + } + device->const_copy_to("__float_volume", float_volumes[i], i); + } + + for(size_t i = 0; i < float3_volumes.size(); ++i) { + if(!float3_volumes[i]) { + continue; + } + device->const_copy_to("__float3_volume", float3_volumes[i], i); + } + + if(progress.get_cancel()) { + return; + } + + dscene->data.tables.num_volumes = float_volumes.size() + float3_volumes.size(); + dscene->data.tables.density_index = find_density_slot(); + + VLOG(1) << "Volume samplers allocate: __float_volume, " << float_volumes.size() * sizeof(float_volume) << " bytes"; + VLOG(1) << "Volume samplers allocate: __float3_volume, " << float3_volumes.size() * sizeof(float3_volume) << " bytes"; + +#ifdef WITH_OPENVDB + for(size_t i = 0; i < scalar_grids.size(); ++i) { + VLOG(1) << scalar_grids[i]->getName() << " memory usage: " << scalar_grids[i]->memUsage() / 1024.0f << " kilobytes.\n"; + } + + for(size_t i = 0; i < vector_grids.size(); ++i) { + VLOG(1) << vector_grids[i]->getName() << " memory usage: " << vector_grids[i]->memUsage() / 1024.0f << " kilobytes.\n"; + } +#endif + + need_update = false; +} + +void VolumeManager::device_free(Device */*device*/, DeviceScene */*dscene*/) +{ +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/openvdb.h b/intern/cycles/render/openvdb.h new file mode 100644 index 00000000000..2aa2162c0ff --- /dev/null +++ b/intern/cycles/render/openvdb.h @@ -0,0 +1,72 @@ +/* + * Copyright 2015 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VOLUMEMANAGER_H__ +#define __VOLUMEMANAGER_H__ + +#include "util_openvdb.h" +#include "util_string.h" +#include "util_types.h" + +CCL_NAMESPACE_BEGIN + +class Device; +class DeviceScene; +class Progress; +class Scene; + +class VolumeManager { + struct GridDescription { + string filename; + string name; + int sampling; + int slot; + }; + + vector<GridDescription> current_grids; + +#ifdef WITH_OPENVDB + vector<openvdb::FloatGrid::Ptr> scalar_grids; + vector<openvdb::Vec3SGrid::Ptr> vector_grids; +#endif + + void delete_volume(int grid_type, int sampling, size_t slot); + + void add_grid_description(const string& filename, const string& name, int sampling, int slot); + int find_existing_slot(const string& filename, const string& name, int sampling, int grid_type); + + bool is_openvdb_file(const string& filename) const; + size_t add_openvdb_volume(const string& filename, const string& name, int sampling, int grid_type); + +public: + VolumeManager(); + ~VolumeManager(); + + int add_volume(const string& filename, const string& name, int sampling, int grid_type); + int find_density_slot(); + + void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress& progress); + void device_free(Device *device, DeviceScene *dscene); + + bool need_update; + + vector<float_volume*> float_volumes; + vector<float3_volume*> float3_volumes; +}; + +CCL_NAMESPACE_END + +#endif /* __VOLUMEMANAGER_H__ */ diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/render/scene.cpp index 19d715d834b..feffff30e89 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/render/scene.cpp @@ -26,6 +26,7 @@ #include "light.h" #include "mesh.h" #include "object.h" +#include "openvdb.h" #include "osl.h" #include "particles.h" #include "scene.h" @@ -43,7 +44,9 @@ CCL_NAMESPACE_BEGIN -Scene::Scene(const SceneParams& params_, const DeviceInfo& device_info_) +Scene::Scene(const SceneParams& params_, + const DeviceInfo& device_info_, + const bool free_data_after_update) : params(params_) { device = NULL; @@ -54,13 +57,14 @@ Scene::Scene(const SceneParams& params_, const DeviceInfo& device_info_) film = new Film(); background = new Background(); light_manager = new LightManager(); - mesh_manager = new MeshManager(); + mesh_manager = new MeshManager(free_data_after_update); object_manager = new ObjectManager(); integrator = new Integrator(); image_manager = new ImageManager(); particle_system_manager = new ParticleSystemManager(); curve_system_manager = new CurveSystemManager(); bake_manager = new BakeManager(); + volume_manager = new VolumeManager(); /* OSL only works on the CPU */ if(device_info_.type == DEVICE_CPU) @@ -118,6 +122,7 @@ void Scene::free_memory(bool final) image_manager->device_free_builtin(device, &dscene); lookup_tables->device_free(device, &dscene); + volume_manager->device_free(device, &dscene); } if(final) { @@ -134,6 +139,7 @@ void Scene::free_memory(bool final) delete curve_system_manager; delete image_manager; delete bake_manager; + delete volume_manager; } } @@ -240,6 +246,11 @@ void Scene::device_update(Device *device_, Progress& progress) if(progress.get_cancel() || device->have_error()) return; + progress.set_status("Updating OpenVDB Volumes"); + volume_manager->device_update(device, &dscene, this, progress); + + if(progress.get_cancel() || device->have_error()) return; + if(device->have_error() == false) { progress.set_status("Updating Device", "Writing constant memory"); device->const_copy_to("__data", &dscene.data, sizeof(dscene.data)); @@ -300,6 +311,7 @@ bool Scene::need_reset() || particle_system_manager->need_update || curve_system_manager->need_update || bake_manager->need_update + || volume_manager->need_update || film->need_update); } diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index 851e5ac0b72..e7460c348ee 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -24,6 +24,7 @@ #include "kernel_types.h" +#include "util_openvdb.h" #include "util_param.h" #include "util_string.h" #include "util_system.h" @@ -55,6 +56,7 @@ class ShaderManager; class Progress; class BakeManager; class BakeData; +class VolumeManager; /* Scene Device Data */ @@ -128,6 +130,7 @@ public: enum BVHType { BVH_DYNAMIC, BVH_STATIC } bvh_type; bool use_bvh_cache; bool use_bvh_spatial_split; + bool use_bvh_triangle_storage; bool use_qbvh; bool persistent_data; @@ -137,6 +140,7 @@ public: bvh_type = BVH_DYNAMIC; use_bvh_cache = false; use_bvh_spatial_split = false; + use_bvh_triangle_storage = true; use_qbvh = false; persistent_data = false; } @@ -146,6 +150,7 @@ public: && bvh_type == params.bvh_type && use_bvh_cache == params.use_bvh_cache && use_bvh_spatial_split == params.use_bvh_spatial_split + && use_bvh_triangle_storage == params.use_bvh_triangle_storage && use_qbvh == params.use_qbvh && persistent_data == params.persistent_data); } }; @@ -177,6 +182,7 @@ public: ParticleSystemManager *particle_system_manager; CurveSystemManager *curve_system_manager; BakeManager *bake_manager; + VolumeManager *volume_manager; /* default shaders */ int default_surface; @@ -194,7 +200,9 @@ public: /* mutex must be locked manually by callers */ thread_mutex mutex; - Scene(const SceneParams& params, const DeviceInfo& device_info); + Scene(const SceneParams& params, + const DeviceInfo& device_info, + const bool free_data_after_update = false); ~Scene(); void device_update(Device *device, Progress& progress); diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp index 57c1500628a..6408ba3adb9 100644 --- a/intern/cycles/render/session.cpp +++ b/intern/cycles/render/session.cpp @@ -812,6 +812,10 @@ void Session::update_scene() progress.set_status("Updating Scene"); scene->device_update(device, progress); } + + if(clear_database_cb) { + clear_database_cb(); + } } void Session::update_status_time(bool show_pause, bool show_done) diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h index c669bccd34b..ab9f2ddb0f2 100644 --- a/intern/cycles/render/session.h +++ b/intern/cycles/render/session.h @@ -128,6 +128,7 @@ public: function<void(RenderTile&)> write_render_tile_cb; function<void(RenderTile&)> update_render_tile_cb; + function<void(void)> clear_database_cb; Session(const SessionParams& params); ~Session(); diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/render/svm.cpp index d0bd34915df..1281beaac46 100644 --- a/intern/cycles/render/svm.cpp +++ b/intern/cycles/render/svm.cpp @@ -76,7 +76,7 @@ void SVMShaderManager::device_update(Device *device, DeviceScene *dscene, Scene if(shader->use_mis && shader->has_surface_emission) scene->light_manager->need_update = true; - SVMCompiler compiler(scene->shader_manager, scene->image_manager); + SVMCompiler compiler(scene->shader_manager, scene->image_manager, scene->volume_manager); compiler.background = ((int)i == scene->default_background); compiler.compile(shader, svm_nodes, i); } @@ -104,10 +104,11 @@ void SVMShaderManager::device_free(Device *device, DeviceScene *dscene, Scene *s /* Graph Compiler */ -SVMCompiler::SVMCompiler(ShaderManager *shader_manager_, ImageManager *image_manager_) +SVMCompiler::SVMCompiler(ShaderManager *shader_manager_, ImageManager *image_manager_, VolumeManager *volume_manager_) { shader_manager = shader_manager_; image_manager = image_manager_; + volume_manager = volume_manager_; max_stack_use = 0; current_type = SHADER_TYPE_SURFACE; current_shader = NULL; diff --git a/intern/cycles/render/svm.h b/intern/cycles/render/svm.h index 4b390fb88f9..db07dd5b9e2 100644 --- a/intern/cycles/render/svm.h +++ b/intern/cycles/render/svm.h @@ -34,6 +34,7 @@ class ShaderGraph; class ShaderInput; class ShaderNode; class ShaderOutput; +class VolumeManager; /* Shader Manager */ @@ -52,7 +53,7 @@ public: class SVMCompiler { public: - SVMCompiler(ShaderManager *shader_manager, ImageManager *image_manager); + SVMCompiler(ShaderManager *shader_manager, ImageManager *image_manager, VolumeManager *volume_manager_); void compile(Shader *shader, vector<int4>& svm_nodes, int index); void stack_assign(ShaderOutput *output); @@ -74,6 +75,7 @@ public: ShaderType output_type() { return current_type; } ImageManager *image_manager; + VolumeManager *volume_manager; ShaderManager *shader_manager; bool background; diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt index 0acb9e9304c..a9217c65901 100644 --- a/intern/cycles/util/CMakeLists.txt +++ b/intern/cycles/util/CMakeLists.txt @@ -52,6 +52,7 @@ set(SRC_HEADERS util_math_fast.h util_md5.h util_opengl.h + util_openvdb.h util_optimization.h util_param.h util_path.h @@ -83,6 +84,12 @@ if(WITH_CYCLES_DEBUG) ) endif() +if(WITH_OPENVDB) + add_definitions( + -DWITH_OPENVDB + ) +endif() + include_directories(${INC}) include_directories(SYSTEM ${INC_SYS}) diff --git a/intern/cycles/util/util_openvdb.h b/intern/cycles/util/util_openvdb.h new file mode 100644 index 00000000000..e262ec94c30 --- /dev/null +++ b/intern/cycles/util/util_openvdb.h @@ -0,0 +1,415 @@ +#ifndef __UTIL_OPENVDB_H__ +#define __UTIL_OPENVDB_H__ + +#ifdef WITH_OPENVDB +# include "util_map.h" +#endif + +#include "util_types.h" + +#include "kernel_types.h" + +CCL_NAMESPACE_BEGIN + +struct Ray; +struct Intersection; + +enum { + OPENVDB_SAMPLE_POINT = 0, + OPENVDB_SAMPLE_BOX = 1, +}; + +class float_volume { +public: + virtual ~float_volume() {} + virtual float sample(float x, float y, float z, int sampling) = 0; + virtual bool intersect(const Ray *ray, Intersection *isect) = 0; + virtual bool march(float *t0, float *t1) = 0; + virtual bool has_uniform_voxels() = 0; +}; + +class float3_volume { +public: + virtual ~float3_volume() {} + virtual float3 sample(float x, float y, float z, int sampling) = 0; + virtual bool intersect(const Ray *ray, Intersection *isect) = 0; + virtual bool march(float *t0, float *t1) = 0; + virtual bool has_uniform_voxels() = 0; +}; + +CCL_NAMESPACE_END + +#ifdef WITH_OPENVDB + +/* They are too many implicit float conversions happening in OpenVDB, disabling + * errors for now (kevin) */ +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-conversion" +# pragma GCC diagnostic ignored "-Wdouble-promotion" +#endif + +#include <openvdb/openvdb.h> +#include <openvdb/tools/Interpolation.h> +#include <openvdb/tools/RayIntersector.h> + +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +CCL_NAMESPACE_BEGIN + +#if defined(HAS_CPP11_FEATURES) +using std::isfinite; +#else +using boost::math::isfinite; +#endif + +class vdb_float_volume : public float_volume { + typedef openvdb::tools::GridSampler<openvdb::FloatGrid::ConstAccessor, openvdb::tools::PointSampler> point_sampler_t; + typedef openvdb::tools::GridSampler<openvdb::FloatGrid::ConstAccessor, openvdb::tools::BoxSampler> box_sampler_t; + typedef openvdb::tools::VolumeRayIntersector<openvdb::FloatGrid> isector_t; + typedef isector_t::RayType vdb_ray_t; + + /* mainly used to ensure thread safety for the accessors */ + typedef unordered_map<pthread_t, isector_t *> isect_map; + typedef unordered_map<pthread_t, point_sampler_t *> point_map; + typedef unordered_map<pthread_t, box_sampler_t *> box_map; + isect_map isectors; + point_map point_samplers; + box_map box_samplers; + + openvdb::FloatGrid::ConstAccessor *accessor; + openvdb::math::Transform *transfrom; + + /* Main intersector, its purpose is to initialize the voxels' bounding box + * so the ones for the various threads do not do this, rather they are + * generated from a copy of it */ + isector_t *main_isector; + + bool uniform_voxels; + +public: + vdb_float_volume(openvdb::FloatGrid::Ptr grid) + : transfrom(&grid->transform()) + { + accessor = new openvdb::FloatGrid::ConstAccessor(grid->getConstAccessor()); + + /* only grids with uniform voxels can be used with VolumeRayIntersector */ + if(grid->hasUniformVoxels()) { + uniform_voxels = true; + main_isector = new isector_t(*grid); + } + else { + uniform_voxels = false; + main_isector = NULL; + } + } + + ~vdb_float_volume() + { + for(point_map::iterator iter = point_samplers.begin(); + iter != point_samplers.end(); + ++iter) + { + delete iter->second; + } + + for(box_map::iterator iter = box_samplers.begin(); + iter != box_samplers.end(); + ++iter) + { + delete iter->second; + } + + if(uniform_voxels) { + delete main_isector; + + for(isect_map::iterator iter = isectors.begin(); + iter != isectors.end(); + ++iter) + { + delete iter->second; + } + } + } + + ccl_always_inline float sample(float x, float y, float z, int sampling) + { + pthread_t thread = pthread_self(); + + if(sampling == OPENVDB_SAMPLE_POINT) { + point_map::iterator iter = point_samplers.find(thread); + point_sampler_t *sampler; + + if(iter == point_samplers.end()) { + openvdb::FloatGrid::ConstAccessor *acc = new openvdb::FloatGrid::ConstAccessor(*accessor); + sampler = new point_sampler_t(*acc, *transfrom); + pair<pthread_t, point_sampler_t *> sampl(thread, sampler); + point_samplers.insert(sampl); + } + else { + sampler = iter->second; + } + + return sampler->wsSample(openvdb::Vec3d(x, y, z)); + } + else { + box_map::iterator iter = box_samplers.find(thread); + box_sampler_t *sampler; + + if(iter == box_samplers.end()) { + openvdb::FloatGrid::ConstAccessor *acc = new openvdb::FloatGrid::ConstAccessor(*accessor); + sampler = new box_sampler_t(*acc, *transfrom); + pair<pthread_t, box_sampler_t *> sampl(thread, sampler); + box_samplers.insert(sampl); + } + else { + sampler = iter->second; + } + + return sampler->wsSample(openvdb::Vec3d(x, y, z)); + } + } + + ccl_always_inline bool intersect(const Ray *ray, Intersection */*isect*/) + { + pthread_t thread = pthread_self(); + isect_map::iterator iter = isectors.find(thread); + isector_t *vdb_isect; + + if(iter == isectors.end()) { + vdb_isect = new isector_t(*main_isector); + pair<pthread_t, isector_t *> inter(thread, vdb_isect); + isectors.insert(inter); + } + else { + vdb_isect = iter->second; + } + + vdb_ray_t::Vec3Type P(ray->P.x, ray->P.y, ray->P.z); + vdb_ray_t::Vec3Type D(ray->D.x, ray->D.y, ray->D.z); + D.normalize(); + + vdb_ray_t vdb_ray(P, D, 1e-5f, ray->t); + + if(vdb_isect->setWorldRay(vdb_ray)) { + // TODO +// isect->t = vdb_ray.t1(); // (kevin) is this correct? +// isect->u = isect->v = 1.0f; +// isect->type = ; +// isect->shad = shader; +// isect->norm = ; +// isect->prim = 0; +// isect->object = 0; + + return true; + } + + return false; + } + + ccl_always_inline bool march(float *t0, float *t1) + { + pthread_t thread = pthread_self(); + isect_map::iterator iter = isectors.find(thread); + isector_t *vdb_isect = iter->second; + + openvdb::Real vdb_t0(*t0), vdb_t1(*t1); + + if(vdb_isect->march(vdb_t0, vdb_t1)) { + *t0 = (float)vdb_isect->getWorldTime(vdb_t0); + *t1 = (float)vdb_isect->getWorldTime(vdb_t1); + + return true; + } + + return false; + } + + ccl_always_inline bool has_uniform_voxels() + { + return uniform_voxels; + } +}; + +/* Same as above, except for vector grids */ +/* TODO(kevin): staggered velocity grid sampling */ +class vdb_float3_volume : public float3_volume { + typedef openvdb::tools::GridSampler<openvdb::Vec3SGrid::ConstAccessor, openvdb::tools::PointSampler> point_sampler_t; + typedef openvdb::tools::GridSampler<openvdb::Vec3SGrid::ConstAccessor, openvdb::tools::BoxSampler> box_sampler_t; + + typedef openvdb::tools::VolumeRayIntersector<openvdb::Vec3SGrid> isector_t; + typedef isector_t::RayType vdb_ray_t; + + /* mainly used to ensure thread safety for the accessors */ + typedef unordered_map<pthread_t, isector_t *> isect_map; + typedef unordered_map<pthread_t, point_sampler_t *> point_map; + typedef unordered_map<pthread_t, box_sampler_t *> box_map; + isect_map isectors; + point_map point_samplers; + box_map box_samplers; + + openvdb::Vec3SGrid::ConstAccessor *accessor; + openvdb::math::Transform *transfrom; + + /* Main intersector, its purpose is to initialize the voxels' bounding box + * so the ones for the various threads do not do this, rather they are + * generated from a copy of it. */ + isector_t *main_isector; + + bool uniform_voxels; + +public: + vdb_float3_volume(openvdb::Vec3SGrid::Ptr grid) + : transfrom(&grid->transform()) + { + accessor = new openvdb::Vec3SGrid::ConstAccessor(grid->getConstAccessor()); + + /* only grids with uniform voxels can be used with VolumeRayIntersector */ + if(grid->hasUniformVoxels()) { + uniform_voxels = true; + main_isector = new isector_t(*grid); + } + else { + uniform_voxels = false; + main_isector = NULL; + } + } + + ~vdb_float3_volume() + { + for(point_map::iterator iter = point_samplers.begin(); + iter != point_samplers.end(); + ++iter) + { + delete iter->second; + } + + for(box_map::iterator iter = box_samplers.begin(); + iter != box_samplers.end(); + ++iter) + { + delete iter->second; + } + + if(uniform_voxels) { + delete main_isector; + + for(isect_map::iterator iter = isectors.begin(); + iter != isectors.end(); + ++iter) + { + delete iter->second; + } + } + } + + ccl_always_inline float3 sample(float x, float y, float z, int sampling) + { + openvdb::Vec3s r; + pthread_t thread = pthread_self(); + + if(sampling == OPENVDB_SAMPLE_POINT) { + point_map::iterator iter = point_samplers.find(thread); + point_sampler_t *sampler; + + if(iter == point_samplers.end()) { + openvdb::Vec3SGrid::ConstAccessor *acc = new openvdb::Vec3SGrid::ConstAccessor(*accessor); + sampler = new point_sampler_t(*acc, *transfrom); + pair<pthread_t, point_sampler_t *> sampl(thread, sampler); + point_samplers.insert(sampl); + } + else { + sampler = iter->second; + } + + r = sampler->wsSample(openvdb::Vec3d(x, y, z)); + } + else { + box_map::iterator iter = box_samplers.find(thread); + box_sampler_t *sampler; + + if(iter == box_samplers.end()) { + openvdb::Vec3SGrid::ConstAccessor *acc = new openvdb::Vec3SGrid::ConstAccessor(*accessor); + sampler = new box_sampler_t(*acc, *transfrom); + pair<pthread_t, box_sampler_t *> sampl(thread, sampler); + box_samplers.insert(sampl); + } + else { + sampler = iter->second; + } + + r = sampler->wsSample(openvdb::Vec3d(x, y, z)); + } + + return make_float3(r.x(), r.y(), r.z()); + } + + ccl_always_inline bool intersect(const Ray *ray, Intersection */*isect*/) + { + pthread_t thread = pthread_self(); + isect_map::iterator iter = isectors.find(thread); + isector_t *vdb_isect; + + if(iter == isectors.end()) { + vdb_isect = new isector_t(*main_isector); + pair<pthread_t, isector_t *> inter(thread, vdb_isect); + isectors.insert(inter); + } + else { + vdb_isect = iter->second; + } + + vdb_ray_t::Vec3Type P(ray->P.x, ray->P.y, ray->P.z); + vdb_ray_t::Vec3Type D(ray->D.x, ray->D.y, ray->D.z); + D.normalize(); + + vdb_ray_t vdb_ray(P, D, 1e-5f, ray->t); + + if(vdb_isect->setWorldRay(vdb_ray)) { + // TODO +// isect->t = vdb_ray.t1(); // (kevin) is this correct? +// isect->u = isect->v = 1.0f; +// isect->type = ; +// isect->shad = shader; +// isect->norm = ; +// isect->prim = 0; +// isect->object = 0; + + return true; + } + + return false; + } + + ccl_always_inline bool march(float *t0, float *t1) + { + pthread_t thread = pthread_self(); + isect_map::iterator iter = isectors.find(thread); + isector_t *vdb_isect = iter->second; + + openvdb::Real vdb_t0(*t0), vdb_t1(*t1); + + if(vdb_isect->march(vdb_t0, vdb_t1)) { + *t0 = (float)vdb_isect->getWorldTime(vdb_t0); + *t1 = (float)vdb_isect->getWorldTime(vdb_t1); + + return true; + } + + return false; + } + + ccl_always_inline bool has_uniform_voxels() + { + return uniform_voxels; + } +}; + +CCL_NAMESPACE_END + +#endif /* WITH_OPENVDB */ + +#endif /* __UTIL_OPENVDB_H__ */ + diff --git a/intern/openvdb/CMakeLists.txt b/intern/openvdb/CMakeLists.txt new file mode 100644 index 00000000000..d7186338a16 --- /dev/null +++ b/intern/openvdb/CMakeLists.txt @@ -0,0 +1,61 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2015, Blender Foundation +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): Kevin Dietrich. +# +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + intern +) + +set(INC_SYS + ${GLEW_INCLUDE_PATH} +) + +set(SRC + openvdb_capi.h +) + +if(WITH_OPENVDB) + add_definitions( + -DWITH_OPENVDB + -DOPENVDB_USE_BLOSC + ) + + list(APPEND INC_SYS + ${OPENEXR_INCLUDE_DIRS} + ${OPENVDB_INCLUDE_DIRS} + ) + + list(APPEND SRC + intern/openvdb_dense_convert.cpp + intern/openvdb_primitive.cpp + intern/openvdb_reader.cpp + intern/openvdb_render.cpp + intern/openvdb_writer.cpp + openvdb_capi.cpp + openvdb_util.cpp + ) +endif() + +blender_add_lib(bf_intern_openvdb "${SRC}" "${INC}" "${INC_SYS}") diff --git a/intern/openvdb/SConscript b/intern/openvdb/SConscript new file mode 100644 index 00000000000..b05437b36fe --- /dev/null +++ b/intern/openvdb/SConscript @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2015, Blender Foundation +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): Kevin Dietrich +# +# ***** END GPL LICENSE BLOCK ***** + +Import ('env') + +sources = [] + +defs = [] +incs = [ + '.', + 'intern', + '../guardedalloc', + '../../source/blender/blenkernel', + '../../source/blender/blenlib', + '../../source/blender/makesdna', + '../../source/blender/makesrna', + env['BF_OPENVDB_INC'], + env['BF_BOOST_INC'], + env['BF_GLEW_INC'] +] + +incs.append(env['BF_OPENEXR_INC'].split()) + +if env['WITH_BF_OPENVDB']: + defs.append('WITH_OPENVDB') + defs.append('DDF_DEBUG=0') + sources = env.Glob('*.cpp') + env.Glob('intern/*.cpp') + +env.BlenderLib ( libname = 'bf_intern_openvdb', sources = sources, includes = Split(incs), defines = defs, libtype=['core'], priority = [364] ) diff --git a/intern/openvdb/intern/openvdb_dense_convert.cpp b/intern/openvdb/intern/openvdb_dense_convert.cpp new file mode 100644 index 00000000000..d7a9896675e --- /dev/null +++ b/intern/openvdb/intern/openvdb_dense_convert.cpp @@ -0,0 +1,263 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <openvdb/tools/ValueTransformer.h> + +#include "openvdb_capi.h" +#include "openvdb_dense_convert.h" +#include "openvdb_writer.h" + +using namespace openvdb; + +namespace internal { + +class MergeScalarGrids { + tree::ValueAccessor<const FloatTree> m_acc_x, m_acc_y, m_acc_z; + +public: + MergeScalarGrids(const FloatTree *x_tree, const FloatTree *y_tree, const FloatTree *z_tree) + : m_acc_x(*x_tree) + , m_acc_y(*y_tree) + , m_acc_z(*z_tree) + {} + + MergeScalarGrids(const MergeScalarGrids &other) + : m_acc_x(other.m_acc_x) + , m_acc_y(other.m_acc_y) + , m_acc_z(other.m_acc_z) + {} + + void operator()(const Vec3STree::ValueOnIter &it) const + { + const math::Coord xyz = it.getCoord(); + float x = m_acc_x.getValue(xyz); + float y = m_acc_y.getValue(xyz); + float z = m_acc_z.getValue(xyz); + + it.setValue(Vec3s(x, y, z)); + } +}; + +GridBase *OpenVDB_export_vector_grid(OpenVDBWriter *writer, + const std::string &name, + const float *data_x, const float *data_y, const float *data_z, + const int res[3], + float fluid_mat[4][4], + VecType vec_type, + const bool is_color, + const FloatGrid *mask) +{ + + math::CoordBBox bbox(Coord(0), Coord(res[0] - 1, res[1] - 1, res[2] - 1)); + + Mat4R mat = Mat4R( + fluid_mat[0][0], fluid_mat[0][1], fluid_mat[0][2], fluid_mat[0][3], + fluid_mat[1][0], fluid_mat[1][1], fluid_mat[1][2], fluid_mat[1][3], + fluid_mat[2][0], fluid_mat[2][1], fluid_mat[2][2], fluid_mat[2][3], + fluid_mat[3][0], fluid_mat[3][1], fluid_mat[3][2], fluid_mat[3][3]); + + math::Transform::Ptr transform = math::Transform::createLinearTransform(mat); + + FloatGrid::Ptr grid[3]; + + grid[0] = FloatGrid::create(0.0f); + tools::Dense<const float, tools::LayoutXYZ> dense_grid_x(bbox, data_x); + tools::copyFromDense(dense_grid_x, grid[0]->tree(), TOLERANCE); + + grid[1] = FloatGrid::create(0.0f); + tools::Dense<const float, tools::LayoutXYZ> dense_grid_y(bbox, data_y); + tools::copyFromDense(dense_grid_y, grid[1]->tree(), TOLERANCE); + + grid[2] = FloatGrid::create(0.0f); + tools::Dense<const float, tools::LayoutXYZ> dense_grid_z(bbox, data_z); + tools::copyFromDense(dense_grid_z, grid[2]->tree(), TOLERANCE); + + Vec3SGrid::Ptr vecgrid = Vec3SGrid::create(Vec3s(0.0f)); + + /* Activate voxels in the vector grid based on the scalar grids to ensure + * thread safety later on */ + for (int i = 0; i < 3; ++i) { + vecgrid->tree().topologyUnion(grid[i]->tree()); + } + + MergeScalarGrids op(&(grid[0]->tree()), &(grid[1]->tree()), &(grid[2]->tree())); + tools::foreach(vecgrid->beginValueOn(), op, true, false); + + vecgrid->setTransform(transform); + + if (mask) { + vecgrid = tools::clip(*vecgrid, *mask); + } + + vecgrid->setName(name); + vecgrid->setIsInWorldSpace(false); + vecgrid->setVectorType(vec_type); + vecgrid->insertMeta("is_color", BoolMetadata(is_color)); + + writer->insert(vecgrid); + + return vecgrid.get(); +} + +class SplitVectorGrid { + FloatGrid::Ptr m_grid_x, m_grid_y, m_grid_z; + +public: + SplitVectorGrid() + {} + + void operator()(const Vec3SGrid::Ptr &vecgrid) + { + Vec3s bg = vecgrid->background(); + m_grid_x = FloatGrid::create(bg.x()); + m_grid_y = FloatGrid::create(bg.y()); + m_grid_z = FloatGrid::create(bg.z()); + + if (math::Transform::Ptr xform = vecgrid->transform().copy()) { + m_grid_x->setTransform(xform); + m_grid_y->setTransform(xform); + m_grid_z->setTransform(xform); + } + + FloatGrid::Accessor acc_x = m_grid_x->getAccessor(), + acc_y = m_grid_y->getAccessor(), + acc_z = m_grid_z->getAccessor(); + + CoordBBox bbox; + for (Vec3SGrid::ValueOnCIter it = vecgrid->cbeginValueOn(); it; ++it) { + if (!it.getBoundingBox(bbox)) continue; + + const Vec3s &val = it.getValue(); + + if (it.isTileValue()) { + m_grid_x->fill(bbox, val.x()); + m_grid_y->fill(bbox, val.y()); + m_grid_z->fill(bbox, val.z()); + } + else { + acc_x.setValueOn(bbox.min(), val.x()); + acc_y.setValueOn(bbox.min(), val.y()); + acc_z.setValueOn(bbox.min(), val.z()); + } + } + } + + const FloatGrid::Ptr &grid_x() { return m_grid_x; } + const FloatGrid::Ptr &grid_y() { return m_grid_y; } + const FloatGrid::Ptr &grid_z() { return m_grid_z; } +}; + +void OpenVDB_import_grid_vector(OpenVDBReader *reader, + const std::string &name, + float **data_x, float **data_y, float **data_z, + const int res[3]) +{ + Vec3SGrid::Ptr vgrid = gridPtrCast<Vec3SGrid>(reader->getGrid(name)); + +#if 0 + SplitVectorGrid vector_split; + vector_split(vgrid); + + FloatGrid::Ptr grid[3]; + math::CoordBBox bbox(Coord(0), Coord(res[0] - 1, res[1] - 1, res[2] - 1)); + + grid[0] = vector_split.grid_x(); + tools::Dense<float, tools::LayoutXYZ> dense_grid_x(bbox); + tools::copyToDense(*grid[0], dense_grid_x); + *data_x = dense_grid_x.data(); + + grid[1] = vector_split.grid_y(); + tools::Dense<float, tools::LayoutXYZ> dense_grid_y(bbox); + tools::copyToDense(*grid[1], dense_grid_y); + *data_y = dense_grid_y.data(); + + grid[2] = vector_split.grid_z(); + tools::Dense<float, tools::LayoutXYZ> dense_grid_z(bbox); + tools::copyToDense(*grid[2], dense_grid_z); + *data_z = dense_grid_z.data(); +#else + Vec3SGrid::Accessor acc = vgrid->getAccessor(); + math::Coord xyz; + int &x = xyz[0], &y = xyz[1], &z = xyz[2]; + + int index = 0; + for (z = 0; z < res[2]; ++z) { + for (y = 0; y < res[1]; ++y) { + for (x = 0; x < res[0]; ++x, ++index) { + math::Vec3s value = acc.getValue(xyz); + (*data_x)[index] = value.x(); + (*data_y)[index] = value.y(); + (*data_z)[index] = value.z(); + } + } + } +#endif +} + +void OpenVDB_update_fluid_transform(const char *filename, float matrix[4][4], float matrix_high[4][4]) +{ + /* TODO(kevin): deduplicate this call */ + initialize(); + + Mat4R fluid_mat = Mat4R( + matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3], + matrix[1][0], matrix[1][1], matrix[1][2], matrix[1][3], + matrix[2][0], matrix[2][1], matrix[2][2], matrix[2][3], + matrix[3][0], matrix[3][1], matrix[3][2], matrix[3][3]); + + Mat4R fluid_matBig = Mat4R( + matrix_high[0][0], matrix_high[0][1], matrix_high[0][2], matrix_high[0][3], + matrix_high[1][0], matrix_high[1][1], matrix_high[1][2], matrix_high[1][3], + matrix_high[2][0], matrix_high[2][1], matrix_high[2][2], matrix_high[2][3], + matrix_high[3][0], matrix_high[3][1], matrix_high[3][2], matrix_high[3][3]); + + math::Transform::Ptr transform = math::Transform::createLinearTransform(fluid_mat); + math::Transform::Ptr transformBig = math::Transform::createLinearTransform(fluid_matBig); + + io::File file(filename); + file.open(); + GridPtrVecPtr grids = file.getGrids(); + GridBase::Ptr grid; + + for (size_t i = 0; i < grids->size(); ++i) { + grid = (*grids)[i]; + + const std::string name = grid->getName(); + size_t found = name.find("High"); + + if (found != std::string::npos) { + grid->setTransform(transformBig); + } + else { + grid->setTransform(transform); + } + } + + file.close(); +} + +} // namespace internal diff --git a/intern/openvdb/intern/openvdb_dense_convert.h b/intern/openvdb/intern/openvdb_dense_convert.h new file mode 100644 index 00000000000..d0243decf53 --- /dev/null +++ b/intern/openvdb/intern/openvdb_dense_convert.h @@ -0,0 +1,140 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __OPENVDB_DENSE_CONVERT_H__ +#define __OPENVDB_DENSE_CONVERT_H__ + +#include <openvdb/openvdb.h> +#include <openvdb/tools/Dense.h> +#include <openvdb/tools/Clip.h> + +#include "openvdb_primitive.h" +#include "openvdb_reader.h" +#include "openvdb_writer.h" + +#define TOLERANCE 1e-3f + +namespace internal { + +template <typename GridType, typename T> +GridType *OpenVDB_export_grid(OpenVDBWriter *writer, + const std::string &name, + const T *data, + const int res[3], + float fluid_mat[4][4], + const openvdb::FloatGrid *mask) +{ + using namespace openvdb; + + math::CoordBBox bbox(Coord(0), Coord(res[0] - 1, res[1] - 1, res[2] - 1)); + + Mat4R mat = Mat4R( + fluid_mat[0][0], fluid_mat[0][1], fluid_mat[0][2], fluid_mat[0][3], + fluid_mat[1][0], fluid_mat[1][1], fluid_mat[1][2], fluid_mat[1][3], + fluid_mat[2][0], fluid_mat[2][1], fluid_mat[2][2], fluid_mat[2][3], + fluid_mat[3][0], fluid_mat[3][1], fluid_mat[3][2], fluid_mat[3][3]); + + math::Transform::Ptr transform = math::Transform::createLinearTransform(mat); + + typename GridType::Ptr grid = GridType::create(T(0)); + + tools::Dense<const T, openvdb::tools::LayoutXYZ> dense_grid(bbox, data); + tools::copyFromDense(dense_grid, grid->tree(), TOLERANCE); + + grid->setTransform(transform); + + if (mask) { + grid = tools::clip(*grid, *mask); + } + + grid->setName(name); + grid->setIsInWorldSpace(false); + + writer->insert(grid); + + return grid.get(); +} + +template <typename GridType, typename T> +OpenVDBPrimitive *OpenVDB_import_grid(OpenVDBReader *reader, + const std::string &name, + T **data, + const int res[3]) +{ + using namespace openvdb; + + typename GridType::Ptr grid_tmp = gridPtrCast<GridType>(reader->getGrid(name)); +#if 0 + math::CoordBBox bbox(Coord(0), Coord(res[0] - 1, res[1] - 1, res[2] - 1)); + + tools::Dense<T, tools::LayoutXYZ> dense_grid(bbox); + tools::copyToDense(*grid_tmp, dense_grid); + memcpy(*data, dense_grid.data(), sizeof(T) * res[0] * res[1] * res[2]); +#else + typename GridType::Accessor acc = grid_tmp->getAccessor(); + math::Coord xyz; + int &x = xyz[0], &y = xyz[1], &z = xyz[2]; + + int index = 0; + for (z = 0; z < res[2]; ++z) { + for (y = 0; y < res[1]; ++y) { + for (x = 0; x < res[0]; ++x, ++index) { + (*data)[index] = acc.getValue(xyz); + } + } + } +#endif + + OpenVDBPrimitive *vdb_prim = new OpenVDBPrimitive(); + vdb_prim->setGrid(grid_tmp); + + return vdb_prim; +} + +openvdb::GridBase *OpenVDB_export_vector_grid(OpenVDBWriter *writer, + const std::string &name, + const float *data_x, const float *data_y, const float *data_z, + const int res[3], + float fluid_mat[4][4], + openvdb::VecType vec_type, + const bool is_color, + const openvdb::FloatGrid *mask); + + +void OpenVDB_import_grid_vector(OpenVDBReader *reader, + const std::string &name, + float **data_x, float **data_y, float **data_z, + const int res[3]); + +void OpenVDB_update_fluid_transform(const char *filename, + float matrix[4][4], + float matrix_high[4][4]); + +} + +#endif /* __OPENVDB_DENSE_CONVERT_H__ */ + diff --git a/intern/openvdb/intern/openvdb_primitive.cpp b/intern/openvdb/intern/openvdb_primitive.cpp new file mode 100644 index 00000000000..301a1a5fcbe --- /dev/null +++ b/intern/openvdb/intern/openvdb_primitive.cpp @@ -0,0 +1,92 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "openvdb_primitive.h" + +OpenVDBPrimitive::OpenVDBPrimitive() +{} + +OpenVDBPrimitive::~OpenVDBPrimitive() +{} + +openvdb::GridBase &OpenVDBPrimitive::getGrid() +{ + return *m_grid; +} + +const openvdb::GridBase &OpenVDBPrimitive::getConstGrid() const +{ + return *m_grid; +} + +openvdb::GridBase::Ptr OpenVDBPrimitive::getGridPtr() +{ + return m_grid; +} + +openvdb::GridBase::ConstPtr OpenVDBPrimitive::getConstGridPtr() const +{ + return m_grid; +} + +void OpenVDBPrimitive::setGrid(openvdb::GridBase::Ptr grid) +{ + m_grid = grid->copyGrid(); +} + +static openvdb::Mat4R convertMatrix(const float mat[4][4]) +{ + return openvdb::Mat4R( + mat[0][0], mat[0][1], mat[0][2], mat[0][3], + mat[1][0], mat[1][1], mat[1][2], mat[1][3], + mat[2][0], mat[2][1], mat[2][2], mat[2][3], + mat[3][0], mat[3][1], mat[3][2], mat[3][3]); +} + +/* A simple protection to avoid crashes for cases when something goes wrong for + * some reason in the matrix creation. */ +static openvdb::math::MapBase::Ptr createAffineMap(const float mat[4][4]) +{ + using namespace openvdb::math; + MapBase::Ptr transform; + + try { + transform.reset(new AffineMap(convertMatrix(mat))); + } + catch (const openvdb::ArithmeticError &e) { + std::cerr << e.what() << "\n"; + transform.reset(new AffineMap()); + } + + return transform; +} + +void OpenVDBPrimitive::setTransform(const float mat[4][4]) +{ + using namespace openvdb::math; + + Transform::Ptr transform = Transform::Ptr(new Transform(createAffineMap(mat))); + m_grid->setTransform(transform); +} diff --git a/intern/openvdb/intern/openvdb_primitive.h b/intern/openvdb/intern/openvdb_primitive.h new file mode 100644 index 00000000000..65633dfa86a --- /dev/null +++ b/intern/openvdb/intern/openvdb_primitive.h @@ -0,0 +1,47 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __OPENVDB_PRIMITIVE_H__ +#define __OPENVDB_PRIMITIVE_H__ + +#include <openvdb/openvdb.h> + +class OpenVDBPrimitive { + openvdb::GridBase::Ptr m_grid; + +public: + OpenVDBPrimitive(); + ~OpenVDBPrimitive(); + + openvdb::GridBase &getGrid(); + const openvdb::GridBase &getConstGrid() const; + openvdb::GridBase::Ptr getGridPtr(); + openvdb::GridBase::ConstPtr getConstGridPtr() const; + + void setGrid(openvdb::GridBase::Ptr grid); + void setTransform(const float mat[4][4]); +}; + +#endif /* __OPENVDB_PRIMITIVE_H__ */ diff --git a/intern/openvdb/intern/openvdb_reader.cpp b/intern/openvdb/intern/openvdb_reader.cpp new file mode 100644 index 00000000000..1ed080e7574 --- /dev/null +++ b/intern/openvdb/intern/openvdb_reader.cpp @@ -0,0 +1,107 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "openvdb_reader.h" + +#define COPY_MAX_BYTES 10485760 /* 10 Mb */ + +OpenVDBReader::OpenVDBReader() + : m_meta_map(new openvdb::MetaMap) +{ + /* Although it is safe, it may not be good to have this here... */ + openvdb::initialize(); + m_file = NULL; +} + +OpenVDBReader::~OpenVDBReader() +{ + if (m_file) { + m_file->close(); + delete m_file; + } +} + +void OpenVDBReader::open(const std::string &filename) +{ + if (m_file) { + m_file->close(); + delete m_file; + } + + m_file = new openvdb::io::File(filename); + m_file->setCopyMaxBytes(COPY_MAX_BYTES); + m_file->open(); + + m_meta_map = m_file->getMetadata(); +} + +void OpenVDBReader::floatMeta(const std::string &name, float &value) +{ + value = m_meta_map->metaValue<float>(name); +} + +void OpenVDBReader::intMeta(const std::string &name, int &value) +{ + value = m_meta_map->metaValue<int>(name); +} + +void OpenVDBReader::vec3sMeta(const std::string &name, float value[3]) +{ + openvdb::Vec3s meta_val = m_meta_map->metaValue<openvdb::Vec3s>(name); + + value[0] = meta_val.x(); + value[1] = meta_val.y(); + value[2] = meta_val.z(); +} + +void OpenVDBReader::vec3IMeta(const std::string &name, int value[3]) +{ + openvdb::Vec3i meta_val = m_meta_map->metaValue<openvdb::Vec3i>(name); + + value[0] = meta_val.x(); + value[1] = meta_val.y(); + value[2] = meta_val.z(); +} + +void OpenVDBReader::mat4sMeta(const std::string &name, float value[4][4]) +{ + openvdb::Mat4s meta_val = m_meta_map->metaValue<openvdb::Mat4s>(name); + + for (int i = 0; i < 4; ++i) { + for (int j = 0; j < 4; ++j) { + value[i][j] = meta_val[i][j]; + } + } +} + +openvdb::GridBase::Ptr OpenVDBReader::getGrid(const std::string &name) +{ + return m_file->readGrid(name); +} + +size_t OpenVDBReader::numGrids() const +{ + return m_file->getGrids()->size(); +} diff --git a/intern/openvdb/intern/openvdb_reader.h b/intern/openvdb/intern/openvdb_reader.h new file mode 100644 index 00000000000..ae79b63c9c7 --- /dev/null +++ b/intern/openvdb/intern/openvdb_reader.h @@ -0,0 +1,51 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __OPENVDB_READER_H__ +#define __OPENVDB_READER_H__ + +#include <openvdb/openvdb.h> + +class OpenVDBReader { + openvdb::MetaMap::Ptr m_meta_map; + openvdb::io::File *m_file; + +public: + OpenVDBReader(); + ~OpenVDBReader(); + + void open(const std::string &filename); + + void floatMeta(const std::string &name, float &value); + void intMeta(const std::string &name, int &value); + void vec3sMeta(const std::string &name, float value[3]); + void vec3IMeta(const std::string &name, int value[3]); + void mat4sMeta(const std::string &name, float value[4][4]); + + openvdb::GridBase::Ptr getGrid(const std::string &name); + size_t numGrids() const; +}; + +#endif /* __OPENVDB_READER_H__ */ diff --git a/intern/openvdb/intern/openvdb_render.cpp b/intern/openvdb/intern/openvdb_render.cpp new file mode 100644 index 00000000000..51aa43676c9 --- /dev/null +++ b/intern/openvdb/intern/openvdb_render.cpp @@ -0,0 +1,136 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <GL/glew.h> +#include <openvdb/openvdb.h> + +#include "openvdb_primitive.h" +#include "openvdb_render.h" + +namespace internal { + +void OpenVDBPrimitive_draw_tree(OpenVDBPrimitive *vdb_prim, const bool draw_root, const bool draw_level_1, const bool draw_level_2, const bool draw_leaves) +{ + using namespace openvdb; + using namespace openvdb::math; + + FloatGrid::Ptr grid = gridPtrCast<FloatGrid>(vdb_prim->getGridPtr()); + + math::Vec3d wmin, wmax; + math::Vec3s color(0.0f); + + math::Vec3s node_color[4] = { + math::Vec3s(0.0060f, 0.2790f, 0.6250f), // leaf nodes + math::Vec3s(0.8710f, 0.3940f, 0.0191f), // intermediate internal node levels + math::Vec3s(0.0432f, 0.3300f, 0.0411f), // first internal node level + math::Vec3s(0.0450f, 0.0450f, 0.0450f) // root + }; + + math::CoordBBox bbox; + + glBegin(GL_LINES); + + for (FloatTree::NodeCIter node_iter = grid->tree().cbeginNode(); node_iter; ++node_iter) { + node_iter.getBoundingBox(bbox); + + const Vec3d min(bbox.min().x() - 0.5, bbox.min().y() - 0.5, bbox.min().z() - 0.5); + const Vec3d max(bbox.max().x() + 0.5, bbox.max().y() + 0.5, bbox.max().z() + 0.5); + + wmin = grid->indexToWorld(min); + wmax = grid->indexToWorld(max); + + const int level = node_iter.getLevel(); + + if (level == 0) { + if (!draw_leaves) { + continue; + } + color = node_color[0]; + } + + if (level == 1) { + if (!draw_level_2) { + continue; + } + color = node_color[1]; + } + + if (level == 2) { + if (!draw_level_1) { + continue; + } + color = node_color[2]; + } + + if (level == 3) { + if (!draw_root) { + continue; + } + color = node_color[3]; + } + + glColor3f(color[0], color[1], color[2]); + + glVertex3f(wmin.x(), wmax.y(), wmax.z()); + glVertex3f(wmax.x(), wmax.y(), wmax.z()); + + glVertex3f(wmin.x(), wmax.y(), wmin.z()); + glVertex3f(wmax.x(), wmax.y(), wmin.z()); + + glVertex3f(wmin.x(), wmin.y(), wmax.z()); + glVertex3f(wmax.x(), wmin.y(), wmax.z()); + + glVertex3f(wmin.x(), wmin.y(), wmin.z()); + glVertex3f(wmax.x(), wmin.y(), wmin.z()); + + glVertex3f(wmin.x(), wmin.y(), wmin.z()); + glVertex3f(wmin.x(), wmin.y(), wmax.z()); + + glVertex3f(wmin.x(), wmax.y(), wmin.z()); + glVertex3f(wmin.x(), wmax.y(), wmax.z()); + + glVertex3f(wmax.x(), wmin.y(), wmin.z()); + glVertex3f(wmax.x(), wmin.y(), wmax.z()); + + glVertex3f(wmax.x(), wmax.y(), wmin.z()); + glVertex3f(wmax.x(), wmax.y(), wmax.z()); + + glVertex3f(wmin.x(), wmin.y(), wmin.z()); + glVertex3f(wmin.x(), wmax.y(), wmin.z()); + + glVertex3f(wmax.x(), wmin.y(), wmin.z()); + glVertex3f(wmax.x(), wmax.y(), wmin.z()); + + glVertex3f(wmax.x(), wmin.y(), wmax.z()); + glVertex3f(wmax.x(), wmax.y(), wmax.z()); + + glVertex3f(wmin.x(), wmin.y(), wmax.z()); + glVertex3f(wmin.x(), wmax.y(), wmax.z()); + } + + glEnd(); +} + +} diff --git a/intern/openvdb/intern/openvdb_render.h b/intern/openvdb/intern/openvdb_render.h new file mode 100644 index 00000000000..06bff3540b7 --- /dev/null +++ b/intern/openvdb/intern/openvdb_render.h @@ -0,0 +1,15 @@ +#ifndef __OPENVDB_RENDER_H__ +#define __OPENVDB_RENDER_H__ + +struct OpenVDBPrimitive; + +namespace internal { + +void OpenVDBPrimitive_draw_tree(OpenVDBPrimitive *vdb_prim, const bool draw_root, + const bool draw_level_1, const bool draw_level_2, + const bool draw_leaves); + +} + +#endif /* __OPENVDB_RENDER_H__ */ + diff --git a/intern/openvdb/intern/openvdb_writer.cpp b/intern/openvdb/intern/openvdb_writer.cpp new file mode 100644 index 00000000000..9c2ad40c425 --- /dev/null +++ b/intern/openvdb/intern/openvdb_writer.cpp @@ -0,0 +1,93 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "openvdb_writer.h" + +OpenVDBWriter::OpenVDBWriter() + : m_grids(new openvdb::GridPtrVec()) + , m_meta_map(new openvdb::MetaMap()) +{ + m_meta_map->insertMeta("creator", openvdb::StringMetadata("Blender/OpenVDBWriter")); +} + +OpenVDBWriter::~OpenVDBWriter() +{} + +void OpenVDBWriter::insert(const openvdb::GridBase::Ptr &grid) +{ + m_grids->push_back(grid); +} + +void OpenVDBWriter::insert(const openvdb::GridBase &grid) +{ + m_grids->push_back(grid.copyGrid()); +} + +void OpenVDBWriter::insertFloatMeta(const std::string &name, const float value) +{ + m_meta_map->insertMeta(name, openvdb::FloatMetadata(value)); +} + +void OpenVDBWriter::insertIntMeta(const std::string &name, const int value) +{ + m_meta_map->insertMeta(name, openvdb::Int32Metadata(value)); +} + +void OpenVDBWriter::insertVec3sMeta(const std::string &name, const openvdb::Vec3s value) +{ + m_meta_map->insertMeta(name, openvdb::Vec3SMetadata(value)); +} + +void OpenVDBWriter::insertVec3IMeta(const std::string &name, const openvdb::Vec3I value) +{ + m_meta_map->insertMeta(name, openvdb::Vec3IMetadata(value)); +} + +void OpenVDBWriter::insertMat4sMeta(const std::string &name, const float value[4][4]) +{ + openvdb::Mat4s mat = openvdb::Mat4s( + value[0][0], value[0][1], value[0][2], value[0][3], + value[1][0], value[1][1], value[1][2], value[1][3], + value[2][0], value[2][1], value[2][2], value[2][3], + value[3][0], value[3][1], value[3][2], value[3][3]); + + m_meta_map->insertMeta(name, openvdb::Mat4SMetadata(mat)); +} + +void OpenVDBWriter::setFileCompression(const int flags) +{ + m_flags = flags; +} + +void OpenVDBWriter::write(const std::string &filename) const +{ + openvdb::io::File file(filename); + file.setCompression(m_flags); + file.write(*m_grids, *m_meta_map); + file.close(); + + /* Should perhaps be an option at some point */ + m_grids->clear(); +} diff --git a/intern/openvdb/intern/openvdb_writer.h b/intern/openvdb/intern/openvdb_writer.h new file mode 100644 index 00000000000..6f36d6233a3 --- /dev/null +++ b/intern/openvdb/intern/openvdb_writer.h @@ -0,0 +1,55 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __OPENVDB_WRITER_H__ +#define __OPENVDB_WRITER_H__ + +#include <openvdb/openvdb.h> + +class OpenVDBWriter { + openvdb::GridPtrVecPtr m_grids; + openvdb::MetaMap::Ptr m_meta_map; + + int m_flags; + +public: + OpenVDBWriter(); + ~OpenVDBWriter(); + + void insert(const openvdb::GridBase::Ptr &grid); + void insert(const openvdb::GridBase &grid); + + void insertFloatMeta(const std::string &name, const float value); + void insertIntMeta(const std::string &name, const int value); + void insertVec3sMeta(const std::string &name, const openvdb::Vec3s value); + void insertVec3IMeta(const std::string &name, const openvdb::Vec3I value); + void insertMat4sMeta(const std::string &name, const float value[4][4]); + + void setFileCompression(const int flags); + + void write(const std::string &filename) const; +}; + +#endif /* __OPENVDB_WRITER_H__ */ diff --git a/intern/openvdb/openvdb_capi.cpp b/intern/openvdb/openvdb_capi.cpp new file mode 100644 index 00000000000..64df4b94c3d --- /dev/null +++ b/intern/openvdb/openvdb_capi.cpp @@ -0,0 +1,248 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "openvdb_capi.h" +#include "openvdb_dense_convert.h" +#include "openvdb_primitive.h" +#include "openvdb_render.h" +#include "openvdb_util.h" + +using namespace openvdb; + +struct OpenVDBFloatGrid { int unused; }; +struct OpenVDBIntGrid { int unused; }; +struct OpenVDBVectorGrid { int unused; }; + +int OpenVDB_getVersionHex() +{ + return OPENVDB_LIBRARY_VERSION; +} + +void OpenVDB_get_grid_info(const char *filename, OpenVDBGridInfoCallback cb, void *userdata) +{ + int ret = OPENVDB_NO_ERROR; + initialize(); + + try { + io::File file(filename); + file.open(); + + GridPtrVecPtr grids = file.getGrids(); + int grid_num = grids->size(); + + for (size_t i = 0; i < grid_num; ++i) { + GridBase::ConstPtr grid = (*grids)[i]; + + std::string name = grid->getName(); + std::string value_type = grid->valueType(); + bool is_color = false; + if (grid->getMetadata< TypedMetadata<bool> >("is_color")) + is_color = grid->metaValue<bool>("is_color"); + + cb(userdata, name.c_str(), value_type.c_str(), is_color); + } + } + catch (...) { + catch_exception(ret); + } +} + +void OpenVDB_update_fluid_transform(const char *filename, + float matrix[4][4], + float matrix_high[4][4]) +{ + int ret = OPENVDB_NO_ERROR; + + try { + internal::OpenVDB_update_fluid_transform(filename, matrix, matrix_high); + } + catch (...) { + catch_exception(ret); + } +} + +OpenVDBFloatGrid *OpenVDB_export_grid_fl(OpenVDBWriter *writer, + const char *name, float *data, + const int res[3], float matrix[4][4], + OpenVDBFloatGrid *mask) +{ + OpenVDBFloatGrid *grid = + (OpenVDBFloatGrid *)internal::OpenVDB_export_grid<FloatGrid>(writer, name, data, res, matrix, (FloatGrid *)mask); + return grid; +} + +OpenVDBIntGrid *OpenVDB_export_grid_ch(OpenVDBWriter *writer, + const char *name, unsigned char *data, + const int res[3], float matrix[4][4], + OpenVDBFloatGrid *mask) +{ + OpenVDBIntGrid *grid = + (OpenVDBIntGrid *)internal::OpenVDB_export_grid<Int32Grid>(writer, name, data, res, matrix, (FloatGrid *)mask); + return grid; +} + +OpenVDBVectorGrid *OpenVDB_export_grid_vec(struct OpenVDBWriter *writer, + const char *name, + const float *data_x, const float *data_y, const float *data_z, + const int res[3], float matrix[4][4], short vec_type, + const bool is_color, OpenVDBFloatGrid *mask) +{ + OpenVDBVectorGrid *grid = + (OpenVDBVectorGrid *)internal::OpenVDB_export_vector_grid(writer, name, + data_x, data_y, data_z, res, matrix, + static_cast<openvdb::VecType>(vec_type), + is_color, (FloatGrid *)mask); + return grid; +} + +OpenVDBPrimitive *OpenVDB_import_grid_fl(OpenVDBReader *reader, + const char *name, float **data, + const int res[3]) +{ + return internal::OpenVDB_import_grid<FloatGrid>(reader, name, data, res); +} + +void OpenVDB_import_grid_ch(OpenVDBReader *reader, + const char *name, unsigned char **data, + const int res[3]) +{ + internal::OpenVDB_import_grid<Int32Grid>(reader, name, data, res); +} + +void OpenVDB_import_grid_vec(struct OpenVDBReader *reader, + const char *name, + float **data_x, float **data_y, float **data_z, + const int res[3]) +{ + internal::OpenVDB_import_grid_vector(reader, name, data_x, data_y, data_z, res); +} + +OpenVDBWriter *OpenVDBWriter_create() +{ + return new OpenVDBWriter(); +} + +void OpenVDBWriter_free(OpenVDBWriter *writer) +{ + delete writer; + writer = NULL; +} + +void OpenVDBWriter_set_compression(OpenVDBWriter *writer, const int flag) +{ + int compression_flags = io::COMPRESS_ACTIVE_MASK; + + if (flag == 0) { + compression_flags |= io::COMPRESS_ZIP; + } + else if (flag == 1) { + compression_flags |= io::COMPRESS_BLOSC; + } + else { + compression_flags = io::COMPRESS_NONE; + } + + writer->setFileCompression(compression_flags); +} + +void OpenVDBWriter_add_meta_fl(OpenVDBWriter *writer, const char *name, const float value) +{ + writer->insertFloatMeta(name, value); +} + +void OpenVDBWriter_add_meta_int(OpenVDBWriter *writer, const char *name, const int value) +{ + writer->insertIntMeta(name, value); +} + +void OpenVDBWriter_add_meta_v3(OpenVDBWriter *writer, const char *name, const float value[3]) +{ + writer->insertVec3sMeta(name, value); +} + +void OpenVDBWriter_add_meta_v3_int(OpenVDBWriter *writer, const char *name, const int value[3]) +{ + writer->insertVec3IMeta(name, value); +} + +void OpenVDBWriter_add_meta_mat4(OpenVDBWriter *writer, const char *name, float value[4][4]) +{ + writer->insertMat4sMeta(name, value); +} + +void OpenVDBWriter_write(OpenVDBWriter *writer, const char *filename) +{ + writer->write(filename); +} + +OpenVDBReader *OpenVDBReader_create() +{ + return new OpenVDBReader(); +} + +void OpenVDBReader_free(OpenVDBReader *reader) +{ + delete reader; + reader = NULL; +} + +void OpenVDBReader_open(OpenVDBReader *reader, const char *filename) +{ + reader->open(filename); +} + +void OpenVDBReader_get_meta_fl(OpenVDBReader *reader, const char *name, float *value) +{ + reader->floatMeta(name, *value); +} + +void OpenVDBReader_get_meta_int(OpenVDBReader *reader, const char *name, int *value) +{ + reader->intMeta(name, *value); +} + +void OpenVDBReader_get_meta_v3(OpenVDBReader *reader, const char *name, float value[3]) +{ + reader->vec3sMeta(name, value); +} + +void OpenVDBReader_get_meta_v3_int(OpenVDBReader *reader, const char *name, int value[3]) +{ + reader->vec3IMeta(name, value); +} + +void OpenVDBReader_get_meta_mat4(OpenVDBReader *reader, const char *name, float value[4][4]) +{ + reader->mat4sMeta(name, value); +} + +void OpenVDB_draw_primitive(OpenVDBPrimitive *vdb_prim, + const bool draw_root, + const bool draw_level_1, + const bool draw_level_2, + const bool draw_leaves) +{ + internal::OpenVDBPrimitive_draw_tree(vdb_prim, draw_root, draw_level_1, draw_level_2, draw_leaves); +} diff --git a/intern/openvdb/openvdb_capi.h b/intern/openvdb/openvdb_capi.h new file mode 100644 index 00000000000..d0d4d80cb19 --- /dev/null +++ b/intern/openvdb/openvdb_capi.h @@ -0,0 +1,131 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __OPENVDB_CAPI_H__ +#define __OPENVDB_CAPI_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct OpenVDBReader; +struct OpenVDBWriter; +struct OpenVDBFloatGrid; +struct OpenVDBIntGrid; +struct OpenVDBPrimitive; +struct OpenVDBVectorGrid; + +int OpenVDB_getVersionHex(void); + +typedef void (*OpenVDBGridInfoCallback)(void *userdata, const char *name, const char *value_type, bool is_color); +void OpenVDB_get_grid_info(const char *filename, OpenVDBGridInfoCallback cb, void *userdata); + +enum { + OPENVDB_NO_ERROR = 0, + OPENVDB_ARITHM_ERROR = 1, + OPENVDB_ILLEGAL_ERROR = 2, + OPENVDB_INDEX_ERROR = 3, + OPENVDB_IO_ERROR = 4, + OPENVDB_KEY_ERROR = 5, + OPENVDB_LOOKUP_ERROR = 6, + OPENVDB_IMPL_ERROR = 7, + OPENVDB_REF_ERROR = 8, + OPENVDB_TYPE_ERROR = 9, + OPENVDB_VALUE_ERROR = 10, + OPENVDB_UNKNOWN_ERROR = 11, +}; + +enum { + VEC_INVARIANT = 0, + VEC_COVARIANT = 1, + VEC_COVARIANT_NORMALIZE = 2, + VEC_CONTRAVARIANT_RELATIVE = 3, + VEC_CONTRAVARIANT_ABSOLUTE = 4, +}; + +void OpenVDB_update_fluid_transform(const char *filename, + float matrix[4][4], + float matrix_high[4][4]); + +struct OpenVDBFloatGrid *OpenVDB_export_grid_fl(struct OpenVDBWriter *writer, + const char *name, float *data, + const int res[3], float matrix[4][4], + struct OpenVDBFloatGrid *mask); + +struct OpenVDBIntGrid *OpenVDB_export_grid_ch(struct OpenVDBWriter *writer, + const char *name, unsigned char *data, + const int res[3], float matrix[4][4], + struct OpenVDBFloatGrid *mask); + +struct OpenVDBVectorGrid *OpenVDB_export_grid_vec(struct OpenVDBWriter *writer, + const char *name, + const float *data_x, const float *data_y, const float *data_z, + const int res[3], float matrix[4][4], short vec_type, + const bool is_color, + struct OpenVDBFloatGrid *mask); + +struct OpenVDBPrimitive *OpenVDB_import_grid_fl(struct OpenVDBReader *reader, + const char *name, float **data, + const int res[3]); + +void OpenVDB_import_grid_ch(struct OpenVDBReader *reader, + const char *name, unsigned char **data, + const int res[3]); + +void OpenVDB_import_grid_vec(struct OpenVDBReader *reader, + const char *name, + float **data_x, float **data_y, float **data_z, + const int res[3]); + +struct OpenVDBWriter *OpenVDBWriter_create(void); +void OpenVDBWriter_free(struct OpenVDBWriter *writer); +void OpenVDBWriter_set_compression(struct OpenVDBWriter *writer, const int flag); +void OpenVDBWriter_add_meta_fl(struct OpenVDBWriter *writer, const char *name, const float value); +void OpenVDBWriter_add_meta_int(struct OpenVDBWriter *writer, const char *name, const int value); +void OpenVDBWriter_add_meta_v3(struct OpenVDBWriter *writer, const char *name, const float value[3]); +void OpenVDBWriter_add_meta_v3_int(struct OpenVDBWriter *writer, const char *name, const int value[3]); +void OpenVDBWriter_add_meta_mat4(struct OpenVDBWriter *writer, const char *name, float value[4][4]); +void OpenVDBWriter_write(struct OpenVDBWriter *writer, const char *filename); + +struct OpenVDBReader *OpenVDBReader_create(void); +void OpenVDBReader_free(struct OpenVDBReader *reader); +void OpenVDBReader_open(struct OpenVDBReader *reader, const char *filename); +void OpenVDBReader_get_meta_fl(struct OpenVDBReader *reader, const char *name, float *value); +void OpenVDBReader_get_meta_int(struct OpenVDBReader *reader, const char *name, int *value); +void OpenVDBReader_get_meta_v3(struct OpenVDBReader *reader, const char *name, float value[3]); +void OpenVDBReader_get_meta_v3_int(struct OpenVDBReader *reader, const char *name, int value[3]); +void OpenVDBReader_get_meta_mat4(struct OpenVDBReader *reader, const char *name, float value[4][4]); + +void OpenVDB_draw_primitive(struct OpenVDBPrimitive *vdb_prim, + const bool draw_root, + const bool draw_level_1, + const bool draw_level_2, + const bool draw_leaves); + +#ifdef __cplusplus +} +#endif + +#endif /* __OPENVDB_CAPI_H__ */ diff --git a/intern/openvdb/openvdb_util.cpp b/intern/openvdb/openvdb_util.cpp new file mode 100644 index 00000000000..4caa62c6395 --- /dev/null +++ b/intern/openvdb/openvdb_util.cpp @@ -0,0 +1,80 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <openvdb/Exceptions.h> + +#include "openvdb_capi.h" +#include "openvdb_util.h" + +void catch_exception(int &ret) +{ + try { + throw; + } + catch (const openvdb::ArithmeticError &e) { + std::cerr << e.what() << std::endl; + ret = OPENVDB_ARITHM_ERROR; + } + catch (const openvdb::IllegalValueException &e) { + std::cerr << e.what() << std::endl; + ret = OPENVDB_ILLEGAL_ERROR; + } + catch (const openvdb::IndexError &e) { + std::cerr << e.what() << std::endl; + ret = OPENVDB_INDEX_ERROR; + } + catch (const openvdb::IoError &e) { + std::cerr << e.what() << std::endl; + ret = OPENVDB_IO_ERROR; + } + catch (const openvdb::KeyError &e) { + std::cerr << e.what() << std::endl; + ret = OPENVDB_KEY_ERROR; + } + catch (const openvdb::LookupError &e) { + std::cerr << e.what() << std::endl; + ret = OPENVDB_LOOKUP_ERROR; + } + catch (const openvdb::NotImplementedError &e) { + std::cerr << e.what() << std::endl; + ret = OPENVDB_IMPL_ERROR; + } + catch (const openvdb::ReferenceError &e) { + std::cerr << e.what() << std::endl; + ret = OPENVDB_REF_ERROR; + } + catch (const openvdb::TypeError &e) { + std::cerr << e.what() << std::endl; + ret = OPENVDB_TYPE_ERROR; + } + catch (const openvdb::ValueError &e) { + std::cerr << e.what() << std::endl; + ret = OPENVDB_VALUE_ERROR; + } + catch (...) { + std::cerr << "Unknown error in OpenVDB library..." << std::endl; + ret = OPENVDB_UNKNOWN_ERROR; + } +} diff --git a/intern/openvdb/openvdb_util.h b/intern/openvdb/openvdb_util.h new file mode 100644 index 00000000000..56ed9ce412b --- /dev/null +++ b/intern/openvdb/openvdb_util.h @@ -0,0 +1,31 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Kevin Dietrich + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __OPENVDB_UTIL_H__ +#define __OPENVDB_UTIL_H__ + +void catch_exception(int &ret); + +#endif /* __OPENVDB_UTIL_H__ */ diff --git a/intern/smoke/extern/smoke_API.h b/intern/smoke/extern/smoke_API.h index 08dbded176e..8fa7daaad79 100644 --- a/intern/smoke/extern/smoke_API.h +++ b/intern/smoke/extern/smoke_API.h @@ -74,7 +74,7 @@ size_t smoke_get_index2d(int x, int max_x, int y); void smoke_dissolve(struct FLUID_3D *fluid, int speed, int log); // wavelet turbulence functions -struct WTURBULENCE *smoke_turbulence_init(int *res, int amplify, int noisetype, const char *noisefile_path, int use_fire, int use_colors); +struct WTURBULENCE *smoke_turbulence_init(int *res, int amplify, int noisetype, const char *noisefile_path, int use_fire, int use_colors, int use_sim); void smoke_turbulence_free(struct WTURBULENCE *wt); void smoke_turbulence_step(struct WTURBULENCE *wt, struct FLUID_3D *fluid); @@ -109,6 +109,7 @@ int smoke_has_colors(struct FLUID_3D *fluid); int smoke_turbulence_has_fuel(struct WTURBULENCE *wt); int smoke_turbulence_has_colors(struct WTURBULENCE *wt); +void smoke_ensure_simulation(struct FLUID_3D *fluid, struct WTURBULENCE *wt); void smoke_ensure_heat(struct FLUID_3D *fluid); void smoke_ensure_fire(struct FLUID_3D *fluid, struct WTURBULENCE *wt); void smoke_ensure_colors(struct FLUID_3D *fluid, struct WTURBULENCE *wt, float init_r, float init_g, float init_b); diff --git a/intern/smoke/intern/WTURBULENCE.cpp b/intern/smoke/intern/WTURBULENCE.cpp index 3d712d2124a..2260057c0d2 100644 --- a/intern/smoke/intern/WTURBULENCE.cpp +++ b/intern/smoke/intern/WTURBULENCE.cpp @@ -51,8 +51,10 @@ static const float persistence = 0.56123f; ////////////////////////////////////////////////////////////////////// // constructor ////////////////////////////////////////////////////////////////////// -WTURBULENCE::WTURBULENCE(int xResSm, int yResSm, int zResSm, int amplify, int noisetype, const char *noisefile_path, int init_fire, int init_colors) +WTURBULENCE::WTURBULENCE(int xResSm, int yResSm, int zResSm, int amplify, int noisetype, const char *noisefile_path, int init_fire, int init_colors, int init_sim) { + _need_sim_data = init_sim != 0; + // if noise magnitude is below this threshold, its contribution // is negilgible, so stop evaluating new octaves _cullingThreshold = 1e-3; @@ -87,11 +89,14 @@ WTURBULENCE::WTURBULENCE(int xResSm, int yResSm, int zResSm, int amplify, int no // allocate high resolution density field _totalStepsBig = 0; _densityBig = new float[_totalCellsBig]; - _densityBigOld = new float[_totalCellsBig]; - - for(int i = 0; i < _totalCellsBig; i++) { - _densityBig[i] = - _densityBigOld[i] = 0.; + memset(_densityBig, 0, sizeof(*_densityBig) * _totalCellsBig); + + if (_need_sim_data) { + _densityBigOld = new float[_totalCellsBig]; + memset(_densityBigOld, 0, sizeof(*_densityBigOld) * _totalCellsBig); + } + else { + _densityBigOld = NULL; } /* fire */ @@ -112,7 +117,6 @@ WTURBULENCE::WTURBULENCE(int xResSm, int yResSm, int zResSm, int amplify, int no _tcU = new float[_totalCellsSm]; _tcV = new float[_totalCellsSm]; _tcW = new float[_totalCellsSm]; - _tcTemp = new float[_totalCellsSm]; // map all const float dx = 1.0f/(float)(_resSm[0]); @@ -126,29 +130,72 @@ WTURBULENCE::WTURBULENCE(int xResSm, int yResSm, int zResSm, int amplify, int no _tcU[index] = x*dx; _tcV[index] = y*dy; _tcW[index] = z*dz; - _tcTemp[index] = 0.; } + if (_need_sim_data) { + _tcTemp = new float[_totalCellsSm]; + memset(_tcTemp, 0, sizeof(*_tcTemp) * _totalCellsSm); + } + else { + _tcTemp = NULL; + } + // noise tiles _noiseTile = new float[noiseTileSize * noiseTileSize * noiseTileSize]; setNoise(noisetype, noisefile_path); } +void WTURBULENCE::initSimulation() +{ + if (_need_sim_data) { + return; + } + + if(_densityBigOld == NULL) { + _densityBigOld = new float[_totalCellsBig]; + memset(_densityBigOld, 0, sizeof(*_densityBigOld) * _totalCellsBig); + } + + if (_tcTemp == NULL) { + _tcTemp = new float[_totalCellsSm]; + memset(_tcTemp, 0, sizeof(*_tcTemp) * _totalCellsSm); + } + + if (_fuelBig != NULL) { + if (_fuelBigOld == NULL) { + _fuelBigOld = new float[_totalCellsBig]; + _reactBigOld = new float[_totalCellsBig]; + memset(_fuelBigOld, 0, sizeof(*_fuelBigOld) * _totalCellsBig); + memset(_reactBigOld, 0, sizeof(*_reactBigOld) * _totalCellsBig); + } + } + + if (_color_rBig != NULL) { + if (_color_rBigOld == NULL) { + _color_rBigOld = new float[_totalCellsBig]; + _color_gBigOld = new float[_totalCellsBig]; + _color_bBigOld = new float[_totalCellsBig]; + memset(_color_rBigOld, 0, sizeof(*_color_rBigOld) * _totalCellsBig); + memset(_color_gBigOld, 0, sizeof(*_color_gBigOld) * _totalCellsBig); + memset(_color_bBigOld, 0, sizeof(*_color_bBigOld) * _totalCellsBig); + } + } +} + void WTURBULENCE::initFire() { if (!_fuelBig) { _flameBig = new float[_totalCellsBig]; _fuelBig = new float[_totalCellsBig]; - _fuelBigOld = new float[_totalCellsBig]; _reactBig = new float[_totalCellsBig]; - _reactBigOld = new float[_totalCellsBig]; - - for(int i = 0; i < _totalCellsBig; i++) { - _flameBig[i] = - _fuelBig[i] = - _fuelBigOld[i] = 0.; - _reactBig[i] = - _reactBigOld[i] = 0.; + memset(_flameBig, 0, sizeof(*_flameBig) * _totalCellsBig); + memset(_fuelBig, 0, sizeof(*_fuelBig) * _totalCellsBig); + memset(_reactBig, 0, sizeof(*_reactBig) * _totalCellsBig); + if (_need_sim_data) { + _fuelBigOld = new float[_totalCellsBig]; + _reactBigOld = new float[_totalCellsBig]; + memset(_fuelBigOld, 0, sizeof(*_fuelBigOld) * _totalCellsBig); + memset(_reactBigOld, 0, sizeof(*_reactBigOld) * _totalCellsBig); } } } @@ -157,19 +204,20 @@ void WTURBULENCE::initColors(float init_r, float init_g, float init_b) { if (!_color_rBig) { _color_rBig = new float[_totalCellsBig]; - _color_rBigOld = new float[_totalCellsBig]; _color_gBig = new float[_totalCellsBig]; - _color_gBigOld = new float[_totalCellsBig]; _color_bBig = new float[_totalCellsBig]; - _color_bBigOld = new float[_totalCellsBig]; - for(int i = 0; i < _totalCellsBig; i++) { _color_rBig[i] = _densityBig[i] * init_r; - _color_rBigOld[i] = 0.0f; _color_gBig[i] = _densityBig[i] * init_g; - _color_gBigOld[i] = 0.0f; _color_bBig[i] = _densityBig[i] * init_b; - _color_bBigOld[i] = 0.0f; + } + if (_need_sim_data) { + _color_rBigOld = new float[_totalCellsBig]; + _color_gBigOld = new float[_totalCellsBig]; + _color_bBigOld = new float[_totalCellsBig]; + memset(_color_rBigOld, 0, sizeof(*_color_rBigOld) * _totalCellsBig); + memset(_color_gBigOld, 0, sizeof(*_color_gBigOld) * _totalCellsBig); + memset(_color_bBigOld, 0, sizeof(*_color_bBigOld) * _totalCellsBig); } } } @@ -179,7 +227,7 @@ void WTURBULENCE::initColors(float init_r, float init_g, float init_b) ////////////////////////////////////////////////////////////////////// WTURBULENCE::~WTURBULENCE() { delete[] _densityBig; - delete[] _densityBigOld; + if (_densityBigOld) delete[] _densityBigOld; if (_flameBig) delete[] _flameBig; if (_fuelBig) delete[] _fuelBig; if (_fuelBigOld) delete[] _fuelBigOld; @@ -196,7 +244,7 @@ WTURBULENCE::~WTURBULENCE() { delete[] _tcU; delete[] _tcV; delete[] _tcW; - delete[] _tcTemp; + if (_tcTemp) delete[] _tcTemp; delete[] _noiseTile; } diff --git a/intern/smoke/intern/WTURBULENCE.h b/intern/smoke/intern/WTURBULENCE.h index 36635325f62..fbc3e9eb8ba 100644 --- a/intern/smoke/intern/WTURBULENCE.h +++ b/intern/smoke/intern/WTURBULENCE.h @@ -36,11 +36,14 @@ struct WTURBULENCE { public: // both config files can be NULL, altCfg might override values from noiseCfg - WTURBULENCE(int xResSm, int yResSm, int zResSm, int amplify, int noisetype, const char *noisefile_path, int init_fire, int init_colors); + WTURBULENCE(int xResSm, int yResSm, int zResSm, int amplify, int noisetype, const char *noisefile_path, int init_fire, int init_colors, int init_sim); /// destructor virtual ~WTURBULENCE(); + // Ensure data needed for simulation is allocated + void initSimulation(); + void initFire(); void initColors(float init_r, float init_g, float init_b); @@ -144,6 +147,8 @@ struct WTURBULENCE void computeEigenvalues(float *_eigMin, float *_eigMax); void decomposeEnergy(float *energy, float *_highFreqEnergy); + + bool _need_sim_data; }; #endif // WTURBULENCE_H diff --git a/intern/smoke/intern/smoke_API.cpp b/intern/smoke/intern/smoke_API.cpp index d79aaf76d56..28b39531b91 100644 --- a/intern/smoke/intern/smoke_API.cpp +++ b/intern/smoke/intern/smoke_API.cpp @@ -44,10 +44,10 @@ extern "C" FLUID_3D *smoke_init(int *res, float dx, float dtdef, int use_heat, i return fluid; } -extern "C" WTURBULENCE *smoke_turbulence_init(int *res, int amplify, int noisetype, const char *noisefile_path, int use_fire, int use_colors) +extern "C" WTURBULENCE *smoke_turbulence_init(int *res, int amplify, int noisetype, const char *noisefile_path, int use_fire, int use_colors, int use_sim) { if (amplify) - return new WTURBULENCE(res[0],res[1],res[2], amplify, noisetype, noisefile_path, use_fire, use_colors); + return new WTURBULENCE(res[0],res[1],res[2], amplify, noisetype, noisefile_path, use_fire, use_colors, use_sim); else return NULL; } @@ -480,6 +480,13 @@ extern "C" int smoke_turbulence_has_colors(WTURBULENCE *wt) } /* additional field initialization */ +extern "C" void smoke_ensure_simulation(FLUID_3D * /*fluid*/, WTURBULENCE *wt) +{ + if (wt) { + wt->initSimulation(); + } +} + extern "C" void smoke_ensure_heat(FLUID_3D *fluid) { if (fluid) { |