diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp index 10bcbf6..8c0d405 100644 --- a/intern/cycles/app/cycles_xml.cpp +++ b/intern/cycles/app/cycles_xml.cpp @@ -455,6 +455,9 @@ static void xml_read_shader_graph(const XMLReadState& state, Shader *shader, pug else if(string_iequals(node.name(), "noise_texture")) { snode = new NoiseTextureNode(); } + else if(string_iequals(node.name(), "gabor_texture")) { + snode = new GaborTextureNode(); + } else if(string_iequals(node.name(), "checker_texture")) { snode = new CheckerTextureNode(); } diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 6175c8e..9c52015 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -629,6 +629,12 @@ static ShaderNode *add_node(Scene *scene, BL::BlendData b_data, BL::Scene b_scen get_tex_mapping(&noise->tex_mapping, b_noise_node.texture_mapping()); node = noise; } + else if (b_node.is_a(&RNA_ShaderNodeTexGabor)) { + BL::ShaderNodeTexGabor b_gabor_node(b_node); + GaborTextureNode *gabor = new GaborTextureNode(); + get_tex_mapping(&gabor->tex_mapping, b_gabor_node.texture_mapping()); + node = gabor; + } else if (b_node.is_a(&RNA_ShaderNodeTexMusgrave)) { BL::ShaderNodeTexMusgrave b_musgrave_node(b_node); MusgraveTextureNode *musgrave = new MusgraveTextureNode(); diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index 96c7cef..8d80765 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -147,6 +147,7 @@ CCL_NAMESPACE_END #include "svm_blackbody.h" #include "svm_closure.h" #include "svm_noisetex.h" +#include "svm_gabortex.h" #include "svm_convert.h" #include "svm_displace.h" #include "svm_fresnel.h" @@ -256,6 +257,9 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, Shade case NODE_TEX_NOISE: svm_node_tex_noise(kg, sd, stack, node, &offset); break; + case NODE_TEX_GABOR: + svm_node_tex_gabor(kg, sd, stack, node, &offset); + break; case NODE_TEX_VORONOI: svm_node_tex_voronoi(kg, sd, stack, node, &offset); break; diff --git a/intern/cycles/kernel/svm/svm_gabortex.h b/intern/cycles/kernel/svm/svm_gabortex.h new file mode 100644 index 0000000..8ba1d9f --- /dev/null +++ b/intern/cycles/kernel/svm/svm_gabortex.h @@ -0,0 +1,59 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +CCL_NAMESPACE_BEGIN + +/* Gabor */ + +ccl_device_inline float svm_gabor(float3 p, float scale, float seed, float impulses) +{ + float3 vec = p * scale; + + GaborParams gp; + setup_gabor_parameters(gp, impulses); + + return min(max(gabor_grid(gp, vec, seed) * 0.5f + 0.5f, 0.0f), 1.0f); +} + +ccl_device void svm_node_tex_gabor(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +{ + uint co_offset, scale_offset, seed_offset, impulse_offset, fac_offset, color_offset; + + decode_node_uchar4(node.y, &co_offset, &scale_offset, &seed_offset, &impulse_offset); + + uint4 node2 = read_node(kg, offset); + + float scale = stack_load_float_default(stack, scale_offset, node2.x); + float seed = stack_load_float_default(stack, seed_offset, node2.y); + float impulses = stack_load_float_default(stack, impulse_offset, node2.z); + float3 co = stack_load_float3(stack, co_offset); + + float fac = svm_gabor(co, scale, seed, impulses); + + float3 color = make_float3(fac, + svm_gabor(make_float3(co.y, co.x, co.z), scale, seed, impulses), + svm_gabor(make_float3(co.y, co.z, co.x), scale, seed, impulses)); + + decode_node_uchar4(node.z, &color_offset, &fac_offset, NULL, NULL); + + if(stack_valid(fac_offset)) + stack_store_float(stack, fac_offset, fac); + if(stack_valid(color_offset)) + stack_store_float3(stack, color_offset, color); +} + +CCL_NAMESPACE_END + diff --git a/intern/cycles/kernel/svm/svm_noise.h b/intern/cycles/kernel/svm/svm_noise.h index 282ad19..c389f77 100644 --- a/intern/cycles/kernel/svm/svm_noise.h +++ b/intern/cycles/kernel/svm/svm_noise.h @@ -5,7 +5,7 @@ * All Rights Reserved. * * Modifications Copyright 2011, Blender Foundation. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: @@ -32,57 +32,58 @@ CCL_NAMESPACE_BEGIN -#ifndef __KERNEL_SSE2__ +/* Used by Perlin and Gabor */ ccl_device int quick_floor(float x) { - return float_to_int(x) - ((x < 0) ? 1 : 0); + return float_to_int(x) - ((x < 0) ? 1 : 0); } -#else + +#ifdef __KERNEL_SSE2__ ccl_device_inline __m128i quick_floor_sse(const __m128& x) { - __m128i b = _mm_cvttps_epi32(x); - __m128i isneg = _mm_castps_si128(_mm_cmplt_ps(x, _mm_set1_ps(0.0f))); - return _mm_add_epi32(b, isneg); // unsaturated add 0xffffffff is the same as subtract -1 + __m128i b = _mm_cvttps_epi32(x); + __m128i isneg = _mm_castps_si128(_mm_cmplt_ps(x, _mm_set1_ps(0.0f))); + return _mm_add_epi32(b, isneg); // unsaturated add 0xffffffff is the same as subtract -1 } #endif #ifndef __KERNEL_SSE2__ ccl_device float bits_to_01(uint bits) { - return bits * (1.0f/(float)0xFFFFFFFF); + return bits * (1.0f/(float)0xFFFFFFFF); } #else ccl_device_inline __m128 bits_to_01_sse(const __m128i& bits) { - return _mm_mul_ps(uint32_to_float(bits), _mm_set1_ps(1.0f/(float)0xFFFFFFFF)); + return _mm_mul_ps(uint32_to_float(bits), _mm_set1_ps(1.0f/(float)0xFFFFFFFF)); } #endif ccl_device uint hash(uint kx, uint ky, uint kz) { - // define some handy macros + // define some handy macros #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) #define final(a,b,c) \ -{ \ - c ^= b; c -= rot(b,14); \ - a ^= c; a -= rot(c,11); \ - b ^= a; b -= rot(a,25); \ - c ^= b; c -= rot(b,16); \ - a ^= c; a -= rot(c,4); \ - b ^= a; b -= rot(a,14); \ - c ^= b; c -= rot(b,24); \ -} - // now hash the data! - uint a, b, c, len = 3; - a = b = c = 0xdeadbeef + (len << 2) + 13; - - c += kz; - b += ky; - a += kx; - final(a, b, c); - - return c; - // macros not needed anymore + { \ + c ^= b; c -= rot(b,14); \ + a ^= c; a -= rot(c,11); \ + b ^= a; b -= rot(a,25); \ + c ^= b; c -= rot(b,16); \ + a ^= c; a -= rot(c,4); \ + b ^= a; b -= rot(a,14); \ + c ^= b; c -= rot(b,24); \ +} + // now hash the data! + uint a, b, c, len = 3; + a = b = c = 0xdeadbeef + (len << 2) + 13; + + c += kz; + b += ky; + a += kx; + final(a, b, c); + + return c; + // macros not needed anymore #undef rot #undef final } @@ -93,21 +94,21 @@ ccl_device_inline __m128i hash_sse(const __m128i& kx, const __m128i& ky, const _ #define rot(x,k) _mm_or_si128(_mm_slli_epi32((x), (k)), _mm_srli_epi32((x), 32-(k))) #define xor_rot(a, b, c) do {a = _mm_xor_si128(a, b); a = _mm_sub_epi32(a, rot(b, c));} while(0) - uint len = 3; - __m128i magic = _mm_set1_epi32(0xdeadbeef + (len << 2) + 13); - __m128i a = _mm_add_epi32(magic, kx); - __m128i b = _mm_add_epi32(magic, ky); - __m128i c = _mm_add_epi32(magic, kz); - - xor_rot(c, b, 14); - xor_rot(a, c, 11); - xor_rot(b, a, 25); - xor_rot(c, b, 16); - xor_rot(a, c, 4); - xor_rot(b, a, 14); - xor_rot(c, b, 24); - - return c; + uint len = 3; + __m128i magic = _mm_set1_epi32(0xdeadbeef + (len << 2) + 13); + __m128i a = _mm_add_epi32(magic, kx); + __m128i b = _mm_add_epi32(magic, ky); + __m128i c = _mm_add_epi32(magic, kz); + + xor_rot(c, b, 14); + xor_rot(a, c, 11); + xor_rot(b, a, 25); + xor_rot(c, b, 16); + xor_rot(a, c, 4); + xor_rot(b, a, 14); + xor_rot(c, b, 24); + + return c; #undef rot #undef xor_rot } @@ -116,256 +117,256 @@ ccl_device_inline __m128i hash_sse(const __m128i& kx, const __m128i& ky, const _ #if 0 // unused ccl_device int imod(int a, int b) { - a %= b; - return a < 0 ? a + b : a; + a %= b; + return a < 0 ? a + b : a; } ccl_device uint phash(int kx, int ky, int kz, int3 p) { - return hash(imod(kx, p.x), imod(ky, p.y), imod(kz, p.z)); + return hash(imod(kx, p.x), imod(ky, p.y), imod(kz, p.z)); } #endif #ifndef __KERNEL_SSE2__ ccl_device float floorfrac(float x, int* i) { - *i = quick_floor(x); - return x - *i; + *i = quick_floor(x); + return x - *i; } #else ccl_device_inline __m128 floorfrac_sse(const __m128& x, __m128i *i) { - *i = quick_floor_sse(x); - return _mm_sub_ps(x, _mm_cvtepi32_ps(*i)); + *i = quick_floor_sse(x); + return _mm_sub_ps(x, _mm_cvtepi32_ps(*i)); } #endif #ifndef __KERNEL_SSE2__ ccl_device float fade(float t) { - return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); + return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); } #else ccl_device_inline __m128 fade_sse(const __m128 *t) { - __m128 a = fma(*t, _mm_set1_ps(6.0f), _mm_set1_ps(-15.0f)); - __m128 b = fma(*t, a, _mm_set1_ps(10.0f)); - return _mm_mul_ps(_mm_mul_ps(*t, *t), _mm_mul_ps(*t, b)); + __m128 a = fma(*t, _mm_set1_ps(6.0f), _mm_set1_ps(-15.0f)); + __m128 b = fma(*t, a, _mm_set1_ps(10.0f)); + return _mm_mul_ps(_mm_mul_ps(*t, *t), _mm_mul_ps(*t, b)); } #endif #ifndef __KERNEL_SSE2__ ccl_device float nerp(float t, float a, float b) { - return (1.0f - t) * a + t * b; + return (1.0f - t) * a + t * b; } #else ccl_device_inline __m128 nerp_sse(const __m128& t, const __m128& a, const __m128& b) { - __m128 x1 = _mm_mul_ps(_mm_sub_ps(_mm_set1_ps(1.0f), t), a); - return fma(t, b, x1); + __m128 x1 = _mm_mul_ps(_mm_sub_ps(_mm_set1_ps(1.0f), t), a); + return fma(t, b, x1); } #endif #ifndef __KERNEL_SSE2__ ccl_device float grad(int hash, float x, float y, float z) { - // use vectors pointing to the edges of the cube - int h = hash & 15; - float u = h<8 ? x : y; - float vt = ((h == 12) | (h == 14)) ? x : z; - float v = h < 4 ? y : vt; - return ((h&1) ? -u : u) + ((h&2) ? -v : v); + // use vectors pointing to the edges of the cube + int h = hash & 15; + float u = h<8 ? x : y; + float vt = ((h == 12) | (h == 14)) ? x : z; + float v = h < 4 ? y : vt; + return ((h&1) ? -u : u) + ((h&2) ? -v : v); } #else ccl_device_inline __m128 grad_sse(const __m128i& hash, const __m128& x, const __m128& y, const __m128& z) { - __m128i c1 = _mm_set1_epi32(1); - __m128i c2 = _mm_set1_epi32(2); + __m128i c1 = _mm_set1_epi32(1); + __m128i c2 = _mm_set1_epi32(2); - __m128i h = _mm_and_si128(hash, _mm_set1_epi32(15)); // h = hash & 15 + __m128i h = _mm_and_si128(hash, _mm_set1_epi32(15)); // h = hash & 15 - __m128i case_ux = _mm_cmplt_epi32(h, _mm_set1_epi32(8)); // 0xffffffff if h < 8 else 0 + __m128i case_ux = _mm_cmplt_epi32(h, _mm_set1_epi32(8)); // 0xffffffff if h < 8 else 0 - __m128 u = blend(_mm_castsi128_ps(case_ux), x, y); // u = h<8 ? x : y + __m128 u = blend(_mm_castsi128_ps(case_ux), x, y); // u = h<8 ? x : y - __m128i case_vy = _mm_cmplt_epi32(h, _mm_set1_epi32(4)); // 0xffffffff if h < 4 else 0 + __m128i case_vy = _mm_cmplt_epi32(h, _mm_set1_epi32(4)); // 0xffffffff if h < 4 else 0 - __m128i case_h12 = _mm_cmpeq_epi32(h, _mm_set1_epi32(12)); // 0xffffffff if h == 12 else 0 - __m128i case_h14 = _mm_cmpeq_epi32(h, _mm_set1_epi32(14)); // 0xffffffff if h == 14 else 0 + __m128i case_h12 = _mm_cmpeq_epi32(h, _mm_set1_epi32(12)); // 0xffffffff if h == 12 else 0 + __m128i case_h14 = _mm_cmpeq_epi32(h, _mm_set1_epi32(14)); // 0xffffffff if h == 14 else 0 - __m128i case_vx = _mm_or_si128(case_h12, case_h14); // 0xffffffff if h == 12 or h == 14 else 0 + __m128i case_vx = _mm_or_si128(case_h12, case_h14); // 0xffffffff if h == 12 or h == 14 else 0 - __m128 v = blend(_mm_castsi128_ps(case_vy), y, blend(_mm_castsi128_ps(case_vx), x, z)); // v = h<4 ? y : h == 12 || h == 14 ? x : z + __m128 v = blend(_mm_castsi128_ps(case_vy), y, blend(_mm_castsi128_ps(case_vx), x, z)); // v = h<4 ? y : h == 12 || h == 14 ? x : z - __m128i case_uneg = _mm_slli_epi32(_mm_and_si128(h, c1), 31); // 1<<31 if h&1 else 0 - __m128 case_uneg_mask = _mm_castsi128_ps(case_uneg); // -0.0 if h&1 else +0.0 - __m128 ru = _mm_xor_ps(u, case_uneg_mask); // -u if h&1 else u (copy float sign) + __m128i case_uneg = _mm_slli_epi32(_mm_and_si128(h, c1), 31); // 1<<31 if h&1 else 0 + __m128 case_uneg_mask = _mm_castsi128_ps(case_uneg); // -0.0 if h&1 else +0.0 + __m128 ru = _mm_xor_ps(u, case_uneg_mask); // -u if h&1 else u (copy float sign) - __m128i case_vneg = _mm_slli_epi32(_mm_and_si128(h, c2), 30); // 2<<30 if h&2 else 0 - __m128 case_vneg_mask = _mm_castsi128_ps(case_vneg); // -0.0 if h&2 else +0.0 - __m128 rv = _mm_xor_ps(v, case_vneg_mask); // -v if h&2 else v (copy float sign) + __m128i case_vneg = _mm_slli_epi32(_mm_and_si128(h, c2), 30); // 2<<30 if h&2 else 0 + __m128 case_vneg_mask = _mm_castsi128_ps(case_vneg); // -0.0 if h&2 else +0.0 + __m128 rv = _mm_xor_ps(v, case_vneg_mask); // -v if h&2 else v (copy float sign) - __m128 r = _mm_add_ps(ru, rv); // ((h&1) ? -u : u) + ((h&2) ? -v : v) - return r; + __m128 r = _mm_add_ps(ru, rv); // ((h&1) ? -u : u) + ((h&2) ? -v : v) + return r; } #endif #ifndef __KERNEL_SSE2__ ccl_device float scale3(float result) { - return 0.9820f * result; + return 0.9820f * result; } #else ccl_device_inline __m128 scale3_sse(const __m128& result) { - return _mm_mul_ps(_mm_set1_ps(0.9820f), result); + return _mm_mul_ps(_mm_set1_ps(0.9820f), result); } #endif #ifndef __KERNEL_SSE2__ ccl_device_noinline float perlin(float x, float y, float z) { - int X; float fx = floorfrac(x, &X); - int Y; float fy = floorfrac(y, &Y); - int Z; float fz = floorfrac(z, &Z); - - float u = fade(fx); - float v = fade(fy); - float w = fade(fz); - - float result; - - result = nerp (w, nerp (v, nerp (u, grad (hash (X , Y , Z ), fx , fy , fz ), - grad (hash (X+1, Y , Z ), fx-1.0f, fy , fz )), - nerp (u, grad (hash (X , Y+1, Z ), fx , fy-1.0f, fz ), - grad (hash (X+1, Y+1, Z ), fx-1.0f, fy-1.0f, fz ))), - nerp (v, nerp (u, grad (hash (X , Y , Z+1), fx , fy , fz-1.0f ), - grad (hash (X+1, Y , Z+1), fx-1.0f, fy , fz-1.0f )), - nerp (u, grad (hash (X , Y+1, Z+1), fx , fy-1.0f, fz-1.0f ), - grad (hash (X+1, Y+1, Z+1), fx-1.0f, fy-1.0f, fz-1.0f )))); - float r = scale3(result); - - /* can happen for big coordinates, things even out to 0.0 then anyway */ - return (isfinite(r))? r: 0.0f; + int X; float fx = floorfrac(x, &X); + int Y; float fy = floorfrac(y, &Y); + int Z; float fz = floorfrac(z, &Z); + + float u = fade(fx); + float v = fade(fy); + float w = fade(fz); + + float result; + + result = nerp (w, nerp (v, nerp (u, grad (hash (X , Y , Z ), fx , fy , fz ), + grad (hash (X+1, Y , Z ), fx-1.0f, fy , fz )), + nerp (u, grad (hash (X , Y+1, Z ), fx , fy-1.0f, fz ), + grad (hash (X+1, Y+1, Z ), fx-1.0f, fy-1.0f, fz ))), + nerp (v, nerp (u, grad (hash (X , Y , Z+1), fx , fy , fz-1.0f ), + grad (hash (X+1, Y , Z+1), fx-1.0f, fy , fz-1.0f )), + nerp (u, grad (hash (X , Y+1, Z+1), fx , fy-1.0f, fz-1.0f ), + grad (hash (X+1, Y+1, Z+1), fx-1.0f, fy-1.0f, fz-1.0f )))); + float r = scale3(result); + + /* can happen for big coordinates, things even out to 0.0 then anyway */ + return (isfinite(r))? r: 0.0f; } #else ccl_device_noinline float perlin(float x, float y, float z) { - __m128 xyz = _mm_setr_ps(x, y, z, 0.0f); - __m128i XYZ; + __m128 xyz = _mm_setr_ps(x, y, z, 0.0f); + __m128i XYZ; - __m128 fxyz = floorfrac_sse(xyz, &XYZ); + __m128 fxyz = floorfrac_sse(xyz, &XYZ); - __m128 uvw = fade_sse(&fxyz); - __m128 u = broadcast<0>(uvw), v = broadcast<1>(uvw), w = broadcast<2>(uvw); + __m128 uvw = fade_sse(&fxyz); + __m128 u = broadcast<0>(uvw), v = broadcast<1>(uvw), w = broadcast<2>(uvw); - __m128i XYZ_ofc = _mm_add_epi32(XYZ, _mm_set1_epi32(1)); - __m128i vdy = shuffle<1, 1, 1, 1>(XYZ, XYZ_ofc); // +0, +0, +1, +1 - __m128i vdz = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(XYZ, XYZ_ofc)); // +0, +1, +0, +1 + __m128i XYZ_ofc = _mm_add_epi32(XYZ, _mm_set1_epi32(1)); + __m128i vdy = shuffle<1, 1, 1, 1>(XYZ, XYZ_ofc); // +0, +0, +1, +1 + __m128i vdz = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(XYZ, XYZ_ofc)); // +0, +1, +0, +1 - __m128i h1 = hash_sse(broadcast<0>(XYZ), vdy, vdz); // hash directions 000, 001, 010, 011 - __m128i h2 = hash_sse(broadcast<0>(XYZ_ofc), vdy, vdz); // hash directions 100, 101, 110, 111 + __m128i h1 = hash_sse(broadcast<0>(XYZ), vdy, vdz); // hash directions 000, 001, 010, 011 + __m128i h2 = hash_sse(broadcast<0>(XYZ_ofc), vdy, vdz); // hash directions 100, 101, 110, 111 - __m128 fxyz_ofc = _mm_sub_ps(fxyz, _mm_set1_ps(1.0f)); - __m128 vfy = shuffle<1, 1, 1, 1>(fxyz, fxyz_ofc); - __m128 vfz = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(fxyz, fxyz_ofc)); + __m128 fxyz_ofc = _mm_sub_ps(fxyz, _mm_set1_ps(1.0f)); + __m128 vfy = shuffle<1, 1, 1, 1>(fxyz, fxyz_ofc); + __m128 vfz = shuffle<0, 2, 0, 2>(shuffle<2, 2, 2, 2>(fxyz, fxyz_ofc)); - __m128 g1 = grad_sse(h1, broadcast<0>(fxyz), vfy, vfz); - __m128 g2 = grad_sse(h2, broadcast<0>(fxyz_ofc), vfy, vfz); - __m128 n1 = nerp_sse(u, g1, g2); + __m128 g1 = grad_sse(h1, broadcast<0>(fxyz), vfy, vfz); + __m128 g2 = grad_sse(h2, broadcast<0>(fxyz_ofc), vfy, vfz); + __m128 n1 = nerp_sse(u, g1, g2); - __m128 n1_half = shuffle<2, 3, 2, 3>(n1); // extract 2 floats to a separate vector - __m128 n2 = nerp_sse(v, n1, n1_half); // process nerp([a, b, _, _], [c, d, _, _]) -> [a', b', _, _] + __m128 n1_half = shuffle<2, 3, 2, 3>(n1); // extract 2 floats to a separate vector + __m128 n2 = nerp_sse(v, n1, n1_half); // process nerp([a, b, _, _], [c, d, _, _]) -> [a', b', _, _] - __m128 n2_second = broadcast<1>(n2); // extract b to a separate vector - __m128 result = nerp_sse(w, n2, n2_second); // process nerp([a', _, _, _], [b', _, _, _]) -> [a'', _, _, _] + __m128 n2_second = broadcast<1>(n2); // extract b to a separate vector + __m128 result = nerp_sse(w, n2, n2_second); // process nerp([a', _, _, _], [b', _, _, _]) -> [a'', _, _, _] - __m128 r = scale3_sse(result); + __m128 r = scale3_sse(result); - __m128 infmask = _mm_castsi128_ps(_mm_set1_epi32(0x7f800000)); - __m128 rinfmask = _mm_cmpeq_ps(_mm_and_ps(r, infmask), infmask); // 0xffffffff if r is inf/-inf/nan else 0 - __m128 rfinite = _mm_andnot_ps(rinfmask, r); // 0 if r is inf/-inf/nan else r - return _mm_cvtss_f32(rfinite); + __m128 infmask = _mm_castsi128_ps(_mm_set1_epi32(0x7f800000)); + __m128 rinfmask = _mm_cmpeq_ps(_mm_and_ps(r, infmask), infmask); // 0xffffffff if r is inf/-inf/nan else 0 + __m128 rfinite = _mm_andnot_ps(rinfmask, r); // 0 if r is inf/-inf/nan else r + return _mm_cvtss_f32(rfinite); } #endif #if 0 // unused ccl_device_noinline float perlin_periodic(float x, float y, float z, float3 pperiod) { - int X; float fx = floorfrac(x, &X); - int Y; float fy = floorfrac(y, &Y); - int Z; float fz = floorfrac(z, &Z); - - int3 p; - - p.x = max(quick_floor(pperiod.x), 1); - p.y = max(quick_floor(pperiod.y), 1); - p.z = max(quick_floor(pperiod.z), 1); - - float u = fade(fx); - float v = fade(fy); - float w = fade(fz); - - float result; - - result = nerp (w, nerp (v, nerp (u, grad (phash (X , Y , Z , p), fx , fy , fz ), - grad (phash (X+1, Y , Z , p), fx-1.0f, fy , fz )), - nerp (u, grad (phash (X , Y+1, Z , p), fx , fy-1.0f, fz ), - grad (phash (X+1, Y+1, Z , p), fx-1.0f, fy-1.0f, fz ))), - nerp (v, nerp (u, grad (phash (X , Y , Z+1, p), fx , fy , fz-1.0f ), - grad (phash (X+1, Y , Z+1, p), fx-1.0f, fy , fz-1.0f )), - nerp (u, grad (phash (X , Y+1, Z+1, p), fx , fy-1.0f, fz-1.0f ), - grad (phash (X+1, Y+1, Z+1, p), fx-1.0f, fy-1.0f, fz-1.0f )))); - float r = scale3(result); - - /* can happen for big coordinates, things even out to 0.0 then anyway */ - return (isfinite(r))? r: 0.0f; + int X; float fx = floorfrac(x, &X); + int Y; float fy = floorfrac(y, &Y); + int Z; float fz = floorfrac(z, &Z); + + int3 p; + + p.x = max(quick_floor(pperiod.x), 1); + p.y = max(quick_floor(pperiod.y), 1); + p.z = max(quick_floor(pperiod.z), 1); + + float u = fade(fx); + float v = fade(fy); + float w = fade(fz); + + float result; + + result = nerp (w, nerp (v, nerp (u, grad (phash (X , Y , Z , p), fx , fy , fz ), + grad (phash (X+1, Y , Z , p), fx-1.0f, fy , fz )), + nerp (u, grad (phash (X , Y+1, Z , p), fx , fy-1.0f, fz ), + grad (phash (X+1, Y+1, Z , p), fx-1.0f, fy-1.0f, fz ))), + nerp (v, nerp (u, grad (phash (X , Y , Z+1, p), fx , fy , fz-1.0f ), + grad (phash (X+1, Y , Z+1, p), fx-1.0f, fy , fz-1.0f )), + nerp (u, grad (phash (X , Y+1, Z+1, p), fx , fy-1.0f, fz-1.0f ), + grad (phash (X+1, Y+1, Z+1, p), fx-1.0f, fy-1.0f, fz-1.0f )))); + float r = scale3(result); + + /* can happen for big coordinates, things even out to 0.0 then anyway */ + return (isfinite(r))? r: 0.0f; } #endif /* perlin noise in range 0..1 */ ccl_device float noise(float3 p) { - float r = perlin(p.x, p.y, p.z); - return 0.5f*r + 0.5f; + float r = perlin(p.x, p.y, p.z); + return 0.5f*r + 0.5f; } /* perlin noise in range -1..1 */ ccl_device float snoise(float3 p) { - return perlin(p.x, p.y, p.z); + return perlin(p.x, p.y, p.z); } /* cell noise */ #ifndef __KERNEL_SSE2__ ccl_device_noinline float cellnoise(float3 p) { - uint ix = quick_floor(p.x); - uint iy = quick_floor(p.y); - uint iz = quick_floor(p.z); + uint ix = quick_floor(p.x); + uint iy = quick_floor(p.y); + uint iz = quick_floor(p.z); - return bits_to_01(hash(ix, iy, iz)); + return bits_to_01(hash(ix, iy, iz)); } ccl_device float3 cellnoise_color(float3 p) { - float r = cellnoise(p); - float g = cellnoise(make_float3(p.y, p.x, p.z)); - float b = cellnoise(make_float3(p.y, p.z, p.x)); + float r = cellnoise(p); + float g = cellnoise(make_float3(p.y, p.x, p.z)); + float b = cellnoise(make_float3(p.y, p.z, p.x)); - return make_float3(r, g, b); + return make_float3(r, g, b); } #else ccl_device float3 cellnoise_color(const float3& p) { - __m128i v_yxz = quick_floor_sse(_mm_setr_ps(p.y, p.x, p.z, 0.0f)); - __m128i v_xyy = shuffle<1, 0, 0, 3>(v_yxz); - __m128i v_zzx = shuffle<2, 2, 1, 3>(v_yxz); - __m128 rgb = bits_to_01_sse(hash_sse(v_xyy, v_yxz, v_zzx)); + __m128i v_yxz = quick_floor_sse(_mm_setr_ps(p.y, p.x, p.z, 0.0f)); + __m128i v_xyy = shuffle<1, 0, 0, 3>(v_yxz); + __m128i v_zzx = shuffle<2, 2, 1, 3>(v_yxz); + __m128 rgb = bits_to_01_sse(hash_sse(v_xyy, v_yxz, v_zzx)); - float3 result = *(float3*)&rgb; - return result; + float3 result = *(float3*)&rgb; + return result; } #endif @@ -373,16 +374,186 @@ ccl_device float3 cellnoise_color(const float3& p) /* periodic perlin noise in range 0..1 */ ccl_device float pnoise(float3 p, float3 pperiod) { - float r = perlin_periodic(p.x, p.y, p.z, pperiod); - return 0.5f*r + 0.5f; + float r = perlin_periodic(p.x, p.y, p.z, pperiod); + return 0.5f*r + 0.5f; } /* periodic perlin noise in range -1..1 */ ccl_device float psnoise(float3 p, float3 pperiod) { - return perlin_periodic(p.x, p.y, p.z, pperiod); + return perlin_periodic(p.x, p.y, p.z, pperiod); } #endif +/* GABOR NOISE + * + * For further details of Gabor Noise Method please see + * http://www.gabornoise.com/ + * + */ + +ccl_device uint cellhash(int cell[3], int seed) +{ + // define macros +#define rot(x,k) (((x)<<(k) | ((x)>>(32-(k))))) +#define final(a,b,c) +#define mix(a,b,c) \ + { \ + a -= c; a ^= rot(c, 4); c += b; \ + b -= a; b ^= rot(a, 6); a += c; \ + c -= b; c ^= rot(b, 8); b += a; \ + a -= c; a ^= rot(c,16); c += b; \ + b -= a; b ^= rot(a,19); a += c; \ + c -= b; c ^= rot(b, 4); b += a; \ +} + + // hash data + int a, b, c; + a = b = c = 3735928588; + + // Put a,b,c in to cell array + a += cell[0]; + b += cell[1]; + c += cell[2]; + mix(a,b,c); + a+=seed; + final(a,b,c); + + return c; + +#undef rot +#undef mix +#undef final +} + +ccl_device void rng_seed(int &rng, int cell[3], int seed) +{ + int chash = cellhash(cell, seed); + if(chash == 0) + chash = 1; + rng = chash * 3039177861; +} + +ccl_device void rng_next(int &rng) +{ + rng *= 3039177861; +} + +ccl_device float rng_uniform(int &rng) +{ + static const float int_max_inv = 1.0f / (float)(0x7FFFFFFF); + float res = (float)rng * int_max_inv * 0.5f + 0.5f; + rng_next(rng); + return res; +} + +ccl_device int rng_poisson(int &rng, float mean) +{ + float g = exp(-mean); + int em = 0; + float t = rng_uniform(rng); + while (t > g) + { + ++em; + t *=rng_uniform(rng); + } + return em; +} + +struct GaborParams { + float weight; + float impulses; + float radius, radius2, radius3, radius_inv; + float a; + float bandwidth; + float lambda, sqrt_lambda_inv; +}; + + +ccl_device void setup_gabor_parameters(GaborParams &gp, float impulses) +{ + /* Only Impulses is user controllable, we might expose more later */ + float sqrt_pi_over_ln2 = sqrtf(M_PI_F / M_LN2); + float frequency = 2.0; + float truncate = 0.02f; + float bandwidth_pow = 2.0f; /* powf(2.0f, gp.bandwidth) */ + + gp.impulses = impulses; + gp.weight = 1.0f; + gp.bandwidth = 1.0; + gp.a = frequency * ((bandwidth_pow - 1.0f) / (bandwidth_pow + 1.0f)) * sqrt_pi_over_ln2; + + gp.radius = sqrtf(-log(truncate) / M_PI_F) / gp.a; + gp.radius_inv = 1.0f / gp.radius; + gp.radius2 = gp.radius * gp.radius; + gp.radius3 = gp.radius2 * gp.radius; + + gp.lambda = impulses / ((1.33333f * M_PI_F) * gp.radius3); + gp.sqrt_lambda_inv = 1.0f / sqrtf(gp.lambda); +} + +ccl_device float gabor_kernel(float weight, float3 omega, float phi, float bandwidth, float3 vector) +{ + float g = exp(-M_PI_F * (bandwidth * bandwidth) * dot(vector, vector)); + float h = cos(M_2PI_F * dot(omega, vector) + phi); + return weight * g * h; +} + + +ccl_device void gabor_sample(GaborParams &gp, int &rng, float3 &omega, float &phi) +{ + float cos_omega_p = rng_uniform(rng) * 2.0f - 1.0f; + float sin_omega_p = sqrtf(1.0f - cos_omega_p*cos_omega_p); + float omega_t = M_2PI_F * rng_uniform(rng); + float sin_omega_t = sin(omega_t); + float cos_omega_t = cos(omega_t); + omega = normalize(make_float3(cos_omega_t*sin_omega_p, sin_omega_t*sin_omega_p, cos_omega_p)); + phi = M_2PI_F * rng_uniform(rng); +} + + +ccl_device float gabor_cell(GaborParams gp, int c_i[3], float3 x_c_i, int seed) +{ + int rng; + rng_seed(rng, c_i, seed); + int num_impulses = rng_poisson(rng, gp.impulses); + float sum = 0.0f; + for (int i = 0; i < num_impulses; ++i) { + float xrand = rng_uniform(rng); + float yrand = rng_uniform(rng); + float zrand = rng_uniform(rng); + float3 x_k_i = (x_c_i - make_float3(xrand, yrand, zrand)) * gp.radius; + float3 omega; + float phi; + gabor_sample(gp, rng, omega, phi); + + if(dot(x_k_i, x_k_i) < gp.radius2) { + sum += gabor_kernel(gp.weight, omega, phi, gp.a, x_k_i); + } + } + return sum; +} + + +ccl_device float gabor_grid(GaborParams gp, float3 x_g, int seed) +{ + float x_g_0 = x_g.x - floorf(x_g.x); + float x_g_1 = x_g.y - floorf(x_g.y); + float x_g_2 = x_g.z - floorf(x_g.z); + float3 x_c = make_float3(x_g_0, x_g_1, x_g_2); + int cell[3] = { quick_floor(x_g.x), quick_floor(x_g.y), quick_floor(x_g.z) }; + float sum = 0.0f; + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + int c_i[3] = { cell[0] + i, cell[1] + j, cell[2] + k }; + float3 x_c_i = x_c - make_float3(i, j, k); + sum += gabor_cell(gp, c_i, x_c_i, seed); + } + } + } + return sum * gp.sqrt_lambda_inv; +} + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/svm_types.h index ad5e1ea..5b0ed9f 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/svm_types.h @@ -59,6 +59,7 @@ typedef enum NodeType { NODE_TEX_WAVE, NODE_TEX_MAGIC, NODE_TEX_NOISE, + NODE_TEX_GABOR, NODE_SHADER_JUMP, NODE_SET_DISPLACEMENT, NODE_GEOMETRY_BUMP_DX, diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index af6fca2..bb86ff6 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -783,6 +783,66 @@ void NoiseTextureNode::compile(OSLCompiler& compiler) compiler.add(this, "node_noise_texture"); } +/* Gabor Texture */ + +GaborTextureNode::GaborTextureNode() +: TextureNode("gabor_texture") +{ + add_input("Vector", SHADER_SOCKET_POINT, ShaderInput::TEXTURE_GENERATED); + add_input("Scale", SHADER_SOCKET_FLOAT, 1.0f); + add_input("Seed", SHADER_SOCKET_FLOAT, 2.0f); + add_input("Impulses", SHADER_SOCKET_FLOAT, 0.0f); + + add_output("Color", SHADER_SOCKET_COLOR); + add_output("Fac", SHADER_SOCKET_FLOAT); +} + +void GaborTextureNode::compile(SVMCompiler& compiler) +{ + ShaderInput *impulses_in = input("Impulses"); + ShaderInput *seed_in = input("Seed"); + ShaderInput *scale_in = input("Scale"); + ShaderInput *vector_in = input("Vector"); + ShaderOutput *color_out = output("Color"); + ShaderOutput *fac_out = output("Fac"); + + if(vector_in->link) compiler.stack_assign(vector_in); + if(scale_in->link) compiler.stack_assign(scale_in); + if(seed_in->link) compiler.stack_assign(seed_in); + if(impulses_in->link) compiler.stack_assign(impulses_in); + + int vector_offset = vector_in->stack_offset; + + if(!tex_mapping.skip()) { + vector_offset = compiler.stack_find_offset(SHADER_SOCKET_VECTOR); + tex_mapping.compile(compiler, vector_in->stack_offset, vector_offset); + } + + if(!fac_out->links.empty()) + compiler.stack_assign(fac_out); + if(!color_out->links.empty()) + compiler.stack_assign(color_out); + + compiler.add_node(NODE_TEX_GABOR, + compiler.encode_uchar4(vector_offset, scale_in->stack_offset, seed_in->stack_offset, impulses_in->stack_offset), + compiler.encode_uchar4(color_out->stack_offset, fac_out->stack_offset)); + compiler.add_node( + __float_as_int(scale_in->value.x), + __float_as_int(seed_in->value.x), + __float_as_int(impulses_in->value.x)); + + if(vector_offset != vector_in->stack_offset) + compiler.stack_clear_offset(vector_in->type, vector_offset); +} + +void GaborTextureNode::compile(OSLCompiler& compiler) +{ + tex_mapping.compile(compiler); + + compiler.add(this, "node_gabor_texture"); +} + + /* Voronoi Texture */ static ShaderEnum voronoi_coloring_init() diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index 86c4f49..9cdeea6 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -134,6 +134,11 @@ public: SHADER_NODE_CLASS(NoiseTextureNode) }; +class GaborTextureNode : public TextureNode { +public: + SHADER_NODE_CLASS(GaborTextureNode) +}; + class VoronoiTextureNode : public TextureNode { public: SHADER_NODE_CLASS(VoronoiTextureNode) diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index 6ade491..5ce8bea 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -201,6 +201,7 @@ shader_node_categories = [ NodeItem("ShaderNodeTexEnvironment"), NodeItem("ShaderNodeTexSky"), NodeItem("ShaderNodeTexNoise"), + NodeItem("ShaderNodeTexGabor"), NodeItem("ShaderNodeTexWave"), NodeItem("ShaderNodeTexVoronoi"), NodeItem("ShaderNodeTexMusgrave"), diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 59ea921..b026b0e 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -744,6 +744,7 @@ struct ShadeResult; #define SH_NODE_COMBHSV 184 #define SH_NODE_BSDF_HAIR 185 #define SH_NODE_LAMP 186 +#define SH_NODE_TEX_GABOR 187 /* custom defines options for Material node */ #define SH_NODE_MAT_DIFF 1 diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index d622556..e1934aa 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -3542,6 +3542,7 @@ static void registerShaderNodes(void) register_node_type_sh_tex_magic(); register_node_type_sh_tex_checker(); register_node_type_sh_tex_brick(); + register_node_type_sh_tex_gabor(); } static void registerTextureNodes(void) diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index 19ef6d0..fadb94f 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -2334,6 +2334,12 @@ void node_tex_noise(vec3 co, float scale, float detail, float distortion, out ve fac = 1.0; } +void node_tex_gabor(vec3 co, float scale, float seed, float impulses, out vec4 color, out float fac) +{ + color = vec4(1.0); + fac = 1.0; +} + void node_tex_sky(vec3 co, out vec4 color) { color = vec4(1.0); diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index cd9595d..647eecf 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -754,6 +754,10 @@ typedef struct NodeTexNoise { NodeTexBase base; } NodeTexNoise; +typedef struct NodeTexGabor { + NodeTexBase base; +} NodeTexGabor; + typedef struct NodeTexVoronoi { NodeTexBase base; int coloring; diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index d893a8c..4f47f44 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -3494,6 +3494,12 @@ static void def_sh_tex_noise(StructRNA *srna) def_sh_tex(srna); } +static void def_sh_tex_gabor(StructRNA *srna) +{ + RNA_def_struct_sdna_from(srna, "NodeTexGabor", "storage"); + def_sh_tex(srna); +} + static void def_sh_tex_checker(StructRNA *srna) { RNA_def_struct_sdna_from(srna, "NodeTexChecker", "storage"); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 16671e8..9c04d7e 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -189,6 +189,7 @@ set(SRC shader/nodes/node_shader_tex_checker.c shader/nodes/node_shader_tex_coord.c shader/nodes/node_shader_tex_environment.c + shader/nodes/node_shader_tex_gabor.c shader/nodes/node_shader_tex_gradient.c shader/nodes/node_shader_tex_image.c shader/nodes/node_shader_tex_magic.c diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h index 0f388a8..55ef0d2 100644 --- a/source/blender/nodes/NOD_shader.h +++ b/source/blender/nodes/NOD_shader.h @@ -127,6 +127,7 @@ void register_node_type_sh_tex_musgrave(void); void register_node_type_sh_tex_noise(void); void register_node_type_sh_tex_checker(void); void register_node_type_sh_bump(void); +void register_node_type_sh_tex_gabor(void); #endif diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index f7f7118..33f0c56 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -107,6 +107,7 @@ DefNode( ShaderNode, SH_NODE_SCRIPT, def_sh_script, "SC DefNode( ShaderNode, SH_NODE_TEX_IMAGE, def_sh_tex_image, "TEX_IMAGE", TexImage, "Image Texture", "" ) DefNode( ShaderNode, SH_NODE_TEX_ENVIRONMENT, def_sh_tex_environment, "TEX_ENVIRONMENT", TexEnvironment, "Environment Texture","" ) DefNode( ShaderNode, SH_NODE_TEX_SKY, def_sh_tex_sky, "TEX_SKY", TexSky, "Sky Texture", "" ) +DefNode( ShaderNode, SH_NODE_TEX_GABOR, def_sh_tex_gabor, "TEX_GABOR", TexGabor, "Gabor Texture", "" ) DefNode( ShaderNode, SH_NODE_TEX_GRADIENT, def_sh_tex_gradient, "TEX_GRADIENT", TexGradient, "Gradient Texture", "" ) DefNode( ShaderNode, SH_NODE_TEX_NOISE, def_sh_tex_noise, "TEX_NOISE", TexNoise, "Noise Texture", "" ) DefNode( ShaderNode, SH_NODE_TEX_MAGIC, def_sh_tex_magic, "TEX_MAGIC", TexMagic, "Magic Texture", "" ) diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_gabor.c b/source/blender/nodes/shader/nodes/node_shader_tex_gabor.c new file mode 100644 index 0000000..5d59c23 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_tex_gabor.c @@ -0,0 +1,79 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include "../node_shader_util.h" + +/* **************** Gabor ******************** */ + +static bNodeSocketTemplate sh_node_tex_gabor_in[] = { + { SOCK_VECTOR, 1, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, + { SOCK_FLOAT, 1, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, + { SOCK_FLOAT, 1, N_("Seed"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f}, + { SOCK_FLOAT, 1, N_("Impulses"), 1.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, + { -1, 0, "" } +}; + +static bNodeSocketTemplate sh_node_tex_gabor_out[] = { + { SOCK_RGBA, 0, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, + { SOCK_FLOAT, 0, N_("Fac"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, + { -1, 0, "" } +}; + +static void node_shader_init_tex_gabor(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeTexGabor *tex = MEM_callocN(sizeof(NodeTexGabor), "NodeTexGabor"); + default_tex_mapping(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); + default_color_mapping(&tex->base.color_mapping); + + node->storage = tex; +} + +static int node_shader_gpu_tex_gabor(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), GPUNodeStack *in, + GPUNodeStack *out) +{ + if (!in[0].link) + in[0].link = GPU_attribute(CD_ORCO, ""); + + node_shader_gpu_tex_mapping(mat, node, in, out); + + return GPU_stack_link(mat, "node_tex_gabor", in, out); +} + +/* node type definition */ +void register_node_type_sh_tex_gabor(void) +{ + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_TEX_GABOR, "Gabor Texture", NODE_CLASS_TEXTURE, 0); + node_type_compatibility(&ntype, NODE_NEW_SHADING); + node_type_socket_templates(&ntype, sh_node_tex_gabor_in, sh_node_tex_gabor_out); + node_type_init(&ntype, node_shader_init_tex_gabor); + node_type_storage(&ntype, "NodeTexGabor", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, node_shader_gpu_tex_gabor); + + nodeRegisterType(&ntype); +}