// Tiles, Bricks and other Patterns
// SimpleTiles.osl, by Zap Andersson
// Modified: 2019-11-25
// Copyright 2019 Autodesk Inc, All rights reserved. This file is licensed under Apache 2.0 license
//    https://github.com/ADN-DevTech/3dsMax-OSL-Shaders/blob/master/LICENSE.txt
 

#define PARAM lookup,fluvw+point(xx,0,0),GapWidth*0.05,Tiling,Edge*0.1,Bump,HitIdx

// Do a single rectangle		
int doRect(float loX, float hiX, float loY, float hiY, int Tile, int InIdx, float Radius,
	// Layout of these match the PARAM macro above
	point uvw, point baseuvw, float GapWidth, vector Tiling, float Edge, output float Bump, output int Index
)
{
	float x = uvw[0] / Tiling[0], y = uvw[1] / Tiling[1];	
	
	float lox = loX / Tiling[0];
	float hix = hiX / Tiling[0];	
	float loy = loY / Tiling[1];
	float hiy = hiY / Tiling[1];	
	

	// First quick rejection when being outside the rectangle	
	if (x < lox + GapWidth || 
	    x > hix - GapWidth || 
	    y < loy + GapWidth ||
	    y > hiy - GapWidth)
	    return 0;
	        
	// Turn coordinate into circle
	float gwr = GapWidth + Radius;	

	float lx = x - (lox + gwr);
	float ly = y - (loy + gwr);
	float hx = x - (hix - gwr);
	float hy = y - (hiy - gwr);

	// Now compute special-circle-coordinate
	// which is really the edges around the rectangle
	vector dp = vector(lx<0?lx:(hx>0?hx:0.0), ly<0?ly:(hy>0.0?hy:0.0), 0.0);	
	float  d = length(dp);	
	// Outside the radius - we missed
	if (d > Radius) return 0;

	// Inside? Compute the bump as a Smoothstep		
	if (Radius > Edge)	
		Bump = 1.0 - smoothstep(Radius-Edge, Radius+Edge, d);
	else
	{
		// If there is an Edge but the radius is smaller, we
		// need to compute the bump differently...
		float lx = x - (lox + GapWidth);
		float ly = y - (loy + GapWidth);
		float hx = x - (hix - GapWidth);
		float hy = y - (hiy - GapWidth);		
		Bump  = smoothstep(-Edge, Edge,  lx);
		Bump *= smoothstep(-Edge, Edge, -hx);
		Bump *= smoothstep(-Edge, Edge,  ly);
		Bump *= smoothstep(-Edge, Edge, -hy);
	}

	// Set index to inIdx
	Index = InIdx;
	// Return the tile ID we are in	
	return Tile;
}

shader SimpleTiles
	[[ string help  = "<font size=+1><b>Simple Tiles</b></font><br>"
		"Allows various kinds of Tiling. The Tiling Offset<br>"
		"modifies the base parameter of the tiling layout,<br>" 
		"and can generate different effects. ",
       string category = "Textures",
	   string label = "Simple Tiles" ]]
(
	// Inputs
	point UVW = point(u,v,0),
	
	float Scale   = 1.0
		[[ float min=0.000001, float max=1000000.0 ]], 
	int   TileMode  = 0 
		[[ string widget  = "mapper", 
		   string label   = "Tiling Mode",
		   string options = 
		       "Checker Tiles:1"
		       "|Running/Stack Bond:0"
		       "|English Bond:2"
		       "|Fine Running/Stack Bond:3"
		       "|Twist Box:4",
		   string help="Choose the kind of tile pattern to use. "
		               "The 'TilingOffset' parameter adjusts the "
		               "appearance of the tiles."
		   ]],		
	float U_Count = 2.0
		[[ string label = "Tiles in U",
  		   float min=0.000001, float max=1000000.0 ]], 
	float V_Count = 2.0	
		[[ string label = "Tiles in V",
  		   float min=0.000001, float max=1000000.0 ]], 
	float TilingOffset = 0.5
		[[ string label = "Tiling Offset",
  		   float min=0.0, float max=1.0,
  		   string help = "An adjustable parameter which changes "
   		      "the appearance of the tiling. Does different things "
  		      "for different Tiling Modes" ]], 
	float GapWidth   = 0.1
		[[ string label = "Gap Width",
		   string help  = "The width of the gap between tiles",
  		   float min=0.0, float max=10.0 ]], 
	float Radius  = 0.2
		[[ string label = "Corner Roundness",
		   string help  = "Adds fillet radius to the corner of the tiles",
  		   float min=0.0, float max=10.0 ]], 
	float Edge      = 0.1
		[[ string label = "Edge Width (for Bump)",
		   string help  = "The width of a (slightly beveled) fake 'Edge' "
		                  "to give the tile a more realistic appearance",
  		   float min=0.0, float max=10.0 ]], 	
	float EdgeBump = 1.0
		[[ float min=-10.0, float max=10.0,
		   string help  = "The amount of Edge bump effect" ]],	
	float ColorBump = 0.0
		[[ float min=-10.0, float max=10.0,
		   string help  = "Adds some contribution of the color "
		                  "settings to the Bump output" ]],		
	color GapColor   = 0.2
		[[ string label = "Gap Color", 
		   string help  = "The color of the gap between tiles" ]], 
	color TileColor1 = 0.4
		[[ string label = "Tile Color 1",
		   string help  = "First Tile Color"
		 ]], 
	color TileColor2 = 0.5
		[[ string label = "Tile Color 2",
		   string help  = "Second Tile Color"
		 ]], 
	color TileColor3 = 0.6
		[[ string label = "Tile Color 3",
		   string help  = "Third Tile Color. Not all Tiling Modes use this."		
		 ]], 
	
	// Outputs
	output color Col = 0.0,
	output float Bump = 0.0,
	output int   Tile = 0,
	output int   TileIdx = 0,
	output int   Index = 0	
)
{
	int   Overlap   = 1;	
	vector Tiling = vector(U_Count, V_Count, 0.0);
	Col = GapColor;
	point uvw = UVW * Tiling / Scale;
	float to = TilingOffset;
	float to2 = to*0.5;
	
	float Roundness = Radius / 10.0;

	// Floor of UVW	
	point fluvw = floor(uvw);
	// Fractional UVW
	point fruvw = uvw - fluvw;
	
	// Loop over neighbours
	for (int xx = -Overlap; xx <= 0; xx++)
	{
		point lookup = uvw - fluvw - point(xx,0,0);
		int   HitIdx = 0;
		// Running Bond
		if (TileMode == 0)
		{
			if (Tile == 0) 
				Tile = doRect(0.0 ,1.0, 0.0, 0.5, 1, 1, Roundness, PARAM);
			if (Tile == 0) 
				Tile = doRect(to, to+1.0, 0.5, 1.0, 2, 2, Roundness, PARAM);
		}
		// Stack Bond
		if (TileMode == 1)
		{
			if (Tile == 0) 
				Tile = doRect(0.0 ,0.5, 0.0, 0.5, 1, 1, Roundness, PARAM);
			if (Tile == 0) 
				Tile = doRect(0.0, 0.5, 0.5, 1.0, 2, 2, Roundness, PARAM);
			if (Tile == 0) 
				Tile = doRect(0.5 ,1.0, 0.0, 0.5, 2, 3, Roundness, PARAM);
			if (Tile == 0) 
				Tile = doRect(0.5, 1.0, 0.5, 1.0, 1, 4, Roundness, PARAM);
		}
		// English Bond
		if (TileMode == 2)
		{
			if (Tile == 0) 
				Tile = doRect(0.0 ,1.0, 0.0, 0.5, 1, 1, Roundness, PARAM);
			if (Tile == 0) 
				Tile = doRect(0.0+to2, 0.5+to2, 0.5, 1.0, 2, 2, Roundness, PARAM);
			if (Tile == 0) 
				Tile = doRect(0.5+to2, 1.0+to2, 0.5, 1.0, 2, 3, Roundness, PARAM);
		}
		// Fine Running Bond
		if (TileMode == 3)
		{
			if (Tile == 0) 
				Tile = doRect(0.0 ,1.0, 0.0, 0.5, 1, 1, Roundness, PARAM);
			if (Tile == 0) 
				Tile = doRect(0.0+to, 0.5+to, 0.5, 0.75, 2, 2, Roundness, PARAM);
			if (Tile == 0) 
				Tile = doRect(0.5+to, 1.0+to, 0.5, 0.75, 2, 3, Roundness, PARAM);
			if (Tile == 0) 
				Tile = doRect(0.0+to2, 0.5+to2, 0.75, 1.0, 3, 4, Roundness, PARAM);
			if (Tile == 0) 
				Tile = doRect(0.5+to2, 1.0+to2, 0.75, 1.0, 3, 5, Roundness, PARAM);
		}
		// Twist Box
		if (TileMode == 4)
		{
			if (Tile == 0) 
				Tile = doRect(0.0, 1.0-to2, 0.0, to2, 1, 1, Roundness, PARAM);
			if (Tile == 0) 
				Tile = doRect(to2, 1.0, 1.0-to2, 1.0, 1, 2, Roundness, PARAM);
			if (Tile == 0) 
				Tile = doRect(0.0, to2, to2, 1.0, 2, 3, Roundness, PARAM);
			if (Tile == 0) 
				Tile = doRect(1.0-to2, 1.0, 0.0, 1.0-to2, 2, 4, Roundness, PARAM);
			if (Tile == 0) 
				Tile = doRect(to2, 1.0-to2, to2, 1.0-to2, 3, 5, Roundness, PARAM);
		}
		if (HitIdx > 0)
		{
			TileIdx = HitIdx;
			Index = HitIdx + int((float)cellnoise(fluvw+point(xx,0,0)) * 1024.0);
		}
	}
		
	if (Tile == 1) Col = TileColor1;		
	if (Tile == 2) Col = TileColor2;
	if (Tile == 3) Col = TileColor3;
	
	Bump *= EdgeBump;	
	Bump += dot(0.3333, Col) * ColorBump;
}
