diff options
Diffstat (limited to 'intern/cycles/scene/attribute.cpp')
-rw-r--r-- | intern/cycles/scene/attribute.cpp | 896 |
1 files changed, 896 insertions, 0 deletions
diff --git a/intern/cycles/scene/attribute.cpp b/intern/cycles/scene/attribute.cpp new file mode 100644 index 00000000000..3401eea307f --- /dev/null +++ b/intern/cycles/scene/attribute.cpp @@ -0,0 +1,896 @@ +/* + * Copyright 2011-2013 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 "scene/attribute.h" +#include "scene/hair.h" +#include "scene/image.h" +#include "scene/mesh.h" + +#include "util/foreach.h" +#include "util/log.h" +#include "util/transform.h" + +CCL_NAMESPACE_BEGIN + +/* Attribute */ + +Attribute::Attribute( + ustring name, TypeDesc type, AttributeElement element, Geometry *geom, AttributePrimitive prim) + : name(name), std(ATTR_STD_NONE), type(type), element(element), flags(0), modified(true) +{ + /* string and matrix not supported! */ + assert(type == TypeDesc::TypeFloat || type == TypeDesc::TypeColor || + type == TypeDesc::TypePoint || type == TypeDesc::TypeVector || + type == TypeDesc::TypeNormal || type == TypeDesc::TypeMatrix || type == TypeFloat2 || + type == TypeFloat4 || type == TypeRGBA); + + if (element == ATTR_ELEMENT_VOXEL) { + buffer.resize(sizeof(ImageHandle)); + new (buffer.data()) ImageHandle(); + } + else { + resize(geom, prim, false); + } +} + +Attribute::~Attribute() +{ + /* For voxel data, we need to free the image handle. */ + if (element == ATTR_ELEMENT_VOXEL && buffer.size()) { + ImageHandle &handle = data_voxel(); + handle.~ImageHandle(); + } +} + +void Attribute::resize(Geometry *geom, AttributePrimitive prim, bool reserve_only) +{ + if (element != ATTR_ELEMENT_VOXEL) { + if (reserve_only) { + buffer.reserve(buffer_size(geom, prim)); + } + else { + buffer.resize(buffer_size(geom, prim), 0); + } + } +} + +void Attribute::resize(size_t num_elements) +{ + if (element != ATTR_ELEMENT_VOXEL) { + buffer.resize(num_elements * data_sizeof(), 0); + } +} + +void Attribute::add(const float &f) +{ + assert(data_sizeof() == sizeof(float)); + + char *data = (char *)&f; + size_t size = sizeof(f); + + for (size_t i = 0; i < size; i++) + buffer.push_back(data[i]); + + modified = true; +} + +void Attribute::add(const uchar4 &f) +{ + assert(data_sizeof() == sizeof(uchar4)); + + char *data = (char *)&f; + size_t size = sizeof(f); + + for (size_t i = 0; i < size; i++) + buffer.push_back(data[i]); + + modified = true; +} + +void Attribute::add(const float2 &f) +{ + assert(data_sizeof() == sizeof(float2)); + + char *data = (char *)&f; + size_t size = sizeof(f); + + for (size_t i = 0; i < size; i++) + buffer.push_back(data[i]); + + modified = true; +} + +void Attribute::add(const float3 &f) +{ + assert(data_sizeof() == sizeof(float3)); + + char *data = (char *)&f; + size_t size = sizeof(f); + + for (size_t i = 0; i < size; i++) + buffer.push_back(data[i]); + + modified = true; +} + +void Attribute::add(const Transform &f) +{ + assert(data_sizeof() == sizeof(Transform)); + + char *data = (char *)&f; + size_t size = sizeof(f); + + for (size_t i = 0; i < size; i++) + buffer.push_back(data[i]); + + modified = true; +} + +void Attribute::add(const char *data) +{ + size_t size = data_sizeof(); + + for (size_t i = 0; i < size; i++) + buffer.push_back(data[i]); + + modified = true; +} + +void Attribute::set_data_from(Attribute &&other) +{ + assert(other.std == std); + assert(other.type == type); + assert(other.element == element); + + this->flags = other.flags; + + if (this->buffer.size() != other.buffer.size()) { + this->buffer = std::move(other.buffer); + modified = true; + } + else if (memcmp(this->data(), other.data(), other.buffer.size()) != 0) { + this->buffer = std::move(other.buffer); + modified = true; + } +} + +size_t Attribute::data_sizeof() const +{ + if (element == ATTR_ELEMENT_VOXEL) + return sizeof(ImageHandle); + else if (element == ATTR_ELEMENT_CORNER_BYTE) + return sizeof(uchar4); + else if (type == TypeDesc::TypeFloat) + return sizeof(float); + else if (type == TypeFloat2) + return sizeof(float2); + else if (type == TypeDesc::TypeMatrix) + return sizeof(Transform); + else + return sizeof(float3); +} + +size_t Attribute::element_size(Geometry *geom, AttributePrimitive prim) const +{ + if (flags & ATTR_FINAL_SIZE) { + return buffer.size() / data_sizeof(); + } + + size_t size = 0; + + switch (element) { + case ATTR_ELEMENT_OBJECT: + case ATTR_ELEMENT_MESH: + case ATTR_ELEMENT_VOXEL: + size = 1; + break; + case ATTR_ELEMENT_VERTEX: + if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { + Mesh *mesh = static_cast<Mesh *>(geom); + size = mesh->get_verts().size() + mesh->get_num_ngons(); + if (prim == ATTR_PRIM_SUBD) { + size -= mesh->get_num_subd_verts(); + } + } + break; + case ATTR_ELEMENT_VERTEX_MOTION: + if (geom->geometry_type == Geometry::MESH) { + Mesh *mesh = static_cast<Mesh *>(geom); + DCHECK_GT(mesh->get_motion_steps(), 0); + size = (mesh->get_verts().size() + mesh->get_num_ngons()) * (mesh->get_motion_steps() - 1); + if (prim == ATTR_PRIM_SUBD) { + size -= mesh->get_num_subd_verts() * (mesh->get_motion_steps() - 1); + } + } + break; + case ATTR_ELEMENT_FACE: + if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { + Mesh *mesh = static_cast<Mesh *>(geom); + if (prim == ATTR_PRIM_GEOMETRY) { + size = mesh->num_triangles(); + } + else { + size = mesh->get_num_subd_faces() + mesh->get_num_ngons(); + } + } + break; + case ATTR_ELEMENT_CORNER: + case ATTR_ELEMENT_CORNER_BYTE: + if (geom->geometry_type == Geometry::MESH) { + Mesh *mesh = static_cast<Mesh *>(geom); + if (prim == ATTR_PRIM_GEOMETRY) { + size = mesh->num_triangles() * 3; + } + else { + size = mesh->get_subd_face_corners().size() + mesh->get_num_ngons(); + } + } + break; + case ATTR_ELEMENT_CURVE: + if (geom->geometry_type == Geometry::HAIR) { + Hair *hair = static_cast<Hair *>(geom); + size = hair->num_curves(); + } + break; + case ATTR_ELEMENT_CURVE_KEY: + if (geom->geometry_type == Geometry::HAIR) { + Hair *hair = static_cast<Hair *>(geom); + size = hair->get_curve_keys().size(); + } + break; + case ATTR_ELEMENT_CURVE_KEY_MOTION: + if (geom->geometry_type == Geometry::HAIR) { + Hair *hair = static_cast<Hair *>(geom); + DCHECK_GT(hair->get_motion_steps(), 0); + size = hair->get_curve_keys().size() * (hair->get_motion_steps() - 1); + } + break; + default: + break; + } + + return size; +} + +size_t Attribute::buffer_size(Geometry *geom, AttributePrimitive prim) const +{ + return element_size(geom, prim) * data_sizeof(); +} + +bool Attribute::same_storage(TypeDesc a, TypeDesc b) +{ + if (a == b) + return true; + + if (a == TypeDesc::TypeColor || a == TypeDesc::TypePoint || a == TypeDesc::TypeVector || + a == TypeDesc::TypeNormal) { + if (b == TypeDesc::TypeColor || b == TypeDesc::TypePoint || b == TypeDesc::TypeVector || + b == TypeDesc::TypeNormal) { + return true; + } + } + return false; +} + +void Attribute::zero_data(void *dst) +{ + memset(dst, 0, data_sizeof()); +} + +void Attribute::add_with_weight(void *dst, void *src, float weight) +{ + if (element == ATTR_ELEMENT_CORNER_BYTE) { + for (int i = 0; i < 4; i++) { + ((uchar *)dst)[i] += uchar(((uchar *)src)[i] * weight); + } + } + else if (same_storage(type, TypeDesc::TypeFloat)) { + *((float *)dst) += *((float *)src) * weight; + } + else if (same_storage(type, TypeFloat2)) { + *((float2 *)dst) += *((float2 *)src) * weight; + } + else if (same_storage(type, TypeDesc::TypeVector)) { + *((float4 *)dst) += *((float4 *)src) * weight; + } + else { + assert(!"not implemented for this type"); + } +} + +const char *Attribute::standard_name(AttributeStandard std) +{ + switch (std) { + case ATTR_STD_VERTEX_NORMAL: + return "N"; + case ATTR_STD_FACE_NORMAL: + return "Ng"; + case ATTR_STD_UV: + return "uv"; + case ATTR_STD_GENERATED: + return "generated"; + case ATTR_STD_GENERATED_TRANSFORM: + return "generated_transform"; + case ATTR_STD_UV_TANGENT: + return "tangent"; + case ATTR_STD_UV_TANGENT_SIGN: + return "tangent_sign"; + case ATTR_STD_VERTEX_COLOR: + return "vertex_color"; + case ATTR_STD_POSITION_UNDEFORMED: + return "undeformed"; + case ATTR_STD_POSITION_UNDISPLACED: + return "undisplaced"; + case ATTR_STD_MOTION_VERTEX_POSITION: + return "motion_P"; + case ATTR_STD_MOTION_VERTEX_NORMAL: + return "motion_N"; + case ATTR_STD_PARTICLE: + return "particle"; + case ATTR_STD_CURVE_INTERCEPT: + return "curve_intercept"; + case ATTR_STD_CURVE_LENGTH: + return "curve_length"; + case ATTR_STD_CURVE_RANDOM: + return "curve_random"; + case ATTR_STD_PTEX_FACE_ID: + return "ptex_face_id"; + case ATTR_STD_PTEX_UV: + return "ptex_uv"; + case ATTR_STD_VOLUME_DENSITY: + return "density"; + case ATTR_STD_VOLUME_COLOR: + return "color"; + case ATTR_STD_VOLUME_FLAME: + return "flame"; + case ATTR_STD_VOLUME_HEAT: + return "heat"; + case ATTR_STD_VOLUME_TEMPERATURE: + return "temperature"; + case ATTR_STD_VOLUME_VELOCITY: + return "velocity"; + case ATTR_STD_POINTINESS: + return "pointiness"; + case ATTR_STD_RANDOM_PER_ISLAND: + return "random_per_island"; + case ATTR_STD_SHADOW_TRANSPARENCY: + return "shadow_transparency"; + case ATTR_STD_NOT_FOUND: + case ATTR_STD_NONE: + case ATTR_STD_NUM: + return ""; + } + + return ""; +} + +AttributeStandard Attribute::name_standard(const char *name) +{ + if (name) { + for (int std = ATTR_STD_NONE; std < ATTR_STD_NUM; std++) { + if (strcmp(name, Attribute::standard_name((AttributeStandard)std)) == 0) { + return (AttributeStandard)std; + } + } + } + + return ATTR_STD_NONE; +} + +AttrKernelDataType Attribute::kernel_type(const Attribute &attr) +{ + if (attr.element == ATTR_ELEMENT_CORNER) { + return AttrKernelDataType::UCHAR4; + } + + if (attr.type == TypeDesc::TypeFloat) { + return AttrKernelDataType::FLOAT; + } + + if (attr.type == TypeFloat2) { + return AttrKernelDataType::FLOAT2; + } + + return AttrKernelDataType::FLOAT3; +} + +void Attribute::get_uv_tiles(Geometry *geom, + AttributePrimitive prim, + unordered_set<int> &tiles) const +{ + if (type != TypeFloat2) { + return; + } + + const int num = element_size(geom, prim); + const float2 *uv = data_float2(); + for (int i = 0; i < num; i++, uv++) { + float u = uv->x, v = uv->y; + int x = (int)u, y = (int)v; + + if (x < 0 || y < 0 || x >= 10) { + continue; + } + + /* Be conservative in corners - precisely touching the right or upper edge of a tile + * should not load its right/upper neighbor as well. */ + if (x > 0 && (u < x + 1e-6f)) { + x--; + } + if (y > 0 && (v < y + 1e-6f)) { + y--; + } + + tiles.insert(1001 + 10 * y + x); + } +} + +/* Attribute Set */ + +AttributeSet::AttributeSet(Geometry *geometry, AttributePrimitive prim) + : modified_flag(~0u), geometry(geometry), prim(prim) +{ +} + +AttributeSet::~AttributeSet() +{ +} + +Attribute *AttributeSet::add(ustring name, TypeDesc type, AttributeElement element) +{ + Attribute *attr = find(name); + + if (attr) { + /* return if same already exists */ + if (attr->type == type && attr->element == element) + return attr; + + /* overwrite attribute with same name but different type/element */ + remove(name); + } + + Attribute new_attr(name, type, element, geometry, prim); + attributes.emplace_back(std::move(new_attr)); + tag_modified(attributes.back()); + return &attributes.back(); +} + +Attribute *AttributeSet::find(ustring name) const +{ + foreach (const Attribute &attr, attributes) + if (attr.name == name) + return (Attribute *)&attr; + + return NULL; +} + +void AttributeSet::remove(ustring name) +{ + Attribute *attr = find(name); + + if (attr) { + list<Attribute>::iterator it; + + for (it = attributes.begin(); it != attributes.end(); it++) { + if (&*it == attr) { + remove(it); + return; + } + } + } +} + +Attribute *AttributeSet::add(AttributeStandard std, ustring name) +{ + Attribute *attr = NULL; + + if (name == ustring()) + name = Attribute::standard_name(std); + + if (geometry->geometry_type == Geometry::MESH) { + switch (std) { + case ATTR_STD_VERTEX_NORMAL: + attr = add(name, TypeDesc::TypeNormal, ATTR_ELEMENT_VERTEX); + break; + case ATTR_STD_FACE_NORMAL: + attr = add(name, TypeDesc::TypeNormal, ATTR_ELEMENT_FACE); + break; + case ATTR_STD_UV: + attr = add(name, TypeFloat2, ATTR_ELEMENT_CORNER); + break; + case ATTR_STD_UV_TANGENT: + attr = add(name, TypeDesc::TypeVector, ATTR_ELEMENT_CORNER); + break; + case ATTR_STD_UV_TANGENT_SIGN: + attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CORNER); + break; + case ATTR_STD_VERTEX_COLOR: + attr = add(name, TypeRGBA, ATTR_ELEMENT_CORNER_BYTE); + break; + case ATTR_STD_GENERATED: + case ATTR_STD_POSITION_UNDEFORMED: + case ATTR_STD_POSITION_UNDISPLACED: + attr = add(name, TypeDesc::TypePoint, ATTR_ELEMENT_VERTEX); + break; + case ATTR_STD_MOTION_VERTEX_POSITION: + attr = add(name, TypeDesc::TypePoint, ATTR_ELEMENT_VERTEX_MOTION); + break; + case ATTR_STD_MOTION_VERTEX_NORMAL: + attr = add(name, TypeDesc::TypeNormal, ATTR_ELEMENT_VERTEX_MOTION); + break; + case ATTR_STD_PTEX_FACE_ID: + attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_FACE); + break; + case ATTR_STD_PTEX_UV: + attr = add(name, TypeDesc::TypePoint, ATTR_ELEMENT_VERTEX); + break; + case ATTR_STD_GENERATED_TRANSFORM: + attr = add(name, TypeDesc::TypeMatrix, ATTR_ELEMENT_MESH); + break; + case ATTR_STD_POINTINESS: + attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VERTEX); + break; + case ATTR_STD_RANDOM_PER_ISLAND: + attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_FACE); + break; + default: + assert(0); + break; + } + } + else if (geometry->geometry_type == Geometry::VOLUME) { + switch (std) { + case ATTR_STD_VERTEX_NORMAL: + attr = add(name, TypeDesc::TypeNormal, ATTR_ELEMENT_VERTEX); + break; + case ATTR_STD_FACE_NORMAL: + attr = add(name, TypeDesc::TypeNormal, ATTR_ELEMENT_FACE); + break; + case ATTR_STD_VOLUME_DENSITY: + case ATTR_STD_VOLUME_FLAME: + case ATTR_STD_VOLUME_HEAT: + case ATTR_STD_VOLUME_TEMPERATURE: + attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VOXEL); + break; + case ATTR_STD_VOLUME_COLOR: + attr = add(name, TypeDesc::TypeColor, ATTR_ELEMENT_VOXEL); + break; + case ATTR_STD_VOLUME_VELOCITY: + attr = add(name, TypeDesc::TypeVector, ATTR_ELEMENT_VOXEL); + break; + default: + assert(0); + break; + } + } + else if (geometry->geometry_type == Geometry::HAIR) { + switch (std) { + case ATTR_STD_UV: + attr = add(name, TypeFloat2, ATTR_ELEMENT_CURVE); + break; + case ATTR_STD_GENERATED: + attr = add(name, TypeDesc::TypePoint, ATTR_ELEMENT_CURVE); + break; + case ATTR_STD_MOTION_VERTEX_POSITION: + attr = add(name, TypeDesc::TypePoint, ATTR_ELEMENT_CURVE_KEY_MOTION); + break; + case ATTR_STD_CURVE_INTERCEPT: + attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE_KEY); + break; + case ATTR_STD_CURVE_LENGTH: + attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE); + break; + case ATTR_STD_CURVE_RANDOM: + attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE); + break; + case ATTR_STD_GENERATED_TRANSFORM: + attr = add(name, TypeDesc::TypeMatrix, ATTR_ELEMENT_MESH); + break; + case ATTR_STD_POINTINESS: + attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VERTEX); + break; + case ATTR_STD_RANDOM_PER_ISLAND: + attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_FACE); + break; + case ATTR_STD_SHADOW_TRANSPARENCY: + attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE_KEY); + break; + default: + assert(0); + break; + } + } + + attr->std = std; + + return attr; +} + +Attribute *AttributeSet::find(AttributeStandard std) const +{ + foreach (const Attribute &attr, attributes) + if (attr.std == std) + return (Attribute *)&attr; + + return NULL; +} + +void AttributeSet::remove(AttributeStandard std) +{ + Attribute *attr = find(std); + + if (attr) { + list<Attribute>::iterator it; + + for (it = attributes.begin(); it != attributes.end(); it++) { + if (&*it == attr) { + remove(it); + return; + } + } + } +} + +Attribute *AttributeSet::find(AttributeRequest &req) +{ + if (req.std == ATTR_STD_NONE) + return find(req.name); + else + return find(req.std); +} + +void AttributeSet::remove(Attribute *attribute) +{ + if (attribute->std == ATTR_STD_NONE) { + remove(attribute->name); + } + else { + remove(attribute->std); + } +} + +void AttributeSet::remove(list<Attribute>::iterator it) +{ + tag_modified(*it); + attributes.erase(it); +} + +void AttributeSet::resize(bool reserve_only) +{ + foreach (Attribute &attr, attributes) { + attr.resize(geometry, prim, reserve_only); + } +} + +void AttributeSet::clear(bool preserve_voxel_data) +{ + if (preserve_voxel_data) { + list<Attribute>::iterator it; + + for (it = attributes.begin(); it != attributes.end();) { + if (it->element == ATTR_ELEMENT_VOXEL || it->std == ATTR_STD_GENERATED_TRANSFORM) { + it++; + } + else { + attributes.erase(it++); + } + } + } + else { + attributes.clear(); + } +} + +void AttributeSet::update(AttributeSet &&new_attributes) +{ + /* add or update old_attributes based on the new_attributes */ + foreach (Attribute &attr, new_attributes.attributes) { + Attribute *nattr = add(attr.name, attr.type, attr.element); + nattr->std = attr.std; + nattr->set_data_from(std::move(attr)); + } + + /* remove any attributes not on new_attributes */ + list<Attribute>::iterator it; + for (it = attributes.begin(); it != attributes.end();) { + if (it->std != ATTR_STD_NONE) { + if (new_attributes.find(it->std) == nullptr) { + remove(it++); + continue; + } + } + else if (it->name != "") { + if (new_attributes.find(it->name) == nullptr) { + remove(it++); + continue; + } + } + + it++; + } + + /* If all attributes were replaced, transform is no longer applied. */ + geometry->transform_applied = false; +} + +void AttributeSet::clear_modified() +{ + foreach (Attribute &attr, attributes) { + attr.modified = false; + } + + modified_flag = 0; +} + +void AttributeSet::tag_modified(const Attribute &attr) +{ + /* Some attributes are not stored in the various kernel attribute arrays + * (DeviceScene::attribute_*), so the modified flags are only set if the associated standard + * corresponds to an attribute which will be stored in the kernel's attribute arrays. */ + const bool modifies_device_array = (attr.std != ATTR_STD_FACE_NORMAL && + attr.std != ATTR_STD_VERTEX_NORMAL); + + if (modifies_device_array) { + AttrKernelDataType kernel_type = Attribute::kernel_type(attr); + modified_flag |= (1u << kernel_type); + } +} + +bool AttributeSet::modified(AttrKernelDataType kernel_type) const +{ + return (modified_flag & (1u << kernel_type)) != 0; +} + +/* AttributeRequest */ + +AttributeRequest::AttributeRequest(ustring name_) +{ + name = name_; + std = ATTR_STD_NONE; + + type = TypeDesc::TypeFloat; + desc.element = ATTR_ELEMENT_NONE; + desc.offset = 0; + desc.type = NODE_ATTR_FLOAT; + + subd_type = TypeDesc::TypeFloat; + subd_desc.element = ATTR_ELEMENT_NONE; + subd_desc.offset = 0; + subd_desc.type = NODE_ATTR_FLOAT; +} + +AttributeRequest::AttributeRequest(AttributeStandard std_) +{ + name = ustring(); + std = std_; + + type = TypeDesc::TypeFloat; + desc.element = ATTR_ELEMENT_NONE; + desc.offset = 0; + desc.type = NODE_ATTR_FLOAT; + + subd_type = TypeDesc::TypeFloat; + subd_desc.element = ATTR_ELEMENT_NONE; + subd_desc.offset = 0; + subd_desc.type = NODE_ATTR_FLOAT; +} + +/* AttributeRequestSet */ + +AttributeRequestSet::AttributeRequestSet() +{ +} + +AttributeRequestSet::~AttributeRequestSet() +{ +} + +bool AttributeRequestSet::modified(const AttributeRequestSet &other) +{ + if (requests.size() != other.requests.size()) + return true; + + for (size_t i = 0; i < requests.size(); i++) { + bool found = false; + + for (size_t j = 0; j < requests.size() && !found; j++) + if (requests[i].name == other.requests[j].name && requests[i].std == other.requests[j].std) { + found = true; + } + + if (!found) { + return true; + } + } + + return false; +} + +void AttributeRequestSet::add(ustring name) +{ + foreach (AttributeRequest &req, requests) { + if (req.name == name) { + return; + } + } + + requests.push_back(AttributeRequest(name)); +} + +void AttributeRequestSet::add(AttributeStandard std) +{ + foreach (AttributeRequest &req, requests) + if (req.std == std) + return; + + requests.push_back(AttributeRequest(std)); +} + +void AttributeRequestSet::add(AttributeRequestSet &reqs) +{ + foreach (AttributeRequest &req, reqs.requests) { + if (req.std == ATTR_STD_NONE) + add(req.name); + else + add(req.std); + } +} + +void AttributeRequestSet::add_standard(ustring name) +{ + if (name.empty()) { + return; + } + + AttributeStandard std = Attribute::name_standard(name.c_str()); + + if (std) { + add(std); + } + else { + add(name); + } +} + +bool AttributeRequestSet::find(ustring name) +{ + foreach (AttributeRequest &req, requests) + if (req.name == name) + return true; + + return false; +} + +bool AttributeRequestSet::find(AttributeStandard std) +{ + foreach (AttributeRequest &req, requests) + if (req.std == std) + return true; + + return false; +} + +size_t AttributeRequestSet::size() +{ + return requests.size(); +} + +void AttributeRequestSet::clear() +{ + requests.clear(); +} + +CCL_NAMESPACE_END |