/* * 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 "scene/pointcloud.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(geom); size = mesh->get_verts().size() + mesh->get_num_ngons(); if (prim == ATTR_PRIM_SUBD) { size -= mesh->get_num_subd_verts(); } } else if (geom->geometry_type == Geometry::POINTCLOUD) { PointCloud *pointcloud = static_cast(geom); size = pointcloud->num_points(); } break; case ATTR_ELEMENT_VERTEX_MOTION: if (geom->geometry_type == Geometry::MESH) { Mesh *mesh = static_cast(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); } } else if (geom->geometry_type == Geometry::POINTCLOUD) { PointCloud *pointcloud = static_cast(geom); size = pointcloud->num_points() * (pointcloud->get_motion_steps() - 1); } break; case ATTR_ELEMENT_FACE: if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { Mesh *mesh = static_cast(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(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(geom); size = hair->num_curves(); } break; case ATTR_ELEMENT_CURVE_KEY: if (geom->geometry_type == Geometry::HAIR) { Hair *hair = static_cast(geom); size = hair->get_curve_keys().size(); } break; case ATTR_ELEMENT_CURVE_KEY_MOTION: if (geom->geometry_type == Geometry::HAIR) { Hair *hair = static_cast(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_POINT_RANDOM: return "point_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; } if (attr.type == TypeFloat4 || attr.type == TypeRGBA || attr.type == TypeDesc::TypeMatrix) { return AttrKernelDataType::FLOAT4; } return AttrKernelDataType::FLOAT3; } void Attribute::get_uv_tiles(Geometry *geom, AttributePrimitive prim, unordered_set &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::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::POINTCLOUD) { switch (std) { case ATTR_STD_UV: attr = add(name, TypeFloat2, ATTR_ELEMENT_VERTEX); break; case ATTR_STD_GENERATED: attr = add(name, TypeDesc::TypePoint, ATTR_ELEMENT_VERTEX); break; case ATTR_STD_MOTION_VERTEX_POSITION: attr = add(name, TypeDesc::TypeFloat4, ATTR_ELEMENT_VERTEX_MOTION); break; case ATTR_STD_POINT_RANDOM: attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_VERTEX); break; case ATTR_STD_GENERATED_TRANSFORM: attr = add(name, TypeDesc::TypeMatrix, ATTR_ELEMENT_MESH); 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::TypeFloat4, 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::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::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::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::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