From 226dc75e7791fae379c5b1b3da5a1267f72277f9 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Sat, 7 May 2016 20:43:22 +0200 Subject: [PATCH] Code refactor: add generic Cycles XML node read and write functions. Differential Revision: https://developer.blender.org/D2016 --- intern/cycles/graph/CMakeLists.txt | 2 + intern/cycles/graph/node_xml.cpp | 444 +++++++++++++++++++++++++++++ intern/cycles/graph/node_xml.h | 35 +++ 3 files changed, 481 insertions(+) create mode 100644 intern/cycles/graph/node_xml.cpp create mode 100644 intern/cycles/graph/node_xml.h diff --git a/intern/cycles/graph/CMakeLists.txt b/intern/cycles/graph/CMakeLists.txt index 401bdb47392..4ea18728f1c 100644 --- a/intern/cycles/graph/CMakeLists.txt +++ b/intern/cycles/graph/CMakeLists.txt @@ -7,12 +7,14 @@ set(INC set(SRC node.cpp node_type.cpp + node_xml.cpp ) set(SRC_HEADERS node.h node_enum.h node_type.h + node_xml.h ) include_directories(${INC}) diff --git a/intern/cycles/graph/node_xml.cpp b/intern/cycles/graph/node_xml.cpp new file mode 100644 index 00000000000..6a85c66fd8b --- /dev/null +++ b/intern/cycles/graph/node_xml.cpp @@ -0,0 +1,444 @@ +/* + * Copyright 2011-2016 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 "node_xml.h" + +#include "util_foreach.h" +#include "util_string.h" +#include "util_transform.h" + +CCL_NAMESPACE_BEGIN + +static bool xml_read_boolean(const char *value) +{ + return string_iequals(value, "true") || (atoi(value) != 0); +} + +static const char *xml_write_boolean(bool value) +{ + return (value) ? "true" : "false"; +} + +template +static void xml_read_float_array(T& value, pugi::xml_attribute attr) +{ + vector tokens; + string_split(tokens, attr.value()); + + if(tokens.size() % VECTOR_SIZE != 0) { + return; + } + + value.resize(tokens.size() / VECTOR_SIZE); + for(size_t i = 0; i < value.size(); i++) { + float *value_float = (float*)&value[i]; + + for(size_t j = 0; j < VECTOR_SIZE; j++) + value_float[j] = (float)atof(tokens[i * VECTOR_SIZE + j].c_str()); + } +} + +void xml_read_node(XMLReader& reader, Node *node, pugi::xml_node xml_node) +{ + pugi::xml_attribute name_attr = xml_node.attribute("name"); + if(name_attr) { + node->name = ustring(name_attr.value()); + } + + foreach(const NodeType::SocketMap::value_type& it, node->type->inputs) { + const SocketType& socket = it.second; + + if(socket.type == SocketType::CLOSURE || socket.type == SocketType::UNDEFINED) { + continue; + } + if(socket.flags & SocketType::INTERNAL) { + continue; + } + + pugi::xml_attribute attr = xml_node.attribute(socket.name.c_str()); + + if(!attr) { + continue; + } + + switch(socket.type) + { + case SocketType::BOOLEAN: + { + node->set(socket, xml_read_boolean(attr.value())); + break; + } + case SocketType::BOOLEAN_ARRAY: + { + vector tokens; + string_split(tokens, attr.value()); + + array value; + value.resize(tokens.size()); + for(size_t i = 0; i < value.size(); i++) + value[i] = xml_read_boolean(tokens[i].c_str()); + node->set(socket, value); + break; + } + case SocketType::FLOAT: + { + node->set(socket, (float)atof(attr.value())); + break; + } + case SocketType::FLOAT_ARRAY: + { + array value; + xml_read_float_array<1>(value, attr); + node->set(socket, value); + break; + } + case SocketType::INT: + { + node->set(socket, (int)atoi(attr.value())); + break; + } + case SocketType::INT_ARRAY: + { + vector tokens; + string_split(tokens, attr.value()); + + array value; + value.resize(tokens.size()); + for(size_t i = 0; i < value.size(); i++) + value[i] = (int)atoi(attr.value()); + node->set(socket, value); + break; + } + case SocketType::COLOR: + case SocketType::VECTOR: + case SocketType::POINT: + case SocketType::NORMAL: + { + array value; + xml_read_float_array<3>(value, attr); + if(value.size() == 1) { + node->set(socket, value[0]); + } + break; + } + case SocketType::COLOR_ARRAY: + case SocketType::VECTOR_ARRAY: + case SocketType::POINT_ARRAY: + case SocketType::NORMAL_ARRAY: + { + array value; + xml_read_float_array<3>(value, attr); + node->set(socket, value); + break; + } + case SocketType::POINT2: + { + array value; + xml_read_float_array<2>(value, attr); + if(value.size() == 1) { + node->set(socket, value[0]); + } + break; + } + case SocketType::POINT2_ARRAY: + { + array value; + xml_read_float_array<2>(value, attr); + node->set(socket, value); + break; + } + case SocketType::STRING: + case SocketType::ENUM: + { + node->set(socket, attr.value()); + break; + } + case SocketType::STRING_ARRAY: + { + vector tokens; + string_split(tokens, attr.value()); + + array value; + value.resize(tokens.size()); + for(size_t i = 0; i < value.size(); i++) + value[i] = ustring(tokens[i]); + node->set(socket, value); + break; + } + case SocketType::TRANSFORM: + { + array value; + xml_read_float_array<16>(value, attr); + if(value.size() == 1) { + node->set(socket, value[0]); + } + break; + } + case SocketType::TRANSFORM_ARRAY: + { + array value; + xml_read_float_array<16>(value, attr); + node->set(socket, value); + break; + } + case SocketType::NODE: + { + ustring value(attr.value()); + map::iterator it = reader.node_map.find(value); + if(it != reader.node_map.end()) + { + Node *value_node = it->second; + if(value_node->type == *(socket.node_type)) + node->set(socket, it->second); + } + break; + } + case SocketType::NODE_ARRAY: + { + vector tokens; + string_split(tokens, attr.value()); + + array value; + value.resize(tokens.size()); + for(size_t i = 0; i < value.size(); i++) + { + map::iterator it = reader.node_map.find(ustring(tokens[i])); + if(it != reader.node_map.end()) + { + Node *value_node = it->second; + value[i] = (value_node->type == *(socket.node_type)) ? value_node : NULL; + } + else + { + value[i] = NULL; + } + } + node->set(socket, value); + break; + } + case SocketType::CLOSURE: + case SocketType::UNDEFINED: + break; + } + } + + if(node->name) + reader.node_map[node->name] = node; +} + +pugi::xml_node xml_write_node(Node *node, pugi::xml_node xml_root) +{ + pugi::xml_node xml_node = xml_root.append_child(node->type->name.c_str()); + + xml_node.append_attribute("name") = node->name.c_str(); + + foreach(const NodeType::SocketMap::value_type& it, node->type->inputs) { + const SocketType& socket = it.second; + + if(socket.type == SocketType::CLOSURE || socket.type == SocketType::UNDEFINED) { + continue; + } + if(socket.flags & SocketType::INTERNAL) { + continue; + } + if(node->has_default_value(socket)) { + continue; + } + + pugi::xml_attribute attr = xml_node.append_attribute(socket.name.c_str()); + + switch(socket.type) + { + case SocketType::BOOLEAN: + { + attr = xml_write_boolean(node->get_bool(socket)); + break; + } + case SocketType::BOOLEAN_ARRAY: + { + std::stringstream ss; + const array& value = node->get_bool_array(socket); + for(size_t i = 0; i < value.size(); i++) { + ss << xml_write_boolean(value[i]); + if(i != value.size() - 1) + ss << " "; + } + attr = ss.str().c_str(); + break; + } + case SocketType::FLOAT: + { + attr = node->get_float(socket); + break; + } + case SocketType::FLOAT_ARRAY: + { + std::stringstream ss; + const array& value = node->get_float_array(socket); + for(size_t i = 0; i < value.size(); i++) { + ss << value[i]; + if(i != value.size() - 1) { + ss << " "; + } + } + attr = ss.str().c_str(); + break; + } + case SocketType::INT: + { + attr = node->get_int(socket); + break; + } + case SocketType::INT_ARRAY: + { + std::stringstream ss; + const array& value = node->get_int_array(socket); + for(size_t i = 0; i < value.size(); i++) { + ss << value[i]; + if(i != value.size() - 1) { + ss << " "; + } + } + attr = ss.str().c_str(); + break; + } + case SocketType::COLOR: + case SocketType::VECTOR: + case SocketType::POINT: + case SocketType::NORMAL: + { + float3 value = node->get_float3(socket); + attr = string_printf("%g %g %g", value.x, value.y, value.z).c_str(); + break; + } + case SocketType::COLOR_ARRAY: + case SocketType::VECTOR_ARRAY: + case SocketType::POINT_ARRAY: + case SocketType::NORMAL_ARRAY: + { + std::stringstream ss; + const array& value = node->get_float3_array(socket); + for(size_t i = 0; i < value.size(); i++) { + ss << string_printf("%g %g %g", value[i].x, value[i].y, value[i].z); + if(i != value.size() - 1) { + ss << " "; + } + } + attr = ss.str().c_str(); + break; + } + case SocketType::POINT2: + { + float2 value = node->get_float2(socket); + attr = string_printf("%g %g", value.x, value.y).c_str(); + break; + } + case SocketType::POINT2_ARRAY: + { + std::stringstream ss; + const array& value = node->get_float2_array(socket); + for(size_t i = 0; i < value.size(); i++) { + ss << string_printf("%g %g", value[i].x, value[i].y); + if(i != value.size() - 1) { + ss << " "; + } + } + attr = ss.str().c_str(); + break; + } + case SocketType::STRING: + case SocketType::ENUM: + { + attr = node->get_string(socket).c_str(); + break; + } + case SocketType::STRING_ARRAY: + { + std::stringstream ss; + const array& value = node->get_string_array(socket); + for(size_t i = 0; i < value.size(); i++) { + ss << value[i]; + if(i != value.size() - 1) { + ss << " "; + } + } + attr = ss.str().c_str(); + break; + } + case SocketType::TRANSFORM: + { + Transform tfm = node->get_transform(socket); + std::stringstream ss; + for(int i = 0; i < 4; i++) { + ss << string_printf("%g %g %g %g", tfm[i][0], tfm[i][1], tfm[i][2], tfm[i][3]); + if(i != 3) { + ss << " "; + } + } + attr = ss.str().c_str(); + break; + } + case SocketType::TRANSFORM_ARRAY: + { + std::stringstream ss; + const array& value = node->get_transform_array(socket); + for(size_t j = 0; j < value.size(); j++) { + const Transform& tfm = value[j]; + + for(int i = 0; i < 4; i++) { + ss << string_printf("%g %g %g %g", tfm[i][0], tfm[i][1], tfm[i][2], tfm[i][3]); + if(j != value.size() - 1 || i != 3) { + ss << " "; + } + } + } + attr = ss.str().c_str(); + break; + } + case SocketType::NODE: + { + Node *value = node->get_node(socket); + if(value) { + attr = value->name.c_str(); + } + break; + } + case SocketType::NODE_ARRAY: + { + std::stringstream ss; + const array& value = node->get_node_array(socket); + for(size_t i = 0; i < value.size(); i++) { + if(value[i]) { + ss << value[i]->name.c_str(); + } + if(i != value.size() - 1) { + ss << " "; + } + } + attr = ss.str().c_str(); + break; + } + case SocketType::CLOSURE: + case SocketType::UNDEFINED: + break; + } + } + + return xml_node; +} + +CCL_NAMESPACE_END + diff --git a/intern/cycles/graph/node_xml.h b/intern/cycles/graph/node_xml.h new file mode 100644 index 00000000000..7494c5e6e55 --- /dev/null +++ b/intern/cycles/graph/node_xml.h @@ -0,0 +1,35 @@ +/* + * Copyright 2011-2016 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. + */ + +#pragma once + +#include "node.h" + +#include "util_map.h" +#include "util_string.h" +#include "util_xml.h" + +CCL_NAMESPACE_BEGIN + +struct XMLReader { + map node_map; +}; + +void xml_read_node(XMLReader& reader, Node *node, pugi::xml_node xml_node); +pugi::xml_node xml_write_node(Node *node, pugi::xml_node xml_root); + +CCL_NAMESPACE_END +