PHOTOSHOP PLUGINS
PHOTOSHOP: Socket Connection, Cookie Management System, HTTP I/O
PHOTOSHOP: Isometric Grid Generator
PHOTOSHOP: Texture Atlas Generator
UNITY
/*
// BEGIN__HARVEST_EXCEPTION_ZSTRING
Fotofuze...
6F17BFA7-EFC9-11EA-B960-7B96ED7EA713
in (PSHOP_ImageMode, RGBMode, CMYKMode, HSLMode, HSBMode, LabMode, RGB48Mode) && (PSHOP_IsTargetVisible && ! PSHOP_IsTargetSection)
filter
// END__HARVEST_EXCEPTION_ZSTRING
*/
#target photoshop
app.bringToFront();
/////////////////////////
// SETUP
/////////////////////////
var inputURL = "";
var requesthtml = "";
var redirectURL = "";
var fotofuzeDomain = "fotofuze.com";
var username = "";
var password = "";
var cookieFile= new File("~/Desktop/FotofuzeCookie.txt");
var cookieContents = {};
var now = new Date();
var monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
var dayNames = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
var timeOutRequests = 0;
if(app.documents.length == 0) {
alert("No Document Actively Open");
}
/////////////////////////
// FUNCTIONS
/////////////////////////
////////////////////////////////////////////////
// GENERATE A RANDOM NAME FOR TEMPORARY FILES
////////////////////////////////////////////////
function generateRandomName(){
var docName = app.activeDocument.name.replace(/\.[^\.]+$/, '');
var randomNumber = Math.floor((Math.random() * 100) + 1);
var randomlyGeneratedName = docName + randomNumber;
return randomlyGeneratedName;
};
///////////////////////////////////////////////
// CONSTRUCT CURRENT DATE INTO HEADER FORMAT
///////////////////////////////////////////////
function constructCurrentDate(monthInteger_and_fullYearFlag){
if(monthInteger_and_fullYearFlag == undefined || !monthInteger_and_fullYearFlag || monthInteger_and_fullYearFlag == false){
var currentDate = dayNames[now.getDay()] + ", " + now.getDate() + "-" + monthNames[now.getMonth()] + "-" + String(now.getFullYear()).substr(2,2) + " " + now.getHours()+":"+now.getMinutes()+":"+now.getSeconds() + " GMT";
}
else{
var currentDate = dayNames[now.getDay()] + ", " + now.getDate() + "-" + now.getMonth() + "-" + now.getFullYear() + " " + now.getHours()+":"+now.getMinutes()+":"+now.getSeconds() + " GMT";
}
return currentDate;
};
///////////////////////////////////////////////
// READ COOKIE FILE INTO COOKIE CONTENTS
///////////////////////////////////////////////
function readCookieFile(){
var cookieCache = "";
cookieFile.open('r');
cookieCache = cookieFile.read();
cookieFile.close();
parseCookie(cookieCache, cookieContents);
}
///////////////////////////
// PARSE RECIEVED COOKIE
///////////////////////////
function parseCookie(binary, cookieTarget){
var headerEnd = binary.indexOf("\n\n");
headerEnd = headerEnd == -1? binary.length: headerEnd;
var header = binary.substr(0,headerEnd);
//if(header.indexOf(fotofuzeDomain) > -1){ //make sure header comes from Fotofuze
var headerElements = header.split("\n");
for (var i = 0; i < headerElements.length; i++){
var token = headerElements[i];
if (token.indexOf("Set-Cookie: ") == 0){
var cookieToAdd = parseHeaderObject(token, "Set-Cookie: ");
var cookieValuePosition = cookieToAdd.indexOf("=");
var cookieKey = cookieToAdd.substr(0, cookieValuePosition);
var cookieValue = cookieToAdd.substr(cookieValuePosition);
cookieTarget[cookieKey] = cookieValue;
}
}
//}
};
////////////////////////////////////////////////
// ITERATE THROUGH COOKIES TO CHECK EXPIRATION
////////////////////////////////////////////////
function iterateThroughCookiesForExpirations(){
var expiredFlag = 0;
for (var cookie in cookieContents) {
if( cookieContents.hasOwnProperty(cookie) ){
expiredFlag += checkCookieExpiration(cookie);
}
}
if (expiredFlag > 0){
writeCookieFile();
}
}
///////////////////////////////////////////////
// COMPARE COOKIE EXPIRATION TO CURRENT DATE
///////////////////////////////////////////////
function checkCookieExpiration(Cookie){
var cookieExpiration = parseHeaderObject(cookieContents[Cookie], "expires=");
cookieExpiration = cookieExpiration.substr(5,11);
cookieExpiration = cookieExpiration.replace(/[`~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, ' ');
var cookieDate = new Date(cookieExpiration).getTime();
var currentDate = new Date().getTime();
if(currentDate > cookieDate){
delete cookieContents[Cookie];
return 1;
} else {return 0;}
};
///////////////////////////////
// WRITE COOKIE INFO TO FILE
///////////////////////////////
function writeCookieFile(){
var finalCookies = "";
for (var cookie in cookieContents) {
if( cookieContents.hasOwnProperty(cookie) ){
finalCookies += "Set-Cookie: " + cookie + cookieContents[cookie] + "\n";
}
}
cookieFile.open('w');
cookieFile.encoding = "UTF-8";
cookieFile.lineFeed = "Unix";
cookieFile.writeln(finalCookies);
cookieFile.close();
};
/////////////////////////////////////
// CONSTRUCT A COOKIE FOR POSTS
/////////////////////////////////////
function constructCookie(){
var cookieString = "";
for (var cookie in cookieContents) {
if( cookieContents.hasOwnProperty(cookie) ){
cookieString += "Set-Cookie: " + cookie + cookieContents[cookie] + "\n";
}
}
return cookieString;
};
/////////////////////////////////
// PARSE SPECIFIED HEADER DATA
/////////////////////////////////
function parseHeaderObject(headerObject, Tag, dataEndTag){
var Position = headerObject.indexOf(Tag) + Tag.length;
var Length;
if (!dataEndTag || dataEndTag == undefined){
Length = headerObject.length - Position;
}else{Length = headerObject.indexOf(dataEndTag, Position) - Position;}
var Data = headerObject.substr(Position, Length);
if(Data.charCodeAt(Data.length-1) == 13)
{
Data = Data.substr(0,Data.length - 1);
}
return Data;
}
/////////////////////////////////////
// CONSTRUCT A HEADER FOR REQUESTS
/////////////////////////////////////
function constructHeader(method, targetPath, attachedCookie, postLength){
var OriginAndContent= "\nUser-Agent: Photoshop" +
"\nContent-Type: application/x-www-form-urlencoded" +
"\nContent-Length: " + postLength +
"\nAccept: text/plain, text/html */*";
var theDate = "\nDate: " + constructCurrentDate();
return method + " " +
targetPath + " HTTP/1.0" +
"\nHost: " + fotofuzeDomain +
OriginAndContent +
attachedCookie +
theDate +
"\n\n";
};
////////////
// LOG-IN
////////////
function logIn(){
username = prompt("Please Enter your Fotofuze Username: ","Zephir62@Gmail.com");
var usernameEncoded = encodeURIComponent(username);
password = prompt("Please Enter your Fotofuze Password for " + username + ": ","ZEPHZEPH");
var passwordEncoded = encodeURIComponent(password);
var loginURL = "/auth/login?" + "user=" + usernameEncoded + "&pass=" + passwordEncoded;
Get(fotofuzeDomain, loginURL);
};
////////////////////////////
// POST REQUEST TO SERVER
////////////////////////////
function Post(domain, targetPath, attachedCookie, postBody){
var port = ":80";
var socket = new Socket;
//socket.timeout = 3;
if(postBody == undefined || !postBody){
postBody = "";
}
if(attachedCookie == undefined || !attachedCookie){
attachedCookie = "";
}
var Header = constructHeader("POST", targetPath, attachedCookie, postBody.length);
var post = Header + postBody;
alert("Post: " + post);
if (socket.open(domain + port, "binary")){
socket.write(post); // get the file
var binary = socket.read(99999999);
socket.close();
if(domain == fotofuzeDomain){
parseCookie(binary, cookieContents);
writeCookieFile();
}
}
};
///////////////////////////
// GET REQUEST TO SERVER
///////////////////////////
function Get(domain, targetPath, attachedCookie){
var port = ":80";
var socket = new Socket;
//socket.timeout = 3;
var postBody = "";
if(attachedCookie == undefined || !attachedCookie){
attachedCookie = "";
}
var Header = constructHeader("GET", targetPath, attachedCookie, postBody.length);
var get = Header;
alert("Get: " + get);
if (socket.open(domain + port, "binary")){
socket.write(get); // get the file
var binary = socket.read(99999999);
socket.close();
if(domain == fotofuzeDomain){
parseCookie(binary, cookieContents);
writeCookieFile();
}
//return binary;
}
};
/////////////////////////////////////////
// SAVE CURRENT DOCUMENT TO A PNG FILE
/////////////////////////////////////////
function saveDocument(){
var outputFolder = Folder.selectDialog("Select a folder for the output files");
var randomName = generateRandomName();
var outputFile = new File(outputFolder + "/" + randomName + ".png"); //"~/Desktop/"
var currentLayer = activeDocument.activeLayer;
var pngSaveOptions = new PNGSaveOptions();
pngSaveOptions.interlaced = false;
pngSaveOptions.compression = 9;
pngSaveOptions.transparency = false;
pngSaveOptions.PNG8 = false;
pngSaveOptions.includeProfile = false;
activeDocument.saveAs(outputFile, pngSaveOptions, true);
//outputFile.remove();
return outputFile; // SHOULD WE RETURN THE OUTPUT FILE?
};
/////////////////////////////////////////
// ASK FOTOFUZE IF IT HAS FINISHED YET
/////////////////////////////////////////
function checkProgress(){
$.sleep(5000);
var success = Get(fotofuzeDomain, "targetPath", constructCookie());
timeOutRequests += 1;
if(success){
//DO STUFF
} else {checkProgress();}
};
///////////////////////////
// GET IMAGE FROM SERVER
///////////////////////////
function getImage(url){
var html = "";
var request = "";
var binary = "";
var socket = new Socket;
var http = "http://";
var domain = "" // the domain for the file we want
var sImg = ""; // the rest of the url for the file we want
var port = ":80"; // the port for the file we want
// Check for Redirects
function checkRedirect(binary){
if (binary.indexOf("Location: ") > 1){
redirectURL = parseHeaderObject(binary, "Location: ");
return true;
} else { return false; }
};
// Remove header lines from HTTP response
function removeHeaders(binary){
var bContinue = true ; // flag for finding end of header
var line = "";
var httpheader = "";
var nFirst = 0;
var count = 0;
while (bContinue) {
line = getLine(binary) ; // each header line
httpheader = httpheader + line;
bContinue = line.length >= 2 ; // blank header == end of header
nFirst = line.length + 1 ;
binary = binary.substr(nFirst) ;
}
if (httpheader.indexOf("Bad Request") != -1 || httpheader.indexOf("Not Found") != -1) {
alert ("RequestHTML: " + requesthtml + " Request: " + request + " HTTPHeader: " + httpheader);
var binary = "";
}
return binary;
};
// Get a response line from the HTML
function getLine(html){
var line = "" ;
for (var i = 0; html.charCodeAt(i) != 10; i++){ // finding line end
line += html[i] ;
}
return line ;
};
// Place image into Photoshop Layer
function placeImage(fileRef){
//create a new smart object layer using a file
try {
var desc = new ActionDescriptor();
desc.putPath( charIDToTypeID( "null" ), new File( fileRef ) );
desc.putEnumerated( charIDToTypeID( "FTcs" ), charIDToTypeID( "QCSt" ), charIDToTypeID( "Qcsa" ) );
desc.putBoolean( charIDToTypeID( "Lnkd" ), true );
executeAction( charIDToTypeID( "Plc " ), desc, DialogModes.NO );
activeDocument.activeLayer.merge(); //merge new layer with the one below it
} catch (e) { alert("Placeing file: '" + fileRef + "' failed" + e); }
};
/////////////////////////
// MAIN
/////////////////////////
if (url != null && url != ""){
if ( (url.indexOf(http) != -1) || (url.indexOf(http.toUpperCase()) != -1) ) {
domainPathLength = url.length - http.length;
domainPath = url.substr(http.length, domainPathLength);
pathOffset = domainPath.indexOf("/");
domain = domainPath.substr(0, pathOffset);
sImgPath = domainPath.substr(pathOffset, domainPath.length - pathOffset );
if ( domain != "" && sImgPath != "" && sImgPath != "/" ) { //&& Name.indexOf(".") != -1
var theDate = "\nDate: " + constructCurrentDate();
requesthtml ="\n\nDmain:" + domain + " Port" + port + " binary\n";
request = "GET " +
sImgPath +" HTTP/1.0" +
"\nHost: " + domain +
"\nAccept: image/gif, image/x-xbitmap, image/png, image/jpeg, */*" +
theDate +
"\nUser-Agent: Photoshop" +
"\nContent-Type: application/x-www-form-urlencoded" +
"\n\n";
if (socket.open(domain + port, "binary")){
socket.write(request); // get the file
var binary = socket.read(99999999);
socket.close();
if(domain == fotofuzeDomain){
parseCookie(binary, cookieContents); // DO WE NEED TO RE-WRITE COOKIE?
writeCookieFile();
}
if (checkRedirect(binary) == true){
return;
}
var randomName = generateRandomName();
var fileExtension = sImgPath;
while (fileExtension.indexOf(".") != -1 ) { // Strip Path
fileExtension = fileExtension.substr(fileExtension.indexOf(".") + 1 ,);
}
var f = File("~/" + randomName + "." + fileExtension); // Image file name
f.encoding = "binary"; // set binary mode
f.open("w");
binary = removeHeaders(binary);
f.write(binary);
f.close();
if (binary.length != 0) {
placeImage( f );
}
f.remove(); // Remove temporary downloaded files
} else { alert("Connection to Domain:" + domain + " Port" + port + " Failed " + url);}
} else { alert("Invalid Image URL: " + url ); }
} else { alert("Invalid URL: " + url ); }
} else { if ( url == "" ) alert("No URL Entered"); }
};
// This script will create a new Isometric Grid of various dimensions on it's own art layer.
//
// Author: Matt Olick
// Version: 1.0.1
// Date: April 2020
// Homepage: http://www.mattOlick.com/
//
// This script is released under the GPLv3 Software License. You are free to use it for free or commercial purposes.
// If you make any modifications or improvements to this script, please do share them back with us.
/*
// BEGIN__HARVEST_EXCEPTION_ZSTRING
Isometric Grid Generator
6F17BFA7-EFC9-11EA-B960-7B96ED7EA770
in (PSHOP_ImageMode, RGBMode, CMYKMode, HSLMode, HSBMode, LabMode, RGB48Mode) && (PSHOP_IsTargetVisible && ! PSHOP_IsTargetSection)
filter
// END__HARVEST_EXCEPTION_ZSTRING
*/
#target photoshop
app.bringToFront();
/////////////////////////
// SETUP
/////////////////////////
const ScriptTitle = "Isometric Grid Generator";
const UserOptionsKey = "isoGridGen_UserOptions";
const UserOptionsWidthKey = "gridWidthSize";
const UserOptionsHeightKey = "gridHeightSize";
const UserOptionsResizeKey = "matchGridResize";
function Ruler_savePrefs()
{
prevRulerUnits = app.preferences.rulerUnits;
}
function Ruler_setPixels()
{
app.preferences.rulerUnits = Units.PIXELS;
}
function Ruler_setPoints()
{
app.preferences.rulerUnits = Units.POINTS;
}
function Ruler_returnPrefs()
{
app.preferences.rulerUnits = prevRulerUnits;
}
/////////////////////////
// MAIN
/////////////////////////
if(app.documents.length == 0) {
alert("No Document Actively Open");
}
else{
main();
}
function main()
{
Ruler_savePrefs();
// Get the user's input options
var userOptions = getUserOptions();
if ( userOptions == false ) {
return;
}
var srcDoc = app.activeDocument;
// Make sure to match ppi to 72, to match Points Ruler
if(srcDoc.resolution != 72) { srcDoc.resizeImage(undefined, undefined, 72, ResampleMethod.NONE); }
var targetLayer = srcDoc.artLayers.add();
Ruler_setPixels();
gridWidth = userOptions[UserOptionsWidthKey];
gridHeight = userOptions[UserOptionsHeightKey];
Canvas_setSize(srcDoc);
Ruler_setPoints();
//gridWidth = userOptions[UserOptionsWidthKey]*(72/srcDoc.resolution);
//gridHeight = userOptions[UserOptionsHeightKey]*(72/srcDoc.resolution);
createGrid(srcDoc, targetLayer);
if(userOptions[UserOptionsResizeKey] == true)
{
Ruler_setPixels();
Canvas_returnSize(srcDoc);
}
Ruler_returnPrefs();
}
function Canvas_setSize(srcDoc)
{
prevWidth = srcDoc.width;
prevHeight = srcDoc.height;
var newWidth = prevWidth;
var newHeight = prevHeight;
var remainderWidth = parseInt( srcDoc.width % gridWidth );
var remainderHeight = parseInt( srcDoc.height % gridHeight );
if(remainderWidth > 0)
{ newWidth += gridWidth - remainderWidth; }
if(remainderHeight > 0)
{ newHeight += gridHeight - remainderHeight; }
srcDoc.resizeCanvas( UnitValue(newWidth,"px"), UnitValue(newHeight,"px") );
}
function Canvas_returnSize(srcDoc)
{
srcDoc.resizeCanvas( UnitValue(prevWidth,"px"), UnitValue(prevHeight,"px") );
}
/////////////////////////
// USER OPTIONS
/////////////////////////
function putActionDescValue(desc, method, key, value)
{
if ( key == undefined || value == undefined ) {
return;
}
desc[method](app.stringIDToTypeID(key), value);
}
function getActionDescValue(desc, method, key, defaultValue)
{
if ( desc.hasKey(app.stringIDToTypeID(key)) ) {
return desc[method](app.stringIDToTypeID(key));
}
return defaultValue;
}
function storeLastUserOptions(userOptions)
{
var desc = new ActionDescriptor();
putActionDescValue(desc, "putInteger", UserOptionsWidthKey, userOptions[UserOptionsWidthKey]);
putActionDescValue(desc, "putInteger", UserOptionsHeightKey, userOptions[UserOptionsHeightKey]);
putActionDescValue(desc, "putBoolean", UserOptionsResizeKey, userOptions[UserOptionsResizeKey]);
app.putCustomOptions( UserOptionsKey, desc, true );
}
function retrieveLastUserOptions()
{
try {
var desc = app.getCustomOptions(UserOptionsKey);
} catch( e ) { return { }; }
var result = { };
result[UserOptionsWidthKey] = getActionDescValue(desc, "getInteger", UserOptionsWidthKey, 0);
result[UserOptionsHeightKey] = getActionDescValue(desc, "getInteger", UserOptionsHeightKey, 0);
result[UserOptionsResizeKey] = getActionDescValue(desc, "getBoolean", UserOptionsResizeKey, false);
return result;
}
function indexOf(array, obj)
{
for( var i = 0; i < array.length; ++i )
{
if ( array[i] == obj ) {
return i;
}
}
return -1;
}
function getUserOptions()
{
var lastUserOptions = retrieveLastUserOptions();
var sizeItems = [8, 16, 32, 64, 128, 256, 512];
var sizeItemsStrings = ["8", "16", "32", "64", "128", "256", "512"];
var dlg = new Window('dialog', ScriptTitle);
dlg.widthSizeGrp = dlg.add('group', undefined, );
dlg.widthSizeGrp.orientation = "row";
dlg.widthSizeGrp.alignment = 'left';
dlg.widthSizeGrp.dropDownWidthSize = dlg.widthSizeGrp.add('DropDownList', undefined, sizeItemsStrings);
dlg.widthSizeGrp.dropDownWidthSize.text = "Width Size: ";
dlg.widthSizeGrp.dropDownWidthSize.selection = 0;
dlg.heightSizeGrp = dlg.add('group', undefined, );
dlg.heightSizeGrp.orientation = "row";
dlg.heightSizeGrp.alignment = 'left';
dlg.heightSizeGrp.dropDownHeightSize = dlg.heightSizeGrp.add('DropDownList', undefined, sizeItemsStrings);
dlg.heightSizeGrp.dropDownHeightSize.text = "Height Size: ";
dlg.heightSizeGrp.dropDownHeightSize.selection = 0;
dlg.resizeGrp = dlg.add('group');
dlg.resizeGrp.orientation = 'row';
dlg.resizeGrp.alignment = 'left';
dlg.resizeGrp.resizeCheck = dlg.resizeGrp.add('CheckBox', undefined, "Return Canvas to Non-Grid Size?");
dlg.buttonGrp = dlg.add('group');
dlg.buttonGrp.okButton = dlg.buttonGrp.add('button', undefined, 'OK');
dlg.buttonGrp.cancelButton = dlg.buttonGrp.add('button', undefined, 'Cancel');
// Before presenting, update from user options (if applicable)
if ( lastUserOptions[UserOptionsWidthKey] > 0 )
{
dlg.widthSizeGrp.dropDownWidthSize.selection = indexOf(sizeItems, lastUserOptions[UserOptionsWidthKey]);
}
if ( lastUserOptions[UserOptionsHeightKey] > 0 )
{
dlg.heightSizeGrp.dropDownHeightSize.selection = indexOf(sizeItems, lastUserOptions[UserOptionsHeightKey]);
}
if ( lastUserOptions[UserOptionsResizeKey] !== undefined )
{
dlg.resizeGrp.resizeCheck.value = lastUserOptions[UserOptionsResizeKey];
}
dlg.center();
var dlgResult = dlg.show(); // 1 = ok, 2 = cancel
// Note: .selection as a string is the value in the drop down, as an int it's the item's index!
var userOptions = { };
userOptions[UserOptionsWidthKey] = parseInt(sizeItems[parseInt(dlg.widthSizeGrp.dropDownWidthSize.selection)]);
userOptions[UserOptionsHeightKey] = parseInt(sizeItems[parseInt(dlg.heightSizeGrp.dropDownHeightSize.selection)]);
userOptions[UserOptionsResizeKey] = dlg.resizeGrp.resizeCheck.value;
storeLastUserOptions(userOptions);
if ( dlgResult == 2 ) {
return false;
}
return userOptions;
}
/////////////////////////
// LOGIC
/////////////////////////
function createGrid(srcDoc, targetLayer)
{
//Photoshop counts from Top-Left corner
var totalX_iterations = (srcDoc.width - gridWidth) / gridWidth;
var totalY_iterations = (srcDoc.height - gridHeight) / gridHeight;
var gridBegin_x = gridWidth/2;
var gridBegin_y = gridHeight/2;
//left -- bottom to top -- downslope
for(var y = totalY_iterations; y>= 0;y--)
{
var startX = parseInt(0);
var startY = parseInt(gridBegin_y + gridHeight*y);
var endX = parseInt(gridBegin_x + gridWidth*(totalY_iterations-y));
var endY = parseInt(srcDoc.height);
drawLine( startX,
startY,
endX,
endY, srcDoc);
}
//top -- left to right -- downslope
for(var x = 0; x<= totalX_iterations;x++)
{
var startX = parseInt(gridBegin_x + gridWidth*x);
var startY = parseInt(0);
var endX = parseInt(gridBegin_x + (totalY_iterations+1 + x)*gridWidth);
var endY = parseInt(srcDoc.height);
drawLine( startX,
startY,
endX,
endY, srcDoc);
}
//left -- top to bottom -- upslope
for(var y = 0; y<= totalY_iterations;y++)
{
var startX = parseInt(0);
var startY = parseInt(gridBegin_y + gridHeight*y);
var endX = parseInt(gridBegin_x + gridWidth*y);
var endY = parseInt(0);
drawLine( startX,
startY,
endX,
endY, srcDoc);
}
//bottom -- left to right -- upslope
for(var x = 0; x<= totalX_iterations;x++)
{
var startX = parseInt(gridBegin_x + gridWidth*x);
var startY = parseInt(srcDoc.height);
var endX = parseInt(gridBegin_x + (totalY_iterations+1 + x)*gridWidth);
var endY = parseInt(0);
drawLine( startX,
startY,
endX,
endY, srcDoc);
}
}
function drawLine(startX, startY, endX, endY, srcDoc)
{
//alert("sX " + startX + "sY " + startY + "eX " + endX + "eY " + endY );
var startPoint = new PathPointInfo();
startPoint.kind = PointKind.CORNERPOINT;
startPoint.anchor = [startX, startY];
startPoint.leftDirection = startPoint.anchor;
startPoint.rightDirection = startPoint.anchor;
var endPoint = new PathPointInfo();
endPoint.kind = PointKind.CORNERPOINT;
endPoint.anchor = [endX, endY];
endPoint.leftDirection = endPoint.anchor;
endPoint.rightDirection = endPoint.anchor;
var lineSubPath = new SubPathInfo();
lineSubPath.operation = ShapeOperation.SHAPEXOR;
lineSubPath.closed = false;
lineSubPath.entireSubPath = [startPoint, endPoint];
var gridLine = srcDoc.pathItems.add("GridLine", [lineSubPath]);
srcDoc.pathItems[0].subPathItems[0].pathPoints[0].anchor = [0, 0];
//alert(lineSubPath.entireSubPath[0].anchor);
//alert(gridLine.subPathItems[0].pathPoints[0].anchor);
gridLine.strokePath(ToolType.PENCIL);
gridLine.remove();
}
// This script will export a texture atlas as a new document given either a set of documents or a layered document.
//
// Author: Matt Olick
// Version: 1.0.2
// Date: June 2018
// Homepage: http://www.mattOlick.com/
//
// This script is released under the GPLv3 Software License. You are free to use it for free or commercial purposes.
// If you make any modifications or improvements to this script, please do share them back with us.
/*
// BEGIN__HARVEST_EXCEPTION_ZSTRING
Texture Atlas Generator
6F17BFA7-EFC9-11EA-B960-7B96ED7EA769
in (PSHOP_ImageMode, RGBMode, CMYKMode, HSLMode, HSBMode, LabMode, RGB48Mode) && (PSHOP_IsTargetVisible && ! PSHOP_IsTargetSection)
filter
// END__HARVEST_EXCEPTION_ZSTRING
*/
#target photoshop
app.bringToFront();
/////////////////////////
// SETUP
/////////////////////////
const ScriptTitle = "Texture Atlas Generator";
const UserOptionsKey = "texAtlasGen_UserOptions";
const UserOptionsDocNameKey = "documentName";
const UserOptionsSourceKey = "Current Document Layers";
const UserOptionsLayerSetKey = "layerSetIndex";
const UserOptionsResizeKey = "mipMapping";
const UserOptionsSourceAllDocs = 0;
const UserOptionsSourceDocument = 1;
const UserOptionsSourceLayerSet = 2;
const UserOptionsSourceFrameAnim = 3;
function Ruler_setPixels()
{
prevRulerUnits = app.preferences.rulerUnits;
app.preferences.rulerUnits = Units.PIXELS;
}
function Ruler_returnPrefs()
{
app.preferences.rulerUnits = prevRulerUnits;
}
/////////////////////////
// MAIN
/////////////////////////
if(app.documents.length == 0) {
alert("No Document Actively Open");
}
else{
main();
}
function main()
{
Ruler_setPixels();
var srcDoc = app.activeDocument;
// Fix for broken resolution images
if(srcDoc.resolution != 72) { srcDoc.resizeImage(undefined, undefined, 72, ResampleMethod.NONE); }
// Get the user's input options
var userOptions = getUserOptions();
if ( userOptions == false ) {
return;
}
var destDoc = createDestDoc(userOptions, srcDoc);
returnAllCanvasSizes(destDoc);
//Remove white base layer created with new documents
app.activeDocument = destDoc;
destDoc.layers[destDoc.layers.length-1].remove();
layoutLayersAsSprites(destDoc, userOptions);
Ruler_returnPrefs();
}
/////////////////////////
// USER OPTIONS
/////////////////////////
function putActionDescValue(desc, method, key, value)
{
if ( key == undefined || value == undefined ) {
return;
}
desc[method](app.stringIDToTypeID(key), value);
}
function getActionDescValue(desc, method, key, defaultValue)
{
if ( desc.hasKey(app.stringIDToTypeID(key)) ) {
return desc[method](app.stringIDToTypeID(key));
}
return defaultValue;
}
function storeLastUserOptions(userOptions)
{
var desc = new ActionDescriptor();
putActionDescValue(desc, "putString", UserOptionsDocNameKey, userOptions[UserOptionsDocNameKey]);
putActionDescValue(desc, "putInteger", UserOptionsSourceKey, userOptions[UserOptionsSourceKey]);
putActionDescValue(desc, "putInteger", UserOptionsLayerSetKey, userOptions[UserOptionsLayerSetKey]);
putActionDescValue(desc, "putBoolean", UserOptionsResizeKey, userOptions[UserOptionsResizeKey]);
app.putCustomOptions( UserOptionsKey, desc, true );
}
function retrieveLastUserOptions()
{
try {
var desc = app.getCustomOptions(UserOptionsKey);
} catch( e ) { return { }; }
var result = { };
result[UserOptionsDocNameKey] = getActionDescValue(desc, "getString", UserOptionsDocNameKey, "");
result[UserOptionsSourceKey] = getActionDescValue(desc, "getInteger", UserOptionsSourceKey, 0);
result[UserOptionsLayerSetKey] = getActionDescValue(desc, "getInteger", UserOptionsLayerSetKey, 0);
result[UserOptionsResizeKey] = getActionDescValue(desc, "getBoolean", UserOptionsResizeKey, false);
return result;
}
function getUserOptions()
{
var lastUserOptions = retrieveLastUserOptions();
var sourceItems = ["All Open Documents", "Current Document Layers", "Specific Layer Group", "Frame Animation"];
var layerSetItems = [];
var documentItems = [];
for( i = 0; i < app.activeDocument.layerSets.length; i++ ) {
var layerSet = app.activeDocument.layerSets[i];
layerSetItems[layerSetItems.length] = layerSet.name;
}
for( i = 0; i < app.documents.length; i++ ) {
var documentSet = app.documents[i];
documentItems[documentItems.length] = documentSet.name;
}
if ( layerSetItems.length == 0 ) { layerSetItems[0] = "No Layer Groups Available"; }
if ( documentItems.length == 0 ) { documentItems[0] = "No Documents Available"; }
var dlg = new Window('dialog', ScriptTitle);
dlg.sourceGrp = dlg.add('group', undefined);
dlg.sourceGrp.orientation = "column";
dlg.sourceGrp.alignment = "left";
dlg.sourceGrp.dropDownSource = dlg.sourceGrp.add('DropDownList', undefined, sourceItems);
dlg.sourceGrp.dropDownSource.text = "Source: ";
dlg.sourceGrp.dropDownSource.selection = 0;
dlg.sourceGrp.dropDownSource.onChange = function() {
userOptionsDlg_updateControls(dlg);
};
dlg.sourceGrp.dropDownLayerSet = dlg.sourceGrp.add('DropDownList', undefined, layerSetItems);
dlg.sourceGrp.dropDownLayerSet.text = "Layer Group: ";
dlg.sourceGrp.dropDownLayerSet.selection = 0;
dlg.resizeGrp = dlg.add('group');
dlg.resizeGrp.orientation = 'row';
dlg.resizeGrp.alignment = 'left';
dlg.resizeGrp.resizeCheck = dlg.resizeGrp.add('CheckBox', undefined, "Use Powers of 2 for MipMapping?");
dlg.buttonGrp = dlg.add('group');
dlg.buttonGrp.okButton = dlg.buttonGrp.add('button', undefined, 'OK');
dlg.buttonGrp.cancelButton = dlg.buttonGrp.add('button', undefined, 'Cancel');
// Before presenting, update from user options (if applicable)
if ( lastUserOptions[UserOptionsSourceKey] > 0 ) { dlg.sourceGrp.dropDownSource.selection = lastUserOptions[UserOptionsSourceKey]; }
if ( (lastUserOptions[UserOptionsDocNameKey] == app.activeDocument.name) && (lastUserOptions[UserOptionsLayerSetKey] < app.activeDocument.layerSets.length) ) {
dlg.sourceGrp.dropDownLayerSet.selection = lastUserOptions[UserOptionsLayerSetKey];
}
if ( lastUserOptions[UserOptionsResizeKey] !== undefined )
{
dlg.resizeGrp.resizeCheck.value = lastUserOptions[UserOptionsResizeKey];
}
userOptionsDlg_updateControls(dlg);
dlg.center();
var dlgResult = dlg.show(); // 1 = ok, 2 = cancel
// Note: .selection as a string is the value in the drop down, as an int it's the item's index!
var userOptions = { };
userOptions[UserOptionsDocNameKey] = app.activeDocument.name;
userOptions[UserOptionsSourceKey] = parseInt(dlg.sourceGrp.dropDownSource.selection);
userOptions[UserOptionsLayerSetKey] = parseInt(dlg.sourceGrp.dropDownLayerSet.selection);
userOptions[UserOptionsResizeKey] = dlg.resizeGrp.resizeCheck.value;
storeLastUserOptions(userOptions);
if ( dlgResult == 2 ) {
return false;
}
return userOptions;
}
function userOptionsDlg_updateControls(dlg)
{
if ( dlg.sourceGrp.dropDownSource.selection == UserOptionsSourceLayerSet ) {
dlg.sourceGrp.dropDownLayerSet.visible = true;
dlg.sourceGrp.dropDownLayerSet.text = "Layer Group: ";
} else {
dlg.sourceGrp.dropDownLayerSet.visible = false;
dlg.sourceGrp.dropDownLayerSet.text = "";
}
}
/////////////////////////
// LOGIC
/////////////////////////
function createDestDoc(userOptions, srcDoc)
{
if ( userOptions === false ) {
return false;
}
var sourceType = userOptions[UserOptionsSourceKey];
var layerSetIndex = userOptions[UserOptionsLayerSetKey];
var destDoc = false;
destDoc = app.documents.add(srcDoc.width, srcDoc.height, srcDoc.resolution, srcDoc.name + " Sheet");
if ( sourceType == UserOptionsSourceAllDocs )
{
var biggestCanvasSize = findBiggestCanvas();
matchAllCanvasSizes(biggestCanvasSize);
copy_perDocument(destDoc);
}
else if ( sourceType == UserOptionsSourceDocument )
{
copy_perLayer(srcDoc, destDoc, srcDoc.layers);
}
else if ( sourceType == UserOptionsSourceLayerSet )
{
if ( layerSetIndex >= srcDoc.layerSets.length ) {
alert ("The 'Specific Layer Set' option was selected, but there is no selected layer set in the open document.");
}
if ( srcDoc.layerSets[layerSetIndex].layers.length == 0 ) {
alert ("The selected layer set contains no layers.");
}
copy_perLayer(srcDoc, destDoc, srcDoc.layerSets[layerSetIndex].layers);
}
else if (sourceType == UserOptionsSourceFrameAnim)
{
copy_perFrame(srcDoc, destDoc);
}
return destDoc;
}
function findBiggestCanvas()
{
var biggestCanvas = {width: 0, height: 0};
// Find and return the biggest canvas size of all open documents
for( var i = 0; i < app.documents.length; i++ )
{
// Fix for broken resolution images
app.activeDocument = app.documents[i];
app.documents[i].resizeImage(undefined, undefined, 72, ResampleMethod.NONE);
// Compare canvas sizes to find the biggest size
if(app.documents[i].width > biggestCanvas.width)
{
biggestCanvas.width = app.documents[i].width;
}
if(app.documents[i].height > biggestCanvas.height)
{
biggestCanvas.height = app.documents[i].height;
}
}
return biggestCanvas;
}
function matchAllCanvasSizes(biggestCanvasSize)
{
prevSize = new Array();
for( var i = 0; i < app.documents.length; i++ )
{
app.activeDocument = app.documents[i];
prevSize[i] = {width: app.documents[i].width, height: app.documents[i].height};
app.documents[i].resizeCanvas(biggestCanvasSize.width, biggestCanvasSize.height);
}
}
function returnAllCanvasSizes(destDoc)
{
for( var i = 0; i < app.documents.length; i++ )
{
if(app.documents[i] != destDoc)
{
app.activeDocument = app.documents[i];
app.documents[i].resizeCanvas(prevSize[i].width, prevSize[i].height);
}
}
}
function copy_perDocument(destDoc)
{
// Copy Merged Documents Layers across to new image Texture Atlas
for( var i = 0; i < app.documents.length; i++ )
{
if(app.documents[i] != destDoc)
{
app.activeDocument = app.documents[i];
processLayers(destDoc);
}
}
}
function copy_perLayer(srcDoc, destDoc, layers)
{
app.activeDocument = srcDoc;
if ( srcDoc == undefined || destDoc == undefined || layers == undefined ) {
alert ("source, destination, or layers were undefined.");
}
var wasVisible = [];
var wasParentVisible = undefined;
// Pre process
for( var i = 0; i < layers.length; i++ ) {
wasVisible[i] = layers[i].visible;
}
if ( layers[0].parent.visible !== undefined ) {
wasParentVisible = layers[0].parent.visible;
layers[0].parent.visible = true;
}
// Do the work
for( var i = layers.length-1; i > -1; i-- )
{
app.activeDocument = srcDoc;
// Don't process background layer as it causes issues (probably don't want it as a sprite anyway)
if ( layers[i].isBackgroundLayer == true ) {
continue;
}
// Turn off all layers (but don't mess with the background layer)
for( var j = 0; j < layers.length; j++ ) {
if ( layers[j].isBackgroundLayer == false ) {
layers[j].visible = false;
}
}
// Turn on active layer
layers[i].visible = true;
// Move to Texture Atlas
processLayers(destDoc);
}
// Undo Pre-Process
app.activeDocument = srcDoc;
for( var i = 0; i < layers.length; i++ ) {
layers[i].visible = wasVisible[i];
}
if ( layers[0].parent.visible !== undefined ) {
layers[0].parent.visible = wasParentVisible;
}
}
function copy_perFrame(srcDoc, destDoc)
{
app.activeDocument = srcDoc;
var frameCount = getFrameCount();
if ( frameCount == 0 ) {
alert ("No animation frames were found in this file.");
}
else {
// Copy Merged Frames across to new image
for( var i = 1; i <= frameCount; i++ )
{
app.activeDocument = srcDoc;
goToFrame(i);
processLayers(destDoc);
}
}
}
function processLayers(destDoc)
{
selectAllLayers();
duplicateSelectedLayers();
if( (app.activeDocument.layers.length > 2 || app.activeDocument.layerSets.length > 1) )
{
mergeSelectedLayers();
}
var layer = app.activeDocument.activeLayer.duplicate(destDoc, ElementPlacement.PLACEATEND);
deleteSelectedLayers();
app.activeDocument = destDoc;
layer.name = "Frame " + i;
}
function layoutLayersAsSprites(destDoc, userOptions)
{
// Figure out atlas size
var frameWidth = parseInt(destDoc.width);
var frameHeight = parseInt(destDoc.height);
var frameCount = destDoc.layers.length;
if(userOptions[UserOptionsResizeKey] == true)
{
var atlasSize = atlasResizeForMipMapping(frameWidth, frameHeight, frameCount);
}
else
{
var atlasSize = atlasResize(frameWidth, frameHeight, frameCount);
}
destDoc.resizeCanvas(atlasSize.width, atlasSize.height, AnchorPosition.TOPLEFT);
// Layout Layers
var x = 0;
var y = 0;
for( var i = 0; i < frameCount; i++ )
{
try {
destDoc.layers[i].translate(x, y);
} catch ( e ) { }
x += frameWidth;
if ( (x+frameWidth) > atlasSize.width )
{
x = 0;
y += frameHeight;
}
}
//flatten image
selectAllLayers ();
mergeSelectedLayers ();
}
function atlasResizeForMipMapping(frameWidth, frameHeight, frameCount)
{
var atlasWidth = 2;
var atlasHeight = 2;
var didFit = false;
//determine the resolution of the texture atlas for bin-packing
while( didFit == false )
{
var gridWidth = Math.floor(atlasWidth / frameWidth);
if(gridWidth == 0) {atlasWidth *= 2; continue;}
var requiredGridHeight = Math.ceil(frameCount / gridWidth);
var requiredPixelHeight = requiredGridHeight * frameHeight;
if ( requiredPixelHeight > atlasHeight )
{
if ( (atlasWidth <= atlasHeight) ) { atlasWidth *= 2; }
else { atlasHeight *= 2; }
didFit = false;
}
else
{
didFit = true;
}
}
var result = {width: atlasWidth, height: atlasHeight};
return result;
}
function atlasResize(frameWidth, frameHeight, frameCount)
{
var atlasWidth = frameWidth;
var atlasHeight = frameHeight;
var didFit = false;
//determine the resolution of the texture atlas for bin-packing
while( didFit == false )
{
var gridWidth = Math.floor(atlasWidth / frameWidth);
if(gridWidth == 0) {atlasWidth += frameWidth; continue;}
var requiredGridHeight = Math.ceil(frameCount / gridWidth);
var requiredPixelHeight = requiredGridHeight * frameHeight;
if ( requiredPixelHeight > atlasHeight )
{
if ( (atlasWidth <= atlasHeight) ) { atlasWidth += frameWidth; }
else { atlasHeight += frameHeight; }
didFit = false;
}
else
{
didFit = true;
}
}
var result = {width: atlasWidth, height: atlasHeight};
return result;
}
/////////////////////////
// LAYER FUNCTIONS
/////////////////////////
// Select all layers in the active document
function selectAllLayers()
{
var idselectAllLayers = stringIDToTypeID( "selectAllLayers" );
var desc4 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref1 = new ActionReference();
var idLyr = charIDToTypeID( "Lyr " );
var idOrdn = charIDToTypeID( "Ordn" );
var idTrgt = charIDToTypeID( "Trgt" );
ref1.putEnumerated( idLyr, idOrdn, idTrgt );
desc4.putReference( idnull, ref1 );
executeAction( idselectAllLayers, desc4, DialogModes.NO );
}
// Duplicate all layers that are currently selected
function duplicateSelectedLayers()
{
var idDplc = charIDToTypeID( "Dplc" );
var desc5 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref2 = new ActionReference();
var idLyr = charIDToTypeID( "Lyr " );
var idOrdn = charIDToTypeID( "Ordn" );
var idTrgt = charIDToTypeID( "Trgt" );
ref2.putEnumerated( idLyr, idOrdn, idTrgt );
desc5.putReference( idnull, ref2 );
var idVrsn = charIDToTypeID( "Vrsn" );
desc5.putInteger( idVrsn, 2 );
executeAction( idDplc, desc5, DialogModes.NO );
}
// Merge all currently selected layers
function mergeSelectedLayers()
{
var idMrgtwo = charIDToTypeID( "Mrg2" );
try {
executeAction( idMrgtwo, undefined, DialogModes.NO );
} catch( e ) { }
}
// Delete all selected layers
function deleteSelectedLayers()
{
// Delete selected layers
var idDlt = charIDToTypeID( "Dlt " );
var desc8 = new ActionDescriptor();
var idnull = charIDToTypeID( "null" );
var ref6 = new ActionReference();
var idLyr = charIDToTypeID( "Lyr " );
var idOrdn = charIDToTypeID( "Ordn" );
var idTrgt = charIDToTypeID( "Trgt" );
ref6.putEnumerated( idLyr, idOrdn, idTrgt );
desc8.putReference( idnull, ref6 );
try{
executeAction( idDlt, desc8, DialogModes.NO );
} catch(e) { }
}
/////////////////////////
// FRAME FUNCTIONS
/////////////////////////
// Count the number of frames in the current frame animation.
function getFrameCount()
{
for( var f = 1; f < 999; f++ )
{
if ( goToFrame(f) == false ) {
return f - 1;
}
}
return 0;
}
// Jump to the given frame in the frame animation in the active document
function goToFrame(frameIndex)
{
try {
var desc = new ActionDescriptor();
var ref1 = new ActionReference();
ref1.putIndex( stringIDToTypeID( "animationFrameClass" ), frameIndex);
desc.putReference( charIDToTypeID( "null" ), ref1 );
executeAction( charIDToTypeID( "slct" ), desc, DialogModes.NO );
return true;
} catch(e) {
}
return false;
}
C#: IK Solver
C#: Networking, Sync Position & Rotation
C#: Line-by-Line Text Writer
C#: Transform Throw & Jump Tweening
C#: Isometric Y Sorting
C#: Procedural 9-Slice 3D Primitives
C#: Voxel-on-Voxel Destruction
SHADERS
using UnityEngine;
// CHARACTER MUST BE BUILT WITH Z BEING THE HEIGHT OF THE CHARACTER (instead of Y), OR WHICHEVER FORWARD-VECTOR UNITY IS SET TO
[ExecuteInEditMode]
public class IKsolver : MonoBehaviour
{
public Transform aPoint, bPoint, cTarget;
[HideInInspector] // HideInInspector forces serialization
public float aLength=0f, bLength=0f, cLength=0f;
void Awake() {
SetLengths();
}
void OnValidate() {
SetLengths();
}
void SetLengths() {
if(aPoint != null && bPoint != null && cTarget != null) {
aLength = (bPoint.position - aPoint.position).magnitude;
bLength = (cTarget.position - bPoint.position).magnitude;
cLength = (cTarget.position - aPoint.position).magnitude;
}
}
void Update() {
if(aLength > 0f && bLength > 0f && cLength > 0f) {
cLength = (cTarget.position - aPoint.position).magnitude;
if(cLength > aLength+bLength)
ClampTarget();
BaseRotation();
Solve();
}
}
Vector3 cTargetdir;
void ClampTarget() {
cTargetdir = (cTarget.position - aPoint.position).normalized;
cTarget.position = aPoint.position + cTargetdir*(aLength+bLength);
}
Vector3 targetDir;
Vector3 poleNormal;
Vector3 upAxis;
Vector3 targetPoleDir;
public Vector3 offsetRotation;
Quaternion targetRotation;
void BaseRotation()
{
targetDir = cTarget.position - aPoint.position;
// pole vector constraint
poleNormal = cTarget.rotation * Vector3.left;
targetPoleDir = Vector3.ProjectOnPlane(targetDir, poleNormal);
// get up axis
upAxis = Vector3.Cross(poleNormal, targetPoleDir);
targetRotation = Quaternion.LookRotation(targetDir, upAxis);
aPoint.rotation = targetRotation * Quaternion.Euler(offsetRotation.x, offsetRotation.y, offsetRotation.z);
bPoint.localRotation = Quaternion.identity;
}
Vector3 jointBend;
void Solve()
{
float a = aLength;
float b = bLength;
float c = cLength;
var B = Mathf.Acos((a * a + c * c - b * b) / (2 * a * c)) * Mathf.Rad2Deg;
var C = Mathf.Acos((a * a + b * b - c * c) / (2 * a * b)) * Mathf.Rad2Deg;
if (!float.IsNaN(B) && !float.IsNaN(C)) {
jointBend = cTarget.localRotation * (Vector3.right*jointDir);
var aPointRotation = Quaternion.AngleAxis((-B), jointBend);
aPoint.localRotation *= aPointRotation;
var bPointRotation = Quaternion.AngleAxis((180-C), jointBend);
bPoint.localRotation = bPointRotation;
}
}
public bool invertJointDirection;
public float jointDir = 1f;
public Vector3 cPos = Vector3.up*1000f;
public void SetBindPose() {
cPos = cTarget.position; Debug.Log("bind pose set");
}
public void GotoBindPose() {
cTarget.position = cPos;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
[NetworkSettings(sendInterval = 0.07f)]
public class Sync_Position : NetworkBehaviour {
[SyncVar]
public Vector3 serverPosition;
private Coroutine UpdatePositionServer_Coroutine;
public bool snapPosition;
private Coroutine SnapPosition_Coroutine;
public float threshold_Radius;
public Rigidbody syncObject;
void Awake()
{
syncObject = gameObject.GetComponent();
//RADIUS WORKS BY PREVENTING SERVER FROM UPDATING CLIENT'S LATEST STATE, UNLESS THERE IS A LARGE DISPARITY.
threshold_Radius = 0.05f;
}
void Start () {
serverPosition = transform.position;
if(!isServer) {return;}
UpdatePositionServer_Coroutine = StartCoroutine( UpdatePosition_Server(0.07f) );
SnapPosition_Coroutine = StartCoroutine(SnapPosition(3f));
}
//SERVER RESPONSIBILITES::
IEnumerator UpdatePosition_Server(float delay)
{
if(isServer)
{
WaitForSeconds Wait = new WaitForSeconds(delay);
while(true)
{
yield return Wait;
if (syncObject.velocity.magnitude > 0.01)
{
//RECONCILE WITH CLIENT IF LARGE DISPARITY BETWEEN CLIENT-SIDE PREDICTION AND SERVER POSITIONS
if (Vector3.Distance(transform.position, serverPosition) > threshold_Radius)
{
serverPosition = transform.position;
}
}
}
}
}
//SNAP THE PLAYER TO THE SERVER POSITION EVERY ONCE IN A WHILE, JUST INCASE THEY HAVE DE-SYNCED FROM THE RADIUS OF TRUST
IEnumerator SnapPosition(float delay)
{
WaitForSeconds Wait = new WaitForSeconds(delay);
while(true)
{
yield return Wait;
Rpc_SnapPosition(transform.position);
}
}
[ClientRpc]
public void Rpc_SnapPosition(Vector3 snappedPosition)
{
if(serverPosition != snappedPosition)
{
serverPosition = snappedPosition;
if(snapPosition)
{
transform.position = snappedPosition;
snapPosition = false;
}
else if (Vector3.Distance(transform.position, serverPosition) > threshold_Radius)
{
transform.position = snappedPosition;
}
}
}
//CLIENT RESPONSIBILITES::
void FixedUpdate()
{
//INTERPOLATION
if(!isServer)
{
if(Vector3.Distance(transform.position, serverPosition) > threshold_Radius)
{
if(snapPosition)
{
if(Vector3.Distance(transform.position, serverPosition) < threshold_Radius*3f)
{ transform.position = serverPosition; snapPosition = false;}
}
transform.position = Vector3.Lerp(transform.position, serverPosition, GameState.Instance.fixedTime_Framerate);
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
[NetworkSettings(sendInterval = 0.07f)]
public class Sync_Rotation : NetworkBehaviour {
[SyncVar]
public Quaternion serverRotation;
private Coroutine UpdateRotation_Coroutine;
private Coroutine SnapRotation_Coroutine;
private float threshold_Angle;
public bool snapRotation;
void OnCollisionEnter()
{
snapRotation = true;
}
void Start ()
{
threshold_Angle = 90f;
if(!isServer) {return;}
UpdateRotation_Coroutine = StartCoroutine( UpdateRotation_Server(0.07f) );
SnapRotation_Coroutine = StartCoroutine(SnapRotation(3f));
}
//SERVER RESPONSIBILITES::
IEnumerator UpdateRotation_Server (float delay)
{
if(isServer)
{
WaitForSeconds Wait = new WaitForSeconds(delay);
while(true)
{
yield return Wait;
if (Vector3.Angle(transform.rotation * transform.forward, serverRotation * transform.forward) > threshold_Angle)
{
serverRotation = transform.rotation;
}
}
}
}
IEnumerator SnapRotation(float delay)
{
WaitForSeconds Wait = new WaitForSeconds(delay);
while(true)
{
yield return Wait;
Rpc_SnapRotation(transform.rotation);
}
}
[ClientRpc]
void Rpc_SnapRotation(Quaternion snappedRotation)
{
if(serverRotation != snappedRotation)
{
transform.rotation = snappedRotation;
serverRotation = snappedRotation;
}
}
//CLIENT RESPONSIBILITES::
void FixedUpdate()
{
if(!isServer)
{
//INTERPOLATION
if (Vector3.Angle(transform.rotation * transform.forward, serverRotation * transform.forward) > threshold_Angle) {
if(snapRotation)
{
transform.rotation = serverRotation;
snapRotation = false;
}
transform.rotation = Quaternion.Slerp(transform.rotation, serverRotation, GameState.Instance.fixedTime_Framerate);
}
}
}
}
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class LineTextWriter : MonoBehaviour
{
public Text[] TextField;
private int _currentIndex_Text;
public float CharatersPerSecond = 30f; //15 for letter-by-letter,
public float newLineDelay = 0.33f;
private string[] _textToWrite;
private int _currentIndex_Letter;
private bool initiated = false;
private IEnumerator coroutine;
public bool Word_by_Word = false;
public bool Line_by_Line = false;
private void Start ()
{
_currentIndex_Letter = 0;
_currentIndex_Text = 0;
if(TextField == null) {TextField = new Text[1]; TextField[0] = GetComponent();}
_textToWrite = new string[TextField.Length];
StartCoroutine (FrameDelay(1f / CharatersPerSecond));
}
IEnumerator FrameDelay(float delay)
{
yield return null;
if(TextField[_currentIndex_Text] != null)
{
for(int i = 0; i < TextField.Length; i++)
{
_textToWrite[i] = TextField[i].text;
TextField[i].text = "";
}
initiated = true;
coroutine = Running(delay);
StartCoroutine(coroutine);
}
}
IEnumerator Running(float delay)
{
yield return new WaitForSeconds(0.5f);
WaitForSeconds characterWait = new WaitForSeconds(delay);
WaitForSeconds newLineWait = new WaitForSeconds(newLineDelay);
string stringToWrite;
while(initiated)
{
yield return characterWait;
if(_currentIndex_Letter >= _textToWrite[_currentIndex_Text].Length)
{
_currentIndex_Text++;
_currentIndex_Letter = 0;
yield return newLineWait;
if(_currentIndex_Text >= TextField.Length)
{
WrapUp();
yield break;
}
}
SetAnim_Talk();
stringToWrite = _textToWrite[_currentIndex_Text].Substring(_currentIndex_Letter, 1);
if(Word_by_Word)
{
int i = 1;
while(!_textToWrite[_currentIndex_Text].Substring(_currentIndex_Letter, 1).Contains(" "))
{
_currentIndex_Letter++;
if (_currentIndex_Letter >= _textToWrite[_currentIndex_Text].Length) { break; }
stringToWrite += _textToWrite[_currentIndex_Text].Substring(_currentIndex_Letter, 1);
if(i < 9) { yield return characterWait; i++;}
}
}
else if(Line_by_Line)
{
int i = 1;
while(_currentIndex_Letter < _textToWrite[_currentIndex_Text].Length)
{
_currentIndex_Letter++;
stringToWrite += _textToWrite[_currentIndex_Text].Substring(_currentIndex_Letter, 1);
if(i < 9) { yield return characterWait; i++;}
}
}
TextField[_currentIndex_Text].text += stringToWrite;
_currentIndex_Letter++;
if (stringToWrite == System.Environment.NewLine
|| stringToWrite.Contains(".")
|| stringToWrite.Contains("!")
|| stringToWrite.Contains("?")
|| stringToWrite.Contains(",")
|| stringToWrite.Contains(">"))
{
yield return newLineWait;
}
}
}
private void Update ()
{
if (Input.GetMouseButton (0))
{
WrapUp();
StopCoroutine(coroutine);
}
}
private void WrapUp()
{
for(int i = 0; i < TextField.Length; i++)
{
TextField[i].text = _textToWrite[i];
}
initiated = false;
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Assertions;
public static class JumpObject
{
/* EXAMPLE USAGE
this.CreateJumpRoutine(transform, duration, height, offset,
delegate() { m_CanPickUp = true; });
*/
public static Coroutine CreateJumpRoutine(this MonoBehaviour value, Transform targetTransform, float duration, float jumpHeight, Vector3 targetOffset, Action onComplete = null) {
return value.StartCoroutine(JumpRoutine(targetTransform, duration, jumpHeight, targetOffset, onComplete));
}
private static IEnumerator JumpRoutine(Transform targetTransform, float duration, float jumpHeight, Vector3 targetOffset, Action onComplete)
{
float deltaTime = Time.deltaTime;
WaitForSeconds wait = new WaitForSeconds(deltaTime);
Vector3 startPosition = targetTransform.position;
Vector3 apexPosition = new Vector3( startPosition.x + (targetOffset.x/2),
startPosition.y + jumpHeight,
0f);
Vector3 endPosition = startPosition + targetOffset;
float lengthA = (apexPosition - startPosition).magnitude;
float lengthB = (endPosition - apexPosition).magnitude;
float ratioA = lengthA / (lengthB + lengthA);
Vector3 velocityStep;
float totalSteps;
float progressStep;
float durationA = duration * ratioA;
float progress = 0;
while(progress < 1f)
{
totalSteps = (int)(durationA / Time.deltaTime);
progressStep = 1f / totalSteps;
progress += progressStep;
velocityStep = new Vector3( Mathf.Lerp(startPosition.x, apexPosition.x, progress),
Mathf.Lerp(startPosition.y, apexPosition.y, easeOutSine(progress)),
0f);
targetTransform.position = velocityStep;
yield return null;
}
float durationB = duration - durationA;
progress = 0;
while(progress < 1f)
{
totalSteps = (int)(durationB / Time.deltaTime);
progressStep = 1f / totalSteps;
progress += progressStep;
velocityStep = new Vector3( Mathf.Lerp(apexPosition.x, endPosition.x, progress),
Mathf.Lerp(apexPosition.y, endPosition.y, easeInSine(progress)),
0f);
targetTransform.position = velocityStep;
yield return null;
}
targetTransform.position = endPosition;
onComplete?.Invoke();
}
// https://easings.net/
private static float easeOutSine(float x) {
return Mathf.Sin((x * Mathf.PI) / 2f);
}
private static float easeInSine(float x) {
return 1 - Mathf.Cos((x * Mathf.PI) / 2);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(SpriteRenderer))]
[ExecuteInEditMode]
public class SpriteSort : MonoBehaviour
{
public const float tileWidth = 2.6660f *0.5f;
public Vector3 bounds;
[HideInInspector]
public Vector3 boundsMax;
[HideInInspector]
public SpriteRenderer SpriteRend;
void Awake()
{
SpriteRend = GetComponent();
Collider2D[] ResultsFront = new Collider2D[MaxCache];
Collider2D[] ResultsBehind = new Collider2D[MaxCache];
}
void Start()
{
SetCamera();
SetObjectToCameraPos();
lastObjPos = transform.position;
SpriteRend.sortingOrder = SortByPosition();
}
const float PixelsPerUnit = 10f;
int SortByPosition()
{
return -(int)((transform.position.y+bounds.y)*PixelsPerUnit);
}
[HideInInspector]
public Camera currentCamera;
void SetCamera() {
currentCamera = Camera.main;
}
void SetObjectToCameraPos()
{
if(System.Object.ReferenceEquals(currentCamera, null))
SetCamera();
else
refreshPos = Mathf.Abs(currentCamera.WorldToViewportPoint(transform.position+bounds).y) %1;
}
bool invoked = false;
float refreshPos;
const float refreshRate = 0.1f;
void Update()
{
if(!invoked)
{
SetObjectToCameraPos();
Invoke("CheckSort", (1f-refreshPos)*refreshRate );
invoked = true;
}
}
Vector3 ObjPos;
Vector3 lastObjPos;
Vector3 CamPos;
Vector3 lastCamPos;
void CheckSort()
{
// only re-sort moving objects
ObjPos = transform.position;
if(ObjPos != lastObjPos) {
SpriteRend.sortingOrder = GetSortOrder();
lastObjPos = ObjPos;
}
// sort downward from camera top
// this will group objects into batches, based upon Y position, for further performance
if(!System.Object.ReferenceEquals(currentCamera, null))
{
CamPos = currentCamera.transform.position;
if(CamPos != lastCamPos) {
SetObjectToCameraPos();
lastCamPos = CamPos;
}
}
else
SetCamera();
Invoke("ReadyForNextIteration", refreshPos*refreshRate );
}
void ReadyForNextIteration()
{
invoked = false;
}
const int MaxCache = 30;
Collider2D[] ResultsFront = new Collider2D[MaxCache];
int FrontLength=0;
Collider2D[] ResultsBehind = new Collider2D[MaxCache];
int BehindLength=0;
SpriteRenderer resultRend;
int GetSortOrder()
{
// clear previous results
for(int i=0; i();
if(!System.Object.ReferenceEquals(resultRend, null)) {
resultOrder = resultRend.sortingOrder;
if(resultOrder>BehindClosestMax)
BehindClosestMax=resultOrder;
}
}
}
return BehindClosestMax+1;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
[RequireComponent(typeof(MeshRenderer))]
[ExecuteInEditMode]
public class PrimitiveConstructor_Box : MonoBehaviour
{
private MeshFilter mf;
private Mesh BoxMesh;
private Vector3 lossyScale;
private List<Vector3> VertexList;
private List<Vector2> UV_List;
private List<int> TriangleList;
private float edgeMargin = 1f / 4f;
private float uWidth = 1f / 4f;
// the texture includes both the top and side art stacked vertically
private float vHeight = 1f / 8f;
void Start()
{
mf = GetComponent<MeshFilter>();
// record scale so we only update when changes occur
lossyScale = transform.lossyScale;
CreatePrimitive ();
}
void Update()
{
if ((transform.lossyScale - lossyScale).magnitude > 0.01f)
{
lossyScale = transform.lossyScale;
CreatePrimitive ();
}
}
void CreatePrimitive()
{
ClearMesh ();
// meshing is calculated Bottom to Top, Left to Right
CalculateVertex ();
CalculateUV ();
CalculateTriangle ();
BoxMesh.vertices = VertexList.ToArray();
BoxMesh.uv = UV_List.ToArray();
BoxMesh.triangles = TriangleList.ToArray();
BoxMesh.RecalculateNormals();
BoxMesh.RecalculateBounds();
}
void ClearMesh()
{
VertexList = new List<Vector3>();
UV_List = new List<Vector2>();
TriangleList = new List<int>();
BoxMesh = mf.sharedMesh;
if (BoxMesh)
BoxMesh.Clear();
BoxMesh = new Mesh ();
mf.mesh = BoxMesh;
}
void CalculateVertex()
{
// face order: bottom, top, front, back, left, right
for(int face=0; face<6; ++face)
{
//Vertex cache
Vector3 vertPos = Vector3.zero;
// top, back, and right faces have constant maximum value for a respective vector component
switch (face) {
case 1: vertPos.y = 1f; break;
case 3: vertPos.z = 1f; break;
case 5: vertPos.x = 1f; break;
}
// ROWS
float rowPos = 0f;
float rowScale;
if (face < 2) rowScale = lossyScale.z;
else rowScale = lossyScale.y;
for (int row = 0; row < 4; row++)
{
switch (row)
{
case 0: rowPos = 0f; break;
case 1: rowPos = edgeMargin / rowScale; break;
case 2: rowPos = 1f - edgeMargin / rowScale; break;
case 3: rowPos = 1f; break;
}
// Rows on Faces 0 (Bottom) and 1 (Top) operate on Z axis, while other faces Rows operate on Y axis
if (face < 2)
vertPos.z = rowPos;
else
vertPos.y = rowPos;
// COLUMNS
float columnPos = 0f;
float columnScale;
if (face < 4)
columnScale = lossyScale.x;
else
columnScale = lossyScale.z;
for (int column = 0; column < 4; ++column)
{
switch(column)
{
case 0: columnPos = 0f; break;
case 1: columnPos = edgeMargin / columnScale; break;
case 2: columnPos = 1f - edgeMargin / columnScale; break;
case 3: columnPos = 1f; break;
}
// Back and Left faces are reversed
if(face == 3 || face == 4)
columnPos = 1f - columnPos;
if(face < 4)
vertPos.x = columnPos;
else
vertPos.z = columnPos;
VertexList.Add(vertPos);
}
}
}
}
void CalculateUV()
{
// face order: bottom, top, front, back, left, right
for(int face=0; face<6; ++face)
{
Vector2 uvPos = Vector2.zero;
// ROWS
// let's just put all the UVs for the bottom face in the middle of the side-texture
if(face == 0)
uvPos.y = 0.5f * 0.5f;
// top face's texture coordinates start at half vertical-height of texture
if(face == 1)
uvPos.y = 0.5f;
for (int row = 0; row < 4; row++)
{
if(face != 0) {
if(row == 2)
uvPos.y += vHeight*2f;
else if(row != 0)
uvPos.y += vHeight;
}
// COLUMNS
uvPos.x = 0f;
for (int column = 0; column < 4; ++column)
{
if(face != 0) {
if(column == 2)
uvPos.x += uWidth*2f;
else if(column != 0)
uvPos.x += uWidth;
}
UV_List.Add(uvPos);
}
}
}
}
void CalculateTriangle()
{
int prevIteration = 0;
// face order: bottom, top, front, back, left, right
for(int face=0; face<6; ++face)
{
// triangle cache
int[] quad = new int[6];
for(int row=0; row<3; ++row)
{
for(int column=0; column<3; ++column)
{
// calculation for generating triangle based upon vertex order
int triCalc = prevIteration + (row * 4) + column;
for(int i=0; i<quad.Length; ++i)
{
switch(i){
case 0: quad[0] = triCalc; break;
case 1: quad[1] = triCalc + 4 + 1; break;
case 2: quad[2] = triCalc + 4; break;
case 3: quad[3] = triCalc; break;
case 4: quad[4] = triCalc + 1; break;
case 5: quad[5] = triCalc + 4 + 1; break;
}
}
TriangleList.Add (quad[0]);
TriangleList.Add (quad[2]);
TriangleList.Add (quad[1]);
TriangleList.Add (quad[3]);
TriangleList.Add (quad[5]);
TriangleList.Add (quad[4]);
}
}
prevIteration += 4 * 4;
}
}
}
// ADD THIS TO QB IMPORTED OBJECT, NEXT TO VOLUME SCRIPT
// ADD MESH COLLIDER TO CHUNK VIA VOLUME SCRIPT
[RequireComponent(typeof(Rigidbody))]
public sealed class CuttableMatrix : MonoBehaviour
{
public Volume volume;
private Frame currentFrame;
public Color32 damageColor;
private Vector3 impactVelocity;
private float voxelSize;
private Vector3 voxelCenter;
private float voxelParticleSize;
private int xSize;
private int ySize;
private int zSize;
void Awake()
{
volume = GetComponent();
currentFrame = volume.GetCurrentFrame();
voxelSize = volume.VoxelSize;
voxelCenter = Vector3.one*(voxelSize/2f);
voxelParticleSize = voxelSize*0.75f;
xSize = currentFrame.XSize;
ySize = currentFrame.YSize;
zSize = currentFrame.ZSize;
cuttableArrayExtents[0] = new int[3];
cuttableArrayExtents[1] = new int[3];
}
/////////
// CUTTING EVENT
/////////
private bool cutSomething;
private Voxel[] cuttableVoxelArray;
private Voxel[] cuttingVoxelArray;
private Frame cuttingFrame;
private int voxelArrayPosition;
private PicaVoxelPoint voxelPointPos;
private Vector3 voxelWorldPosition;
Matrix4x4 cuttableMatrixTransInverse = Matrix4x4.identity;
Matrix4x4 cuttableMatrixTrans = Matrix4x4.identity;
Matrix4x4 cuttingMatrixTrans = Matrix4x4.identity;
public void cutVolume(Volume inVolume, Vector3 impactVelocity_World)
{
cutSomething = false;
impactVelocity = impactVelocity_World;
cuttableVoxelArray = currentFrame.Voxels;
cuttingFrame = inVolume.GetCurrentFrame();
cuttingVoxelArray = cuttingFrame.Voxels;
cuttableMatrixTransInverse = currentFrame.transform.worldToLocalMatrix;
cuttableMatrixTrans = currentFrame.transform.localToWorldMatrix;
cuttingMatrixTrans = cuttingFrame.transform.localToWorldMatrix;
DefineArrays();
ArrayBoolean();
for(int z=booleanArrayExtentsMin[2]; z -1 && voxelPointPos.Y > -1 && voxelPointPos.Z > -1 && voxelPointPos.X < xSize && voxelPointPos.Y < ySize && voxelPointPos.Z < zSize)
{
voxelArrayPosition = GetVoxelArrayPositionFromPoint(voxelPointPos.X,voxelPointPos.Y,voxelPointPos.Z);
if(cuttableVoxelArray[voxelArrayPosition].Active)
{
ColorAdjacentVoxels(voxelPointPos.X, voxelPointPos.Y, voxelPointPos.Z);
cuttableVoxelArray[voxelArrayPosition].State = VoxelState.Hidden;
SpawnParticles();
// we found a voxel to cut!
cutSomething = true;
}
}
}
}
}
}
// update in batches
if(cutSomething) {
currentFrame.Voxels = cuttableVoxelArray;
currentFrame.UpdateAllChunksNextFrame();
}
else
impactEvent(false);
}
private PicaVoxelPoint[] cuttableBounds_inCuttingSpace = new PicaVoxelPoint[2];
private int[][] cuttableArrayExtents = new int[2][];
private int[] cuttingArrayExtents = new int[3];
private int[] booleanArrayExtentsMin = new int[3];
private int[] booleanArrayExtentsMax = new int[3];
void DefineArrays()
{
cuttableBounds_inCuttingSpace[0] = cuttingFrame.GetVoxelArrayPosition(
GetVoxelApproximateWorldPosition(0, 0, 0) );
cuttableBounds_inCuttingSpace[1] = cuttingFrame.GetVoxelArrayPosition(
GetVoxelApproximateWorldPosition(xSize, ySize, zSize) );
for(int i=0; i<2; ++i)
{
cuttableArrayExtents[i][0] = cuttableBounds_inCuttingSpace[i].X;
cuttableArrayExtents[i][1] = cuttableBounds_inCuttingSpace[i].Y;
cuttableArrayExtents[i][2] = cuttableBounds_inCuttingSpace[i].Z;
}
cuttingArrayExtents[0] = cuttingFrame.XSize;
cuttingArrayExtents[1] = cuttingFrame.YSize;
cuttingArrayExtents[2] = cuttingFrame.ZSize;
for(int i=0; i<3; ++i) {
booleanArrayExtentsMin[i]=cuttingArrayExtents[i];
booleanArrayExtentsMax[i]=0;
}
}
void ArrayBoolean()
{
// get smallest and largest values
for(int p=0; p<2; ++p)
{
for(int i=0; i<3; ++i)
{
// get smallest values of cuttable array extents
booleanArrayExtentsMin[i] = Mathf.Min(booleanArrayExtentsMin[i],cuttableArrayExtents[p][i]);
// get largest values of cuttable array extents
booleanArrayExtentsMax[i] = Mathf.Max(booleanArrayExtentsMax[i],cuttableArrayExtents[p][i]);
}
}
// clamp smallest and largest values
for(int i=0; i<3; ++i)
{
// clamp smallest values to 0
booleanArrayExtentsMin[i] = Mathf.Max(booleanArrayExtentsMin[i],0);
// clamp largest values to cutting array
booleanArrayExtentsMax[i] = Mathf.Min(booleanArrayExtentsMax[i],cuttingArrayExtents[i]);
}
}
private const int adjacentVoxelsLength = 18;
private int[] adjacentVoxels = new int[18];
private int[] adjacentMath = new int[]
{
-1, 0, 0,
1, 0, 0,
0, -1, 0,
0, 1, 0,
0, 0, -1,
0, 0, 1
};
private void ColorAdjacentVoxels(int x, int y, int z)
{
// Get adjacent voxel positions
for(int i=0; i -1 && adjacentVoxels[v+1] > -1 && adjacentVoxels[v+2] > -1
&& adjacentVoxels[v] < xSize && adjacentVoxels[v+1] < ySize && adjacentVoxels[v+2] < zSize)
{
// GetVoxelArrayPositionFromPoint
int index = adjacentVoxels[v] + xSize * (adjacentVoxels[v+1] + ySize * adjacentVoxels[v+2]);
cuttableVoxelArray[ index ].Color = damageColor;
}
}
}
private void SpawnParticles()
{
//////// Let's make pieces fly off where it got sliced
// Get Velocity Direction of voxels to fly off
//directionFromVolumeCenter = (voxelWorldPosition - volumeWorldCenter).normalized;
if(UnityEngine.Random.Range(0f, 3f) < 1f)
VoxelParticleSystem.Instance.SpawnSingle( voxelWorldPosition,
damageColor,
voxelParticleSize,
impactVelocity*3f + Random.onUnitSphere);
}
Vector3 localVoxelPos = Vector3.zero;
PicaVoxelPoint localVoxelPoint = new PicaVoxelPoint(0,0,0);
PicaVoxelPoint GetVoxelPointPosition(Vector3 worldPos)
{
//localVoxelPos = currentFrame.transform.InverseTransformPoint(worldPos);
localVoxelPos = cuttableMatrixTransInverse.MultiplyPoint3x4(worldPos);
localVoxelPoint.X = (int)(localVoxelPos.x / voxelSize);
localVoxelPoint.Y = (int)(localVoxelPos.y / voxelSize);
localVoxelPoint.Z = (int)(localVoxelPos.z / voxelSize);
return localVoxelPoint;
}
Vector3 GetVoxelWorldPosition(int x, int y, int z)
{
localVoxelPos.x = x * voxelSize;
localVoxelPos.y = y * voxelSize;
localVoxelPos.z = z * voxelSize;
localVoxelPos += voxelCenter;
return cuttingMatrixTrans.MultiplyPoint3x4(localVoxelPos);
//return frame.transform.TransformPoint(funcVoxelWorldPos);
}
Vector3 GetVoxelApproximateWorldPosition(int x, int y, int z)
{
return cuttableMatrixTrans.MultiplyPoint3x4(new Vector3(x * voxelSize, y * voxelSize, z * voxelSize));
//return currentFrame.transform.TransformPoint(new Vector3(x * voxelSize, y * voxelSize, z * voxelSize));
}
private int GetVoxelArrayPositionFromPoint(PicaVoxelPoint point, Frame frame)
{
return point.X + frame.XSize * (point.Y + frame.YSize * point.Z);
}
private int GetVoxelArrayPositionFromPoint(int x, int y, int z, Frame frame)
{
return x + frame.XSize * (y + frame.YSize * z);
}
private int GetVoxelArrayPositionFromPoint(int x, int y, int z)
{
return x + xSize * (y + ySize * z);
}
CG Shader: Horizon-Based Ambient Occlusion
CG Shader: 2D Isometric Water
CG Shader: Dissolve to ashes
CG Shader: Volumetric Fog
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
[ExecuteInEditMode]
public class SSAO_Cavity_CameraEffect : MonoBehaviour
{
public bool resetMaterial;
private bool setupSuccess;
public Shader shader;
private Material material;
private Texture2D noiseTexture;
private Camera cam;
void Awake()
{
SetupMaterial();
}
void OnValidate()
{
if(resetMaterial)
{
DestroyImmediate(material);
material = null;
resetMaterial = false;
}
SetupMaterial();
}
private float AOradiusToScreen;
private float edgeRadiusToScreen;
private float negInvRadius2;
private Vector2 uvToView;
private Vector2 texelSize;
void SetupMaterial()
{
if(shader == null)
setupSuccess = false;
else if(material == null && shader != null)
{
this.material = new Material(shader);
material.hideFlags = HideFlags.HideAndDontSave;
CreateNoiseTexture();
material.SetTexture("_NoiseTex", noiseTexture);
}
if(cam == null)
{
cam = Camera.main;
cam.depthTextureMode |= DepthTextureMode.DepthNormals;
}
if(material != null)
{
float camWidth = cam.pixelWidth;
float camHeight = cam.pixelHeight;
// this gets half of the length of the farplane of the camera
// according to a normalized triangle, ie. farplane distance = 1
float tanHalfFovY = Mathf.Tan(0.5f * cam.fieldOfView * Mathf.Deg2Rad);
// get X length of farplane by multiplying by the ratio of width/height of camera
float tanHalfFovX = tanHalfFovY * (camWidth / camHeight);
// expand the length of the farplane to it's full length by using *2
uvToView = new Vector4(2.0f * tanHalfFovX, -2.0f * tanHalfFovY);
// this calculates the X delta distance between pixels along the farplane
float projScale = camWidth / tanHalfFovX;
// *0.5f seems unnecessary here
AOradiusToScreen = radiusPixels * 0.5f * projScale;
negInvRadius2 = -1.0f / (radiusPixels * radiusPixels);
edgeRadiusToScreen = edgeRadius * 0.5f * projScale;
texelSize = new Vector4(1f / camWidth, 1f / camHeight);
setupSuccess = true;
}
}
[SerializeField] private bool debug = false;
[Range(0.3f, 5f)][SerializeField] private float radiusPixels = 1.2f;
[Range(0f, 4f)][SerializeField] private float AO_Intensity = 1f;
[Range(0.3f, 1f)][SerializeField] private float feathering = 0.85f;
[Range(1f, 6f)][SerializeField] private float cavityRadius = 3f;
[Range(0f, 4f)][SerializeField] private float cavityIntensity = 0.325f;
[SerializeField] private bool edges = false;
[Range(0f, 1f)][SerializeField] private float edgeRadius = 0.12f;
[Range(0f, 4f)][SerializeField] private float edgeIntensity = 0.44f;
[Range(0f, 20f)][SerializeField] private float wireRadius = 2f;
[Range(0f, 1f)][SerializeField] private float wireIntensity = 0.073f;
void OnRenderImage(RenderTexture source, RenderTexture destination)
{
if(setupSuccess)
{
int screenWidth = Screen.width;
int screenHeight = Screen.height;
material.SetFloat("_AOIntensity", AO_Intensity);
material.SetFloat("_Feathering", feathering);
material.SetFloat("_RadiusToScreen", AOradiusToScreen);
material.SetFloat("_NegInvRadius2", negInvRadius2);
material.SetVector("_UVtoView", uvToView);
material.SetVector("_TexelSize", texelSize);
//ao
var tmp = RenderTexture.GetTemporary(screenWidth, screenHeight);
Graphics.Blit(source, tmp, material, 0);
material.SetTexture("_OcclusionTex", tmp);
//cavity
var tmp1 = RenderTexture.GetTemporary(screenWidth, screenHeight);
material.SetFloat("_CavityIntensity", cavityIntensity);
material.SetFloat("_CavityRadius", cavityRadius);
Graphics.Blit(source, tmp1, material, 1);
material.SetTexture("_CavityTex", tmp1);
//edges
material.SetFloat("_Edges", edges ? 1.0f : 0.0f);
var tmp2 = RenderTexture.GetTemporary(screenWidth, screenHeight);
var tmp3 = RenderTexture.GetTemporary(screenWidth, screenHeight);
if(edges) {
material.SetFloat("_EdgeIntensity", edgeIntensity);
material.SetFloat("_EdgeRadius", edgeRadiusToScreen);
Graphics.Blit(source, tmp2, material, 2);
material.SetTexture("_EdgesTex", tmp2);
//wireframe
material.SetFloat("_WireIntensity", wireIntensity);
material.SetFloat("_WireRadius", wireRadius);
Graphics.Blit(source, tmp3, material, 3);
material.SetTexture("_WireTex", tmp3);
}
//blur
var tmp4 = RenderTexture.GetTemporary(Screen.width, Screen.height);
Graphics.Blit(source, tmp4, material, 4);
material.SetTexture("_OcclusionTex", tmp4);
//composite
if(!debug)
Graphics.Blit(source, destination, material, 5);
else
Graphics.Blit(source, destination, material, 6);
RenderTexture.ReleaseTemporary(tmp);
RenderTexture.ReleaseTemporary(tmp1);
RenderTexture.ReleaseTemporary(tmp2);
RenderTexture.ReleaseTemporary(tmp3);
RenderTexture.ReleaseTemporary(tmp4);
}
else
Debug.Log("Setup Failed, please Reset Material with the NoiseTex and Shader");
}
private static float[] MersenneTwister = new float[] {
0.795755f, 0.581869f, 0.970863f, 0.770839f, 0.704620f, 0.245404f, 0.905371f, 0.772047f, 0.893339f, 0.734592f, 0.157900f, 0.310195f,
0.013235f, 0.520863f, 0.253941f, 0.623978f, 0.330538f, 0.115472f, 0.611865f, 0.413736f, 0.019519f, 0.062350f, 0.259302f, 0.127570f,
0.911142f, 0.761439f, 0.673785f, 0.379541f, 0.732038f, 0.974906f, 0.384873f, 0.776879f, 0.956725f, 0.645520f, 0.708315f, 0.583199f,
0.236644f, 0.992380f, 0.981091f, 0.619804f, 0.810866f, 0.360499f, 0.361497f, 0.557862f, 0.839955f, 0.132871f, 0.417807f, 0.220779f,
0.730747f, 0.076690f, 0.408562f, 0.660104f, 0.428921f, 0.311342f, 0.587871f, 0.206406f, 0.137980f, 0.620309f, 0.462196f, 0.219485f,
0.835646f, 0.795892f, 0.044437f, 0.891731f, 0.501241f, 0.229229f, 0.899218f, 0.707733f, 0.415115f, 0.175218f, 0.757357f, 0.568868f,
};
private void CreateNoiseTexture()
{
noiseTexture = new Texture2D(4,4, TextureFormat.RGB24, false, true);
noiseTexture.filterMode = FilterMode.Point;
noiseTexture.wrapMode = TextureWrapMode.Repeat;
int z = 0;
for(int y=-1; y<5; ++y)
for(int x=-1; x<5; ++x){
float r = MersenneTwister[z++];
float g = MersenneTwister[z++];
Color noiseCol = new Color(r,g,0f);
noiseTexture.SetPixel(x, y, noiseCol);
}
noiseTexture.Apply();
}
}
///// HLSL SHADER
Pass
{
Name "HBAO - Occlusion"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 vertex : SV_POSITION;
};
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
sampler2D _NoiseTex;
sampler2D_float _CameraDepthTexture;
float2 _UVtoView;
float2 _TexelSize;
float _Feathering;
float _NegInvRadius2;
float _RadiusToScreen;
inline float FetchDepth(float2 uv)
{
return LinearEyeDepth( SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, uv) );
}
inline float3 FetchViewPos(float2 uv)
{
float depth = FetchDepth(uv);
uv = uv * _UVtoView;
return float3((uv) * depth, depth);
} //orthographic, float3((uv)*float2(1,depth),depth)
inline float3 MinDiff(float3 P, float3 Pr, float3 Pl)
{
float3 V1 = Pr - P;
float3 V2 = P - Pl;
return (dot(V1,V1) < dot(V2,V2)) ? V1 : V2;
}
float3 ReconstructNormal(float2 uv, float2 texelDelta, float3 P)
{
float3 Pr = FetchViewPos(uv + float2(texelDelta.x, 0));
float3 Pl = FetchViewPos(uv + float2(-texelDelta.x, 0));
float3 Pt = FetchViewPos(uv + float2(0, texelDelta.y));
float3 Pb = FetchViewPos(uv + float2(0, -texelDelta.y));
return normalize( cross( MinDiff(P,Pr,Pl), MinDiff(P,Pt,Pb) ) );
}
inline float Falloff(float distanceSquare) {
// 1 scalar mad instruction
return distanceSquare * _NegInvRadius2 + 1.0;
}
//-----------------------------------------------
// P = view-space position at the kernel center
// N = view-space normal at the kernel center
// S = view-space position of the current sample
//-----------------------------------------------
float ComputeAO(float3 P, float3 N, float3 S)
{
float3 V = S - P;
float VdotV = dot(V, V);
float NdotV = dot(N, V) * 1.0/sqrt(VdotV);
// Use saturate(x) instead of max(x,0.f) because that is faster on Kepler
//angleBias of 0.05
return saturate(NdotV - 0.05) * saturate(Falloff(VdotV));
}
float ComputeCoarseAO(float2 uv, float radius, float2 rand, float3 P, float3 N)
{
const float2 vec[8] = { float2(1,0),
float2(0.5f,0.5f),
float2(0,1),
float2(-0.5f,0.5f),
float2(-1,0),
float2(-0.5f,-0.5f),
float2(0,-1),
float2(0.5f,-0.5f)
};
float stepSizePixels = radius / (8 + 1);
float AO = 0;
UNITY_UNROLL
for(uint d=0; d<8; ++d) //numDirections
{
// Compute normalized 2D direction
float2 direction = reflect(vec[d], rand) * _Feathering;
// Jitter starting sample within the first step
float rayPixels = (rand.x * stepSizePixels + 1.0);
for(uint s=0; s<4; ++s) //numSteps
{
float2 snappedUV = round(rayPixels * direction) * _TexelSize + uv;
float3 S = FetchViewPos(snappedUV);
rayPixels += stepSizePixels;
AO += ComputeAO(P, N, S);
}
}
return AO/(8*4);
}
fixed4 frag (v2f i) : SV_Target
{
float3 P = FetchViewPos(i.uv);
float3 N = ReconstructNormal(i.uv, _TexelSize, P);
// Compute projection of disk of radius into screen space
float radius = _RadiusToScreen / P.z;
// angle, jitter
float2 rand = normalize(tex2D(_NoiseTex, i.vertex*0.25).xy );
float AO = ComputeCoarseAO(i.uv, radius, rand, P, N);
return fixed4(AO.xxx, 1);
}
ENDCG
}
Shader "Unlit/waterShader_2"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_Color("Color", Color) = (0.85,0.875,0.83,0.93)
_DistortTex ("Distort Texture", 2D) = "white" {}
_WaveReflectAlpha ("Wave Reflection Alpha", Range(0,0.1)) = 0.03
_WaveCrestAlpha ("Wave Crest Alpha", Range(0,10)) = 4.39
_WaveOne_UVScale ("Wave 1 UV Scale", Float) = 1
_WaveTwo_UVScale ("Wave 2 UV Scale", Float) = 0.75
_WaveOne_ScrollSpeedSine("Wave 1 ScrollSpeed Sine", Float) = 1
_WaveOne_ScrollSpeedX("Wave 1 ScrollSpeed X", Float) = 2
_WaveOne_ScrollSpeedY("Wave 1 ScrollSpeed Y", Float) = 1
_WaveTwo_ScrollSpeedSine("Wave 2 ScrollSpeed Sine", Float) = 1
_WaveTwo_ScrollSpeedX("Wave 2 ScrollSpeed X", Float) = -3
_WaveTwo_ScrollSpeedY("Wave 2 ScrollSpeed Y", Float) = -2
_Speed("Displace Speed", Range(0.01, 1)) = 0.353
_Frequency("Displace Frequency", Range(2,100)) = 89.1
_ResolutionX("Resolution X", Range(1,2)) = 1.789
_ResolutionY("Resolution Y", Range(1,2)) = 1.694
_AmountX("Displace Amount X", Range(0,0.1)) = 0.0067
_AmountY("Displace Amount Y", Range(0,0.1)) = 0.0044
}
SubShader
{
Tags {
"Queue" = "Transparent+1"
"IgnoreProjector"="True"
"RenderType"="Transparent"
}
LOD 100
Cull Off
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
GrabPass{
"_BackgroundTex"
}
Pass
{
Stencil
{
Ref 5
Comp Always
Pass Replace
}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float2 uv_dis : TEXCOORD2;
};
struct v2f
{
float2 uv : TEXCOORD0;
float4 grabUV: TEXCOORD1;
float2 uv_dis: TEXCOORD2;
float4 vertex : SV_POSITION;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _DistortTex;
float4 _DistortTex_ST;
sampler2D _BackgroundTex;
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.uv_dis = TRANSFORM_TEX(v.uv_dis, _DistortTex);
o.grabUV = ComputeGrabScreenPos(o.vertex);
return o;
}
fixed4 _Color;
float _WaveReflectAlpha;
float _WaveCrestAlpha;
float _WaveOne_UVScale;
float _WaveTwo_UVScale;
float _WaveOne_ScrollSpeedSine;
float _WaveOne_ScrollSpeedX;
float _WaveOne_ScrollSpeedY;
float _WaveTwo_ScrollSpeedSine;
float _WaveTwo_ScrollSpeedX;
float _WaveTwo_ScrollSpeedY;
float _Speed;
float _Frequency;
float _ResolutionX;
float _ResolutionY;
float _AmountX;
float _AmountY;
fixed4 frag (v2f i) : SV_Target
{
// wave distortion for textures
fixed displaceWaveX = floor(sin((i.uv.x + _Time*_Speed) * _Frequency) * _ResolutionX)/_ResolutionX;
fixed displaceWaveY = floor(sin((i.uv.y + _Time*_Speed) * _Frequency) * _ResolutionY)/_ResolutionY;
float2 displaceUV = float2(displaceWaveY * _AmountY, displaceWaveX * _AmountX);
fixed4 tileMap = tex2D(_MainTex, i.uv + displaceUV);
//scroll textures
fixed2 scrollUVone = i.uv_dis;
fixed scrollX = _WaveOne_ScrollSpeedX * sin(_Time * _WaveOne_ScrollSpeedSine);
fixed scrollY = _WaveOne_ScrollSpeedY * sin(_Time * _WaveOne_ScrollSpeedSine);
scrollUVone += fixed2( scrollX, scrollY );
fixed4 waveOne = tex2D(_DistortTex, scrollUVone*_WaveOne_UVScale + fixed2(displaceUV.x*_DistortTex_ST.x, displaceUV.y*_DistortTex_ST.y));
fixed2 scrollUVtwo = i.uv_dis;
scrollX = _WaveTwo_ScrollSpeedX * sin(_Time * _WaveTwo_ScrollSpeedSine);
scrollY = _WaveTwo_ScrollSpeedY * sin(_Time * _WaveTwo_ScrollSpeedSine);
scrollUVtwo += fixed2( scrollX, scrollY );
fixed4 waveTwo = tex2D(_DistortTex, scrollUVtwo*_WaveTwo_UVScale + fixed2(displaceUV.x*_DistortTex_ST.x, displaceUV.y*_DistortTex_ST.y));
//tileMap.r+waveOne.r accentuates the edges with the wave pattern
//*tileMap.a isolates the edges
//*waveOne.r isolates the wave pattern along the edges
waveOne = (tileMap.r * _WaveCrestAlpha + waveOne.r) * tileMap.a * waveOne.r;
// Grab Pass background
fixed4 grabUV = i.grabUV + fixed4(displaceUV.x, displaceUV.y, displaceUV.x, displaceUV.y);
fixed4 backCol = tex2Dproj(_BackgroundTex, UNITY_PROJ_COORD(grabUV));
//waveOne - (waveTwo-tileMap.r) removes the edges from WaveTwo pattern, and removes this resulting pattern from waveOne
fixed4 finCol = ( waveOne - (waveTwo-tileMap.r) ) * _WaveReflectAlpha;
finCol += backCol;
finCol.a = finCol.a * tileMap.a;
if (finCol.a<0.5) discard;
return finCol * _Color;
}
ENDCG
}
}
}
Shader "Custom/Dissolve" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_SliceAmount("Slice Amount", Range(0.0, 1.0)) = 0
_BWAmount("Black White LERP", Range(0.0, 1.0)) = 0
_BurnSize("Burn Size", Range(0.0, 1.0)) = 0.15
_BurnRamp("Burn Ramp (RGB)", 2D) = "white" {}
_BurnColor("Burn Color", Color) = (1,1,1,1)
_EmissionAmount("Emission amount", float) = 2.0
_PerlinOctaves ("Perlin Octaves", Range(1,1)) = 1
_PerlinPeriod ("Perlin Period", Range(1,13)) = 1
_PerlinBrightness ("Perlin Brightness", Range(0,0.6)) = 0.6
_PerlinContrast ("Perlin Contrast", Range(0,5)) = 5
_WorleyOctaves ("Worley Octaves", Range(4,4)) = 4 //2 thru 6?
_WorleyPeriod ("Worley Period", Range(9,13)) = 10
_WorleyBrightness ("Worley Brightness", Float) = 1.2
_WorleyContrast ("Worley Contrast", Float) = 1
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
Cull Off
CGPROGRAM
#pragma surface surf Lambert addshadow
#pragma target 3.0
#include "CloudNoiseLib.cginc"
fixed4 _Color;
sampler2D _MainTex;
sampler2D _SliceGuide;
sampler2D _BumpMap;
sampler2D _BurnRamp;
fixed4 _BurnColor;
float _BurnSize;
float _SliceAmount;
float _EmissionAmount;
float _BWAmount;
int _PerlinOctaves;
int _PerlinPeriod;
float _PerlinBrightness;
float _PerlinContrast;
int _WorleyOctaves;
int _WorleyPeriod;
float _WorleyBrightness;
float _WorleyContrast;
struct Input {
float2 uv_MainTex;
};
float fbm_perlin (float3 st, int octaves, int rep)
{
// Initial values
float value = 0;
float amplitude = 0.5;
float frequency = 0;
for (int i = 0; i < octaves; i++)
{
value += amplitude * pnoise(st, rep);
rep *= 2; // Modify the repetition instead of the texture coordinates
amplitude *= 0.5;
}
return value * 0.5 + 0.5; // [-1, 1] -> [0, 1]
}
float fbm_worley (float3 st, int octaves, int rep)
{
// Initial values
float value = 0;
float amplitude = 0.5;
float frequency = 0;
for (int i = 0; i < octaves; i++)
{
value += amplitude * (1 - worley(st, 1, false, rep).x);
rep *= 2; // Modify the repetition instead of the texture coordinates
amplitude *= 0.5;
}
return value;
}
void surf (Input IN, inout SurfaceOutput o) {
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
fixed4 bw = tex2D (_MainTex, IN.uv_MainTex) * _Color;
//resolution of noise
fixed4 test = 0;
float3 st = float3(floor(IN.uv_MainTex.x*128)/128, floor(IN.uv_MainTex.y*128)/128, 0);
// Perlin
float perlin = fbm_perlin(st, _PerlinOctaves, _PerlinPeriod);
perlin = (perlin-0.5) * _PerlinContrast + 0.5;
perlin += _PerlinBrightness - 1;
// Worley
float worley = fbm_worley(st, _WorleyOctaves, _WorleyPeriod);
worley = (worley-0.5) * _WorleyContrast + 0.5;
worley += _WorleyBrightness - 1;
//frameRate
float sliceAmount = floor(_SliceAmount*12)/12;
//limit the colors in the noise
test.r = floor((worley - perlin * (1-worley) - sliceAmount)*10)/10;
//match luminance for black-white to viewer's eye perception
float lum = bw.r*.3 + bw.g*.59 + bw.b*.11;
bw.rgb = float3(lum*0.6,lum*0.6,lum*0.6);
//clip blackwhite
bw.a = 0;
if(test.r > 0) {
bw.a = 1;
}
//clip color
c.a = 0;
if(test.r < 0) {
c.a = 1;
}
//lerp color to bw
c.rgb = lerp(c.rgb, bw.rgb, _BWAmount);
//clip(test);
if (test.r < _BurnSize && _SliceAmount > 0 && test.r > 0) {
o.Emission = tex2D(_BurnRamp, float2(test.r * (1 / _BurnSize), 0)) * _BurnColor * _EmissionAmount;
}
o.Albedo = c.rgb*c.a + bw.rgb*bw.a;
o.Alpha = c.a + bw.a;
}
ENDCG
}
FallBack "Diffuse"
}
Shader "Custom/Volumetric_Shader" {
Properties{
_Color ("Color", Color) = (1.0, 0.639, 0, 0.7)
}
SubShader {
Tags{ "RenderType" = "Transparent" "Queue" = "Transparent" "IgnoreProjector" = "True" "ForceNoShadowCasting" = "True" "LightMode" = "ForwardBase" }
ZWrite Off
Lighting Off
Blend SrcAlpha OneMinusSrcAlpha
LOD 300
Pass {
Cull Back
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float4 _MainTex_ST;
uniform float4 _Color;
struct vertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
struct vertexOutput {
float4 pos : SV_POSITION;
float4 posWorld : TEXCOORD1;
float3 normalDir : TEXCOORD2;
float4 color : COLOR0;
float2 uv : TEXCOORD0;
};
vertexOutput vert(vertexInput v) { //appdata_base
vertexOutput o;
UNITY_INITIALIZE_OUTPUT(vertexOutput, o);
o.posWorld = mul(unity_ObjectToWorld, v.vertex);
o.normalDir = normalize( mul( float4( v.normal, 0.0 ), unity_WorldToObject ).xyz );
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX (v.uv, _MainTex);
o.color = float4 (1.0, 1.0, 1.0, 1);
return o;
}
float4 frag(vertexOutput i) : SV_Target{
float3 normalDirection = i.normalDir;
float3 viewDirection = normalize( _WorldSpaceCameraPos.xyz - i.posWorld.xyz );
float3 rim = saturate( dot( viewDirection, normalDirection ) );
return float4(_Color.rgb, (clamp(distance(_WorldSpaceCameraPos.xyz, i.posWorld.xyz)*0.005, 0.0, 1.0)) *_Color.a *rim.x);
}
ENDCG
}
Pass {
Cull Front
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float4 _MainTex_ST;
uniform float4 _Color;
struct vertexInput {
float4 vertex : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
struct vertexOutput {
float4 pos : SV_POSITION;
float4 posWorld : TEXCOORD1;
float3 normalDir : TEXCOORD2;
float4 color : COLOR0;
float2 uv : TEXCOORD0;
};
vertexOutput vert(vertexInput v) { //appdata_base
vertexOutput o;
UNITY_INITIALIZE_OUTPUT(vertexOutput, o);
v.normal.xyz = v.normal*-1;
o.posWorld = mul(unity_ObjectToWorld, v.vertex);
o.normalDir = normalize( mul( float4( v.normal, 0.0 ), unity_WorldToObject ).xyz );
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX (v.uv, _MainTex);
o.color = float4 (1.0, 1.0, 1.0, 1);
return o;
}
float4 frag(vertexOutput i) : SV_Target{
float3 normalDirection = i.normalDir;
float3 viewDirection = normalize( _WorldSpaceCameraPos.xyz - i.posWorld.xyz );
float3 rim = saturate( dot( viewDirection, normalDirection ) );
return float4(_Color.rgb, (clamp(distance(_WorldSpaceCameraPos.xyz, i.posWorld.xyz)*0.005, 0.0, 1.0) ) *_Color.a *rim.x);
}
ENDCG
}
}
FallBack "Unlit"
}