Merge branch 'master' into blender2.8
Conflicts: source/blender/editors/animation/anim_draw.c
This commit is contained in:
@@ -447,6 +447,7 @@ option(WITH_BOOST "Enable features depending on boost" ON)
|
||||
|
||||
# Unit testsing
|
||||
option(WITH_GTESTS "Enable GTest unit testing" OFF)
|
||||
option(WITH_OPENGL_TESTS "Enable OpenGL related unit testing (Experimental)" OFF)
|
||||
|
||||
|
||||
# Documentation
|
||||
|
||||
@@ -74,6 +74,7 @@ elseif(CMAKE_COMPILER_IS_GNUCC)
|
||||
if(CXX_HAS_AVX2)
|
||||
set(CYCLES_AVX2_KERNEL_FLAGS "-ffast-math -msse -msse2 -msse3 -mssse3 -msse4.1 -mavx -mavx2 -mfma -mlzcnt -mbmi -mbmi2 -mf16c -mfpmath=sse")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffast-math -fno-finite-math-only")
|
||||
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
check_cxx_compiler_flag(-msse CXX_HAS_SSE)
|
||||
check_cxx_compiler_flag(-mavx CXX_HAS_AVX)
|
||||
@@ -89,6 +90,7 @@ elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
if(CXX_HAS_AVX2)
|
||||
set(CYCLES_AVX2_KERNEL_FLAGS "-ffast-math -msse -msse2 -msse3 -mssse3 -msse4.1 -mavx -mavx2 -mfma -mlzcnt -mbmi -mbmi2 -mf16c")
|
||||
endif()
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffast-math -fno-finite-math-only")
|
||||
endif()
|
||||
|
||||
if(CXX_HAS_SSE)
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "subd_patch.h"
|
||||
#include "subd_split.h"
|
||||
|
||||
#include "util_algorithm.h"
|
||||
#include "util_foreach.h"
|
||||
#include "util_logging.h"
|
||||
#include "util_math.h"
|
||||
@@ -525,69 +526,177 @@ static void attr_create_uv_map(Scene *scene,
|
||||
}
|
||||
|
||||
/* Create vertex pointiness attributes. */
|
||||
|
||||
/* Compare vertices by sum of their coordinates. */
|
||||
class VertexAverageComparator {
|
||||
public:
|
||||
VertexAverageComparator(const array<float3>& verts)
|
||||
: verts_(verts) {
|
||||
}
|
||||
|
||||
bool operator()(const int& vert_idx_a, const int& vert_idx_b)
|
||||
{
|
||||
const float3 &vert_a = verts_[vert_idx_a];
|
||||
const float3 &vert_b = verts_[vert_idx_b];
|
||||
if(vert_a == vert_b) {
|
||||
/* Special case for doubles, so we ensure ordering. */
|
||||
return vert_idx_a > vert_idx_b;
|
||||
}
|
||||
const float x1 = vert_a.x + vert_a.y + vert_a.z;
|
||||
const float x2 = vert_b.x + vert_b.y + vert_b.z;
|
||||
return x1 < x2;
|
||||
}
|
||||
|
||||
protected:
|
||||
const array<float3>& verts_;
|
||||
};
|
||||
|
||||
static void attr_create_pointiness(Scene *scene,
|
||||
Mesh *mesh,
|
||||
BL::Mesh& b_mesh,
|
||||
bool subdivision)
|
||||
{
|
||||
if(mesh->need_attribute(scene, ATTR_STD_POINTINESS)) {
|
||||
const int numverts = b_mesh.vertices.length();
|
||||
AttributeSet& attributes = (subdivision)? mesh->subd_attributes: mesh->attributes;
|
||||
Attribute *attr = attributes.add(ATTR_STD_POINTINESS);
|
||||
float *data = attr->data_float();
|
||||
int *counter = new int[numverts];
|
||||
float *raw_data = new float[numverts];
|
||||
float3 *edge_accum = new float3[numverts];
|
||||
|
||||
/* Calculate pointiness using single ring neighborhood. */
|
||||
memset(counter, 0, sizeof(int) * numverts);
|
||||
memset(raw_data, 0, sizeof(float) * numverts);
|
||||
memset(edge_accum, 0, sizeof(float3) * numverts);
|
||||
BL::Mesh::edges_iterator e;
|
||||
int i = 0;
|
||||
for(b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e, ++i) {
|
||||
int v0 = b_mesh.edges[i].vertices()[0],
|
||||
v1 = b_mesh.edges[i].vertices()[1];
|
||||
float3 co0 = get_float3(b_mesh.vertices[v0].co()),
|
||||
co1 = get_float3(b_mesh.vertices[v1].co());
|
||||
float3 edge = normalize(co1 - co0);
|
||||
edge_accum[v0] += edge;
|
||||
edge_accum[v1] += -edge;
|
||||
++counter[v0];
|
||||
++counter[v1];
|
||||
}
|
||||
i = 0;
|
||||
BL::Mesh::vertices_iterator v;
|
||||
for(b_mesh.vertices.begin(v); v != b_mesh.vertices.end(); ++v, ++i) {
|
||||
if(counter[i] > 0) {
|
||||
float3 normal = get_float3(b_mesh.vertices[i].normal());
|
||||
float angle = safe_acosf(dot(normal, edge_accum[i] / counter[i]));
|
||||
raw_data[i] = angle * M_1_PI_F;
|
||||
if(!mesh->need_attribute(scene, ATTR_STD_POINTINESS)) {
|
||||
return;
|
||||
}
|
||||
const int num_verts = b_mesh.vertices.length();
|
||||
/* STEP 1: Find out duplicated vertices and point duplicates to a single
|
||||
* original vertex.
|
||||
*/
|
||||
vector<int> sorted_vert_indeices(num_verts);
|
||||
for(int vert_index = 0; vert_index < num_verts; ++vert_index) {
|
||||
sorted_vert_indeices[vert_index] = vert_index;
|
||||
}
|
||||
VertexAverageComparator compare(mesh->verts);
|
||||
sort(sorted_vert_indeices.begin(), sorted_vert_indeices.end(), compare);
|
||||
/* This array stores index of the original vertex for the given vertex
|
||||
* index.
|
||||
*/
|
||||
vector<int> vert_orig_index(num_verts);
|
||||
for(int sorted_vert_index = 0;
|
||||
sorted_vert_index < num_verts;
|
||||
++sorted_vert_index)
|
||||
{
|
||||
const int vert_index = sorted_vert_indeices[sorted_vert_index];
|
||||
const float3 &vert_co = mesh->verts[vert_index];
|
||||
bool found = false;
|
||||
for(int other_sorted_vert_index = sorted_vert_index + 1;
|
||||
other_sorted_vert_index < num_verts;
|
||||
++other_sorted_vert_index)
|
||||
{
|
||||
const int other_vert_index =
|
||||
sorted_vert_indeices[other_sorted_vert_index];
|
||||
const float3 &other_vert_co = mesh->verts[other_vert_index];
|
||||
/* We are too far away now, we wouldn't have duplicate. */
|
||||
if ((other_vert_co.x + other_vert_co.y + other_vert_co.z) -
|
||||
(vert_co.x + vert_co.y + vert_co.z) > 3 * FLT_EPSILON)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else {
|
||||
raw_data[i] = 0.0f;
|
||||
/* Found duplicate. */
|
||||
if(len_squared(other_vert_co - vert_co) < FLT_EPSILON) {
|
||||
found = true;
|
||||
vert_orig_index[vert_index] = other_vert_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Blur vertices to approximate 2 ring neighborhood. */
|
||||
memset(counter, 0, sizeof(int) * numverts);
|
||||
memcpy(data, raw_data, sizeof(float) * numverts);
|
||||
i = 0;
|
||||
for(b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e, ++i) {
|
||||
int v0 = b_mesh.edges[i].vertices()[0],
|
||||
v1 = b_mesh.edges[i].vertices()[1];
|
||||
data[v0] += raw_data[v1];
|
||||
data[v1] += raw_data[v0];
|
||||
++counter[v0];
|
||||
++counter[v1];
|
||||
if(!found) {
|
||||
vert_orig_index[vert_index] = vert_index;
|
||||
}
|
||||
for(i = 0; i < numverts; ++i) {
|
||||
data[i] /= counter[i] + 1;
|
||||
}
|
||||
/* Make sure we always points to the very first orig vertex. */
|
||||
for(int vert_index = 0; vert_index < num_verts; ++vert_index) {
|
||||
int orig_index = vert_orig_index[vert_index];
|
||||
while(orig_index != vert_orig_index[orig_index]) {
|
||||
orig_index = vert_orig_index[orig_index];
|
||||
}
|
||||
|
||||
delete [] counter;
|
||||
delete [] raw_data;
|
||||
delete [] edge_accum;
|
||||
vert_orig_index[vert_index] = orig_index;
|
||||
}
|
||||
sorted_vert_indeices.free_memory();
|
||||
/* STEP 2: Calculate vertex normals taking into account their possible
|
||||
* duplicates which gets "welded" together.
|
||||
*/
|
||||
vector<float3> vert_normal(num_verts, make_float3(0.0f, 0.0f, 0.0f));
|
||||
/* First we accumulate all vertex normals in the original index. */
|
||||
for(int vert_index = 0; vert_index < num_verts; ++vert_index) {
|
||||
const float3 normal = get_float3(b_mesh.vertices[vert_index].normal());
|
||||
const int orig_index = vert_orig_index[vert_index];
|
||||
vert_normal[orig_index] += normal;
|
||||
}
|
||||
/* Then we normalize the accumulated result and flush it to all duplicates
|
||||
* as well.
|
||||
*/
|
||||
for(int vert_index = 0; vert_index < num_verts; ++vert_index) {
|
||||
const int orig_index = vert_orig_index[vert_index];
|
||||
vert_normal[vert_index] = normalize(vert_normal[orig_index]);
|
||||
}
|
||||
/* STEP 3: Calculate pointiness using single ring neighborhood. */
|
||||
vector<int> counter(num_verts, 0);
|
||||
vector<float> raw_data(num_verts, 0.0f);
|
||||
vector<float3> edge_accum(num_verts, make_float3(0.0f, 0.0f, 0.0f));
|
||||
BL::Mesh::edges_iterator e;
|
||||
EdgeMap visited_edges;
|
||||
int edge_index = 0;
|
||||
memset(&counter[0], 0, sizeof(int) * counter.size());
|
||||
for(b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e, ++edge_index) {
|
||||
const int v0 = vert_orig_index[b_mesh.edges[edge_index].vertices()[0]],
|
||||
v1 = vert_orig_index[b_mesh.edges[edge_index].vertices()[1]];
|
||||
if(visited_edges.exists(v0, v1)) {
|
||||
continue;
|
||||
}
|
||||
visited_edges.insert(v0, v1);
|
||||
float3 co0 = get_float3(b_mesh.vertices[v0].co()),
|
||||
co1 = get_float3(b_mesh.vertices[v1].co());
|
||||
float3 edge = normalize(co1 - co0);
|
||||
edge_accum[v0] += edge;
|
||||
edge_accum[v1] += -edge;
|
||||
++counter[v0];
|
||||
++counter[v1];
|
||||
}
|
||||
for(int vert_index = 0; vert_index < num_verts; ++vert_index) {
|
||||
const int orig_index = vert_orig_index[vert_index];
|
||||
if(orig_index != vert_index) {
|
||||
/* Skip duplicates, they'll be overwritten later on. */
|
||||
continue;
|
||||
}
|
||||
if(counter[vert_index] > 0) {
|
||||
const float3 normal = vert_normal[vert_index];
|
||||
const float angle =
|
||||
safe_acosf(dot(normal,
|
||||
edge_accum[vert_index] / counter[vert_index]));
|
||||
raw_data[vert_index] = angle * M_1_PI_F;
|
||||
}
|
||||
else {
|
||||
raw_data[vert_index] = 0.0f;
|
||||
}
|
||||
}
|
||||
/* STEP 3: Blur vertices to approximate 2 ring neighborhood. */
|
||||
AttributeSet& attributes = (subdivision)? mesh->subd_attributes: mesh->attributes;
|
||||
Attribute *attr = attributes.add(ATTR_STD_POINTINESS);
|
||||
float *data = attr->data_float();
|
||||
memcpy(data, &raw_data[0], sizeof(float) * raw_data.size());
|
||||
memset(&counter[0], 0, sizeof(int) * counter.size());
|
||||
edge_index = 0;
|
||||
visited_edges.clear();
|
||||
for(b_mesh.edges.begin(e); e != b_mesh.edges.end(); ++e, ++edge_index) {
|
||||
const int v0 = vert_orig_index[b_mesh.edges[edge_index].vertices()[0]],
|
||||
v1 = vert_orig_index[b_mesh.edges[edge_index].vertices()[1]];
|
||||
if(visited_edges.exists(v0, v1)) {
|
||||
continue;
|
||||
}
|
||||
visited_edges.insert(v0, v1);
|
||||
data[v0] += raw_data[v1];
|
||||
data[v1] += raw_data[v0];
|
||||
++counter[v0];
|
||||
++counter[v1];
|
||||
}
|
||||
for(int vert_index = 0; vert_index < num_verts; ++vert_index) {
|
||||
data[vert_index] /= counter[vert_index] + 1;
|
||||
}
|
||||
/* STEP 4: Copy attribute to the duplicated vertices. */
|
||||
for(int vert_index = 0; vert_index < num_verts; ++vert_index) {
|
||||
const int orig_index = vert_orig_index[vert_index];
|
||||
data[vert_index] = data[orig_index];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -656,9 +765,6 @@ static void create_mesh(Scene *scene,
|
||||
generated[i++] = get_float3(v->undeformed_co())*size - loc;
|
||||
}
|
||||
|
||||
/* Create needed vertex attributes. */
|
||||
attr_create_pointiness(scene, mesh, b_mesh, subdivision);
|
||||
|
||||
/* create faces */
|
||||
vector<int> nverts(numfaces);
|
||||
vector<int> face_flags(numfaces, FACE_FLAG_NONE);
|
||||
@@ -718,6 +824,7 @@ static void create_mesh(Scene *scene,
|
||||
/* Create all needed attributes.
|
||||
* The calculate functions will check whether they're needed or not.
|
||||
*/
|
||||
attr_create_pointiness(scene, mesh, b_mesh, subdivision);
|
||||
attr_create_vertex_color(scene, mesh, b_mesh, nverts, face_flags, subdivision);
|
||||
attr_create_uv_map(scene, mesh, b_mesh, nverts, face_flags, subdivision, subdivide_uvs);
|
||||
|
||||
@@ -1178,4 +1285,3 @@ void BlenderSync::sync_mesh_motion(BL::Object& b_ob,
|
||||
}
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
|
||||
@@ -609,7 +609,8 @@ static ShaderNode *add_node(Scene *scene,
|
||||
bool is_builtin = b_image.packed_file() ||
|
||||
b_image.source() == BL::Image::source_GENERATED ||
|
||||
b_image.source() == BL::Image::source_MOVIE ||
|
||||
b_engine.is_preview();
|
||||
(b_engine.is_preview() &&
|
||||
b_image.source() != BL::Image::source_SEQUENCE);
|
||||
|
||||
if(is_builtin) {
|
||||
/* for builtin images we're using image datablock name to find an image to
|
||||
@@ -662,7 +663,8 @@ static ShaderNode *add_node(Scene *scene,
|
||||
bool is_builtin = b_image.packed_file() ||
|
||||
b_image.source() == BL::Image::source_GENERATED ||
|
||||
b_image.source() == BL::Image::source_MOVIE ||
|
||||
b_engine.is_preview();
|
||||
(b_engine.is_preview() &&
|
||||
b_image.source() != BL::Image::source_SEQUENCE);
|
||||
|
||||
if(is_builtin) {
|
||||
int scene_frame = b_scene.frame_current();
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "mesh.h"
|
||||
|
||||
#include "util_algorithm.h"
|
||||
#include "util_map.h"
|
||||
#include "util_path.h"
|
||||
#include "util_set.h"
|
||||
@@ -79,6 +80,7 @@ static inline BL::Mesh object_to_mesh(BL::BlendData& data,
|
||||
}
|
||||
else {
|
||||
me.split_faces();
|
||||
me.calc_normals_split();
|
||||
}
|
||||
}
|
||||
if(subdivision_type == Mesh::SUBDIVISION_NONE) {
|
||||
@@ -786,6 +788,35 @@ struct ParticleSystemKey {
|
||||
}
|
||||
};
|
||||
|
||||
class EdgeMap {
|
||||
public:
|
||||
EdgeMap() {
|
||||
}
|
||||
|
||||
void clear() {
|
||||
edges_.clear();
|
||||
}
|
||||
|
||||
void insert(int v0, int v1) {
|
||||
get_sorted_verts(v0, v1);
|
||||
edges_.insert(std::pair<int, int>(v0, v1));
|
||||
}
|
||||
|
||||
bool exists(int v0, int v1) {
|
||||
get_sorted_verts(v0, v1);
|
||||
return edges_.find(std::pair<int, int>(v0, v1)) != edges_.end();
|
||||
}
|
||||
|
||||
protected:
|
||||
void get_sorted_verts(int& v0, int& v1) {
|
||||
if(v0 > v1) {
|
||||
swap(v0, v1);
|
||||
}
|
||||
}
|
||||
|
||||
set< std::pair<int, int> > edges_;
|
||||
};
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
#endif /* __BLENDER_UTIL_H__ */
|
||||
|
||||
@@ -81,6 +81,7 @@ void BVH::build(Progress& progress)
|
||||
pack.prim_type,
|
||||
pack.prim_index,
|
||||
pack.prim_object,
|
||||
pack.prim_time,
|
||||
params,
|
||||
progress);
|
||||
BVHNode *root = bvh_build.run();
|
||||
@@ -256,6 +257,10 @@ void BVH::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
|
||||
pack.leaf_nodes.resize(leaf_nodes_size);
|
||||
pack.object_node.resize(objects.size());
|
||||
|
||||
if(params.num_motion_curve_steps > 0 || params.num_motion_triangle_steps > 0) {
|
||||
pack.prim_time.resize(prim_index_size);
|
||||
}
|
||||
|
||||
int *pack_prim_index = (pack.prim_index.size())? &pack.prim_index[0]: NULL;
|
||||
int *pack_prim_type = (pack.prim_type.size())? &pack.prim_type[0]: NULL;
|
||||
int *pack_prim_object = (pack.prim_object.size())? &pack.prim_object[0]: NULL;
|
||||
@@ -264,6 +269,7 @@ void BVH::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
|
||||
uint *pack_prim_tri_index = (pack.prim_tri_index.size())? &pack.prim_tri_index[0]: NULL;
|
||||
int4 *pack_nodes = (pack.nodes.size())? &pack.nodes[0]: NULL;
|
||||
int4 *pack_leaf_nodes = (pack.leaf_nodes.size())? &pack.leaf_nodes[0]: NULL;
|
||||
float2 *pack_prim_time = (pack.prim_time.size())? &pack.prim_time[0]: NULL;
|
||||
|
||||
/* merge */
|
||||
foreach(Object *ob, objects) {
|
||||
@@ -309,6 +315,7 @@ void BVH::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
|
||||
int *bvh_prim_type = &bvh->pack.prim_type[0];
|
||||
uint *bvh_prim_visibility = &bvh->pack.prim_visibility[0];
|
||||
uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0];
|
||||
float2 *bvh_prim_time = bvh->pack.prim_time.size()? &bvh->pack.prim_time[0]: NULL;
|
||||
|
||||
for(size_t i = 0; i < bvh_prim_index_size; i++) {
|
||||
if(bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) {
|
||||
@@ -324,6 +331,9 @@ void BVH::pack_instances(size_t nodes_size, size_t leaf_nodes_size)
|
||||
pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i];
|
||||
pack_prim_visibility[pack_prim_index_offset] = bvh_prim_visibility[i];
|
||||
pack_prim_object[pack_prim_index_offset] = 0; // unused for instances
|
||||
if(bvh_prim_time != NULL) {
|
||||
pack_prim_time[pack_prim_index_offset] = bvh_prim_time[i];
|
||||
}
|
||||
pack_prim_index_offset++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,8 @@ struct PackedBVH {
|
||||
array<int> prim_index;
|
||||
/* mapping from BVH primitive index, to the object id of that primitive. */
|
||||
array<int> prim_object;
|
||||
/* Time range of BVH primitive. */
|
||||
array<float2> prim_time;
|
||||
|
||||
/* index of the root node. */
|
||||
int root_index;
|
||||
|
||||
@@ -93,12 +93,14 @@ BVHBuild::BVHBuild(const vector<Object*>& objects_,
|
||||
array<int>& prim_type_,
|
||||
array<int>& prim_index_,
|
||||
array<int>& prim_object_,
|
||||
array<float2>& prim_time_,
|
||||
const BVHParams& params_,
|
||||
Progress& progress_)
|
||||
: objects(objects_),
|
||||
prim_type(prim_type_),
|
||||
prim_index(prim_index_),
|
||||
prim_object(prim_object_),
|
||||
prim_time(prim_time_),
|
||||
params(params_),
|
||||
progress(progress_),
|
||||
progress_start_time(0.0),
|
||||
@@ -465,6 +467,9 @@ BVHNode* BVHBuild::run()
|
||||
}
|
||||
spatial_free_index = 0;
|
||||
|
||||
need_prim_time = params.num_motion_curve_steps > 0 ||
|
||||
params.num_motion_triangle_steps > 0;
|
||||
|
||||
/* init progress updates */
|
||||
double build_start_time;
|
||||
build_start_time = progress_start_time = time_dt();
|
||||
@@ -475,6 +480,12 @@ BVHNode* BVHBuild::run()
|
||||
prim_type.resize(references.size());
|
||||
prim_index.resize(references.size());
|
||||
prim_object.resize(references.size());
|
||||
if(need_prim_time) {
|
||||
prim_time.resize(references.size());
|
||||
}
|
||||
else {
|
||||
prim_time.resize(0);
|
||||
}
|
||||
|
||||
/* build recursively */
|
||||
BVHNode *rootnode;
|
||||
@@ -849,6 +860,9 @@ BVHNode *BVHBuild::create_object_leaf_nodes(const BVHReference *ref, int start,
|
||||
prim_type[start] = ref->prim_type();
|
||||
prim_index[start] = ref->prim_index();
|
||||
prim_object[start] = ref->prim_object();
|
||||
if(need_prim_time) {
|
||||
prim_time[start] = make_float2(ref->time_from(), ref->time_to());
|
||||
}
|
||||
|
||||
uint visibility = objects[ref->prim_object()]->visibility;
|
||||
BVHNode *leaf_node = new LeafNode(ref->bounds(), visibility, start, start+1);
|
||||
@@ -896,6 +910,7 @@ BVHNode* BVHBuild::create_leaf_node(const BVHRange& range,
|
||||
vector<int, LeafStackAllocator> p_type[PRIMITIVE_NUM_TOTAL];
|
||||
vector<int, LeafStackAllocator> p_index[PRIMITIVE_NUM_TOTAL];
|
||||
vector<int, LeafStackAllocator> p_object[PRIMITIVE_NUM_TOTAL];
|
||||
vector<float2, LeafStackAllocator> p_time[PRIMITIVE_NUM_TOTAL];
|
||||
vector<BVHReference, LeafReferenceStackAllocator> p_ref[PRIMITIVE_NUM_TOTAL];
|
||||
|
||||
/* TODO(sergey): In theory we should be able to store references. */
|
||||
@@ -918,6 +933,8 @@ BVHNode* BVHBuild::create_leaf_node(const BVHRange& range,
|
||||
p_type[type_index].push_back(ref.prim_type());
|
||||
p_index[type_index].push_back(ref.prim_index());
|
||||
p_object[type_index].push_back(ref.prim_object());
|
||||
p_time[type_index].push_back(make_float2(ref.time_from(),
|
||||
ref.time_to()));
|
||||
|
||||
bounds[type_index].grow(ref.bounds());
|
||||
visibility[type_index] |= objects[ref.prim_object()]->visibility;
|
||||
@@ -947,9 +964,13 @@ BVHNode* BVHBuild::create_leaf_node(const BVHRange& range,
|
||||
vector<int, LeafStackAllocator> local_prim_type,
|
||||
local_prim_index,
|
||||
local_prim_object;
|
||||
vector<float2, LeafStackAllocator> local_prim_time;
|
||||
local_prim_type.resize(num_new_prims);
|
||||
local_prim_index.resize(num_new_prims);
|
||||
local_prim_object.resize(num_new_prims);
|
||||
if(need_prim_time) {
|
||||
local_prim_time.resize(num_new_prims);
|
||||
}
|
||||
for(int i = 0; i < PRIMITIVE_NUM_TOTAL; ++i) {
|
||||
int num = (int)p_type[i].size();
|
||||
if(num != 0) {
|
||||
@@ -962,6 +983,9 @@ BVHNode* BVHBuild::create_leaf_node(const BVHRange& range,
|
||||
local_prim_type[index] = p_type[i][j];
|
||||
local_prim_index[index] = p_index[i][j];
|
||||
local_prim_object[index] = p_object[i][j];
|
||||
if(need_prim_time) {
|
||||
local_prim_time[index] = p_time[i][j];
|
||||
}
|
||||
if(params.use_unaligned_nodes && !alignment_found) {
|
||||
alignment_found =
|
||||
unaligned_heuristic.compute_aligned_space(p_ref[i][j],
|
||||
@@ -1028,11 +1052,17 @@ BVHNode* BVHBuild::create_leaf_node(const BVHRange& range,
|
||||
prim_type.reserve(reserve);
|
||||
prim_index.reserve(reserve);
|
||||
prim_object.reserve(reserve);
|
||||
if(need_prim_time) {
|
||||
prim_time.reserve(reserve);
|
||||
}
|
||||
}
|
||||
|
||||
prim_type.resize(range_end);
|
||||
prim_index.resize(range_end);
|
||||
prim_object.resize(range_end);
|
||||
if(need_prim_time) {
|
||||
prim_time.resize(range_end);
|
||||
}
|
||||
}
|
||||
spatial_spin_lock.unlock();
|
||||
|
||||
@@ -1041,6 +1071,9 @@ BVHNode* BVHBuild::create_leaf_node(const BVHRange& range,
|
||||
memcpy(&prim_type[start_index], &local_prim_type[0], new_leaf_data_size);
|
||||
memcpy(&prim_index[start_index], &local_prim_index[0], new_leaf_data_size);
|
||||
memcpy(&prim_object[start_index], &local_prim_object[0], new_leaf_data_size);
|
||||
if(need_prim_time) {
|
||||
memcpy(&prim_time[start_index], &local_prim_time[0], sizeof(float2)*num_new_leaf_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1053,6 +1086,9 @@ BVHNode* BVHBuild::create_leaf_node(const BVHRange& range,
|
||||
memcpy(&prim_type[start_index], &local_prim_type[0], new_leaf_data_size);
|
||||
memcpy(&prim_index[start_index], &local_prim_index[0], new_leaf_data_size);
|
||||
memcpy(&prim_object[start_index], &local_prim_object[0], new_leaf_data_size);
|
||||
if(need_prim_time) {
|
||||
memcpy(&prim_time[start_index], &local_prim_time[0], sizeof(float2)*num_new_leaf_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ public:
|
||||
array<int>& prim_type,
|
||||
array<int>& prim_index,
|
||||
array<int>& prim_object,
|
||||
array<float2>& prim_time,
|
||||
const BVHParams& params,
|
||||
Progress& progress);
|
||||
~BVHBuild();
|
||||
@@ -112,6 +113,9 @@ protected:
|
||||
array<int>& prim_type;
|
||||
array<int>& prim_index;
|
||||
array<int>& prim_object;
|
||||
array<float2>& prim_time;
|
||||
|
||||
bool need_prim_time;
|
||||
|
||||
/* Build parameters. */
|
||||
BVHParams params;
|
||||
|
||||
@@ -104,6 +104,7 @@ public:
|
||||
primitive_mask = PRIMITIVE_ALL;
|
||||
|
||||
num_motion_curve_steps = 0;
|
||||
num_motion_triangle_steps = 0;
|
||||
}
|
||||
|
||||
/* SAH costs */
|
||||
|
||||
@@ -357,7 +357,7 @@ ccl_device_inline float3 ray_offset(float3 P, float3 Ng)
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__SHADOW_RECORD_ALL__) || defined (__VOLUME_RECORD_ALL__)
|
||||
#if defined(__VOLUME_RECORD_ALL__) || (defined(__SHADOW_RECORD_ALL__) && defined(__KERNEL_CPU__))
|
||||
/* ToDo: Move to another file? */
|
||||
ccl_device int intersections_compare(const void *a, const void *b)
|
||||
{
|
||||
@@ -373,5 +373,28 @@ ccl_device int intersections_compare(const void *a, const void *b)
|
||||
}
|
||||
#endif
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
#if defined(__SHADOW_RECORD_ALL__)
|
||||
ccl_device_inline void sort_intersections(Intersection *hits, uint num_hits)
|
||||
{
|
||||
#ifdef __KERNEL_GPU__
|
||||
/* Use bubble sort which has more friendly memory pattern on GPU. */
|
||||
bool swapped;
|
||||
do {
|
||||
swapped = false;
|
||||
for(int j = 0; j < num_hits - 1; ++j) {
|
||||
if(hits[j].t > hits[j + 1].t) {
|
||||
struct Intersection tmp = hits[j];
|
||||
hits[j] = hits[j + 1];
|
||||
hits[j + 1] = tmp;
|
||||
swapped = true;
|
||||
}
|
||||
}
|
||||
--num_hits;
|
||||
} while(swapped);
|
||||
#else
|
||||
qsort(hits, num_hits, sizeof(Intersection), intersections_compare);
|
||||
#endif
|
||||
}
|
||||
#endif /* __SHADOW_RECORD_ALL__ | __VOLUME_RECORD_ALL__ */
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
@@ -229,6 +229,15 @@ ccl_device_forceinline bool bvh_cardinal_curve_intersect(KernelGlobals *kg, Inte
|
||||
float3 P, float3 dir, uint visibility, int object, int curveAddr, float time,int type, uint *lcg_state, float difl, float extmax)
|
||||
#endif
|
||||
{
|
||||
const bool is_curve_primitive = (type & PRIMITIVE_CURVE);
|
||||
|
||||
if(!is_curve_primitive && kernel_data.bvh.use_bvh_steps) {
|
||||
const float2 prim_time = kernel_tex_fetch(__prim_time, curveAddr);
|
||||
if(time < prim_time.x || time > prim_time.y) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int segment = PRIMITIVE_UNPACK_SEGMENT(type);
|
||||
float epsilon = 0.0f;
|
||||
float r_st, r_en;
|
||||
@@ -257,7 +266,7 @@ ccl_device_forceinline bool bvh_cardinal_curve_intersect(KernelGlobals *kg, Inte
|
||||
|
||||
#ifdef __KERNEL_AVX2__
|
||||
avxf P_curve_0_1, P_curve_2_3;
|
||||
if(type & PRIMITIVE_CURVE) {
|
||||
if(is_curve_primitive) {
|
||||
P_curve_0_1 = _mm256_loadu2_m128(&kg->__curve_keys.data[k0].x, &kg->__curve_keys.data[ka].x);
|
||||
P_curve_2_3 = _mm256_loadu2_m128(&kg->__curve_keys.data[kb].x, &kg->__curve_keys.data[k1].x);
|
||||
}
|
||||
@@ -268,7 +277,7 @@ ccl_device_forceinline bool bvh_cardinal_curve_intersect(KernelGlobals *kg, Inte
|
||||
#else /* __KERNEL_AVX2__ */
|
||||
ssef P_curve[4];
|
||||
|
||||
if(type & PRIMITIVE_CURVE) {
|
||||
if(is_curve_primitive) {
|
||||
P_curve[0] = load4f(&kg->__curve_keys.data[ka].x);
|
||||
P_curve[1] = load4f(&kg->__curve_keys.data[k0].x);
|
||||
P_curve[2] = load4f(&kg->__curve_keys.data[k1].x);
|
||||
@@ -363,7 +372,7 @@ ccl_device_forceinline bool bvh_cardinal_curve_intersect(KernelGlobals *kg, Inte
|
||||
|
||||
float4 P_curve[4];
|
||||
|
||||
if(type & PRIMITIVE_CURVE) {
|
||||
if(is_curve_primitive) {
|
||||
P_curve[0] = kernel_tex_fetch(__curve_keys, ka);
|
||||
P_curve[1] = kernel_tex_fetch(__curve_keys, k0);
|
||||
P_curve[2] = kernel_tex_fetch(__curve_keys, k1);
|
||||
@@ -689,6 +698,15 @@ ccl_device_forceinline bool bvh_curve_intersect(KernelGlobals *kg, Intersection
|
||||
# define dot3(x, y) dot(x, y)
|
||||
#endif
|
||||
|
||||
const bool is_curve_primitive = (type & PRIMITIVE_CURVE);
|
||||
|
||||
if(!is_curve_primitive && kernel_data.bvh.use_bvh_steps) {
|
||||
const float2 prim_time = kernel_tex_fetch(__prim_time, curveAddr);
|
||||
if(time < prim_time.x || time > prim_time.y) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int segment = PRIMITIVE_UNPACK_SEGMENT(type);
|
||||
/* curve Intersection check */
|
||||
int flags = kernel_data.curve.curveflags;
|
||||
@@ -703,7 +721,7 @@ ccl_device_forceinline bool bvh_curve_intersect(KernelGlobals *kg, Intersection
|
||||
#ifndef __KERNEL_SSE2__
|
||||
float4 P_curve[2];
|
||||
|
||||
if(type & PRIMITIVE_CURVE) {
|
||||
if(is_curve_primitive) {
|
||||
P_curve[0] = kernel_tex_fetch(__curve_keys, k0);
|
||||
P_curve[1] = kernel_tex_fetch(__curve_keys, k1);
|
||||
}
|
||||
@@ -738,7 +756,7 @@ ccl_device_forceinline bool bvh_curve_intersect(KernelGlobals *kg, Intersection
|
||||
#else
|
||||
ssef P_curve[2];
|
||||
|
||||
if(type & PRIMITIVE_CURVE) {
|
||||
if(is_curve_primitive) {
|
||||
P_curve[0] = load4f(&kg->__curve_keys.data[k0].x);
|
||||
P_curve[1] = load4f(&kg->__curve_keys.data[k1].x);
|
||||
}
|
||||
|
||||
@@ -76,7 +76,10 @@ typedef struct KernelGlobals {
|
||||
#ifdef __KERNEL_CUDA__
|
||||
|
||||
__constant__ KernelData __data;
|
||||
typedef struct KernelGlobals {} KernelGlobals;
|
||||
typedef struct KernelGlobals {
|
||||
/* NOTE: Keep the size in sync with SHADOW_STACK_MAX_HITS. */
|
||||
Intersection hits_stack[64];
|
||||
} KernelGlobals;
|
||||
|
||||
# ifdef __KERNEL_CUDA_TEX_STORAGE__
|
||||
# define KERNEL_TEX(type, ttype, name) ttype name;
|
||||
|
||||
@@ -16,9 +16,84 @@
|
||||
|
||||
CCL_NAMESPACE_BEGIN
|
||||
|
||||
#ifdef __SHADOW_RECORD_ALL__
|
||||
/* Attenuate throughput accordingly to the given intersection event.
|
||||
* Returns true if the throughput is zero and traversal can be aborted.
|
||||
*/
|
||||
ccl_device_forceinline bool shadow_handle_transparent_isect(
|
||||
KernelGlobals *kg,
|
||||
ShaderData *shadow_sd,
|
||||
ccl_addr_space PathState *state,
|
||||
# ifdef __VOLUME__
|
||||
struct PathState *volume_state,
|
||||
# endif
|
||||
Intersection *isect,
|
||||
Ray *ray,
|
||||
float3 *throughput)
|
||||
{
|
||||
#ifdef __VOLUME__
|
||||
/* Attenuation between last surface and next surface. */
|
||||
if(volume_state->volume_stack[0].shader != SHADER_NONE) {
|
||||
Ray segment_ray = *ray;
|
||||
segment_ray.t = isect->t;
|
||||
kernel_volume_shadow(kg,
|
||||
shadow_sd,
|
||||
volume_state,
|
||||
&segment_ray,
|
||||
throughput);
|
||||
}
|
||||
#endif
|
||||
/* Setup shader data at surface. */
|
||||
shader_setup_from_ray(kg, shadow_sd, isect, ray);
|
||||
/* Attenuation from transparent surface. */
|
||||
if(!(ccl_fetch(shadow_sd, flag) & SD_HAS_ONLY_VOLUME)) {
|
||||
path_state_modify_bounce(state, true);
|
||||
shader_eval_surface(kg,
|
||||
shadow_sd,
|
||||
NULL,
|
||||
state,
|
||||
0.0f,
|
||||
PATH_RAY_SHADOW,
|
||||
SHADER_CONTEXT_SHADOW);
|
||||
path_state_modify_bounce(state, false);
|
||||
*throughput *= shader_bsdf_transparency(kg, shadow_sd);
|
||||
}
|
||||
/* Stop if all light is blocked. */
|
||||
if(is_zero(*throughput)) {
|
||||
return true;
|
||||
}
|
||||
#ifdef __VOLUME__
|
||||
/* Exit/enter volume. */
|
||||
kernel_volume_stack_enter_exit(kg, shadow_sd, volume_state->volume_stack);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Shadow function to compute how much light is blocked, CPU variation.
|
||||
/* Special version which only handles opaque shadows. */
|
||||
ccl_device bool shadow_blocked_opaque(KernelGlobals *kg,
|
||||
ShaderData *shadow_sd,
|
||||
ccl_addr_space PathState *state,
|
||||
Ray *ray,
|
||||
Intersection *isect,
|
||||
float3 *shadow)
|
||||
{
|
||||
const bool blocked = scene_intersect(kg,
|
||||
*ray,
|
||||
PATH_RAY_SHADOW_OPAQUE,
|
||||
isect,
|
||||
NULL,
|
||||
0.0f, 0.0f);
|
||||
#ifdef __VOLUME__
|
||||
if(!blocked && state->volume_stack[0].shader != SHADER_NONE) {
|
||||
/* Apply attenuation from current volume shader. */
|
||||
kernel_volume_shadow(kg, shadow_sd, state, ray, shadow);
|
||||
}
|
||||
#endif
|
||||
return blocked;
|
||||
}
|
||||
|
||||
#ifdef __TRANSPARENT_SHADOWS__
|
||||
# ifdef __SHADOW_RECORD_ALL__
|
||||
/* Shadow function to compute how much light is blocked,
|
||||
*
|
||||
* We trace a single ray. If it hits any opaque surface, or more than a given
|
||||
* number of transparent surfaces is hit, then we consider the geometry to be
|
||||
@@ -36,261 +111,355 @@ CCL_NAMESPACE_BEGIN
|
||||
* or there is a performance increase anyway due to avoiding the need to send
|
||||
* two rays with transparent shadows.
|
||||
*
|
||||
* This is CPU only because of qsort, and malloc or high stack space usage to
|
||||
* record all these intersections. */
|
||||
* On CPU it'll handle all transparent bounces (by allocating storage for
|
||||
* intersections when they don't fit into the stack storage).
|
||||
*
|
||||
* On GPU it'll only handle SHADOW_STACK_MAX_HITS-1 intersections, so this
|
||||
* is something to be kept an eye on.
|
||||
*/
|
||||
|
||||
#define STACK_MAX_HITS 64
|
||||
# define SHADOW_STACK_MAX_HITS 64
|
||||
|
||||
ccl_device_inline bool shadow_blocked(KernelGlobals *kg, ShaderData *shadow_sd, PathState *state, Ray *ray, float3 *shadow)
|
||||
/* Actual logic with traversal loop implementation which is free from device
|
||||
* specific tweaks.
|
||||
*
|
||||
* Note that hits array should be as big as max_hits+1.
|
||||
*/
|
||||
ccl_device bool shadow_blocked_transparent_all_loop(KernelGlobals *kg,
|
||||
ShaderData *shadow_sd,
|
||||
ccl_addr_space PathState *state,
|
||||
Ray *ray,
|
||||
Intersection *hits,
|
||||
uint max_hits,
|
||||
float3 *shadow)
|
||||
{
|
||||
*shadow = make_float3(1.0f, 1.0f, 1.0f);
|
||||
|
||||
if(ray->t == 0.0f)
|
||||
return false;
|
||||
|
||||
bool blocked;
|
||||
|
||||
if(kernel_data.integrator.transparent_shadows) {
|
||||
/* check transparent bounces here, for volume scatter which can do
|
||||
* lighting before surface path termination is checked */
|
||||
if(state->transparent_bounce >= kernel_data.integrator.transparent_max_bounce)
|
||||
return true;
|
||||
|
||||
/* intersect to find an opaque surface, or record all transparent surface hits */
|
||||
Intersection hits_stack[STACK_MAX_HITS];
|
||||
Intersection *hits = hits_stack;
|
||||
const int transparent_max_bounce = kernel_data.integrator.transparent_max_bounce;
|
||||
uint max_hits = transparent_max_bounce - state->transparent_bounce - 1;
|
||||
|
||||
/* prefer to use stack but use dynamic allocation if too deep max hits
|
||||
* we need max_hits + 1 storage space due to the logic in
|
||||
* scene_intersect_shadow_all which will first store and then check if
|
||||
* the limit is exceeded */
|
||||
if(max_hits + 1 > STACK_MAX_HITS) {
|
||||
if(kg->transparent_shadow_intersections == NULL) {
|
||||
kg->transparent_shadow_intersections =
|
||||
(Intersection*)malloc(sizeof(Intersection)*(transparent_max_bounce + 1));
|
||||
/* Intersect to find an opaque surface, or record all transparent
|
||||
* surface hits.
|
||||
*/
|
||||
uint num_hits;
|
||||
const bool blocked = scene_intersect_shadow_all(kg,
|
||||
ray,
|
||||
hits,
|
||||
max_hits,
|
||||
&num_hits);
|
||||
/* If no opaque surface found but we did find transparent hits,
|
||||
* shade them.
|
||||
*/
|
||||
if(!blocked && num_hits > 0) {
|
||||
float3 throughput = make_float3(1.0f, 1.0f, 1.0f);
|
||||
float3 Pend = ray->P + ray->D*ray->t;
|
||||
float last_t = 0.0f;
|
||||
int bounce = state->transparent_bounce;
|
||||
Intersection *isect = hits;
|
||||
# ifdef __VOLUME__
|
||||
PathState ps = *state;
|
||||
# endif
|
||||
sort_intersections(hits, num_hits);
|
||||
for(int hit = 0; hit < num_hits; hit++, isect++) {
|
||||
/* Adjust intersection distance for moving ray forward. */
|
||||
float new_t = isect->t;
|
||||
isect->t -= last_t;
|
||||
/* Skip hit if we did not move forward, step by step raytracing
|
||||
* would have skipped it as well then.
|
||||
*/
|
||||
if(last_t == new_t) {
|
||||
continue;
|
||||
}
|
||||
hits = kg->transparent_shadow_intersections;
|
||||
}
|
||||
|
||||
uint num_hits;
|
||||
blocked = scene_intersect_shadow_all(kg, ray, hits, max_hits, &num_hits);
|
||||
|
||||
/* if no opaque surface found but we did find transparent hits, shade them */
|
||||
if(!blocked && num_hits > 0) {
|
||||
float3 throughput = make_float3(1.0f, 1.0f, 1.0f);
|
||||
float3 Pend = ray->P + ray->D*ray->t;
|
||||
float last_t = 0.0f;
|
||||
int bounce = state->transparent_bounce;
|
||||
Intersection *isect = hits;
|
||||
last_t = new_t;
|
||||
/* Attenuate the throughput. */
|
||||
if(shadow_handle_transparent_isect(kg,
|
||||
shadow_sd,
|
||||
state,
|
||||
#ifdef __VOLUME__
|
||||
PathState ps = *state;
|
||||
&ps,
|
||||
#endif
|
||||
|
||||
qsort(hits, num_hits, sizeof(Intersection), intersections_compare);
|
||||
|
||||
for(int hit = 0; hit < num_hits; hit++, isect++) {
|
||||
/* adjust intersection distance for moving ray forward */
|
||||
float new_t = isect->t;
|
||||
isect->t -= last_t;
|
||||
|
||||
/* skip hit if we did not move forward, step by step raytracing
|
||||
* would have skipped it as well then */
|
||||
if(last_t == new_t)
|
||||
continue;
|
||||
|
||||
last_t = new_t;
|
||||
|
||||
#ifdef __VOLUME__
|
||||
/* attenuation between last surface and next surface */
|
||||
if(ps.volume_stack[0].shader != SHADER_NONE) {
|
||||
Ray segment_ray = *ray;
|
||||
segment_ray.t = isect->t;
|
||||
kernel_volume_shadow(kg, shadow_sd, &ps, &segment_ray, &throughput);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* setup shader data at surface */
|
||||
shader_setup_from_ray(kg, shadow_sd, isect, ray);
|
||||
|
||||
/* attenuation from transparent surface */
|
||||
if(!(shadow_sd->flag & SD_HAS_ONLY_VOLUME)) {
|
||||
path_state_modify_bounce(state, true);
|
||||
shader_eval_surface(kg, shadow_sd, NULL, state, 0.0f, PATH_RAY_SHADOW, SHADER_CONTEXT_SHADOW);
|
||||
path_state_modify_bounce(state, false);
|
||||
|
||||
throughput *= shader_bsdf_transparency(kg, shadow_sd);
|
||||
}
|
||||
|
||||
/* stop if all light is blocked */
|
||||
if(is_zero(throughput)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* move ray forward */
|
||||
ray->P = shadow_sd->P;
|
||||
if(ray->t != FLT_MAX) {
|
||||
ray->D = normalize_len(Pend - ray->P, &ray->t);
|
||||
}
|
||||
|
||||
#ifdef __VOLUME__
|
||||
/* exit/enter volume */
|
||||
kernel_volume_stack_enter_exit(kg, shadow_sd, ps.volume_stack);
|
||||
#endif
|
||||
|
||||
bounce++;
|
||||
isect,
|
||||
ray,
|
||||
&throughput))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef __VOLUME__
|
||||
/* attenuation for last line segment towards light */
|
||||
if(ps.volume_stack[0].shader != SHADER_NONE)
|
||||
kernel_volume_shadow(kg, shadow_sd, &ps, ray, &throughput);
|
||||
#endif
|
||||
|
||||
*shadow = throughput;
|
||||
|
||||
return is_zero(throughput);
|
||||
/* Move ray forward. */
|
||||
ray->P = ccl_fetch(shadow_sd, P);
|
||||
if(ray->t != FLT_MAX) {
|
||||
ray->D = normalize_len(Pend - ray->P, &ray->t);
|
||||
}
|
||||
bounce++;
|
||||
}
|
||||
# ifdef __VOLUME__
|
||||
/* Attenuation for last line segment towards light. */
|
||||
if(ps.volume_stack[0].shader != SHADER_NONE) {
|
||||
kernel_volume_shadow(kg, shadow_sd, &ps, ray, &throughput);
|
||||
}
|
||||
# endif
|
||||
*shadow = throughput;
|
||||
return is_zero(throughput);
|
||||
}
|
||||
else {
|
||||
Intersection isect;
|
||||
blocked = scene_intersect(kg, *ray, PATH_RAY_SHADOW_OPAQUE, &isect, NULL, 0.0f, 0.0f);
|
||||
}
|
||||
|
||||
#ifdef __VOLUME__
|
||||
# ifdef __VOLUME__
|
||||
if(!blocked && state->volume_stack[0].shader != SHADER_NONE) {
|
||||
/* apply attenuation from current volume shader */
|
||||
/* Apply attenuation from current volume shader/ */
|
||||
kernel_volume_shadow(kg, shadow_sd, state, ray, shadow);
|
||||
}
|
||||
#endif
|
||||
|
||||
# endif
|
||||
return blocked;
|
||||
}
|
||||
|
||||
#undef STACK_MAX_HITS
|
||||
/* Here we do all device specific trickery before invoking actual traversal
|
||||
* loop to help readability of the actual logic.
|
||||
*/
|
||||
ccl_device bool shadow_blocked_transparent_all(KernelGlobals *kg,
|
||||
ShaderData *shadow_sd,
|
||||
ccl_addr_space PathState *state,
|
||||
Ray *ray,
|
||||
uint max_hits,
|
||||
float3 *shadow)
|
||||
{
|
||||
# ifdef __KERNEL_CUDA__
|
||||
Intersection *hits = kg->hits_stack;
|
||||
# else
|
||||
Intersection hits_stack[SHADOW_STACK_MAX_HITS];
|
||||
Intersection *hits = hits_stack;
|
||||
# endif
|
||||
# ifndef __KERNEL_GPU__
|
||||
/* Prefer to use stack but use dynamic allocation if too deep max hits
|
||||
* we need max_hits + 1 storage space due to the logic in
|
||||
* scene_intersect_shadow_all which will first store and then check if
|
||||
* the limit is exceeded.
|
||||
*
|
||||
* Ignore this on GPU because of slow/unavailable malloc().
|
||||
*/
|
||||
if(max_hits + 1 > SHADOW_STACK_MAX_HITS) {
|
||||
if(kg->transparent_shadow_intersections == NULL) {
|
||||
const int transparent_max_bounce = kernel_data.integrator.transparent_max_bounce;
|
||||
kg->transparent_shadow_intersections =
|
||||
(Intersection*)malloc(sizeof(Intersection)*(transparent_max_bounce + 1));
|
||||
}
|
||||
hits = kg->transparent_shadow_intersections;
|
||||
}
|
||||
# endif /* __KERNEL_GPU__ */
|
||||
/* Invoke actual traversal. */
|
||||
return shadow_blocked_transparent_all_loop(kg,
|
||||
shadow_sd,
|
||||
state,
|
||||
ray,
|
||||
hits,
|
||||
max_hits,
|
||||
shadow);
|
||||
}
|
||||
# endif /* __SHADOW_RECORD_ALL__ */
|
||||
|
||||
#else
|
||||
|
||||
/* Shadow function to compute how much light is blocked, GPU variation.
|
||||
# ifdef __KERNEL_GPU__
|
||||
/* Shadow function to compute how much light is blocked,
|
||||
*
|
||||
* Here we raytrace from one transparent surface to the next step by step.
|
||||
* To minimize overhead in cases where we don't need transparent shadows, we
|
||||
* first trace a regular shadow ray. We check if the hit primitive was
|
||||
* potentially transparent, and only in that case start marching. this gives
|
||||
* one extra ray cast for the cases were we do want transparency. */
|
||||
* one extra ray cast for the cases were we do want transparency.
|
||||
*/
|
||||
|
||||
ccl_device_noinline bool shadow_blocked(KernelGlobals *kg,
|
||||
ShaderData *shadow_sd,
|
||||
ccl_addr_space PathState *state,
|
||||
ccl_addr_space Ray *ray_input,
|
||||
float3 *shadow)
|
||||
/* This function is only implementing device-independent traversal logic
|
||||
* which requires some precalculation done.
|
||||
*/
|
||||
ccl_device bool shadow_blocked_transparent_stepped_loop(
|
||||
KernelGlobals *kg,
|
||||
ShaderData *shadow_sd,
|
||||
ccl_addr_space PathState *state,
|
||||
Ray *ray,
|
||||
Intersection *isect,
|
||||
const bool blocked,
|
||||
const bool is_transparent_isect,
|
||||
float3 *shadow)
|
||||
{
|
||||
*shadow = make_float3(1.0f, 1.0f, 1.0f);
|
||||
|
||||
if(ray_input->t == 0.0f)
|
||||
return false;
|
||||
|
||||
#ifdef __SPLIT_KERNEL__
|
||||
Ray private_ray = *ray_input;
|
||||
Ray *ray = &private_ray;
|
||||
#else
|
||||
Ray *ray = ray_input;
|
||||
#endif
|
||||
|
||||
#ifdef __SPLIT_KERNEL__
|
||||
Intersection *isect = &kg->isect_shadow[SD_THREAD];
|
||||
#else
|
||||
Intersection isect_object;
|
||||
Intersection *isect = &isect_object;
|
||||
#endif
|
||||
|
||||
bool blocked = scene_intersect(kg, *ray, PATH_RAY_SHADOW_OPAQUE, isect, NULL, 0.0f, 0.0f);
|
||||
|
||||
#ifdef __TRANSPARENT_SHADOWS__
|
||||
if(blocked && kernel_data.integrator.transparent_shadows) {
|
||||
if(shader_transparent_shadow(kg, isect)) {
|
||||
float3 throughput = make_float3(1.0f, 1.0f, 1.0f);
|
||||
float3 Pend = ray->P + ray->D*ray->t;
|
||||
int bounce = state->transparent_bounce;
|
||||
#ifdef __VOLUME__
|
||||
PathState ps = *state;
|
||||
#endif
|
||||
|
||||
for(;;) {
|
||||
if(bounce >= kernel_data.integrator.transparent_max_bounce)
|
||||
return true;
|
||||
|
||||
if(!scene_intersect(kg, *ray, PATH_RAY_SHADOW_TRANSPARENT, isect, NULL, 0.0f, 0.0f))
|
||||
{
|
||||
#ifdef __VOLUME__
|
||||
/* attenuation for last line segment towards light */
|
||||
if(ps.volume_stack[0].shader != SHADER_NONE)
|
||||
kernel_volume_shadow(kg, shadow_sd, &ps, ray, &throughput);
|
||||
#endif
|
||||
|
||||
*shadow *= throughput;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!shader_transparent_shadow(kg, isect)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef __VOLUME__
|
||||
/* attenuation between last surface and next surface */
|
||||
if(ps.volume_stack[0].shader != SHADER_NONE) {
|
||||
Ray segment_ray = *ray;
|
||||
segment_ray.t = isect->t;
|
||||
kernel_volume_shadow(kg, shadow_sd, &ps, &segment_ray, &throughput);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* setup shader data at surface */
|
||||
shader_setup_from_ray(kg, shadow_sd, isect, ray);
|
||||
|
||||
/* attenuation from transparent surface */
|
||||
if(!(ccl_fetch(shadow_sd, flag) & SD_HAS_ONLY_VOLUME)) {
|
||||
path_state_modify_bounce(state, true);
|
||||
shader_eval_surface(kg, shadow_sd, NULL, state, 0.0f, PATH_RAY_SHADOW, SHADER_CONTEXT_SHADOW);
|
||||
path_state_modify_bounce(state, false);
|
||||
|
||||
throughput *= shader_bsdf_transparency(kg, shadow_sd);
|
||||
}
|
||||
|
||||
/* stop if all light is blocked */
|
||||
if(is_zero(throughput)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* move ray forward */
|
||||
ray->P = ray_offset(ccl_fetch(shadow_sd, P), -ccl_fetch(shadow_sd, Ng));
|
||||
if(ray->t != FLT_MAX) {
|
||||
ray->D = normalize_len(Pend - ray->P, &ray->t);
|
||||
}
|
||||
|
||||
#ifdef __VOLUME__
|
||||
/* exit/enter volume */
|
||||
kernel_volume_stack_enter_exit(kg, shadow_sd, ps.volume_stack);
|
||||
#endif
|
||||
|
||||
bounce++;
|
||||
if(blocked && is_transparent_isect) {
|
||||
float3 throughput = make_float3(1.0f, 1.0f, 1.0f);
|
||||
float3 Pend = ray->P + ray->D*ray->t;
|
||||
int bounce = state->transparent_bounce;
|
||||
# ifdef __VOLUME__
|
||||
PathState ps = *state;
|
||||
# endif
|
||||
for(;;) {
|
||||
if(bounce >= kernel_data.integrator.transparent_max_bounce) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!scene_intersect(kg,
|
||||
*ray,
|
||||
PATH_RAY_SHADOW_TRANSPARENT,
|
||||
isect,
|
||||
NULL,
|
||||
0.0f, 0.0f))
|
||||
{
|
||||
break;
|
||||
}
|
||||
if(!shader_transparent_shadow(kg, isect)) {
|
||||
return true;
|
||||
}
|
||||
/* Attenuate the throughput. */
|
||||
if(shadow_handle_transparent_isect(kg,
|
||||
shadow_sd,
|
||||
state,
|
||||
#ifdef __VOLUME__
|
||||
else if(!blocked && state->volume_stack[0].shader != SHADER_NONE) {
|
||||
/* apply attenuation from current volume shader */
|
||||
&ps,
|
||||
#endif
|
||||
isect,
|
||||
ray,
|
||||
&throughput))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
/* Move ray forward. */
|
||||
ray->P = ray_offset(ccl_fetch(shadow_sd, P), -ccl_fetch(shadow_sd, Ng));
|
||||
if(ray->t != FLT_MAX) {
|
||||
ray->D = normalize_len(Pend - ray->P, &ray->t);
|
||||
}
|
||||
bounce++;
|
||||
}
|
||||
# ifdef __VOLUME__
|
||||
/* Attenuation for last line segment towards light. */
|
||||
if(ps.volume_stack[0].shader != SHADER_NONE) {
|
||||
kernel_volume_shadow(kg, shadow_sd, &ps, ray, &throughput);
|
||||
}
|
||||
# endif
|
||||
*shadow *= throughput;
|
||||
return is_zero(throughput);
|
||||
}
|
||||
# ifdef __VOLUME__
|
||||
if(!blocked && state->volume_stack[0].shader != SHADER_NONE) {
|
||||
/* Apply attenuation from current volume shader. */
|
||||
kernel_volume_shadow(kg, shadow_sd, state, ray, shadow);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
# endif
|
||||
return blocked;
|
||||
}
|
||||
|
||||
ccl_device bool shadow_blocked_transparent_stepped(
|
||||
KernelGlobals *kg,
|
||||
ShaderData *shadow_sd,
|
||||
ccl_addr_space PathState *state,
|
||||
Ray *ray,
|
||||
Intersection *isect,
|
||||
float3 *shadow)
|
||||
{
|
||||
const bool blocked = scene_intersect(kg,
|
||||
*ray,
|
||||
PATH_RAY_SHADOW_OPAQUE,
|
||||
isect,
|
||||
NULL,
|
||||
0.0f, 0.0f);
|
||||
const bool is_transparent_isect = blocked
|
||||
? shader_transparent_shadow(kg, isect)
|
||||
: false;
|
||||
return shadow_blocked_transparent_stepped_loop(kg,
|
||||
shadow_sd,
|
||||
state,
|
||||
ray,
|
||||
isect,
|
||||
blocked,
|
||||
is_transparent_isect,
|
||||
shadow);
|
||||
}
|
||||
|
||||
# endif /* __KERNEL_GPU__ */
|
||||
#endif /* __TRANSPARENT_SHADOWS__ */
|
||||
|
||||
ccl_device_inline bool shadow_blocked(KernelGlobals *kg,
|
||||
ShaderData *shadow_sd,
|
||||
ccl_addr_space PathState *state,
|
||||
ccl_addr_space Ray *ray_input,
|
||||
float3 *shadow)
|
||||
{
|
||||
/* Special trickery for split kernel: some data is coming from the
|
||||
* global memory.
|
||||
*/
|
||||
#ifdef __SPLIT_KERNEL__
|
||||
Ray private_ray = *ray_input;
|
||||
Ray *ray = &private_ray;
|
||||
Intersection *isect = &kg->isect_shadow[SD_THREAD];
|
||||
#else /* __SPLIT_KERNEL__ */
|
||||
Ray *ray = ray_input;
|
||||
Intersection isect_object;
|
||||
Intersection *isect = &isect_object;
|
||||
#endif /* __SPLIT_KERNEL__ */
|
||||
/* Some common early checks. */
|
||||
*shadow = make_float3(1.0f, 1.0f, 1.0f);
|
||||
if(ray->t == 0.0f) {
|
||||
return false;
|
||||
}
|
||||
/* Do actual shadow shading. */
|
||||
/* First of all, we check if integrator requires transparent shadows.
|
||||
* if not, we use simplest and fastest ever way to calculate occlusion.
|
||||
*/
|
||||
#ifdef __TRANSPARENT_SHADOWS__
|
||||
if(!kernel_data.integrator.transparent_shadows)
|
||||
#endif
|
||||
{
|
||||
return shadow_blocked_opaque(kg,
|
||||
shadow_sd,
|
||||
state,
|
||||
ray,
|
||||
isect,
|
||||
shadow);
|
||||
}
|
||||
#ifdef __TRANSPARENT_SHADOWS__
|
||||
# ifdef __SHADOW_RECORD_ALL__
|
||||
/* For the transparent shadows we try to use record-all logic on the
|
||||
* devices which supports this.
|
||||
*/
|
||||
const int transparent_max_bounce = kernel_data.integrator.transparent_max_bounce;
|
||||
/* Check transparent bounces here, for volume scatter which can do
|
||||
* lighting before surface path termination is checked.
|
||||
*/
|
||||
if(state->transparent_bounce >= transparent_max_bounce) {
|
||||
return true;
|
||||
}
|
||||
const uint max_hits = transparent_max_bounce - state->transparent_bounce - 1;
|
||||
# ifdef __KERNEL_GPU__
|
||||
/* On GPU we do trickey with tracing opaque ray first, this avoids speed
|
||||
* regressions in some files.
|
||||
*
|
||||
* TODO(sergey): Check why using record-all behavior causes slowdown in such
|
||||
* cases. Could that be caused by a higher spill pressure?
|
||||
*/
|
||||
const bool blocked = scene_intersect(kg,
|
||||
*ray,
|
||||
PATH_RAY_SHADOW_OPAQUE,
|
||||
isect,
|
||||
NULL,
|
||||
0.0f, 0.0f);
|
||||
const bool is_transparent_isect = blocked
|
||||
? shader_transparent_shadow(kg, isect)
|
||||
: false;
|
||||
if(!blocked || !is_transparent_isect ||
|
||||
max_hits + 1 >= SHADOW_STACK_MAX_HITS)
|
||||
{
|
||||
return shadow_blocked_transparent_stepped_loop(kg,
|
||||
shadow_sd,
|
||||
state,
|
||||
ray,
|
||||
isect,
|
||||
blocked,
|
||||
is_transparent_isect,
|
||||
shadow);
|
||||
}
|
||||
# endif /* __KERNEL_GPU__ */
|
||||
return shadow_blocked_transparent_all(kg,
|
||||
shadow_sd,
|
||||
state,
|
||||
ray,
|
||||
max_hits,
|
||||
shadow);
|
||||
# else /* __SHADOW_RECORD_ALL__ */
|
||||
/* Fallback to a slowest version which works on all devices. */
|
||||
return shadow_blocked_transparent_stepped(kg,
|
||||
shadow_sd,
|
||||
state,
|
||||
ray,
|
||||
isect,
|
||||
shadow);
|
||||
# endif /* __SHADOW_RECORD_ALL__ */
|
||||
#endif /* __TRANSPARENT_SHADOWS__ */
|
||||
}
|
||||
|
||||
#undef SHADOW_STACK_MAX_HITS
|
||||
|
||||
CCL_NAMESPACE_END
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ KERNEL_TEX(uint, texture_uint, __prim_visibility)
|
||||
KERNEL_TEX(uint, texture_uint, __prim_index)
|
||||
KERNEL_TEX(uint, texture_uint, __prim_object)
|
||||
KERNEL_TEX(uint, texture_uint, __object_node)
|
||||
KERNEL_TEX(float2, texture_float2, __prim_time)
|
||||
|
||||
/* objects */
|
||||
KERNEL_TEX(float4, texture_float4, __objects)
|
||||
@@ -177,7 +178,6 @@ KERNEL_IMAGE_TEX(uchar4, texture_image_uchar4, __tex_image_byte4_085)
|
||||
KERNEL_IMAGE_TEX(uchar4, texture_image_uchar4, __tex_image_byte4_086)
|
||||
KERNEL_IMAGE_TEX(uchar4, texture_image_uchar4, __tex_image_byte4_087)
|
||||
KERNEL_IMAGE_TEX(uchar4, texture_image_uchar4, __tex_image_byte4_088)
|
||||
KERNEL_IMAGE_TEX(uchar4, texture_image_uchar4, __tex_image_byte4_089)
|
||||
|
||||
# else
|
||||
/* bindless textures */
|
||||
|
||||
@@ -84,6 +84,7 @@ CCL_NAMESPACE_BEGIN
|
||||
# define __VOLUME_SCATTER__
|
||||
# define __SUBSURFACE__
|
||||
# define __CMJ__
|
||||
# define __SHADOW_RECORD_ALL__
|
||||
#endif /* __KERNEL_CUDA__ */
|
||||
|
||||
#ifdef __KERNEL_OPENCL__
|
||||
@@ -1201,7 +1202,8 @@ typedef struct KernelBVH {
|
||||
int have_curves;
|
||||
int have_instancing;
|
||||
int use_qbvh;
|
||||
int pad1, pad2;
|
||||
int use_bvh_steps;
|
||||
int pad1;
|
||||
} KernelBVH;
|
||||
static_assert_align(KernelBVH, 16);
|
||||
|
||||
|
||||
@@ -130,8 +130,10 @@ kernel_cuda_path_trace(float *buffer, uint *rng_state, int sample, int sx, int s
|
||||
int x = sx + blockDim.x*blockIdx.x + threadIdx.x;
|
||||
int y = sy + blockDim.y*blockIdx.y + threadIdx.y;
|
||||
|
||||
if(x < sx + sw && y < sy + sh)
|
||||
kernel_path_trace(NULL, buffer, rng_state, sample, x, y, offset, stride);
|
||||
if(x < sx + sw && y < sy + sh) {
|
||||
KernelGlobals kg;
|
||||
kernel_path_trace(&kg, buffer, rng_state, sample, x, y, offset, stride);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __BRANCHED_PATH__
|
||||
@@ -142,8 +144,10 @@ kernel_cuda_branched_path_trace(float *buffer, uint *rng_state, int sample, int
|
||||
int x = sx + blockDim.x*blockIdx.x + threadIdx.x;
|
||||
int y = sy + blockDim.y*blockIdx.y + threadIdx.y;
|
||||
|
||||
if(x < sx + sw && y < sy + sh)
|
||||
kernel_branched_path_trace(NULL, buffer, rng_state, sample, x, y, offset, stride);
|
||||
if(x < sx + sw && y < sy + sh) {
|
||||
KernelGlobals kg;
|
||||
kernel_branched_path_trace(&kg, buffer, rng_state, sample, x, y, offset, stride);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -154,8 +158,9 @@ kernel_cuda_convert_to_byte(uchar4 *rgba, float *buffer, float sample_scale, int
|
||||
int x = sx + blockDim.x*blockIdx.x + threadIdx.x;
|
||||
int y = sy + blockDim.y*blockIdx.y + threadIdx.y;
|
||||
|
||||
if(x < sx + sw && y < sy + sh)
|
||||
if(x < sx + sw && y < sy + sh) {
|
||||
kernel_film_convert_to_byte(NULL, rgba, buffer, sample_scale, x, y, offset, stride);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void
|
||||
@@ -165,8 +170,9 @@ kernel_cuda_convert_to_half_float(uchar4 *rgba, float *buffer, float sample_scal
|
||||
int x = sx + blockDim.x*blockIdx.x + threadIdx.x;
|
||||
int y = sy + blockDim.y*blockIdx.y + threadIdx.y;
|
||||
|
||||
if(x < sx + sw && y < sy + sh)
|
||||
if(x < sx + sw && y < sy + sh) {
|
||||
kernel_film_convert_to_half_float(NULL, rgba, buffer, sample_scale, x, y, offset, stride);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" __global__ void
|
||||
@@ -183,7 +189,8 @@ kernel_cuda_shader(uint4 *input,
|
||||
int x = sx + blockDim.x*blockIdx.x + threadIdx.x;
|
||||
|
||||
if(x < sx + sw) {
|
||||
kernel_shader_evaluate(NULL,
|
||||
KernelGlobals kg;
|
||||
kernel_shader_evaluate(&kg,
|
||||
input,
|
||||
output,
|
||||
output_luma,
|
||||
@@ -200,8 +207,10 @@ kernel_cuda_bake(uint4 *input, float4 *output, int type, int filter, int sx, int
|
||||
{
|
||||
int x = sx + blockDim.x*blockIdx.x + threadIdx.x;
|
||||
|
||||
if(x < sx + sw)
|
||||
kernel_bake_evaluate(NULL, input, output, (ShaderEvalType)type, filter, x, offset, sample);
|
||||
if(x < sx + sw) {
|
||||
KernelGlobals kg;
|
||||
kernel_bake_evaluate(&kg, input, output, (ShaderEvalType)type, filter, x, offset, sample);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -144,7 +144,6 @@ ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y,
|
||||
case 86: r = kernel_tex_image_interp(__tex_image_byte4_086, x, y); break;
|
||||
case 87: r = kernel_tex_image_interp(__tex_image_byte4_087, x, y); break;
|
||||
case 88: r = kernel_tex_image_interp(__tex_image_byte4_088, x, y); break;
|
||||
case 89: r = kernel_tex_image_interp(__tex_image_byte4_089, x, y); break;
|
||||
default:
|
||||
kernel_assert(0);
|
||||
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
||||
@@ -73,7 +73,7 @@ public:
|
||||
|
||||
bool need_update;
|
||||
|
||||
int total_pixel_samples;
|
||||
size_t total_pixel_samples;
|
||||
|
||||
private:
|
||||
BakeData *m_bake_data;
|
||||
|
||||
@@ -486,10 +486,18 @@ static void background_cdf(int start,
|
||||
float2 *cond_cdf)
|
||||
{
|
||||
/* Conditional CDFs (rows, U direction). */
|
||||
/* NOTE: It is possible to have some NaN pixels on background
|
||||
* which will ruin CDF causing wrong shading. We replace such
|
||||
* pixels with black.
|
||||
*/
|
||||
for(int i = start; i < end; i++) {
|
||||
float sin_theta = sinf(M_PI_F * (i + 0.5f) / res);
|
||||
float3 env_color = (*pixels)[i * res];
|
||||
float ave_luminance = average(env_color);
|
||||
/* TODO(sergey): Consider adding average_safe(). */
|
||||
if(!isfinite(ave_luminance)) {
|
||||
ave_luminance = 0.0f;
|
||||
}
|
||||
|
||||
cond_cdf[i * cdf_count].x = ave_luminance * sin_theta;
|
||||
cond_cdf[i * cdf_count].y = 0.0f;
|
||||
@@ -497,6 +505,9 @@ static void background_cdf(int start,
|
||||
for(int j = 1; j < res; j++) {
|
||||
env_color = (*pixels)[i * res + j];
|
||||
ave_luminance = average(env_color);
|
||||
if(!isfinite(ave_luminance)) {
|
||||
ave_luminance = 0.0f;
|
||||
}
|
||||
|
||||
cond_cdf[i * cdf_count + j].x = ave_luminance * sin_theta;
|
||||
cond_cdf[i * cdf_count + j].y = cond_cdf[i * cdf_count + j - 1].y + cond_cdf[i * cdf_count + j - 1].x / res;
|
||||
|
||||
@@ -1873,9 +1873,14 @@ void MeshManager::device_update_bvh(Device *device, DeviceScene *dscene, Scene *
|
||||
dscene->prim_object.reference((uint*)&pack.prim_object[0], pack.prim_object.size());
|
||||
device->tex_alloc("__prim_object", dscene->prim_object);
|
||||
}
|
||||
if(pack.prim_time.size()) {
|
||||
dscene->prim_time.reference((float2*)&pack.prim_time[0], pack.prim_time.size());
|
||||
device->tex_alloc("__prim_time", dscene->prim_time);
|
||||
}
|
||||
|
||||
dscene->data.bvh.root = pack.root_index;
|
||||
dscene->data.bvh.use_qbvh = scene->params.use_qbvh;
|
||||
dscene->data.bvh.use_bvh_steps = (scene->params.num_bvh_time_steps != 0);
|
||||
}
|
||||
|
||||
void MeshManager::device_update_flags(Device * /*device*/,
|
||||
@@ -2152,6 +2157,7 @@ void MeshManager::device_free(Device *device, DeviceScene *dscene)
|
||||
device->tex_free(dscene->prim_visibility);
|
||||
device->tex_free(dscene->prim_index);
|
||||
device->tex_free(dscene->prim_object);
|
||||
device->tex_free(dscene->prim_time);
|
||||
device->tex_free(dscene->tri_shader);
|
||||
device->tex_free(dscene->tri_vnormal);
|
||||
device->tex_free(dscene->tri_vindex);
|
||||
@@ -2173,6 +2179,7 @@ void MeshManager::device_free(Device *device, DeviceScene *dscene)
|
||||
dscene->prim_visibility.clear();
|
||||
dscene->prim_index.clear();
|
||||
dscene->prim_object.clear();
|
||||
dscene->prim_time.clear();
|
||||
dscene->tri_shader.clear();
|
||||
dscene->tri_vnormal.clear();
|
||||
dscene->tri_vindex.clear();
|
||||
|
||||
@@ -69,6 +69,7 @@ public:
|
||||
device_vector<uint> prim_visibility;
|
||||
device_vector<uint> prim_index;
|
||||
device_vector<uint> prim_object;
|
||||
device_vector<float2> prim_time;
|
||||
|
||||
/* mesh */
|
||||
device_vector<uint> tri_shader;
|
||||
|
||||
25
make.bat
25
make.bat
@@ -5,8 +5,8 @@ REM This is for users who like to configure & build Blender with a single comman
|
||||
setlocal ENABLEEXTENSIONS
|
||||
set BLENDER_DIR=%~dp0
|
||||
set BLENDER_DIR_NOSPACES=%BLENDER_DIR: =%
|
||||
if not "%BLENDER_DIR%"=="%BLENDER_DIR_NOSPACES%" (
|
||||
echo There are spaces detected in the build path "%BLENDER_DIR%", this is currently not supported, exiting....
|
||||
if not "%BLENDER_DIR%"=="%BLENDER_DIR_NOSPACES%" (
|
||||
echo There are spaces detected in the build path "%BLENDER_DIR%", this is currently not supported, exiting....
|
||||
goto EOF
|
||||
)
|
||||
set BUILD_DIR=%BLENDER_DIR%..\build_windows
|
||||
@@ -79,7 +79,7 @@ if NOT "%1" == "" (
|
||||
set NOBUILD=1
|
||||
) else if "%1" == "showhash" (
|
||||
for /f "delims=" %%i in ('git rev-parse HEAD') do echo Branch_hash=%%i
|
||||
cd release/datafiles/locale
|
||||
cd release/datafiles/locale
|
||||
for /f "delims=" %%i in ('git rev-parse HEAD') do echo Locale_hash=%%i
|
||||
cd %~dp0
|
||||
cd release/scripts/addons
|
||||
@@ -132,13 +132,13 @@ if "%BUILD_ARCH%"=="x64" (
|
||||
|
||||
|
||||
if "%target%"=="Release" (
|
||||
rem for vc12 check for both cuda 7.5 and 8
|
||||
rem for vc12 check for both cuda 7.5 and 8
|
||||
if "%CUDA_PATH%"=="" (
|
||||
echo Cuda Not found, aborting!
|
||||
goto EOF
|
||||
)
|
||||
set BUILD_CMAKE_ARGS=%BUILD_CMAKE_ARGS% ^
|
||||
-C"%BLENDER_DIR%\build_files\cmake\config\blender_release.cmake"
|
||||
-C"%BLENDER_DIR%\build_files\cmake\config\blender_release.cmake"
|
||||
)
|
||||
|
||||
:DetectMSVC
|
||||
@@ -157,7 +157,7 @@ if DEFINED MSVC_VC_DIR goto msvc_detect_finally
|
||||
if DEFINED MSVC_VC_DIR call "%MSVC_VC_DIR%\vcvarsall.bat"
|
||||
if DEFINED MSVC_VC_DIR goto sanity_checks
|
||||
|
||||
rem MSVC Build environment 2017 and up.
|
||||
rem MSVC Build environment 2017 and up.
|
||||
for /F "usebackq skip=2 tokens=1-2*" %%A IN (`REG QUERY "HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\SXS\VS7" /v %BUILD_VS_VER%.0 2^>nul`) DO set MSVC_VS_DIR=%%C
|
||||
if DEFINED MSVC_VS_DIR goto msvc_detect_finally_2017
|
||||
REM Check 32 bits
|
||||
@@ -202,7 +202,7 @@ if NOT EXIST %BLENDER_DIR%..\lib\nul (
|
||||
if "%TARGET%"=="" (
|
||||
echo Error: Convenience target not set
|
||||
echo This is required for building, aborting!
|
||||
echo .
|
||||
echo .
|
||||
goto HELP
|
||||
)
|
||||
|
||||
@@ -266,15 +266,15 @@ echo.
|
||||
echo At any point you can optionally modify your build configuration by editing:
|
||||
echo "%BUILD_DIR%\CMakeCache.txt", then run "make" again to build with the changes applied.
|
||||
echo.
|
||||
echo Blender successfully built, run from: "%BUILD_DIR%\bin\%BUILD_TYPE%"
|
||||
echo Blender successfully built, run from: "%BUILD_DIR%\bin\%BUILD_TYPE%\blender.exe"
|
||||
echo.
|
||||
goto EOF
|
||||
:HELP
|
||||
echo.
|
||||
echo Convenience targets
|
||||
echo - release ^(identical to the offical blender.org builds^)
|
||||
echo - release ^(identical to the official blender.org builds^)
|
||||
echo - full ^(same as release minus the cuda kernels^)
|
||||
echo - lite
|
||||
echo - lite
|
||||
echo - headless
|
||||
echo - cycles
|
||||
echo - bpy
|
||||
@@ -289,11 +289,10 @@ goto EOF
|
||||
echo - with_tests ^(enable building unit tests^)
|
||||
echo - debug ^(Build an unoptimized debuggable build^)
|
||||
echo - packagename [newname] ^(override default cpack package name^)
|
||||
echo - x86 ^(override host autodetect and build 32 bit code^)
|
||||
echo - x64 ^(override host autodetect and build 64 bit code^)
|
||||
echo - x86 ^(override host auto-detect and build 32 bit code^)
|
||||
echo - x64 ^(override host auto-detect and build 64 bit code^)
|
||||
echo - 2013 ^(build with visual studio 2013^)
|
||||
echo - 2015 ^(build with visual studio 2015^) [EXPERIMENTAL]
|
||||
echo.
|
||||
|
||||
:EOF
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
height="640"
|
||||
id="svg2"
|
||||
sodipodi:version="0.32"
|
||||
inkscape:version="0.91 r"
|
||||
inkscape:version="0.91 r13725"
|
||||
version="1.0"
|
||||
sodipodi:docname="blender_icons.svg"
|
||||
inkscape:output_extension="org.inkscape.output.svg.inkscape"
|
||||
@@ -31338,6 +31338,26 @@
|
||||
d="m 125.5,433.5 23,0 0,41 -33,0 0,-31 10,-10 z"
|
||||
style="display:inline;fill:url(#linearGradient13110);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1;marker:none" />
|
||||
</clipPath>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient1610-6"
|
||||
id="linearGradient18199"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="189.76083"
|
||||
y1="248.13905"
|
||||
x2="116.05637"
|
||||
y2="183.6826" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient22562"
|
||||
id="radialGradient23167-6"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.99220964,-0.12457927,0.11585516,0.92272644,-34.13325,22.766225)"
|
||||
cx="-0.78262758"
|
||||
cy="294.63174"
|
||||
fx="-0.78262758"
|
||||
fy="294.63174"
|
||||
r="6.6750002" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
@@ -31349,16 +31369,16 @@
|
||||
objecttolerance="10000"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="14.413868"
|
||||
inkscape:cx="480.24726"
|
||||
inkscape:cy="269.95478"
|
||||
inkscape:zoom="19.997864"
|
||||
inkscape:cx="462.52244"
|
||||
inkscape:cy="435.14241"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
inkscape:current-layer="g23149-4"
|
||||
showgrid="true"
|
||||
inkscape:window-width="1680"
|
||||
inkscape:window-height="1020"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="30"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1025"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:snap-nodes="true"
|
||||
inkscape:snap-bbox="true"
|
||||
showguides="true"
|
||||
@@ -92660,6 +92680,56 @@
|
||||
style="opacity:0.51999996;fill:url(#radialGradient21448-8-143);fill-opacity:1;fill-rule:evenodd;stroke:none" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
transform="translate(335.99871,21.048284)"
|
||||
style="display:inline;enable-background:new"
|
||||
id="ICON_ROTATE-7">
|
||||
<rect
|
||||
y="178"
|
||||
x="110"
|
||||
height="16"
|
||||
width="16"
|
||||
id="rect37989-8"
|
||||
style="display:inline;overflow:visible;visibility:visible;opacity:0;fill:#b3b3b3;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.79999995;marker:none;enable-background:accumulate" />
|
||||
<path
|
||||
style="display:inline;overflow:visible;visibility:visible;fill:none;stroke:#000000;stroke-width:2.4000001;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:new"
|
||||
d="m 114.5,192.5 -3,0 0,-3 m 13,0 0,3 -3,0 m -0.25,-13 3.25,0 0,3 m -13,0 0,-3 3,0"
|
||||
id="path37498-7"
|
||||
sodipodi:nodetypes="cccccccccccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
sodipodi:nodetypes="cccccccccccc"
|
||||
id="rect38140-7"
|
||||
d="m 114.5,192.5 -3,0 0,-3 m 13,0 0,3 -3,0 m -0.25,-13 3.25,0 0,3 m -13,0 0,-3 3,0"
|
||||
style="display:inline;overflow:visible;visibility:visible;fill:none;stroke:url(#linearGradient18199);stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:new"
|
||||
inkscape:connector-curvature="0" />
|
||||
<g
|
||||
transform="matrix(0.59971056,0,0,0.59971056,116.78278,9.7425599)"
|
||||
style="display:inline;enable-background:new"
|
||||
id="g23145-9">
|
||||
<g
|
||||
id="g23149-4">
|
||||
<path
|
||||
id="path39832-9"
|
||||
style="display:inline;overflow:visible;visibility:visible;fill:none;stroke:#1a1a1a;stroke-width:4.66725159;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
|
||||
d="m -4.3682386,287.81345 1.5,0 c 0.999089,0 2.07885534,1.30514 2.50490386,2.78207 1.06592652,3.69512 2.80867074,9.82446 5.88525404,9.96406 2.6782554,0 1.6181317,-5.11535 3.1736046,-5.26275 l 0.25,0"
|
||||
sodipodi:nodetypes="cssccc"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccscc"
|
||||
d="m 9.3647983,295.22328 -0.4793018,0 c -2.2335161,0 0.1796731,4.94901 -3.4398065,5.09984 -4.44796752,0.18536 -5.37272213,-12.59185 -8.0767581,-12.56237 l -2,0"
|
||||
style="display:inline;overflow:visible;visibility:visible;fill:none;stroke:#a8df84;stroke-width:2.93474906;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
|
||||
id="path39834-2"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<path
|
||||
id="path39836-9"
|
||||
style="display:inline;overflow:visible;visibility:visible;opacity:0.35;fill:none;stroke:url(#radialGradient23167-6);stroke-width:3.53503864;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
|
||||
d="M 5.6770841,300.48165 C 0.7393262,300.21066 0.54777814,287.99792 -2.9522219,287.99792"
|
||||
sodipodi:nodetypes="cc"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
|
||||
|
Before Width: | Height: | Size: 4.4 MiB After Width: | Height: | Size: 4.4 MiB |
BIN
release/datafiles/blender_icons16/icon16_normalize_fcurves.dat
Normal file
BIN
release/datafiles/blender_icons16/icon16_normalize_fcurves.dat
Normal file
Binary file not shown.
BIN
release/datafiles/blender_icons32/icon32_normalize_fcurves.dat
Normal file
BIN
release/datafiles/blender_icons32/icon32_normalize_fcurves.dat
Normal file
Binary file not shown.
@@ -1170,6 +1170,7 @@ class Seed:
|
||||
|
||||
_seed = Seed()
|
||||
|
||||
|
||||
def get_dashed_pattern(linestyle):
|
||||
"""Extracts the dashed pattern from the various UI options """
|
||||
pattern = []
|
||||
@@ -1185,6 +1186,15 @@ def get_dashed_pattern(linestyle):
|
||||
return pattern
|
||||
|
||||
|
||||
def get_grouped_objects(group):
|
||||
for ob in group.objects:
|
||||
if ob.dupli_type == 'GROUP' and ob.dupli_group is not None:
|
||||
for dupli in get_grouped_objects(ob.dupli_group):
|
||||
yield dupli
|
||||
else:
|
||||
yield ob
|
||||
|
||||
|
||||
integration_types = {
|
||||
'MEAN': IntegrationType.MEAN,
|
||||
'MIN': IntegrationType.MIN,
|
||||
@@ -1267,7 +1277,7 @@ def process(layer_name, lineset_name):
|
||||
# prepare selection criteria by group of objects
|
||||
if lineset.select_by_group:
|
||||
if lineset.group is not None:
|
||||
names = {getQualifiedObjectName(ob): True for ob in lineset.group.objects}
|
||||
names = {getQualifiedObjectName(ob): True for ob in get_grouped_objects(lineset.group)}
|
||||
upred = ObjectNamesUP1D(names, lineset.group_negation == 'EXCLUSIVE')
|
||||
selection_criteria.append(upred)
|
||||
# prepare selection criteria by image border
|
||||
|
||||
@@ -1320,7 +1320,9 @@ class DATA_PT_modifiers(ModifierButtonsPanel, Panel):
|
||||
row.prop(md, "thickness_vertex_group", text="Factor")
|
||||
|
||||
col.prop(md, "use_crease", text="Crease Edges")
|
||||
col.prop(md, "crease_weight", text="Crease Weight")
|
||||
row = col.row()
|
||||
row.active = md.use_crease
|
||||
row.prop(md, "crease_weight", text="Crease Weight")
|
||||
|
||||
col = split.column()
|
||||
|
||||
|
||||
@@ -152,6 +152,33 @@ class OBJECT_PT_relations(ObjectButtonsPanel, Panel):
|
||||
sub.active = (parent is not None)
|
||||
|
||||
|
||||
class OBJECT_PT_relations_extras(ObjectButtonsPanel, Panel):
|
||||
bl_label = "Relations Extras"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
ob = context.object
|
||||
|
||||
split = layout.split()
|
||||
|
||||
if context.scene.render.engine != 'BLENDER_GAME':
|
||||
col = split.column()
|
||||
col.label(text="Tracking Axes:")
|
||||
col.prop(ob, "track_axis", text="Axis")
|
||||
col.prop(ob, "up_axis", text="Up Axis")
|
||||
|
||||
col = split.column()
|
||||
col.prop(ob, "use_slow_parent")
|
||||
row = col.row()
|
||||
row.active = ((ob.parent is not None) and (ob.use_slow_parent))
|
||||
row.prop(ob, "slow_parent_offset", text="Offset")
|
||||
|
||||
layout.prop(ob, "use_extra_recalc_object")
|
||||
layout.prop(ob, "use_extra_recalc_data")
|
||||
|
||||
|
||||
class GROUP_MT_specials(Menu):
|
||||
bl_label = "Group Specials"
|
||||
|
||||
@@ -296,33 +323,6 @@ class OBJECT_PT_duplication(ObjectButtonsPanel, Panel):
|
||||
layout.prop(ob, "dupli_group", text="Group")
|
||||
|
||||
|
||||
class OBJECT_PT_relations_extras(ObjectButtonsPanel, Panel):
|
||||
bl_label = "Relations Extras"
|
||||
bl_options = {'DEFAULT_CLOSED'}
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
ob = context.object
|
||||
|
||||
split = layout.split()
|
||||
|
||||
if context.scene.render.engine != 'BLENDER_GAME':
|
||||
col = split.column()
|
||||
col.label(text="Tracking Axes:")
|
||||
col.prop(ob, "track_axis", text="Axis")
|
||||
col.prop(ob, "up_axis", text="Up Axis")
|
||||
|
||||
col = split.column()
|
||||
col.prop(ob, "use_slow_parent")
|
||||
row = col.row()
|
||||
row.active = ((ob.parent is not None) and (ob.use_slow_parent))
|
||||
row.prop(ob, "slow_parent_offset", text="Offset")
|
||||
|
||||
layout.prop(ob, "use_extra_recalc_object")
|
||||
layout.prop(ob, "use_extra_recalc_data")
|
||||
|
||||
|
||||
from bl_ui.properties_animviz import (
|
||||
MotionPathButtonsPanel,
|
||||
OnionSkinButtonsPanel,
|
||||
|
||||
@@ -42,10 +42,11 @@ class GRAPH_HT_header(Header):
|
||||
|
||||
dopesheet_filter(layout, context)
|
||||
|
||||
layout.prop(st, "use_normalization", text="Normalize")
|
||||
row = layout.row()
|
||||
row.active = st.use_normalization
|
||||
row.prop(st, "use_auto_normalization", text="Auto")
|
||||
row = layout.row(align=True)
|
||||
row.prop(st, "use_normalization", icon='NORMALIZE_FCURVES', text="Normalize", toggle=True)
|
||||
sub = row.row(align=True)
|
||||
sub.active = st.use_normalization
|
||||
sub.prop(st, "use_auto_normalization", icon='FILE_REFRESH', text="", toggle=True)
|
||||
|
||||
row = layout.row(align=True)
|
||||
|
||||
|
||||
@@ -652,17 +652,39 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel):
|
||||
col.prop(strip, "rotation_start", text="Rotation")
|
||||
|
||||
elif strip.type == 'MULTICAM':
|
||||
layout.prop(strip, "multicam_source")
|
||||
col = layout.column(align=True)
|
||||
strip_channel = strip.channel
|
||||
|
||||
row = layout.row(align=True)
|
||||
sub = row.row(align=True)
|
||||
sub.scale_x = 2.0
|
||||
col.prop(strip, "multicam_source", text="Source Channel")
|
||||
|
||||
sub.operator("screen.animation_play", text="", icon='PAUSE' if context.screen.is_animation_playing else 'PLAY')
|
||||
# The multicam strip needs at least 2 strips to be useful
|
||||
if strip_channel > 2:
|
||||
BT_ROW = 4
|
||||
|
||||
col.label("Cut To:")
|
||||
row = col.row()
|
||||
|
||||
for i in range(1, strip_channel):
|
||||
if (i % BT_ROW) == 1:
|
||||
row = col.row(align=True)
|
||||
|
||||
# Workaround - .active has to have a separate UI block to work
|
||||
if i == strip.multicam_source:
|
||||
sub = row.row(align=True)
|
||||
sub.active = False
|
||||
sub.operator("sequencer.cut_multicam", text="%d" % i).camera = i
|
||||
else:
|
||||
sub_1 = row.row(align=True)
|
||||
sub_1.active = True
|
||||
sub_1.operator("sequencer.cut_multicam", text="%d" % i).camera = i
|
||||
|
||||
if strip.channel > BT_ROW and (strip_channel - 1) % BT_ROW:
|
||||
for i in range(strip.channel, strip_channel + ((BT_ROW + 1 - strip_channel) % BT_ROW)):
|
||||
row.label("")
|
||||
else:
|
||||
col.separator()
|
||||
col.label(text="Two or more channels are needed below this strip.", icon="INFO")
|
||||
|
||||
row.label("Cut To")
|
||||
for i in range(1, strip.channel):
|
||||
row.operator("sequencer.cut_multicam", text="%d" % i).camera = i
|
||||
|
||||
elif strip.type == 'TEXT':
|
||||
col = layout.column()
|
||||
|
||||
@@ -49,7 +49,10 @@ class TIME_HT_header(Header):
|
||||
row.prop(scene, "frame_preview_start", text="Start")
|
||||
row.prop(scene, "frame_preview_end", text="End")
|
||||
|
||||
layout.prop(scene, "frame_current", text="")
|
||||
if scene.show_subframe:
|
||||
layout.prop(scene, "frame_float", text="")
|
||||
else:
|
||||
layout.prop(scene, "frame_current", text="")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@@ -135,6 +138,7 @@ class TIME_MT_view(Menu):
|
||||
|
||||
layout.prop(st, "show_frame_indicator")
|
||||
layout.prop(scene, "show_keys_from_selected_only")
|
||||
layout.prop(scene, "show_subframe")
|
||||
|
||||
layout.separator()
|
||||
|
||||
|
||||
@@ -1722,6 +1722,7 @@ class VIEW3D_MT_brush_paint_modes(Menu):
|
||||
layout.prop(brush, "use_paint_weight", text="Weight Paint")
|
||||
layout.prop(brush, "use_paint_image", text="Texture Paint")
|
||||
|
||||
|
||||
# ********** Vertex paint menu **********
|
||||
|
||||
|
||||
@@ -1791,6 +1792,7 @@ class VIEW3D_MT_vertex_group(Menu):
|
||||
layout.operator("object.vertex_group_remove", text="Remove Active Group").all = False
|
||||
layout.operator("object.vertex_group_remove", text="Remove All Groups").all = True
|
||||
|
||||
|
||||
# ********** Weight paint menu **********
|
||||
|
||||
|
||||
@@ -1829,6 +1831,7 @@ class VIEW3D_MT_paint_weight(Menu):
|
||||
|
||||
layout.operator("paint.weight_set")
|
||||
|
||||
|
||||
# ********** Sculpt menu **********
|
||||
|
||||
|
||||
@@ -1982,6 +1985,7 @@ class VIEW3D_MT_particle_specials(Menu):
|
||||
class VIEW3D_MT_particle_showhide(ShowHideMenu, Menu):
|
||||
_operator_name = "particle"
|
||||
|
||||
|
||||
# ********** Pose Menu **********
|
||||
|
||||
|
||||
@@ -2255,6 +2259,7 @@ class VIEW3D_MT_bone_options_disable(Menu, BoneOptions):
|
||||
bl_label = "Disable Bone Options"
|
||||
type = 'DISABLE'
|
||||
|
||||
|
||||
# ********** Edit Menus, suffix from ob.type **********
|
||||
|
||||
|
||||
@@ -2422,6 +2427,7 @@ class VIEW3D_MT_edit_mesh_vertices(Menu):
|
||||
with_bullet = bpy.app.build_options.bullet
|
||||
|
||||
layout.operator("mesh.merge")
|
||||
layout.operator("mesh.remove_doubles")
|
||||
layout.operator("mesh.rip_move")
|
||||
layout.operator("mesh.rip_move_fill")
|
||||
layout.operator("mesh.rip_edge_move")
|
||||
@@ -2444,7 +2450,6 @@ class VIEW3D_MT_edit_mesh_vertices(Menu):
|
||||
if with_bullet:
|
||||
layout.operator("mesh.convex_hull")
|
||||
layout.operator("mesh.vertices_smooth")
|
||||
layout.operator("mesh.remove_doubles")
|
||||
|
||||
layout.operator("mesh.blend_from_shape")
|
||||
|
||||
@@ -2601,6 +2606,7 @@ class VIEW3D_MT_edit_mesh_clean(Menu):
|
||||
layout.operator("mesh.face_make_planar")
|
||||
layout.operator("mesh.vert_connect_nonplanar")
|
||||
layout.operator("mesh.vert_connect_concave")
|
||||
layout.operator("mesh.remove_doubles")
|
||||
layout.operator("mesh.fill_holes")
|
||||
|
||||
|
||||
|
||||
@@ -113,25 +113,25 @@ static OArchive create_archive(std::ostream *ostream,
|
||||
Alembic::Abc::MetaData &md,
|
||||
bool ogawa)
|
||||
{
|
||||
md.set(Alembic::Abc::kApplicationNameKey, "Blender");
|
||||
md.set(Alembic::Abc::kApplicationNameKey, "Blender");
|
||||
md.set(Alembic::Abc::kUserDescriptionKey, scene_name);
|
||||
|
||||
time_t raw_time;
|
||||
time(&raw_time);
|
||||
char buffer[128];
|
||||
time_t raw_time;
|
||||
time(&raw_time);
|
||||
char buffer[128];
|
||||
|
||||
#if defined _WIN32 || defined _WIN64
|
||||
ctime_s(buffer, 128, &raw_time);
|
||||
ctime_s(buffer, 128, &raw_time);
|
||||
#else
|
||||
ctime_r(&raw_time, buffer);
|
||||
ctime_r(&raw_time, buffer);
|
||||
#endif
|
||||
|
||||
const std::size_t buffer_len = strlen(buffer);
|
||||
if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') {
|
||||
buffer[buffer_len - 1] = '\0';
|
||||
}
|
||||
const std::size_t buffer_len = strlen(buffer);
|
||||
if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') {
|
||||
buffer[buffer_len - 1] = '\0';
|
||||
}
|
||||
|
||||
md.set(Alembic::Abc::kDateWrittenKey, buffer);
|
||||
md.set(Alembic::Abc::kDateWrittenKey, buffer);
|
||||
|
||||
ErrorHandler::Policy policy = ErrorHandler::kThrowPolicy;
|
||||
|
||||
|
||||
@@ -361,7 +361,7 @@ void read_curve_sample(Curve *cu, const ICurvesSchema &schema, const float time)
|
||||
* object directly and create a new DerivedMesh from that. Also we might need to
|
||||
* create new or delete existing NURBS in the curve.
|
||||
*/
|
||||
DerivedMesh *AbcCurveReader::read_derivedmesh(DerivedMesh */*dm*/, const float time, int /*read_flag*/, const char **/*err_str*/)
|
||||
DerivedMesh *AbcCurveReader::read_derivedmesh(DerivedMesh * /*dm*/, const float time, int /*read_flag*/, const char ** /*err_str*/)
|
||||
{
|
||||
ISampleSelector sample_sel(time);
|
||||
const ICurvesSchema::Sample sample = m_curves_schema.getValue(sample_sel);
|
||||
|
||||
@@ -47,7 +47,7 @@ extern "C" {
|
||||
|
||||
#ifdef WIN32
|
||||
/* needed for MSCV because of snprintf from BLI_string */
|
||||
# include "BLI_winstuff.h"
|
||||
# include "BLI_winstuff.h"
|
||||
#endif
|
||||
|
||||
#include "BKE_anim.h"
|
||||
|
||||
@@ -200,7 +200,7 @@ void read_points_sample(const IPointsSchema &schema,
|
||||
read_mverts(config.mvert, positions, vnormals);
|
||||
}
|
||||
|
||||
DerivedMesh *AbcPointsReader::read_derivedmesh(DerivedMesh *dm, const float time, int /*read_flag*/, const char **/*err_str*/)
|
||||
DerivedMesh *AbcPointsReader::read_derivedmesh(DerivedMesh *dm, const float time, int /*read_flag*/, const char ** /*err_str*/)
|
||||
{
|
||||
ISampleSelector sample_sel(time);
|
||||
const IPointsSchema::Sample sample = m_schema.getValue(sample_sel);
|
||||
|
||||
@@ -122,7 +122,7 @@ Imath::Box3d AbcTransformWriter::bounds()
|
||||
return Imath::transform(bounds, m_matrix);
|
||||
}
|
||||
|
||||
bool AbcTransformWriter::hasAnimation(Object */*ob*/) const
|
||||
bool AbcTransformWriter::hasAnimation(Object * /*ob*/) const
|
||||
{
|
||||
/* TODO(kevin): implement this. */
|
||||
return true;
|
||||
@@ -146,6 +146,6 @@ bool AbcEmptyReader::valid() const
|
||||
|
||||
void AbcEmptyReader::readObjectData(Main *bmain, float /*time*/)
|
||||
{
|
||||
m_object = BKE_object_add_only_object(bmain, OB_EMPTY, m_object_name.c_str());
|
||||
m_object = BKE_object_add_only_object(bmain, OB_EMPTY, m_data_name.c_str());
|
||||
m_object->data = NULL;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,6 @@ typedef struct BVHTreeFromEditMesh {
|
||||
/* default callbacks to bvh nearest and raycast */
|
||||
BVHTree_NearestPointCallback nearest_callback;
|
||||
BVHTree_RayCastCallback raycast_callback;
|
||||
BVHTree_NearestToRayCallback nearest_to_ray_callback;
|
||||
|
||||
struct BMEditMesh *em;
|
||||
|
||||
@@ -75,7 +74,6 @@ typedef struct BVHTreeFromMesh {
|
||||
/* default callbacks to bvh nearest and raycast */
|
||||
BVHTree_NearestPointCallback nearest_callback;
|
||||
BVHTree_RayCastCallback raycast_callback;
|
||||
BVHTree_NearestToRayCallback nearest_to_ray_callback;
|
||||
|
||||
/* Vertex array, so that callbacks have instante access to data */
|
||||
const struct MVert *vert;
|
||||
@@ -104,7 +102,7 @@ typedef struct BVHTreeFromMesh {
|
||||
* The tree is build in mesh space coordinates, this means special care must be made on queries
|
||||
* so that the coordinates and rays are first translated on the mesh local coordinates.
|
||||
* Reason for this is that bvh_from_mesh_* can use a cache in some cases and so it becomes possible to reuse a BVHTree.
|
||||
*
|
||||
*
|
||||
* free_bvhtree_from_mesh should be called when the tree is no longer needed.
|
||||
*/
|
||||
BVHTree *bvhtree_from_editmesh_verts(
|
||||
@@ -118,7 +116,7 @@ BVHTree *bvhtree_from_editmesh_verts_ex(
|
||||
BVHTree *bvhtree_from_mesh_verts(
|
||||
struct BVHTreeFromMesh *data, struct DerivedMesh *mesh, float epsilon, int tree_type, int axis);
|
||||
BVHTree *bvhtree_from_mesh_verts_ex(
|
||||
struct BVHTreeFromMesh *data, struct MVert *vert, const int numVerts,
|
||||
struct BVHTreeFromMesh *data, const struct MVert *vert, const int numVerts,
|
||||
const bool vert_allocated, const BLI_bitmap *mask, int verts_num_active,
|
||||
float epsilon, int tree_type, int axis);
|
||||
|
||||
@@ -135,8 +133,8 @@ BVHTree *bvhtree_from_mesh_edges(
|
||||
float epsilon, int tree_type, int axis);
|
||||
BVHTree *bvhtree_from_mesh_edges_ex(
|
||||
struct BVHTreeFromMesh *data,
|
||||
struct MVert *vert, const bool vert_allocated,
|
||||
struct MEdge *edge, const int edges_num, const bool edge_allocated,
|
||||
const struct MVert *vert, const bool vert_allocated,
|
||||
const struct MEdge *edge, const int edges_num, const bool edge_allocated,
|
||||
const BLI_bitmap *edges_mask, int edges_num_active,
|
||||
float epsilon, int tree_type, int axis);
|
||||
|
||||
@@ -145,8 +143,8 @@ BVHTree *bvhtree_from_mesh_faces(
|
||||
int tree_type, int axis);
|
||||
BVHTree *bvhtree_from_mesh_faces_ex(
|
||||
struct BVHTreeFromMesh *data,
|
||||
struct MVert *vert, const bool vert_allocated,
|
||||
struct MFace *face, const int numFaces, const bool face_allocated,
|
||||
const struct MVert *vert, const bool vert_allocated,
|
||||
const struct MFace *face, const int numFaces, const bool face_allocated,
|
||||
const BLI_bitmap *mask, int numFaces_active,
|
||||
float epsilon, int tree_type, int axis);
|
||||
|
||||
|
||||
@@ -140,8 +140,6 @@ void BKE_boundbox_init_from_minmax(struct BoundBox *bb, const float min[3], cons
|
||||
void BKE_boundbox_calc_center_aabb(const struct BoundBox *bb, float r_cent[3]);
|
||||
void BKE_boundbox_calc_size_aabb(const struct BoundBox *bb, float r_size[3]);
|
||||
void BKE_boundbox_minmax(const struct BoundBox *bb, float obmat[4][4], float r_min[3], float r_max[3]);
|
||||
struct BoundBox *BKE_boundbox_ensure_minimum_dimensions(
|
||||
struct BoundBox *bb, struct BoundBox *bb_temp, const float epsilon);
|
||||
|
||||
struct BoundBox *BKE_object_boundbox_get(struct Object *ob);
|
||||
void BKE_object_dimensions_get(struct Object *ob, float vec[3]);
|
||||
|
||||
@@ -121,7 +121,7 @@ static bool test_path(char *targetpath, const char *path_base, const char *path_
|
||||
if (path_sep) BLI_join_dirfile(tmppath, sizeof(tmppath), path_base, path_sep);
|
||||
else BLI_strncpy(tmppath, path_base, sizeof(tmppath));
|
||||
|
||||
/* rare cases folder_name is omitted (when looking for ~/.blender/2.xx dir only) */
|
||||
/* rare cases folder_name is omitted (when looking for ~/.config/blender/2.xx dir only) */
|
||||
if (folder_name)
|
||||
BLI_make_file_string("/", targetpath, tmppath, folder_name);
|
||||
else
|
||||
|
||||
@@ -376,45 +376,6 @@ static void mesh_edges_spherecast(void *userdata, int index, const BVHTreeRay *r
|
||||
}
|
||||
}
|
||||
|
||||
#define V3_MUL_ELEM(a, b) \
|
||||
(a)[0] * (b)[0], \
|
||||
(a)[1] * (b)[1], \
|
||||
(a)[2] * (b)[2]
|
||||
|
||||
/* Callback to bvh tree nearest edge to ray.
|
||||
* The tree must have been built using bvhtree_from_mesh_edges.
|
||||
* userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. */
|
||||
static void mesh_edges_nearest_to_ray(
|
||||
void *userdata, const float ray_co[3], const float ray_dir[3],
|
||||
const float scale[3], int index, BVHTreeNearest *nearest)
|
||||
{
|
||||
struct BVHTreeFromMesh *data = userdata;
|
||||
const MVert *vert = data->vert;
|
||||
const MEdge *e = &data->edge[index];
|
||||
|
||||
const float t0[3] = {V3_MUL_ELEM(vert[e->v1].co, scale)};
|
||||
const float t1[3] = {V3_MUL_ELEM(vert[e->v2].co, scale)};
|
||||
const float origin_sc[3] = {V3_MUL_ELEM(ray_co, scale)};
|
||||
const float dir_sc[3] = {V3_MUL_ELEM(ray_dir, scale)};
|
||||
|
||||
float depth, point[3];
|
||||
const float dist_sq = dist_squared_ray_to_seg_v3(origin_sc, dir_sc, t0, t1, point, &depth);
|
||||
|
||||
if (dist_sq < nearest->dist_sq) {
|
||||
nearest->dist_sq = dist_sq;
|
||||
nearest->index = index;
|
||||
|
||||
point[0] /= scale[0];
|
||||
point[1] /= scale[1];
|
||||
point[2] /= scale[2];
|
||||
|
||||
copy_v3_v3(nearest->co, point);
|
||||
sub_v3_v3v3(nearest->no, t0, t1);
|
||||
}
|
||||
}
|
||||
|
||||
#undef V3_MUL_ELEM
|
||||
|
||||
/** \} */
|
||||
|
||||
/*
|
||||
@@ -459,7 +420,7 @@ static BVHTree *bvhtree_from_editmesh_verts_create_tree(
|
||||
|
||||
static BVHTree *bvhtree_from_mesh_verts_create_tree(
|
||||
float epsilon, int tree_type, int axis,
|
||||
MVert *vert, const int verts_num,
|
||||
const MVert *vert, const int verts_num,
|
||||
const BLI_bitmap *verts_mask, int verts_num_active)
|
||||
{
|
||||
BLI_assert(vert != NULL);
|
||||
@@ -488,31 +449,23 @@ static BVHTree *bvhtree_from_mesh_verts_create_tree(
|
||||
|
||||
static void bvhtree_from_mesh_verts_setup_data(
|
||||
BVHTreeFromMesh *data, BVHTree *tree, const bool is_cached, float epsilon,
|
||||
MVert *vert, const bool vert_allocated)
|
||||
const MVert *vert, const bool vert_allocated)
|
||||
{
|
||||
memset(data, 0, sizeof(*data));
|
||||
|
||||
if (tree) {
|
||||
data->tree = tree;
|
||||
data->cached = is_cached;
|
||||
data->tree = tree;
|
||||
data->cached = is_cached;
|
||||
|
||||
/* a NULL nearest callback works fine
|
||||
* remember the min distance to point is the same as the min distance to BV of point */
|
||||
data->nearest_callback = NULL;
|
||||
data->raycast_callback = mesh_verts_spherecast;
|
||||
data->nearest_to_ray_callback = NULL;
|
||||
/* a NULL nearest callback works fine
|
||||
* remember the min distance to point is the same as the min distance to BV of point */
|
||||
data->nearest_callback = NULL;
|
||||
data->raycast_callback = mesh_verts_spherecast;
|
||||
|
||||
data->vert = vert;
|
||||
data->vert_allocated = vert_allocated;
|
||||
//data->face = DM_get_tessface_array(dm, &data->face_allocated); /* XXX WHY???? */
|
||||
data->vert = vert;
|
||||
data->vert_allocated = vert_allocated;
|
||||
//data->face = DM_get_tessface_array(dm, &data->face_allocated); /* XXX WHY???? */
|
||||
|
||||
data->sphere_radius = epsilon;
|
||||
}
|
||||
else {
|
||||
if (vert_allocated) {
|
||||
MEM_freeN(vert);
|
||||
}
|
||||
}
|
||||
data->sphere_radius = epsilon;
|
||||
}
|
||||
|
||||
/* Builds a bvh tree where nodes are the vertices of the given em */
|
||||
@@ -531,7 +484,6 @@ BVHTree *bvhtree_from_editmesh_verts_ex(
|
||||
data->em = em;
|
||||
data->nearest_callback = NULL;
|
||||
data->raycast_callback = editmesh_verts_spherecast;
|
||||
data->nearest_to_ray_callback = NULL;
|
||||
}
|
||||
|
||||
return tree;
|
||||
@@ -588,11 +540,18 @@ BVHTree *bvhtree_from_mesh_verts(
|
||||
/* printf("BVHTree is already build, using cached tree\n"); */
|
||||
}
|
||||
|
||||
/* Setup BVHTreeFromMesh */
|
||||
bvhtree_from_mesh_verts_setup_data(
|
||||
data, tree, true, epsilon, vert, vert_allocated);
|
||||
|
||||
return data->tree;
|
||||
if (tree) {
|
||||
/* Setup BVHTreeFromMesh */
|
||||
bvhtree_from_mesh_verts_setup_data(
|
||||
data, tree, true, epsilon, vert, vert_allocated);
|
||||
}
|
||||
else {
|
||||
if (vert_allocated) {
|
||||
MEM_freeN(vert);
|
||||
}
|
||||
memset(data, 0, sizeof(*data));
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -602,7 +561,7 @@ BVHTree *bvhtree_from_mesh_verts(
|
||||
* \param verts_num_active if >= 0, number of active verts to add to BVH tree (else will be computed from mask).
|
||||
*/
|
||||
BVHTree *bvhtree_from_mesh_verts_ex(
|
||||
BVHTreeFromMesh *data, MVert *vert, const int verts_num, const bool vert_allocated,
|
||||
BVHTreeFromMesh *data, const MVert *vert, const int verts_num, const bool vert_allocated,
|
||||
const BLI_bitmap *verts_mask, int verts_num_active,
|
||||
float epsilon, int tree_type, int axis)
|
||||
{
|
||||
@@ -613,7 +572,7 @@ BVHTree *bvhtree_from_mesh_verts_ex(
|
||||
bvhtree_from_mesh_verts_setup_data(
|
||||
data, tree, false, epsilon, vert, vert_allocated);
|
||||
|
||||
return data->tree;
|
||||
return tree;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -661,7 +620,7 @@ static BVHTree *bvhtree_from_editmesh_edges_create_tree(
|
||||
}
|
||||
|
||||
static BVHTree *bvhtree_from_mesh_edges_create_tree(
|
||||
MVert *vert, MEdge *edge, const int edge_num,
|
||||
const MVert *vert, const MEdge *edge, const int edge_num,
|
||||
const BLI_bitmap *edges_mask, int edges_num_active,
|
||||
float epsilon, int tree_type, int axis)
|
||||
{
|
||||
@@ -694,34 +653,26 @@ static BVHTree *bvhtree_from_mesh_edges_create_tree(
|
||||
}
|
||||
|
||||
static void bvhtree_from_mesh_edges_setup_data(
|
||||
BVHTreeFromMesh *data, BVHTree *tree, const bool is_cached, float epsilon,
|
||||
MVert *vert, const bool vert_allocated, MEdge *edge, const bool edge_allocated)
|
||||
BVHTreeFromMesh *data, BVHTree *tree,
|
||||
const bool is_cached, float epsilon,
|
||||
const MVert *vert, const bool vert_allocated,
|
||||
const MEdge *edge, const bool edge_allocated)
|
||||
{
|
||||
memset(data, 0, sizeof(*data));
|
||||
|
||||
data->tree = tree;
|
||||
|
||||
if (data->tree) {
|
||||
data->cached = is_cached;
|
||||
data->cached = is_cached;
|
||||
|
||||
data->nearest_callback = mesh_edges_nearest_point;
|
||||
data->raycast_callback = mesh_edges_spherecast;
|
||||
data->nearest_to_ray_callback = mesh_edges_nearest_to_ray;
|
||||
data->nearest_callback = mesh_edges_nearest_point;
|
||||
data->raycast_callback = mesh_edges_spherecast;
|
||||
|
||||
data->vert = vert;
|
||||
data->vert_allocated = vert_allocated;
|
||||
data->edge = edge;
|
||||
data->edge_allocated = edge_allocated;
|
||||
data->vert = vert;
|
||||
data->vert_allocated = vert_allocated;
|
||||
data->edge = edge;
|
||||
data->edge_allocated = edge_allocated;
|
||||
|
||||
data->sphere_radius = epsilon;
|
||||
}
|
||||
else {
|
||||
if (vert_allocated) {
|
||||
MEM_freeN(vert);
|
||||
}
|
||||
if (edge_allocated) {
|
||||
MEM_freeN(edge);
|
||||
}
|
||||
}
|
||||
data->sphere_radius = epsilon;
|
||||
}
|
||||
|
||||
/* Builds a bvh tree where nodes are the edges of the given em */
|
||||
@@ -742,8 +693,6 @@ BVHTree *bvhtree_from_editmesh_edges_ex(
|
||||
data->em = em;
|
||||
data->nearest_callback = NULL; /* TODO */
|
||||
data->raycast_callback = NULL; /* TODO */
|
||||
/* TODO: not urgent however since users currently define own callbacks */
|
||||
data->nearest_to_ray_callback = NULL;
|
||||
}
|
||||
|
||||
return tree;
|
||||
@@ -795,11 +744,21 @@ BVHTree *bvhtree_from_mesh_edges(
|
||||
/* printf("BVHTree is already build, using cached tree\n"); */
|
||||
}
|
||||
|
||||
/* Setup BVHTreeFromMesh */
|
||||
bvhtree_from_mesh_edges_setup_data(
|
||||
data, tree, true, epsilon, vert, vert_allocated, edge, edge_allocated);
|
||||
|
||||
return data->tree;
|
||||
if (tree) {
|
||||
/* Setup BVHTreeFromMesh */
|
||||
bvhtree_from_mesh_edges_setup_data(
|
||||
data, tree, true, epsilon, vert, vert_allocated, edge, edge_allocated);
|
||||
}
|
||||
else {
|
||||
if (vert_allocated) {
|
||||
MEM_freeN(vert);
|
||||
}
|
||||
if (edge_allocated) {
|
||||
MEM_freeN(edge);
|
||||
}
|
||||
memset(data, 0, sizeof(*data));
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -810,8 +769,8 @@ BVHTree *bvhtree_from_mesh_edges(
|
||||
*/
|
||||
BVHTree *bvhtree_from_mesh_edges_ex(
|
||||
BVHTreeFromMesh *data,
|
||||
MVert *vert, const bool vert_allocated,
|
||||
MEdge *edge, const int edges_num, const bool edge_allocated,
|
||||
const MVert *vert, const bool vert_allocated,
|
||||
const MEdge *edge, const int edges_num, const bool edge_allocated,
|
||||
const BLI_bitmap *edges_mask, int edges_num_active,
|
||||
float epsilon, int tree_type, int axis)
|
||||
{
|
||||
@@ -823,7 +782,7 @@ BVHTree *bvhtree_from_mesh_edges_ex(
|
||||
bvhtree_from_mesh_edges_setup_data(
|
||||
data, tree, false, epsilon, vert, vert_allocated, edge, edge_allocated);
|
||||
|
||||
return data->tree;
|
||||
return tree;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -836,7 +795,7 @@ BVHTree *bvhtree_from_mesh_edges_ex(
|
||||
|
||||
static BVHTree *bvhtree_from_mesh_faces_create_tree(
|
||||
float epsilon, int tree_type, int axis,
|
||||
MVert *vert, MFace *face, const int faces_num,
|
||||
const MVert *vert, const MFace *face, const int faces_num,
|
||||
const BLI_bitmap *faces_mask, int faces_num_active)
|
||||
{
|
||||
BVHTree *tree = NULL;
|
||||
@@ -880,34 +839,23 @@ static BVHTree *bvhtree_from_mesh_faces_create_tree(
|
||||
|
||||
static void bvhtree_from_mesh_faces_setup_data(
|
||||
BVHTreeFromMesh *data, BVHTree *tree, const bool is_cached, float epsilon,
|
||||
MVert *vert, const bool vert_allocated,
|
||||
MFace *face, const bool face_allocated)
|
||||
const MVert *vert, const bool vert_allocated,
|
||||
const MFace *face, const bool face_allocated)
|
||||
{
|
||||
memset(data, 0, sizeof(*data));
|
||||
|
||||
if (tree) {
|
||||
data->tree = tree;
|
||||
data->cached = is_cached;
|
||||
data->tree = tree;
|
||||
data->cached = is_cached;
|
||||
|
||||
data->nearest_callback = mesh_faces_nearest_point;
|
||||
data->raycast_callback = mesh_faces_spherecast;
|
||||
data->nearest_to_ray_callback = NULL;
|
||||
data->nearest_callback = mesh_faces_nearest_point;
|
||||
data->raycast_callback = mesh_faces_spherecast;
|
||||
|
||||
data->vert = vert;
|
||||
data->vert_allocated = vert_allocated;
|
||||
data->face = face;
|
||||
data->face_allocated = face_allocated;
|
||||
data->vert = vert;
|
||||
data->vert_allocated = vert_allocated;
|
||||
data->face = face;
|
||||
data->face_allocated = face_allocated;
|
||||
|
||||
data->sphere_radius = epsilon;
|
||||
}
|
||||
else {
|
||||
if (vert_allocated) {
|
||||
MEM_freeN(vert);
|
||||
}
|
||||
if (face_allocated) {
|
||||
MEM_freeN(face);
|
||||
}
|
||||
}
|
||||
data->sphere_radius = epsilon;
|
||||
}
|
||||
|
||||
/* Builds a bvh tree where nodes are the tesselated faces of the given dm */
|
||||
@@ -950,10 +898,21 @@ BVHTree *bvhtree_from_mesh_faces(
|
||||
/* printf("BVHTree is already build, using cached tree\n"); */
|
||||
}
|
||||
|
||||
/* Setup BVHTreeFromMesh */
|
||||
bvhtree_from_mesh_faces_setup_data(data, tree, true, epsilon, vert, vert_allocated, face, face_allocated);
|
||||
|
||||
return data->tree;
|
||||
if (tree) {
|
||||
/* Setup BVHTreeFromMesh */
|
||||
bvhtree_from_mesh_faces_setup_data(
|
||||
data, tree, true, epsilon, vert, vert_allocated, face, face_allocated);
|
||||
}
|
||||
else {
|
||||
if (vert_allocated) {
|
||||
MEM_freeN(vert);
|
||||
}
|
||||
if (face_allocated) {
|
||||
MEM_freeN(face);
|
||||
}
|
||||
memset(data, 0, sizeof(*data));
|
||||
}
|
||||
return tree;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -964,8 +923,8 @@ BVHTree *bvhtree_from_mesh_faces(
|
||||
* \param numFaces_active if >= 0, number of active faces to add to BVH tree (else will be computed from mask).
|
||||
*/
|
||||
BVHTree *bvhtree_from_mesh_faces_ex(
|
||||
BVHTreeFromMesh *data, MVert *vert, const bool vert_allocated,
|
||||
MFace *face, const int numFaces, const bool face_allocated,
|
||||
BVHTreeFromMesh *data, const MVert *vert, const bool vert_allocated,
|
||||
const MFace *face, const int numFaces, const bool face_allocated,
|
||||
const BLI_bitmap *faces_mask, int faces_num_active,
|
||||
float epsilon, int tree_type, int axis)
|
||||
{
|
||||
@@ -975,9 +934,10 @@ BVHTree *bvhtree_from_mesh_faces_ex(
|
||||
faces_mask, faces_num_active);
|
||||
|
||||
/* Setup BVHTreeFromMesh */
|
||||
bvhtree_from_mesh_faces_setup_data(data, tree, false, epsilon, vert, vert_allocated, face, face_allocated);
|
||||
bvhtree_from_mesh_faces_setup_data(
|
||||
data, tree, false, epsilon, vert, vert_allocated, face, face_allocated);
|
||||
|
||||
return data->tree;
|
||||
return tree;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -1088,34 +1048,20 @@ static void bvhtree_from_mesh_looptri_setup_data(
|
||||
{
|
||||
memset(data, 0, sizeof(*data));
|
||||
|
||||
if (tree) {
|
||||
data->tree = tree;
|
||||
data->cached = is_cached;
|
||||
data->tree = tree;
|
||||
data->cached = is_cached;
|
||||
|
||||
data->nearest_callback = mesh_looptri_nearest_point;
|
||||
data->raycast_callback = mesh_looptri_spherecast;
|
||||
data->nearest_to_ray_callback = NULL;
|
||||
data->nearest_callback = mesh_looptri_nearest_point;
|
||||
data->raycast_callback = mesh_looptri_spherecast;
|
||||
|
||||
data->vert = vert;
|
||||
data->vert_allocated = vert_allocated;
|
||||
data->loop = mloop;
|
||||
data->loop_allocated = loop_allocated;
|
||||
data->looptri = looptri;
|
||||
data->looptri_allocated = looptri_allocated;
|
||||
data->vert = vert;
|
||||
data->vert_allocated = vert_allocated;
|
||||
data->loop = mloop;
|
||||
data->loop_allocated = loop_allocated;
|
||||
data->looptri = looptri;
|
||||
data->looptri_allocated = looptri_allocated;
|
||||
|
||||
data->sphere_radius = epsilon;
|
||||
}
|
||||
else {
|
||||
if (vert_allocated) {
|
||||
MEM_freeN((void *)vert);
|
||||
}
|
||||
if (loop_allocated) {
|
||||
MEM_freeN((void *)mloop);
|
||||
}
|
||||
if (looptri_allocated) {
|
||||
MEM_freeN((void *)looptri);
|
||||
}
|
||||
}
|
||||
data->sphere_radius = epsilon;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1160,7 +1106,6 @@ BVHTree *bvhtree_from_editmesh_looptri_ex(
|
||||
data->tree = tree;
|
||||
data->nearest_callback = editmesh_looptri_nearest_point;
|
||||
data->raycast_callback = editmesh_looptri_spherecast;
|
||||
data->nearest_to_ray_callback = NULL;
|
||||
data->sphere_radius = 0.0f;
|
||||
data->em = em;
|
||||
data->cached = bvhCache != NULL;
|
||||
@@ -1242,14 +1187,28 @@ BVHTree *bvhtree_from_mesh_looptri(
|
||||
/* printf("BVHTree is already build, using cached tree\n"); */
|
||||
}
|
||||
|
||||
/* Setup BVHTreeFromMesh */
|
||||
bvhtree_from_mesh_looptri_setup_data(
|
||||
data, tree, true, epsilon,
|
||||
mvert, vert_allocated,
|
||||
mloop, loop_allocated,
|
||||
looptri, looptri_allocated);
|
||||
if (tree) {
|
||||
/* Setup BVHTreeFromMesh */
|
||||
bvhtree_from_mesh_looptri_setup_data(
|
||||
data, tree, true, epsilon,
|
||||
mvert, vert_allocated,
|
||||
mloop, loop_allocated,
|
||||
looptri, looptri_allocated);
|
||||
}
|
||||
else {
|
||||
if (vert_allocated) {
|
||||
MEM_freeN(mvert);
|
||||
}
|
||||
if (loop_allocated) {
|
||||
MEM_freeN(mloop);
|
||||
}
|
||||
if (looptri_allocated) {
|
||||
MEM_freeN((void *)looptri);
|
||||
}
|
||||
memset(data, 0, sizeof(*data));
|
||||
}
|
||||
|
||||
return data->tree;
|
||||
return tree;
|
||||
}
|
||||
|
||||
BVHTree *bvhtree_from_mesh_looptri_ex(
|
||||
@@ -1272,7 +1231,7 @@ BVHTree *bvhtree_from_mesh_looptri_ex(
|
||||
mloop, loop_allocated,
|
||||
looptri, looptri_allocated);
|
||||
|
||||
return data->tree;
|
||||
return tree;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -1292,29 +1251,27 @@ void free_bvhtree_from_editmesh(struct BVHTreeFromEditMesh *data)
|
||||
/* Frees data allocated by a call to bvhtree_from_mesh_*. */
|
||||
void free_bvhtree_from_mesh(struct BVHTreeFromMesh *data)
|
||||
{
|
||||
if (data->tree) {
|
||||
if (!data->cached) {
|
||||
BLI_bvhtree_free(data->tree);
|
||||
}
|
||||
|
||||
if (data->vert_allocated) {
|
||||
MEM_freeN((void *)data->vert);
|
||||
}
|
||||
if (data->edge_allocated) {
|
||||
MEM_freeN((void *)data->edge);
|
||||
}
|
||||
if (data->face_allocated) {
|
||||
MEM_freeN((void *)data->face);
|
||||
}
|
||||
if (data->loop_allocated) {
|
||||
MEM_freeN((void *)data->loop);
|
||||
}
|
||||
if (data->looptri_allocated) {
|
||||
MEM_freeN((void *)data->looptri);
|
||||
}
|
||||
|
||||
memset(data, 0, sizeof(*data));
|
||||
if (data->tree && !data->cached) {
|
||||
BLI_bvhtree_free(data->tree);
|
||||
}
|
||||
|
||||
if (data->vert_allocated) {
|
||||
MEM_freeN((void *)data->vert);
|
||||
}
|
||||
if (data->edge_allocated) {
|
||||
MEM_freeN((void *)data->edge);
|
||||
}
|
||||
if (data->face_allocated) {
|
||||
MEM_freeN((void *)data->face);
|
||||
}
|
||||
if (data->loop_allocated) {
|
||||
MEM_freeN((void *)data->loop);
|
||||
}
|
||||
if (data->looptri_allocated) {
|
||||
MEM_freeN((void *)data->looptri);
|
||||
}
|
||||
|
||||
memset(data, 0, sizeof(*data));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2428,8 +2428,12 @@ static DerivedMesh *cddm_copy_ex(DerivedMesh *source,
|
||||
dm->cd_flag = source->cd_flag;
|
||||
dm->dirty = source->dirty;
|
||||
|
||||
/* Tessellation data is never copied, so tag it here. */
|
||||
dm->dirty |= DM_DIRTY_TESS_CDLAYERS;
|
||||
/* Tessellation data is never copied, so tag it here.
|
||||
* Only tag dirty layers if we really ignored tessellation faces.
|
||||
*/
|
||||
if (!copy_tessface_data) {
|
||||
dm->dirty |= DM_DIRTY_TESS_CDLAYERS;
|
||||
}
|
||||
|
||||
CustomData_copy_data(&source->vertData, &dm->vertData, 0, 0, numVerts);
|
||||
CustomData_copy_data(&source->edgeData, &dm->edgeData, 0, 0, numEdges);
|
||||
|
||||
@@ -3159,7 +3159,7 @@ static ImBuf *load_sequence_single(Image *ima, ImageUser *iuser, int frame, cons
|
||||
struct ImBuf *ibuf;
|
||||
char name[FILE_MAX];
|
||||
int flag;
|
||||
ImageUser iuser_t;
|
||||
ImageUser iuser_t = {0};
|
||||
|
||||
/* XXX temp stuff? */
|
||||
if (ima->lastframe != frame)
|
||||
@@ -3167,8 +3167,12 @@ static ImBuf *load_sequence_single(Image *ima, ImageUser *iuser, int frame, cons
|
||||
|
||||
ima->lastframe = frame;
|
||||
|
||||
if (iuser)
|
||||
if (iuser) {
|
||||
iuser_t = *iuser;
|
||||
}
|
||||
else {
|
||||
/* TODO(sergey): Do we need to initialize something here? */
|
||||
}
|
||||
|
||||
iuser_t.view = view_id;
|
||||
BKE_image_user_file_path(&iuser_t, ima, name);
|
||||
|
||||
@@ -67,6 +67,11 @@
|
||||
|
||||
#include "DEG_depsgraph.h"
|
||||
|
||||
/* Define for cases when you want extra validation of mesh
|
||||
* after certain modifications.
|
||||
*/
|
||||
// #undef VALIDATE_MESH
|
||||
|
||||
enum {
|
||||
MESHCMP_DVERT_WEIGHTMISMATCH = 1,
|
||||
MESHCMP_DVERT_GROUPMISMATCH,
|
||||
@@ -2094,7 +2099,243 @@ void BKE_mesh_calc_normals_split(Mesh *mesh)
|
||||
}
|
||||
}
|
||||
|
||||
/* Spli faces based on the edge angle.
|
||||
/* Split faces helper functions. */
|
||||
|
||||
enum {
|
||||
/* Vertex is adjacent to some loop which normal is different,
|
||||
* hence split of this vertex is required.
|
||||
*/
|
||||
SPLIT_VERT_NEED_SPLIT = (1 << 0),
|
||||
/* Original vertex was already re-used by split logic. */
|
||||
SPLIT_VERT_REUSED = (1 << 1),
|
||||
};
|
||||
enum {
|
||||
/* Edge is adjacent to any of vertex tagged for split.
|
||||
*/
|
||||
SPLIT_EDGE_NEED_SPLIT = (1 << 0),
|
||||
/* Original edge was already re-used by split logic. */
|
||||
SPLIT_EDGE_REUSED = (1 << 1),
|
||||
};
|
||||
|
||||
/* Tag vertices which normals are not equal to any adjacent loop
|
||||
* and hence split on that vertex is required.
|
||||
*
|
||||
* Returns truth if any of vertex needs to be split.
|
||||
*/
|
||||
static bool split_faces_tag_verts(const Mesh *mesh, uchar *vert_flags)
|
||||
{
|
||||
const int num_polys = mesh->totpoly;
|
||||
const MVert *mvert = mesh->mvert;
|
||||
const MLoop *mloop = mesh->mloop;
|
||||
const MPoly *mpoly = mesh->mpoly;
|
||||
float (*lnors)[3] = CustomData_get_layer(&mesh->ldata, CD_NORMAL);
|
||||
bool has_split_verts = false;
|
||||
for (int poly = 0; poly < num_polys; poly++) {
|
||||
const MPoly *mp = &mpoly[poly];
|
||||
for (int loop = 0; loop < mp->totloop; loop++) {
|
||||
const MLoop *ml = &mloop[mp->loopstart + loop];
|
||||
const MVert *mv = &mvert[ml->v];
|
||||
float vn[3];
|
||||
normal_short_to_float_v3(vn, mv->no);
|
||||
if (len_squared_v3v3(vn, lnors[mp->loopstart + loop]) > FLT_EPSILON) {
|
||||
vert_flags[ml->v] |= SPLIT_VERT_NEED_SPLIT;
|
||||
has_split_verts = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return has_split_verts;
|
||||
}
|
||||
|
||||
/* Count number of new vertices to be added.
|
||||
*
|
||||
* Note that one of the loop where split is required will re-use
|
||||
* it's vertex in order to avoid creation of loose vertices.
|
||||
*/
|
||||
static int split_faces_count_new_verts(const Mesh *mesh, uchar *vert_flags)
|
||||
{
|
||||
const int num_polys = mesh->totpoly;
|
||||
const MLoop *mloop = mesh->mloop;
|
||||
const MPoly *mpoly = mesh->mpoly;
|
||||
int num_new_verts = 0;
|
||||
for (int poly = 0; poly < num_polys; poly++) {
|
||||
const MPoly *mp = &mpoly[poly];
|
||||
for (int loop = 0; loop < mp->totloop; loop++) {
|
||||
const MLoop *ml = &mloop[mp->loopstart + loop];
|
||||
if (vert_flags[ml->v] & SPLIT_VERT_NEED_SPLIT) {
|
||||
if (vert_flags[ml->v] & SPLIT_VERT_REUSED) {
|
||||
++num_new_verts;
|
||||
}
|
||||
else {
|
||||
vert_flags[ml->v] |= SPLIT_VERT_REUSED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return num_new_verts;
|
||||
}
|
||||
|
||||
/* Tag edges which are adjacent to at least one vertex tagged for split. */
|
||||
static void split_faces_tag_edges(Mesh *mesh,
|
||||
const uchar *vert_flags,
|
||||
uchar *edge_flags)
|
||||
{
|
||||
const int num_polys = mesh->totpoly;
|
||||
const MLoop *mloop = mesh->mloop;
|
||||
const MPoly *mpoly = mesh->mpoly;
|
||||
for (int poly = 0; poly < num_polys; poly++) {
|
||||
const MPoly *mp = &mpoly[poly];
|
||||
int loop_prev = mp->totloop - 1;
|
||||
for (int loop = 0; loop < mp->totloop; loop++) {
|
||||
const int poly_loop_prev = mp->loopstart + loop_prev;
|
||||
const MLoop *ml = &mloop[mp->loopstart + loop];
|
||||
const MLoop *ml_prev = &mloop[poly_loop_prev];
|
||||
const int mv_flag = vert_flags[ml->v];
|
||||
const int mv_prev_flag = vert_flags[ml_prev->v];
|
||||
bool need_split = false;
|
||||
if (mv_flag & SPLIT_VERT_NEED_SPLIT) {
|
||||
if (mv_prev_flag & SPLIT_VERT_NEED_SPLIT) {
|
||||
/* Create new edge between twp split vertices. */
|
||||
need_split = true;
|
||||
}
|
||||
else {
|
||||
/* Create new edge from existing vertex to a split one. */
|
||||
need_split = true;
|
||||
}
|
||||
}
|
||||
else if (mv_prev_flag & SPLIT_VERT_NEED_SPLIT) {
|
||||
/* Create new edge from split vertex to existing one. */
|
||||
need_split = true;
|
||||
}
|
||||
if (need_split) {
|
||||
edge_flags[ml_prev->e] |= SPLIT_EDGE_NEED_SPLIT;
|
||||
}
|
||||
loop_prev = loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Count number of new edges to be added.
|
||||
*
|
||||
* Note that one of the loop where split is required will re-use
|
||||
* it's edge in order to avoid creation of loose edges.
|
||||
*/
|
||||
static int split_faces_count_new_edges(const Mesh *mesh, uchar *edge_flags)
|
||||
{
|
||||
const int num_polys = mesh->totpoly;
|
||||
const MLoop *mloop = mesh->mloop;
|
||||
const MPoly *mpoly = mesh->mpoly;
|
||||
int num_new_edges = 0;
|
||||
for (int poly = 0; poly < num_polys; poly++) {
|
||||
const MPoly *mp = &mpoly[poly];
|
||||
for (int loop = 0; loop < mp->totloop; loop++) {
|
||||
const MLoop *ml = &mloop[mp->loopstart + loop];
|
||||
if (edge_flags[ml->e] & SPLIT_EDGE_NEED_SPLIT) {
|
||||
if (edge_flags[ml->e] & SPLIT_EDGE_REUSED) {
|
||||
++num_new_edges;
|
||||
}
|
||||
else {
|
||||
edge_flags[ml->e] |= SPLIT_EDGE_REUSED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return num_new_edges;
|
||||
}
|
||||
|
||||
/* Perform actual split of vertices.
|
||||
*
|
||||
* NOTE: Will leave edges in inconsistent state.
|
||||
*/
|
||||
static void split_faces_split_verts(Mesh *mesh,
|
||||
const int num_new_verts,
|
||||
uchar *vert_flags)
|
||||
{
|
||||
const int num_verts = mesh->totvert - num_new_verts;
|
||||
const int num_polys = mesh->totpoly;
|
||||
MVert *mvert = mesh->mvert;
|
||||
MLoop *mloop = mesh->mloop;
|
||||
MPoly *mpoly = mesh->mpoly;
|
||||
const float (*lnors)[3] = CustomData_get_layer(&mesh->ldata, CD_NORMAL);
|
||||
int num_added_verts = 0;
|
||||
/* Clear reused flag, we need it again. */
|
||||
for (int i = 0; i < num_verts; ++i) {
|
||||
vert_flags[i] &= ~SPLIT_VERT_REUSED;
|
||||
}
|
||||
for (int poly = 0; poly < num_polys; poly++) {
|
||||
MPoly *mp = &mpoly[poly];
|
||||
/* First we split all vertices to get proper flag whether they are
|
||||
* split or not for all of them before handling edges.
|
||||
*/
|
||||
for (int loop = 0; loop < mp->totloop; loop++) {
|
||||
int poly_loop = mp->loopstart + loop;
|
||||
MLoop *ml = &mloop[poly_loop];
|
||||
if (vert_flags[ml->v] & SPLIT_VERT_NEED_SPLIT) {
|
||||
if ((vert_flags[ml->v] & SPLIT_VERT_REUSED) == 0) {
|
||||
/* Ignore first split on vertex, re-use it instead. */
|
||||
vert_flags[ml->v] |= SPLIT_VERT_REUSED;
|
||||
continue;
|
||||
}
|
||||
/* Create new vertex. */
|
||||
int new_vert = num_verts + num_added_verts;
|
||||
CustomData_copy_data(&mesh->vdata, &mesh->vdata,
|
||||
ml->v, new_vert, 1);
|
||||
normal_float_to_short_v3(mvert[new_vert].no,
|
||||
lnors[poly_loop]);
|
||||
ml->v = new_vert;
|
||||
num_added_verts++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform actual split of edges.
|
||||
*
|
||||
* NOTE: Will correct all edges.
|
||||
*/
|
||||
static void split_faces_split_edges(Mesh *mesh,
|
||||
const int num_new_edges,
|
||||
uchar *edge_flags)
|
||||
{
|
||||
const int num_edges = mesh->totedge - num_new_edges;
|
||||
const int num_polys = mesh->totpoly;
|
||||
MEdge *medge = mesh->medge;
|
||||
MLoop *mloop = mesh->mloop;
|
||||
MPoly *mpoly = mesh->mpoly;
|
||||
int num_added_edges = 0;
|
||||
/* Clear reused flag, we need it again. */
|
||||
for (int i = 0; i < num_edges; ++i) {
|
||||
edge_flags[i] &= ~SPLIT_EDGE_REUSED;
|
||||
}
|
||||
for (int poly = 0; poly < num_polys; poly++) {
|
||||
MPoly *mp = &mpoly[poly];
|
||||
for (int loop = 0, loop_prev = mp->totloop - 1; loop < mp->totloop; loop++) {
|
||||
const int poly_loop_prev = mp->loopstart + loop_prev;
|
||||
const MLoop *ml = &mloop[mp->loopstart + loop];
|
||||
MLoop *ml_prev = &mloop[poly_loop_prev];
|
||||
MEdge *me_prev = &medge[ml_prev->e];
|
||||
if (edge_flags[ml_prev->e] & SPLIT_EDGE_NEED_SPLIT) {
|
||||
if ((edge_flags[ml_prev->e] & SPLIT_EDGE_REUSED) == 0) {
|
||||
edge_flags[ml_prev->e] |= SPLIT_EDGE_REUSED;
|
||||
me_prev->v1 = ml_prev->v;
|
||||
me_prev->v2 = ml->v;
|
||||
}
|
||||
else {
|
||||
const int index = num_edges + num_added_edges;
|
||||
CustomData_copy_data(&mesh->edata, &mesh->edata,
|
||||
ml_prev->e, index, 1);
|
||||
MEdge *me_new = &medge[index];
|
||||
me_new->v1 = ml_prev->v;
|
||||
me_new->v2 = ml->v;
|
||||
ml_prev->e = index;
|
||||
num_added_edges++;
|
||||
}
|
||||
}
|
||||
loop_prev = loop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Split faces based on the edge angle.
|
||||
* Matches behavior of face splitting in render engines.
|
||||
*/
|
||||
void BKE_mesh_split_faces(Mesh *mesh)
|
||||
@@ -2102,96 +2343,52 @@ void BKE_mesh_split_faces(Mesh *mesh)
|
||||
const int num_verts = mesh->totvert;
|
||||
const int num_edges = mesh->totedge;
|
||||
const int num_polys = mesh->totpoly;
|
||||
MVert *mvert = mesh->mvert;
|
||||
MEdge *medge = mesh->medge;
|
||||
MLoop *mloop = mesh->mloop;
|
||||
MPoly *mpoly = mesh->mpoly;
|
||||
float (*lnors)[3];
|
||||
int poly, num_new_verts = 0;
|
||||
if ((mesh->flag & ME_AUTOSMOOTH) == 0) {
|
||||
return;
|
||||
}
|
||||
if (num_polys == 0) {
|
||||
return;
|
||||
}
|
||||
BKE_mesh_tessface_clear(mesh);
|
||||
/* Compute loop normals if needed. */
|
||||
if (!CustomData_has_layer(&mesh->ldata, CD_NORMAL)) {
|
||||
BKE_mesh_calc_normals_split(mesh);
|
||||
}
|
||||
lnors = CustomData_get_layer(&mesh->ldata, CD_NORMAL);
|
||||
/* Count number of vertices to be split. */
|
||||
for (poly = 0; poly < num_polys; poly++) {
|
||||
MPoly *mp = &mpoly[poly];
|
||||
int loop;
|
||||
for (loop = 0; loop < mp->totloop; loop++) {
|
||||
MLoop *ml = &mloop[mp->loopstart + loop];
|
||||
MVert *mv = &mvert[ml->v];
|
||||
float vn[3];
|
||||
normal_short_to_float_v3(vn, mv->no);
|
||||
if (!equals_v3v3(vn, lnors[mp->loopstart + loop])) {
|
||||
num_new_verts++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (num_new_verts == 0) {
|
||||
/* No new vertices are to be added, can do early exit. */
|
||||
/* Runtime flags. */
|
||||
uchar *vert_flags = MEM_callocN(sizeof(*vert_flags) * num_verts,
|
||||
"split faces vert flags");
|
||||
/* Tag vertces and check whether anything is tagged. */
|
||||
if (!split_faces_tag_verts(mesh, vert_flags)) {
|
||||
/* No new vertices to be split added, can do early exit. */
|
||||
MEM_freeN(vert_flags);
|
||||
return;
|
||||
}
|
||||
/* Flush vertex flags to edges. */
|
||||
uchar *edge_flags = MEM_callocN(sizeof(*edge_flags) * num_edges,
|
||||
"split faces edge flags");
|
||||
split_faces_tag_edges(mesh, vert_flags, edge_flags);
|
||||
/* Count amount of new geometry. */
|
||||
int num_new_verts = split_faces_count_new_verts(mesh, vert_flags);
|
||||
int num_new_edges = split_faces_count_new_edges(mesh, edge_flags);
|
||||
/* Reallocate all vert and edge related data. */
|
||||
mesh->totvert += num_new_verts;
|
||||
mesh->totedge += 2 * num_new_verts;
|
||||
mesh->totedge += num_new_edges;
|
||||
CustomData_realloc(&mesh->vdata, mesh->totvert);
|
||||
CustomData_realloc(&mesh->edata, mesh->totedge);
|
||||
/* Update pointers to a newly allocated memory. */
|
||||
BKE_mesh_update_customdata_pointers(mesh, false);
|
||||
mvert = mesh->mvert;
|
||||
medge = mesh->medge;
|
||||
/* Perform actual vertex split. */
|
||||
num_new_verts = 0;
|
||||
for (poly = 0; poly < num_polys; poly++) {
|
||||
MPoly *mp = &mpoly[poly];
|
||||
int loop;
|
||||
for (loop = 0; loop < mp->totloop; loop++) {
|
||||
int poly_loop = mp->loopstart + loop;
|
||||
MLoop *ml = &mloop[poly_loop];
|
||||
MVert *mv = &mvert[ml->v];
|
||||
float vn[3];
|
||||
normal_short_to_float_v3(vn, mv->no);
|
||||
if (!equals_v3v3(vn, lnors[mp->loopstart + loop])) {
|
||||
int poly_loop_prev = mp->loopstart + (loop + mp->totloop - 1) % mp->totloop;
|
||||
MLoop *ml_prev = &mloop[poly_loop_prev];
|
||||
int new_edge_prev, new_edge;
|
||||
/* Cretae new vertex. */
|
||||
int new_vert = num_verts + num_new_verts;
|
||||
CustomData_copy_data(&mesh->vdata, &mesh->vdata,
|
||||
ml->v, new_vert, 1);
|
||||
normal_float_to_short_v3(mvert[new_vert].no,
|
||||
lnors[poly_loop]);
|
||||
/* Create new edges. */
|
||||
new_edge_prev = num_edges + 2 * num_new_verts;
|
||||
new_edge = num_edges + 2 * num_new_verts + 1;
|
||||
CustomData_copy_data(&mesh->edata, &mesh->edata,
|
||||
ml_prev->e, new_edge_prev, 1);
|
||||
CustomData_copy_data(&mesh->edata, &mesh->edata,
|
||||
ml->e, new_edge, 1);
|
||||
if (medge[new_edge_prev].v1 == ml->v) {
|
||||
medge[new_edge_prev].v1 = new_vert;
|
||||
}
|
||||
else {
|
||||
medge[new_edge_prev].v2 = new_vert;
|
||||
}
|
||||
if (medge[new_edge].v1 == ml->v) {
|
||||
medge[new_edge].v1 = new_vert;
|
||||
}
|
||||
else {
|
||||
medge[new_edge].v2 = new_vert;
|
||||
}
|
||||
|
||||
ml->v = new_vert;
|
||||
ml_prev->e = new_edge_prev;
|
||||
ml->e = new_edge;
|
||||
num_new_verts++;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Perform actual split of vertices and adjacent edges. */
|
||||
split_faces_split_verts(mesh, num_new_verts, vert_flags);
|
||||
split_faces_split_edges(mesh, num_new_edges, edge_flags);
|
||||
/* CD_NORMAL is expected to be temporary only, and it's invalid at
|
||||
* this point anyway.
|
||||
*/
|
||||
CustomData_free_layers(&mesh->ldata, CD_NORMAL, mesh->totloop);
|
||||
MEM_freeN(vert_flags);
|
||||
MEM_freeN(edge_flags);
|
||||
#ifdef VALIDATE_MESH
|
||||
BKE_mesh_validate(mesh, true, true);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* settings: 1 - preview, 2 - render */
|
||||
|
||||
@@ -2241,66 +2241,6 @@ void BKE_boundbox_minmax(const BoundBox *bb, float obmat[4][4], float r_min[3],
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a BBox which each dimensions are at least epsilon.
|
||||
* \note In case a given dimension needs to be enlarged, its final value will be in [epsilon, 3 * epsilon] range.
|
||||
*
|
||||
* \param bb the input bbox to check.
|
||||
* \param bb_temp the temp bbox to modify (\a bb content is never changed).
|
||||
* \param epsilon the minimum dimension to ensure.
|
||||
* \return either bb (if nothing needed to be changed) or bb_temp.
|
||||
*/
|
||||
BoundBox *BKE_boundbox_ensure_minimum_dimensions(BoundBox *bb, BoundBox *bb_temp, const float epsilon)
|
||||
{
|
||||
if (fabsf(bb->vec[0][0] - bb->vec[4][0]) < epsilon) {
|
||||
/* Flat along X axis... */
|
||||
*bb_temp = *bb;
|
||||
bb = bb_temp;
|
||||
bb->vec[0][0] -= epsilon;
|
||||
bb->vec[1][0] -= epsilon;
|
||||
bb->vec[2][0] -= epsilon;
|
||||
bb->vec[3][0] -= epsilon;
|
||||
bb->vec[4][0] += epsilon;
|
||||
bb->vec[5][0] += epsilon;
|
||||
bb->vec[6][0] += epsilon;
|
||||
bb->vec[7][0] += epsilon;
|
||||
}
|
||||
|
||||
if (fabsf(bb->vec[0][1] - bb->vec[3][1]) < epsilon) {
|
||||
/* Flat along Y axis... */
|
||||
if (bb != bb_temp) {
|
||||
*bb_temp = *bb;
|
||||
bb = bb_temp;
|
||||
}
|
||||
bb->vec[0][1] -= epsilon;
|
||||
bb->vec[1][1] -= epsilon;
|
||||
bb->vec[4][1] -= epsilon;
|
||||
bb->vec[5][1] -= epsilon;
|
||||
bb->vec[2][1] += epsilon;
|
||||
bb->vec[3][1] += epsilon;
|
||||
bb->vec[6][1] += epsilon;
|
||||
bb->vec[7][1] += epsilon;
|
||||
}
|
||||
|
||||
if (fabsf(bb->vec[0][2] - bb->vec[1][2]) < epsilon) {
|
||||
/* Flat along Z axis... */
|
||||
if (bb != bb_temp) {
|
||||
*bb_temp = *bb;
|
||||
bb = bb_temp;
|
||||
}
|
||||
bb->vec[0][2] -= epsilon;
|
||||
bb->vec[3][2] -= epsilon;
|
||||
bb->vec[4][2] -= epsilon;
|
||||
bb->vec[7][2] -= epsilon;
|
||||
bb->vec[1][2] += epsilon;
|
||||
bb->vec[2][2] += epsilon;
|
||||
bb->vec[5][2] += epsilon;
|
||||
bb->vec[6][2] += epsilon;
|
||||
}
|
||||
|
||||
return bb;
|
||||
}
|
||||
|
||||
BoundBox *BKE_object_boundbox_get(Object *ob)
|
||||
{
|
||||
BoundBox *bb = NULL;
|
||||
|
||||
@@ -95,10 +95,6 @@ typedef void (*BVHTree_NearestPointCallback)(void *userdata, int index, const fl
|
||||
/* callback must update hit in case it finds a nearest successful hit */
|
||||
typedef void (*BVHTree_RayCastCallback)(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit);
|
||||
|
||||
/* callback must update nearest in case it finds a nearest result */
|
||||
typedef void (*BVHTree_NearestToRayCallback)(void *userdata, const float ray_co[3], const float ray_dir[3],
|
||||
const float scale[3], int index, BVHTreeNearest *nearest);
|
||||
|
||||
/* callback to check if 2 nodes overlap (use thread if intersection results need to be stored) */
|
||||
typedef bool (*BVHTree_OverlapCallback)(void *userdata, int index_a, int index_b, int thread);
|
||||
|
||||
@@ -143,18 +139,6 @@ int BLI_bvhtree_find_nearest(
|
||||
BVHTree *tree, const float co[3], BVHTreeNearest *nearest,
|
||||
BVHTree_NearestPointCallback callback, void *userdata);
|
||||
|
||||
int BLI_bvhtree_find_nearest_to_ray_angle(
|
||||
BVHTree *tree, const float co[3], const float dir[3],
|
||||
const bool ray_is_normalized, const float scale[3],
|
||||
BVHTreeNearest *nearest,
|
||||
BVHTree_NearestToRayCallback callback, void *userdata);
|
||||
|
||||
int BLI_bvhtree_find_nearest_to_ray(
|
||||
BVHTree *tree, const float co[3], const float dir[3],
|
||||
const bool ray_is_normalized, const float scale[3],
|
||||
BVHTreeNearest *nearest,
|
||||
BVHTree_NearestToRayCallback callback, void *userdata);
|
||||
|
||||
int BLI_bvhtree_ray_cast_ex(
|
||||
BVHTree *tree, const float co[3], const float dir[3], float radius, BVHTreeRayHit *hit,
|
||||
BVHTree_RayCastCallback callback, void *userdata,
|
||||
|
||||
@@ -298,23 +298,6 @@ bool isect_ray_aabb_v3_simple(
|
||||
const float bb_min[3], const float bb_max[3],
|
||||
float *tmin, float *tmax);
|
||||
|
||||
struct NearestRayToAABB_Precalc {
|
||||
float ray_origin[3];
|
||||
float ray_direction[3];
|
||||
float ray_inv_dir[3];
|
||||
float cdot_axis[3];
|
||||
float idiag_sq[3];
|
||||
bool sign[3];
|
||||
};
|
||||
|
||||
void dist_squared_ray_to_aabb_v3_precalc(
|
||||
struct NearestRayToAABB_Precalc *data,
|
||||
const float ray_origin[3], const float ray_direction[3]);
|
||||
float dist_squared_ray_to_aabb_v3(
|
||||
const struct NearestRayToAABB_Precalc *data,
|
||||
const float bb_min[3], const float bb_max[3],
|
||||
bool r_axis_closest[3]);
|
||||
|
||||
/* other */
|
||||
bool isect_sweeping_sphere_tri_v3(const float p1[3], const float p2[3], const float radius,
|
||||
const float v0[3], const float v1[3], const float v2[3], float *r_lambda, float ipoint[3]);
|
||||
|
||||
@@ -159,29 +159,6 @@ typedef struct BVHRayCastData {
|
||||
BVHTreeRayHit hit;
|
||||
} BVHRayCastData;
|
||||
|
||||
typedef struct BVHNearestRayData {
|
||||
BVHTree *tree;
|
||||
BVHTree_NearestToRayCallback callback;
|
||||
void *userdata;
|
||||
|
||||
struct {
|
||||
bool sign[3];
|
||||
float origin[3];
|
||||
float direction[3];
|
||||
|
||||
float direction_scaled_square[3];
|
||||
float inv_dir[3];
|
||||
|
||||
float cdot_axis[3];
|
||||
} ray;
|
||||
|
||||
bool pick_smallest[3];
|
||||
|
||||
BVHTreeNearest nearest;
|
||||
|
||||
float scale[3];
|
||||
} BVHNearestRayData;
|
||||
|
||||
/** \} */
|
||||
|
||||
|
||||
@@ -1898,453 +1875,6 @@ void BLI_bvhtree_ray_cast_all(
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
/** \name BLI_bvhtree_find_nearest_to_ray functions
|
||||
*
|
||||
* \{ */
|
||||
|
||||
static void dist_squared_ray_to_aabb_scaled_v3_precalc(
|
||||
BVHNearestRayData *data,
|
||||
const float ray_origin[3], const float ray_direction[3],
|
||||
const bool ray_is_normalized, const float scale[3])
|
||||
{
|
||||
if (scale) {
|
||||
copy_v3_v3(data->scale, scale);
|
||||
}
|
||||
else {
|
||||
copy_v3_fl(data->scale, 1.0f);
|
||||
}
|
||||
/* un-normalize ray */
|
||||
if (ray_is_normalized && scale &&
|
||||
(data->scale[0] != 1.0f || data->scale[1] != 1.0f || data->scale[2] != 1.0f))
|
||||
{
|
||||
data->ray.direction[0] = ray_direction[0] * data->scale[0];
|
||||
data->ray.direction[1] = ray_direction[1] * data->scale[1];
|
||||
data->ray.direction[2] = ray_direction[2] * data->scale[2];
|
||||
|
||||
mul_v3_v3fl(data->ray.direction, ray_direction, 1 / len_v3(data->ray.direction));
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(data->ray.direction, ray_direction);
|
||||
}
|
||||
|
||||
float dir_sq[3];
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
data->ray.origin[i] = ray_origin[i];
|
||||
data->ray.inv_dir[i] = (data->ray.direction[i] != 0.0f) ?
|
||||
(1.0f / data->ray.direction[i]) : FLT_MAX;
|
||||
/* It has to be in function of `ray.inv_dir`,
|
||||
* since the division of 1 by 0.0f, can be -inf or +inf */
|
||||
data->ray.sign[i] = (data->ray.inv_dir[i] < 0.0f);
|
||||
|
||||
data->ray.direction_scaled_square[i] = data->ray.direction[i] * data->scale[i];
|
||||
|
||||
dir_sq[i] = SQUARE(data->ray.direction_scaled_square[i]);
|
||||
|
||||
data->ray.direction_scaled_square[i] *= data->scale[i];
|
||||
}
|
||||
|
||||
/* `diag_sq` Length square of each face diagonal */
|
||||
float diag_sq[3] = {
|
||||
dir_sq[1] + dir_sq[2],
|
||||
dir_sq[0] + dir_sq[2],
|
||||
dir_sq[0] + dir_sq[1],
|
||||
};
|
||||
|
||||
data->ray.cdot_axis[0] = (diag_sq[0] != 0.0f) ? data->ray.direction[0] / diag_sq[0] : FLT_MAX;
|
||||
data->ray.cdot_axis[1] = (diag_sq[1] != 0.0f) ? data->ray.direction[1] / diag_sq[1] : FLT_MAX;
|
||||
data->ray.cdot_axis[2] = (diag_sq[2] != 0.0f) ? data->ray.direction[2] / diag_sq[2] : FLT_MAX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the squared distance from a ray to a bound-box `AABB`.
|
||||
* It is based on `fast_ray_nearest_hit` solution to obtain
|
||||
* the coordinates of the nearest edge of Bound Box to the ray
|
||||
*/
|
||||
MINLINE float dist_squared_ray_to_aabb_scaled_v3__impl(
|
||||
const BVHNearestRayData *data,
|
||||
const float bv[6], float *r_depth_sq, bool r_axis_closest[3])
|
||||
{
|
||||
|
||||
/* `tmin` is a vector that has the smaller distances to each of the
|
||||
* infinite planes of the `AABB` faces (hit in nearest face X plane,
|
||||
* nearest face Y plane and nearest face Z plane) */
|
||||
float local_bvmin[3], local_bvmax[3];
|
||||
|
||||
if (data->ray.sign[0]) {
|
||||
local_bvmin[0] = bv[1];
|
||||
local_bvmax[0] = bv[0];
|
||||
}
|
||||
else {
|
||||
local_bvmin[0] = bv[0];
|
||||
local_bvmax[0] = bv[1];
|
||||
}
|
||||
|
||||
if (data->ray.sign[1]) {
|
||||
local_bvmin[1] = bv[3];
|
||||
local_bvmax[1] = bv[2];
|
||||
}
|
||||
else {
|
||||
local_bvmin[1] = bv[2];
|
||||
local_bvmax[1] = bv[3];
|
||||
}
|
||||
|
||||
if (data->ray.sign[2]) {
|
||||
local_bvmin[2] = bv[5];
|
||||
local_bvmax[2] = bv[4];
|
||||
}
|
||||
else {
|
||||
local_bvmin[2] = bv[4];
|
||||
local_bvmax[2] = bv[5];
|
||||
}
|
||||
|
||||
sub_v3_v3(local_bvmin, data->ray.origin);
|
||||
sub_v3_v3(local_bvmax, data->ray.origin);
|
||||
|
||||
const float tmin[3] = {
|
||||
local_bvmin[0] * data->ray.inv_dir[0],
|
||||
local_bvmin[1] * data->ray.inv_dir[1],
|
||||
local_bvmin[2] * data->ray.inv_dir[2],
|
||||
};
|
||||
|
||||
/* `tmax` is a vector that has the longer distances to each of the
|
||||
* infinite planes of the `AABB` faces (hit in farthest face X plane,
|
||||
* farthest face Y plane and farthest face Z plane) */
|
||||
const float tmax[3] = {
|
||||
local_bvmax[0] * data->ray.inv_dir[0],
|
||||
local_bvmax[1] * data->ray.inv_dir[1],
|
||||
local_bvmax[2] * data->ray.inv_dir[2],
|
||||
};
|
||||
/* `v1` and `v3` is be the coordinates of the nearest `AABB` edge to the ray*/
|
||||
float v1[3], v2[3];
|
||||
/* `rtmin` is the highest value of the smaller distances. == max_axis_v3(tmin)
|
||||
* `rtmax` is the lowest value of longer distances. == min_axis_v3(tmax)*/
|
||||
float rtmin, rtmax, mul;
|
||||
/* `main_axis` is the axis equivalent to edge close to the ray */
|
||||
int main_axis;
|
||||
|
||||
r_axis_closest[0] = false;
|
||||
r_axis_closest[1] = false;
|
||||
r_axis_closest[2] = false;
|
||||
|
||||
/* *** min_axis_v3(tmax) *** */
|
||||
if ((tmax[0] <= tmax[1]) && (tmax[0] <= tmax[2])) {
|
||||
// printf("# Hit in X %s\n", data->sign[0] ? "min", "max");
|
||||
rtmax = tmax[0];
|
||||
v1[0] = v2[0] = local_bvmax[0];
|
||||
mul = local_bvmax[0] * data->ray.direction_scaled_square[0];
|
||||
main_axis = 3;
|
||||
r_axis_closest[0] = data->ray.sign[0];
|
||||
}
|
||||
else if ((tmax[1] <= tmax[0]) && (tmax[1] <= tmax[2])) {
|
||||
// printf("# Hit in Y %s\n", data->sign[1] ? "min", "max");
|
||||
rtmax = tmax[1];
|
||||
v1[1] = v2[1] = local_bvmax[1];
|
||||
mul = local_bvmax[1] * data->ray.direction_scaled_square[1];
|
||||
main_axis = 2;
|
||||
r_axis_closest[1] = data->ray.sign[1];
|
||||
}
|
||||
else {
|
||||
// printf("# Hit in Z %s\n", data->sign[2] ? "min", "max");
|
||||
rtmax = tmax[2];
|
||||
v1[2] = v2[2] = local_bvmax[2];
|
||||
mul = local_bvmax[2] * data->ray.direction_scaled_square[2];
|
||||
main_axis = 1;
|
||||
r_axis_closest[2] = data->ray.sign[2];
|
||||
}
|
||||
|
||||
/* *** max_axis_v3(tmin) *** */
|
||||
if ((tmin[0] >= tmin[1]) && (tmin[0] >= tmin[2])) {
|
||||
// printf("# To X %s\n", data->sign[0] ? "max", "min");
|
||||
rtmin = tmin[0];
|
||||
v1[0] = v2[0] = local_bvmin[0];
|
||||
mul += local_bvmin[0] * data->ray.direction_scaled_square[0];
|
||||
main_axis -= 3;
|
||||
r_axis_closest[0] = !data->ray.sign[0];
|
||||
}
|
||||
else if ((tmin[1] >= tmin[0]) && (tmin[1] >= tmin[2])) {
|
||||
// printf("# To Y %s\n", data->sign[1] ? "max", "min");
|
||||
rtmin = tmin[1];
|
||||
v1[1] = v2[1] = local_bvmin[1];
|
||||
mul += local_bvmin[1] * data->ray.direction_scaled_square[1];
|
||||
main_axis -= 1;
|
||||
r_axis_closest[1] = !data->ray.sign[1];
|
||||
}
|
||||
else {
|
||||
// printf("# To Z %s\n", data->sign[2] ? "max", "min");
|
||||
rtmin = tmin[2];
|
||||
v1[2] = v2[2] = local_bvmin[2];
|
||||
mul += local_bvmin[2] * data->ray.direction_scaled_square[2];
|
||||
main_axis -= 2;
|
||||
r_axis_closest[2] = !data->ray.sign[2];
|
||||
}
|
||||
/* *** end min/max axis *** */
|
||||
|
||||
if (main_axis < 0)
|
||||
main_axis += 3;
|
||||
|
||||
/* if rtmin < rtmax, ray intersect `AABB` */
|
||||
if (rtmin <= rtmax) {
|
||||
#ifdef IGNORE_BEHIND_RAY
|
||||
/* `if rtmax < depth_min`, the whole `AABB` is behind us */
|
||||
if (rtmax < min_depth) {
|
||||
return fallback;
|
||||
}
|
||||
#endif
|
||||
const float proj = rtmin * data->ray.direction[main_axis];
|
||||
|
||||
if (data->ray.sign[main_axis])
|
||||
r_axis_closest[main_axis] = (proj - local_bvmax[main_axis]) < (local_bvmin[main_axis] - proj);
|
||||
else
|
||||
r_axis_closest[main_axis] = (proj - local_bvmin[main_axis]) < (local_bvmax[main_axis] - proj);
|
||||
|
||||
//if (r_depth_sq)
|
||||
// *r_depth_sq = SQUARE(rtmin);
|
||||
|
||||
return 0.0f;
|
||||
}
|
||||
#ifdef IGNORE_BEHIND_RAY
|
||||
/* `if rtmin < depth_min`, the whole `AABB` is behing us */
|
||||
else if (rtmin < min_depth) {
|
||||
return fallback;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (data->ray.sign[main_axis]) {
|
||||
v1[main_axis] = local_bvmax[main_axis];
|
||||
v2[main_axis] = local_bvmin[main_axis];
|
||||
}
|
||||
else {
|
||||
v1[main_axis] = local_bvmin[main_axis];
|
||||
v2[main_axis] = local_bvmax[main_axis];
|
||||
}
|
||||
{
|
||||
/* `proj` equals to nearest point on the ray closest to the edge `v1 v2` of the `AABB`. */
|
||||
const float proj = mul * data->ray.cdot_axis[main_axis];
|
||||
float depth_sq, r_point[3];
|
||||
if (v1[main_axis] > proj) { /* the nearest point to the ray is the point v1 */
|
||||
r_axis_closest[main_axis] = true;
|
||||
/* `depth` is equivalent the distance of the the projection of v1 on the ray */
|
||||
depth_sq = mul + data->ray.direction_scaled_square[main_axis] * v1[main_axis];
|
||||
|
||||
copy_v3_v3(r_point, v1);
|
||||
}
|
||||
else if (v2[main_axis] < proj) { /* the nearest point of the ray is the point v2 */
|
||||
r_axis_closest[main_axis] = false;
|
||||
|
||||
depth_sq = mul + data->ray.direction_scaled_square[main_axis] * v2[main_axis];
|
||||
|
||||
copy_v3_v3(r_point, v2);
|
||||
}
|
||||
else { /* the nearest point of the ray is on the edge of the `AABB`. */
|
||||
r_axis_closest[main_axis] = (proj - v1[main_axis]) < (v2[main_axis] - proj);
|
||||
|
||||
depth_sq = mul + data->ray.direction_scaled_square[main_axis] * proj;
|
||||
#if 0
|
||||
r_point[0] = main_axis == 0 ? proj : v2[0];
|
||||
r_point[1] = main_axis == 1 ? proj : v2[1];
|
||||
r_point[2] = main_axis == 2 ? proj : v2[2];
|
||||
#else
|
||||
v2[main_axis] = proj;
|
||||
copy_v3_v3(r_point, v2);
|
||||
#endif
|
||||
}
|
||||
depth_sq *= depth_sq;
|
||||
|
||||
if (r_depth_sq)
|
||||
*r_depth_sq = depth_sq;
|
||||
|
||||
/* TODO: scale can be optional */
|
||||
r_point[0] *= data->scale[0];
|
||||
r_point[1] *= data->scale[1];
|
||||
r_point[2] *= data->scale[2];
|
||||
|
||||
return len_squared_v3(r_point) - depth_sq;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* + r_point
|
||||
* |
|
||||
* | dist
|
||||
* |
|
||||
* +----depth----+orig <-- dir
|
||||
*
|
||||
* tangent = dist/depth
|
||||
* </pre>
|
||||
*/
|
||||
static float calc_tangent_sq(BVHNearestRayData *data, BVHNode *node)
|
||||
{
|
||||
float depth_sq;
|
||||
const float dist_sq = dist_squared_ray_to_aabb_scaled_v3__impl(
|
||||
data, node->bv, &depth_sq, data->pick_smallest);
|
||||
|
||||
return (dist_sq != 0.0f) ? (dist_sq / depth_sq) : 0.0f;
|
||||
}
|
||||
|
||||
static float calc_dist_sq_to_ray(BVHNearestRayData *data, BVHNode *node)
|
||||
{
|
||||
return dist_squared_ray_to_aabb_scaled_v3__impl(
|
||||
data, node->bv, NULL,
|
||||
data->pick_smallest);
|
||||
}
|
||||
|
||||
static void dfs_find_lowest_tangent_dfs(BVHNearestRayData *data, BVHNode *node)
|
||||
{
|
||||
if (node->totnode == 0) {
|
||||
if (data->callback) {
|
||||
data->callback(data->userdata, data->ray.origin, data->ray.direction,
|
||||
data->scale, node->index, &data->nearest);
|
||||
}
|
||||
else {
|
||||
data->nearest.index = node->index;
|
||||
data->nearest.dist_sq = calc_tangent_sq(data, node);
|
||||
/* TODO: return a value to the data->nearest.co
|
||||
* not urgent however since users currently define own callbacks */
|
||||
}
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
/* First pick the closest node to dive on */
|
||||
if (data->pick_smallest[node->main_axis]) {
|
||||
for (i = 0; i != node->totnode; i++) {
|
||||
if (calc_tangent_sq(data, node->children[i]) < data->nearest.dist_sq) {
|
||||
dfs_find_lowest_tangent_dfs(data, node->children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = node->totnode - 1; i >= 0; i--) {
|
||||
if (calc_tangent_sq(data, node->children[i]) < data->nearest.dist_sq) {
|
||||
dfs_find_lowest_tangent_dfs(data, node->children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void dfs_find_nearest_to_ray_dfs(BVHNearestRayData *data, BVHNode *node)
|
||||
{
|
||||
if (node->totnode == 0) {
|
||||
if (data->callback) {
|
||||
data->callback(data->userdata, data->ray.origin, data->ray.direction,
|
||||
data->scale, node->index, &data->nearest);
|
||||
}
|
||||
else {
|
||||
data->nearest.index = node->index;
|
||||
data->nearest.dist_sq = calc_dist_sq_to_ray(data, node);
|
||||
/* TODO: return a value to the data->nearest.co
|
||||
* not urgent however since users currently define own callbacks */
|
||||
}
|
||||
}
|
||||
else {
|
||||
int i;
|
||||
/* First pick the closest node to dive on */
|
||||
if (data->pick_smallest[node->main_axis]) {
|
||||
for (i = 0; i != node->totnode; i++) {
|
||||
if (calc_dist_sq_to_ray(data, node->children[i]) < data->nearest.dist_sq) {
|
||||
dfs_find_nearest_to_ray_dfs(data, node->children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = node->totnode - 1; i >= 0; i--) {
|
||||
if (calc_dist_sq_to_ray(data, node->children[i]) < data->nearest.dist_sq) {
|
||||
dfs_find_nearest_to_ray_dfs(data, node->children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the point whose tangent defined by the angle between the point and ray is the lowest
|
||||
* nearest.dist_sq returns the angle's tangent
|
||||
*/
|
||||
int BLI_bvhtree_find_nearest_to_ray_angle(
|
||||
BVHTree *tree, const float co[3], const float dir[3],
|
||||
const bool ray_is_normalized, const float scale[3],
|
||||
BVHTreeNearest *nearest,
|
||||
BVHTree_NearestToRayCallback callback, void *userdata)
|
||||
{
|
||||
BVHNearestRayData data;
|
||||
BVHNode *root = tree->nodes[tree->totleaf];
|
||||
|
||||
data.tree = tree;
|
||||
|
||||
data.callback = callback;
|
||||
data.userdata = userdata;
|
||||
|
||||
dist_squared_ray_to_aabb_scaled_v3_precalc(&data, co, dir, ray_is_normalized, scale);
|
||||
|
||||
if (nearest) {
|
||||
memcpy(&data.nearest, nearest, sizeof(*nearest));
|
||||
}
|
||||
else {
|
||||
data.nearest.index = -1;
|
||||
data.nearest.dist_sq = FLT_MAX;
|
||||
}
|
||||
|
||||
/* dfs search */
|
||||
if (root) {
|
||||
if (calc_tangent_sq(&data, root) < data.nearest.dist_sq)
|
||||
dfs_find_lowest_tangent_dfs(&data, root);
|
||||
}
|
||||
|
||||
/* copy back results */
|
||||
if (nearest) {
|
||||
memcpy(nearest, &data.nearest, sizeof(*nearest));
|
||||
}
|
||||
|
||||
return data.nearest.index;
|
||||
}
|
||||
|
||||
/* return the nearest point to ray */
|
||||
int BLI_bvhtree_find_nearest_to_ray(
|
||||
BVHTree *tree, const float co[3], const float dir[3],
|
||||
const bool ray_is_normalized, const float scale[3],
|
||||
BVHTreeNearest *nearest,
|
||||
BVHTree_NearestToRayCallback callback, void *userdata)
|
||||
{
|
||||
BVHNearestRayData data;
|
||||
BVHNode *root = tree->nodes[tree->totleaf];
|
||||
|
||||
data.tree = tree;
|
||||
|
||||
data.callback = callback;
|
||||
data.userdata = userdata;
|
||||
|
||||
dist_squared_ray_to_aabb_scaled_v3_precalc(&data, co, dir, ray_is_normalized, scale);
|
||||
|
||||
if (nearest) {
|
||||
memcpy(&data.nearest, nearest, sizeof(*nearest));
|
||||
}
|
||||
else {
|
||||
data.nearest.index = -1;
|
||||
data.nearest.dist_sq = FLT_MAX;
|
||||
}
|
||||
|
||||
/* dfs search */
|
||||
if (root) {
|
||||
if (calc_dist_sq_to_ray(&data, root) < data.nearest.dist_sq) {
|
||||
dfs_find_nearest_to_ray_dfs(&data, root);
|
||||
}
|
||||
}
|
||||
|
||||
/* copy back results */
|
||||
if (nearest) {
|
||||
memcpy(nearest, &data.nearest, sizeof(*nearest));
|
||||
}
|
||||
|
||||
return data.nearest.index;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
/** \name BLI_bvhtree_range_query
|
||||
|
||||
@@ -2337,224 +2337,6 @@ bool isect_ray_aabb_v3_simple(
|
||||
}
|
||||
}
|
||||
|
||||
void dist_squared_ray_to_aabb_v3_precalc(
|
||||
struct NearestRayToAABB_Precalc *data,
|
||||
const float ray_origin[3], const float ray_direction[3])
|
||||
{
|
||||
float dir_sq[3];
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
data->ray_origin[i] = ray_origin[i];
|
||||
data->ray_direction[i] = ray_direction[i];
|
||||
data->ray_inv_dir[i] = (data->ray_direction[i] != 0.0f) ? (1.0f / data->ray_direction[i]) : FLT_MAX;
|
||||
/* It has to be a function of `ray_inv_dir`,
|
||||
* since the division of 1 by 0.0f, can be -inf or +inf */
|
||||
data->sign[i] = (data->ray_inv_dir[i] < 0.0f);
|
||||
|
||||
dir_sq[i] = SQUARE(data->ray_direction[i]);
|
||||
}
|
||||
|
||||
/* `diag_sq` Length square of each face diagonal */
|
||||
float diag_sq[3] = {
|
||||
dir_sq[1] + dir_sq[2],
|
||||
dir_sq[0] + dir_sq[2],
|
||||
dir_sq[0] + dir_sq[1],
|
||||
};
|
||||
data->idiag_sq[0] = (diag_sq[0] > FLT_EPSILON) ? (1.0f / diag_sq[0]) : FLT_MAX;
|
||||
data->idiag_sq[1] = (diag_sq[1] > FLT_EPSILON) ? (1.0f / diag_sq[1]) : FLT_MAX;
|
||||
data->idiag_sq[2] = (diag_sq[2] > FLT_EPSILON) ? (1.0f / diag_sq[2]) : FLT_MAX;
|
||||
|
||||
data->cdot_axis[0] = data->ray_direction[0] * data->idiag_sq[0];
|
||||
data->cdot_axis[1] = data->ray_direction[1] * data->idiag_sq[1];
|
||||
data->cdot_axis[2] = data->ray_direction[2] * data->idiag_sq[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the squared distance from a ray to a bound-box `AABB`.
|
||||
* It is based on `fast_ray_nearest_hit` solution to obtain
|
||||
* the coordinates of the nearest edge of Bound Box to the ray
|
||||
*/
|
||||
float dist_squared_ray_to_aabb_v3(
|
||||
const struct NearestRayToAABB_Precalc *data,
|
||||
const float bb_min[3], const float bb_max[3],
|
||||
bool r_axis_closest[3])
|
||||
{
|
||||
/* `tmin` is a vector that has the smaller distances to each of the
|
||||
* infinite planes of the `AABB` faces (hit in nearest face X plane,
|
||||
* nearest face Y plane and nearest face Z plane) */
|
||||
float local_bvmin[3], local_bvmax[3];
|
||||
|
||||
if (data->sign[0] == 0) {
|
||||
local_bvmin[0] = bb_min[0] - data->ray_origin[0];
|
||||
local_bvmax[0] = bb_max[0] - data->ray_origin[0];
|
||||
}
|
||||
else {
|
||||
local_bvmin[0] = bb_max[0] - data->ray_origin[0];
|
||||
local_bvmax[0] = bb_min[0] - data->ray_origin[0];
|
||||
}
|
||||
|
||||
if (data->sign[1] == 0) {
|
||||
local_bvmin[1] = bb_min[1] - data->ray_origin[1];
|
||||
local_bvmax[1] = bb_max[1] - data->ray_origin[1];
|
||||
}
|
||||
else {
|
||||
local_bvmin[1] = bb_max[1] - data->ray_origin[1];
|
||||
local_bvmax[1] = bb_min[1] - data->ray_origin[1];
|
||||
}
|
||||
|
||||
if (data->sign[2] == 0) {
|
||||
local_bvmin[2] = bb_min[2] - data->ray_origin[2];
|
||||
local_bvmax[2] = bb_max[2] - data->ray_origin[2];
|
||||
}
|
||||
else {
|
||||
local_bvmin[2] = bb_max[2] - data->ray_origin[2];
|
||||
local_bvmax[2] = bb_min[2] - data->ray_origin[2];
|
||||
}
|
||||
|
||||
const float tmin[3] = {
|
||||
local_bvmin[0] * data->ray_inv_dir[0],
|
||||
local_bvmin[1] * data->ray_inv_dir[1],
|
||||
local_bvmin[2] * data->ray_inv_dir[2],
|
||||
};
|
||||
|
||||
/* `tmax` is a vector that has the longer distances to each of the
|
||||
* infinite planes of the `AABB` faces (hit in farthest face X plane,
|
||||
* farthest face Y plane and farthest face Z plane) */
|
||||
const float tmax[3] = {
|
||||
local_bvmax[0] * data->ray_inv_dir[0],
|
||||
local_bvmax[1] * data->ray_inv_dir[1],
|
||||
local_bvmax[2] * data->ray_inv_dir[2],
|
||||
};
|
||||
/* `v1` and `v3` is be the coordinates of the nearest `AABB` edge to the ray*/
|
||||
float v1[3], v2[3];
|
||||
/* `rtmin` is the highest value of the smaller distances. == max_axis_v3(tmin)
|
||||
* `rtmax` is the lowest value of longer distances. == min_axis_v3(tmax)*/
|
||||
float rtmin, rtmax, mul, rdist;
|
||||
/* `main_axis` is the axis equivalent to edge close to the ray */
|
||||
int main_axis;
|
||||
|
||||
r_axis_closest[0] = false;
|
||||
r_axis_closest[1] = false;
|
||||
r_axis_closest[2] = false;
|
||||
|
||||
/* *** min_axis_v3(tmax) *** */
|
||||
if ((tmax[0] <= tmax[1]) && (tmax[0] <= tmax[2])) {
|
||||
// printf("# Hit in X %s\n", data->sign[0] ? "min", "max");
|
||||
rtmax = tmax[0];
|
||||
v1[0] = v2[0] = local_bvmax[0];
|
||||
mul = local_bvmax[0] * data->ray_direction[0];
|
||||
main_axis = 3;
|
||||
r_axis_closest[0] = data->sign[0];
|
||||
}
|
||||
else if ((tmax[1] <= tmax[0]) && (tmax[1] <= tmax[2])) {
|
||||
// printf("# Hit in Y %s\n", data->sign[1] ? "min", "max");
|
||||
rtmax = tmax[1];
|
||||
v1[1] = v2[1] = local_bvmax[1];
|
||||
mul = local_bvmax[1] * data->ray_direction[1];
|
||||
main_axis = 2;
|
||||
r_axis_closest[1] = data->sign[1];
|
||||
}
|
||||
else {
|
||||
// printf("# Hit in Z %s\n", data->sign[2] ? "min", "max");
|
||||
rtmax = tmax[2];
|
||||
v1[2] = v2[2] = local_bvmax[2];
|
||||
mul = local_bvmax[2] * data->ray_direction[2];
|
||||
main_axis = 1;
|
||||
r_axis_closest[2] = data->sign[2];
|
||||
}
|
||||
|
||||
/* *** max_axis_v3(tmin) *** */
|
||||
if ((tmin[0] >= tmin[1]) && (tmin[0] >= tmin[2])) {
|
||||
// printf("# To X %s\n", data->sign[0] ? "max", "min");
|
||||
rtmin = tmin[0];
|
||||
v1[0] = v2[0] = local_bvmin[0];
|
||||
mul += local_bvmin[0] * data->ray_direction[0];
|
||||
main_axis -= 3;
|
||||
r_axis_closest[0] = !data->sign[0];
|
||||
}
|
||||
else if ((tmin[1] >= tmin[0]) && (tmin[1] >= tmin[2])) {
|
||||
// printf("# To Y %s\n", data->sign[1] ? "max", "min");
|
||||
rtmin = tmin[1];
|
||||
v1[1] = v2[1] = local_bvmin[1];
|
||||
mul += local_bvmin[1] * data->ray_direction[1];
|
||||
main_axis -= 1;
|
||||
r_axis_closest[1] = !data->sign[1];
|
||||
}
|
||||
else {
|
||||
// printf("# To Z %s\n", data->sign[2] ? "max", "min");
|
||||
rtmin = tmin[2];
|
||||
v1[2] = v2[2] = local_bvmin[2];
|
||||
mul += local_bvmin[2] * data->ray_direction[2];
|
||||
main_axis -= 2;
|
||||
r_axis_closest[2] = !data->sign[2];
|
||||
}
|
||||
/* *** end min/max axis *** */
|
||||
|
||||
|
||||
/* `if rtmax < 0`, the whole `AABB` is behing us */
|
||||
if ((rtmax < 0.0f) && (rtmin < 0.0f)) {
|
||||
return FLT_MAX;
|
||||
}
|
||||
|
||||
if (main_axis < 0) {
|
||||
main_axis += 3;
|
||||
}
|
||||
|
||||
if (data->sign[main_axis] == 0) {
|
||||
v1[main_axis] = local_bvmin[main_axis];
|
||||
v2[main_axis] = local_bvmax[main_axis];
|
||||
}
|
||||
else {
|
||||
v1[main_axis] = local_bvmax[main_axis];
|
||||
v2[main_axis] = local_bvmin[main_axis];
|
||||
}
|
||||
|
||||
/* if rtmin < rtmax, ray intersect `AABB` */
|
||||
if (rtmin <= rtmax) {
|
||||
const float proj = rtmin * data->ray_direction[main_axis];
|
||||
rdist = 0.0f;
|
||||
r_axis_closest[main_axis] = (proj - v1[main_axis]) < (v2[main_axis] - proj);
|
||||
}
|
||||
else {
|
||||
/* `proj` equals to nearest point on the ray closest to the edge `v1 v2` of the `AABB`. */
|
||||
const float proj = mul * data->cdot_axis[main_axis];
|
||||
float depth;
|
||||
if (v1[main_axis] > proj) { /* the nearest point to the ray is the point v1 */
|
||||
/* `depth` is equivalent the distance from the origin to the point v1,
|
||||
* Here's a faster way to calculate the dot product of v1 and ray
|
||||
* (depth = dot_v3v3(v1, data->ray.direction))*/
|
||||
depth = mul + data->ray_direction[main_axis] * v1[main_axis];
|
||||
rdist = len_squared_v3(v1) - SQUARE(depth);
|
||||
r_axis_closest[main_axis] = true;
|
||||
}
|
||||
else if (v2[main_axis] < proj) { /* the nearest point of the ray is the point v2 */
|
||||
depth = mul + data->ray_direction[main_axis] * v2[main_axis];
|
||||
rdist = len_squared_v3(v2) - SQUARE(depth);
|
||||
r_axis_closest[main_axis] = false;
|
||||
}
|
||||
else { /* the nearest point of the ray is on the edge of the `AABB`. */
|
||||
float v[2];
|
||||
mul *= data->idiag_sq[main_axis];
|
||||
if (main_axis == 0) {
|
||||
v[0] = (mul * data->ray_direction[1]) - v1[1];
|
||||
v[1] = (mul * data->ray_direction[2]) - v1[2];
|
||||
}
|
||||
else if (main_axis == 1) {
|
||||
v[0] = (mul * data->ray_direction[0]) - v1[0];
|
||||
v[1] = (mul * data->ray_direction[2]) - v1[2];
|
||||
}
|
||||
else {
|
||||
v[0] = (mul * data->ray_direction[0]) - v1[0];
|
||||
v[1] = (mul * data->ray_direction[1]) - v1[1];
|
||||
}
|
||||
rdist = len_squared_v2(v);
|
||||
r_axis_closest[main_axis] = (proj - v1[main_axis]) < (v2[main_axis] - proj);
|
||||
}
|
||||
}
|
||||
|
||||
return rdist;
|
||||
}
|
||||
|
||||
/* find closest point to p on line through (l1, l2) and return lambda,
|
||||
* where (0 <= lambda <= 1) when cp is in the line segment (l1, l2)
|
||||
*/
|
||||
|
||||
@@ -21,8 +21,15 @@
|
||||
/** \file blender/blenlib/intern/polyfill2d.c
|
||||
* \ingroup bli
|
||||
*
|
||||
* A simple implementation of the ear cutting algorithm
|
||||
* to triangulate simple polygons without holes.
|
||||
* An ear clipping algorithm to triangulate single boundary polygons.
|
||||
*
|
||||
* Details:
|
||||
*
|
||||
* - The algorithm guarantees all triangles are assigned (number of coords - 2)
|
||||
* and that triangles will have non-overlapping indices (even for degenerate geometry).
|
||||
* - Self-intersections are considered degenerate (resulting triangles will overlap).
|
||||
* - While multiple polygons aren't supported, holes can still be defined using *key-holes*
|
||||
* (where the polygon doubles back on its self with *exactly* matching coordinates).
|
||||
*
|
||||
* \note
|
||||
*
|
||||
@@ -74,6 +81,12 @@ typedef signed char eSign;
|
||||
|
||||
#ifdef USE_KDTREE
|
||||
/**
|
||||
* Spatial optimization for point-in-triangle intersection checks.
|
||||
* The simple version of this algorithm is ``O(n^2)`` complexity
|
||||
* (every point needing to check the triangle defined by every other point),
|
||||
* Using a binary-tree reduces the complexity to ``O(n log n)``
|
||||
* plus some overhead of creating the tree.
|
||||
*
|
||||
* This is a single purpose KDTree based on BLI_kdtree with some modifications
|
||||
* to better suit polyfill2d.
|
||||
*
|
||||
|
||||
@@ -10520,6 +10520,9 @@ static void read_libraries(FileData *basefd, ListBase *mainlist)
|
||||
else {
|
||||
mainptr->curlib->filedata = NULL;
|
||||
mainptr->curlib->id.tag |= LIB_TAG_MISSING;
|
||||
/* Set lib version to current main one... Makes assert later happy. */
|
||||
mainptr->versionfile = mainptr->curlib->versionfile = mainl->versionfile;
|
||||
mainptr->subversionfile = mainptr->curlib->subversionfile = mainl->subversionfile;
|
||||
}
|
||||
|
||||
if (fd == NULL) {
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
* - write #TEST (#RenderInfo struct. 128x128 blend file preview is optional).
|
||||
* - write #GLOB (#FileGlobal struct) (some global vars).
|
||||
* - write #DNA1 (#SDNA struct)
|
||||
* - write #USER (#UserDef struct) if filename is ``~/X.XX/config/startup.blend``.
|
||||
* - write #USER (#UserDef struct) if filename is ``~/.config/blender/X.XX/config/startup.blend``.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -46,6 +46,8 @@
|
||||
#include "BLI_dlrbTree.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_curve.h"
|
||||
#include "BKE_fcurve.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_nla.h"
|
||||
#include "BKE_mask.h"
|
||||
@@ -121,7 +123,8 @@ void ANIM_draw_cfra(const bContext *C, View2D *v2d, short flag)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
|
||||
const float x = (float)(scene->r.cfra * scene->r.framelen);
|
||||
const float time = scene->r.cfra + scene->r.subframe;
|
||||
const float x = (float)(time * scene->r.framelen);
|
||||
|
||||
glLineWidth((flag & DRAWCFRA_WIDE) ? 3.0 : 2.0);
|
||||
|
||||
@@ -330,7 +333,8 @@ static float normalization_factor_get(Scene *scene, FCurve *fcu, short flag, flo
|
||||
|
||||
fcu->prev_norm_factor = 1.0f;
|
||||
if (fcu->bezt) {
|
||||
BezTriple *bezt;
|
||||
const bool use_preview_only = PRVRANGEON;
|
||||
const BezTriple *bezt;
|
||||
int i;
|
||||
float max_coord = -FLT_MAX;
|
||||
float min_coord = FLT_MAX;
|
||||
@@ -340,28 +344,77 @@ static float normalization_factor_get(Scene *scene, FCurve *fcu, short flag, flo
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
if (PRVRANGEON) {
|
||||
for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
|
||||
if (IN_RANGE_INCL(bezt->vec[1][0], scene->r.psfra, scene->r.pefra)) {
|
||||
max_coord = max_ff(max_coord, bezt->vec[0][1]);
|
||||
max_coord = max_ff(max_coord, bezt->vec[1][1]);
|
||||
max_coord = max_ff(max_coord, bezt->vec[2][1]);
|
||||
|
||||
min_coord = min_ff(min_coord, bezt->vec[0][1]);
|
||||
min_coord = min_ff(min_coord, bezt->vec[1][1]);
|
||||
min_coord = min_ff(min_coord, bezt->vec[2][1]);
|
||||
}
|
||||
for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
|
||||
if (use_preview_only && !IN_RANGE_INCL(bezt->vec[1][0],
|
||||
scene->r.psfra,
|
||||
scene->r.pefra))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) {
|
||||
max_coord = max_ff(max_coord, bezt->vec[0][1]);
|
||||
max_coord = max_ff(max_coord, bezt->vec[1][1]);
|
||||
max_coord = max_ff(max_coord, bezt->vec[2][1]);
|
||||
|
||||
min_coord = min_ff(min_coord, bezt->vec[0][1]);
|
||||
if (i == 0) {
|
||||
/* We ignore extrapolation flags and handle here, and use the
|
||||
* control point position only. so we normalize "interesting"
|
||||
* part of the curve.
|
||||
*
|
||||
* Here we handle left extrapolation.
|
||||
*/
|
||||
max_coord = max_ff(max_coord, bezt->vec[1][1]);
|
||||
|
||||
min_coord = min_ff(min_coord, bezt->vec[1][1]);
|
||||
min_coord = min_ff(min_coord, bezt->vec[2][1]);
|
||||
}
|
||||
else {
|
||||
const BezTriple *prev_bezt = bezt - 1;
|
||||
if (prev_bezt->ipo == BEZT_IPO_CONST) {
|
||||
/* Constant interpolation: previous CV value is used up
|
||||
* to the current keyframe.
|
||||
*/
|
||||
max_coord = max_ff(max_coord, bezt->vec[1][1]);
|
||||
min_coord = min_ff(min_coord, bezt->vec[1][1]);
|
||||
}
|
||||
else if (prev_bezt->ipo == BEZT_IPO_LIN) {
|
||||
/* Linear interpolation: min/max using both previous and
|
||||
* and current CV.
|
||||
*/
|
||||
max_coord = max_ff(max_coord, bezt->vec[1][1]);
|
||||
min_coord = min_ff(min_coord, bezt->vec[1][1]);
|
||||
max_coord = max_ff(max_coord, prev_bezt->vec[1][1]);
|
||||
min_coord = min_ff(min_coord, prev_bezt->vec[1][1]);
|
||||
}
|
||||
else if (prev_bezt->ipo == BEZT_IPO_BEZ) {
|
||||
const int resol = fcu->driver
|
||||
? 32
|
||||
: min_ii((int)(5.0f * len_v2v2(bezt->vec[1], prev_bezt->vec[1])), 32);
|
||||
if (resol < 2) {
|
||||
max_coord = max_ff(max_coord, prev_bezt->vec[1][1]);
|
||||
min_coord = min_ff(min_coord, prev_bezt->vec[1][1]);
|
||||
}
|
||||
else {
|
||||
float data[120];
|
||||
float v1[2], v2[2], v3[2], v4[2];
|
||||
|
||||
v1[0] = prev_bezt->vec[1][0];
|
||||
v1[1] = prev_bezt->vec[1][1];
|
||||
v2[0] = prev_bezt->vec[2][0];
|
||||
v2[1] = prev_bezt->vec[2][1];
|
||||
|
||||
v3[0] = bezt->vec[0][0];
|
||||
v3[1] = bezt->vec[0][1];
|
||||
v4[0] = bezt->vec[1][0];
|
||||
v4[1] = bezt->vec[1][1];
|
||||
|
||||
correct_bezpart(v1, v2, v3, v4);
|
||||
|
||||
BKE_curve_forward_diff_bezier(v1[0], v2[0], v3[0], v4[0], data, resol, sizeof(float) * 3);
|
||||
BKE_curve_forward_diff_bezier(v1[1], v2[1], v3[1], v4[1], data + 1, resol, sizeof(float) * 3);
|
||||
|
||||
for (int j = 0; j <= resol; ++j) {
|
||||
const float *fp = &data[j * 3];
|
||||
max_coord = max_ff(max_coord, fp[1]);
|
||||
min_coord = min_ff(min_coord, fp[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ static void change_frame_apply(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
int frame = RNA_int_get(op->ptr, "frame");
|
||||
float frame = RNA_float_get(op->ptr, "frame");
|
||||
bool do_snap = RNA_boolean_get(op->ptr, "snap");
|
||||
|
||||
if (do_snap && CTX_wm_space_seq(C)) {
|
||||
@@ -103,10 +103,15 @@ static void change_frame_apply(bContext *C, wmOperator *op)
|
||||
}
|
||||
|
||||
/* set the new frame number */
|
||||
CFRA = frame;
|
||||
CFRA = (int)frame;
|
||||
if (scene->r.flag & SCER_SHOW_SUBFRAME) {
|
||||
SUBFRA = frame - (int)frame;
|
||||
}
|
||||
else {
|
||||
SUBFRA = 0.0f;
|
||||
}
|
||||
FRAMENUMBER_MIN_CLAMP(CFRA);
|
||||
SUBFRA = 0.0f;
|
||||
|
||||
|
||||
/* do updates */
|
||||
BKE_sound_seek_scene(bmain, scene);
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_FRAME, scene);
|
||||
@@ -125,18 +130,18 @@ static int change_frame_exec(bContext *C, wmOperator *op)
|
||||
/* ---- */
|
||||
|
||||
/* Get frame from mouse coordinates */
|
||||
static int frame_from_event(bContext *C, const wmEvent *event)
|
||||
static float frame_from_event(bContext *C, const wmEvent *event)
|
||||
{
|
||||
ARegion *region = CTX_wm_region(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
float viewx;
|
||||
int frame;
|
||||
float frame;
|
||||
|
||||
/* convert from region coordinates to View2D 'tot' space */
|
||||
viewx = UI_view2d_region_to_view_x(®ion->v2d, event->mval[0]);
|
||||
|
||||
/* round result to nearest int (frames are ints!) */
|
||||
frame = iroundf(viewx);
|
||||
frame = viewx;
|
||||
|
||||
if (scene->r.flag & SCER_LOCK_FRAME_SELECTION) {
|
||||
CLAMP(frame, PSFRA, PEFRA);
|
||||
@@ -187,7 +192,7 @@ static int change_frame_invoke(bContext *C, wmOperator *op, const wmEvent *event
|
||||
* as user could click on a single frame (jump to frame) as well as
|
||||
* click-dragging over a range (modal scrubbing).
|
||||
*/
|
||||
RNA_int_set(op->ptr, "frame", frame_from_event(C, event));
|
||||
RNA_float_set(op->ptr, "frame", frame_from_event(C, event));
|
||||
|
||||
change_frame_seq_preview_begin(C, event);
|
||||
|
||||
@@ -215,7 +220,7 @@ static int change_frame_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
break;
|
||||
|
||||
case MOUSEMOVE:
|
||||
RNA_int_set(op->ptr, "frame", frame_from_event(C, event));
|
||||
RNA_float_set(op->ptr, "frame", frame_from_event(C, event));
|
||||
change_frame_apply(C, op);
|
||||
break;
|
||||
|
||||
@@ -268,7 +273,7 @@ static void ANIM_OT_change_frame(wmOperatorType *ot)
|
||||
ot->undo_group = "FRAME_CHANGE";
|
||||
|
||||
/* rna */
|
||||
ot->prop = RNA_def_int(ot->srna, "frame", 0, MINAFRAME, MAXFRAME, "Frame", "", MINAFRAME, MAXFRAME);
|
||||
ot->prop = RNA_def_float(ot->srna, "frame", 0, MINAFRAME, MAXFRAME, "Frame", "", MINAFRAME, MAXFRAME);
|
||||
prop = RNA_def_boolean(ot->srna, "snap", false, "Snap", "");
|
||||
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
||||
}
|
||||
|
||||
@@ -1196,6 +1196,11 @@ void ED_curve_editnurb_load(Object *obedit)
|
||||
|
||||
remap_hooks_and_vertex_parents(obedit);
|
||||
|
||||
/* We have to apply shapekeys *before* copying nurbs into newnurb, otherwise the reset to
|
||||
* refkey/original curve data that has to be done when editing non-refkey shapekey would be useless,
|
||||
* only affecting editnurb and not ob->data. */
|
||||
calc_shapeKeys(obedit);
|
||||
|
||||
for (nu = editnurb->first; nu; nu = nu->next) {
|
||||
newnu = BKE_nurb_duplicate(nu);
|
||||
BLI_addtail(&newnurb, newnu);
|
||||
@@ -1207,7 +1212,6 @@ void ED_curve_editnurb_load(Object *obedit)
|
||||
|
||||
cu->nurb = newnurb;
|
||||
|
||||
calc_shapeKeys(obedit);
|
||||
ED_curve_updateAnimPaths(obedit->data);
|
||||
|
||||
BKE_nurbList_free(&oldnurb);
|
||||
@@ -1228,7 +1232,6 @@ void ED_curve_editnurb_make(Object *obedit)
|
||||
if (actkey) {
|
||||
// XXX strcpy(G.editModeTitleExtra, "(Key) ");
|
||||
undo_editmode_clear();
|
||||
BKE_keyblock_convert_to_curve(actkey, cu, &cu->nurb);
|
||||
}
|
||||
|
||||
if (editnurb) {
|
||||
@@ -1249,12 +1252,16 @@ void ED_curve_editnurb_make(Object *obedit)
|
||||
nu = nu->next;
|
||||
}
|
||||
|
||||
if (actkey)
|
||||
editnurb->shapenr = obedit->shapenr;
|
||||
|
||||
/* animation could be added in editmode even if there was no animdata in
|
||||
* object mode hence we always need CVs index be created */
|
||||
init_editNurb_keyIndex(editnurb, &cu->nurb);
|
||||
|
||||
if (actkey) {
|
||||
editnurb->shapenr = obedit->shapenr;
|
||||
/* Apply shapekey to new nurbs of editnurb, not those of original curve (and *after* we generated keyIndex),
|
||||
* else we do not have valid 'original' data to properly restore curve when leaving editmode. */
|
||||
BKE_keyblock_convert_to_curve(actkey, cu, &editnurb->nurbs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -953,6 +953,9 @@ static int gp_dissolve_selected_points(bContext *C)
|
||||
/* skip strokes that are invalid for current view */
|
||||
if (ED_gpencil_stroke_can_use(C, gps) == false)
|
||||
continue;
|
||||
/* check if the color is editable */
|
||||
if (ED_gpencil_stroke_color_use(gpl, gps) == false)
|
||||
continue;
|
||||
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
bGPDspoint *pt;
|
||||
@@ -1165,6 +1168,9 @@ static int gp_delete_selected_points(bContext *C)
|
||||
/* skip strokes that are invalid for current view */
|
||||
if (ED_gpencil_stroke_can_use(C, gps) == false)
|
||||
continue;
|
||||
/* check if the color is editable */
|
||||
if (ED_gpencil_stroke_color_use(gpl, gps) == false)
|
||||
continue;
|
||||
|
||||
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
@@ -1204,7 +1210,7 @@ static int gp_delete_exec(bContext *C, wmOperator *op)
|
||||
case GP_DELETEOP_POINTS: /* selected points (breaks the stroke into segments) */
|
||||
result = gp_delete_selected_points(C);
|
||||
break;
|
||||
|
||||
|
||||
case GP_DELETEOP_FRAME: /* active frame */
|
||||
result = gp_actframe_delete_exec(C, op);
|
||||
break;
|
||||
|
||||
@@ -653,9 +653,9 @@ DEF_ICON(IPO_BACK)
|
||||
DEF_ICON(IPO_EASE_IN)
|
||||
DEF_ICON(IPO_EASE_OUT)
|
||||
DEF_ICON(IPO_EASE_IN_OUT)
|
||||
DEF_ICON(NORMALIZE_FCURVES)
|
||||
#ifndef DEF_ICON_BLANK_SKIP
|
||||
/* available */
|
||||
DEF_ICON(BLANK203)
|
||||
DEF_ICON(BLANK204)
|
||||
DEF_ICON(BLANK205)
|
||||
DEF_ICON(BLANK206)
|
||||
|
||||
@@ -1596,7 +1596,7 @@ static void widget_draw_text_icon(uiFontStyle *fstyle, uiWidgetColors *wcol, uiB
|
||||
temp.xmin = temp.xmax - (BLI_rcti_size_y(rect) * 1.08f);
|
||||
|
||||
if (extra_icon_type == UI_BUT_ICONEXTRA_CLEAR) {
|
||||
widget_draw_icon(but, ICON_X, alpha, &temp, false);
|
||||
widget_draw_icon(but, ICON_PANEL_CLOSE, alpha, &temp, false);
|
||||
}
|
||||
else if (extra_icon_type == UI_BUT_ICONEXTRA_EYEDROPPER) {
|
||||
widget_draw_icon(but, ICON_EYEDROPPER, alpha, &temp, false);
|
||||
|
||||
@@ -887,13 +887,13 @@ static void write_result_func(TaskPool * __restrict pool,
|
||||
ReportList reports;
|
||||
BKE_reports_init(&reports, oglrender->reports->flag & ~RPT_PRINT);
|
||||
/* Do actual save logic here, depending on the file format. */
|
||||
Scene tmp_scene = *scene;
|
||||
tmp_scene.r.cfra = cfra;
|
||||
if (is_movie) {
|
||||
/* We have to construct temporary scene with proper scene->r.cfra.
|
||||
* This is because underlying calls do not use r.cfra but use scene
|
||||
* for that.
|
||||
*/
|
||||
Scene tmp_scene = *scene;
|
||||
tmp_scene.r.cfra = cfra;
|
||||
ok = RE_WriteRenderViewsMovie(&reports,
|
||||
rr,
|
||||
&tmp_scene,
|
||||
@@ -917,8 +917,8 @@ static void write_result_func(TaskPool * __restrict pool,
|
||||
true,
|
||||
NULL);
|
||||
|
||||
BKE_render_result_stamp_info(scene, scene->camera, rr, false);
|
||||
ok = RE_WriteRenderViewsImage(NULL, rr, scene, true, name);
|
||||
BKE_render_result_stamp_info(&tmp_scene, tmp_scene.camera, rr, false);
|
||||
ok = RE_WriteRenderViewsImage(NULL, rr, &tmp_scene, true, name);
|
||||
if (!ok) {
|
||||
BKE_reportf(&reports,
|
||||
RPT_ERROR,
|
||||
|
||||
@@ -1258,21 +1258,6 @@ static void space_view3d_listener(bScreen *UNUSED(sc), ScrArea *sa, struct wmNot
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* removed since BKE_image_user_frame_calc is now called in view3d_draw_bgpic because screen_ops doesnt call the notifier. */
|
||||
#if 0
|
||||
if (wmn->category == NC_SCENE && wmn->data == ND_FRAME) {
|
||||
View3D *v3d = area->spacedata.first;
|
||||
BGpic *bgpic = v3d->bgpicbase.first;
|
||||
|
||||
for (; bgpic; bgpic = bgpic->next) {
|
||||
if (bgpic->ima) {
|
||||
Scene *scene = wmn->reference;
|
||||
BKE_image_user_frame_calc(&bgpic->iuser, scene->r.cfra, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
const char *view3d_context_dir[] = {
|
||||
|
||||
@@ -1051,7 +1051,6 @@ static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt)
|
||||
static bool snapDerivedMesh(
|
||||
SnapObjectContext *sctx, SnapData *snapdata,
|
||||
Object *ob, DerivedMesh *dm, float obmat[4][4], const unsigned int ob_index,
|
||||
bool do_bb,
|
||||
/* read/write args */
|
||||
float *ray_depth, float *dist_px,
|
||||
/* return args */
|
||||
@@ -1112,39 +1111,31 @@ static bool snapDerivedMesh(
|
||||
copy_v3_v3(ray_org_local, snapdata->ray_origin);
|
||||
mul_m4_v3(imat, ray_org_local);
|
||||
|
||||
if (do_bb) {
|
||||
BoundBox *bb = BKE_object_boundbox_get(ob);
|
||||
|
||||
if (bb) {
|
||||
BoundBox bb_temp;
|
||||
|
||||
/* We cannot afford a bounding box with some null dimension, which may happen in some cases...
|
||||
* Threshold is rather high, but seems to be needed to get good behavior, see T46099. */
|
||||
bb = BKE_boundbox_ensure_minimum_dimensions(bb, &bb_temp, 1e-1f);
|
||||
|
||||
/* In vertex and edges you need to get the pixel distance from ray to BoundBox, see T46816. */
|
||||
if (ELEM(snapdata->snap_to, SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE)) {
|
||||
float dist_px_sq = dist_squared_to_projected_aabb_simple(
|
||||
lpmat, snapdata->win_half, ray_min_dist, snapdata->mval,
|
||||
ray_org_local, ray_normal_local, bb->vec[0], bb->vec[6]);
|
||||
if (dist_px_sq > SQUARE(*dist_px))
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
/* Test BoundBox */
|
||||
BoundBox *bb = BKE_object_boundbox_get(ob);
|
||||
if (bb) {
|
||||
/* In vertex and edges you need to get the pixel distance from ray to BoundBox, see: T46099, T46816 */
|
||||
if (ELEM(snapdata->snap_to, SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE)) {
|
||||
float dist_px_sq = dist_squared_to_projected_aabb_simple(
|
||||
lpmat, snapdata->win_half, ray_min_dist, snapdata->mval,
|
||||
ray_org_local, ray_normal_local, bb->vec[0], bb->vec[6]);
|
||||
if (dist_px_sq > SQUARE(*dist_px))
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
else {
|
||||
/* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */
|
||||
if (!isect_ray_aabb_v3_simple(
|
||||
ray_start_local, ray_normal_local, bb->vec[0], bb->vec[6], NULL, NULL))
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
/* was local_depth, see: T47838 */
|
||||
len_diff = dist_aabb_to_plane(bb->vec[0], bb->vec[6], ray_start_local, ray_normal_local);
|
||||
if (len_diff < 0) len_diff = 0.0f;
|
||||
need_ray_start_correction_init = false;
|
||||
}
|
||||
else {
|
||||
/* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */
|
||||
if (!isect_ray_aabb_v3_simple(
|
||||
ray_start_local, ray_normal_local, bb->vec[0], bb->vec[6], NULL, NULL))
|
||||
{
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
/* was local_depth, see: T47838 */
|
||||
len_diff = dist_aabb_to_plane(bb->vec[0], bb->vec[6], ray_start_local, ray_normal_local);
|
||||
if (len_diff < 0) len_diff = 0.0f;
|
||||
need_ray_start_correction_init = false;
|
||||
}
|
||||
|
||||
SnapObjectData_Mesh *sod = NULL;
|
||||
@@ -1295,10 +1286,17 @@ static bool snapDerivedMesh(
|
||||
}
|
||||
/* SCE_SNAP_MODE_VERTEX or SCE_SNAP_MODE_EDGE */
|
||||
else {
|
||||
|
||||
/* Warning: the depth_max is currently being used only in perspective view.
|
||||
* It is not correct to limit the maximum depth for elements obtained with nearest
|
||||
* since this limitation depends on the normal and the size of the occlusion face.
|
||||
* And more... ray_depth is being confused with Z-depth here... (varies only the precision) */
|
||||
const float ray_depth_max_global = *ray_depth + snapdata->depth_range[0];
|
||||
|
||||
Nearest2dUserData neasrest2d = {
|
||||
.dist_px_sq = SQUARE(*dist_px),
|
||||
.r_axis_closest = {1.0f, 1.0f, 1.0f},
|
||||
.depth_range = {snapdata->depth_range[0], *ray_depth + snapdata->depth_range[0]},
|
||||
.depth_range = {snapdata->depth_range[0], ray_depth_max_global},
|
||||
.userdata = treedata,
|
||||
.get_edge_verts = (Nearest2DGetEdgeVertsCallback)get_dm_edge_verts,
|
||||
.copy_vert_no = (Nearest2DCopyVertNoCallback)copy_dm_vert_no,
|
||||
@@ -1650,7 +1648,6 @@ static bool snapObject(
|
||||
}
|
||||
retval = snapDerivedMesh(
|
||||
sctx, snapdata, ob, dm, obmat, ob_index,
|
||||
true,
|
||||
ray_depth, dist_px,
|
||||
r_loc, r_no,
|
||||
r_index, r_hit_list);
|
||||
|
||||
@@ -1765,6 +1765,7 @@ typedef struct Scene {
|
||||
#define SCER_LOCK_FRAME_SELECTION (1<<1)
|
||||
/* timeline/keyframe jumping - only selected items (on by default) */
|
||||
#define SCE_KEYS_NO_SELONLY (1<<2)
|
||||
#define SCER_SHOW_SUBFRAME (1<<3)
|
||||
|
||||
/* mode (int now) */
|
||||
#define R_OSA 0x0001
|
||||
|
||||
@@ -807,6 +807,21 @@ static void rna_Scene_frame_current_set(PointerRNA *ptr, int value)
|
||||
data->r.cfra = value;
|
||||
}
|
||||
|
||||
static float rna_Scene_frame_float_get(PointerRNA *ptr)
|
||||
{
|
||||
Scene *data = (Scene *)ptr->data;
|
||||
return (float)data->r.cfra + data->r.subframe;
|
||||
}
|
||||
|
||||
static void rna_Scene_frame_float_set(PointerRNA *ptr, float value)
|
||||
{
|
||||
Scene *data = (Scene *)ptr->data;
|
||||
/* if negative frames aren't allowed, then we can't use them */
|
||||
FRAMENUMBER_MIN_CLAMP(value);
|
||||
data->r.cfra = (int)value;
|
||||
data->r.subframe = value - data->r.cfra;
|
||||
}
|
||||
|
||||
static float rna_Scene_frame_current_final_get(PointerRNA *ptr)
|
||||
{
|
||||
Scene *scene = (Scene *)ptr->data;
|
||||
@@ -887,6 +902,12 @@ static void rna_Scene_preview_range_end_frame_set(PointerRNA *ptr, int value)
|
||||
data->r.pefra = value;
|
||||
}
|
||||
|
||||
static void rna_Scene_show_subframe_update(Main *UNUSED(bmain), Scene *UNUSED(current_scene), PointerRNA *ptr)
|
||||
{
|
||||
Scene *scene = (Scene *)ptr->id.data;
|
||||
scene->r.subframe = 0.0f;
|
||||
}
|
||||
|
||||
static void rna_Scene_frame_update(Main *bmain, Scene *UNUSED(current_scene), PointerRNA *ptr)
|
||||
{
|
||||
Scene *scene = (Scene *)ptr->id.data;
|
||||
@@ -8470,8 +8491,19 @@ void RNA_def_scene(BlenderRNA *brna)
|
||||
prop = RNA_def_property(srna, "frame_subframe", PROP_FLOAT, PROP_TIME);
|
||||
RNA_def_property_float_sdna(prop, NULL, "r.subframe");
|
||||
RNA_def_property_ui_text(prop, "Current Sub-Frame", "");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE | PROP_EDITABLE);
|
||||
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_range(prop, 0.0f, 1.0f);
|
||||
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.01, 2);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_FRAME, "rna_Scene_frame_update");
|
||||
|
||||
prop = RNA_def_property(srna, "frame_float", PROP_FLOAT, PROP_TIME);
|
||||
RNA_def_property_ui_text(prop, "Current Sub-Frame", "");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_range(prop, MINAFRAME, MAXFRAME);
|
||||
RNA_def_property_ui_range(prop, MINAFRAME, MAXFRAME, 0.1, 2);
|
||||
RNA_def_property_float_funcs(prop, "rna_Scene_frame_float_get", "rna_Scene_frame_float_set", NULL);
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_FRAME, "rna_Scene_frame_update");
|
||||
|
||||
prop = RNA_def_property(srna, "frame_start", PROP_INT, PROP_TIME);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_int_sdna(prop, NULL, "r.sfra");
|
||||
@@ -8536,7 +8568,15 @@ void RNA_def_scene(BlenderRNA *brna)
|
||||
RNA_def_property_int_funcs(prop, NULL, "rna_Scene_preview_range_end_frame_set", NULL);
|
||||
RNA_def_property_ui_text(prop, "Preview Range End Frame", "Alternative end frame for UI playback");
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_FRAME, NULL);
|
||||
|
||||
|
||||
/* Subframe for moblur debug. */
|
||||
prop = RNA_def_property(srna, "show_subframe", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "r.flag", SCER_SHOW_SUBFRAME);
|
||||
RNA_def_property_ui_text(prop, "Show Subframe",
|
||||
"Show current scene subframe and allow set it using interface tools");
|
||||
RNA_def_property_update(prop, NC_SCENE | ND_FRAME, "rna_Scene_show_subframe_update");
|
||||
|
||||
/* Timeline / Time Navigation settings */
|
||||
prop = RNA_def_property(srna, "show_keys_from_selected_only", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SCE_KEYS_NO_SELONLY);
|
||||
|
||||
@@ -95,6 +95,11 @@ add_test(bevel ${TEST_BLENDER_EXE}
|
||||
--python-text run_tests
|
||||
)
|
||||
|
||||
add_test(split_faces ${TEST_BLENDER_EXE}
|
||||
${TEST_SRC_DIR}/modeling/split_faces_test.blend
|
||||
--python-text run_tests
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# IO TESTS
|
||||
|
||||
@@ -417,7 +422,12 @@ if(WITH_CYCLES)
|
||||
-idiff "${OPENIMAGEIO_IDIFF}"
|
||||
)
|
||||
endif()
|
||||
endmacro()
|
||||
endmacro()
|
||||
if(WITH_OPENGL_TESTS)
|
||||
add_cycles_render_test(opengl)
|
||||
endif()
|
||||
add_cycles_render_test(image)
|
||||
add_cycles_render_test(mblur)
|
||||
add_cycles_render_test(reports)
|
||||
add_cycles_render_test(render)
|
||||
add_cycles_render_test(shader)
|
||||
|
||||
@@ -47,20 +47,41 @@ def printMessage(type, status, message):
|
||||
|
||||
|
||||
def render_file(filepath):
|
||||
command = (
|
||||
BLENDER,
|
||||
"--background",
|
||||
"-noaudio",
|
||||
"--factory-startup",
|
||||
"--enable-autoexec",
|
||||
filepath,
|
||||
"-E", "CYCLES",
|
||||
# Run with OSL enabled
|
||||
# "--python-expr", "import bpy; bpy.context.scene.cycles.shading_system = True",
|
||||
"-o", TEMP_FILE_MASK,
|
||||
"-F", "PNG",
|
||||
"-f", "1",
|
||||
dirname = os.path.dirname(filepath)
|
||||
basedir = os.path.dirname(dirname)
|
||||
subject = os.path.basename(dirname)
|
||||
if subject == 'opengl':
|
||||
command = (
|
||||
BLENDER,
|
||||
"--window-geometry", "0", "0", "1", "1",
|
||||
"-noaudio",
|
||||
"--factory-startup",
|
||||
"--enable-autoexec",
|
||||
filepath,
|
||||
"-E", "CYCLES",
|
||||
# Run with OSL enabled
|
||||
# "--python-expr", "import bpy; bpy.context.scene.cycles.shading_system = True",
|
||||
"-o", TEMP_FILE_MASK,
|
||||
"-F", "PNG",
|
||||
'--python', os.path.join(basedir,
|
||||
"util",
|
||||
"render_opengl.py")
|
||||
)
|
||||
else:
|
||||
command = (
|
||||
BLENDER,
|
||||
"--background",
|
||||
"-noaudio",
|
||||
"--factory-startup",
|
||||
"--enable-autoexec",
|
||||
filepath,
|
||||
"-E", "CYCLES",
|
||||
# Run with OSL enabled
|
||||
# "--python-expr", "import bpy; bpy.context.scene.cycles.shading_system = True",
|
||||
"-o", TEMP_FILE_MASK,
|
||||
"-F", "PNG",
|
||||
"-f", "1",
|
||||
)
|
||||
try:
|
||||
output = subprocess.check_output(command)
|
||||
if VERBOSE:
|
||||
|
||||
Reference in New Issue
Block a user