/****************************************************************************
* pigment.c
*
* This module implements solid texturing functions that modify the color
* transparency of an object's surface.
*
* from Persistence of Vision(tm) Ray Tracer
* Copyright 1996 Persistence of Vision Team
*---------------------------------------------------------------------------
* NOTICE: This source code file is provided so that users may experiment
* with enhancements to POV-Ray and to port the software to platforms other
* than those supported by the POV-Ray Team. There are strict rules under
* which you are permitted to use this file. The rules are in the file
* named POVLEGAL.DOC which should be distributed with this file. If
* POVLEGAL.DOC is not available or for more info please contact the POV-Ray
* Team Coordinator by leaving a message in CompuServe's Graphics Developer's
* Forum. The latest version of POV-Ray may be found there as well.
*
* This program is based on the popular DKB raytracer version 2.12.
* DKBTrace was originally written by David K. Buck.
* DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
*
*****************************************************************************/
/*
Some texture ideas garnered from SIGGRAPH '85 Volume 19 Number 3,
"An Image Synthesizer" By Ken Perlin.
Further Ideas Garnered from "The RenderMan Companion" (Addison Wesley).
*/
#include "frame.h"
#include "vector.h"
#include "povproto.h"
#include "texture.h"
#include "colour.h"
#include "image.h"
#include "matrices.h"
#include "pigment.h"
#include "txttest.h"
/*****************************************************************************
* Local preprocessor defines
******************************************************************************/
/*****************************************************************************
* Local typedefs
******************************************************************************/
/*****************************************************************************
* Local variables
******************************************************************************/
static BLEND_MAP_ENTRY Black_White_Entries[2] /* =
{{0.0, FALSE, {{0.0, 0.0, 0.0, 0.0, 0.0}}},
{1.0, FALSE, {{1.0, 1.0, 1.0, 0.0, 0.0}}}} */ ;
static BLEND_MAP Gray_Default_Map =
{ 2, FALSE, COLOUR_TYPE, -1, Black_White_Entries};
static BLEND_MAP_ENTRY Bozo_Entries[6] /* =
{{0.4, FALSE, {{1.0, 1.0, 1.0, 0.0, 0.0}}},
{0.4, FALSE, {{0.0, 1.0, 0.0, 0.0, 0.0}}},
{0.6, FALSE, {{0.0, 1.0, 0.0, 0.0, 0.0}}},
{0.6, FALSE, {{0.0, 0.0, 1.0, 0.0, 0.0}}},
{0.8, FALSE, {{0.0, 0.0, 1.0, 0.0, 0.0}}},
{0.8, FALSE, {{1.0, 0.0, 0.0, 0.0, 0.0}}}} */ ;
static BLEND_MAP Bozo_Default_Map =
{ 6, FALSE, COLOUR_TYPE, -1, Bozo_Entries};
static BLEND_MAP_ENTRY Wood_Entries[2] /* =
{{0.6, FALSE, {{0.666, 0.312, 0.2, 0.0, 0.0}}},
{0.6, FALSE, {{0.4, 0.1333, 0.066, 0.0, 0.0}}}} */ ;
static BLEND_MAP Wood_Default_Map =
{ 2, FALSE, COLOUR_TYPE, -1, Wood_Entries};
static BLEND_MAP_ENTRY Mandel_Entries[5] /* =
{{0.001, FALSE, {{0.0, 0.0, 0.0, 0.0, 0.0}}},
{0.001, FALSE, {{0.0, 1.0, 1.0, 0.0, 0.0}}},
{0.012, FALSE, {{1.0, 1.0, 0.0, 0.0, 0.0}}},
{0.015, FALSE, {{1.0, 0.0, 1.0, 0.0, 0.0}}},
{0.1, FALSE, {{0.0, 1.0, 1.0, 0.0, 0.0}}}} */ ;
static BLEND_MAP Mandel_Default_Map =
{ 5, FALSE, COLOUR_TYPE, -1, Mandel_Entries};
static BLEND_MAP_ENTRY Agate_Entries[6] /* =
{{0.0, FALSE, {{1.0, 1.0, 1.0, 0.0, 0.0}}},
{0.5, FALSE, {{0.95, 0.75, 0.5, 0.0, 0.0}}},
{0.5, FALSE, {{0.9, 0.7, 0.5, 0.0, 0.0}}},
{0.6, FALSE, {{0.9, 0.7, 0.4, 0.0, 0.0}}},
{0.6, FALSE, {{1.0, 0.7, 0.4, 0.0, 0.0}}},
{1.0, FALSE, {{0.6, 0.3, 0.0, 0.0, 0.0}}}} */ ;
static BLEND_MAP Agate_Default_Map =
{ 6, FALSE, COLOUR_TYPE, -1, Agate_Entries};
static BLEND_MAP_ENTRY Radial_Entries[4] /* =
{{0.0, FALSE, {{0.0, 1.0, 1.0, 0.0, 0.0}}},
{0.333, FALSE, {{1.0, 1.0, 0.0, 0.0, 0.0}}},
{0.666, FALSE, {{1.0, 0.0, 1.0, 0.0, 0.0}}},
{1.0, FALSE, {{0.0, 1.0, 1.0, 0.0, 0.0}}}} */ ;
static BLEND_MAP Radial_Default_Map =
{ 4, FALSE, COLOUR_TYPE, -1, Radial_Entries};
static BLEND_MAP_ENTRY Marble_Entries[3] /* =
{{0.0, FALSE, {{0.9, 0.8, 0.8, 0.0, 0.0}}},
{0.9, FALSE, {{0.9, 0.08, 0.08, 0.0, 0.0}}},
{0.9, FALSE, {{0.0, 0.0, 0.0, 0.0, 0.0}}}} */ ;
static BLEND_MAP Marble_Default_Map =
{ 3, FALSE, COLOUR_TYPE, -1, Marble_Entries};
static BLEND_MAP_ENTRY Brick_Entries[2] /* =
{{0.0, FALSE, {{0.5, 0.5, 0.5, 0.0, 0.0}}},
{1.0, FALSE, {{0.6, 0.15, 0.15, 0.0, 0.0}}}} */ ;
BLEND_MAP Brick_Default_Map =
{ 2, FALSE, COLOUR_TYPE, -1, Brick_Entries};
static BLEND_MAP_ENTRY Hex_Entries[3] /*=
{{0.0, FALSE, {{0.0, 0.0, 1.0, 0.0, 0.0}}},
{1.0, FALSE, {{0.0, 1.0, 0.0, 0.0, 0.0}}},
{2.0, FALSE, {{1.0, 0.0, 0.0, 0.0, 0.0}}}} */;
BLEND_MAP Hex_Default_Map =
{ 3, FALSE,COLOUR_TYPE, -1, Hex_Entries};
BLEND_MAP Check_Default_Map =
{ 2, FALSE,COLOUR_TYPE, -1, Hex_Entries}; /* Yes... Hex_Entries, not Check [CY] */
/*****************************************************************************
* Static functions
******************************************************************************/
static void Do_Average_Pigments PARAMS((COLOUR Colour, PIGMENT *Pigment, VECTOR EPoint));
/*****************************************************************************
*
* FUNCTION
*
* Create_Pigment
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* pointer to the created pigment
*
* AUTHOR
*
* POV-Ray Team
*
* DESCRIPTION : Allocate memory for new pigment and initialize it to
* system default values.
*
* CHANGES
*
******************************************************************************/
PIGMENT *Create_Pigment ()
{
PIGMENT *New;
New = (PIGMENT *)POV_MALLOC(sizeof (PIGMENT), "pigment");
Init_TPat_Fields((TPATTERN *)New);
Make_Colour(New->Colour, 0.0,0.0,0.0) ;
New->Blend_Map = NULL;
return (New);
}
/*****************************************************************************
*
* FUNCTION
*
* Copy_Pigment
*
* INPUT
*
* Old -- point to pigment to be copied
*
* RETURNS
*
* pointer to the created pigment
*
* AUTHOR
*
* POV-Ray Team
*
* DESCRIPTION : Allocate memory for new pigment and initialize it to
* values in existing pigment Old.
*
* CHANGES
*
******************************************************************************/
PIGMENT *Copy_Pigment (Old)
PIGMENT *Old;
{
PIGMENT *New;
if (Old != NULL)
{
New = Create_Pigment ();
Copy_TPat_Fields ((TPATTERN *)New, (TPATTERN *)Old);
if (Old->Type == PLAIN_PATTERN)
{
Assign_Colour(New->Colour,Old->Colour);
}
}
else
{
New = NULL;
}
return (New);
}
/*****************************************************************************
*
* FUNCTION
*
* Destroy_Pigment
*
* INPUT
*
* pointer to pigment to destroied
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* POV-Ray Team
*
* DESCRIPTION : free all memory associated with given pigment
*
* CHANGES
*
******************************************************************************/
void Destroy_Pigment (Pigment)
PIGMENT *Pigment;
{
if (Pigment != NULL)
{
Destroy_Pigment((PIGMENT *)Pigment->Next);
Destroy_TPat_Fields ((TPATTERN *)Pigment);
POV_FREE(Pigment);
}
}
/*****************************************************************************
*
* FUNCTION
*
* Post_Pigment
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* Chris Young
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/
int Post_Pigment(Pigment)
PIGMENT *Pigment;
{
int i, Has_Filter;
BLEND_MAP *Map;
if (Pigment == NULL)
{
Error("Missing pigment");
}
if (Pigment->Flags & POST_DONE)
{
return(Pigment->Flags & HAS_FILTER);
}
if (Pigment->Type == NO_PATTERN)
{
Pigment->Type = PLAIN_PATTERN;
Make_Colour(Pigment->Colour, 0.0, 0.0, 0.0) ;
Warning(1.5, "No pigment type given.\n");
}
Pigment->Flags |= POST_DONE;
switch (Pigment->Type)
{
case PLAIN_PATTERN:
Destroy_Warps (Pigment->Warps);
Pigment->Warps = NULL;
break;
case NO_PATTERN:
case BITMAP_PATTERN:
break;
default:
if (Pigment->Blend_Map == NULL)
{
switch (Pigment->Type)
{
case BOZO_PATTERN: Pigment->Blend_Map = &Bozo_Default_Map; break;
case BRICK_PATTERN: Pigment->Blend_Map = &Brick_Default_Map; break;
case WOOD_PATTERN: Pigment->Blend_Map = &Wood_Default_Map; break;
case MANDEL_PATTERN: Pigment->Blend_Map = &Mandel_Default_Map;break;
case RADIAL_PATTERN: Pigment->Blend_Map = &Radial_Default_Map;break;
case AGATE_PATTERN: Pigment->Blend_Map = &Agate_Default_Map; break;
case MARBLE_PATTERN: Pigment->Blend_Map = &Marble_Default_Map;break;
case HEXAGON_PATTERN: Pigment->Blend_Map = &Hex_Default_Map; break;
case CHECKER_PATTERN: Pigment->Blend_Map = &Check_Default_Map; break;
case AVERAGE_PATTERN: Error("Missing pigment_map in average pigment"); break;
default: Pigment->Blend_Map = &Gray_Default_Map; break;
}
}
break;
}
/* Now we test wether this pigment is opaque or not. [DB 8/94] */
Has_Filter = FALSE;
if ((fabs(Pigment->Colour[FILTER]) > EPSILON) ||
(fabs(Pigment->Colour[TRANSM]) > EPSILON))
{
Has_Filter = TRUE;
}
if ((Map = Pigment->Blend_Map) != NULL)
{
if (Map->Type == PIGMENT_TYPE)
{
for (i = 0; i < Map->Number_Of_Entries; i++)
{
Has_Filter |= Post_Pigment(Map->Blend_Map_Entries[i].Vals.Pigment);
}
}
else
{
for (i = 0; i < Map->Number_Of_Entries; i++)
{
Has_Filter |= fabs(Map->Blend_Map_Entries[i].Vals.Colour[FILTER])>EPSILON;
Has_Filter |= fabs(Map->Blend_Map_Entries[i].Vals.Colour[TRANSM])>EPSILON;
}
}
}
if (Has_Filter)
{
Pigment->Flags |= HAS_FILTER;
}
return(Has_Filter);
}
/*****************************************************************************
*
* FUNCTION
*
* Compute_Pigment
*
* INPUT
*
* Pigment - Info about this pigment
* EPoint - 3-D point at which pattern is evaluated
*
* OUTPUT
*
* Colour - Resulting color is returned here.
*
* RETURNS
*
* int - TRUE, if a color was found for the given point
* FALSE, if no color was found (e.g. areas outside an image map
* that has the once option)
*
* AUTHOR
*
* POV-Ray Team
*
* DESCRIPTION
* Given a 3d point and a pigment, compute colour from that layer.
* (Formerly called "Colour_At", or "Add_Pigment")
*
* CHANGES
* Added pigment map support [CY 11/94]
*
******************************************************************************/
int Compute_Pigment (Colour, Pigment, EPoint)
COLOUR Colour;
PIGMENT *Pigment;
VECTOR EPoint;
{
int Colour_Found;
VECTOR TPoint;
DBL value;
register DBL fraction;
BLEND_MAP_ENTRY *Cur, *Prev;
COLOUR Temp_Colour;
BLEND_MAP *Blend_Map = Pigment->Blend_Map;
if (Pigment->Type <= LAST_SPECIAL_PATTERN)
{
Colour_Found = TRUE;
switch (Pigment->Type)
{
case NO_PATTERN:
Make_Colour(Colour, 0.0, 0.0, 0.0);
break;
case PLAIN_PATTERN:
Assign_Colour(Colour,Pigment->Colour);
break;
case AVERAGE_PATTERN:
Warp_EPoint (TPoint, EPoint, (TPATTERN *)Pigment);
Do_Average_Pigments(Colour,Pigment,TPoint);
break;
case BITMAP_PATTERN:
Warp_EPoint (TPoint, EPoint, (TPATTERN *)Pigment);
Make_Colour(Colour, 0.0, 0.0, 0.0);
Colour_Found = image_map (TPoint, Pigment, Colour);
break;
default:
Error("Pigment type %d not yet implemented",Pigment->Type);
}
return(Colour_Found);
}
Colour_Found = FALSE;
value = Evaluate_TPat ((TPATTERN *)Pigment,EPoint);
Search_Blend_Map (value, Blend_Map, &Prev, &Cur);
if (Blend_Map->Type == COLOUR_TYPE)
{
Colour_Found = TRUE;
Assign_Colour(Colour, Cur->Vals.Colour);
}
else
{
Warp_EPoint (TPoint, EPoint, (TPATTERN *)Pigment);
if (Compute_Pigment(Colour, Cur->Vals.Pigment,TPoint))
{
Colour_Found = TRUE;
}
}
if (Prev != Cur)
{
if (Blend_Map->Type == COLOUR_TYPE)
{
Colour_Found = TRUE;
Assign_Colour(Temp_Colour, Prev->Vals.Colour);
}
else
{
if (Compute_Pigment(Temp_Colour, Prev->Vals.Pigment, TPoint))
{
Colour_Found = TRUE;
}
}
fraction = (value - Prev->value) / (Cur->value - Prev->value);
Colour[RED] = Temp_Colour[RED] + fraction * (Colour[RED] - Temp_Colour[RED]);
Colour[GREEN] = Temp_Colour[GREEN] + fraction * (Colour[GREEN] - Temp_Colour[GREEN]);
Colour[BLUE] = Temp_Colour[BLUE] + fraction * (Colour[BLUE] - Temp_Colour[BLUE]);
Colour[FILTER] = Temp_Colour[FILTER] + fraction * (Colour[FILTER] - Temp_Colour[FILTER]);
Colour[TRANSM] = Temp_Colour[TRANSM] + fraction * (Colour[TRANSM] - Temp_Colour[TRANSM]);
}
return(Colour_Found);
}
/*****************************************************************************
*
* FUNCTION
*
* INPUT
*
* OUTPUT
*
* RETURNS
*
* AUTHOR
*
* DESCRIPTION
*
* CHANGES
*
******************************************************************************/
static void Do_Average_Pigments (Colour, Pigment, EPoint)
COLOUR Colour;
PIGMENT *Pigment;
VECTOR EPoint;
{
int i;
COLOUR LC;
BLEND_MAP *Map = Pigment->Blend_Map;
SNGL Value;
SNGL Total = 0.0;
Make_Colour (Colour, 0.0, 0.0, 0.0);
for (i = 0; i < Map->Number_Of_Entries; i++)
{
Value = Map->Blend_Map_Entries[i].value;
Compute_Pigment (LC,Map->Blend_Map_Entries[i].Vals.Pigment,EPoint);
Colour[RED] += LC[RED] *Value;
Colour[GREEN] += LC[GREEN] *Value;
Colour[BLUE] += LC[BLUE] *Value;
Colour[FILTER]+= LC[FILTER]*Value;
Colour[TRANSM]+= LC[TRANSM]*Value;
Total += Value;
}
Colour[RED] /= Total;
Colour[GREEN] /= Total;
Colour[BLUE] /= Total;
Colour[FILTER]/= Total;
Colour[TRANSM]/= Total;
}
/*****************************************************************************
*
* FUNCTION Make_Pigment_Entries
*
* INPUT None
*
* OUTPUT Initializes default pigment blend_map values.
*
* RETURNS None
*
* AUTHOR Steve Demlow, Dec. '95
*
* DESCRIPTION Some pre-ANSI compilers won't auto-initialize unions, so these
* have to be done in regular code.
*
* CHANGES
*
******************************************************************************/
void Make_Pigment_Entries()
{
static unsigned char Made = FALSE;
if (Made) {
return;
}
Made = TRUE;
Make_Blend_Map_Entry(Black_White_Entries[0] , 0.0, FALSE, 0.0, 0.0, 0.0, 0.0, 0.0);
Make_Blend_Map_Entry(Black_White_Entries[1] , 1.0, FALSE, 1.0, 1.0, 1.0, 0.0, 0.0);
Make_Blend_Map_Entry(Bozo_Entries[0], 0.4, FALSE, 1.0, 1.0, 1.0, 0.0, 0.0);
Make_Blend_Map_Entry(Bozo_Entries[1], 0.4, FALSE, 0.0, 1.0, 0.0, 0.0, 0.0);
Make_Blend_Map_Entry(Bozo_Entries[2], 0.6, FALSE, 0.0, 1.0, 0.0, 0.0, 0.0);
Make_Blend_Map_Entry(Bozo_Entries[3], 0.6, FALSE, 0.0, 0.0, 1.0, 0.0, 0.0);
Make_Blend_Map_Entry(Bozo_Entries[4], 0.8, FALSE, 0.0, 0.0, 1.0, 0.0, 0.0);
Make_Blend_Map_Entry(Bozo_Entries[5], 0.8, FALSE, 1.0, 0.0, 0.0, 0.0, 0.0);
Make_Blend_Map_Entry(Wood_Entries[0], 0.6, FALSE, 0.666, 0.312, 0.2, 0.0, 0.0);
Make_Blend_Map_Entry(Wood_Entries[1], 0.6, FALSE, 0.4, 0.1333, 0.066, 0.0, 0.0);
Make_Blend_Map_Entry(Mandel_Entries[0], 0.001, FALSE, 0.0, 0.0, 0.0, 0.0, 0.0);
Make_Blend_Map_Entry(Mandel_Entries[1], 0.001, FALSE, 0.0, 1.0, 1.0, 0.0, 0.0);
Make_Blend_Map_Entry(Mandel_Entries[2], 0.012, FALSE, 1.0, 1.0, 0.0, 0.0, 0.0);
Make_Blend_Map_Entry(Mandel_Entries[3], 0.015, FALSE, 1.0, 0.0, 1.0, 0.0, 0.0);
Make_Blend_Map_Entry(Mandel_Entries[4], 0.1, FALSE, 0.0, 1.0, 1.0, 0.0, 0.0);
Make_Blend_Map_Entry(Agate_Entries[0], 0.0, FALSE, 1.0, 1.0, 1.0, 0.0, 0.0);
Make_Blend_Map_Entry(Agate_Entries[1], 0.5, FALSE, 0.95, 0.75, 0.5, 0.0, 0.0);
Make_Blend_Map_Entry(Agate_Entries[2], 0.5, FALSE, 0.9, 0.7, 0.5, 0.0, 0.0);
Make_Blend_Map_Entry(Agate_Entries[3], 0.6, FALSE, 0.9, 0.7, 0.4, 0.0, 0.0);
Make_Blend_Map_Entry(Agate_Entries[4], 0.6, FALSE, 1.0, 0.7, 0.4, 0.0, 0.0);
Make_Blend_Map_Entry(Agate_Entries[5], 1.0, FALSE, 0.6, 0.3, 0.0, 0.0, 0.0);
Make_Blend_Map_Entry(Radial_Entries[0], 0.0, FALSE, 0.0, 1.0, 1.0, 0.0, 0.0);
Make_Blend_Map_Entry(Radial_Entries[1], 0.333, FALSE, 1.0, 1.0, 0.0, 0.0, 0.0);
Make_Blend_Map_Entry(Radial_Entries[2], 0.666, FALSE, 1.0, 0.0, 1.0, 0.0, 0.0);
Make_Blend_Map_Entry(Radial_Entries[3], 1.0, FALSE, 0.0, 1.0, 1.0, 0.0, 0.0);
Make_Blend_Map_Entry(Marble_Entries[0], 0.0, FALSE, 0.9, 0.8, 0.8, 0.0, 0.0);
Make_Blend_Map_Entry(Marble_Entries[1], 0.9, FALSE, 0.9, 0.08, 0.08, 0.0, 0.0);
Make_Blend_Map_Entry(Marble_Entries[2], 0.9, FALSE, 0.0, 0.0, 0.0, 0.0, 0.0);
Make_Blend_Map_Entry(Brick_Entries[0], 0.0, FALSE, 0.5, 0.5, 0.5, 0.0, 0.0);
Make_Blend_Map_Entry(Brick_Entries[1], 1.0, FALSE, 0.6, 0.15, 0.15, 0.0, 0.0);
Make_Blend_Map_Entry(Hex_Entries[0], 0.0, FALSE, 0.0, 0.0, 1.0, 0.0, 0.0);
Make_Blend_Map_Entry(Hex_Entries[1], 1.0, FALSE, 0.0, 1.0, 0.0, 0.0, 0.0);
Make_Blend_Map_Entry(Hex_Entries[2], 2.0, FALSE, 1.0, 0.0, 0.0, 0.0, 0.0);
}