Merge branch 'master' into blender2.8

Conflicts:
	source/blender/editors/animation/anim_draw.c
This commit is contained in:
2017-02-17 20:03:55 +01:00
66 changed files with 1626 additions and 1557 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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();

View File

@@ -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__ */

View File

@@ -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++;
}
}

View File

@@ -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;

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -104,6 +104,7 @@ public:
primitive_mask = PRIMITIVE_ALL;
num_motion_curve_steps = 0;
num_motion_triangle_steps = 0;
}
/* SAH costs */

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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

View File

@@ -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 */

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -73,7 +73,7 @@ public:
bool need_update;
int total_pixel_samples;
size_t total_pixel_samples;
private:
BakeData *m_bake_data;

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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,

View File

@@ -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)

View File

@@ -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()

View File

@@ -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()

View File

@@ -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")

View File

@@ -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;

View File

@@ -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);

View File

@@ -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"

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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]);

View File

@@ -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

View File

@@ -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));
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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 */

View File

@@ -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;

View File

@@ -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,

View File

@@ -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]);

View File

@@ -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

View File

@@ -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)
*/

View File

@@ -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.
*

View File

@@ -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) {

View File

@@ -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``.
*/

View File

@@ -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]);
}
}
}
}
}

View File

@@ -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(&region->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);
}

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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)

View File

@@ -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);

View File

@@ -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,

View File

@@ -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[] = {

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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)

View File

@@ -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: