This only frees brush_rng and random_tex_array when they were actually previously allocated. In a unit test (see D6246) I want to be able to partially start Blender so that I can load a blend file. To prevent memory leaks, I also want to be able to release memory, which currently requires calling `BKE_blender_free()`. This unconditionally calls `RE_texture_rng_exit()` and `BKE_brush_system_exit()`, which now crash on freeing `NULL`. This patch fixes that. Allocation (`BKE_brush_system_init()`) and freeing (`BKE_brush_system_exit()`) are done asymmetrically. The allocation functions are called from `main()` in the creator module, but the freeing is done by `BKE_blender_free()` the Window Manager. Ideally we symmetrise this and initialise Blender from outside the window manager (so that the initialisation can be done without WM and Python too), but for now I'm happy when things don't crash. Reviewed by: sergey via pair programming
1808 lines
52 KiB
C
1808 lines
52 KiB
C
/*
|
|
* 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) 2001-2002 by NaN Holding BV.
|
|
* All rights reserved.
|
|
*/
|
|
|
|
/** \file
|
|
* \ingroup render
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#include "BLI_math.h"
|
|
#include "BLI_noise.h"
|
|
#include "BLI_rand.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "DNA_anim_types.h"
|
|
#include "DNA_texture_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_light_types.h"
|
|
#include "DNA_meshdata_types.h"
|
|
#include "DNA_material_types.h"
|
|
#include "DNA_image_types.h"
|
|
#include "DNA_node_types.h"
|
|
|
|
#include "IMB_imbuf_types.h"
|
|
#include "IMB_colormanagement.h"
|
|
|
|
#include "BKE_image.h"
|
|
#include "BKE_node.h"
|
|
|
|
#include "BKE_animsys.h"
|
|
#include "BKE_colorband.h"
|
|
#include "BKE_material.h"
|
|
#include "BKE_scene.h"
|
|
|
|
#include "BKE_texture.h"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "render_types.h"
|
|
#include "texture.h"
|
|
|
|
#include "RE_render_ext.h"
|
|
#include "RE_shader_ext.h"
|
|
|
|
static RNG_THREAD_ARRAY *random_tex_array;
|
|
|
|
void RE_texture_rng_init(void)
|
|
{
|
|
random_tex_array = BLI_rng_threaded_new();
|
|
}
|
|
|
|
void RE_texture_rng_exit(void)
|
|
{
|
|
if (random_tex_array == NULL) {
|
|
return;
|
|
}
|
|
BLI_rng_threaded_free(random_tex_array);
|
|
random_tex_array = NULL;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* this allows colorbanded textures to control normals as well */
|
|
static void tex_normal_derivate(Tex *tex, TexResult *texres)
|
|
{
|
|
if (tex->flag & TEX_COLORBAND) {
|
|
float col[4];
|
|
if (BKE_colorband_evaluate(tex->coba, texres->tin, col)) {
|
|
float fac0, fac1, fac2, fac3;
|
|
|
|
fac0 = (col[0] + col[1] + col[2]);
|
|
BKE_colorband_evaluate(tex->coba, texres->nor[0], col);
|
|
fac1 = (col[0] + col[1] + col[2]);
|
|
BKE_colorband_evaluate(tex->coba, texres->nor[1], col);
|
|
fac2 = (col[0] + col[1] + col[2]);
|
|
BKE_colorband_evaluate(tex->coba, texres->nor[2], col);
|
|
fac3 = (col[0] + col[1] + col[2]);
|
|
|
|
texres->nor[0] = (fac0 - fac1) / 3.0f;
|
|
texres->nor[1] = (fac0 - fac2) / 3.0f;
|
|
texres->nor[2] = (fac0 - fac3) / 3.0f;
|
|
|
|
return;
|
|
}
|
|
}
|
|
texres->nor[0] = texres->tin - texres->nor[0];
|
|
texres->nor[1] = texres->tin - texres->nor[1];
|
|
texres->nor[2] = texres->tin - texres->nor[2];
|
|
}
|
|
|
|
static int blend(Tex *tex, const float texvec[3], TexResult *texres)
|
|
{
|
|
float x, y, t;
|
|
|
|
if (tex->flag & TEX_FLIPBLEND) {
|
|
x = texvec[1];
|
|
y = texvec[0];
|
|
}
|
|
else {
|
|
x = texvec[0];
|
|
y = texvec[1];
|
|
}
|
|
|
|
if (tex->stype == TEX_LIN) { /* lin */
|
|
texres->tin = (1.0f + x) / 2.0f;
|
|
}
|
|
else if (tex->stype == TEX_QUAD) { /* quad */
|
|
texres->tin = (1.0f + x) / 2.0f;
|
|
if (texres->tin < 0.0f) {
|
|
texres->tin = 0.0f;
|
|
}
|
|
else {
|
|
texres->tin *= texres->tin;
|
|
}
|
|
}
|
|
else if (tex->stype == TEX_EASE) { /* ease */
|
|
texres->tin = (1.0f + x) / 2.0f;
|
|
if (texres->tin <= 0.0f) {
|
|
texres->tin = 0.0f;
|
|
}
|
|
else if (texres->tin >= 1.0f) {
|
|
texres->tin = 1.0f;
|
|
}
|
|
else {
|
|
t = texres->tin * texres->tin;
|
|
texres->tin = (3.0f * t - 2.0f * t * texres->tin);
|
|
}
|
|
}
|
|
else if (tex->stype == TEX_DIAG) { /* diag */
|
|
texres->tin = (2.0f + x + y) / 4.0f;
|
|
}
|
|
else if (tex->stype == TEX_RAD) { /* radial */
|
|
texres->tin = (atan2f(y, x) / (float)(2 * M_PI) + 0.5f);
|
|
}
|
|
else { /* sphere TEX_SPHERE */
|
|
texres->tin = 1.0f - sqrtf(x * x + y * y + texvec[2] * texvec[2]);
|
|
if (texres->tin < 0.0f) {
|
|
texres->tin = 0.0f;
|
|
}
|
|
if (tex->stype == TEX_HALO) {
|
|
texres->tin *= texres->tin; /* halo */
|
|
}
|
|
}
|
|
|
|
BRICONT;
|
|
|
|
return TEX_INT;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* ************************************************************************* */
|
|
|
|
/* newnoise: all noisebased types now have different noisebases to choose from */
|
|
|
|
static int clouds(Tex *tex, const float texvec[3], TexResult *texres)
|
|
{
|
|
int rv = TEX_INT;
|
|
|
|
texres->tin = BLI_gTurbulence(tex->noisesize,
|
|
texvec[0],
|
|
texvec[1],
|
|
texvec[2],
|
|
tex->noisedepth,
|
|
(tex->noisetype != TEX_NOISESOFT),
|
|
tex->noisebasis);
|
|
|
|
if (texres->nor != NULL) {
|
|
/* calculate bumpnormal */
|
|
texres->nor[0] = BLI_gTurbulence(tex->noisesize,
|
|
texvec[0] + tex->nabla,
|
|
texvec[1],
|
|
texvec[2],
|
|
tex->noisedepth,
|
|
(tex->noisetype != TEX_NOISESOFT),
|
|
tex->noisebasis);
|
|
texres->nor[1] = BLI_gTurbulence(tex->noisesize,
|
|
texvec[0],
|
|
texvec[1] + tex->nabla,
|
|
texvec[2],
|
|
tex->noisedepth,
|
|
(tex->noisetype != TEX_NOISESOFT),
|
|
tex->noisebasis);
|
|
texres->nor[2] = BLI_gTurbulence(tex->noisesize,
|
|
texvec[0],
|
|
texvec[1],
|
|
texvec[2] + tex->nabla,
|
|
tex->noisedepth,
|
|
(tex->noisetype != TEX_NOISESOFT),
|
|
tex->noisebasis);
|
|
|
|
tex_normal_derivate(tex, texres);
|
|
rv |= TEX_NOR;
|
|
}
|
|
|
|
if (tex->stype == TEX_COLOR) {
|
|
/* in this case, int. value should really be computed from color,
|
|
* and bumpnormal from that, would be too slow, looks ok as is */
|
|
texres->tr = texres->tin;
|
|
texres->tg = BLI_gTurbulence(tex->noisesize,
|
|
texvec[1],
|
|
texvec[0],
|
|
texvec[2],
|
|
tex->noisedepth,
|
|
(tex->noisetype != TEX_NOISESOFT),
|
|
tex->noisebasis);
|
|
texres->tb = BLI_gTurbulence(tex->noisesize,
|
|
texvec[1],
|
|
texvec[2],
|
|
texvec[0],
|
|
tex->noisedepth,
|
|
(tex->noisetype != TEX_NOISESOFT),
|
|
tex->noisebasis);
|
|
BRICONTRGB;
|
|
texres->ta = 1.0;
|
|
return (rv | TEX_RGB);
|
|
}
|
|
|
|
BRICONT;
|
|
|
|
return rv;
|
|
}
|
|
|
|
/* creates a sine wave */
|
|
static float tex_sin(float a)
|
|
{
|
|
a = 0.5f + 0.5f * sinf(a);
|
|
|
|
return a;
|
|
}
|
|
|
|
/* creates a saw wave */
|
|
static float tex_saw(float a)
|
|
{
|
|
const float b = 2 * M_PI;
|
|
|
|
int n = (int)(a / b);
|
|
a -= n * b;
|
|
if (a < 0) {
|
|
a += b;
|
|
}
|
|
return a / b;
|
|
}
|
|
|
|
/* creates a triangle wave */
|
|
static float tex_tri(float a)
|
|
{
|
|
const float b = 2 * M_PI;
|
|
const float rmax = 1.0;
|
|
|
|
a = rmax - 2.0f * fabsf(floorf((a * (1.0f / b)) + 0.5f) - (a * (1.0f / b)));
|
|
|
|
return a;
|
|
}
|
|
|
|
/* computes basic wood intensity value at x,y,z */
|
|
static float wood_int(Tex *tex, float x, float y, float z)
|
|
{
|
|
float wi = 0;
|
|
/* wave form: TEX_SIN=0, TEX_SAW=1, TEX_TRI=2 */
|
|
short wf = tex->noisebasis2;
|
|
/* wood type: TEX_BAND=0, TEX_RING=1, TEX_BANDNOISE=2, TEX_RINGNOISE=3 */
|
|
short wt = tex->stype;
|
|
|
|
float (*waveform[3])(float); /* create array of pointers to waveform functions */
|
|
waveform[0] = tex_sin; /* assign address of tex_sin() function to pointer array */
|
|
waveform[1] = tex_saw;
|
|
waveform[2] = tex_tri;
|
|
|
|
if ((wf > TEX_TRI) || (wf < TEX_SIN)) {
|
|
wf = 0; /* check to be sure noisebasis2 is initialized ahead of time */
|
|
}
|
|
|
|
if (wt == TEX_BAND) {
|
|
wi = waveform[wf]((x + y + z) * 10.0f);
|
|
}
|
|
else if (wt == TEX_RING) {
|
|
wi = waveform[wf](sqrtf(x * x + y * y + z * z) * 20.0f);
|
|
}
|
|
else if (wt == TEX_BANDNOISE) {
|
|
wi = tex->turbul *
|
|
BLI_gNoise(tex->noisesize, x, y, z, (tex->noisetype != TEX_NOISESOFT), tex->noisebasis);
|
|
wi = waveform[wf]((x + y + z) * 10.0f + wi);
|
|
}
|
|
else if (wt == TEX_RINGNOISE) {
|
|
wi = tex->turbul *
|
|
BLI_gNoise(tex->noisesize, x, y, z, (tex->noisetype != TEX_NOISESOFT), tex->noisebasis);
|
|
wi = waveform[wf](sqrtf(x * x + y * y + z * z) * 20.0f + wi);
|
|
}
|
|
|
|
return wi;
|
|
}
|
|
|
|
static int wood(Tex *tex, const float texvec[3], TexResult *texres)
|
|
{
|
|
int rv = TEX_INT;
|
|
|
|
texres->tin = wood_int(tex, texvec[0], texvec[1], texvec[2]);
|
|
if (texres->nor != NULL) {
|
|
/* calculate bumpnormal */
|
|
texres->nor[0] = wood_int(tex, texvec[0] + tex->nabla, texvec[1], texvec[2]);
|
|
texres->nor[1] = wood_int(tex, texvec[0], texvec[1] + tex->nabla, texvec[2]);
|
|
texres->nor[2] = wood_int(tex, texvec[0], texvec[1], texvec[2] + tex->nabla);
|
|
|
|
tex_normal_derivate(tex, texres);
|
|
rv |= TEX_NOR;
|
|
}
|
|
|
|
BRICONT;
|
|
|
|
return rv;
|
|
}
|
|
|
|
/* computes basic marble intensity at x,y,z */
|
|
static float marble_int(Tex *tex, float x, float y, float z)
|
|
{
|
|
float n, mi;
|
|
short wf = tex->noisebasis2; /* wave form: TEX_SIN=0, TEX_SAW=1, TEX_TRI=2 */
|
|
short mt = tex->stype; /* marble type: TEX_SOFT=0, TEX_SHARP=1, TEX_SHAPER=2 */
|
|
|
|
float (*waveform[3])(float); /* create array of pointers to waveform functions */
|
|
waveform[0] = tex_sin; /* assign address of tex_sin() function to pointer array */
|
|
waveform[1] = tex_saw;
|
|
waveform[2] = tex_tri;
|
|
|
|
if ((wf > TEX_TRI) || (wf < TEX_SIN)) {
|
|
wf = 0; /* check to be sure noisebasis2 isn't initialized ahead of time */
|
|
}
|
|
|
|
n = 5.0f * (x + y + z);
|
|
|
|
mi = n + tex->turbul * BLI_gTurbulence(tex->noisesize,
|
|
x,
|
|
y,
|
|
z,
|
|
tex->noisedepth,
|
|
(tex->noisetype != TEX_NOISESOFT),
|
|
tex->noisebasis);
|
|
|
|
if (mt >= TEX_SOFT) { /* TEX_SOFT always true */
|
|
mi = waveform[wf](mi);
|
|
if (mt == TEX_SHARP) {
|
|
mi = sqrtf(mi);
|
|
}
|
|
else if (mt == TEX_SHARPER) {
|
|
mi = sqrtf(sqrtf(mi));
|
|
}
|
|
}
|
|
|
|
return mi;
|
|
}
|
|
|
|
static int marble(Tex *tex, const float texvec[3], TexResult *texres)
|
|
{
|
|
int rv = TEX_INT;
|
|
|
|
texres->tin = marble_int(tex, texvec[0], texvec[1], texvec[2]);
|
|
|
|
if (texres->nor != NULL) {
|
|
/* calculate bumpnormal */
|
|
texres->nor[0] = marble_int(tex, texvec[0] + tex->nabla, texvec[1], texvec[2]);
|
|
texres->nor[1] = marble_int(tex, texvec[0], texvec[1] + tex->nabla, texvec[2]);
|
|
texres->nor[2] = marble_int(tex, texvec[0], texvec[1], texvec[2] + tex->nabla);
|
|
|
|
tex_normal_derivate(tex, texres);
|
|
|
|
rv |= TEX_NOR;
|
|
}
|
|
|
|
BRICONT;
|
|
|
|
return rv;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
static int magic(Tex *tex, const float texvec[3], TexResult *texres)
|
|
{
|
|
float x, y, z, turb;
|
|
int n;
|
|
|
|
n = tex->noisedepth;
|
|
turb = tex->turbul / 5.0f;
|
|
|
|
x = sinf((texvec[0] + texvec[1] + texvec[2]) * 5.0f);
|
|
y = cosf((-texvec[0] + texvec[1] - texvec[2]) * 5.0f);
|
|
z = -cosf((-texvec[0] - texvec[1] + texvec[2]) * 5.0f);
|
|
if (n > 0) {
|
|
x *= turb;
|
|
y *= turb;
|
|
z *= turb;
|
|
y = -cosf(x - y + z);
|
|
y *= turb;
|
|
if (n > 1) {
|
|
x = cosf(x - y - z);
|
|
x *= turb;
|
|
if (n > 2) {
|
|
z = sinf(-x - y - z);
|
|
z *= turb;
|
|
if (n > 3) {
|
|
x = -cosf(-x + y - z);
|
|
x *= turb;
|
|
if (n > 4) {
|
|
y = -sinf(-x + y + z);
|
|
y *= turb;
|
|
if (n > 5) {
|
|
y = -cosf(-x + y + z);
|
|
y *= turb;
|
|
if (n > 6) {
|
|
x = cosf(x + y + z);
|
|
x *= turb;
|
|
if (n > 7) {
|
|
z = sinf(x + y - z);
|
|
z *= turb;
|
|
if (n > 8) {
|
|
x = -cosf(-x - y + z);
|
|
x *= turb;
|
|
if (n > 9) {
|
|
y = -sinf(x - y + z);
|
|
y *= turb;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (turb != 0.0f) {
|
|
turb *= 2.0f;
|
|
x /= turb;
|
|
y /= turb;
|
|
z /= turb;
|
|
}
|
|
texres->tr = 0.5f - x;
|
|
texres->tg = 0.5f - y;
|
|
texres->tb = 0.5f - z;
|
|
|
|
texres->tin = (1.0f / 3.0f) * (texres->tr + texres->tg + texres->tb);
|
|
|
|
BRICONTRGB;
|
|
texres->ta = 1.0f;
|
|
|
|
return TEX_RGB;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* newnoise: stucci also modified to use different noisebasis */
|
|
static int stucci(Tex *tex, const float texvec[3], TexResult *texres)
|
|
{
|
|
float nor[3], b2, ofs;
|
|
int retval = TEX_INT;
|
|
|
|
b2 = BLI_gNoise(tex->noisesize,
|
|
texvec[0],
|
|
texvec[1],
|
|
texvec[2],
|
|
(tex->noisetype != TEX_NOISESOFT),
|
|
tex->noisebasis);
|
|
|
|
ofs = tex->turbul / 200.0f;
|
|
|
|
if (tex->stype) {
|
|
ofs *= (b2 * b2);
|
|
}
|
|
nor[0] = BLI_gNoise(tex->noisesize,
|
|
texvec[0] + ofs,
|
|
texvec[1],
|
|
texvec[2],
|
|
(tex->noisetype != TEX_NOISESOFT),
|
|
tex->noisebasis);
|
|
nor[1] = BLI_gNoise(tex->noisesize,
|
|
texvec[0],
|
|
texvec[1] + ofs,
|
|
texvec[2],
|
|
(tex->noisetype != TEX_NOISESOFT),
|
|
tex->noisebasis);
|
|
nor[2] = BLI_gNoise(tex->noisesize,
|
|
texvec[0],
|
|
texvec[1],
|
|
texvec[2] + ofs,
|
|
(tex->noisetype != TEX_NOISESOFT),
|
|
tex->noisebasis);
|
|
|
|
texres->tin = nor[2];
|
|
|
|
if (texres->nor) {
|
|
|
|
copy_v3_v3(texres->nor, nor);
|
|
tex_normal_derivate(tex, texres);
|
|
|
|
if (tex->stype == TEX_WALLOUT) {
|
|
texres->nor[0] = -texres->nor[0];
|
|
texres->nor[1] = -texres->nor[1];
|
|
texres->nor[2] = -texres->nor[2];
|
|
}
|
|
|
|
retval |= TEX_NOR;
|
|
}
|
|
|
|
if (tex->stype == TEX_WALLOUT) {
|
|
texres->tin = 1.0f - texres->tin;
|
|
}
|
|
|
|
if (texres->tin < 0.0f) {
|
|
texres->tin = 0.0f;
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* newnoise: musgrave terrain noise types */
|
|
|
|
static float mg_mFractalOrfBmTex(Tex *tex, const float texvec[3], TexResult *texres)
|
|
{
|
|
int rv = TEX_INT;
|
|
float (*mgravefunc)(float, float, float, float, float, float, int);
|
|
|
|
if (tex->stype == TEX_MFRACTAL) {
|
|
mgravefunc = mg_MultiFractal;
|
|
}
|
|
else {
|
|
mgravefunc = mg_fBm;
|
|
}
|
|
|
|
texres->tin = tex->ns_outscale * mgravefunc(texvec[0],
|
|
texvec[1],
|
|
texvec[2],
|
|
tex->mg_H,
|
|
tex->mg_lacunarity,
|
|
tex->mg_octaves,
|
|
tex->noisebasis);
|
|
|
|
if (texres->nor != NULL) {
|
|
float offs = tex->nabla / tex->noisesize; /* also scaling of texvec */
|
|
|
|
/* calculate bumpnormal */
|
|
texres->nor[0] = tex->ns_outscale * mgravefunc(texvec[0] + offs,
|
|
texvec[1],
|
|
texvec[2],
|
|
tex->mg_H,
|
|
tex->mg_lacunarity,
|
|
tex->mg_octaves,
|
|
tex->noisebasis);
|
|
texres->nor[1] = tex->ns_outscale * mgravefunc(texvec[0],
|
|
texvec[1] + offs,
|
|
texvec[2],
|
|
tex->mg_H,
|
|
tex->mg_lacunarity,
|
|
tex->mg_octaves,
|
|
tex->noisebasis);
|
|
texres->nor[2] = tex->ns_outscale * mgravefunc(texvec[0],
|
|
texvec[1],
|
|
texvec[2] + offs,
|
|
tex->mg_H,
|
|
tex->mg_lacunarity,
|
|
tex->mg_octaves,
|
|
tex->noisebasis);
|
|
|
|
tex_normal_derivate(tex, texres);
|
|
rv |= TEX_NOR;
|
|
}
|
|
|
|
BRICONT;
|
|
|
|
return rv;
|
|
}
|
|
|
|
static float mg_ridgedOrHybridMFTex(Tex *tex, const float texvec[3], TexResult *texres)
|
|
{
|
|
int rv = TEX_INT;
|
|
float (*mgravefunc)(float, float, float, float, float, float, float, float, int);
|
|
|
|
if (tex->stype == TEX_RIDGEDMF) {
|
|
mgravefunc = mg_RidgedMultiFractal;
|
|
}
|
|
else {
|
|
mgravefunc = mg_HybridMultiFractal;
|
|
}
|
|
|
|
texres->tin = tex->ns_outscale * mgravefunc(texvec[0],
|
|
texvec[1],
|
|
texvec[2],
|
|
tex->mg_H,
|
|
tex->mg_lacunarity,
|
|
tex->mg_octaves,
|
|
tex->mg_offset,
|
|
tex->mg_gain,
|
|
tex->noisebasis);
|
|
|
|
if (texres->nor != NULL) {
|
|
float offs = tex->nabla / tex->noisesize; /* also scaling of texvec */
|
|
|
|
/* calculate bumpnormal */
|
|
texres->nor[0] = tex->ns_outscale * mgravefunc(texvec[0] + offs,
|
|
texvec[1],
|
|
texvec[2],
|
|
tex->mg_H,
|
|
tex->mg_lacunarity,
|
|
tex->mg_octaves,
|
|
tex->mg_offset,
|
|
tex->mg_gain,
|
|
tex->noisebasis);
|
|
texres->nor[1] = tex->ns_outscale * mgravefunc(texvec[0],
|
|
texvec[1] + offs,
|
|
texvec[2],
|
|
tex->mg_H,
|
|
tex->mg_lacunarity,
|
|
tex->mg_octaves,
|
|
tex->mg_offset,
|
|
tex->mg_gain,
|
|
tex->noisebasis);
|
|
texres->nor[2] = tex->ns_outscale * mgravefunc(texvec[0],
|
|
texvec[1],
|
|
texvec[2] + offs,
|
|
tex->mg_H,
|
|
tex->mg_lacunarity,
|
|
tex->mg_octaves,
|
|
tex->mg_offset,
|
|
tex->mg_gain,
|
|
tex->noisebasis);
|
|
|
|
tex_normal_derivate(tex, texres);
|
|
rv |= TEX_NOR;
|
|
}
|
|
|
|
BRICONT;
|
|
|
|
return rv;
|
|
}
|
|
|
|
static float mg_HTerrainTex(Tex *tex, const float texvec[3], TexResult *texres)
|
|
{
|
|
int rv = TEX_INT;
|
|
|
|
texres->tin = tex->ns_outscale * mg_HeteroTerrain(texvec[0],
|
|
texvec[1],
|
|
texvec[2],
|
|
tex->mg_H,
|
|
tex->mg_lacunarity,
|
|
tex->mg_octaves,
|
|
tex->mg_offset,
|
|
tex->noisebasis);
|
|
|
|
if (texres->nor != NULL) {
|
|
float offs = tex->nabla / tex->noisesize; /* also scaling of texvec */
|
|
|
|
/* calculate bumpnormal */
|
|
texres->nor[0] = tex->ns_outscale * mg_HeteroTerrain(texvec[0] + offs,
|
|
texvec[1],
|
|
texvec[2],
|
|
tex->mg_H,
|
|
tex->mg_lacunarity,
|
|
tex->mg_octaves,
|
|
tex->mg_offset,
|
|
tex->noisebasis);
|
|
texres->nor[1] = tex->ns_outscale * mg_HeteroTerrain(texvec[0],
|
|
texvec[1] + offs,
|
|
texvec[2],
|
|
tex->mg_H,
|
|
tex->mg_lacunarity,
|
|
tex->mg_octaves,
|
|
tex->mg_offset,
|
|
tex->noisebasis);
|
|
texres->nor[2] = tex->ns_outscale * mg_HeteroTerrain(texvec[0],
|
|
texvec[1],
|
|
texvec[2] + offs,
|
|
tex->mg_H,
|
|
tex->mg_lacunarity,
|
|
tex->mg_octaves,
|
|
tex->mg_offset,
|
|
tex->noisebasis);
|
|
|
|
tex_normal_derivate(tex, texres);
|
|
rv |= TEX_NOR;
|
|
}
|
|
|
|
BRICONT;
|
|
|
|
return rv;
|
|
}
|
|
|
|
static float mg_distNoiseTex(Tex *tex, const float texvec[3], TexResult *texres)
|
|
{
|
|
int rv = TEX_INT;
|
|
|
|
texres->tin = mg_VLNoise(
|
|
texvec[0], texvec[1], texvec[2], tex->dist_amount, tex->noisebasis, tex->noisebasis2);
|
|
|
|
if (texres->nor != NULL) {
|
|
float offs = tex->nabla / tex->noisesize; /* also scaling of texvec */
|
|
|
|
/* calculate bumpnormal */
|
|
texres->nor[0] = mg_VLNoise(texvec[0] + offs,
|
|
texvec[1],
|
|
texvec[2],
|
|
tex->dist_amount,
|
|
tex->noisebasis,
|
|
tex->noisebasis2);
|
|
texres->nor[1] = mg_VLNoise(texvec[0],
|
|
texvec[1] + offs,
|
|
texvec[2],
|
|
tex->dist_amount,
|
|
tex->noisebasis,
|
|
tex->noisebasis2);
|
|
texres->nor[2] = mg_VLNoise(texvec[0],
|
|
texvec[1],
|
|
texvec[2] + offs,
|
|
tex->dist_amount,
|
|
tex->noisebasis,
|
|
tex->noisebasis2);
|
|
|
|
tex_normal_derivate(tex, texres);
|
|
rv |= TEX_NOR;
|
|
}
|
|
|
|
BRICONT;
|
|
|
|
return rv;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
/* newnoise: Voronoi texture type
|
|
*
|
|
* probably the slowest, especially with minkovsky, bumpmapping, could be done another way.
|
|
*/
|
|
|
|
static float voronoiTex(Tex *tex, const float texvec[3], TexResult *texres)
|
|
{
|
|
int rv = TEX_INT;
|
|
float da[4], pa[12]; /* distance and point coordinate arrays of 4 nearest neighbors */
|
|
float aw1 = fabsf(tex->vn_w1);
|
|
float aw2 = fabsf(tex->vn_w2);
|
|
float aw3 = fabsf(tex->vn_w3);
|
|
float aw4 = fabsf(tex->vn_w4);
|
|
float sc = (aw1 + aw2 + aw3 + aw4);
|
|
if (sc != 0.f) {
|
|
sc = tex->ns_outscale / sc;
|
|
}
|
|
|
|
voronoi(texvec[0], texvec[1], texvec[2], da, pa, tex->vn_mexp, tex->vn_distm);
|
|
texres->tin = sc * fabsf(tex->vn_w1 * da[0] + tex->vn_w2 * da[1] + tex->vn_w3 * da[2] +
|
|
tex->vn_w4 * da[3]);
|
|
|
|
if (tex->vn_coltype) {
|
|
float ca[3]; /* cell color */
|
|
cellNoiseV(pa[0], pa[1], pa[2], ca);
|
|
texres->tr = aw1 * ca[0];
|
|
texres->tg = aw1 * ca[1];
|
|
texres->tb = aw1 * ca[2];
|
|
cellNoiseV(pa[3], pa[4], pa[5], ca);
|
|
texres->tr += aw2 * ca[0];
|
|
texres->tg += aw2 * ca[1];
|
|
texres->tb += aw2 * ca[2];
|
|
cellNoiseV(pa[6], pa[7], pa[8], ca);
|
|
texres->tr += aw3 * ca[0];
|
|
texres->tg += aw3 * ca[1];
|
|
texres->tb += aw3 * ca[2];
|
|
cellNoiseV(pa[9], pa[10], pa[11], ca);
|
|
texres->tr += aw4 * ca[0];
|
|
texres->tg += aw4 * ca[1];
|
|
texres->tb += aw4 * ca[2];
|
|
if (tex->vn_coltype >= 2) {
|
|
float t1 = (da[1] - da[0]) * 10;
|
|
if (t1 > 1) {
|
|
t1 = 1;
|
|
}
|
|
if (tex->vn_coltype == 3) {
|
|
t1 *= texres->tin;
|
|
}
|
|
else {
|
|
t1 *= sc;
|
|
}
|
|
texres->tr *= t1;
|
|
texres->tg *= t1;
|
|
texres->tb *= t1;
|
|
}
|
|
else {
|
|
texres->tr *= sc;
|
|
texres->tg *= sc;
|
|
texres->tb *= sc;
|
|
}
|
|
}
|
|
|
|
if (texres->nor != NULL) {
|
|
float offs = tex->nabla / tex->noisesize; /* also scaling of texvec */
|
|
|
|
/* calculate bumpnormal */
|
|
voronoi(texvec[0] + offs, texvec[1], texvec[2], da, pa, tex->vn_mexp, tex->vn_distm);
|
|
texres->nor[0] = sc * fabsf(tex->vn_w1 * da[0] + tex->vn_w2 * da[1] + tex->vn_w3 * da[2] +
|
|
tex->vn_w4 * da[3]);
|
|
voronoi(texvec[0], texvec[1] + offs, texvec[2], da, pa, tex->vn_mexp, tex->vn_distm);
|
|
texres->nor[1] = sc * fabsf(tex->vn_w1 * da[0] + tex->vn_w2 * da[1] + tex->vn_w3 * da[2] +
|
|
tex->vn_w4 * da[3]);
|
|
voronoi(texvec[0], texvec[1], texvec[2] + offs, da, pa, tex->vn_mexp, tex->vn_distm);
|
|
texres->nor[2] = sc * fabsf(tex->vn_w1 * da[0] + tex->vn_w2 * da[1] + tex->vn_w3 * da[2] +
|
|
tex->vn_w4 * da[3]);
|
|
|
|
tex_normal_derivate(tex, texres);
|
|
rv |= TEX_NOR;
|
|
}
|
|
|
|
if (tex->vn_coltype) {
|
|
BRICONTRGB;
|
|
texres->ta = 1.0;
|
|
return (rv | TEX_RGB);
|
|
}
|
|
|
|
BRICONT;
|
|
|
|
return rv;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
static int texnoise(Tex *tex, TexResult *texres, int thread)
|
|
{
|
|
float div = 3.0;
|
|
int val, ran, loop, shift = 29;
|
|
|
|
ran = BLI_rng_thread_rand(random_tex_array, thread);
|
|
|
|
loop = tex->noisedepth;
|
|
|
|
/* start from top bits since they have more variance */
|
|
val = ((ran >> shift) & 3);
|
|
|
|
while (loop--) {
|
|
shift -= 2;
|
|
val *= ((ran >> shift) & 3);
|
|
div *= 3.0f;
|
|
}
|
|
|
|
texres->tin = ((float)val) / div;
|
|
|
|
BRICONT;
|
|
return TEX_INT;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
static int cubemap_glob(const float n[3], float x, float y, float z, float *adr1, float *adr2)
|
|
{
|
|
float x1, y1, z1, nor[3];
|
|
int ret;
|
|
|
|
if (n == NULL) {
|
|
nor[0] = x;
|
|
nor[1] = y;
|
|
nor[2] = z; /* use local render coord */
|
|
}
|
|
else {
|
|
copy_v3_v3(nor, n);
|
|
}
|
|
|
|
x1 = fabsf(nor[0]);
|
|
y1 = fabsf(nor[1]);
|
|
z1 = fabsf(nor[2]);
|
|
|
|
if (z1 >= x1 && z1 >= y1) {
|
|
*adr1 = (x + 1.0f) / 2.0f;
|
|
*adr2 = (y + 1.0f) / 2.0f;
|
|
ret = 0;
|
|
}
|
|
else if (y1 >= x1 && y1 >= z1) {
|
|
*adr1 = (x + 1.0f) / 2.0f;
|
|
*adr2 = (z + 1.0f) / 2.0f;
|
|
ret = 1;
|
|
}
|
|
else {
|
|
*adr1 = (y + 1.0f) / 2.0f;
|
|
*adr2 = (z + 1.0f) / 2.0f;
|
|
ret = 2;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
static void do_2d_mapping(
|
|
const MTex *mtex, float texvec[3], const float n[3], float dxt[3], float dyt[3])
|
|
{
|
|
Tex *tex;
|
|
float fx, fy, fac1, area[8];
|
|
int ok, proj, areaflag = 0, wrap;
|
|
|
|
/* mtex variables localized, only cubemap doesn't cooperate yet... */
|
|
wrap = mtex->mapping;
|
|
tex = mtex->tex;
|
|
|
|
if (!(dxt && dyt)) {
|
|
|
|
if (wrap == MTEX_FLAT) {
|
|
fx = (texvec[0] + 1.0f) / 2.0f;
|
|
fy = (texvec[1] + 1.0f) / 2.0f;
|
|
}
|
|
else if (wrap == MTEX_TUBE) {
|
|
map_to_tube(&fx, &fy, texvec[0], texvec[1], texvec[2]);
|
|
}
|
|
else if (wrap == MTEX_SPHERE) {
|
|
map_to_sphere(&fx, &fy, texvec[0], texvec[1], texvec[2]);
|
|
}
|
|
else {
|
|
cubemap_glob(n, texvec[0], texvec[1], texvec[2], &fx, &fy);
|
|
}
|
|
|
|
/* repeat */
|
|
if (tex->extend == TEX_REPEAT) {
|
|
if (tex->xrepeat > 1) {
|
|
float origf = fx *= tex->xrepeat;
|
|
|
|
if (fx > 1.0f) {
|
|
fx -= (int)(fx);
|
|
}
|
|
else if (fx < 0.0f) {
|
|
fx += 1 - (int)(fx);
|
|
}
|
|
|
|
if (tex->flag & TEX_REPEAT_XMIR) {
|
|
int orig = (int)floor(origf);
|
|
if (orig & 1) {
|
|
fx = 1.0f - fx;
|
|
}
|
|
}
|
|
}
|
|
if (tex->yrepeat > 1) {
|
|
float origf = fy *= tex->yrepeat;
|
|
|
|
if (fy > 1.0f) {
|
|
fy -= (int)(fy);
|
|
}
|
|
else if (fy < 0.0f) {
|
|
fy += 1 - (int)(fy);
|
|
}
|
|
|
|
if (tex->flag & TEX_REPEAT_YMIR) {
|
|
int orig = (int)floor(origf);
|
|
if (orig & 1) {
|
|
fy = 1.0f - fy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* crop */
|
|
if (tex->cropxmin != 0.0f || tex->cropxmax != 1.0f) {
|
|
fac1 = tex->cropxmax - tex->cropxmin;
|
|
fx = tex->cropxmin + fx * fac1;
|
|
}
|
|
if (tex->cropymin != 0.0f || tex->cropymax != 1.0f) {
|
|
fac1 = tex->cropymax - tex->cropymin;
|
|
fy = tex->cropymin + fy * fac1;
|
|
}
|
|
|
|
texvec[0] = fx;
|
|
texvec[1] = fy;
|
|
}
|
|
else {
|
|
|
|
if (wrap == MTEX_FLAT) {
|
|
fx = (texvec[0] + 1.0f) / 2.0f;
|
|
fy = (texvec[1] + 1.0f) / 2.0f;
|
|
dxt[0] /= 2.0f;
|
|
dxt[1] /= 2.0f;
|
|
dxt[2] /= 2.0f;
|
|
dyt[0] /= 2.0f;
|
|
dyt[1] /= 2.0f;
|
|
dyt[2] /= 2.0f;
|
|
}
|
|
else if (ELEM(wrap, MTEX_TUBE, MTEX_SPHERE)) {
|
|
/* exception: the seam behind (y<0.0) */
|
|
ok = 1;
|
|
if (texvec[1] <= 0.0f) {
|
|
fx = texvec[0] + dxt[0];
|
|
fy = texvec[0] + dyt[0];
|
|
if (fx >= 0.0f && fy >= 0.0f && texvec[0] >= 0.0f) {
|
|
/* pass */
|
|
}
|
|
else if (fx <= 0.0f && fy <= 0.0f && texvec[0] <= 0.0f) {
|
|
/* pass */
|
|
}
|
|
else {
|
|
ok = 0;
|
|
}
|
|
}
|
|
|
|
if (ok) {
|
|
if (wrap == MTEX_TUBE) {
|
|
map_to_tube(area, area + 1, texvec[0], texvec[1], texvec[2]);
|
|
map_to_tube(
|
|
area + 2, area + 3, texvec[0] + dxt[0], texvec[1] + dxt[1], texvec[2] + dxt[2]);
|
|
map_to_tube(
|
|
area + 4, area + 5, texvec[0] + dyt[0], texvec[1] + dyt[1], texvec[2] + dyt[2]);
|
|
}
|
|
else {
|
|
map_to_sphere(area, area + 1, texvec[0], texvec[1], texvec[2]);
|
|
map_to_sphere(
|
|
area + 2, area + 3, texvec[0] + dxt[0], texvec[1] + dxt[1], texvec[2] + dxt[2]);
|
|
map_to_sphere(
|
|
area + 4, area + 5, texvec[0] + dyt[0], texvec[1] + dyt[1], texvec[2] + dyt[2]);
|
|
}
|
|
areaflag = 1;
|
|
}
|
|
else {
|
|
if (wrap == MTEX_TUBE) {
|
|
map_to_tube(&fx, &fy, texvec[0], texvec[1], texvec[2]);
|
|
}
|
|
else {
|
|
map_to_sphere(&fx, &fy, texvec[0], texvec[1], texvec[2]);
|
|
}
|
|
dxt[0] /= 2.0f;
|
|
dxt[1] /= 2.0f;
|
|
dyt[0] /= 2.0f;
|
|
dyt[1] /= 2.0f;
|
|
}
|
|
}
|
|
else {
|
|
|
|
proj = cubemap_glob(n, texvec[0], texvec[1], texvec[2], &fx, &fy);
|
|
|
|
if (proj == 1) {
|
|
SWAP(float, dxt[1], dxt[2]);
|
|
SWAP(float, dyt[1], dyt[2]);
|
|
}
|
|
else if (proj == 2) {
|
|
float f1 = dxt[0], f2 = dyt[0];
|
|
dxt[0] = dxt[1];
|
|
dyt[0] = dyt[1];
|
|
dxt[1] = dxt[2];
|
|
dyt[1] = dyt[2];
|
|
dxt[2] = f1;
|
|
dyt[2] = f2;
|
|
}
|
|
|
|
dxt[0] *= 0.5f;
|
|
dxt[1] *= 0.5f;
|
|
dxt[2] *= 0.5f;
|
|
|
|
dyt[0] *= 0.5f;
|
|
dyt[1] *= 0.5f;
|
|
dyt[2] *= 0.5f;
|
|
}
|
|
|
|
/* if area, then reacalculate dxt[] and dyt[] */
|
|
if (areaflag) {
|
|
fx = area[0];
|
|
fy = area[1];
|
|
dxt[0] = area[2] - fx;
|
|
dxt[1] = area[3] - fy;
|
|
dyt[0] = area[4] - fx;
|
|
dyt[1] = area[5] - fy;
|
|
}
|
|
|
|
/* repeat */
|
|
if (tex->extend == TEX_REPEAT) {
|
|
float max = 1.0f;
|
|
if (tex->xrepeat > 1) {
|
|
float origf = fx *= tex->xrepeat;
|
|
|
|
/* TXF: omit mirror here, see comments in do_material_tex() after do_2d_mapping() call */
|
|
if (tex->texfilter == TXF_BOX) {
|
|
if (fx > 1.0f) {
|
|
fx -= (int)(fx);
|
|
}
|
|
else if (fx < 0.0f) {
|
|
fx += 1 - (int)(fx);
|
|
}
|
|
|
|
if (tex->flag & TEX_REPEAT_XMIR) {
|
|
int orig = (int)floor(origf);
|
|
if (orig & 1) {
|
|
fx = 1.0f - fx;
|
|
}
|
|
}
|
|
}
|
|
|
|
max = tex->xrepeat;
|
|
|
|
dxt[0] *= tex->xrepeat;
|
|
dyt[0] *= tex->xrepeat;
|
|
}
|
|
if (tex->yrepeat > 1) {
|
|
float origf = fy *= tex->yrepeat;
|
|
|
|
/* TXF: omit mirror here, see comments in do_material_tex() after do_2d_mapping() call */
|
|
if (tex->texfilter == TXF_BOX) {
|
|
if (fy > 1.0f) {
|
|
fy -= (int)(fy);
|
|
}
|
|
else if (fy < 0.0f) {
|
|
fy += 1 - (int)(fy);
|
|
}
|
|
|
|
if (tex->flag & TEX_REPEAT_YMIR) {
|
|
int orig = (int)floor(origf);
|
|
if (orig & 1) {
|
|
fy = 1.0f - fy;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (max < tex->yrepeat) {
|
|
max = tex->yrepeat;
|
|
}
|
|
|
|
dxt[1] *= tex->yrepeat;
|
|
dyt[1] *= tex->yrepeat;
|
|
}
|
|
if (max != 1.0f) {
|
|
dxt[2] *= max;
|
|
dyt[2] *= max;
|
|
}
|
|
}
|
|
/* crop */
|
|
if (tex->cropxmin != 0.0f || tex->cropxmax != 1.0f) {
|
|
fac1 = tex->cropxmax - tex->cropxmin;
|
|
fx = tex->cropxmin + fx * fac1;
|
|
dxt[0] *= fac1;
|
|
dyt[0] *= fac1;
|
|
}
|
|
if (tex->cropymin != 0.0f || tex->cropymax != 1.0f) {
|
|
fac1 = tex->cropymax - tex->cropymin;
|
|
fy = tex->cropymin + fy * fac1;
|
|
dxt[1] *= fac1;
|
|
dyt[1] *= fac1;
|
|
}
|
|
|
|
texvec[0] = fx;
|
|
texvec[1] = fy;
|
|
}
|
|
}
|
|
|
|
/* ************************************** */
|
|
|
|
static int multitex(Tex *tex,
|
|
float texvec[3],
|
|
float dxt[3],
|
|
float dyt[3],
|
|
int osatex,
|
|
TexResult *texres,
|
|
const short thread,
|
|
const short which_output,
|
|
struct ImagePool *pool,
|
|
const bool skip_load_image,
|
|
const bool texnode_preview,
|
|
const bool use_nodes)
|
|
{
|
|
float tmpvec[3];
|
|
int retval = 0; /* return value, int:0, col:1, nor:2, everything:3 */
|
|
|
|
texres->talpha = false; /* is set when image texture returns alpha (considered premul) */
|
|
|
|
if (use_nodes && tex->use_nodes && tex->nodetree) {
|
|
const float cfra = 1.0f; /* This was only set for Blender Internal render before. */
|
|
retval = ntreeTexExecTree(tex->nodetree,
|
|
texres,
|
|
texvec,
|
|
dxt,
|
|
dyt,
|
|
osatex,
|
|
thread,
|
|
tex,
|
|
which_output,
|
|
cfra,
|
|
texnode_preview,
|
|
NULL);
|
|
}
|
|
else {
|
|
switch (tex->type) {
|
|
case 0:
|
|
texres->tin = 0.0f;
|
|
return 0;
|
|
case TEX_CLOUDS:
|
|
retval = clouds(tex, texvec, texres);
|
|
break;
|
|
case TEX_WOOD:
|
|
retval = wood(tex, texvec, texres);
|
|
break;
|
|
case TEX_MARBLE:
|
|
retval = marble(tex, texvec, texres);
|
|
break;
|
|
case TEX_MAGIC:
|
|
retval = magic(tex, texvec, texres);
|
|
break;
|
|
case TEX_BLEND:
|
|
retval = blend(tex, texvec, texres);
|
|
break;
|
|
case TEX_STUCCI:
|
|
retval = stucci(tex, texvec, texres);
|
|
break;
|
|
case TEX_NOISE:
|
|
retval = texnoise(tex, texres, thread);
|
|
break;
|
|
case TEX_IMAGE:
|
|
if (osatex) {
|
|
retval = imagewraposa(
|
|
tex, tex->ima, NULL, texvec, dxt, dyt, texres, pool, skip_load_image);
|
|
}
|
|
else {
|
|
retval = imagewrap(tex, tex->ima, NULL, texvec, texres, pool, skip_load_image);
|
|
}
|
|
if (tex->ima) {
|
|
BKE_image_tag_time(tex->ima);
|
|
}
|
|
break;
|
|
case TEX_MUSGRAVE:
|
|
/* newnoise: musgrave types */
|
|
|
|
/* ton: added this, for Blender convention reason.
|
|
* artificer: added the use of tmpvec to avoid scaling texvec
|
|
*/
|
|
copy_v3_v3(tmpvec, texvec);
|
|
mul_v3_fl(tmpvec, 1.0f / tex->noisesize);
|
|
|
|
switch (tex->stype) {
|
|
case TEX_MFRACTAL:
|
|
case TEX_FBM:
|
|
retval = mg_mFractalOrfBmTex(tex, tmpvec, texres);
|
|
break;
|
|
case TEX_RIDGEDMF:
|
|
case TEX_HYBRIDMF:
|
|
retval = mg_ridgedOrHybridMFTex(tex, tmpvec, texres);
|
|
break;
|
|
case TEX_HTERRAIN:
|
|
retval = mg_HTerrainTex(tex, tmpvec, texres);
|
|
break;
|
|
}
|
|
break;
|
|
/* newnoise: voronoi type */
|
|
case TEX_VORONOI:
|
|
/* ton: added this, for Blender convention reason.
|
|
* artificer: added the use of tmpvec to avoid scaling texvec
|
|
*/
|
|
copy_v3_v3(tmpvec, texvec);
|
|
mul_v3_fl(tmpvec, 1.0f / tex->noisesize);
|
|
|
|
retval = voronoiTex(tex, tmpvec, texres);
|
|
break;
|
|
case TEX_DISTNOISE:
|
|
/* ton: added this, for Blender convention reason.
|
|
* artificer: added the use of tmpvec to avoid scaling texvec
|
|
*/
|
|
copy_v3_v3(tmpvec, texvec);
|
|
mul_v3_fl(tmpvec, 1.0f / tex->noisesize);
|
|
|
|
retval = mg_distNoiseTex(tex, tmpvec, texres);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (tex->flag & TEX_COLORBAND) {
|
|
float col[4];
|
|
if (BKE_colorband_evaluate(tex->coba, texres->tin, col)) {
|
|
texres->talpha = true;
|
|
texres->tr = col[0];
|
|
texres->tg = col[1];
|
|
texres->tb = col[2];
|
|
texres->ta = col[3];
|
|
retval |= TEX_RGB;
|
|
}
|
|
}
|
|
return retval;
|
|
}
|
|
|
|
static int multitex_nodes_intern(Tex *tex,
|
|
float texvec[3],
|
|
float dxt[3],
|
|
float dyt[3],
|
|
int osatex,
|
|
TexResult *texres,
|
|
const short thread,
|
|
short which_output,
|
|
MTex *mtex,
|
|
struct ImagePool *pool,
|
|
const bool scene_color_manage,
|
|
const bool skip_load_image,
|
|
const bool texnode_preview,
|
|
const bool use_nodes)
|
|
{
|
|
if (tex == NULL) {
|
|
memset(texres, 0, sizeof(TexResult));
|
|
return 0;
|
|
}
|
|
|
|
if (mtex) {
|
|
which_output = mtex->which_output;
|
|
}
|
|
|
|
if (tex->type == TEX_IMAGE) {
|
|
int rgbnor;
|
|
|
|
if (mtex) {
|
|
/* we have mtex, use it for 2d mapping images only */
|
|
do_2d_mapping(mtex, texvec, NULL, dxt, dyt);
|
|
rgbnor = multitex(tex,
|
|
texvec,
|
|
dxt,
|
|
dyt,
|
|
osatex,
|
|
texres,
|
|
thread,
|
|
which_output,
|
|
pool,
|
|
skip_load_image,
|
|
texnode_preview,
|
|
use_nodes);
|
|
|
|
if (mtex->mapto & (MAP_COL)) {
|
|
ImBuf *ibuf = BKE_image_pool_acquire_ibuf(tex->ima, &tex->iuser, pool);
|
|
|
|
/* don't linearize float buffers, assumed to be linear */
|
|
if (ibuf != NULL && ibuf->rect_float == NULL && (rgbnor & TEX_RGB) && scene_color_manage) {
|
|
IMB_colormanagement_colorspace_to_scene_linear_v3(&texres->tr, ibuf->rect_colorspace);
|
|
}
|
|
|
|
BKE_image_pool_release_ibuf(tex->ima, ibuf, pool);
|
|
}
|
|
}
|
|
else {
|
|
/* we don't have mtex, do default flat 2d projection */
|
|
MTex localmtex;
|
|
float texvec_l[3], dxt_l[3], dyt_l[3];
|
|
|
|
localmtex.mapping = MTEX_FLAT;
|
|
localmtex.tex = tex;
|
|
localmtex.object = NULL;
|
|
localmtex.texco = TEXCO_ORCO;
|
|
|
|
copy_v3_v3(texvec_l, texvec);
|
|
if (dxt && dyt) {
|
|
copy_v3_v3(dxt_l, dxt);
|
|
copy_v3_v3(dyt_l, dyt);
|
|
}
|
|
else {
|
|
zero_v3(dxt_l);
|
|
zero_v3(dyt_l);
|
|
}
|
|
|
|
do_2d_mapping(&localmtex, texvec_l, NULL, dxt_l, dyt_l);
|
|
rgbnor = multitex(tex,
|
|
texvec_l,
|
|
dxt_l,
|
|
dyt_l,
|
|
osatex,
|
|
texres,
|
|
thread,
|
|
which_output,
|
|
pool,
|
|
skip_load_image,
|
|
texnode_preview,
|
|
use_nodes);
|
|
|
|
{
|
|
ImBuf *ibuf = BKE_image_pool_acquire_ibuf(tex->ima, &tex->iuser, pool);
|
|
|
|
/* don't linearize float buffers, assumed to be linear */
|
|
if (ibuf != NULL && ibuf->rect_float == NULL && (rgbnor & TEX_RGB) && scene_color_manage) {
|
|
IMB_colormanagement_colorspace_to_scene_linear_v3(&texres->tr, ibuf->rect_colorspace);
|
|
}
|
|
|
|
BKE_image_pool_release_ibuf(tex->ima, ibuf, pool);
|
|
}
|
|
}
|
|
|
|
return rgbnor;
|
|
}
|
|
else {
|
|
return multitex(tex,
|
|
texvec,
|
|
dxt,
|
|
dyt,
|
|
osatex,
|
|
texres,
|
|
thread,
|
|
which_output,
|
|
pool,
|
|
skip_load_image,
|
|
texnode_preview,
|
|
use_nodes);
|
|
}
|
|
}
|
|
|
|
/* this is called from the shader and texture nodes
|
|
* Use it from render pipeline only!
|
|
*/
|
|
int multitex_nodes(Tex *tex,
|
|
float texvec[3],
|
|
float dxt[3],
|
|
float dyt[3],
|
|
int osatex,
|
|
TexResult *texres,
|
|
const short thread,
|
|
short which_output,
|
|
MTex *mtex,
|
|
struct ImagePool *pool)
|
|
{
|
|
return multitex_nodes_intern(tex,
|
|
texvec,
|
|
dxt,
|
|
dyt,
|
|
osatex,
|
|
texres,
|
|
thread,
|
|
which_output,
|
|
mtex,
|
|
pool,
|
|
true,
|
|
false,
|
|
false,
|
|
true);
|
|
}
|
|
|
|
/**
|
|
* \warning if the texres's values are not declared zero,
|
|
* check the return value to be sure the color values are set before using the r/g/b values,
|
|
* otherwise you may use uninitialized values - Campbell
|
|
*
|
|
* Use it for stuff which is out of render pipeline.
|
|
*/
|
|
int multitex_ext(Tex *tex,
|
|
float texvec[3],
|
|
float dxt[3],
|
|
float dyt[3],
|
|
int osatex,
|
|
TexResult *texres,
|
|
const short thread,
|
|
struct ImagePool *pool,
|
|
bool scene_color_manage,
|
|
const bool skip_load_image)
|
|
{
|
|
return multitex_nodes_intern(tex,
|
|
texvec,
|
|
dxt,
|
|
dyt,
|
|
osatex,
|
|
texres,
|
|
thread,
|
|
0,
|
|
NULL,
|
|
pool,
|
|
scene_color_manage,
|
|
skip_load_image,
|
|
false,
|
|
true);
|
|
}
|
|
|
|
/* extern-tex doesn't support nodes (ntreeBeginExec() can't be called when rendering is going on)\
|
|
*
|
|
* Use it for stuff which is out of render pipeline.
|
|
*/
|
|
int multitex_ext_safe(Tex *tex,
|
|
float texvec[3],
|
|
TexResult *texres,
|
|
struct ImagePool *pool,
|
|
bool scene_color_manage,
|
|
const bool skip_load_image)
|
|
{
|
|
return multitex_nodes_intern(tex,
|
|
texvec,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
texres,
|
|
0,
|
|
0,
|
|
NULL,
|
|
pool,
|
|
scene_color_manage,
|
|
skip_load_image,
|
|
false,
|
|
false);
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
/* in = destination, tex = texture, out = previous color */
|
|
/* fact = texture strength, facg = button strength value */
|
|
void texture_rgb_blend(
|
|
float in[3], const float tex[3], const float out[3], float fact, float facg, int blendtype)
|
|
{
|
|
float facm;
|
|
|
|
switch (blendtype) {
|
|
case MTEX_BLEND:
|
|
fact *= facg;
|
|
facm = 1.0f - fact;
|
|
|
|
in[0] = (fact * tex[0] + facm * out[0]);
|
|
in[1] = (fact * tex[1] + facm * out[1]);
|
|
in[2] = (fact * tex[2] + facm * out[2]);
|
|
break;
|
|
|
|
case MTEX_MUL:
|
|
fact *= facg;
|
|
facm = 1.0f - fact;
|
|
in[0] = (facm + fact * tex[0]) * out[0];
|
|
in[1] = (facm + fact * tex[1]) * out[1];
|
|
in[2] = (facm + fact * tex[2]) * out[2];
|
|
break;
|
|
|
|
case MTEX_SCREEN:
|
|
fact *= facg;
|
|
facm = 1.0f - fact;
|
|
in[0] = 1.0f - (facm + fact * (1.0f - tex[0])) * (1.0f - out[0]);
|
|
in[1] = 1.0f - (facm + fact * (1.0f - tex[1])) * (1.0f - out[1]);
|
|
in[2] = 1.0f - (facm + fact * (1.0f - tex[2])) * (1.0f - out[2]);
|
|
break;
|
|
|
|
case MTEX_OVERLAY:
|
|
fact *= facg;
|
|
facm = 1.0f - fact;
|
|
|
|
if (out[0] < 0.5f) {
|
|
in[0] = out[0] * (facm + 2.0f * fact * tex[0]);
|
|
}
|
|
else {
|
|
in[0] = 1.0f - (facm + 2.0f * fact * (1.0f - tex[0])) * (1.0f - out[0]);
|
|
}
|
|
if (out[1] < 0.5f) {
|
|
in[1] = out[1] * (facm + 2.0f * fact * tex[1]);
|
|
}
|
|
else {
|
|
in[1] = 1.0f - (facm + 2.0f * fact * (1.0f - tex[1])) * (1.0f - out[1]);
|
|
}
|
|
if (out[2] < 0.5f) {
|
|
in[2] = out[2] * (facm + 2.0f * fact * tex[2]);
|
|
}
|
|
else {
|
|
in[2] = 1.0f - (facm + 2.0f * fact * (1.0f - tex[2])) * (1.0f - out[2]);
|
|
}
|
|
break;
|
|
|
|
case MTEX_SUB:
|
|
fact = -fact;
|
|
ATTR_FALLTHROUGH;
|
|
case MTEX_ADD:
|
|
fact *= facg;
|
|
in[0] = (fact * tex[0] + out[0]);
|
|
in[1] = (fact * tex[1] + out[1]);
|
|
in[2] = (fact * tex[2] + out[2]);
|
|
break;
|
|
|
|
case MTEX_DIV:
|
|
fact *= facg;
|
|
facm = 1.0f - fact;
|
|
|
|
if (tex[0] != 0.0f) {
|
|
in[0] = facm * out[0] + fact * out[0] / tex[0];
|
|
}
|
|
if (tex[1] != 0.0f) {
|
|
in[1] = facm * out[1] + fact * out[1] / tex[1];
|
|
}
|
|
if (tex[2] != 0.0f) {
|
|
in[2] = facm * out[2] + fact * out[2] / tex[2];
|
|
}
|
|
|
|
break;
|
|
|
|
case MTEX_DIFF:
|
|
fact *= facg;
|
|
facm = 1.0f - fact;
|
|
in[0] = facm * out[0] + fact * fabsf(tex[0] - out[0]);
|
|
in[1] = facm * out[1] + fact * fabsf(tex[1] - out[1]);
|
|
in[2] = facm * out[2] + fact * fabsf(tex[2] - out[2]);
|
|
break;
|
|
|
|
case MTEX_DARK:
|
|
fact *= facg;
|
|
facm = 1.0f - fact;
|
|
|
|
in[0] = min_ff(out[0], tex[0]) * fact + out[0] * facm;
|
|
in[1] = min_ff(out[1], tex[1]) * fact + out[1] * facm;
|
|
in[2] = min_ff(out[2], tex[2]) * fact + out[2] * facm;
|
|
break;
|
|
|
|
case MTEX_LIGHT:
|
|
fact *= facg;
|
|
|
|
in[0] = max_ff(fact * tex[0], out[0]);
|
|
in[1] = max_ff(fact * tex[1], out[1]);
|
|
in[2] = max_ff(fact * tex[2], out[2]);
|
|
break;
|
|
|
|
case MTEX_BLEND_HUE:
|
|
fact *= facg;
|
|
copy_v3_v3(in, out);
|
|
ramp_blend(MA_RAMP_HUE, in, fact, tex);
|
|
break;
|
|
case MTEX_BLEND_SAT:
|
|
fact *= facg;
|
|
copy_v3_v3(in, out);
|
|
ramp_blend(MA_RAMP_SAT, in, fact, tex);
|
|
break;
|
|
case MTEX_BLEND_VAL:
|
|
fact *= facg;
|
|
copy_v3_v3(in, out);
|
|
ramp_blend(MA_RAMP_VAL, in, fact, tex);
|
|
break;
|
|
case MTEX_BLEND_COLOR:
|
|
fact *= facg;
|
|
copy_v3_v3(in, out);
|
|
ramp_blend(MA_RAMP_COLOR, in, fact, tex);
|
|
break;
|
|
case MTEX_SOFT_LIGHT:
|
|
fact *= facg;
|
|
copy_v3_v3(in, out);
|
|
ramp_blend(MA_RAMP_SOFT, in, fact, tex);
|
|
break;
|
|
case MTEX_LIN_LIGHT:
|
|
fact *= facg;
|
|
copy_v3_v3(in, out);
|
|
ramp_blend(MA_RAMP_LINEAR, in, fact, tex);
|
|
break;
|
|
}
|
|
}
|
|
|
|
float texture_value_blend(float tex, float out, float fact, float facg, int blendtype)
|
|
{
|
|
float in = 0.0, facm, col, scf;
|
|
int flip = (facg < 0.0f);
|
|
|
|
facg = fabsf(facg);
|
|
|
|
fact *= facg;
|
|
facm = 1.0f - fact;
|
|
if (flip) {
|
|
SWAP(float, fact, facm);
|
|
}
|
|
|
|
switch (blendtype) {
|
|
case MTEX_BLEND:
|
|
in = fact * tex + facm * out;
|
|
break;
|
|
|
|
case MTEX_MUL:
|
|
facm = 1.0f - facg;
|
|
in = (facm + fact * tex) * out;
|
|
break;
|
|
|
|
case MTEX_SCREEN:
|
|
facm = 1.0f - facg;
|
|
in = 1.0f - (facm + fact * (1.0f - tex)) * (1.0f - out);
|
|
break;
|
|
|
|
case MTEX_OVERLAY:
|
|
facm = 1.0f - facg;
|
|
if (out < 0.5f) {
|
|
in = out * (facm + 2.0f * fact * tex);
|
|
}
|
|
else {
|
|
in = 1.0f - (facm + 2.0f * fact * (1.0f - tex)) * (1.0f - out);
|
|
}
|
|
break;
|
|
|
|
case MTEX_SUB:
|
|
fact = -fact;
|
|
ATTR_FALLTHROUGH;
|
|
case MTEX_ADD:
|
|
in = fact * tex + out;
|
|
break;
|
|
|
|
case MTEX_DIV:
|
|
if (tex != 0.0f) {
|
|
in = facm * out + fact * out / tex;
|
|
}
|
|
break;
|
|
|
|
case MTEX_DIFF:
|
|
in = facm * out + fact * fabsf(tex - out);
|
|
break;
|
|
|
|
case MTEX_DARK:
|
|
in = min_ff(out, tex) * fact + out * facm;
|
|
break;
|
|
|
|
case MTEX_LIGHT:
|
|
col = fact * tex;
|
|
if (col > out) {
|
|
in = col;
|
|
}
|
|
else {
|
|
in = out;
|
|
}
|
|
break;
|
|
|
|
case MTEX_SOFT_LIGHT:
|
|
scf = 1.0f - (1.0f - tex) * (1.0f - out);
|
|
in = facm * out + fact * ((1.0f - out) * tex * out) + (out * scf);
|
|
break;
|
|
|
|
case MTEX_LIN_LIGHT:
|
|
if (tex > 0.5f) {
|
|
in = out + fact * (2.0f * (tex - 0.5f));
|
|
}
|
|
else {
|
|
in = out + fact * (2.0f * tex - 1.0f);
|
|
}
|
|
break;
|
|
}
|
|
|
|
return in;
|
|
}
|
|
|
|
/* ------------------------------------------------------------------------- */
|
|
|
|
int externtex(const MTex *mtex,
|
|
const float vec[3],
|
|
float *tin,
|
|
float *tr,
|
|
float *tg,
|
|
float *tb,
|
|
float *ta,
|
|
const int thread,
|
|
struct ImagePool *pool,
|
|
const bool skip_load_image,
|
|
const bool texnode_preview)
|
|
{
|
|
Tex *tex;
|
|
TexResult texr;
|
|
float dxt[3], dyt[3], texvec[3];
|
|
int rgb;
|
|
|
|
tex = mtex->tex;
|
|
if (tex == NULL) {
|
|
return 0;
|
|
}
|
|
texr.nor = NULL;
|
|
|
|
/* placement */
|
|
if (mtex->projx) {
|
|
texvec[0] = mtex->size[0] * (vec[mtex->projx - 1] + mtex->ofs[0]);
|
|
}
|
|
else {
|
|
texvec[0] = mtex->size[0] * (mtex->ofs[0]);
|
|
}
|
|
|
|
if (mtex->projy) {
|
|
texvec[1] = mtex->size[1] * (vec[mtex->projy - 1] + mtex->ofs[1]);
|
|
}
|
|
else {
|
|
texvec[1] = mtex->size[1] * (mtex->ofs[1]);
|
|
}
|
|
|
|
if (mtex->projz) {
|
|
texvec[2] = mtex->size[2] * (vec[mtex->projz - 1] + mtex->ofs[2]);
|
|
}
|
|
else {
|
|
texvec[2] = mtex->size[2] * (mtex->ofs[2]);
|
|
}
|
|
|
|
/* texture */
|
|
if (tex->type == TEX_IMAGE) {
|
|
do_2d_mapping(mtex, texvec, NULL, dxt, dyt);
|
|
}
|
|
|
|
rgb = multitex(tex,
|
|
texvec,
|
|
dxt,
|
|
dyt,
|
|
0,
|
|
&texr,
|
|
thread,
|
|
mtex->which_output,
|
|
pool,
|
|
skip_load_image,
|
|
texnode_preview,
|
|
true);
|
|
|
|
if (rgb) {
|
|
texr.tin = IMB_colormanagement_get_luminance(&texr.tr);
|
|
}
|
|
else {
|
|
texr.tr = mtex->r;
|
|
texr.tg = mtex->g;
|
|
texr.tb = mtex->b;
|
|
}
|
|
|
|
*tin = texr.tin;
|
|
*tr = texr.tr;
|
|
*tg = texr.tg;
|
|
*tb = texr.tb;
|
|
*ta = texr.ta;
|
|
|
|
return (rgb != 0);
|
|
}
|