My Passion is bringing data to Life

— MattOlick.3D @gmail.com



The start-ups I helped create total over:
  • 50 million sales & downloads
  • 1 billion youtube views
  • 10 million social followers
  • $60,000,000+ crowdfunding raised
  • $30,000,000+ ad spend managed
  • 25,000+ marketing courses sold


Moderator / Admin of:

CHARTS:
  • #8 Top Upcoming Games (Ascent of Ashes, Steam NextFest Q4 2023)
  • #161 Top Wishlisted Steam Games (Ascent of Ashes, Steam, SteamDB)
  • #280 Top Wishlisted Steam Games (Macabre, Steam, SteamDB)
  • #15,000 Top Ranked 2020 Instagram Accounts​ (Ecommerce Academy, HypeAuditor)
  • #3 Simulation 2019​ (Fit The Fat 3, iTunes)
  • #20 Sports 2019​ (Fit The Fat 3, iTunes)
  • #30 Role Playing 2019​ (Fit The Fat 3, iTunes)
  • #10,000 Top Ranked 2016 Instagram Accounts​ (SimplyShredded, HypeAuditor)
  • #60 Top 100 Games of 2017 (Clone Drone in the Danger Zone, Steam250.com)
  • #175 Top Games of 2021 (Clone Drone in the Danger Zone, Steam250.com)
  • #122 Top 200 Best Rated Steam Games Of All Time (Clone Drone in the Danger Zone, Steam250.com)
  • #26 Top 100 Best Rated Steam Games Of All Time (Starbound, Steam250.com)
  • #53 Top 100 Best Rated Steam Games Of All Time (Pirates Vikings Knights 2, Steam250.com)
  • #1 HypeMachine, 4x Top #10 HypeMachine (Retrotation, Hypem.com)

AWARDS:
  • "Most Fulfilling Community-Funded Game 2017" (Starbound, SXSW)
  • "Top 100 Greatest PC Games"​ (Starbound, PC Gamer)
  • "IGN Editors' Choice Award" (Starbound, IGN)
  • "Game, Simulation" (Starbound, NAVGTR Awards 2017, nominated)
  • "Top 100 of Indie of The Year 2016"​ (Clone Drone in the Danger Zone, IndieDB)
  • "No. 1 of Indie of The Year 2013" (Starbound, IndieDB)
  • "Most Anticipated Game of 2013" (Starbound, Indie Game Magazine)
  • "IGM Reader's Choice: The Best Indie Games of the Year 2012" (Starbound)
  • "Artist of the Month" (Retrotation, Chillbass)
  • "2008 Mod of the Year" (Pirates Vikings Knights 2, ModDB)


Gaming Clients:
click individual businesses to read more
Artist
Coding
UIX
Audio
Video
Marketing
Director
Camlann Games Publishing

Camlann Games Publishing

Marketing Director & Strategist

(2023)


Camlann Games Publishing is an investor-backed video game publisher behind "Ascent of Ashes" (#161 Top Wishlisted), Dungeons & Kingdoms (#380 Top Wishlisted), Underspace, and more. We currently fund and manage over 20 upcoming Steam releases, and have raised over $1,100,000+ in seed funding between 2023 to 2024.


Camlann's platform is powered by proprietary AI technology and a specialized client-portal for game project management, Steam store management & automated marketing optimization.


   star   star2

  • Talent sourcing for and co-direction of community management teams.
  • Kickstarter campaign strategy, page design, and ads management.
  • Web development & landing page design using WordPress and Shopify.
  • Steam NextFest, Play Demo, Wishlist, Release, and Steam Sale ad campaigns for upcoming game releases using Facebook, TikTok, Google, and Youtube ads. These campaigns acquired wishlists between $0.10 to $1 for:
    • 156,000 wishlists for 'Ascent of Ashes'
    • 95,000 wishlists for 'Underspace'
    • 80,000 wishlists for 'Dungeons & Kingdoms'
    • 60,000 wishlists for 'Kristala'
CHARTS:
  • #380 Top Wishlisted Steam Games (Dungeons & Kingdoms, Steam Upcoming Charts)
  • #161 Top Wishlisted Steam Games (Ascent of Ashes, Steam Upcoming Charts)
  • #8 Top Upcoming Games (Ascent of Ashes, Steam NextFest Q4 2023)
Creed's Codex

Creed's Codex

Crowdfunding Specialist

(2022)


Creed's Codex: Arcane Secrets of the Summoners is an A 5E supplement to bring your character concept to life. Brand new summoning class, spells, feats, gear, convergent magic, and more.


While utilizing my proprietary marketing strategy for gaming, Creed's Codex raised $52,000 total for a whopping 500% ROI on ad-spend.


   star   star2

  • Provision of proprietary training resources and templates for Landing Pages, Kickstarter Pages, Rewards and Stretch Goals, Facebook Ads, Email Marketing, Press outreach, and Influencer outreach marketing.
  • Coaching and consultation upon the execution of Kickstarter marketing strategies for pre-launch and live campaign.
  • Sponsored promotions within my own communities.
Pericle

Pericle

Crowdfunding Specialist / Public Relations

(2022)


Pericle is an app-driven tabletop RPG in a dangerous world of savage beasts, cunning foes, and forgotten evils. Can your heroes withstand the tide of enemies awaiting them?


While utilizing my proprietary marketing strategy for gaming, Pericle raised $182,000 total.


   star   star2

  • Provision of proprietary training resources and templates for Landing Pages, Kickstarter Pages, Rewards and Stretch Goals, Facebook Ads, Email Marketing, Press outreach, and Influencer outreach marketing.
  • Coaching and consultation upon the execution of Kickstarter marketing strategies for pre-launch and live campaign.
  • Public relations consultation.
  • Sponsored promotions within my own communities.
Broken Planet

Broken Planet

Crowdfunding Specialist

(2022)


A strategic prediction game by Pigeon Games. Players are scavenging tribes that collect powerful gemstones during their turn on the floating islands of the Broken Planet.


While utilizing my marketing consultation, Broken Planet raised $25,500 total.


   star   star2

  • Re-design and copywriting for lead-generation landing page.
  • Coaching and consultation upon the execution of Kickstarter marketing strategies for pre-launch and live campaign.
Space Chef

Space Chef

PPC Marketing / Crowdfunding Specialist

(2021)


Space Chef is a Stardew-like cooking game where you journey to other planets & making out-of-this-world recipes.


While utilizing my proprietary marketing strategy for video games, Space Chef raised $52,000 total.


   star   star2

  • Provision of proprietary training resources and templates for Facebook Ads, Email Marketing, Press outreach, and Influencer outreach marketing.
  • Coaching and consultation upon the execution of Kickstarter marketing strategies for pre-launch and live campaign.
  • Direction, script-writing, role casting, technical implementation, and event organization for livestream of final countdown and interview.
  • Grassroots posting on various social media communities.
Cranky Chinchillas

Cranky Chinchillas

PPC Marketing / Growth Hacking / Crowdfunding Specialist

(2021)


A cute and mischievous Card Game of hero battles, secret alliances, and betrayal with your friends and fluffy Chinchillas.


While utilizing my proprietary marketing strategy for board games, Cranky Chinchillas became fully funded within 20 minutes of launching and went on to raise $50,000 within the first 24 hours, and $150,000 total.


   star   star2

  • Graphic Design and Copywriting for PPC Ads.
  • Strategy, implementation and management of both PPC Ads & Ad Accounts for Facebook.
  • Creation, execution, and management of Social Media growth-hacking strategy & branded communities.
  • Grassroots posting on various social media communities.
  • Provision of proprietary training resources and templates for Facebook Ads, Email Marketing, and Influencer outreach marketing.
  • Coaching and consultation upon the execution of Kickstarter marketing strategies for pre-launch and live campaign.
Unicorn Ninjas

Unicorn Ninjas

PPC Marketing / Crowdfunding Specialist

(2020)


Fast-paced Card Game that unlocks programming skills! Play to learn HTML terms, website design, and more... Fun for friends and family!


Launched on Kickstarter in October 2020, raising 350% of it's initial funding goal.


   star   star2

  • Graphic Design and Copywriting for PPC Ads, Kickstarter Stretch Goal Chart & Video Poster.
  • Strategy, creation, implementation and management of both PPC Ads & Ad Accounts, for Facebook and Instagram.
  • Setup and analysis of conversion and Return On Investment, using Time-Series Data Modelling and Facebook Pixel data.
  • Consultation, strategy, and copywriting of publication outreach email templates & procurement of target-editor and publication lists.
  • Consultation on Kickstarter Page design & layout, and pre-launch advertising campaign.
Dwerve

Dwerve

Facebook Ads / Crowdfunding Specialist

(2020)


A beautifully crafted, Zelda-inspired action RPG with Tower-Defense combat. With my specialty consultation, I helped them raise $60,000 during our crowdfunding campaign. This was 600% above the campaign goal, raising 20% beyond our last stretch goal. The total raised was also 40% greater than the initial estimated expectations by the Dwerve team.


   star   star2

  • Graphic Design and Copywriting for PPC Ads.
  • Strategy, creation, implementation and management of both PPC Ads & Ad Accounts, for Facebook, Instagram, and Google Adwords.
  • Grassroots promotional activities, including content creation of social media posts and procurement of target-community lists.
  • Setup and analysis of conversion and Return On Investment, using Time-Series Data Modelling and Facebook Pixel data.
  • Consultation, strategy, and copywriting of publication outreach email templates & procurement of target-editor and publication lists.
  • Writing and promotion of technical articles and post-mortem on the Kickstarter Campaign.
  • Consultation on Kickstarter Page design & layout, funnel planning, web redirects scripting & strategy, audience psychographic profiling, pre-launch advertising campaign, finale celebration stream, and vertical & horizontal scaling strategies.
  • Scouting and contracting of specialist consultants for Facebook Ad troubleshooting and Google Adwords best-practices.
WikiCoder

WikiCoder

Branding / Programming Consultant

(2020)


Wiki dedicated to programming & math concepts across multiple languages.


   star   star2

  • Designed the logo.
  • Laid out roughly a hundred article pages, and have written extensively about certain topics including Procedural Generation, Noise Generation, Shaders, Vector Maths, Trigonometry, IK Solvers, Horizon Based Ambient Occlusion, and more.
CraftSmith

CraftSmith

Concept Artist / Branding

(2020)


Expand your workshop as you hire Workers, increase their Training, upgrade Tools and complete Research. Gather materials and craft objects in this idle crafting game.


   star   star2

  • Designed hand-painted & procedurally textured artwork and icons for app store promotions and in-game.
  • Consulted on app store optimization, copywriting strategy and marketing strategy.
  • Consulted on systems programming for saving-game functionality.
NOTABLE:
  • Over 200,000 players
/r/PlayMyGame

Reddit /r/PlayMyGame

Community Development / Community Management

(2020)


'Play My Game' is a subreddit of 50,000 subscribers, where Independent Game Developers around the globe share their demos, betas, and free-to-play games for community testing and promotions.


   star   star2

  • Moderation of community postings and comments.
  • Creation of new group rules and weekly events.
NOTABLE:
  • Over 50,000 members
Facebook IGD Group Network

Facebook IGD Group Network

Community Development / Management

(2019 - 2020)


Indie Game Developers is a network of Facebook groups, with themes ranging from video game promotion, discussion, talent acquisition, tutorials, and troubleshooting.


   star   star2

  • Moderation of member posts, new member-approvals, and dispute resolution between members and moderators.
  • Participation in Admin channels during discussion of creating new group rules, events, and creation/voting of contests.
  • Management of community contests.
  • Content creation & post management, acquiring over 30,000 engagements in 6 months.
NOTABLE:
  • 350,000+ Facebook group members!
Fit The Fat 3

Fit the Fat 3

Facebook Ads Specialist / Video Production

(2019)


Fit The Fat 3 is a Casual Mobile Game in the Runner genre. Start out Fat and get your character Fit by playing mini-games and scoring points.


Over 10,000,000 total players obtained via Paid Ads, Influencers, and K-Factor. I managed a Facebook ad spend of over $80,000 acquiring 1M+ downloads on the Apple store for iPhone, and produced video ads for in-app ad channels to gather Android Play Store downloads.


   star   star2

  • Designed and A/B tested ad campaigns, creatives, and copywrite text.
  • Programming of data analytic tools and development of financial projections.
  • Produced dozens of video ads for A/B testing.
  • Troubleshooted the client’s pre-existing campaigns, and improved the cost-per-download by over 100%.
NOTABLE:
  • 10,000,000+ downloads.
CHARTS:
  • #3 in Simulation 2019​ (iTunes)
  • #20 in Sports 2019​ (iTunes)
  • #30 in Role Playing 2019​ (iTunes)
Kingdom Of Night

Kingdom of Night

Audio / Software Engineer / Technical Art Director

(2018 - 2020)


80's themed Action RPG - Diablo meets Earthbound. Kingdom of Night is a horror, fantasy, action RPG set in small-town USA during the iconic 1980s that celebrates classic genre films of the era.


The Kickstarter raised 550% of it's campaign goal.


   star   star2

  • Design, Development, and Implementation of custom level editor & tile systems, including: rule-brush functionality, procedural level generation systems, co-development of Y-Sorting algorithms, tile templates, and technical documentation for other artists, editor scripts and wizards to improve and enhance the developer workflow, asset debugging and specification direction for other artists.
  • Development of general game shaders, UI shaders & canvas particle systems & VFX animations and associated systems, spell-casting particle systems, monster enemy VFX systems, world-environment particle systems and VFX systems, co-development of character animation systems, loot drop systems & animations, and water shaders/VFX systems.
  • Development of Dialogue, Item, and Interactable object trigger systems. Art design of item iconography, and trigger iconography.
  • Programming of Audio Engine and Music Engine for best performance and presentation to players, including a production pipeline & interface for ease-of-use by other developers.
  • Work with other project directors to develop, maintain and execute the audio vision and technical vision for the game.
  • Establishment of requirements via creation of an Audio Guide, and supervising other sound designer's work to accomplish consistency and quality,
  • Overseeing final mix and master. Design, mixing, and mastering of Sound Effects and Music for trailer and in-game.
Three Hearts

Three Hearts

Live Production / Community Building / Event Management

(2018 - 2022)


Three Hearts was the #1 most active gaming community on Facebook worldwide by daily engagement from 2021 to 2023, and is currently a super-fan community for my personal brand & studio.


   star   star2

  • Graphic design of 40+ original viral memes (1 million upvotes on reddit, /u/Zephir62, and 10 million engagements on Facebook Page including reshares).
  • Creation of Facebook Ads for growth-hacking, along with surveys, polls, ads and livestream event production for gaming clients.
  • Design of branding graphics, logo, brand symbol, and associated merchandise for sale within the Three Hearts channels.
  • Recruitment, management, and direction of admins. Moderation of community content, and dispute resolution. Design of community rules to substantially enhance content quality & engagement rates.
  • Hosting of contests, giveaways, special events and community gaming nights.
NOTABLE:
  • The most active gaming community on Facebook by daily engagement.
  • 1,000,000 average daily content views, and 25,000 daily engagements (2021 - 2023)
Nubbl

Nubbl

Graphic Design / UX Consultant

(2018)


Nubbl is a social network for gamers and developers. Find gamer creators and gamers. Artists, and streamers. PC, boardgames, consoles, RPGs, and all other platforms.


   star   star2

  • Graphic design of various icons throughout the website.
  • Consulted and iterated upon various User Interfaces and Flow of the website.
Long Live Santa

Long Live Santa

Technical Designer

(2017)


Santa is dead. Now the remaining bearded men of the Norwegian wilderness must fight to the death. Will you be the next Santa, or die in the snow? Punch, slice, outwit your way to a fulfilling new seasonal job in this online PvP third-person fighter!


The game was downloaded and played by over 500,000+ gamers within the first 3 weeks of it's release.


   star   star2

  • From concept, to 3D Modelling/Animation, to basic Gameplay-System integration of levels, level assets, props, and characters.
  • Development of level tools, procedural level objects.
  • Designed UI's visual appearance from skeleton.
  • Development of CG Shaders, level textures, lighting, and scripted Shuriken Particle Systems.
NOTABLE:
  • 500,000 downloads via Influencer, SEO and Viral Marketing within 3 weeks.
Cubers: Arena

Cubers: Arena

Sound Engineer

(2017)


Cubers: Arena is the Twin Stick Slasher where player has to defeat hordes of enemies on arenas to reclaim freedom. To achieve that he will unlock epic gear and mass destruction skills!


   star   star2

  • Studio mastering services for sound effects.
vGolf

vGolf

UI Artist

(2017)


vGolf is a mixed reality golf simulation and training system (Patented). It revolutionizes how golfers play and train by blending the real world with a virtual reality overlay.

   star   star2

  • Design of Font and UI Art for virtual reality overlay, including club selection, signs, distance markers, numbers, and animated speedometer.
Realtime Immersion RTI

Realtime Immersion RTI

Web Developer / Video Production

(2017)


Raising over $11.2 million in Series A seed funding, RTI ( http://www.rtistudios.com/ ) powers diverse products that deliver high-quality, low-cost 360° video for news, entertainment, and sports. The cameras are rugged, lightweight, portable, and even wearable, making them ideal for a range of applications and markets.


   star   star2

  • Produced and Rendered the Logo-Branding video utilizing V-Ray and After Effects.
  • Designed homepage and hero banner with sleek transition-FX.
NOTABLE:
  • Raised $11.2 million in Series A seed funding
Clone Drone In The Danger Zone

Clone Drone in the Danger Zone

Co-Creator, Artist & Creative Direction

(2016-2018)


Clone Drone in the Danger Zone is a third person sword fighter where any part of your body can be sliced off. With your mind downloaded into a robot gladiator, you must survive the sinister trials of the arena.


The game has garnered over 600+ million youtube viewers, and over 2,000,000 sales between all platforms.


   star   star2

  • Creation of Style Guides, standard Color palettes, Concept art, and Branding consultation.
  • Creative Direction via Moodboards from competitors/references/documents, design programming and animation of Cinematic Sequences, Storyboard consultation, and aided in gameplay element design and definition of features' vision & direction.
  • Concept to game-ready execution of modular environment assets, particle systems, weapons, vehicles, and characters.
  • C# Scripting of tools, procedural generation of art assets/levels, VFX, and CG programming of Shaders.
  • Design of promotional materials and artwork.
NOTABLE:
  • Over 600+ million verified youtube viewers!
  • Over 2,000,000 sales.
AWARDS:
  • "Top 100 of Indie of The Year 2016"​ (IndieDB)
  • #60 on the Top 100 Games of 2017 (Steam250.com)
  • #175 Top Games of 2021 (Glitchwave.com)
  • #125 Top-Rated Steam Game Of All Time (Steam250.com)
VR Amp

VR AMP

Technical Artist VFX / Branding

(2016)


vrAMP is the definitive music visualizer and player for virtual reality. No need to move your music into yet another media player, vrAMP plays music from any program or web browser. You can even use the in-game Steam web-browser to play music.


   star   star2

  • Branched an open-source MaxScript FGA generator, for which I debugged long-standing errors and further optimized plugin processing speeds by over 1000%
  • Developed a range of vector field FGAs, and designed their respective particle systems in Cascade for use in Unreal Engine 4.11
  • Creation of branding material, controller diagrams, competitive analysis, marketing plan, and potential design routes.
Forge Quest II

Forge Quest II

Character Art Contractor

(2016)


Forge Quest is a dungeon crawling RPG, set in a voxel world. Delve deep into the randomly generated dungeons to battle monsters, find treasure, and craft your equipment. Invite your friends to join your world and help you in your adventure.


   star   star2

  • Design, production, and testing of character races, genders, hairstyles, clothing, armors, and material color-palettes for their next title.
Bink! Video

Bink Video

UI Art Sub-Contractor

(2015)


Bink Video is a proprietary video file format (extension .bik) developed by RAD Game Tools, and primarily used for full-motion video sequences in video games. It has been used in over 10,000 games for Windows, Mac OS, Xbox 360, Xbox, GameCube, Wii, PlayStation 3, PC, PlayStation 2, Dreamcast, Nintendo DS, and Sony PSP.


   star   star2

  • Created UI Art for Bink Video's player in the Unreal Engine extension.
Staxel

Staxel

Level Design Contractor

(2015)


Staxel is a farming/discovery/town-life game with a relaxing, laid back feel. You can build a farm and grow a huge variety of crops and flowers, create an endless garden, make friends with the people in your village, explore the world around you, and discover amazing new sights.


   star   star2

  • Quickly adapted to Staxel's pipeline to meet a tight deadline.
  • Produced a level design and environment-based puzzles to prototype dungeon-based gameplay in time for a festival event.
  • Designed and modelling of dungeon block-set assets, based upon direction from the Lead Artist.
NOTABLE:
  • Over 50 million verified youtube viewers!
  • 300,000+ sales.
Orion: Prelude

Orion: Prelude

UI Design Consultant

(2015)


'ORION: Prelude' is an indie Sci-Fi shooter (FPS/TPS) that seamlessly blends together incredible visuals and addictive combat. It puts you and your friends together into intense, cinematic battles using some of the most incredible weaponry and amazing vehicles in which you must work or compete against one another to accomplish mission objectives, explore giant worlds and survive the devastating Dinosaur Horde.


A turn-around success with 10,000,000 copies sold after promotions went viral.


   star   star2

  • Aided in redesign of branded intro sequence and main menu UI.
  • Designed and prototyped a new Main Menu & associated assets using Photoshop and After Effects.
NOTABLE:
  • 10,000,000+ sales
Voxelnauts

Voxelnauts

Technical Designer / Audio Director / Co-Founder

(2014-2015)


Voxelnauts is an open-source Sandbox MMO, set in a metaverse of player-modded planets. Produced by Mark Kern, previously of fame for leading the World of Warcraft and Blizzard's franchises, we successfully secured pre-seed funding of $2M to develope a prototype for a Kickstarter campaign.


   star   star2

  • Environment Technical Lead, designed / produced art assets and biome contents. Helped develop new techniques, pipelines, systems for detecting neighboring blocks in the world-matrix, and for generating modular 3D environment art from 2D pixel / heightmap data.
  • Voxel Character Artist, taking rough sketches given by the Art Director to carry from pixel-orthographics to finish by Rigging/Animation with bones/keyframes/easing curves using in-house Flash tools developed by the Art Director. Created player animations and creature/monster animations.
  • Audio direction & vision, design, mixing, mastering, and implementation. Selection and supervision of external sound designers and music composers.
  • Integration, templating, and scripting of behaviors for art and sound assets in C.
  • Recruitment/management of a team of a half-dozen art/level/audio contractors and interns.
Oculus Arcade (Official Gear VR)

Oculus Arcade

UI Art Sub-Contractor

(2013)


Oculus Arcade is the official arcade emulator for Oculus Rift and ships pre-packaged with the Gear VR, where you can currently play over 20 classic titles from Sega, Midway, and Bandai Namco.


   star   star2

  • Produced and refined artistic direction for UI button graphics.
NOTABLE:
  • Over 5,000,000 players
Starbound

Starbound

Co-Creator, Character Design

(2013)


Starbound is an indie game produced by the UK-based independent game studio, Chucklefish Ltd.


Over 15,000,000+ sales across all platforms, and raised over $2.1 million in pre-order crowdfunding. My works were featured on key promotions from social media, newsletter, to the climax of the release trailer.


   star   star2

  • Co-Creation, Design, and Animation of what became known as the popular 'Vanity'​ cosmetic system.
  • Design and animation of Apex and Avian weapon & armor tiers.
  • Design and animation of tool equipment items and regular clothes such as pants, jean, shirts, sweaters, head gear, capes, etc.
  • Provided lighting art polish on various vehicles art designs, and contributed some environment assets.
  • Participated in gameplay/feature design discussion roundtables, and aided in developing the standard color palettes used across all characters in-game.
NOTABLE:
  • Over $2 million in crowdfunding raised!
  • Over 300 million verified youtube viewers!
  • 15,000,000+ sales.
  • #25 Top-Rated Steam Games Of All Time (Steam250.com)
AWARDS:
  • "Game, Simulation" (NAVGTR Awards 2017, nominated)
  • "Most Fulfilling Community-Funded Game 2017" (SXSW)
  • "Top 100 Greatest PC Games"​ (PC Gamer)
  • "IGN Editors' Choice Award" (IGN)
  • "No. 1 of Indie of The Year 2013" (IndieDB)
  • "Most Anticipated Game of 2013" (Indie Game Magazine)
  • "IGM Reader's Choice: The Best Indie Games of the Year 2012"
EMUya

EMUya

UI / Sound / Branding

(2013)


EMUya is the official NES emulator for the Ouya microconsole, with an indie game store to download and play homebrew NES titles.


   star   star2

  • Sound FX.
  • Logos/loading animation screens, and UI Art.
  • Consulted on the design of indie store and UI.
NOTABLE:
  • 200,000+ players.
Pinball Sundae

Pinball Sundae

2D Artist

(2013)


Pinball Sundae is block-breaker game for Android by Mike Belotti (R&D at High 5 Games), in which you use pinball flippers to hit your ball and break blocks. Different flipper flavors can be switched in and out for unique gameplay effects.


   star   star2

  • Redesigned existing programmer art & UI elements into a polished product within a limited timeframe.
  • Consulted on gameplay and level-design tweaks to improve difficulty balance and fun factor.
Microsoft XBox 360

Microsoft / Samsung

Brand Ambassador / Floor Manager

(2012-2013)


Mosaic Sales Solutions is a dedicated marketing agency for a multitude of Fortune 500 clients, such as CocaCola, NFL, Microsoft, and Samsung.


   star   star2

  • Performed promotional/marketing/training services for Microsoft (Xbox 360, Windows 8, and Windows RT), and Samsung (Galaxy 2 and 3) at Retail locations across the Eastern Pennsylvania region.
  • Acted as a repair technician for hundreds of Samsung phone/tablet/laptop service requests during an in-store setting at Best Buy, and performed marketing/sales services for the 2013 Samsung product-line.
  • Trained store employees/management on best practices, available promotional tools, and how to operate their new and upcoming technology products.
Firefall

Firefall (Firefall AR)

FX Artist / UI / Branding / Web Design

(2012)


   star   star2

  • Developed animations, shader maps (diffuse, normal, transparency, etc.), and 3D models for targeting with explosive, explosion crater, and bullet graphics.
  • Created health bar UI, website logo, and finetuned website layout from a Weebly template.
  • Intro/outro for Youtube video via Adobe After Effects.
  • Delivered as Keynote II presentation for the annual High Performance Graphics 2012 conference, in Paris, France.
NOTABLE:
  • Over 5 million players!
Retrotation

Retrotation

Music Producer / Sound Engineer / Marketing

(2010-2012)


Retrotation is a music project I began in 2010 centered around videogame, classical, and electronic music. I have released three official works ('Classic EP'​, 'Parting Ways EP', 'Press Start EP') and a handful of unsigned tracks, which have been downloaded a combined 100,000+ free and paid copies, and about 10,000,000 verified listens.


   star   star2

  • Provided licensing, studio production & mastering services for dozens of clients, sponsors and other artists such as Aspire Ventures, Asher Jay, and Sugarpill.
  • Live performance DJ tour across the USA from coast to coast at 6 different venue locations.
  • Active in 2010-2012, had multiple songs featured in many of the largest promotional venues for music: BBC Radio 1 (Xmas Eve Special) 2010, MadDecent, EarMilk, , #1 on HypeMachine, etc.
  • Regularly released popular instrument patches for Propellerhead Reason and wrote sound-design tutorials for publications such as SilenceNoGood: https://silencenogood.net/reason-dubstep-tutorial/
NOTABLE:
  • Over 10 million verified listens across Soundcloud, YouTube, and radio!
  • 100,000+ downloads.
CHARTS:
  • #1 on HypeMachine (hypem.com)
  • 4x Top 10 on HypeMachine (hypem.com)
AWARDS:
  • "Artist of the Month" (Chillbass)
SIGGRAPH 2008

SIGGRAPH 2008

Beyond Programmable Shading: In Action

Hi-Poly Artist / Talent Acquisition


SIGGRAPH is the annual conference on computer graphics by the ACM SIGGRAPH organization. The keynote presentation topic was for demonstrating Sparse Voxel Octree ray-casting in real-time gaming. It became a major success, heavily referenced by the voxel game-dev industry such as Minecraft / Mojang and has been cited as "a huge inspiration" that initiated development for the core 'Nanite' technology in Unreal Engine 5: https://x.com/JonOlick/status/1260597975702802433


   star   star2

  • Designed assets for testing the rendering software.
  • Scouted talent for a showcase piece and connected Oriku Inc. with Dmitry Parkin, first-place winner of the "Dominance War III" PolyCount 2008 challenge.
Alien Monster Bowling League

Alien Monster Bowling League

UI Artist / UIX Designer

(2007)


Alien Monster Bowling League is a quirky and space-based bowling game developed originally for PC, Xbox 360, and Wii, using the Vicious Engine. Originally released as 'Galactic Bowling,'


   star   star2

  • Designed a UI system for handling/throwing the bowling ball, and developed the associated art assets.
NOTABLE:
  • 200,000+ sales between all console and PC platforms.
Pirates Vikings and Knights II

Pirates Vikings Knights II

Co-Creator, Environment Artist

(2004-2008)


Pirates, Vikings and Knights II is a multiplayer team-based first-person action video game, developed as a total conversion mod for Valve's Source engine. I contributed to development of the first prototypes and release under the moniker "Zephyr" (credits: octosharkstudios.com/pvkii/#/about/pvkii-team).


   star   star2

  • Modelled, unwrapped, and painted textures for Knights & Vikings environment assets and props.
  • Participated in gameplay design roundtables during early design and pre-alpha development, most notably using inspiration from Jedi Knight series to help shape the directional-based combat system.
NOTABLE:
  • 2,000,000+ players.
PEAK CHART POSITIONS:
  • #53 Top-Rated Steam Game of All Time (Steam250).
AWARDS:
  • "2008 Mod of the Year" (ModDB)
Master Sword: Continued

Master Sword: Continued

Weapon / Prop Artist

(2003 - 2005)


Master Sword is a cult-classic multiplayer roleplaying action videogame, developed as a total conversion modification released for Half Life 1. Features a character development system, weapon classes such as axes, swords, bows, and small arms, a magic system, and more.


   star   star2

  • Created various first-person weaponry and props around town.
  • Level design in Hammer Editor.
  • Was highly active in building their online forums' community.
NOTABLE:
  • 100,000+ players.


Other Industry Clients:
Artist
Coding
UIX
Audio
Video
Marketing
Director
Tandem Shower

Tandem Shower

Crowdfunding Specialist / Influencer Marketing

(2022)


Tandem Shower is a dual shower made for couples. Every Tandem Shower includes a valve that controls the flow between the two showerheads. You have total control over the distribution of water.


While utilizing my proprietary marketing strategies, Tandem Shower raised $775,000 total.


   star   star2

  • Provision of proprietary training resources and templates for Landing Pages, Kickstarter Pages, Rewards and Stretch Goals, Facebook Ads, Email Marketing, Press outreach, and Influencer outreach marketing.
  • Coaching and consultation upon the execution of Kickstarter marketing strategies for pre-launch and live campaign.
  • Helped generate an influencer marketing campaign with custom outreach templates, media contact list, and outline for an influencer kit.
LaunchBoom

LaunchBoom

Director of Product Development

(2021 - 2022)


At LaunchBoom I managed the development & team of their initial Consulting and Accelerator programs. While handling our mutual client 'Pericle' I invented a solution to the bulk application of deposits, a system for VIP Exclusive Add-ons, and training LaunchBoom staff on usage.


   star   star2

  • Created a templated crowdfunding system and associated course materials, which raised over $10m on Kickstarter and Indiegogo during the first year of operation.
  • Direction & Management of activities for a team of 5 consultants and product developers. Worked with sales reps to develop sales strategies & sales materials for the program.
  • Direction & Development of Academy curriculum plan and guide materials. Writing and production of video courses. Research and tracking of student client progress to optimize course materials.
  • Design & Management of Academy website, course layout, and course description format.
  • Mapped the business-process of Agency departments & partner companies, and developed optimizations & solutions to various company bottlenecks.
  • Production of funnel templates, email templates, video tutorials, checklists, guides, worksheets, calculators, and other tools.
  • Production of assets for student clients, such as campaign video production, audio mix/mastering, page design, ad creative design, copywriting, and custom pricing-tier strategies.
  • Academy Facebook Group management. Handling of email support tickets, one-on-one coaching calls.
  • Hosting weekly Office Hours group coaching sessions, created a bi-weekly presentation & question-management system, and writing of voice scripts to guide the session's flow.
  • Hosting weekly Guest Speaker presentations, featuring directors and c-level executives from various top app services, media-buying, and creative agencies. Designed promotional graphics for the presentations.
NOTABLE:
  • Raised over $100 million in crowdfunding (total company)
  • Raised over $10 million in crowdfunding (my department)
AWARDS: (I was not involved in this particular project)
  • #1 top-grossing​ campaign of 2021 (Indiegogo)
TSTAND2

TSTAND2

Crowdfunding Consultant

(2021)


TSTAND is a laptop, tablet, and phone stand that fits all devices and models & has raised over $500,000 in two campaigns on Kickstarter.


   star   star2

  • Provision of proprietary training resources and templates for Facebook Ads and PR marketing.
  • Coaching and consultation upon the execution of Kickstarter marketing strategies for a Pre-Launch campaign.
  • Consultation and troubleshooting services for Facebook Ad management.
Redux & Co.

Redux & Co.

Crowdfunding Specialist

(2021)


Watches inspired by mission critical pilot instruments, fused with diver tools. Titanium. Powered by your adventures. Ridiculous value.


   star   star2

  • Strategy and planning for Kickstarter campaign, pre-launch campaign, and $1 reservation system.
  • Handling of cross-promotion marketing and associated outreach.
NOTABLE:
  • Raised over $500,000 in crowdfunding
NextErgo

NextErgo

PPC Marketing / Crowdfunding Specialist

(2021)


NextErgo is the next generation of Ergonomic Standing Desks. Our AI-powered desks help improve your posture, mood, energy levels, and overall productivity. Launching this May 2021 on Kickstarter.


   star   star2

  • Creation and execution of ad campaign marketing strategy and content angles.
  • Video production, video editing, and copywrite for PPC ads.
  • Facebook ad campaign management.
  • Wireframe mockups and copywriting for Newsletter Signup and VIP Reservation landing pages.
  • Business relations and handling communications between NextErgo and Indiegogo & Kickstarter, to navigate their exclusive sponsorship offers.
Vizbl

Vizbl

PPC Marketing / Crowdfunding Specialist / Growth-Hacking

(2021)


Vizbl, a new N-95 equivalent mask which combines the elegance of a baseball cap with features of Powered Respirators to provide 2.5x improvement in filtration.


   star   star2

  • Creation, execution, and management of prelaunch campaign strategy & ad accounts.
  • Creation, execution, and management of Social Media growth strategy.
  • Copywriting for PPC Ads.
  • Compositing of 3D product rendering onto stock photography.
  • Development and implementation of software stack to execute a $1 reservation system.
  • Wireframe mockups for VIP Reservation and Facebook Group Invite landing pages.
Ripples Hair

Ripples Hair

Front-End Developer

(2021)


The hairstyle made popular by Kim Kardashian can now effortlessly be created within seconds using the hottest trending Ripples Hair Waver this season.


   star   star2

  • Fixed various front-end bugs left from previous developer.
  • Integrated conversion-boosting elements & widgets throughout the site to enhance the customer experience.
  • Designed visual charts of value propositions on product pages.
Meal Cards

Meal Cards And More Savings!

Front-End Designer

(2021)


Meal Cards And More Savings! provides AWESOME savings coupons, discounts, and more for resale sent right to your home.


   star   star2

  • Consultation upon, and optimization of UIX & website layout to increase sales conversion. Built with Wix.
Tao Tea Tumbler

Tao Tea Tumbler

PPC Marketing / Crowdfunding Specialist

(2020)


The Tao Tea Tumbler is a multi-functional tea infuser that allows you to brew the perfect cup of loose leaf tea on the go.


Launched on Kickstarter in November 2020, the campaign raised 220% of it's initial funding goal.


   star   star2

  • Design and Management of both PPC Ads & Ad Accounts, for Facebook & Instagram.
  • Consulted on email & mailing list strategy.
  • Analysis of conversion and Return On Investment, using Time-Series Data Modelling.
Avorai

Avorai

Back-End Developer / Branding & Business Consultant

(2020)


Avorai is a gemstone-based cosmetic line, initially launching in Q2 2020 with private-labelled Rose-Quartz Body Scrub and Moisturizing Spray.


   star   star2

  • Created a Business Plan Document and consulted on business strategy and marketing strategy. Planning services included Business Statements, Business Opportunity Brief, Market Analysis, Product Briefs, Buyer Personas, Perceptual Map, Marketing Brief, Funnel Map, Creative Brief, and Branding Brief.
  • Consulted on copywriting patterns and copywriting strategy.
  • Designed UIX wireframes, vector UI graphics, typography selection, and color palettes.
  • Developed the Back-end and Front-end of custom Shopify Theme via Liquid API, jQuery, and SCSS.
Boxerworks Performance

Boxerworks Performance

Web Developer / Shopify Specialist

(2020)


Boxerworks Performance specializes in everything Subaru - parts, repairs and maintenance, special diagnostics, installs, engine rebuilds, tire services & more! ( www.boxerworksperformance.com )


   star   star2

  • Installation and integration of Affirm monthly financing app, using jQuery and Liquid API.
L'Dorée Body

L'Dorée Body

Back-End Developer / Branding & Marketing Director

(2019)


L'Dorée Body is a CBD Coffee body-scrub cosmetic, backed by capital investment.


   star   star2

  • Created a Brand Brief, Branding Guides, Perceptual Maps, Marketing Brief, Funnel Maps, Buyer Personas, and PPC & Leadpage Marketing Strategy documentation.
  • Devised company financial projections, department budgets, product pricing and pricing strategy.
  • Designed UIX wireframes, photo touch-up, vector UI graphics, color palettes, and production art for product packaging.
  • Developed the Front-end and Back-end of custom Shopify Theme via Liquid API, jQuery, and SCSS.
  • Drafted and revised copywrite for website funnel, navigation, product description, usage manual, and packaging.
  • Designed, executed, and acquired participants for marketing studies and surveys.
  • Scouted, retained, managed, and directed contractor talent via group meetings, project requirement documentation, and branding & marketing guides.
Isasuma Art LLC

Isasuma Art LLC

SEO / Etsy Consultant

(2019)


Isasuma is a team of like-minded individuals looking to support and promote artisans from local communities, indigenous tribes, small businesses and humanitarian causes through fair trade practices and equal opportunities.


   star   star2

  • Setup of Etsy Store with best practices for SEO.
EnsoCDN
Orlando Dog Walkers

Orlando Dog Walkers

Web Development / UIX Consultant

(2019)


Orlando Dog Walkers provides pet sitting services to the greater Orlando Area - also pet taxi, exotic pet sitting, poop scooping, dog training and more. A medium-sized company with 5 employees, having serviced thousands of happy dog owners.


   star   star2

  • Debugging of broken plugins and widgets on website.
  • Consultation on how to improve UIX and client-acquisition via website email signups.
  • Dog walker, pet sitting, client-customer interactions.
Shredded-Union

Shredded Union

Web Development / Ebook Design / Marketing Consultant

(2019)


One of the largest social networks for Fitness and Bodybuilding!


   star   star2

  • Creation of branding templates for eBook series.
  • Design of eBooks for popular athletes.
  • Developed copywrite for sales portals and funnels.
  • Front-end Web Design for their website and sales portals.
  • Graphic design of social media promotional content.
NOTABLE:
  • Over 10 million subscribers!
Ecommerce Academy

ECommerce Academy

Full Stack Developer / Marketing Technology

(2018 - 2020)


ECommerce Academy has amassed over 1,400,000 followers on Instagram @ecommerce_mentor (now @tonyfolly.fit), and over 23,000+ marketing courses sold and workshop attendees.


ECommerce Academy ( theecomacademy.com & eCommerceMentor.academy ) is a marketing masterclass created by Tony Folly, creator of multiple multi-million dollar start-up companies with over 10 million collective social-media followers.


   star   star2

  • Clickfunnels: Stripe integration and Email list sync automation.
  • Shopify: Back-end Customizer interfaces and front-end design for Shopify sites using Liquid API, jQuery, and SCSS. Created AJAX scripts and utilized Storefront API to A/B test Shopify cart and ReCharge Subscriptions for modifying the cart, auto-applying discounts, and redirecting to checkout.
  • WordPress: Overhauled PHP for two WordPress themes, and created new PHP templates, widgets, and functionality.
  • Designed Email Marketing campaigns, including lead-pages, subscription pages, email templates, campaign flows, and technical documentation on usage.
  • Server Administration, coding Apache HTTPS in .htaccess, handling DNS settings, DKIM signatures, SPF records, page redirections, SSL certificate management, and backups & reversions. Transfer of XML customer data between eCommerce shops.
  • Produced marketing materials, funnel maps, and technical documentation for e-course sales techniques.
  • Managed the agency-side of the business, consulting & executing jobs for dozens of high-ticket clients. Business & funnel planning, web design, graphic design, and influencer campaigns.
NOTABLE:
  • Over 1.4 million subscribers!
  • 23,000+ courses sold!
  • #15,000 in Top Ranked 2020 Instagram Accounts (HypeAuditor)
Nightmode

Nightmode

Video Production / Branding

(2019)


Nightmode is a synthwave band. It is pretty cool.


   star   star2

  • Created a logo animation within After Effects for use in video and related promotional materials.
Tierra De Fuego

Tierra De Fuego

Web Developer

(2019)


Tierra De Fuego ( http://www.eastonfuego.com/ ) is a family friendly modern Colombian restaurant, where you can find the best of our cuisine, everything we make will taste like home and seasoned with LOVE.


   star   star2

  • Designed and developed website, sliders, iFrame system, and other sleek UX features using WordPress and Javascript.
  • Wrote Business Proposals to secure funding from local grants.
Force 1

Force 1

Web Developer

(2018)


Force 1 is a entertainment services firm that provides exotic luxury car rentals, hospitality rider, and security personnel for celebrity events through the Melbourne, Australia area.


Previous celebrity clients include Post Malone, Quavo, Migos, French Montana, Ice Cube, Kevin Hart, Tyga, Timberland, BMW, and more. ( http://f1s.com.au/ , Instagram @force1lifestyles ).


   star   star2

  • Designed "Security" website using WordPress.
  • Developed custom Shopify theme for "Apparel" web store using Liquid API and jQuery.
Interval Mode Generator

Interval Mode Generator

Software Engineer

(2018)


Interval Mode Generator is a diatonic Musical Scale Generator based upon the Common Tone Theorem. Mix and match major modes to intuitively express emotions through your melodies. The power is at your fingertips!


   star   star2

  • Design and Development of website and PC / Android Application, which was independently conceived of and expands upon the mathematical diatonic set Common Tone Theorem.
Delivered Fresh

Delivered Fresh

Production Artist

(2018)


Delivered Fresh are a group of local farmers who are working together to make sure our community gets the freshest, most healthy food possible.


   star   star2

  • Design of Postcard-style table-top advertisements in Illustrator.
NOTABLE:
  • $2,000,000+ annual revenue.
Aspire Ventures

Aspire Ventures

Sound Engineer Contractor

(2017)


Aspire Ventures, LLC is a venture capital firm specializing in early-stage investments. The firm primarily invests in the technology sector which includes digital campaign management, patient engagement, mobile payments, financial risk management, management of complex information systems, cloud services, industrial management, and healthcare.


   star   star2

  • Composition, Sound Design, Mixing, and Studio Mastering services.
SlayIT.shop

SlayIT

Web Developer / Video Editor

(2017)


The ultimate backless, strapless, stick on, push up bra for max cleavage


   star   star2

  • Developed custom Shopify theme using jQuery and Liquid API, front-end and back-end.
  • Created customizer interface for client via Liquid Schema and API.
  • Copywrite services for FAQ, product features, and product description.
  • Video production and editing services for product display video.
  • Consulted on marketing strategy.
SimplyShredded

Simply Shredded

Web Developer / Server Administrator

(2017 - 2020)


The #1 Bodybuilding & Fitness Informational Site Online! Over 10,000,000 followers between all social media platforms.


   star   star2

  • Web Development and Design for their article database site, ebook sales portal, and apparel webstore for both WordPress and Shopify sites using PHP, jQuery, Liquid API, and SCSS.
  • Optimization of page load speed, lazy loading, content caching, security CDN via CloudFlare.
  • Server Administration & Migration to new servers, SSL Certificate management, PHP versioning, backups, feature enabling & maintenance via cPanel and WHM.
NOTABLE:
  • Over 10 million social followers!
  • $1,000,000+ in annual revenue
  • #10,000 in Top Ranked 2016 Instagram Accounts (HypeAuditor)
WatchnSeeMe

WatchnSeeMe

Production Artist

(2016)


WatchnSeeMe produces events and merchandise for local performing artists in the Lehigh Valley, Pennsylvania.


   star   star2

  • Graphic Design for event fliers, stickers, and t-shirts.
The Common Wheel

The Common Wheel

Music Composition

(2014)


The Common Wheel is a community bike center to encourage bicycling in Lancaster, Pennsylvania USA, by creating a fun welcoming space providing access to hands-on education and tools to all members of the community. The video was developed for their successful fundraising campaign, which raised 230% of it's goal.


  • Produced the music backtrack for their crowdfunding campaign.
Roaring Brook Market

Roaring Brook Market

Music Composition

(2013)


Roaring Brook Market is a sustainably-minded local foods grocery store and café in downtown Lancaster, Pennsylvania USA. I produced music and aided in the production of their Kickstarter campaign.

Samsung

Microsoft / Samsung

Brand Ambassador / Floor Manager

(2012-2013)


Mosaic Sales Solutions is a dedicated marketing agency for a multitude of Fortune 500 clients, such as CocaCola, NFL, Microsoft, and Samsung.


   star   star2

  • Performed promotional/marketing/training services for Microsoft (Xbox 360, Windows 8, and Windows RT), at Walmart locations across the Lehigh Valley, Pennsylvania USA.
  • Acted as a repair technician for hundreds of Samsung phone/tablet/laptop service requests during an in-store setting at Best Buy, and performed marketing/sales services for the 2013 Samsung product-line.
  • Trained store employees/management on best practices, available promotional tools, and how to operate their new and upcoming technology products.
Asher Jay

Asher Jay

Audio Engineer

(2012-2013)


Creative conservationist Asher Jay is a designer, artist, writer, and activist who uses creative concepts and design to advance animal rights, sustainable development, and humanitarian causes. Her art, sculpture, design installations, films, and advocacy advertising campaigns (including these listed below) have been presented through National Geographic, United Nations, TED Talks, and more.


   star   star2

  • "Full Breath Cycle" premiered at the United Nations. The short film focuses on the cyclical nature of life, and the inherent connection shared between organisms on land and in water, anchoring all life in the instant of a single inhale and exhale. In an effort to understand what it means to breathe one's last and what is encompassed and united by a single breath, Asher Jay's Last Exhale project explores the value of life by grieving loss. I acted as audio engineer and performed studio mastering services.
  • "Transparent Sea" is a conceptual, animated, silent short that examines the impact of humanity's daily consumer choices on marine ecosystems. Premiered at the Blue Water Film Festival, I acted as audio engineer and performed studio mastering services.
Discovery Channel

Discovery Channel's Amish Mafia

Background Actor

(2012)


Amish Mafia is an American reality television series that debuted on December 12, 2012, on the Discovery Channel. The series follows "Lebanon Levi", along with three of his assistants, who are purported to be a "mafia" in an Amish community.


   star   star2

  • I was a contracted background actor for Episode 4, Season 1 of Amish Mafia.
Fotofuze

Fotofuze

Co-Founder, Video Production / UI / Marketing / Software Developer

(2008 - 2017)


Fotofuze.com is automated professional photography cleanup service for Etsy users and Ecommerce Businesses. Over 500,000+ Etsy shops have connected accounts with Fotofuze.


   star   star2

  • Developed the company business plan, break-even analysis, financial projections, competitive analysis, and more. Provided marketing and monetization strategy consultancy.
  • Designed UI iconography, branding graphics, logos, and branding animations.
  • Developed UX / UI flowcharts. Directed and produced tutorial videos for web-app and mobile-app : contracting & management of a voice actor, recorded audio, SFX design, raw footage, produced animations in After Effects. Since the video's release on the homepage, sign-ups on the website increased over 250%.
  • Developing a software plugin for Photoshop to integrate with Fotofuze API via REST and Javascript.
NOTABLE:
  • Over 500,000 Etsy Shop-owner users!


Languages:
  • HTML5
  • CSS
  • SCSS
  • JavaScript
  • jQuery
  • AJAX
  • PHP
  • Liquid
  • MaxScript
  • GLSL
  • CG
  • C#
PHOTOSHOP PLUGINS
PHOTOSHOP: Socket Connection, Cookie Management System, HTTP I/O


/*
// BEGIN__HARVEST_EXCEPTION_ZSTRING

Fotofuze...
filter
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"); }
    
};
PHOTOSHOP: Isometric Grid Generator




// 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
filter
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();
}


PHOTOSHOP: Texture Atlas Generator



// 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
filter
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;
}

UNITY
C#: IK Solver
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;
	}
}
C#: Networking, Sync Position & Rotation

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);
            }
        }
    }
    
}


C#: Line-by-Line Text Writer
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;
    }
}
C#: Transform Throw & Jump Tweening
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);
	}
}

C#: Isometric Y Sorting
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;
    }
}

C#: Procedural 9-Slice 3D Primitives
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;
		}
	}
}


C#: Voxel-on-Voxel Destruction
// 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);
    }
SHADERS
CG Shader: Horizon-Based Ambient Occlusion
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
        }

CG Shader: 2D Isometric Water

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
        }
    }
}
CG Shader: Dissolve to ashes
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"
}

CG Shader: Volumetric Fog

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"
}