diff --git a/projects/CMakeLists.txt b/projects/CMakeLists.txt index c3d6f269d2..ac6f9e3a8d 100644 --- a/projects/CMakeLists.txt +++ b/projects/CMakeLists.txt @@ -44,6 +44,7 @@ set(ZENO_EXTENSIONS Roads Geometry PluginPOC + Nemo ) foreach (name IN ITEMS ${ZENO_EXTENSIONS}) diff --git a/projects/Nemo/AnimSequence.cpp b/projects/Nemo/AnimSequence.cpp new file mode 100644 index 0000000000..8d0d5d2656 --- /dev/null +++ b/projects/Nemo/AnimSequence.cpp @@ -0,0 +1,69 @@ +/* + * MIT License + * + * Copyright (c) 2024 wuzhen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 2. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "AnimSequence.h" +#include +#include + +namespace nemo { +void Animation::load(std::string path) { + duration.first = INT_MAX; + duration.second = INT_MIN; + channels.clear(); + + std::ifstream fin(path); + if (!fin.is_open()) + throw std::runtime_error("Could not load animation: " + path); + nlohmann::json root = nlohmann::json::parse(fin); + for (const auto &element : root.items()) { + Channel channel; + channel.name = element.key(); + std::string type = element.value()["type"]; + for (const auto &frame : element.value()["values"].items()) { + int key = std::stoi(frame.key()); + duration.first = std::min(duration.first, key); + duration.second = std::max(duration.second, key); + ChannelValue value; + nlohmann::json j = frame.value(); + if (type == "matrix") { + glm::dmat4 matrix; + for (int i = 0; i != 16; ++i) + matrix[i / 4][i % 4] = j[i].get(); + value = matrix; + } else if (type == "double3") { + value = glm::dvec3{j[0].get(), j[1].get(), j[2].get()}; + } else if (type == "double") { + if (j.is_boolean()) + value = static_cast(j.get()); + else + value = j.get(); + } else { + throw std::runtime_error("unknown type: " + type); + } + channel.frames.insert(std::make_pair(key, value)); + } + channels.push_back(std::move(channel)); + } +} +} // namespace nemo diff --git a/projects/Nemo/AnimSequence.h b/projects/Nemo/AnimSequence.h new file mode 100644 index 0000000000..57f4a9ca72 --- /dev/null +++ b/projects/Nemo/AnimSequence.h @@ -0,0 +1,63 @@ +/* + * MIT License + * + * Copyright (c) 2024 wuzhen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 2. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once +#include +#include +#include +#include +#include + +namespace nemo { +using ChannelValue = std::variant; +struct Channel { + std::string name; + std::map frames; +}; + +struct Animation { + std::vector channels; + std::pair duration; + + void load(std::string path); + + template T get(unsigned id, float time) { + int framebegin = channels[id].frames.begin()->first; + int frameend = channels[id].frames.rbegin()->first; + time = std::max(time, framebegin); + time = std::min(time, frameend); + + if (isIntegral(time)) + return std::get(channels[id].frames.at(static_cast(time))); + + float beg = std::floor(time); + float end = std::ceil(time); + double alpha = (time - beg) / (end - beg); + return (1.0 - alpha) * std::get(channels[id].frames.at(static_cast(beg))) + alpha * std::get(channels[id].frames.at(static_cast(end))); + } + +private: + static bool isIntegral(float x) { return std::abs(x - std::roundf(x)) < 1E-5F; } +}; +} // namespace nemo \ No newline at end of file diff --git a/projects/Nemo/CMakeLists.txt b/projects/Nemo/CMakeLists.txt new file mode 100644 index 0000000000..65d7e1744a --- /dev/null +++ b/projects/Nemo/CMakeLists.txt @@ -0,0 +1,6 @@ +file(GLOB NEMO_SOURCE *.cpp *.h) + +target_sources(zeno PRIVATE ${NEMO_SOURCE}) +target_include_directories(zeno PRIVATE .) + + diff --git a/projects/Nemo/Context.cpp b/projects/Nemo/Context.cpp new file mode 100644 index 0000000000..fa93db84f0 --- /dev/null +++ b/projects/Nemo/Context.cpp @@ -0,0 +1,100 @@ +/* + * MIT License + * + * Copyright (c) 2024 wuzhen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 2. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "Context.h" + +namespace nemo { +void DataStorage::cleanup() { + if (!instance || !fnFree) + return; + fnFree(instance); +} + +void DataStorage::init(MOD mod) { + fnNew = get_fn(mod, "dataNew"); + fnFree = get_fn(mod, "dataFree"); + instance = fnNew(); + + fnResize = get_fn(mod, "dataResize"); + fnGlobalIdOffset = get_fn(mod, "dataGlobalIdOffset"); + fnTypeid = get_fn(mod, "dataTypeid"); + + fnGetBool = get_fn(mod, "dataGetBool"); + fnGetFloat = get_fn(mod, "dataGetFloat"); + fnGetDouble = get_fn(mod, "dataGetDouble"); + fnGetInt = get_fn(mod, "dataGetInt"); + fnGetVec3 = get_fn(mod, "dataGetVec3"); + fnGetDVec3 = get_fn(mod, "dataGetDVec3"); + fnGetMat4 = get_fn(mod, "dataGetMat4"); + fnGetDMat4 = get_fn(mod, "dataGetDMat4"); + fnGetMesh = get_fn(mod, "dataGetMesh"); + fnGetDMesh = get_fn(mod, "dataGetDMesh"); + fnGetCuShape = get_fn(mod, "dataGetCuShape"); + fnPullCuShape = get_fn(mod, "dataPullCuShape"); + fnGetDCuShape = get_fn(mod, "dataGetDCuShape"); + fnPullDCuShape = get_fn(mod, "dataPullDCuShape"); + fnGetCurve = get_fn(mod, "dataGetCurve"); + fnGetDCurve = get_fn(mod, "dataGetDCurve"); + fnGetSurface = get_fn(mod, "dataGetSurface"); + fnGetDSurface = get_fn(mod, "dataGetDSurface"); + + fnSetBool = get_fn(mod, "dataSetBool"); + fnSetFloat = get_fn(mod, "dataSetFloat"); + fnSetDouble = get_fn(mod, "dataSetDouble"); + fnSetInt = get_fn(mod, "dataSetInt"); + fnSetVec2 = get_fn(mod, "dataSetVec2"); + fnSetDVec2 = get_fn(mod, "dataSetDVec2"); + fnSetVec3 = get_fn(mod, "dataSetVec3"); + fnSetDVec3 = get_fn(mod, "dataSetDVec3"); + fnSetMat4 = get_fn(mod, "dataSetMat4"); + fnSetDMat4 = get_fn(mod, "dataSetDMat4"); + fnSetMesh = get_fn(mod, "dataSetMesh"); + fnSetDMesh = get_fn(mod, "dataSetDMesh"); + fnSetCurve = get_fn(mod, "dataSetCurve"); + fnSetDCurve = get_fn(mod, "dataSetDCurve"); + fnSetSurface = get_fn(mod, "dataSetSurface"); + fnSetDSurface = get_fn(mod, "dataSetDSurface"); +} + +void ResourcePool::cleanup() { + if (!instance || !fnFree) + return; + fnFree(instance); +} + +void ResourcePool::init(MOD mod, std::string path) { + fnNew = get_fn(mod, "resNew"); + fnFree = get_fn(mod, "resFree"); + instance = fnNew(); + + fnLoad = get_fn(mod, "resLoad"); + fnLoad(instance, path); + + fnGetTopo = get_fn(mod, "resGetTopo"); + fnGetUV = get_fn(mod, "resGetUV"); + fnGetColor = get_fn(mod, "resGetColor"); + fnGetNormal = get_fn(mod, "resGetNormal"); + fnGetUVector = get_fn(mod, "resGetUVector"); +} +} // namespace nemo diff --git a/projects/Nemo/Context.h b/projects/Nemo/Context.h new file mode 100644 index 0000000000..464ed2a952 --- /dev/null +++ b/projects/Nemo/Context.h @@ -0,0 +1,197 @@ +/* + * MIT License + * + * Copyright (c) 2024 wuzhen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 2. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include +#include +#include +#include + +#ifdef WIN32 +#include +using MOD = HMODULE; +template T get_fn(HMODULE mod, const char *name) { return (T)GetProcAddress(mod, name); } +#else +#include +using MOD = void *; +template T get_fn(void *mod, const char *name) { return (T)dlsym(mod, name); } +#endif + +namespace nemo { +struct DataStorage { + void init(MOD mod); + + void cleanup(); + + void resize(std::string key, std::size_t size, bool sp) { fnResize(instance, fnTypeid(key), size, sp); } + + unsigned typeidFromStr(std::string key) const { return fnTypeid(key); } + + unsigned globalIdOffset(std::string key) const { return fnGlobalIdOffset(instance, fnTypeid(key)); } + + template void setCurve(Args &&...args) { fnSetCurve(instance, std::forward(args)...); } + template void getCurve(Args &&...args) const { fnGetCurve(instance, std::forward(args)...); } + template void setDCurve(Args &&...args) { fnSetDCurve(instance, std::forward(args)...); } + template void getDCurve(Args &&...args) { fnGetDCurve(instance, std::forward(args)...); } + template void setSurface(Args &&...args) { fnSetSurface(instance, std::forward(args)...); } + template void getSurface(Args &&...args) const { fnGetSurface(instance, std::forward(args)...); } + template void setDSurface(Args &&...args) { fnSetDSurface(instance, std::forward(args)...); } + template void getDSurface(Args &&...args) { fnGetDSurface(instance, std::forward(args)...); } + template void setMesh(Args &&...args) const { fnSetMesh(instance, std::forward(args)...); } + template void getMesh(Args &&...args) const { fnGetMesh(instance, std::forward(args)...); } + template void setDMesh(Args &&...args) const { fnSetDMesh(instance, std::forward(args)...); } + template void getDMesh(Args &&...args) const { fnGetDMesh(instance, std::forward(args)...); } + template void getCuShape(Args &&...args) const { fnGetCuShape(instance, std::forward(args)...); } + template void pullCuShape(Args &&...args) const { fnPullCuShape(instance, std::forward(args)...); } + template void getDCuShape(Args &&...args) const { fnGetDCuShape(instance, std::forward(args)...); } + template void pullDCuShape(Args &&...args) const { fnPullDCuShape(instance, std::forward(args)...); } + +#define ADD_DATA_READ(K, T) \ + T get##K(unsigned id) const { return fnGet##K(instance, id); } \ + T (*fnGet##K)(void *inst, unsigned idx) = nullptr; + +#define ADD_DATA_WRITE(K, T) \ + void set##K(unsigned id, T val) { fnSet##K(instance, id, val); } \ + void (*fnSet##K)(void *inst, unsigned idx, T value) = nullptr; + +#define ADD_DATA_TYPE(K, T) \ + ADD_DATA_READ(K, T) \ + ADD_DATA_WRITE(K, T) + + ADD_DATA_TYPE(Bool, bool) + ADD_DATA_TYPE(Float, float) + ADD_DATA_TYPE(Double, double) + ADD_DATA_TYPE(Int, int) + ADD_DATA_WRITE(Vec2, glm::vec2) + ADD_DATA_WRITE(DVec2, glm::dvec2) + ADD_DATA_WRITE(Vec3, glm::vec3) + ADD_DATA_WRITE(DVec3, glm::dvec3) + ADD_DATA_WRITE(Mat4, glm::mat4) + ADD_DATA_WRITE(DMat4, glm::dmat4) + +#undef ADD_DATA_TYPE +#undef ADD_DATA_READ +#undef ADD_DATA_WRITE + +#define ADD_DATA_READ(K, T) \ + T get##K(unsigned id) const { \ + T v; \ + fnGet##K(instance, id, v); \ + return v; \ + } \ + void (*fnGet##K)(void *inst, unsigned idx, T &v) = nullptr; + + ADD_DATA_READ(Vec3, glm::vec3); + ADD_DATA_READ(DVec3, glm::dvec3); + ADD_DATA_READ(Mat4, glm::mat4); + ADD_DATA_READ(DMat4, glm::dmat4); + +#undef ADD_DATA_READ + + void *instance = nullptr; + +private: + void *(*fnNew)() = nullptr; + void (*fnFree)(void *) = nullptr; + void (*fnResize)(void *inst, unsigned datatype, std::size_t size, bool sp) = nullptr; + unsigned (*fnGlobalIdOffset)(void *inst, unsigned datatype) = nullptr; + unsigned (*fnTypeid)(std::string name) = nullptr; + + void (*fnSetCurve)(void *inst, unsigned idx, unsigned degree, unsigned form, std::vector cv, std::vector knots, glm::mat4 matrix) = nullptr; + void (*fnGetCurve)(void *inst, unsigned idx, unsigned °ree, unsigned &form, std::vector &cv, std::vector &knots) = nullptr; + void (*fnSetDCurve)(void *inst, unsigned idx, unsigned degree, unsigned form, std::vector cv, std::vector knots, + glm::dmat4 matrix) = nullptr; + void (*fnGetDCurve)(void *inst, unsigned idx, unsigned °ree, unsigned &form, std::vector &cv, std::vector &knots) = nullptr; + + void (*fnSetSurface)(void *inst, unsigned idx, std::vector cv, unsigned degreeU, unsigned formU, std::vector knotsU, unsigned degreeV, + unsigned formV, std::vector knotsV, glm::mat4 matrix) = nullptr; + void (*fnGetSurface)(void *inst, unsigned idx, std::vector &cv, unsigned °reeU, unsigned &formU, std::vector &knotsU, unsigned °reeV, + unsigned &formV, std::vector &knotsV) = nullptr; + void (*fnSetDSurface)(void *inst, unsigned idx, std::vector cv, unsigned degreeU, unsigned formU, std::vector knotsU, unsigned degreeV, + unsigned formV, std::vector knotsV, glm::dmat4 matrix) = nullptr; + void (*fnGetDSurface)(void *inst, unsigned idx, std::vector &cv, unsigned °reeU, unsigned &formU, std::vector &knotsU, + unsigned °reeV, unsigned &formV, std::vector &knotsV) = nullptr; + + void (*fnSetMesh)(void *inst, unsigned idx, std::vector points, glm::mat4 matrix) = nullptr; + void (*fnGetMesh)(void *inst, unsigned idx, std::vector &points) = nullptr; + void (*fnSetDMesh)(void *inst, unsigned idx, std::vector points, glm::mat4 matrix) = nullptr; + void (*fnGetDMesh)(void *inst, unsigned idx, std::vector &points) = nullptr; + void (*fnGetCuShape)(void *inst, unsigned idx, glm::vec3 *&data, unsigned &size) = nullptr; + void (*fnPullCuShape)(void *inst, unsigned idx, std::vector &points) = nullptr; + void (*fnGetDCuShape)(void *inst, unsigned idx, glm::vec3 *&data, unsigned &size) = nullptr; + void (*fnPullDCuShape)(void *inst, unsigned idx, std::vector &points) = nullptr; +}; + +struct ResourcePool { + void cleanup(); + + void init(MOD mod, std::string path); + + std::pair, std::vector> getTopo(unsigned id) { + std::vector counts, connection; + fnGetTopo(instance, id, counts, connection); + return {counts, connection}; + } + + std::tuple, std::vector, std::vector> getUV(unsigned id) { + std::vector uValues, vValues; + std::vector uvIds; + fnGetUV(instance, id, uValues, vValues, uvIds); + return std::make_tuple(uValues, vValues, uvIds); + } + + std::pair, std::vector> getColor(unsigned id) { + std::vector colors; + std::vector colorIds; + fnGetColor(instance, id, colors, colorIds); + return std::make_pair(colors, colorIds); + } + + std::tuple, std::vector, std::vector> getUserNormal(unsigned id) { + std::vector normals; + std::vector faces, vertices; + fnGetNormal(instance, id, normals, faces, vertices); + return std::make_tuple(normals, faces, vertices); + } + + std::vector getUVector(unsigned id) { + std::vector data; + fnGetUVector(instance, id, data); + return data; + } + + void *instance = nullptr; + +private: + void *(*fnNew)() = nullptr; + void (*fnFree)(void *res) = nullptr; + void *(*fnLoad)(void *res, std::string path) = nullptr; + void (*fnGetTopo)(void *res, unsigned id, std::vector &, std::vector &) = nullptr; + void (*fnGetUV)(void *res, unsigned id, std::vector &, std::vector &, std::vector &) = nullptr; + void (*fnGetColor)(void *res, unsigned id, std::vector &, std::vector &) = nullptr; + void (*fnGetNormal)(void *res, unsigned id, std::vector &, std::vector &, std::vector &) = nullptr; + void (*fnGetUVector)(void *res, unsigned id, std::vector &) = nullptr; +}; +} // namespace nemo diff --git a/projects/Nemo/Evaluate.cpp b/projects/Nemo/Evaluate.cpp new file mode 100644 index 0000000000..a80e1733e5 --- /dev/null +++ b/projects/Nemo/Evaluate.cpp @@ -0,0 +1,262 @@ +/* + * MIT License + * + * Copyright (c) 2024 wuzhen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 2. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "Evaluate.h" +#include +#include +#include + +namespace glm { +inline void from_json(const nlohmann::json &j, mat4 &mat) { + for (int i = 0; i != 16; ++i) + mat[i / 4][i % 4] = j[i].get(); +} +} // namespace glm + +namespace nemo { +Evaluator::Evaluator(std::string path_config, std::string path_anim) { + // load config + { + std::ifstream fin(path_config); + if (!fin.is_open()) + throw std::runtime_error("Could not load config: " + path_config); + nlohmann::json config = nlohmann::json::parse(fin); + runtime.init(config, path_config); + load_plugs(config); + load_topology(config); + } + + // try load material + { + std::string path_material = path_config; + boost::algorithm::replace_last(path_material, "__CONFIG", "__MAT"); + std::ifstream fin(path_material); + if (fin.is_open()) + load_faceset(nlohmann::json::parse(fin)); + } + + // load anim + { + animation.load(path_anim); + for (const Channel &channel : animation.channels) { + auto iter = std::find_if(plugs.begin(), plugs.end(), [&channel](const PlugInfo &info) { return info.name == channel.name; }); + inputs.push_back(std::distance(plugs.begin(), iter)); + } + } +} + +void Evaluator::evaluate(float frame) { + update_inputs(frame); + runtime.evaluate_all(); +} + +void Evaluator::load_plugs(const nlohmann::json &root) { + std::vector all_ios = root["inputs"]; + for (auto x : root["outputs"]) + all_ios.push_back(x); + + for (const auto &element : all_ios) { + PlugInfo info; + info.name = element.at("name"); + info.dataIndex = element.at("data_id"); + info.dataTypeStr = element.at("type"); + plugs.push_back(info); + if (info.dataTypeStr == "Mesh" || info.dataTypeStr == "CuShape") { + meshes.push_back(plugs.size() - 1); + } + } +} + +void Evaluator::load_topology(const nlohmann::json &root) { + for (auto element : root.at("topology")) { + unsigned plug_id = element.at("vtx"); + LUT_path[plug_id] = element.value("path", ""); + LUT_topology[plug_id] = element.at("topo").get(); + if (element.count("uv")) { + LUT_uvsets[plug_id].push_back(std::make_pair("map1", element.at("uv").get())); + } else { + for (const auto &jItem : element.at("uvs")) { + LUT_uvsets[plug_id].push_back(std::make_pair(jItem.at("name").get(), jItem.at("id").get())); + } + } + + auto jVisibility = element.at("visibility"); + if (!jVisibility.is_null()) { + LUT_visible[plug_id] = std::make_pair(jVisibility.at("data_id"), jVisibility.at("data_type")); + } + + auto jTransform = element.at("transform"); + if (jTransform.is_null()) + LUT_transform[plug_id] = glm::identity(); + else if (jTransform.is_array()) + LUT_transform[plug_id] = jTransform.get(); + else + LUT_transform[plug_id] = jTransform.get(); + } +} + +void Evaluator::load_faceset(const nlohmann::json &root) { + for (auto [groupName, members] : root.items()) { + FaceSet group; + group.name = groupName; + + for (std::string x : members) { + std::string shape = x; + if (x.find('.') != std::string::npos) { + shape = x.substr(0, x.find('.')); + } + int shapeId = 0; + for (const auto &[meshId, meshPath] : LUT_path) { + if (boost::algorithm::ends_with(meshPath, shape)) { + shapeId = meshId; + break; + } + } + if (shapeId < 0) + break; + + std::size_t pos_dot = x.find('.'); + if (pos_dot == std::string::npos) { + group.members.insert(std::make_pair(shapeId, std::vector{})); + continue; + } + + std::string components = x.substr(pos_dot); + unsigned beg = components.find('[') + 1; + unsigned end = components.rfind(']'); + components = components.substr(beg, end - beg); + + if (components.find(':') == std::string::npos) + group.members[shapeId].push_back(std::stoi(components)); + else { + unsigned first = std::stoi(components.substr(0, components.find(':'))); + unsigned last = std::stoi(components.substr(components.find(':') + 1)); + for (unsigned face = first; face <= last; ++face) + group.members[shapeId].push_back(face); + } + } + facesets.push_back(group); + } +} + +void Evaluator::update_inputs(float frame) { + for (unsigned channel_id = 0; channel_id != inputs.size(); ++channel_id) { + unsigned plug_id = inputs[channel_id]; + const PlugInfo &info = plugs[plug_id]; + if ("Bool" == info.dataTypeStr) { + runtime.data.setBool(info.dataIndex, animation.get(channel_id, frame)); + } else if ("Decimal" == info.dataTypeStr) { + if (runtime.singlePrecision) + runtime.data.setFloat(info.dataIndex, animation.get(channel_id, frame)); + else + runtime.data.setDouble(info.dataIndex, animation.get(channel_id, frame)); + } else if ("Angle" == info.dataTypeStr) { + if (runtime.singlePrecision) + runtime.data.setFloat(info.dataIndex, glm::radians(animation.get(channel_id, frame))); + else + runtime.data.setDouble(info.dataIndex, glm::radians(animation.get(channel_id, frame))); + } else if ("Int" == info.dataTypeStr) { + runtime.data.setInt(info.dataIndex, animation.get(channel_id, frame)); + } else if ("Vec3" == info.dataTypeStr) { + if (runtime.singlePrecision) + runtime.data.setVec3(info.dataIndex, animation.get(channel_id, frame)); + else + runtime.data.setDVec3(info.dataIndex, animation.get(channel_id, frame)); + } else if ("Euler" == info.dataTypeStr) { + if (runtime.singlePrecision) + runtime.data.setVec3(info.dataIndex, glm::radians(animation.get(channel_id, frame))); + else + runtime.data.setDVec3(info.dataIndex, glm::radians(animation.get(channel_id, frame))); + } else if ("Mat4" == info.dataTypeStr) { + if (runtime.singlePrecision) + runtime.data.setMat4(info.dataIndex, animation.get(channel_id, frame)); + else + runtime.data.setDMat4(info.dataIndex, animation.get(channel_id, frame)); + } else { + throw std::runtime_error("unknown input type: " + info.dataTypeStr); + } + } +} + +bool Evaluator::isVisible(unsigned plug_id) const { + if (!LUT_visible.count(plug_id)) + return true; + + auto [data_id, data_type] = LUT_visible.at(plug_id); + if ("Bool" == data_type) + return runtime.data.getBool(data_id); + else if ("Decimal" == data_type) + return runtime.singlePrecision ? runtime.data.getFloat(data_id) : runtime.data.getDouble(data_id); + else if ("Int" == data_type) + return runtime.data.getInt(data_id); + else if ("Angle" == data_type) + return runtime.singlePrecision ? runtime.data.getFloat(data_id) : runtime.data.getDouble(data_id); + else + return true; +} + +std::vector Evaluator::getPoints(unsigned plug_id) const { + std::vector points; + unsigned data_id = plugs[plug_id].dataIndex; + std::string dataType = plugs[plug_id].dataTypeStr; + if (runtime.singlePrecision) { + if (dataType == "CuShape") + runtime.data.pullCuShape(data_id, points); + else + runtime.data.getMesh(data_id, points); + } else { + if (dataType == "CuShape") { + runtime.data.pullDCuShape(data_id, points); + } else { + std::vector _points; + runtime.data.getDMesh(data_id, _points); + points.resize(_points.size()); + for (int i = 0; i != points.size(); ++i) + points[i] = _points[i]; + } + } + + glm::mat4 transform; + { + auto item = LUT_transform.at(plug_id); + if (item.index() == 0) { + unsigned data_id = std::get(item); + if (runtime.singlePrecision) + transform = runtime.data.getMat4(data_id); + else + transform = runtime.data.getDMat4(data_id); + } else { + transform = std::get(item); + } + } + + glm::mat3 basis = transform; + glm::vec3 offset = transform[3]; + for (auto &pnt : points) { + pnt = basis * pnt + offset; + } + + return points; +} +} // namespace nemo diff --git a/projects/Nemo/Evaluate.h b/projects/Nemo/Evaluate.h new file mode 100644 index 0000000000..f7e11f554d --- /dev/null +++ b/projects/Nemo/Evaluate.h @@ -0,0 +1,102 @@ +/* + * MIT License + * + * Copyright (c) 2024 wuzhen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 2. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "AnimSequence.h" +#include "Runtime.h" + +namespace nemo { +struct Evaluator { + Runtime runtime; + Animation animation; + + struct PlugInfo { + std::string name; + unsigned dataIndex; + std::string dataTypeStr; + }; + std::vector plugs; + + // value: input id + std::vector inputs; + + // value: mesh id + std::vector meshes; + + // key: mesh id + // value: full path + std::map LUT_path; + + // key: mesh id + // value: topology + std::map LUT_topology; + + // key: mesh id + // value: uvs + std::map>> LUT_uvsets; + + // key: mesh id + // value: (dataLocation(-1 if constant), type) + std::map> LUT_visible; + + // key: mesh id + // value: dataLocation(dynamic) or worldMatrix(static) + std::map> LUT_transform; + + struct FaceSet { + std::string name; + // key: mesh_id + // value: faces(full if empty) + std::map> members; + }; + std::vector facesets; + +public: + Evaluator(std::string path_config, std::string path_anim); + + void evaluate(float frame); + + std::pair duration() const { return animation.duration; } + + std::pair, std::vector> getTopo(unsigned plug_id) { return runtime.resource.getTopo(LUT_topology.at(plug_id)); } + + std::tuple, std::vector, std::vector> getDefaultUV(unsigned plug_id) { + return runtime.resource.getUV(LUT_uvsets.at(plug_id).front().second); + } + + bool isVisible(unsigned plug_id) const; + + std::vector getPoints(unsigned plug_id) const; + +private: + void load_plugs(const nlohmann::json &root); + + void load_topology(const nlohmann::json &root); + + void load_faceset(const nlohmann::json &root); + + void update_inputs(float frame); +}; +} // namespace nemo \ No newline at end of file diff --git a/projects/Nemo/Runtime.cpp b/projects/Nemo/Runtime.cpp new file mode 100644 index 0000000000..bb5831c99c --- /dev/null +++ b/projects/Nemo/Runtime.cpp @@ -0,0 +1,259 @@ +/* + * MIT License + * + * Copyright (c) 2024 wuzhen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 2. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "Runtime.h" +#include +#include "zeno/utils/format.h" +#include +#include +#include +#include +#include + +namespace nemo { + +std::filesystem::path create_temporary_directory(unsigned long long max_tries = 1000) { + auto tmp_dir = std::filesystem::temp_directory_path(); + unsigned long long i = 0; + std::random_device dev; + std::mt19937 prng(dev()); + std::uniform_int_distribution rand(0); + std::filesystem::path path; + while (true) { + std::stringstream ss; + ss << std::hex << rand(prng); + path = tmp_dir / ss.str(); + // true if the directory was created. + if (std::filesystem::create_directory(path)) { + break; + } + if (i == max_tries) { + throw std::runtime_error("could not find non-existing directory"); + } + i++; + } + return path; +} + +void Runtime::init(const nlohmann::json &root, std::string pathConfig) { + singlePrecision = root.value("singlePrecision", true); + cuda = root.value("cuda", false); + + std::string path_bin = getAbsolutePath(pathConfig, root.at("bin")); + bool has_ext = boost::algorithm::ends_with(path_bin, ".dll") || boost::algorithm::ends_with(path_bin, ".so"); +#ifdef WIN32 + if (!has_ext) + path_bin += ".dll"; +#else + if (!has_ext) { + int sep = path_bin.rfind('/'); + std::string folder = path_bin.substr(0, sep + 1); + std::string filename = path_bin.substr(sep + 1); + if (filename.compare(0, 3, "lib")) // not start with "lib" + filename = "lib" + filename; + path_bin = folder + filename + ".so"; + } +#endif + using namespace std::filesystem; + path temp_path_bin = create_temporary_directory() / path(path_bin).filename(); + copy(path_bin, temp_path_bin); + path_bin = temp_path_bin.string(); + +#ifdef WIN32 + hGetProcIDDLL = LoadLibrary(path_bin.c_str()); +#else + hGetProcIDDLL = dlopen(path_bin.c_str(), RTLD_NOW); +#endif + if (hGetProcIDDLL == nullptr) { + throw std::runtime_error(zeno::format("Could not load the binary: {}", path_bin)); + } + data.init(hGetProcIDDLL); + + auto jDataBlock = root["dataBlock"]; + for (auto [key, value] : jDataBlock.items()) { + if (!cuda && key == "Slot") + continue; + data.resize(key, value.get(), singlePrecision); + } + + std::string path_resource = getAbsolutePath(pathConfig, root.at("resource")); + resource.init(hGetProcIDDLL, path_resource); + + load_plugs(root); + load_tasks(root); +} + +void Runtime::dirty(unsigned input) { + for (unsigned id_task : LUT_tasks_affected.at(input)) + tasks.at(id_task).dirty = true; +} + +std::vector Runtime::evaluate(unsigned id_output) { + std::vector outputs; + for (unsigned id_task : LUT_tasks_affecting.at(id_output)) { + ComputeTask &task = tasks.at(id_task); + if (task.dirty) { + (task.execute)(data.instance, resource.instance); + task.dirty = false; + outputs.insert(outputs.end(), task.outputs.begin(), task.outputs.end()); + } + } + return outputs; +} + +void Runtime::evaluate_all() { + for (ComputeTask &task : tasks) { + (task.execute)(data.instance, resource.instance); + task.dirty = false; + } +} + +void Runtime::load_plugs(const nlohmann::json &root) { + for (const auto &element : root["inputs"]) { + if (element.count("tasks")) + LUT_tasks_affected.push_back(element.at("tasks").get>()); + } + + for (const auto &element : root["outputs"]) { + if (element.count("tasks")) + LUT_tasks_affecting.push_back(element.at("tasks").get>()); + } +} + +void Runtime::load_tasks(const nlohmann::json &root) { + const auto &jTasks = root["computeTask"]; + for (auto element : jTasks) { + unsigned id_task = tasks.size(); + ComputeTask task{get_fn(hGetProcIDDLL, zeno::format("ComputeTask{}", id_task).c_str())}; + task.outputs = element.value>("outputs", {}); + tasks.push_back(task); + if (element.value("static", false)) + (task.execute)(data.instance, resource.instance); + } + if (!LUT_tasks_affected.empty()) // pre-computed + return; + + const unsigned num_inputs = root["inputs"].size(); + const unsigned num_outputs = root["outputs"].size(); + + LUT_tasks_affected.resize(num_inputs); + const unsigned num_tasks = jTasks.size(); + std::vector> relative_inputs_of_output(num_outputs); + std::vector> inputs_of_task(num_tasks); + for (unsigned id_task = 0; id_task != num_tasks; ++id_task) { + const nlohmann::json &element = jTasks[id_task]; + std::vector task_affecings = element["inputs"]; + inputs_of_task[id_task] = {task_affecings.begin(), task_affecings.end()}; + if (element.count("outputs")) { + for (unsigned id_data : element["outputs"]) { + unsigned id_output = id_data - num_inputs; + for (unsigned id_input : task_affecings) + relative_inputs_of_output[id_output].insert(id_input); + } + } + for (unsigned id_input : task_affecings) { + LUT_tasks_affected[id_input].push_back(id_task); + } + } + + LUT_tasks_affecting.resize(num_outputs); + for (unsigned id_output = 0; id_output != num_outputs; ++id_output) { + std::set given_inputs = relative_inputs_of_output.at(id_output); + + // tasks depending on inputs + std::set candidate_tasks; + for (unsigned id_input : given_inputs) { + for (unsigned id_task : LUT_tasks_affected.at(id_input)) { + candidate_tasks.insert(id_task); + } + } + + std::set tasks; + for (unsigned id_task : candidate_tasks) { + std::set task_inputs = inputs_of_task.at(id_task); + // only tasks satisfying "inputs no-expanding" condition can be executed + if (std::includes(given_inputs.begin(), given_inputs.end(), task_inputs.begin(), task_inputs.end())) + tasks.insert(id_task); + } + LUT_tasks_affecting[id_output] = {tasks.begin(), tasks.end()}; + } +} + +std::string expandEnvironmentVariables(const std::string &s) { + if (s.find("${") == std::string::npos) + return s; + + std::string pre = s.substr(0, s.find("${")); + std::string post = s.substr(s.find("${") + 2); + + if (post.find('}') == std::string::npos) + return s; + + std::string variable = post.substr(0, post.find('}')); + std::string value = ""; + + post = post.substr(post.find('}') + 1); + + const char *v = getenv(variable.c_str()); + if (v != NULL) + value = std::string(v); + + return expandEnvironmentVariables(pre + value + expandEnvironmentVariables(post)); +} + +Runtime::~Runtime() { + if (hGetProcIDDLL == 0) + return; + + data.cleanup(); + resource.cleanup(); + + std::string error; +#ifdef WIN32 + if (0 == FreeLibrary(hGetProcIDDLL)) { + DWORD errorMessageID = GetLastError(); + if (errorMessageID != NULL) { + LPSTR messageBuffer = nullptr; + size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, errorMessageID, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); + error = std::string(messageBuffer, size); + LocalFree(messageBuffer); + } + } +#else + if (dlclose(hGetProcIDDLL) != 0) + error = dlerror(); +#endif + if (!error.empty()) + std::cerr << zeno::format("[Nemo]Free binary failed: {}", error) << std::endl; +} + +std::string getAbsolutePath(std::string parent, std::string path) { + std::filesystem::path p{expandEnvironmentVariables(path)}; + if (p.has_root_path()) + return p.string(); + auto base = std::filesystem::path(parent).parent_path(); + return std::filesystem::absolute(base / p).string(); +} +} // namespace nemo diff --git a/projects/Nemo/Runtime.h b/projects/Nemo/Runtime.h new file mode 100644 index 0000000000..2f7ff5309e --- /dev/null +++ b/projects/Nemo/Runtime.h @@ -0,0 +1,77 @@ +/* + * MIT License + * + * Copyright (c) 2024 wuzhen + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * 1. The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * 2. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#pragma once + +#include "Context.h" +#include + +namespace nemo { +struct ComputeTask { + using Execute = void (*)(void *, void *); + explicit ComputeTask(Execute execute) : execute(execute) {} + bool dirty = true; + Execute execute; + std::vector outputs; +}; + +struct Runtime { + bool singlePrecision = true; + bool cuda = false; + + DataStorage data; + ResourcePool resource; + + // compiled binary handle + MOD hGetProcIDDLL = nullptr; + + std::vector tasks; + + // key: input id + // value: tasks affected by this input + std::vector> LUT_tasks_affected; + + // key: output id + // value: tasks affecting this input + std::vector> LUT_tasks_affecting; + +public: + ~Runtime(); + + void init(const nlohmann::json &config, std::string pathConfig); + + void dirty(unsigned input); + + std::vector evaluate(unsigned output); + + void evaluate_all(); + +private: + void load_plugs(const nlohmann::json &root); + + void load_tasks(const nlohmann::json &root); +}; + +std::string getAbsolutePath(std::string parent, std::string path); +} // namespace nemo diff --git a/projects/Nemo/node.cpp b/projects/Nemo/node.cpp new file mode 100644 index 0000000000..fab29bbad8 --- /dev/null +++ b/projects/Nemo/node.cpp @@ -0,0 +1,303 @@ +// +// Created by zh on 2024/9/18. +// +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Evaluate.h" +#include "zeno/utils/log.h" +#include "zeno/types/ListObject.h" +#include "zeno/funcs/PrimitiveUtils.h" + +namespace zeno { +namespace zeno_nemo { +struct NemoObject : PrimitiveObject { + std::unique_ptr evaluator; +}; + +struct NemoEvaluator : INode { + virtual void apply() override { + auto path_config = get_input2("Nemo Config"); + auto path_anim = get_input2("Animation"); + auto nemo = std::make_shared(); + nemo->evaluator = std::make_unique(path_config, path_anim); + nemo->userData().set2("path_config", path_config); + nemo->userData().set2("path_anim", path_anim); + set_output2("Evaluator", nemo); + } +}; + +ZENDEFNODE(NemoEvaluator, { + { + { "readpath", "Nemo Config" }, + { "readpath", "Animation" }, + }, + { + "Evaluator", + }, + {}, + { "Nemo" }, +}); + +static std::shared_ptr nemo_split_by_name(std::shared_ptr prim, bool add_when_none) { + auto list = std::make_shared(); + if (prim->verts.size() == 0) { + return list; + } + int faceset_count = prim->userData().get2("faceset_count"); + if (add_when_none && faceset_count == 0) { + auto name = prim->userData().get2("abcpath_0"); + prim_set_faceset(prim.get(), name); + faceset_count = 1; + } + std::map> faceset_map; + for (auto f = 0; f < faceset_count; f++) { + faceset_map[f] = {}; + } + if (prim->polys.size()) { + auto &faceset = prim->polys.add_attr("faceset"); + for (auto j = 0; j < faceset.size(); j++) { + auto f = faceset[j]; + faceset_map[f].push_back(j); + } + for (auto f = 0; f < faceset_count; f++) { + auto name = prim->userData().get2(zeno::format("faceset_{}", f)); + auto new_prim = std::dynamic_pointer_cast(prim->clone()); + new_prim->polys.resize(faceset_map[f].size()); + for (auto i = 0; i < faceset_map[f].size(); i++) { + new_prim->polys[i] = prim->polys[faceset_map[f][i]]; + } + new_prim->polys.foreach_attr([&](auto const &key, auto &arr) { + using T = std::decay_t; + auto &attr = prim->polys.attr(key); + for (auto i = 0; i < arr.size(); i++) { + arr[i] = attr[faceset_map[f][i]]; + } + }); + for (auto j = 0; j < faceset_count; j++) { + new_prim->userData().del(zeno::format("faceset_{}", j)); + } + prim_set_faceset(new_prim.get(), name); + list->arr.push_back(new_prim); + } + } + return list; +} + +struct NemoPlay : INode { + virtual void apply() override { + auto evaluator = get_input2("Evaluator"); + if (!evaluator) { + // TODO: return error(); + } + float frame = getGlobalState()->frameid; + if (has_input("frameid")) { + frame = get_input2("frameid"); + } + evaluator->evaluator->evaluate(frame); + bool skipHiddenPrim = get_input2("skipHiddenPrim"); + std::map> mapping_mesh_to_faceset; + + auto readFaceset = get_input2("readFaceset"); + if (readFaceset) { + for (auto i = 0; i < evaluator->evaluator->facesets.size(); i++) { + auto const &faceset = evaluator->evaluator->facesets[i]; + for (auto [mesh_id, _]: faceset.members) { + if (mapping_mesh_to_faceset.count(mesh_id) == 0) { + mapping_mesh_to_faceset[mesh_id] = {}; + } + mapping_mesh_to_faceset[mesh_id].push_back(i); + } + } + } + + auto splitByFaceset = get_input2("splitByFaceset"); + auto killDeadVerts = get_input2("killDeadVerts"); + auto triangulate = get_input2("triangulate"); + + auto prims = std::make_shared(); + std::map meshes; + for (unsigned mesh_id = 0; mesh_id != evaluator->evaluator->meshes.size(); ++mesh_id) { + unsigned plug_id = evaluator->evaluator->meshes[mesh_id]; + if (skipHiddenPrim && evaluator->evaluator->isVisible(plug_id) == 0) { + continue; + } + + std::string path = evaluator->evaluator->LUT_path.at(plug_id); + boost::algorithm::replace_all(path, "|", "/"); + meshes[plug_id] = path; + std::vector points = evaluator->evaluator->getPoints(plug_id); + auto sub_prim = std::make_shared(); + sub_prim->verts.resize(points.size()); + for (auto i = 0; i < points.size(); i++) { + sub_prim->verts[i] = bit_cast(points[i]); + } + auto [counts, connection] = evaluator->evaluator->getTopo(plug_id); + sub_prim->loops.reserve(counts.size()); + for (auto i: connection) { + sub_prim->loops.push_back(i); + } + auto offset = 0; + sub_prim->polys.reserve(counts.size()); + for (auto i: counts) { + sub_prim->polys.emplace_back(offset, i); + offset += i; + } + auto [uValues, vValues, uvIds] = evaluator->evaluator->getDefaultUV(plug_id); + if (uvIds.size() == connection.size()) { + auto &uvs = sub_prim->loops.add_attr("uvs"); + for (auto i = 0; i < uvs.size(); i++) { + uvs[i] = uvIds[i]; + } + sub_prim->uvs.reserve(uValues.size()); + for (auto i = 0; i < uValues.size(); i++) { + sub_prim->uvs.emplace_back(uValues[i], vValues[i]); + } + } + std::vector faceSetNames; + if (readFaceset) { + auto &faceset_attr = sub_prim->polys.add_attr("faceset"); + std::fill(faceset_attr.begin(), faceset_attr.end(), -1); + if (mapping_mesh_to_faceset.count(plug_id)) { + for (auto fi: mapping_mesh_to_faceset[plug_id]) { + auto &faceset = evaluator->evaluator->facesets[fi]; + auto cur_index = faceSetNames.size(); + faceSetNames.push_back(faceset.name); + for (auto i: faceset.members[plug_id]) { + faceset_attr[i] = cur_index; + } + } + } + for (auto i = 0; i < faceSetNames.size(); i++) { + auto n = faceSetNames[i]; + sub_prim->userData().set2(zeno::format("faceset_{}", i), n); + } + sub_prim->userData().set2("faceset_count", int(faceSetNames.size())); + } + prim_set_abcpath(sub_prim.get(), "/ABC"+path); + sub_prim->userData().set2("vis", int(evaluator->evaluator->isVisible(plug_id))); + if (splitByFaceset && faceSetNames.size() >= 2) { + auto list = nemo_split_by_name(sub_prim, true); + for (auto p: list->arr) { + auto np = std::dynamic_pointer_cast(p); + if (killDeadVerts) { + primKillDeadVerts(np.get()); + } + prims->arr.emplace_back(p); + } + } + else { + prims->arr.emplace_back(sub_prim); + } + } + + if (triangulate) { + for (auto &prim: prims->arr) { + auto _prim = std::dynamic_pointer_cast(prim); + zeno::primTriangulate(_prim.get()); + } + } + + set_output2("prims", prims); + } +}; + + +ZENDEFNODE(NemoPlay, { + { + { "Evaluator" }, + { "frame" }, + { "bool", "skipHiddenPrim", "1" }, + { "bool", "readFaceset", "1" }, + { "bool", "splitByFaceset", "0"}, + { "bool", "triangulate", "0"}, + { "bool", "killDeadVerts", "1"}, + }, + { + "prims", + }, + {}, + { "Nemo" }, +}); + +struct NemoPrimListFilter : INode { + virtual void apply() override { + auto prims = get_input2("prims"); + auto pathInclude = zeno::split_str(get_input2("pathInclude"), {' ', '\n'}); + auto pathExclude = zeno::split_str(get_input2("pathExclude"), {' ', '\n'}); + auto facesetInclude = zeno::split_str(get_input2("facesetInclude"), {' ', '\n'}); + auto facesetExclude = zeno::split_str(get_input2("facesetExclude"), {' ', '\n'}); + for (auto it = prims->arr.begin(); it != prims->arr.end();) { + auto np = std::dynamic_pointer_cast(*it); + auto abc_path = np->userData().template get2("abcpath_0"); + bool contain = false; + if (pathInclude.empty()) { + contain = true; + } + else { + for (const auto & p: pathInclude) { + if (starts_with(abc_path, p)) { + contain = true; + } + } + } + if (contain) { + for (const auto & p: pathExclude) { + if (starts_with(abc_path, p)) { + contain = false; + } + } + } + if (contain && np->userData().template has("faceset_0")) { + auto faceset = np->userData().template get2("faceset_0"); + contain = false; + if (facesetInclude.empty()) { + contain = true; + } + else { + for (const auto & p: facesetInclude) { + if (starts_with(faceset, p)) { + contain = true; + } + } + } + if (contain) { + for (const auto & p: facesetExclude) { + if (starts_with(faceset, p)) { + contain = false; + } + } + } + } + if (contain) { + ++it; + } else { + it = prims->arr.erase(it); + } + } + set_output2("prims", prims); + } +}; + +ZENDEFNODE(NemoPrimListFilter, { + { + "prims", + { "string", "pathInclude", "" }, + { "string", "pathExclude", "" }, + { "string", "facesetInclude", "" }, + { "string", "facesetExclude", "" }, + }, + { + "prims", + }, + {}, + { "Nemo" }, +}); +} +}