text:
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { GuestTopNavComponent } from './guest-top-nav.component';

describe('GuestTopNavComponent', () => {
  let component: GuestTopNavComponent;
  let fixture: ComponentFixture<GuestTopNavComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ GuestTopNavComponent ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(GuestTopNavComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
  it('should render menu in the top navigation', async(() => {
    const compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('nav').textContent).toContain('LinkDisabled');
  }));
});


text:
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-guest-top-nav',
  templateUrl: './guest-top-nav.component.html',
  styleUrls: ['./guest-top-nav.component.scss']
})
export class GuestTopNavComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

}

repo_language: TypeScript

mime_type: text/x-java


text:
        Home (current)

        Link

        Disabled


text:
import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-guest-footer',
  templateUrl: './guest-footer.component.html',
  styleUrls: ['./guest-footer.component.scss']
})
export class GuestFooterComponent implements OnInit {

  constructor() { }

  ngOnInit() {
  }

}


text:
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { GuestFooterComponent } from './guest-footer.component';

describe('GuestFooterComponent', () => {
  let component: GuestFooterComponent;
  let fixture: ComponentFixture<GuestFooterComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ GuestFooterComponent ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(GuestFooterComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
  it('should render brand name in a footer tag', async(() => {
    const compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('footer').textContent).toContain('keep_growing');
  }));
});


text:
export const environment = {
  production: true
};


text:
// This file can be replaced during build by using the `fileReplacements` array.
// `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
// The list of file replacements can be found in `angular.json`.

export const environment = {
  production: false
};

/*
 * In development mode, to ignore zone related error stack frames such as
 * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
 * import the following file, but please comment it out in production mode
 * because it will have performance impact when throw error
 */
// import 'zone.js/dist/zone-error';  // Included with Angular CLI.


text:
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts

const { SpecReporter } = require('jasmine-spec-reporter');

exports.config = {
  allScriptsTimeout: 11000,
  specs: [
    './src/**/*.e2e-spec.ts'
  ],
  capabilities: {
    'browserName': 'chrome'
  },
  directConnect: true,
  baseUrl: 'http://localhost:4200/',
  framework: 'jasmine',
  jasmineNodeOpts: {
    showColors: true,
    defaultTimeoutInterval: 30000,
    print: function() {}
  },
  onPrepare() {
    require('ts-node').register({
      project: require('path').join(__dirname, './tsconfig.e2e.json')
    });
    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
  }
};


text:
import { AppPage } from './app.po';

describe('workspace-project App', () => {
  let page: AppPage;

  beforeEach(() => {
    page = new AppPage();
  });

  it('should display welcome message', () => {
    page.navigateTo();
    expect(page.getParagraphText()).toEqual('Welcome to angular-multi-layout-scaffolding!');
  });
});


text:
import { browser, by, element } from 'protractor';

export class AppPage {
  navigateTo() {
    return browser.get('/');
  }

  getParagraphText() {
    return element(by.css('app-root h1')).getText();
  }
}


text:
# Contributing

When contributing to this repository, please first discuss the change you wish to make via issue,
email, or any other method with the owners of this repository before making a change.

Please note we have a code of conduct, please follow it in all your interactions with the project.

## Code of Conduct

### Our Pledge

In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.

### Our Standards

Examples of behavior that contributes to creating a positive environment
include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
  address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

### Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.

### Scope

This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.

### Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at jdnichollsc@hotmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.

### Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

mime_type: text/html


text:
# Code Intermediate
A list of intermediate topics working with C# in Unity

  Generic Lists

  ```csharp
  using UnityEngine;
  using System.Collections;
  using System.Collections.Generic;

  public class SceneController: MonoBehaviour {

      public List<string> shapes;

      void Start()
      {
          shapes = new List<string> { "circle", "square", "triangle", "octagon" };
          shapes.Add("rectangle");
          shapes.Insert(2, "diamond");

          shapes.Sort();
          var triangleShape = shapes.Find(s => s == "triangle");
      }
  }
  ```

  Dictionaries

  ```csharp
  using UnityEngine;
  using System.Collections;
  using System.Collections.Generic;

  public class SceneController: MonoBehaviour {
      public Dictionary<string, Shape> shapeDictionary;

      void Start()
      {
          shapeDictionary = new Dictionary<string, Shape>();
          var shapeName = "Circle";
          var circle = new Shape { Name = shapeName };

          shapeDictionary.Add(shapeName, circle);
      }

      private void SetColorByName(string shapeName, Color color)
      {
          shapeDictionary[shapeName].SetColor(color);
      }

      void Update()
      {
          if (Input.GetKeyDown(KeyCode.C))
          {
              SetColorByName("Circle", Color.red);
          }
      }
  }
  ```

  Queues

  ```csharp
  using UnityEngine;
  using System.Collections;
  using System.Collections.Generic;

  public class EnemiesController: MonoBehaviour {
      public Queue<Enemy> enemyQueue;

      void Start()
      {
          enemyQueue = new Queue<Enemy>();
          enemyQueue.Enqueue(new Enemy { Name = "Boss1" });
          enemyQueue.Enqueue(new Enemy { Name = "Boss2" });
          enemyQueue.Enqueue(new Enemy { Name = "Boss3" });
      }

      public Enemy GetFirstEnemy()
      {
          if (enemyQueue.Count > 0) {
              var enemy = enemyQueue.Dequeue();
              // Call any method if you want
              enemy.Animate();
              return enemy;
          }
          Debug.Log("Enemy queue is empty");
          return null;
      }
  }
  ```

  Stacks

  ```csharp
  using UnityEngine;
  using System.Collections;
  using System.Collections.Generic;

  public class EnemiesController: MonoBehaviour {
      public Stack<Enemy> enemyStack;

      void Start()
      {
          enemyStack = new Stack<Enemy>();
          enemyStack.Push(new Enemy { Name = "Final boss" });
          enemyStack.Push(new Enemy { Name = "Superboss" });
          enemyStack.Push(new Enemy { Name = "Miniboss" });
      }

      public Enemy GetNextBoss()
      {
          if (enemyStack.Count > 0) {
              var enemy = enemyStack.Pop();
              // Call any method if you want
              enemy.Animate();
              return enemy;
          }
          Debug.Log("Final boss was killed");
          return null;
      }
  }
  ```

  Coroutines

  ```csharp
  using System.Collections;
  using System.Collections.Generic;
  using UnityEngine;

  public class TaskController : MonoBehaviour
  {
      public List<Task> tasks;
      private Coroutine runTasksRoutine;

      private IEnumerator ExecuteTasksInSeries()
      {
          foreach(var task in tasks) {
              task.doSomething();
              // Delay before execute next task
              yield return new WaitForSeconds(2);
              task.doSomethingElse();
              // Other delay options
              // WaitUntil()
              // WaitWhile()
              // WaitForEndOfFrame()
              // WaitForFixedUpdate()
              // WaitForSecondsRealtime()
          }
          // Pause the game
          Time.timeScale = 0;
          yield return new WaitForSecondsRealtime(1);
          Debug.Log("I just wasted for a second, it's not affected by scaled time");
      }

      void Start()
      {
          tasks = new List<Task>();
          // Add your tasks here
          runTasksRoutine = StartCoroutine(ExecuteTasksInSeries());
      }

      void Update()
      {
          if (Input.GetKeyDown(KeyCode.S))
          {
              // Stop all coroutines
              StopAllCoroutines();
              // Specify the coroutine
              StopCoroutine(runTasksRoutine);
          }
      }
  }
  ```

  Events

  - EventManager.cs

  ```csharp
  using UnityEngine;
  using System.Collections;

  public class EventManager : MonoBehaviour
  {
      public delegate void ClickAction();
      public static event ClickAction OnClicked;

      void OnGUI()
      {
          if(GUI.Button(new Rect(Screen.width / 2 - 50, 5, 100, 30), "Click"))
          {
              if(OnClicked != null)
                  OnClicked();
          }
      }
  }
  ```

  - TeleportScript.cs

  ```csharp
  using UnityEngine;
  using System.Collections;

  public class TeleportScript : MonoBehaviour
  {
      void OnEnable()
      {
          EventManager.OnClicked += Teleport;
      }

      void OnDisable()
      {
          EventManager.OnClicked -= Teleport;
      }

      void Teleport()
      {
          Vector3 pos = transform.position;
          pos.y = Random.Range(1.0f, 3.0f);
          transform.position = pos;
      }
  }
  ```

  - TurnColorScript.cs

  ```csharp
  using UnityEngine;
  using System.Collections;

  public class TurnColorScript : MonoBehaviour
  {
      void OnEnable()
      {
          EventManager.OnClicked += TurnColor;
      }

      void OnDisable()
      {
          EventManager.OnClicked -= TurnColor;
      }

      void TurnColor()
      {
          Color col = new Color(Random.value, Random.value, Random.value);
          renderer.material.color = col;
      }
  }
  ```


text:
# Code Basics
A list of basics topics working with C# in Unity

  Player Movement

  ```csharp
  using UnityEngine;
  using System.Collections;

  public class PlayerController : MonoBehaviour {

      public float speed;

      private Rigidbody rb;

      void Start ()
      {
          rb = GetComponent<Rigidbody>();
      }

      // FixedUpdate is called at a fixed interval and is independent of frame rate.
      // Put physics code here.
      void FixedUpdate ()
      {
          float moveHorizontal = Input.GetAxis ("Horizontal");
          float moveVertical = Input.GetAxis ("Vertical");

          Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);
          rb.AddForce (movement * speed);
      }
  }
  ```

  Moving Camera

  ```csharp
using UnityEngine;
using System.Collections;

public class CameraController : MonoBehaviour {

    public GameObject player;

    private Vector3 offset;

    void Start ()
    {
        offset = transform.position - player.transform.position;
    }

    void LateUpdate ()
    {
        transform.position = player.transform.position + offset;
    }
}
  ```

  Collectable Objects

  ```csharp
using UnityEngine;
using System.Collections;

public class PlayerController : MonoBehaviour {

    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.CompareTag ("Pick Up"))
        {
            other.gameObject.SetActive (false);
        }
    }
}
  ```

  Score Display

  ```csharp
using UnityEngine;
using UnityEngine.UI;
using System.Collections;

public class PlayerController : MonoBehaviour {

    public Text countText;
    public Text winText;
    private int count;

    void Start ()
    {
        count = 0;
        SetCountText();
        winText.text = "";
    }

    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.CompareTag ( "Pick Up"))
        {
            count = count + 1;
            SetCountText ();
        }
    }

    void SetCountText ()
    {
        countText.text = "Count: " + count.ToString ();
        if (count >= 12)
        {
            winText.text = "You Win!";
        }
    }
}
  ```

  Behaviour Components

  ```csharp
using UnityEngine;
using System.Collections;

public class ExampleBehaviourScript : MonoBehaviour
{
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.R))
        {
            GetComponent<Renderer> ().material.color = Color.red;
        }
    }
}
  ```

  Check State

  ```csharp
using UnityEngine;
using System.Collections;

public class CheckState : MonoBehaviour
{
    public GameObject myObject;

    void Start ()
    {
        Debug.Log("Active Self: " + myObject.activeSelf);
        Debug.Log("Active in Hierarchy" + myObject.activeInHierarchy);
    }
}
  ```

  Translate and Rotate

  ```csharp
using UnityEngine;
using System.Collections;

public class TransformFunctions : MonoBehaviour
{
    public float moveSpeed = 10f;
    public float turnSpeed = 50f;

    // Update is called once per frame
    void Update ()
    {
        if(Input.GetKey(KeyCode.UpArrow))
            transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);

        if(Input.GetKey(KeyCode.DownArrow))
            transform.Translate(-Vector3.forward * moveSpeed * Time.deltaTime);

        if(Input.GetKey(KeyCode.LeftArrow))
            transform.Rotate(Vector3.up, -turnSpeed * Time.deltaTime);

        if(Input.GetKey(KeyCode.RightArrow))
            transform.Rotate(Vector3.up, turnSpeed * Time.deltaTime);
    }
}
  ```

  Look At

  ```csharp
using UnityEngine;
using System.Collections;

public class CameraLookAt : MonoBehaviour
{
    public Transform target;

    void Update ()
    {
        transform.LookAt(target);
    }
}
  ```

  Linear Interpolation

  ```csharp
// In this case, result = 4
float result = Mathf.Lerp (3f, 5f, 0.5f);
// The Mathf.Lerp function takes 3 float parameters: one representing the value to interpolate from; another representing the value to interpolate to and a final float representing how far to interpolate

Vector3 from = new Vector3 (1f, 2f, 3f);
Vector3 to = new Vector3 (5f, 6f, 7f);

// Here result = (4, 5, 6)
Vector3 result = Vector3.Lerp (from, to, 0.75f);

// The same principle is applied when using Color.Lerp.
// In the Color struct, colours are represented by 4 floats representing red, blue, green and alpha.
// When using Lerp, these floats are interpolated just as with Mathf.Lerp and Vector3.Lerp.

void Update ()
{
    light.intensity = Mathf.Lerp(light.intensity, 8f, 0.5f * Time.deltaTime);
    // With deltaTime, the change to intensity would happen per second instead of per frame.
}
  ```

  GetAxis Horizontal and Vertical

  ```csharp
using UnityEngine;
using System.Collections;

public class DualAxisExample : MonoBehaviour
{
    public float range;
    public GUIText textOutput;

    void Update ()
    {
        float h = Input.GetAxis("Horizontal");
        float v = Input.GetAxis("Vertical");
        float xPos = h * range;
        float yPos = v * range;

        transform.position = new Vector3(xPos, yPos, 0);
        textOutput.text = "Horizontal Value Returned: "+h.ToString("F2")+"\nVertical Value Returned: "+v.ToString("F2");
    }
}
  ```

  OnMouseDown

  ```csharp
using UnityEngine;
using System.Collections;

public class MouseClick : MonoBehaviour
{
    void OnMouseDown ()
    {
        rigidbody.AddForce(-transform.forward * 500f);
        rigidbody.useGravity = true;
    }
}
  ```

  Using Other Components

  ```csharp
using UnityEngine;
using System.Collections;

public class UsingOtherComponents : MonoBehaviour
{
    public GameObject otherGameObject;

    private AnotherScript anotherScript;
    private YetAnotherScript yetAnotherScript;
    private BoxCollider boxCol;

    void Awake ()
    {
        anotherScript = GetComponent<AnotherScript>();
        yetAnotherScript = otherGameObject.GetComponent<YetAnotherScript>();
        boxCol = otherGameObject.GetComponent<BoxCollider>();
    }

    void Start ()
    {
        boxCol.size = new Vector3(3,3,3);
        Debug.Log("The player's score is " + anotherScript.playerScore);
        Debug.Log("The player has died " + yetAnotherScript.numberOfPlayerDeaths + " times");
    }
}
  ```

  Using Delta Times

  ```csharp
using UnityEngine;
using System.Collections;

public class UsingDeltaTime : MonoBehaviour
{
    public float speed = 8f;
    public float countdown = 3.0f;

    void Update ()
    {
        countdown -= Time.deltaTime;
        if(countdown <= 0.0f)
            light.enabled = true;

         if(Input.GetKey(KeyCode.RightArrow))
            transform.position += new Vector3(speed * Time.deltaTime, 0.0f, 0.0f);
    }
}
  ```

  Using Data Types

  ```csharp
using UnityEngine;
using System.Collections;

public class DatatypeScript : MonoBehaviour
{
    void Start ()
    {
        //Value type variable
        Vector3 pos = transform.position;
        pos = new Vector3(0, 2, 0);

        //Reference type variable
        Transform tran = transform;
        tran.position = new Vector3(0, 2, 0);
    }
}
  ```

  Using Instantiate

  ```csharp
using UnityEngine;
using System.Collections;

public class UsingInstantiate : MonoBehaviour
{
    public Rigidbody rocketPrefab;
    public Transform barrelEnd;

    void Update ()
    {
        if(Input.GetButtonDown("Fire1"))
        {
            Rigidbody rocketInstance = Instantiate(rocketPrefab, barrelEnd.position, barrelEnd.rotation) as Rigidbody;
            rocketInstance.AddForce(barrelEnd.forward * 5000);
        }
    }
}
  ```

  Arrays

  ```csharp
using UnityEngine;
using System.Collections;

public class Arrays : MonoBehaviour
{
    public GameObject[] players;

    void Start ()
    {
        players = GameObject.FindGameObjectsWithTag("Player");

        for(int i = 0; i < players.Length; i++)
        {
            Debug.Log("Player Number "+i+" is named "+players[i].name);
        }
    }
}
  ```

  Invoke

  ```csharp
using UnityEngine;
using System.Collections;

public class InvokeScript : MonoBehaviour
{
    public GameObject target;

    void Start()
    {
        Invoke (nameof(SpawnObject), 2);
        // Repeating
        InvokeRepeating(nameof(SpawnObject), 2, 1);
        // TODO: Use UniRx/Observables instead :)
    }

    void SpawnObject()
    {
        float x = Random.Range(-2.0f, 2.0f);
        float z = Random.Range(-2.0f, 2.0f);
        Instantiate(target, new Vector3(x, 2, z), Quaternion.identity);
    }
}
  ```

  Enumerations

  ```csharp
using UnityEngine;
using System.Collections;

public class EnumScript : MonoBehaviour
{
    enum Direction {North, East, South, West};

    void Start ()
    {
        Direction myDirection;

        myDirection = Direction.North;
    }

    Direction ReverseDirection (Direction dir)
    {
        if(dir == Direction.North)
            dir = Direction.South;
        else if(dir == Direction.South)
            dir = Direction.North;
        else if(dir == Direction.East)
            dir = Direction.West;
        else if(dir == Direction.West)
            dir = Direction.East;

        return dir;
    }
}
  ```


text:
Awesome Unity Games 🎮

A curated list of awesome Unity Games (Open Source), code examples and resources.

    Credits to https://codepen.io/boldfacedesign/pen/EoGgD

## Adventure
- [San Andreas Unity](https://github.com/GTA-ASM/SanAndreasUnity) - An open source reimplementation of GTA San Andreas game engine in Unity.

## Puzzle
- [Nodulus](https://github.com/Hyperparticle/nodulus) - A puzzle game with a clever twist. Based on the mathematical theory behind plank puzzles, consists of a grid of cubes and rods which can be rotated with a swipe.
- [Angry Birds style game](https://github.com/dgkanatsios/AngryBirdsStyleGame) - An effort to replicate a level and relevant mechanisms of Rovio's famous Angry Birds game, built in Unity game engine. Source code is provided for educational purposes.
- [Match-3 game](https://github.com/dgkanatsios/MatchThreeGame) - A match-3 game in Unity (like Candy Crush and Bejeweled).

## Role
- [Darkest Dungeon](https://github.com/Reinisch/Darkest-Dungeon-Unity) - Darkest Dungeon port in Unity. Almost completely identical to the original.

## RPG
- [Hogwarts](https://github.com/OpenHogwarts/hogwarts) - A Hogwarts (Harry Potter) open sandbox game made in Unity.

## Runner
- [InfiniteRunner3D](https://github.com/dgkanatsios/InfiniteRunner3D) - Creating an infinite 3D runner game in Unity.

## Sandbox
- [CubeWorld](https://github.com/federicodangelo/CubeWorld) - Minecraft like game made in Unity.

## Shooter
- [Multiplayer-FPS](https://github.com/Armour/Multiplayer-FPS) - A multiplayer first-person shooter game based on Unity3D. Different types of input devices are supported, including Kinect, Xbox controllers, Leap motion, and VR Glasses.

## Strategy
- [Starcraft Unity3D](https://github.com/coconauts/startcraft-unity3d) - A recreation of the classic Starcraft game by Blizzard, on Unity3D.

---

## Code Examples
- [Basics](./CODE_BASICS.md)
- [Intermediate](./CODE_INTERMEDIATE.md)

## Augmented reality
- [ARCore](https://developers.google.com/ar/develop/unity) - Build new augmented reality experiences that seamlessly blend the digital and physical worlds. Transform the way people play, shop, learn, create, and experience the world together—at Google scale.
- [ARKit](https://developer.apple.com/augmented-reality) - Build unparalleled augmented reality experiences for hundreds of millions of users on iOS and iPadOS, the biggest AR platforms in the world.
- [Vuforia](https://engine.vuforia.com/engine) - The World’s Most Widely Deployed AR.

## Virtual Reality

- [Innoactive Creator](https://github.com/Innoactive/Creator) - Scalable and maintainable Unity-based VR training.
- [Innoactive Creator Examples](https://github.com/Innoactive/Creator-Examples) - Simple examples showcasing the capabilities of the Innoactive Creator.
- [XR-Interaction-Toolkit-Examples](https://github.com/Unity-Technologies/XR-Interaction-Toolkit-Examples) - Various examples to use with the XR Interaction Toolkit.
- [OpenVR XR](https://github.com/ValveSoftware/unity-xr-plugin) - OpenVR plugin for Unity's XR API.

## Controllers
- [2D-Platformer-Hunter](https://github.com/ta-david-yu/2D-Platformer-Hunter) - A 2D Platformer Controller in Unity.

## Editor
- [Runtime Unity Editor/Debugging Tools](https://github.com/ManlyMarco/RuntimeUnityEditor) - In-game inspector and debugging tools for applications made with Unity3D game engine.
- [Path-Creator](https://github.com/SebLague/Path-Creator) - An intuitive and lightweight editor for quickly creating smooth paths in the editor.
- [UIWidgets](https://github.com/Unity-Technologies/com.unity.uiwidgets) - A Unity Package which helps developers to create, debug and deploy efficient, cross-platform Apps.

## Scripting
- [ScriptableObject-Architecture](https://github.com/DanielEverland/ScriptableObject-Architecture) - Makes using Scriptable Objects as a fundamental part of your architecture in Unity super easy.
- [UniRx](https://github.com/neuecc/UniRx) - Reactive Extensions for Unity.
- [C-Sharp-Promise](https://github.com/Real-Serious-Games/C-Sharp-Promise) - Promises library for C# for management of asynchronous operations.
- [FSM_Command_Patterns](https://github.com/hansschaa/FSM_Command_Patterns) - A basic platform game developed for help to undertanding State pattern and Command pattern.

## Networking
- [RestClient](https://github.com/proyecto26/RestClient) - 🦄 Simple HTTP and REST client for Unity based on Promises, also supports Callbacks! 🎮.

## Platforms
- [Native Toolkit](https://github.com/ryanw3bb/unity-native-toolkit) - Easily integrate native iOS & Android functionality into Unity projects.

## Utilities
- [Yarn Spinner](https://github.com/YarnSpinnerTool/YarnSpinner) - A tool for building interactive dialogue in games!
- [Unity2D Components](https://github.com/cmilr/Unity2D-Components) - A varied collection of Unity3D components that I've built for use in the 2D Platformer/RPG I'm currently developing.
- [Unity Image Cropper](https://github.com/yasirkula/UnityImageCropper) - A uGUI based image cropping solution for Unity 3D.
- [ChainSafe Gaming](https://github.com/ChainSafe/web3.unity) - Unity SDK for building games that interact with blockchains.

## Guides
- [Coding in C# in Unity for beginners](https://unity3d.com/learning-c-sharp-in-unity-for-beginners) - The very basics of coding, like variables, functions and classes, and how to use them.
- [Design Patterns Written in Unity3D](https://github.com/QianMo/Unity-Design-Pattern) - All Gang of Four Design Patterns written in Unity C# with many examples.

## Tutorials
- [Introduction to Roll-a-Ball](https://youtu.be/RFlh8pTf4DU) - An introduction to the Roll-a-ball project, showing the final game and describing what will be covered in this tutorials.
- [Setting up the Game](https://youtu.be/W_fAidYRGzs) - Creating a new project and setting up the basic game.
- [Moving the Player](https://youtu.be/7C7WWxUxPZE) - Moving the player object using player input and physics forces.
- [Moving the Camera](https://youtu.be/Xcm5H2J95iI) - Moving the camera relative to the player.
- [Setting up the Play Area](https://youtu.be/dahT0wRVO1Q) - Setting up the play area.
- [Creating Collectable Objects](https://youtu.be/HlDGSStxuHI) - Creating and placing the "Pick Up" collectables.
- [Collecting the Pick Up Objects](https://youtu.be/XtR29MmzuT0) - Collecting the pick-up objects; discussing physics, collisions and triggers.
- [Displaying the Score and Text](https://youtu.be/bFSLI2cmYYo) - Counting, displaying text and ending the game.
- [Building the Game](https://youtu.be/hSg3e1M3hKY) - Building the game as a Standalone application.
- [Scripts as Behaviour Components](https://youtu.be/Z0Z7xc18CcA) - Learn about the behaviour component that is a Unity script, and how to Create and Attach them to objects.
- [Variables And Functions](https://youtu.be/-c1RsydH2nA) - What are Variables and Functions, and how do they store and process information for us?
- [Conventions and Syntax](https://youtu.be/0mks0QaWCNQ) - Learn about some basic conventions and syntax of writing code.
- [If Statements](https://youtu.be/PQihrWCOSic) - How to use IF statements to set conditions in your code.
- [Loops](https://youtu.be/Jefkb3Gm7vE) - How to use the For, While and Do-While Loops as well as the For Each loop to repeat actions in code.
- [Scope and Access Modifiers](https://youtu.be/_0oBLCJcpCs) - Understanding variable & function scope and accessibility.
- [Awake and Start](https://youtu.be/4QdjoV63wjM) - How to use Awake and Start, two of Unity's initialisation functions.
- [Update and FixedUpdate](https://youtu.be/u42aWzAIAqg) - How to effect changes every frame with the Update and FixedUpdate functions, and their differences.
- [Vector Maths](https://youtu.be/e3z91RqZPAk) - A primer on Vector maths - as well as information on the Dot and Cross products.
- [Enabling and Disabling Components](https://youtu.be/PCdg3cnQfZ4) - How to enable and disable components via script during runtime.
- [Activating GameObjects](https://youtu.be/MhPFB-rAdlg) - Learn about the behaviour component that is a Unity script, and how to Create and Attach them to objects.
- [Translate and Rotate](https://youtu.be/32JkMANaMpk) - How to use the two transform functions Translate and Rotate to effect a non-rigidbody object's position and rotation.
- [LookAt](https://youtu.be/cAAqf5J7_9w) - How to make a game object's transform face another's by using the LookAt function.
- [Destroy](https://youtu.be/pRDj3jss5t8) - How to use the **Destroy** function to remove GameObjects and Components at runtime.
- [GetButton and GetKey](https://youtu.be/-A7D5Rcumz4) - How to get button or key for input and how these axes behave / can be modified with the Input manager.
- [GetAxis](https://youtu.be/MK4OmsViqMA) - How to "get axis" based input for your games in Unity and how these axes can be modified with the Input manager.
- [OnMouseDown](https://youtu.be/c69oZprM1oc) - How to detect mouse clicks on a Collider or GUI element.
- [GetComponent](https://youtu.be/xbDKC4zP9XY) - How to use the GetComponent function to address properties of other scripts or components.
- [DeltaTime](https://youtu.be/Gcoj3llfzSw) - What is Delta Time and how can it be used in your games to smooth and interpret values.
- [DataTypes](https://youtu.be/IVcx-tSxjys) - Learn the important differences between Value and Reference data types, in order to better understand how variables work.
- [Classes](https://youtu.be/odKtPBsyFnw) - How to use Classes to store and organise your information, and how to create constructors to work with parts of your class.
- [Instantiate](https://youtu.be/Q3u0x8VRJS4) - How to use Instantiate to create clones of a Prefab during runtime.
- [Arrays](https://youtu.be/Zn4BDIXhy-M) - Using arrays to collect variables together into a more manageable form.
- [Invoke](https://youtu.be/-YgM4DXGeq4) - The Invoke functions allow you to schedule method calls to occur at a later time.
- [Enumerations](https://youtu.be/L2E2aB1CMYw) - Enumerations allow you to create a collection of related constants.
- [Switch Statements](https://youtu.be/-PWvI3q6_OE) - Switch statements act like streamline conditionals. They are useful for when you want to compare a single variable against a series of constants.
- [Events](https://youtu.be/k4JlFxPcqlg) - Learn how to use special delegates called Events in order to subscribe methods (functions) to create flexible broadcast systems in your code.
- [Creating a Text Based Adventure Part 1](https://youtu.be/jAf1I1UWo5Q) - Learn how to program a text based adventure game in which the player explores a series of rooms by reading text and inputting commands via the keyboard.
- [Creating a Text Based Adventure Part 2](https://youtu.be/Bak8azAM_cA) - Learn how to to display the descriptions of all the items in a room when we enter it.
- [How to Play Test Game Mods (Official Unity Tutorial)](https://youtu.be/kZCJmKVQAPQ) - In the Play Testing In-Editor Tutorial, you will learn how to play and mod your Microgame in Unity.

## Tools
- [OpenUPM](https://openupm.com) - Open Source Unity Package Registry.

## YouTube Channels
- [Brackeys](https://www.youtube.com/user/Brackeys/videos) - Game Dev Tutorials

## Architecture
- [SOLID](https://github.com/ugonnathelma/articles/blob/master/the-solid-principles-in-pictures/the-solid-principles-in-pictures.md) - The S.O.L.I.D Principles in Pictures.
- [.Net Documentation](https://github.com/proyecto26/dotnet-documentation) - SOLID, DI, N-Tier, Logs, etc with dotnet.

## Other Awesome Lists
- [GameDev-Resources](https://github.com/Kavex/GameDev-Resources) - A wonderful list of Game Development resources.
- [awesome-unity3d](https://github.com/insthync/awesome-unity3d) - A categorized collection of awesome opensource unity3d repos.
- [Awesome Unity Open Source on GitHub (800+)](https://github.com/baba-s/awesome-unity-open-source-on-github) - A categorized collection of awesome Unity open source on GitHub.
- [Awesome Unity FREE](https://github.com/netpyoung/awesome-unity-free) - A community driven list of useful Unity Game Engine "FREE" packages, libraries and others.
- [Awesome Unity Community](https://github.com/UnityCommunity/AwesomeUnityCommunity) - A categorized community-driven collection of high-quality awesome Unity assets, projects, and resources.

## Concepts
* **3DOF:** A term often used in the context of virtual reality, refers to tracking of rotational motion only: pitch, yaw, and roll.
* **6DOF:** Refers to the freedom of movement of a rigid body in three-dimensional space.

## Supporting 🍻
I believe in Unicorns 🦄
Support [me](http://www.paypal.me/jdnichollsc/2), if you do too.

Donate **Ethereum**, **ADA**, **BNB**, **SHIBA**, **USDT**, **DOGE**:

![Wallet address](https://user-images.githubusercontent.com/2154886/123501719-84bf1900-d60c-11eb-882c-98a499cea323.png)

> Wallet address: 0x3F9fA8021B43ACe578C2352861Cf335449F33427

Please let us know your contributions! 🙏

## Happy coding 💯
Made with ❤


text:
<?xml version="1.0" encoding="UTF-8"?>

<JUCERPROJECT id="EHAJpP" name="RenderMan" projectType="dll" version="1.0.0"
              bundleIdentifier="com.yourcompany.RenderMan" includeBinaryInAppConfig="1"
              jucerVersion="5.3.0" displaySplashScreen="0" reportAppUsage="0"
              splashScreenColour="Dark" cppLanguageStandard="11" companyCopyright="">
  <MAINGROUP id="tONOKg" name="RenderMan">
    <GROUP id="{4A809F36-0B47-21E3-7688-8CF65B68EF74}" name="Maximilian">
      <GROUP id="{581DA4A2-AF43-FE18-47C2-CA1585A8F30E}" name="libs">
        <FILE id="WJArWJ" name="fft.cpp" compile="1" resource="0" file="Source/Maximilian/libs/fft.cpp"/>
        <FILE id="PNwhtj" name="fft.h" compile="0" resource="0" file="Source/Maximilian/libs/fft.h"/>
        <FILE id="DMQKGv" name="maxiFFT.cpp" compile="1" resource="0" file="Source/Maximilian/libs/maxiFFT.cpp"/>
        <FILE id="wtlBbE" name="maxiFFT.h" compile="0" resource="0" file="Source/Maximilian/libs/maxiFFT.h"/>
        <FILE id="j3GCgR" name="maxiMFCC.cpp" compile="1" resource="0" file="Source/Maximilian/libs/maxiMFCC.cpp"/>
        <FILE id="iOVBDy" name="maxiMFCC.h" compile="0" resource="0" file="Source/Maximilian/libs/maxiMFCC.h"/>
        <FILE id="aoJm4P" name="sineTable.h" compile="0" resource="0" file="Source/Maximilian/libs/sineTable.h"/>
      </GROUP>
      <FILE id="rye4Hq" name="maximilian.cpp" compile="1" resource="0" file="Source/Maximilian/maximilian.cpp"/>
      <FILE id="ducH0M" name="maximilian.h" compile="0" resource="0" file="Source/Maximilian/maximilian.h"/>
    </GROUP>
    <GROUP id="{6A50F3BF-C55C-AF7D-5BA6-E62FF469B8C4}" name="Source"/>
    <FILE id="EmwSk7" name="PatchGenerator.cpp" compile="1" resource="0"
          file="Source/PatchGenerator.cpp"/>
    <FILE id="eITv36" name="PatchGenerator.h" compile="0" resource="0"
          file="Source/PatchGenerator.h"/>
    <FILE id="pWXd7a" name="RenderEngine.cpp" compile="1" resource="0"
          file="Source/RenderEngine.cpp"/>
    <FILE id="oio1wd" name="RenderEngine.h" compile="0" resource="0" file="Source/RenderEngine.h"/>
    <FILE id="PqgGm9" name="source.cpp" compile="1" resource="0" file="Source/source.cpp"/>
  </MAINGROUP>
  <EXPORTFORMATS>
    <XCODE_MAC targetFolder="Builds/MacOSX" vst3Folder="VST3_SDK" extraLinkerFlags="-shared -lpython3.6m -lboost_python3"
               extraCompilerFlags="-fPIC">
      <CONFIGURATIONS>
        <CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="librenderman.so"
                       osxCompatibility="10.9 SDK" osxArchitecture="Native" headerPath="/usr/local/include
/usr/local/include/python3.6"
                       libraryPath="/usr/local/lib
/usr/local/lib/python3.6/lib"
                       enablePluginBinaryCopyStep="1"/>
        <CONFIGURATION name="Release" isDebug="0" optimisation="3" targetName="librenderman.so"
                       osxCompatibility="10.9 SDK" osxArchitecture="Native" headerPath="/usr/local/include
/usr/local/include/python3.6
"
                       libraryPath="/usr/local/lib
/usr/local/lib/python3.6/lib"
                       enablePluginBinaryCopyStep="1"/>
      </CONFIGURATIONS>
      <MODULEPATHS>
        <MODULEPATH id="juce_core" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_events" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_graphics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_data_structures" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_gui_basics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_gui_extra" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_cryptography" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_video" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_opengl" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_basics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_devices" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_formats" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_processors" path="JuceLibraryCode/modules"/>
      </MODULEPATHS>
    </XCODE_MAC>
    <VS2015 targetFolder="Builds/VisualStudio2015" vst3Folder="VST3_SDK"
            windowsTargetPlatformVersion="8.1">
      <CONFIGURATIONS>
        <CONFIGURATION name="Debug" winWarningLevel="4" generateManifest="1" winArchitecture="x64"
                       isDebug="1" optimisation="1" targetName="renderman" debugInformationFormat="ProgramDatabase"
                       enablePluginBinaryCopyStep="0"/>
        <CONFIGURATION name="Release" winWarningLevel="4" generateManifest="1" winArchitecture="x64"
                       isDebug="0" optimisation="3" targetName="renderman" debugInformationFormat="ProgramDatabase"
                       enablePluginBinaryCopyStep="0" linkTimeOptimisation="1"/>
      </CONFIGURATIONS>
      <MODULEPATHS>
        <MODULEPATH id="juce_core" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_events" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_graphics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_data_structures" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_gui_basics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_gui_extra" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_cryptography" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_video" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_opengl" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_basics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_devices" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_formats" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_processors" path="JuceLibraryCode/modules"/>
      </MODULEPATHS>
    </VS2015>
    <VS2013 targetFolder="Builds/VisualStudio2013" vst3Folder="VST3_SDK"
            windowsTargetPlatformVersion="8.1">
      <CONFIGURATIONS>
        <CONFIGURATION name="Debug" winWarningLevel="4" generateManifest="1" winArchitecture="x64"
                       isDebug="1" optimisation="1" targetName="renderman" debugInformationFormat="ProgramDatabase"
                       enablePluginBinaryCopyStep="0"/>
        <CONFIGURATION name="Release" winWarningLevel="4" generateManifest="1" winArchitecture="x64"
                       isDebug="0" optimisation="3" targetName="renderman" debugInformationFormat="ProgramDatabase"
                       enablePluginBinaryCopyStep="0" linkTimeOptimisation="1"/>
      </CONFIGURATIONS>
      <MODULEPATHS>
        <MODULEPATH id="juce_core" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_events" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_graphics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_data_structures" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_gui_basics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_gui_extra" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_cryptography" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_video" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_opengl" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_basics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_devices" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_formats" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_processors" path="JuceLibraryCode/modules"/>
      </MODULEPATHS>
    </VS2013>
    <LINUX_MAKE targetFolder="Builds/LinuxMakefile" vst3Folder="VST3_SDK" extraCompilerFlags="-fPIC"
                extraLinkerFlags="-shared -lpython2.7 -lboost_python" cppLanguageStandard="-std=c++11">
      <CONFIGURATIONS>
        <CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="renderman"
                       headerPath="/usr/include/python2.7"/>
        <CONFIGURATION name="Release" isDebug="0" optimisation="3" targetName="renderman"
                       headerPath="/usr/include/python2.7"/>
      </CONFIGURATIONS>
      <MODULEPATHS>
        <MODULEPATH id="juce_core" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_events" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_graphics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_data_structures" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_gui_basics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_gui_extra" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_cryptography" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_video" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_opengl" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_basics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_devices" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_formats" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_processors" path="JuceLibraryCode/modules"/>
      </MODULEPATHS>
    </LINUX_MAKE>
  </EXPORTFORMATS>
  <MODULES>
    <MODULE id="juce_audio_basics" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_audio_devices" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_audio_formats" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_audio_processors" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_core" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_cryptography" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_data_structures" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_events" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_graphics" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_gui_basics" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_gui_extra" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_opengl" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_video" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
  </MODULES>
  <JUCEOPTIONS JUCE_PLUGINHOST_VST="1" JUCE_PLUGINHOST_VST3="1" JUCE_PLUGINHOST_AU="1"
               JUCE_CHECK_MEMORY_LEAKS="0" JUCE_WEB_BROWSER="0"/>
  <LIVE_SETTINGS>
    <LINUX/>
    <OSX/>
  </LIVE_SETTINGS>
</JUCERPROJECT>

repo_name: fedden/RenderMan

mime_type: text/x-c++


text:
<?xml version="1.0" encoding="UTF-8"?>

<JUCERPROJECT id="EHAJpP" name="RenderMan" projectType="dll" version="1.0.0"
              bundleIdentifier="com.yourcompany.RenderMan" includeBinaryInAppConfig="1"
              jucerVersion="5.2.0" displaySplashScreen="0" reportAppUsage="0"
              splashScreenColour="Dark" cppLanguageStandard="11" companyCopyright="">
  <MAINGROUP id="tONOKg" name="RenderMan">
    <GROUP id="{4A809F36-0B47-21E3-7688-8CF65B68EF74}" name="Maximilian">
      <GROUP id="{581DA4A2-AF43-FE18-47C2-CA1585A8F30E}" name="libs">
        <FILE id="WJArWJ" name="fft.cpp" compile="1" resource="0" file="Source/Maximilian/libs/fft.cpp"/>
        <FILE id="PNwhtj" name="fft.h" compile="0" resource="0" file="Source/Maximilian/libs/fft.h"/>
        <FILE id="DMQKGv" name="maxiFFT.cpp" compile="1" resource="0" file="Source/Maximilian/libs/maxiFFT.cpp"/>
        <FILE id="wtlBbE" name="maxiFFT.h" compile="0" resource="0" file="Source/Maximilian/libs/maxiFFT.h"/>
        <FILE id="j3GCgR" name="maxiMFCC.cpp" compile="1" resource="0" file="Source/Maximilian/libs/maxiMFCC.cpp"/>
        <FILE id="iOVBDy" name="maxiMFCC.h" compile="0" resource="0" file="Source/Maximilian/libs/maxiMFCC.h"/>
        <FILE id="aoJm4P" name="sineTable.h" compile="0" resource="0" file="Source/Maximilian/libs/sineTable.h"/>
      </GROUP>
      <FILE id="rye4Hq" name="maximilian.cpp" compile="1" resource="0" file="Source/Maximilian/maximilian.cpp"/>
      <FILE id="ducH0M" name="maximilian.h" compile="0" resource="0" file="Source/Maximilian/maximilian.h"/>
    </GROUP>
    <GROUP id="{6A50F3BF-C55C-AF7D-5BA6-E62FF469B8C4}" name="Source"/>
    <FILE id="EmwSk7" name="PatchGenerator.cpp" compile="1" resource="0"
          file="Source/PatchGenerator.cpp"/>
    <FILE id="eITv36" name="PatchGenerator.h" compile="0" resource="0"
          file="Source/PatchGenerator.h"/>
    <FILE id="pWXd7a" name="RenderEngine.cpp" compile="1" resource="0"
          file="Source/RenderEngine.cpp"/>
    <FILE id="oio1wd" name="RenderEngine.h" compile="0" resource="0" file="Source/RenderEngine.h"/>
    <FILE id="PqgGm9" name="source.cpp" compile="1" resource="0" file="Source/source.cpp"/>
  </MAINGROUP>
  <EXPORTFORMATS>
    <XCODE_MAC targetFolder="Builds/MacOSX" vst3Folder="VST3_SDK" extraLinkerFlags="-shared -lpython2.7 -lboost_python"
               extraCompilerFlags="-fPIC">
      <CONFIGURATIONS>
        <CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="librenderman.so"
                       osxCompatibility="10.9 SDK" osxArchitecture="Native" headerPath="/usr/include/python2.7
/usr/local/include
"
                       libraryPath="/usr/local/lib" enablePluginBinaryCopyStep="1"/>
        <CONFIGURATION name="Release" isDebug="0" optimisation="3" targetName="librenderman.so"
                       osxCompatibility="10.9 SDK" osxArchitecture="Native" headerPath="/usr/include/python2.7
/usr/local/include
"
                       libraryPath="/usr/local/lib" enablePluginBinaryCopyStep="1"/>
      </CONFIGURATIONS>
      <MODULEPATHS>
        <MODULEPATH id="juce_core" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_events" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_graphics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_data_structures" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_gui_basics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_gui_extra" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_cryptography" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_video" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_opengl" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_basics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_devices" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_formats" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_processors" path="JuceLibraryCode/modules"/>
      </MODULEPATHS>
    </XCODE_MAC>
    <VS2015 targetFolder="Builds/VisualStudio2015" vst3Folder="VST3_SDK"
            windowsTargetPlatformVersion="8.1">
      <CONFIGURATIONS>
        <CONFIGURATION name="Debug" winWarningLevel="4" generateManifest="1" winArchitecture="x64"
                       isDebug="1" optimisation="1" targetName="renderman" debugInformationFormat="ProgramDatabase"
                       enablePluginBinaryCopyStep="0"/>
        <CONFIGURATION name="Release" winWarningLevel="4" generateManifest="1" winArchitecture="x64"
                       isDebug="0" optimisation="3" targetName="renderman" debugInformationFormat="ProgramDatabase"
                       enablePluginBinaryCopyStep="0" linkTimeOptimisation="1"/>
      </CONFIGURATIONS>
      <MODULEPATHS>
        <MODULEPATH id="juce_core" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_events" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_graphics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_data_structures" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_gui_basics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_gui_extra" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_cryptography" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_video" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_opengl" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_basics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_devices" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_formats" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_processors" path="JuceLibraryCode/modules"/>
      </MODULEPATHS>
    </VS2015>
    <VS2013 targetFolder="Builds/VisualStudio2013" vst3Folder="VST3_SDK"
            windowsTargetPlatformVersion="8.1">
      <CONFIGURATIONS>
        <CONFIGURATION name="Debug" winWarningLevel="4" generateManifest="1" winArchitecture="x64"
                       isDebug="1" optimisation="1" targetName="renderman" debugInformationFormat="ProgramDatabase"
                       enablePluginBinaryCopyStep="0"/>
        <CONFIGURATION name="Release" winWarningLevel="4" generateManifest="1" winArchitecture="x64"
                       isDebug="0" optimisation="3" targetName="renderman" debugInformationFormat="ProgramDatabase"
                       enablePluginBinaryCopyStep="0" linkTimeOptimisation="1"/>
      </CONFIGURATIONS>
      <MODULEPATHS>
        <MODULEPATH id="juce_core" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_events" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_graphics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_data_structures" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_gui_basics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_gui_extra" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_cryptography" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_video" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_opengl" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_basics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_devices" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_formats" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_processors" path="JuceLibraryCode/modules"/>
      </MODULEPATHS>
    </VS2013>
    <LINUX_MAKE targetFolder="Builds/LinuxMakefile" vst3Folder="VST3_SDK" extraCompilerFlags="-fPIC"
                extraLinkerFlags="-shared -lpython2.7 -lboost_python" cppLanguageStandard="-std=c++11">
      <CONFIGURATIONS>
        <CONFIGURATION name="Debug" isDebug="1" optimisation="1" targetName="renderman"
                       headerPath="/usr/include/python2.7"/>
        <CONFIGURATION name="Release" isDebug="0" optimisation="3" targetName="renderman"
                       headerPath="/usr/include/python2.7"/>
      </CONFIGURATIONS>
      <MODULEPATHS>
        <MODULEPATH id="juce_core" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_events" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_graphics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_data_structures" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_gui_basics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_gui_extra" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_cryptography" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_video" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_opengl" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_basics" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_devices" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_formats" path="JuceLibraryCode/modules"/>
        <MODULEPATH id="juce_audio_processors" path="JuceLibraryCode/modules"/>
      </MODULEPATHS>
    </LINUX_MAKE>
  </EXPORTFORMATS>
  <MODULES>
    <MODULE id="juce_audio_basics" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_audio_devices" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_audio_formats" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_audio_processors" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_core" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_cryptography" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_data_structures" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_events" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_graphics" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_gui_basics" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_gui_extra" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_opengl" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
    <MODULE id="juce_video" showAllCode="1" useLocalCopy="1" useGlobalPath="0"/>
  </MODULES>
  <JUCEOPTIONS JUCE_PLUGINHOST_VST="enabled" JUCE_PLUGINHOST_VST3="enabled"
               JUCE_PLUGINHOST_AU="enabled" JUCE_CHECK_MEMORY_LEAKS="disabled"
               JUCE_WEB_BROWSER="disabled"/>
  <LIVE_SETTINGS>
    <LINUX/>
  </LIVE_SETTINGS>
</JUCERPROJECT>


text:
```
       ______               _          ___  ___
       | ___ \             | |         |  \/  |
       | |_/ /___ _ __   __| | ___ _ __| .  . | __ _ _ __
       |    // _ \ '_ \ / _` |/ _ \ '__| |\/| |/ _` | '_ \
       | |\ \  __/ | | | (_| |  __/ |  | |  | | (_| | | | |
       \_| \_\___|_| |_|\__,_|\___|_|  \_|  |_/\__,_|_| |_|

* *  Command Line VSTi Audio, Features and Parameter Renderer  * *
```

| `build` |
|:-------:|
| [![Build Status](https://travis-ci.org/fedden/RenderMan.svg?branch=master)](https://travis-ci.org/fedden/RenderMan) |

# RenderMan

Renderman is a command line VSTi host written in C++ with Python bindings using [JUCE](https://github.com/julianstorer/JUCE) and [Maximilian](https://github.com/micknoise/Maximilian) libraries for the backend. It is designed with ease of use in mind to extract audio and features from VSTi plugins. It has a fast growing list of features, including setting, getting parameters from synthesiers, setting whole patches, getting random patches, obtaining MFCCS, FFT, audio data and much more.

A usage example in the form of an IPython notebook can be found [here](http://doc.gold.ac.uk/~lfedd001/renderman.html).

Here are some quick gifs demonstrating a miniscule amount of the availble features, go towards the bottom of the README to see the full API:

#### Loading Plugins

Here we load a plugin. On Linux it's an .so file, for MacOS it will be a .vst or .au.
![](https://github.com/fedden/RenderMan/raw/master/Documentation/loading_plugin.gif)

#### Getting Parameter Descriptions

We can obtain the available parameters that are used and that can be modified by doing the following.
![](https://github.com/fedden/RenderMan/raw/master/Documentation/plugin_description.gif)

#### Getting a Randomised Patch For a Synthesiser

We can easily get a randomised for a given synth by using the PatchGenerator class.
![](https://github.com/fedden/RenderMan/raw/master/Documentation/print_random_patch.gif)

#### Plotting Rendered Audio Frames

We can plot the output audio frames easily by using matplotlib.
![](https://github.com/fedden/RenderMan/raw/master/Documentation/plotting_audio_frames.gif)

## Building / Installation

### MacOS

If you haven't already, [get brew](https://brew.sh/). The last time I checked the command to install was simply this:
```
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
```

Next, get the boost headers.
```
brew install boost-python3
```

You can also install boost manually if for some reason you don't want to use brew, [see here.](http://www.boost.org/doc/libs/1_50_0/doc/html/quickbook/install.html)

Now just open the Xcode project in the Builds directory and build it! There is a bug in the JUCE projucer app which means the generated shared object will be suffixed with a dylib. This means python wont be able to import the module. Until this bug is fixed, change directory into the Builds/MacOSX/build/<Debug/Release> (depending on your Xcode scheme,) and run:
```
mv librenderman.so.dylib librenderman.so
```

**IMPORTANT:** The project is linked with libpython3.8 and libboost_python38 and the appropriate include directories. If you have a different version of python installed, run `python3-config --includes --ldflags` to obtain the library and include paths, and update the XCode project to point to the correct locations

### Linux

Firstly, you will need the boost library (specifically the python headers) for this code to compile.

Ubuntu:
```
sudo apt-get install libboost-all-dev
```

Arch:
```
sudo pacman -Ss boost
```

Fedora:
```
sudo yum install boost-devel
```

If your distribution's package manager doesn't have boost, [you can get the headers from here.](http://www.boost.org/doc/libs/1_47_0/more/getting_started/unix-variants.html)

Juce itself has a list of dependancies for Linux; it's a very big library - if you don't know it you should definitely take some time out to check it out! Depending on your distribution and setup you may already have some / all of the following libraries. If you are on Ubuntu, the following commands will install your dependancies. Find the respective packages for other distros using google please!

```
sudo apt-get -y install llvm
sudo apt-get -y install clang
sudo apt-get -y install libfreetype6-dev
sudo apt-get -y install libx11-dev
sudo apt-get -y install libxinerama-dev
sudo apt-get -y install libxrandr-dev
sudo apt-get -y install libxcursor-dev
sudo apt-get -y install mesa-common-dev
sudo apt-get -y install libasound2-dev
sudo apt-get -y install freeglut3-dev
sudo apt-get -y install libxcomposite-dev
sudo apt-get -y install libcurl4-gnutls-dev
```

Well done! You've made it this far! Should you still have problems, which is always a possibility with Linux, a good place to start is the JUCE forums, particularly [here](https://forum.juce.com/t/juce-4-2-1-setup-on-apt-based-linux-ubuntu-16-04-lts-mint-elementary-os-freya/17164) and [here](https://forum.juce.com/t/list-of-juce-dependencies-under-linux/15121). Feel free to drop me a note with an error and I'll happily scratch my head over it but you may get better results in the forums!

So to now build the library for Linux, change to the right directory and run make:
```
cd Builds/LinuxMakefile/
make
```

### Windows - VisualStudio2019

Download and Install boost

Download from
https://www.boost.org/users/download/
extract to c:\boost_1_74_0

```
cd c:\boost_1_74_0
bootstrap.bat
.\b2 --toolset=msvc-14.0 --build-type=complete --prefix=C:\Boost install
```

Download and Install python 3.7.9

Download from
https://www.python.org/downloads/release/python-379/

Open RenderMan.sln in VisualStudios2019

When prompt to retarget projects

select Windows SDK Version: 8.1
platform toolset: No Upgrade

add Includes to search path
Project > Properties > C/C++ > General

Additional Include Directories

```
C:\Boost\include\boost-1_74
C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python37_64\include
```

add Libs to search path

Project > Properties > Configuration Properties > VC++ Directories

Library Directories

add

```
C:\Boost\lib
C:\Program Files (x86)\Microsoft Visual Studio\Shared\Python37_64\libs
```

build

after build rename

```
RenderMan\Builds\VisualStudio2019\x64\Debug\Dynamic Library\renderman.dll
```
to
```
librenderman.pyd
```

*put the pyd file in the root of the python program

## Does It Work?

Change directory to where the .so file is and run:
```
python
```
Once in interactive mode, run:
```
import librenderman as rm
```
If this doesn't spit out errors, congratulations! Enjoy RenderMan. :)

_Note you'll need to have the .so binary in the same directory as the Python project or where you call the interative Python shell from. To use it system wide it will need to be added to the PYTHONPATH environment variable. Soon I'll get distutils sorted so there is an easier installation method._

## API

##### class RenderEngine
The constructor takes three arguments, the [sample rate](http://wiki.audacityteam.org/wiki/Sample_Rates), the buffer size and fft size. Good defualt values if you don't really care that much are 44100, 512, 512 repsectively.
```
__init__(int sample_rate,
         int buffer_size,
         int fft_size)
```
Supply a full path to the plugin to this function to load the vst. It will return true if there is a successful loading.
```
bool load_plugin(string plugin_path)
```
We can set a synth's patch by taking a list of tuples and set the parameters at the int index to the float value. The PatchGenerator class can generate random patches with ease for a given synth.
```
void set_patch(list_of_tuples(int, float) patch)
```
Get the current patch.
```
list_of_tuples(int, float) get_patch()
```
Take a midi note (middle C is 40,) a velocity (0 - 127,) and the note length and recording / rendering length and create the features to be extracted!
```
void render_patch(int   midi_note_pitch,
                  int   midi_note_velocity,
                  float note_length_seconds,
                  float render_length_seconds)
```
Get MFCC features as a list of lists. The first length will be dictated by fft size divided by four, and the second length with be 13, which is the amount of coefficients.
```
list_of_lists get_mfcc_frames()
```
Get the int amount of parameters for the loaded plugin.
```
int get_plugin_parameter_size()
```
Get a description of each parameter.
```
string get_plugin_parameters_description()
```
Override a parameter to always be the supplied value. The float is normalised (0 - 1).
```
override_plugin_parameter(int   index,
                          float value)
```
Remove an overriden plugin parameter.
```
remove_overriden_plugin_parameter(int index)
```
Get a list of floats which is the audio from the rendering session.
```
list get_audio_frames()
```
Write the current patch to a wav file at the specified relative or absolute path. This will overwrite existing files and is only a preview; it is mono and currently not quite loud enough.
```
void write_to_wav(string path)
```
Get a list of root mean squared frames derived from the audio samples. Each frame is a root mean squared of an amount of samples equal to the fft size divided by four.
```
list_of_floats get_rms_frames()
```

##### class PatchGenerator
This class is used to generate patches for a given engine.

The constructor takes an argument of a RenderEngine that has succesfully loaded a plugin.
```
__init__(RenderEngine engine)
```
We can obtain a random value (from a real uniform distribution) that for a parameter at a specified index.
```
tuple(int, float) get_random_parameter(int index)
```
We can get a completely random patch sampled from PRNG in the same way above.
```
list_of_tuples(int, float) get_random_patch()
```

## Contributors

I want to express my deep gratitude to [jgefele](https://github.com/jgefele). It is very touching that people want to use this code let alone contribute to it - thanks!

## Context and Contact
This library is a work in progress for my final year project where I am using Neural Networks to generate synth patches to create desired sounds. For example, I wrote a toy VSTi and learnt synthesiser using a neural network to sound match target sounds [here](http://doc.gold.ac.uk/~lfedd001/tf_fm_synth_learn.html). Everything this library has facilitates that but I recognise there may be more applications so if there are any feature requests please drop me a line on here or leonfedden (at) gmail.com :)

Finally, doing something cool with this library? Let me know what you are up to! And if this code was useful for you then please kindly drop me a GitHub star so more developers will trust and use this code.

If you use this code for academic works, feel free to reference this code through the DOI here:
[![DOI](https://zenodo.org/badge/82790125.svg)](https://zenodo.org/badge/latestdoi/82790125)

Thanks for reading this far, you rock!

![](https://media.giphy.com/media/lr2bkSkxMWFCo/giphy.gif)


text:
This is a stripped down version of the Steinberg VST3 SDK 3.6.7. See `base/LICENSE.txt` and `public.sdk/LICENSE.txt`
for the respective licenses. The code has been obtained from their public repository with the following commnds:

```
MODULES="base pluginterfaces public.sdk"
git clone https://github.com/steinbergmedia/vst3sdk.git tmp_vst3sdk
cd tmp_vst3sdk
git checkout vstsdk367_03_03_2017_build_352
git submodule init $MODULES
git submodule update $MODULES
for MODULE in $MODULES; do rm $MODULE/.git; done
rm -rf public.sdk/samples
mv $MODULES ..
cd ..
rm -rf tmp_vst3sdk
```


text:
set(base_sources
	source/baseiids.cpp
	source/classfactoryhelpers.h
	source/fbuffer.cpp
	source/fbuffer.h
	source/fcleanup.h
	source/fcommandline.h
	source/fdebug.cpp
	source/fdebug.h
	source/fdynlib.cpp
	source/fdynlib.h
	source/fobject.cpp
	source/fobject.h
	source/flock.cpp
	source/flock.h
	source/fstreamer.cpp
	source/fstreamer.h
	source/fstring.cpp
	source/fstring.h
	source/timer.cpp
	source/timer.h
	source/updatehandler.cpp
	source/updatehandler.h
)

set(base_sources_ext
	source/basefwd.h
	source/classfactory.cpp
	source/classfactory.h
	source/fatomic.cpp
	source/fatomic.h
	source/fbitset.cpp
	source/fbitset.h
	source/fcontainer.h
	source/fcpu.cpp
	source/fcpu.h
	source/fcriticalperformance.cpp
	source/fcriticalperformance.h
	source/finitializer.cpp
	source/finitializer.h
	source/fmemory.cpp
	source/fmemory.h
	source/fpoint.cpp
	source/fpoint.h
	source/frect.cpp
	source/frect.h
	source/fregion.cpp
	source/fregion.h
	source/frwlock_generic.h
	source/frwlock_macosx.h
	source/frwlock_windows.h
	source/fstdmethods.h
	source/fstringmethods.h
	source/fstringstream.h
	source/fthread.cpp
	source/fthread.h
	source/funknownfactory.h
	source/hexbinary.h
	source/istreamwrapper.cpp
	source/istreamwrapper.h
	source/tringbuffer.h
)
if (VST_SDK)
	add_library(base STATIC ${base_sources})
else()
	add_library(base STATIC ${base_sources} ${base_sources_ext})
endif()

# iOS target
if(MAC AND XCODE AND IOS_DEVELOPMENT_TEAM)
	add_library(base_ios STATIC ${base_sources})
	smtg_set_platform_ios(base_ios)
endif()


text:
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/classfactoryhelpers.h
// Created by  : Steinberg, 03/2017
// Description : Class factory
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#pragma once

//------------------------------------------------------------------------------
// Helper Macros. Not intended for direct use.
// Use:
//	META_CLASS(className),
//	META_CLASS_IFACE(className,Interface),
//	META_CLASS_SINGLE(className,Interface)
// instead.
//------------------------------------------------------------------------------
#define META_CREATE_FUNC(funcName) static FUnknown* funcName ()

#define CLASS_CREATE_FUNC(className)                                               \
	namespace Meta {                                                               \
	META_CREATE_FUNC (make##className) { return (NEW className)->unknownCast (); } \
	}

#define SINGLE_CREATE_FUNC(className)                                                     \
	namespace Meta {                                                                      \
	META_CREATE_FUNC (make##className) { return className::instance ()->unknownCast (); } \
	}

#define _META_CLASS(className)                                                         \
	namespace Meta {                                                                   \
	static Steinberg::MetaClass meta##className ((#className), Meta::make##className); \
	}

#define _META_CLASS_IFACE(className, Interface)                                                  \
	namespace Meta {                                                                             \
	static Steinberg::MetaClass meta##Interface##className ((#className), Meta::make##className, \
	                                                        Interface##_iid);                    \
	}

/** TODO
 */
#define META_CLASS(className)     \
	CLASS_CREATE_FUNC (className) \
	_META_CLASS (className)

/** TODO
 */
#define META_CLASS_IFACE(className, Interface) \
	CLASS_CREATE_FUNC (className)              \
	_META_CLASS_IFACE (className, Interface)

/** TODO
 */
#define META_CLASS_SINGLE(className, Interface) \
	SINGLE_CREATE_FUNC (className)              \
	_META_CLASS_IFACE (className, Interface)


text:
//------------------------------------------------------------------------
// Project     : SDK Base
// Version     : 1.0
//
// Category    : Helpers
// Filename    : base/source/updatehandler.cpp
// Created by  : Steinberg, 2008
// Description :
//
//-----------------------------------------------------------------------------
// LICENSE
// (c) 2017, Steinberg Media Technologies GmbH, All Rights Reserved
//-----------------------------------------------------------------------------
// Redistribution and use in source and binary forms, with or without modification,
// are permitted provided that the following conditions are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright notice,
//     this list of conditions and the following disclaimer in the documentation
//     and/or other materials provided with the distribution.
//   * Neither the name of the Steinberg Media Technologies nor the names of its
//     contributors may be used to endorse or promote products derived from this
//     software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE  OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------

#include "base/source/updatehandler.h"
#include "base/source/classfactoryhelpers.h"
#include "base/source/fstring.h"

#include <list>
#include <map>
#include <algorithm>

#define NON_EXISTING_DEPENDENCY_CHECK 0 // not now, mm
#define CLASS_NAME_TRACKED DEVELOPMENT

namespace Steinberg {

DEF_CLASS_IID (IUpdateManager)

namespace Update {
const uint32 kHashSize = (1 << 8); // must be power of 2 (16 bytes * 256 == 4096)
const uint32 kMapSize = 1024 * 10;

//------------------------------------------------------------------------
inline uint32 hashPointer (void* p)
{
	return (uint32) ((uint64 (p) >> 12) & (kHashSize - 1));
}

//------------------------------------------------------------------------
inline IPtr<FUnknown> getUnknownBase (FUnknown* unknown)
{
	FUnknown* result = 0;
	if (unknown)
		unknown->queryInterface (FUnknown::iid, (void**)&result);

	return owned (result);
}

#if CLASS_NAME_TRACKED
//------------------------------------------------------------------------
struct Dependency
{
	Dependency (FUnknown* o = 0, IDependent* d = 0) : obj (o), dep (d), objClass (0), depClass (0)
	{
	}

	inline bool operator== (const Dependency& d) const { return obj == d.obj; }
	inline bool operator!= (const Dependency& d) const { return obj != d.obj; }
	inline bool operator< (const Dependency& d) const { return obj < d.obj; }
	inline bool operator> (const Dependency& d) const { return obj > d.obj; }
	FUnknown* obj;
	IDependent* dep;

	FClassID objClass;
	FClassID depClass;
};
#endif

//------------------------------------------------------------------------
struct DeferedChange
{
	DeferedChange (FUnknown* o = 0, int32 m = 0) : obj (o), msg (m) {}
	~DeferedChange () {}
	DeferedChange (const DeferedChange& r) : obj (r.obj), msg (r.msg) {}
	inline bool operator== (const DeferedChange& d) const { return obj == d.obj; }
	inline bool operator!= (const DeferedChange& d) const { return obj != d.obj; }
	FUnknown* obj;
	int32 msg;
};

//------------------------------------------------------------------------
struct UpdateData
{
	UpdateData (FUnknown* o = 0, IDependent** d = 0, uint32 c = 0)
	: obj (o), dependents (d), count (c)
	{
	}
	FUnknown* obj;
	IDependent** dependents;
	uint32 count;
	bool operator== (const UpdateData& d) const
	{
		return d.obj == obj && d.dependents == dependents;
	}
};

//------------------------------------------------------------------------
typedef std::list<DeferedChange> DeferedChangeList;
typedef DeferedChangeList::const_iterator DeferedChangeListIterConst;

typedef std::list<UpdateData> UpdateDataList;
typedef UpdateDataList::const_iterator UpdateDataListIterConst;

#if CLASS_NAME_TRACKED
typedef std::list<Dependency> DependentList;
#else
typedef std::list<IDependent*> DependentList;
#endif
typedef DependentList::iterator DependentListIter;
typedef DependentList::const_iterator DependentListIterConst;

typedef std::map<const FUnknown*, DependentList> DependentMap;
typedef DependentMap::iterator DependentMapIter;
typedef DependentMap::const_iterator DependentMapIterConst;

struct Table
{
	DependentMap depMap[kHashSize];
	DeferedChangeList defered;
	UpdateDataList updateData;
};

//------------------------------------------------------------------------
void updateDone (FUnknown* unknown, int32 message)
{
	if (message != IDependent::kDestroyed)
	{
		FObject* obj = FObject::unknownToObject (unknown);
		if (obj)
			obj->updateDone (message);
	}
}
}

//------------------------------------------------------------------------
SINGLE_CREATE_FUNC (UpdateHandler)

//------------------------------------------------------------------------
static int32 countEntries (Update::DependentMap& map)
{
	int32 total = 0;
	Update::DependentMapIterConst iterMap = map.begin ();
	while (iterMap != map.end ())
	{
		const Update::DependentList& list = iterMap->second;
		Update::DependentListIterConst iterList = list.begin ();
		while (iterList != list.end ())
		{
			total++;
			++iterList;
		}
		++iterMap;
	}
	return total;
}

//------------------------------------------------------------------------
UpdateHandler::UpdateHandler () : table (0)
{
	table = NEW Update::Table;
	if (FObject::getUpdateHandler () == 0)
		FObject::setUpdateHandler (this);
}

//------------------------------------------------------------------------
UpdateHandler::~UpdateHandler ()
{
	if (FObject::getUpdateHandler () == this)
		FObject::setUpdateHandler (0);
	delete table;
	table = 0;
}

//------------------------------------------------------------------------
tresult PLUGIN_API UpdateHandler::addDependent (FUnknown* u, IDependent* _dependent)
{
	IPtr<FUnknown> unknown = Update::getUnknownBase (u);
	if (!unknown || !_dependent)
		return kResultFalse;

	FGuard guard (lock);

#if CLASS_NAME_TRACKED
	Update::Dependency dependent (unknown, _dependent);

	FObject* obj = FObject::unknownToObject (unknown);
	if (obj)
		dependent.objClass = obj->isA ();
	obj = FObject::unknownToObject (_dependent);
	if (obj)
		dependent.depClass = obj->isA ();
#else
	IDependent* dependent = _dependent;
#endif

	Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
	Update::DependentMapIter it = map.find (unknown);
	if (it == map.end ())
	{
		Update::DependentList list;
		list.push_back (dependent);
		map[u] = list;
	}
	else
	{
		(*it).second.push_back (dependent);
	}

	return kResultTrue;
}

//------------------------------------------------------------------------
tresult PLUGIN_API UpdateHandler::removeDependent (FUnknown* u, IDependent* dependent)
{
	IPtr<FUnknown> unknown = Update::getUnknownBase (u);
	if (unknown == 0 && dependent == 0)
		return kResultFalse;

	FGuard guard (lock);

	Update::UpdateDataListIterConst iter = table->updateData.begin ();
	while (iter != table->updateData.end ())
	{
		if ((*iter).obj == unknown || unknown == 0)
		{
			for (uint32 count = 0; count < (*iter).count; count++)
			{
				if ((*iter).dependents[count] == dependent)
					(*iter).dependents[count] = 0;
			}
		}
		++iter;
	}
	// Remove all objects for the given dependent
	if (unknown == 0)
	{
		for (uint32 j = 0; j < Update::kHashSize; j++)
		{
			Update::DependentMap& map = table->depMap[j];
			Update::DependentMapIter iterMap = map.begin ();
			while (iterMap != map.end ())
			{
				Update::DependentList& list = (*iterMap).second;
				Update::DependentListIter iterList = list.begin ();
				while (iterList != list.end ())
				{
					if ((*iterList) == dependent)
						list.erase (iterList++);
					else
						++iterList;
				}
				++iterMap;
			}
		}
	}
	else
	{
		bool mustFlush = true;

		Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
		Update::DependentMapIter iterList = map.find (unknown);

#if NON_EXISTING_DEPENDENCY_CHECK
		ASSERT (iterList != map.end () && "ERROR: Trying to remove a non existing dependency!")
#endif
		if (iterList != map.end ())
		{
			if (dependent == 0) // Remove all dependents of object
			{
				map.erase (iterList);
			}
			else // Remove one dependent
			{
				int32 eraseCount = 0;
				Update::DependentList& dependentlist = (*iterList).second;
				Update::DependentListIter iterDependentlist = dependentlist.begin ();
				while (iterDependentlist != dependentlist.end ())
				{
#if CLASS_NAME_TRACKED
					if ((*iterDependentlist).dep == dependent)
#else
					if ((*iterDependentlist) == dependent)
#endif
					{
						dependentlist.erase (iterDependentlist++);
						eraseCount++;
						if (dependentlist.empty ())
						{
							map.erase (iterList);
							break;
						}
					}
					else
					{
						++iterDependentlist;
						mustFlush = false;
					}
				}
			}
		}
		if (mustFlush)
			cancelUpdates (unknown);
	}

	return kResultTrue;
}

//------------------------------------------------------------------------
tresult UpdateHandler::doTriggerUpdates (FUnknown* u, int32 message, bool suppressUpdateDone)
{
	IPtr<FUnknown> unknown = Update::getUnknownBase (u);
	if (!unknown)
		return kResultFalse;

	// to avoid crashes due to stack overflow, we reduce the amount of memory reserved for the
	// dependents
	IDependent* smallDependents[Update::kMapSize / 10]; // 8kB for x64
	IDependent** dependents = smallDependents;
	int32 maxDependents = Update::kMapSize / 10;
	int32 count = 0;

	{
		FGuard guard (lock); // scope for lock

		Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
		Update::DependentMapIterConst iterList = map.find (unknown);
		if (iterList != map.end ())
		{
			const Update::DependentList& dependentlist = (*iterList).second;
			Update::DependentListIterConst iterDependentlist = dependentlist.begin ();
			while (iterDependentlist != dependentlist.end ())
			{
#if CLASS_NAME_TRACKED
				dependents[count] = (*iterDependentlist).dep;
#else
				dependents[count] = *iterDependentlist;
#endif
				count++;

				if (count >= maxDependents)
				{
					if (dependents == smallDependents)
					{
						dependents = new IDependent*[Update::kMapSize];
						memcpy (dependents, smallDependents, count * sizeof (dependents[0]));
						maxDependents = Update::kMapSize;
					}
					else
					{
						WARNING ("Dependency overflow")
						break;
					}
				}
				++iterDependentlist;
			}
		}

		// push update data on the stack
		if (count > 0)
		{
			Update::UpdateData data (unknown, dependents, count);
			table->updateData.push_back (data);
		}
	} // end scope

	int32 i = 0;
	while (i < count)
	{
		if (dependents[i])
			dependents[i]->update (unknown, message);
		i++;
	}

	if (dependents != smallDependents)
		delete[] dependents;

	// remove update data from the stack
	if (count > 0)
	{
		FGuard guard (lock);

		table->updateData.pop_back ();
	}

	// send update done message
	if (suppressUpdateDone == false)
		Update::updateDone (unknown, message);

	return count > 0 ? kResultTrue : kResultFalse; // Object was found and has been updated
}

//------------------------------------------------------------------------
tresult PLUGIN_API UpdateHandler::triggerUpdates (FUnknown* u, int32 message)
{
	return doTriggerUpdates (u, message, false);
}

//------------------------------------------------------------------------
tresult PLUGIN_API UpdateHandler::deferUpdates (FUnknown* u, int32 message)
{
	IPtr<FUnknown> unknown = Update::getUnknownBase (u);
	if (!unknown)
		return kResultFalse;

	FGuard guard (lock);

	Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
	Update::DependentMapIterConst iterList = map.find (unknown);

	bool hasDependency = (iterList != map.end ());
	if (hasDependency == false)
	{
		Update::updateDone (unknown, message);
		return kResultTrue;
	}

	bool found = false;
	Update::DeferedChangeListIterConst iter = table->defered.begin ();
	while (iter != table->defered.end ())
	{
		if ((*iter).obj == unknown && (*iter).msg == message)
		{
			found = true;
			break;
		}
		++iter;
	}

	if (!found)
	{
		Update::DeferedChange change (unknown, message);
		table->defered.push_back (change);
	}

	return kResultTrue;
}

//------------------------------------------------------------------------
tresult PLUGIN_API UpdateHandler::triggerDeferedUpdates (FUnknown* unknown)
{
	Update::DeferedChangeList deferedAgain;
	if (!unknown)
	{
		while (table->defered.empty () == false)
		{
			// Remove first from queue
			lock.lock ();

			FUnknown* obj = table->defered.front ().obj;
			int32 msg = table->defered.front ().msg;
			table->defered.pop_front ();

			// check if this object is currently being updated. in this case, defer update again...
			bool canSignal = true;
			Update::UpdateDataListIterConst iter = table->updateData.begin ();
			while (iter != table->updateData.end ())
			{
				if ((*iter).obj == obj)
				{
					canSignal = false;
					break;
				}
				++iter;
			}
			lock.unlock ();

			if (canSignal)
			{
				triggerUpdates (obj, msg);
			}
			else
			{
				Update::DeferedChange change (obj, msg);
				deferedAgain.push_back (change);
			}
		}
	}
	else
	{
		IPtr<FUnknown> object = Update::getUnknownBase (unknown);
		Update::DeferedChange tmp (object);

		while (true)
		{
			lock.lock ();
			Update::DeferedChangeListIterConst it =
			    std::find (table->defered.begin (), table->defered.end (), tmp);
			if (it == table->defered.end ())
			{
				lock.unlock ();
				return kResultTrue;
			}

			if ((*it).obj != 0)
			{
				int32 msg = (*it).msg;
				table->defered.erase (it);

				// check if this object is currently being updated. in this case, defer update
				// again...
				bool canSignal = true;
				Update::UpdateDataListIterConst iter = table->updateData.begin ();
				while (iter != table->updateData.end ())
				{
					if ((*iter).obj == object)
					{
						canSignal = false;
						break;
					}
					++iter;
				}
				lock.unlock ();

				if (canSignal)
				{
					triggerUpdates (object, msg);
				}
				else
				{
					Update::DeferedChange change (object, msg);
					deferedAgain.push_back (change);
				}
			}
		}
	}

	if (deferedAgain.empty () == false)
	{
		FGuard guard (lock);

		Update::DeferedChangeListIterConst iter = deferedAgain.begin ();
		while (iter != deferedAgain.end ())
		{
			table->defered.push_back (*iter);
			++iter;
		}
	}

	return kResultTrue;
}

//------------------------------------------------------------------------
tresult PLUGIN_API UpdateHandler::cancelUpdates (FUnknown* u)
{
	IPtr<FUnknown> unknown = Update::getUnknownBase (u);
	if (!unknown)
		return kResultFalse;

	FGuard guard (lock);

	Update::DeferedChange change (unknown, 0);
	table->defered.remove (change);

	return kResultTrue;
}

#if DEVELOPMENT
//------------------------------------------------------------------------
bool UpdateHandler::checkDeferred (FUnknown* object)
{
	IPtr<FUnknown> unknown = Update::getUnknownBase (object);

	FGuard guard (lock);

	Update::DeferedChange tmp (unknown);
	Update::DeferedChangeListIterConst it =
	    std::find (table->defered.begin (), table->defered.end (), tmp);
	if (it != table->defered.end ())
		return true;

	return false;
}

//------------------------------------------------------------------------
bool UpdateHandler::hasDependencies (FUnknown* u)
{
	IPtr<FUnknown> unknown = Update::getUnknownBase (u);
	if (!unknown)
		return false;

	FGuard guard (lock);

	Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
	Update::DependentMapIterConst iterList = map.find (unknown);
	bool hasDependency = (iterList != map.end ());

	return hasDependency;
}

//------------------------------------------------------------------------
void UpdateHandler::printForObject (FObject* obj) const
{
	IPtr<FUnknown> unknown = Update::getUnknownBase (obj);
	if (!unknown)
		return;

	FUnknownPtr<IDependent> dep (obj);

	bool header = false;

	Update::DependentMap& map = table->depMap[Update::hashPointer (unknown)];
	Update::DependentMapIterConst iterList = map.begin ();
	while (iterList != map.end ())
	{
		const Update::DependentList& dependentlist = (*iterList).second;
		Update::DependentListIterConst iterDependentlist = dependentlist.begin ();
		while (iterDependentlist != dependentlist.end ())
		{
#if CLASS_NAME_TRACKED
			if ((*iterList).first == unknown || (*iterDependentlist).dep == dep.getInterface ())
			{
				if (!header)
				{
					FDebugPrint ("Dependencies for object %8" FORMAT_INT64A " %s\n", (uint64)obj,
					             obj->isA ());
					header = true;
				}
				FDebugPrint ("%s %8" FORMAT_INT64A "\n <- %s %8" FORMAT_INT64A "\n",
				             (*iterDependentlist).depClass, (uint64) (*iterDependentlist).dep,
				             (*iterDependentlist).objClass, (uint64) ((*iterList).first));
			}
#else
			if ((*iterList).first == unknown || (*iterDependentlist) == dep.getInterface ())
			{
				if (!header)
				{
					FDebugPrint ("Dependencies for object %8" FORMAT_INT64A " %s\n", (uint64)obj,
					             obj->isA ());
					header = true;
				}
				FDebugPrint ("%8" FORMAT_INT64A "\n <- %8" FORMAT_INT64A "\n",
				             (uint64) (*iterDependentlist), (uint64) ((*iterList).first));
			}
#endif
		}

		++iterList;
	}
}
#endif

//------------------------------------------------------------------------
} // namespace Steinberg