From 3373b8154b16d345b0e1fcbdb55d03d8ec088006 Mon Sep 17 00:00:00 2001 From: Stuart Broadfoot Date: Tue, 15 Jan 2013 19:44:41 +0000 Subject: Cycles Hair: Introduction of Cardinal Spline Curve Segments and minor fixes. The curve segment primitive has been added. This includes an intersection function and changes to the BVH. A few small errors in the line segment intersection routine are also fixed. --- intern/cycles/blender/addon/properties.py | 17 +- intern/cycles/blender/addon/ui.py | 9 +- intern/cycles/blender/blender_curves.cpp | 5 +- intern/cycles/bvh/bvh.cpp | 16 +- intern/cycles/bvh/bvh_build.cpp | 20 +- intern/cycles/kernel/kernel_bvh.h | 449 +++++++++++++++++++++++++++--- intern/cycles/kernel/kernel_types.h | 3 +- intern/cycles/render/curves.cpp | 107 +++++-- intern/cycles/render/curves.h | 12 +- 9 files changed, 552 insertions(+), 86 deletions(-) (limited to 'intern') diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 8b90b0bd4ff..9dfacf19c81 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -74,13 +74,14 @@ enum_curve_presets = ( enum_curve_primitives = ( ('TRIANGLES', "Triangles", "Create triangle geometry around strands"), ('LINE_SEGMENTS', "Line Segments", "Use line segment primitives"), - ('CURVE_SEGMENTS', "?Curve Segments?", "Use curve segment primitives (not implemented)"), + ('CURVE_SEGMENTS', "Curve Segments", "Use segmented cardinal curve primitives"), + ('CURVE_RIBBONS', "Curve Ribbons", "Use smooth cardinal curve ribbon primitives"), ) enum_triangle_curves = ( - ('CAMERA', "Planes", "Create individual triangles forming planes that face camera"), - ('RIBBONS', "Ribbons", "Create individual triangles forming ribbon"), - ('TESSELLATED', "Tessellated", "Create mesh surrounding each strand"), + ('CAMERA_TRIANGLES', "Planes", "Create individual triangles forming planes that face camera"), + ('RIBBON_TRIANGLES', "Ribbons", "Create individual triangles forming ribbon"), + ('TESSELLATED_TRIANGLES', "Tessellated", "Create mesh surrounding each strand"), ) enum_line_curves = ( @@ -643,7 +644,7 @@ class CyclesCurveRenderSettings(bpy.types.PropertyGroup): name="Mesh Geometry", description="Method for creating triangle geometry", items=enum_triangle_curves, - default='CAMERA', + default='CAMERA_TRIANGLES', ) cls.line_method = EnumProperty( name="Intersection Method", @@ -730,6 +731,12 @@ class CyclesCurveRenderSettings(bpy.types.PropertyGroup): min=0, max=100.0, default=1.01, ) + cls.subdivisions = IntProperty( + name="Subdivisions", + description="Number of subdivisions used in Cardinal curve intersection (power of 2)", + min=0, max=24, + default=3, + ) @classmethod def unregister(cls): diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index a24da593b43..9767bd70f46 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -976,7 +976,7 @@ class CyclesRender_PT_CurveRendering(CyclesButtonsPanel, Panel): if cscene.primitive == 'TRIANGLES': layout.prop(cscene, "triangle_method", text="Method") - if cscene.triangle_method == 'TESSELLATED': + if cscene.triangle_method == 'TESSELLATED_TRIANGLES': layout.prop(cscene, "resolution", text="Resolution") layout.prop(cscene, "use_smooth", text="Smooth") elif cscene.primitive == 'LINE_SEGMENTS': @@ -997,6 +997,13 @@ class CyclesRender_PT_CurveRendering(CyclesButtonsPanel, Panel): row = layout.row() row.prop(cscene, "segments", text="Segments") row.prop(cscene, "normalmix", text="Ray Mix") + elif cscene.primitive == 'CURVE_SEGMENTS' or cscene.primitive == 'CURVE_RIBBONS': + layout.prop(cscene, "subdivisions", text="Curve subdivisions") + layout.prop(cscene, "use_backfacing", text="Check back-faces") + + layout.prop(cscene, "interpolation", text="Interpolation") + row = layout.row() + row.prop(cscene, "segments", text="Segments") row = layout.row() row.prop(cscene, "use_cache", text="Export cache with children") diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/blender_curves.cpp index 41e1249dae7..f8795ec0a94 100644 --- a/intern/cycles/blender/blender_curves.cpp +++ b/intern/cycles/blender/blender_curves.cpp @@ -929,6 +929,7 @@ void BlenderSync::sync_curve_settings() curve_system_manager->resolution = get_int(csscene, "resolution"); curve_system_manager->segments = get_int(csscene, "segments"); curve_system_manager->use_smooth = get_boolean(csscene, "use_smooth"); + curve_system_manager->subdivisions = get_int(csscene, "subdivisions"); curve_system_manager->normalmix = get_float(csscene, "normalmix"); curve_system_manager->encasing_ratio = get_float(csscene, "encasing_ratio"); @@ -1055,11 +1056,11 @@ void BlenderSync::sync_curves(Mesh *mesh, BL::Mesh b_mesh, BL::Object b_ob, bool if(primitive == CURVE_TRIANGLES){ int vert_num = mesh->triangles.size() * 3; - if(triangle_method == CURVE_CAMERA) { + if(triangle_method == CURVE_CAMERA_TRIANGLES) { ExportCurveTrianglePlanes(mesh, &CData, interpolation, use_smooth, segments, RotCam); ExportCurveTriangleUVs(mesh, &CData, interpolation, use_smooth, segments, vert_num, 1); } - else if(triangle_method == CURVE_RIBBONS) { + else if(triangle_method == CURVE_RIBBON_TRIANGLES) { ExportCurveTriangleRibbons(mesh, &CData, interpolation, use_smooth, segments); ExportCurveTriangleUVs(mesh, &CData, interpolation, use_smooth, segments, vert_num, 1); } diff --git a/intern/cycles/bvh/bvh.cpp b/intern/cycles/bvh/bvh.cpp index 412b44031e6..011406344ea 100644 --- a/intern/cycles/bvh/bvh.cpp +++ b/intern/cycles/bvh/bvh.cpp @@ -18,6 +18,7 @@ #include "mesh.h" #include "object.h" #include "scene.h" +#include "curves.h" #include "bvh.h" #include "bvh_build.h" @@ -631,8 +632,19 @@ void RegularBVH::refit_node(int idx, bool leaf, BoundBox& bbox, uint& visibility int k0 = mesh->curves[pidx - str_offset].first_key + pack.prim_segment[prim]; // XXX! int k1 = k0 + 1; - bbox.grow(mesh->curve_keys[k0].co, mesh->curve_keys[k0].radius); - bbox.grow(mesh->curve_keys[k1].co, mesh->curve_keys[k1].radius); + float3 p[4]; + p[0] = mesh->curve_keys[max(k0 - 1,mesh->curves[pidx - str_offset].first_key)].co; + p[1] = mesh->curve_keys[k0].co; + p[2] = mesh->curve_keys[k1].co; + p[3] = mesh->curve_keys[min(k1 + 1,mesh->curves[pidx - str_offset].first_key + mesh->curves[pidx - str_offset].num_keys - 1)].co; + float3 lower; + float3 upper; + curvebounds(&lower.x, &upper.x, p, 0); + curvebounds(&lower.y, &upper.y, p, 1); + curvebounds(&lower.z, &upper.z, p, 2); + float mr = max(mesh->curve_keys[k0].radius,mesh->curve_keys[k1].radius); + bbox.grow(lower, mr); + bbox.grow(upper, mr); } else { /* triangles */ diff --git a/intern/cycles/bvh/bvh_build.cpp b/intern/cycles/bvh/bvh_build.cpp index 38fb1a15a13..022c4c8d294 100644 --- a/intern/cycles/bvh/bvh_build.cpp +++ b/intern/cycles/bvh/bvh_build.cpp @@ -24,6 +24,7 @@ #include "mesh.h" #include "object.h" #include "scene.h" +#include "curves.h" #include "util_debug.h" #include "util_foreach.h" @@ -91,11 +92,20 @@ void BVHBuild::add_reference_mesh(BoundBox& root, BoundBox& center, Mesh *mesh, for(int k = 0; k < curve.num_keys - 1; k++) { BoundBox bounds = BoundBox::empty; - float3 co0 = mesh->curve_keys[curve.first_key + k].co; - float3 co1 = mesh->curve_keys[curve.first_key + k + 1].co; - - bounds.grow(co0, mesh->curve_keys[curve.first_key + k].radius); - bounds.grow(co1, mesh->curve_keys[curve.first_key + k + 1].radius); + float3 co[4]; + co[0] = mesh->curve_keys[max(curve.first_key + k - 1,curve.first_key)].co; + co[1] = mesh->curve_keys[curve.first_key + k].co; + co[2] = mesh->curve_keys[curve.first_key + k + 1].co; + co[3] = mesh->curve_keys[min(curve.first_key + k + 2, curve.first_key + curve.num_keys - 1)].co; + + float3 lower; + float3 upper; + curvebounds(&lower.x, &upper.x, co, 0); + curvebounds(&lower.y, &upper.y, co, 1); + curvebounds(&lower.z, &upper.z, co, 2); + float mr = max(mesh->curve_keys[curve.first_key + k].radius, mesh->curve_keys[curve.first_key + k + 1].radius); + bounds.grow(lower, mr); + bounds.grow(upper, mr); if(bounds.valid()) { references.push_back(BVHReference(bounds, j, i, k)); diff --git a/intern/cycles/kernel/kernel_bvh.h b/intern/cycles/kernel/kernel_bvh.h index 6d770041b26..d8372a2aab1 100644 --- a/intern/cycles/kernel/kernel_bvh.h +++ b/intern/cycles/kernel/kernel_bvh.h @@ -206,6 +206,315 @@ __device_inline void bvh_triangle_intersect(KernelGlobals *kg, Intersection *ise } #ifdef __HAIR__ +__device_inline void curvebounds(float *lower, float *lowert, float *upper, float *uppert, float p0, float p1, float p2, float p3) +{ + float halfdiscroot = (p2 * p2 - 3 * p3 * p1); + float ta = -1.0f; + float tb = -1.0f; + *uppert = 0.0f; + *upper = p0; + *lowert = 1.0f; + *lower = p0 + p1 + p2 + p3; + if(*lower >= *upper) { + *uppert = 1.0f; + *upper = *lower; + *lowert = 0.0f; + *lower = p0; + } + + if(halfdiscroot >= 0) { + halfdiscroot = sqrt(halfdiscroot); + ta = (-p2 - halfdiscroot) / (3 * p3); + tb = (-p2 + halfdiscroot) / (3 * p3); + } + + float t2; + float t3; + if(ta > 0.0f && ta < 1.0f) { + t2 = ta * ta; + t3 = t2 * ta; + float extrem = p3 * t3 + p2 * t2 + p1 * ta + p0; + if(extrem > *upper) { + *upper = extrem; + *uppert = ta; + } + if(extrem < *lower) { + *lower = extrem; + *lowert = ta; + } + } + if(tb > 0.0f && tb < 1.0f) { + t2 = tb * tb; + t3 = t2 * tb; + float extrem = p3 * t3 + p2 * t2 + p1 * tb + p0; + if(extrem >= *upper) { + *upper = extrem; + *uppert = tb; + } + if(extrem <= *lower) { + *lower = extrem; + *lowert = tb; + } + } +} + +__device_inline void bvh_cardinal_curve_intersect(KernelGlobals *kg, Intersection *isect, + float3 P, float3 idir, uint visibility, int object, int curveAddr, int segment) +{ + int depth = kernel_data.curve_kernel_data.subdivisions; + + /* curve Intersection check */ + float3 dir = 1.0f/idir; + + int flags = kernel_data.curve_kernel_data.curveflags; + + int prim = kernel_tex_fetch(__prim_index, curveAddr); + + float3 curve_coef[4]; + float r_st,r_en; + + /*obtain curve parameters*/ + { + /*ray transform created - this shold be created at beginning of intersection loop*/ + Transform htfm; + float d = sqrtf(dir.x * dir.x + dir.z * dir.z); + htfm = make_transform( + dir.z / d, 0, -dir.x /d, 0, + -dir.x * dir.y /d, d, -dir.y * dir.z /d, 0, + dir.x, dir.y, dir.z, 0, + 0, 0, 0, 1) * make_transform( + 1, 0, 0, -P.x, + 0, 1, 0, -P.y, + 0, 0, 1, -P.z, + 0, 0, 0, 1); + + float4 v00 = kernel_tex_fetch(__curves, prim); + + int k0 = __float_as_int(v00.x) + segment; + int k1 = k0 + 1; + + int ka = max(k0 - 1,__float_as_int(v00.x)); + int kb = min(k1 + 1,__float_as_int(v00.x) + __float_as_int(v00.y) - 1); + + float4 P0 = kernel_tex_fetch(__curve_keys, ka); + float4 P1 = kernel_tex_fetch(__curve_keys, k0); + float4 P2 = kernel_tex_fetch(__curve_keys, k1); + float4 P3 = kernel_tex_fetch(__curve_keys, kb); + + float3 p0 = transform_point(&htfm, float4_to_float3(P0)); + float3 p1 = transform_point(&htfm, float4_to_float3(P1)); + float3 p2 = transform_point(&htfm, float4_to_float3(P2)); + float3 p3 = transform_point(&htfm, float4_to_float3(P3)); + + float fc = 0.71f; + curve_coef[0] = p1; + curve_coef[1] = -fc*p0 + fc*p2; + curve_coef[2] = 2.0f * fc * p0 + (fc - 3.0f) * p1 + (3.0f - 2.0f * fc) * p2 - fc * p3; + curve_coef[3] = -fc * p0 + (2.0f - fc) * p1 + (fc - 2.0f) * p2 + fc * p3; + r_st = P1.w; + r_en = P2.w; + } + + + float r_curr = max(r_st, r_en); + + /*find bounds - this is slow for cubic curves*/ + float xbound[4]; + curvebounds(&xbound[0], &xbound[1], &xbound[2], &xbound[3], curve_coef[0].x, curve_coef[1].x, curve_coef[2].x, curve_coef[3].x); + if(xbound[0] > r_curr || xbound[2] < -r_curr) + return; + + float ybound[4]; + curvebounds(&ybound[0], &ybound[1], &ybound[2], &ybound[3], curve_coef[0].y, curve_coef[1].y, curve_coef[2].y, curve_coef[3].y); + if(ybound[0] > r_curr || ybound[2] < -r_curr) + return; + + float zbound[4]; + curvebounds(&zbound[0], &zbound[1], &zbound[2], &zbound[3], curve_coef[0].z, curve_coef[1].z, curve_coef[2].z, curve_coef[3].z); + if(zbound[0] - r_curr > isect->t || zbound[2] + r_curr < 0.0f) + return; + + + /*setup recurrent loop*/ + int level = 1 << depth; + int tree = 0; + float resol = 0.5f / (float)level; + + int xmin = (int)(xbound[1] / resol); + int xmax = (int)(xbound[3] / resol); + int ymin = (int)(ybound[1] / resol); + int ymax = (int)(ybound[3] / resol); + int zmin = (int)(zbound[1] / resol); + int zmax = (int)(zbound[3] / resol); + /*begin loop*/ + while(!(tree >> (depth + 1))) { + float i_st = tree * resol; + float i_en = i_st + (level * resol); + float3 p_st = ((curve_coef[3] * i_st + curve_coef[2]) * i_st + curve_coef[1]) * i_st + curve_coef[0]; + float3 p_en = ((curve_coef[3] * i_en + curve_coef[2]) * i_en + curve_coef[1]) * i_en + curve_coef[0]; + + + float bminx = min(p_st.x, p_en.x); + float bmaxx = max(p_st.x, p_en.x); + float bminy = min(p_st.y, p_en.y); + float bmaxy = max(p_st.y, p_en.y); + float bminz = min(p_st.z, p_en.z); + float bmaxz = max(p_st.z, p_en.z); + + if(tree == xmin) + bminx = xbound[0]; + if(tree == xmax) + bmaxx = xbound[2]; + if(tree == ymin) + bminy = ybound[0]; + if(tree == ymax) + bmaxy = ybound[2]; + if(tree == zmin) + bminz = zbound[0]; + if(tree == zmax) + bmaxz = zbound[2]; + + + float r1 = r_st + (r_en - r_st) * i_st; + float r2 = r_st + (r_en - r_st) * i_en; + r_curr = max(r1, r2); + + if (bminz - r_curr > isect->t || bmaxz + r_curr < 0.0f|| bminx > r_curr || bmaxx < -r_curr || bminy > r_curr || bmaxy < -r_curr) { + /* the bounding box does not overlap the square centered at O.*/ + tree += level; + level = tree & -tree; + } + else if (level == 1) { + + /* the maximum recursion depth is reached. + * check if dP0.(Q-P0)>=0 and dPn.(Pn-Q)>=0. + * dP* is reversed if necessary.*/ + float t = isect->t; + float u = 0.0f; + if(flags & CURVE_KN_RIBBONS) { + float3 tg = (p_en - p_st); + float w = tg.x * tg.x + tg.y * tg.y; + if (w == 0) { + tree++; + level = tree & -tree; + continue; + } + w = -(p_st.x * tg.x + p_st.y * tg.y) / w; + w = clamp((float)w, 0.0f, 1.0f); + + /* compute u on the curve segment.*/ + u = i_st * (1 - w) + i_en * w; + r_curr = r_st + (r_en - r_st) * u; + /* compare x-y distances.*/ + float3 p_curr = ((curve_coef[3] * u + curve_coef[2]) * u + curve_coef[1]) * u + curve_coef[0]; + + float3 dp_st = (3 * curve_coef[3] * i_st + 2 * curve_coef[2]) * i_st + curve_coef[1]; + if (dot(tg, dp_st)< 0) + dp_st *= -1; + if (dot(dp_st, -p_st) + p_curr.z * dp_st.z < 0) { + tree++; + level = tree & -tree; + continue; + } + float3 dp_en = (3 * curve_coef[3] * i_en + 2 * curve_coef[2]) * i_en + curve_coef[1]; + if (dot(tg, dp_en) < 0) + dp_en *= -1; + if (dot(dp_en, p_en) - p_curr.z * dp_en.z < 0) { + tree++; + level = tree & -tree; + continue; + } + + if (p_curr.x * p_curr.x + p_curr.y * p_curr.y >= r_curr * r_curr || p_curr.z <= 0.0f) { + tree++; + level = tree & -tree; + continue; + } + /* compare z distances.*/ + if (isect->t < p_curr.z) { + tree++; + level = tree & -tree; + continue; + } + t = p_curr.z; + } + else { + float l = len(p_en - p_st); + float3 tg = (p_en - p_st) / l; + float gd = (r2 - r1) / l; + float difz = -dot(p_st,tg); + float cyla = 1.0f - (tg.z * tg.z * (1 + gd*gd)); + float halfb = (-p_st.z - tg.z*(difz + gd*(difz*gd + r1))); + float tcentre = -halfb/cyla; + float zcentre = difz + (tg.z * tcentre); + float3 tdif = - p_st; + tdif.z += tcentre; + float tdifz = dot(tdif,tg); + float tb = 2*(tdif.z - tg.z*(tdifz + gd*(tdifz*gd + r1))); + float tc = dot(tdif,tdif) - tdifz * tdifz * (1 + gd*gd) - r1*r1 - 2*r1*tdifz*gd; + float td = tb*tb - 4*cyla*tc; + if (td < 0.0f){ + tree++; + level = tree & -tree; + continue; + } + + float rootd = sqrtf(td); + float correction = ((-tb - rootd)/(2*cyla)); + t = tcentre + correction; + float w = (zcentre + (tg.z * correction))/l; + + float3 dp_st = (3 * curve_coef[3] * i_st + 2 * curve_coef[2]) * i_st + curve_coef[1]; + if (dot(tg, dp_st)< 0) + dp_st *= -1; + float3 dp_en = (3 * curve_coef[3] * i_en + 2 * curve_coef[2]) * i_en + curve_coef[1]; + if (dot(tg, dp_en) < 0) + dp_en *= -1; + + + if(flags & CURVE_KN_BACKFACING && (dot(dp_st, -p_st) + t * dp_st.z < 0 || dot(dp_en, p_en) - t * dp_en.z < 0 || isect->t < t || t <= 0.0f)) { + correction = ((-tb + rootd)/(2*cyla)); + t = tcentre + correction; + w = (zcentre + (tg.z * correction))/l; + } + + if (dot(dp_st, -p_st) + t * dp_st.z < 0 || dot(dp_en, p_en) - t * dp_en.z < 0 || isect->t < t || t <= 0.0f) { + tree++; + level = tree & -tree; + continue; + } + + w = clamp((float)w, 0.0f, 1.0f); + /* compute u on the curve segment.*/ + u = i_st * (1 - w) + i_en * w; + + } + /* we found a new intersection.*/ +#ifdef __VISIBILITY_FLAG__ + /* visibility flag test. we do it here under the assumption + * that most triangles are culled by node flags */ + if(kernel_tex_fetch(__prim_visibility, curveAddr) & visibility) +#endif + { + /* record intersection */ + isect->prim = curveAddr; + isect->segment = segment; + isect->object = object; + isect->u = u; + isect->v = 0.0f; + isect->t = t; + } + + tree++; + level = tree & -tree; + } + else { + /* split the curve into two curves and process */ + level = level >> 1; + } + } +} + __device_inline void bvh_curve_intersect(KernelGlobals *kg, Intersection *isect, float3 P, float3 idir, uint visibility, int object, int curveAddr, int segment) { @@ -222,7 +531,6 @@ __device_inline void bvh_curve_intersect(KernelGlobals *kg, Intersection *isect, float4 P1 = kernel_tex_fetch(__curve_keys, k0); float4 P2 = kernel_tex_fetch(__curve_keys, k1); - float l = len(P2 - P1); float r1 = P1.w; float r2 = P2.w; float mr = max(r1,r2); @@ -230,6 +538,7 @@ __device_inline void bvh_curve_intersect(KernelGlobals *kg, Intersection *isect, float3 p2 = float4_to_float3(P2); float3 dif = P - p1; float3 dir = 1.0f/idir; + float l = len(p2 - p1); float sp_r = mr + 0.5f * l; float3 sphere_dif = P - ((p1 + p2) * 0.5f); @@ -425,8 +734,12 @@ __device bool bvh_intersect(KernelGlobals *kg, const Ray *ray, const uint visibi /* intersect ray against primitive */ #ifdef __HAIR__ uint segment = kernel_tex_fetch(__prim_segment, primAddr); - if(segment != ~0) - bvh_curve_intersect(kg, isect, P, idir, visibility, object, primAddr, segment); + if(segment != ~0) { + if(kernel_data.curve_kernel_data.curveflags & CURVE_KN_INTERPOLATE) + bvh_cardinal_curve_intersect(kg, isect, P, idir, visibility, object, primAddr, segment); + else + bvh_curve_intersect(kg, isect, P, idir, visibility, object, primAddr, segment); + } else #endif bvh_triangle_intersect(kg, isect, P, idir, visibility, object, primAddr); @@ -551,8 +864,12 @@ __device bool bvh_intersect_motion(KernelGlobals *kg, const Ray *ray, const uint /* intersect ray against primitive */ #ifdef __HAIR__ uint segment = kernel_tex_fetch(__prim_segment, primAddr); - if(segment != ~0) - bvh_curve_intersect(kg, isect, P, idir, visibility, object, primAddr, segment); + if(segment != ~0) { + if(kernel_data.curve_kernel_data.curveflags & CURVE_KN_INTERPOLATE) + bvh_cardinal_curve_intersect(kg, isect, P, idir, visibility, object, primAddr, segment); + else + bvh_curve_intersect(kg, isect, P, idir, visibility, object, primAddr, segment); + } else #endif bvh_triangle_intersect(kg, isect, P, idir, visibility, object, primAddr); @@ -697,6 +1014,32 @@ __device_inline float3 bvh_triangle_refine(KernelGlobals *kg, ShaderData *sd, co } #ifdef __HAIR__ + +__device_inline float3 curvetangent(float t, float3 p0, float3 p1, float3 p2, float3 p3) +{ + float fc = 0.71f; + float data[4]; + float t2 = t * t; + data[0] = -3.0f * fc * t2 + 4.0f * fc * t - fc; + data[1] = 3.0f * (2.0f - fc) * t2 + 2.0f * (fc - 3.0f) * t; + data[2] = 3.0f * (fc - 2.0f) * t2 + 2.0f * (3.0f - 2.0f * fc) * t + fc; + data[3] = 3.0f * fc * t2 - 2.0f * fc * t; + return data[0] * p0 + data[1] * p1 + data[2] * p2 + data[3] * p3; +} + +__device_inline float3 curvepoint(float t, float3 p0, float3 p1, float3 p2, float3 p3) +{ + float data[4]; + float fc = 0.71f; + float t2 = t * t; + float t3 = t2 * t; + data[0] = -fc * t3 + 2.0f * fc * t2 - fc * t; + data[1] = (2.0f - fc) * t3 + (fc - 3.0f) * t2 + 1.0f; + data[2] = (fc - 2.0f) * t3 + (3.0f - 2.0f * fc) * t2 + fc * t; + data[3] = fc * t3 - fc * t2; + return data[0] * p0 + data[1] * p1 + data[2] * p2 + data[3] * p3; +} + __device_inline float3 bvh_curve_refine(KernelGlobals *kg, ShaderData *sd, const Intersection *isect, const Ray *ray, float t) { int flag = kernel_data.curve_kernel_data.curveflags; @@ -723,64 +1066,92 @@ __device_inline float3 bvh_curve_refine(KernelGlobals *kg, ShaderData *sd, const float4 P1 = kernel_tex_fetch(__curve_keys, k0); float4 P2 = kernel_tex_fetch(__curve_keys, k1); - float l = len(P2 - P1); + float l = 1.0f; + float3 tg = normalize_len(float4_to_float3(P2 - P1),&l); float r1 = P1.w; float r2 = P2.w; - float3 tg = float4_to_float3(P2 - P1) / l; - float3 dif = P - float4_to_float3(P1) + t * D; float gd = ((r2 - r1)/l); - + P = P + D*t; - dif = P - float4_to_float3(P1); + if(flag & CURVE_KN_INTERPOLATE) { + int ka = max(k0 - 1,__float_as_int(v00.x)); + int kb = min(k1 + 1,__float_as_int(v00.x) + __float_as_int(v00.y) - 1); - #ifdef __UV__ - sd->u = dot(dif,tg)/l; - sd->v = 0.0f; - #endif + float4 P0 = kernel_tex_fetch(__curve_keys, ka); + float4 P3 = kernel_tex_fetch(__curve_keys, kb); - if (flag & CURVE_KN_TRUETANGENTGNORMAL) { - sd->Ng = -(D - tg * (dot(tg,D) * kernel_data.curve_kernel_data.normalmix)); - sd->Ng = normalize(sd->Ng); - if (flag & CURVE_KN_NORMALCORRECTION) - { - //sd->Ng = normalize(sd->Ng); + float3 p[4]; + p[0] = float4_to_float3(P0); + p[1] = float4_to_float3(P1); + p[2] = float4_to_float3(P2); + p[3] = float4_to_float3(P3); + + tg = normalize(curvetangent(isect->u,p[0],p[1],p[2],p[3])); + float3 p_curr = curvepoint(isect->u,p[0],p[1],p[2],p[3]); + +#ifdef __UV__ + sd->u = isect->u; + sd->v = 0.0f; +#endif + + if(kernel_data.curve_kernel_data.curveflags & CURVE_KN_RIBBONS) + sd->Ng = normalize(-(D - tg * (dot(tg,D)))); + else { + sd->Ng = normalize(P - p_curr); sd->Ng = sd->Ng - gd * tg; sd->Ng = normalize(sd->Ng); } + sd->N = sd->Ng; } else { - sd->Ng = (dif - tg * sd->u * l) / (P1.w + sd->u * l * gd); - if (gd != 0.0f) { - sd->Ng = sd->Ng - gd * tg ; + float3 dif = P - float4_to_float3(P1); + +#ifdef __UV__ + sd->u = dot(dif,tg)/l; + sd->v = 0.0f; +#endif + + if (flag & CURVE_KN_TRUETANGENTGNORMAL) { + sd->Ng = -(D - tg * (dot(tg,D) * kernel_data.curve_kernel_data.normalmix)); sd->Ng = normalize(sd->Ng); + if (flag & CURVE_KN_NORMALCORRECTION) { + sd->Ng = sd->Ng - gd * tg; + sd->Ng = normalize(sd->Ng); + } + } + else { + sd->Ng = (dif - tg * sd->u * l) / (P1.w + sd->u * l * gd); + if (gd != 0.0f) { + sd->Ng = sd->Ng - gd * tg ; + sd->Ng = normalize(sd->Ng); + } } - } - sd->N = sd->Ng; + sd->N = sd->Ng; - if (flag & CURVE_KN_TANGENTGNORMAL && !(flag & CURVE_KN_TRUETANGENTGNORMAL)) { - sd->N = -(D - tg * (dot(tg,D) * kernel_data.curve_kernel_data.normalmix)); - sd->N = normalize(sd->N); - if (flag & CURVE_KN_NORMALCORRECTION) { - //sd->N = normalize(sd->N); - sd->N = sd->N - gd * tg; + if (flag & CURVE_KN_TANGENTGNORMAL && !(flag & CURVE_KN_TRUETANGENTGNORMAL)) { + sd->N = -(D - tg * (dot(tg,D) * kernel_data.curve_kernel_data.normalmix)); sd->N = normalize(sd->N); + if (flag & CURVE_KN_NORMALCORRECTION) { + sd->N = sd->N - gd * tg; + sd->N = normalize(sd->N); + } } - } - if (!(flag & CURVE_KN_TANGENTGNORMAL) && flag & CURVE_KN_TRUETANGENTGNORMAL) { - sd->N = (dif - tg * sd->u * l) / (P1.w + sd->u * l * gd); - if (gd != 0.0f) { - sd->N = sd->N - gd * tg ; - sd->N = normalize(sd->N); + if (!(flag & CURVE_KN_TANGENTGNORMAL) && flag & CURVE_KN_TRUETANGENTGNORMAL) { + sd->N = (dif - tg * sd->u * l) / (P1.w + sd->u * l * gd); + if (gd != 0.0f) { + sd->N = sd->N - gd * tg ; + sd->N = normalize(sd->N); + } } } - #ifdef __DPDU__ +#ifdef __DPDU__ /* dPdu/dPdv */ sd->dPdu = tg; sd->dPdv = cross(tg,sd->Ng); - #endif +#endif if(isect->object != ~0) { #ifdef __OBJECT_MOTION__ diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/kernel_types.h index 83c157b1f36..ce37b54f215 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/kernel_types.h @@ -700,6 +700,7 @@ typedef enum CurveFlag { CURVE_KN_NORMALCORRECTION = 128, /* correct tangent normal for slope? */ CURVE_KN_TRUETANGENTGNORMAL = 256, /* use tangent normal for geometry? */ CURVE_KN_TANGENTGNORMAL = 512, /* use tangent normal for shader? */ + CURVE_KN_RIBBONS = 1024, /* use flat curve ribbons */ } CurveFlag; typedef struct KernelCurves { @@ -707,7 +708,7 @@ typedef struct KernelCurves { float normalmix; float encasing_ratio; int curveflags; - int pad; + int subdivisions; } KernelCurves; diff --git a/intern/cycles/render/curves.cpp b/intern/cycles/render/curves.cpp index 3299503b4ab..3ee441ef095 100644 --- a/intern/cycles/render/curves.cpp +++ b/intern/cycles/render/curves.cpp @@ -29,6 +29,52 @@ CCL_NAMESPACE_BEGIN +/* Curve functions */ + +void curvebounds(float *lower, float *upper, float3 *p, int dim) +{ + float *p0 = &p[0].x; + float *p1 = &p[1].x; + float *p2 = &p[2].x; + float *p3 = &p[3].x; + float fc = 0.71f; + float curve_coef[4]; + curve_coef[0] = p1[dim]; + curve_coef[1] = -fc*p0[dim] + fc*p2[dim]; + curve_coef[2] = 2.0f * fc * p0[dim] + (fc - 3.0f) * p1[dim] + (3.0f - 2.0f * fc) * p2[dim] - fc * p3[dim]; + curve_coef[3] = -fc * p0[dim] + (2.0f - fc) * p1[dim] + (fc - 2.0f) * p2[dim] + fc * p3[dim]; + float discroot = curve_coef[2] * curve_coef[2] - 3 * curve_coef[3] * curve_coef[1]; + float ta = -1.0f; + float tb = -1.0f; + if(discroot >= 0) { + discroot = sqrt(discroot); + ta = (-curve_coef[2] - discroot) / (3 * curve_coef[3]); + tb = (-curve_coef[2] + discroot) / (3 * curve_coef[3]); + ta = (ta > 1.0f || ta < 0.0f) ? -1.0f : ta; + tb = (tb > 1.0f || tb < 0.0f) ? -1.0f : tb; + } + + *upper = max(p1[dim],p2[dim]); + *lower = min(p1[dim],p2[dim]); + float exa = p1[dim]; + float exb = p2[dim]; + float t2; + float t3; + if(ta >= 0.0f) { + t2 = ta * ta; + t3 = t2 * ta; + exa = curve_coef[3] * t3 + curve_coef[2] * t2 + curve_coef[1] * ta + curve_coef[0]; + } + if(tb >= 0.0f) { + t2 = tb * tb; + t3 = t2 * tb; + exb = curve_coef[3] * t3 + curve_coef[2] * t2 + curve_coef[1] * tb + curve_coef[0]; + } + *upper = max(*upper, max(exa,exb)); + *lower = min(*lower, min(exa,exb)); + +} + /* Hair System Manager */ CurveSystemManager::CurveSystemManager() @@ -36,9 +82,10 @@ CurveSystemManager::CurveSystemManager() primitive = CURVE_LINE_SEGMENTS; line_method = CURVE_CORRECTED; interpolation = CURVE_CARDINAL; - triangle_method = CURVE_CAMERA; + triangle_method = CURVE_CAMERA_TRIANGLES; resolution = 3; segments = 1; + subdivisions = 3; normalmix = 1.0f; encasing_ratio = 1.01f; @@ -75,31 +122,36 @@ void CurveSystemManager::device_update(Device *device, DeviceScene *dscene, Scen kcurve->curveflags = 0; - if(primitive == CURVE_SEGMENTS) - kcurve->curveflags |= CURVE_KN_INTERPOLATE; - - if(line_method == CURVE_ACCURATE) - kcurve->curveflags |= CURVE_KN_ACCURATE; - if(line_method == CURVE_CORRECTED) - kcurve->curveflags |= CURVE_KN_INTERSECTCORRECTION; - if(line_method == CURVE_POSTCORRECTED) - kcurve->curveflags |= CURVE_KN_POSTINTERSECTCORRECTION; - - if(use_tangent_normal) - kcurve->curveflags |= CURVE_KN_TANGENTGNORMAL; - if(use_tangent_normal_correction) - kcurve->curveflags |= CURVE_KN_NORMALCORRECTION; - if(use_tangent_normal_geometry) - kcurve->curveflags |= CURVE_KN_TRUETANGENTGNORMAL; - if(use_joined) - kcurve->curveflags |= CURVE_KN_CURVEDATA; - if(use_backfacing) - kcurve->curveflags |= CURVE_KN_BACKFACING; - if(use_encasing) - kcurve->curveflags |= CURVE_KN_ENCLOSEFILTER; - - kcurve->normalmix = normalmix; - kcurve->encasing_ratio = encasing_ratio; + if(use_curves) { + if(primitive == CURVE_SEGMENTS || primitive == CURVE_RIBBONS) + kcurve->curveflags |= CURVE_KN_INTERPOLATE; + if(primitive == CURVE_RIBBONS) + kcurve->curveflags |= CURVE_KN_RIBBONS; + + if(line_method == CURVE_ACCURATE) + kcurve->curveflags |= CURVE_KN_ACCURATE; + if(line_method == CURVE_CORRECTED) + kcurve->curveflags |= CURVE_KN_INTERSECTCORRECTION; + if(line_method == CURVE_POSTCORRECTED) + kcurve->curveflags |= CURVE_KN_POSTINTERSECTCORRECTION; + + if(use_tangent_normal) + kcurve->curveflags |= CURVE_KN_TANGENTGNORMAL; + if(use_tangent_normal_correction) + kcurve->curveflags |= CURVE_KN_NORMALCORRECTION; + if(use_tangent_normal_geometry) + kcurve->curveflags |= CURVE_KN_TRUETANGENTGNORMAL; + if(use_joined) + kcurve->curveflags |= CURVE_KN_CURVEDATA; + if(use_backfacing) + kcurve->curveflags |= CURVE_KN_BACKFACING; + if(use_encasing) + kcurve->curveflags |= CURVE_KN_ENCLOSEFILTER; + + kcurve->normalmix = normalmix; + kcurve->encasing_ratio = encasing_ratio; + kcurve->subdivisions = subdivisions; + } if(progress.get_cancel()) return; @@ -130,7 +182,8 @@ bool CurveSystemManager::modified(const CurveSystemManager& CurveSystemManager) use_curves == CurveSystemManager.use_curves && use_joined == CurveSystemManager.use_joined && segments == CurveSystemManager.segments && - use_parents == CurveSystemManager.use_parents); + use_parents == CurveSystemManager.use_parents && + subdivisions == CurveSystemManager.subdivisions); } bool CurveSystemManager::modified_mesh(const CurveSystemManager& CurveSystemManager) diff --git a/intern/cycles/render/curves.h b/intern/cycles/render/curves.h index bb9ef6d99cf..4e6166c6916 100644 --- a/intern/cycles/render/curves.h +++ b/intern/cycles/render/curves.h @@ -29,6 +29,8 @@ class DeviceScene; class Progress; class Scene; +void curvebounds(float *lower, float *upper, float3 *p, int dim); + typedef enum curve_presets { CURVE_CUSTOM, CURVE_TANGENT_SHADING, @@ -39,13 +41,14 @@ typedef enum curve_presets { typedef enum curve_primitives { CURVE_TRIANGLES, CURVE_LINE_SEGMENTS, - CURVE_SEGMENTS + CURVE_SEGMENTS, + CURVE_RIBBONS } curve_primitives; typedef enum curve_triangles { - CURVE_CAMERA, - CURVE_RIBBONS, - CURVE_TESSELATED + CURVE_CAMERA_TRIANGLES, + CURVE_RIBBON_TRIANGLES, + CURVE_TESSELATED_TRIANGLES } curve_triangles; typedef enum curve_lines { @@ -98,6 +101,7 @@ public: int triangle_method; int resolution; int segments; + int subdivisions; float normalmix; float encasing_ratio; -- cgit v1.2.3