Simple Runtime Placement
The attached Unity package contains an example of a simple decal placement on a car.
Code
using UnityEngine; using System.Collections; using ch.sycoforge.Decal; public class DecalPlacement : MonoBehaviour { //--------------------------------- // Exposed Fields //--------------------------------- public EasyDecal Decal; public float PlacementOffset = 4; public float RotationSpeed = 6; public float Rotation; public float ScaleSpeed = 1; public Vector2 Scale = Vector2.one; public bool Editable; //--------------------------------- // Fields //--------------------------------- private Camera ActiveCamera; //--------------------------------- // Constants //--------------------------------- private const string ROT_INPUT_NAME = "Mouse ScrollWheel"; private const string VER_INPUT_NAME = "Vertical"; private const string HOR_INPUT_NAME = "Horizontal"; //--------------------------------- // Mehods //--------------------------------- private void Start() { // Take main camera if no camera specified if(ActiveCamera == null) { ActiveCamera = Camera.main; } } private void Update () { ChangeAtlasIndex(); if(Input.GetKeyDown(KeyCode.E)) { Editable = !Editable; Decal.Baked = !Editable; } if (!Editable) { return; } MoveRotateDecal(); ScaleDecal(); } private void ChangeAtlasIndex() { int dir = 0; if (Input.GetKeyDown(KeyCode.Y)) { dir = 1; } if (Input.GetKeyDown(KeyCode.X)) { dir = -1; } if(dir != 0) { Decal.Baked = false; Decal.AtlasRegionIndex += dir; Decal.LateBake(); } } private void MoveRotateDecal() { float delta = Input.GetAxis(ROT_INPUT_NAME); // Scroll event received if (Mathf.Abs(delta) > 0f) { // Sum delta rotation Rotation += delta * RotationSpeed * Time.deltaTime * 1000.0f; } Vector2 screenPoint = Input.mousePosition; // Ray from camera's origin Ray ray = ActiveCamera.ScreenPointToRay(screenPoint); RaycastHit hit; if (Physics.Raycast(ray, out hit, ActiveCamera.farClipPlane)) { Vector3 avgNormal = AverageNormal(screenPoint, PlacementOffset); Quaternion quat = Quaternion.AngleAxis(Rotation, avgNormal) * Quaternion.FromToRotation(Vector3.up, avgNormal); // Apply decal transformations Decal.CachedTransform.position = hit.point + avgNormal * 0.2f; Decal.CachedTransform.rotation = quat; } } private void ScaleDecal() { float v = Input.GetAxis(VER_INPUT_NAME) * ScaleSpeed; float h = Input.GetAxis(HOR_INPUT_NAME) * ScaleSpeed; // Return if no input changes if (!(Mathf.Abs(v) > 0f || Mathf.Abs(h) > 0)) { return; } // Sum delta scale Scale += new Vector2(h, v) * Time.deltaTime; Decal.CachedTransform.localScale = new Vector3(Scale.x, 1.0f, Scale.y); } private Vector3 AverageNormal(Vector2 screenPoint, float offset) { Vector3 normal = Vector3.zero; Camera cam = Camera.current; int hits = 0; Vector2[] offsets = new Vector2[] { new Vector2(0, offset), new Vector2(offset, offset), new Vector2(offset, 0), new Vector2(offset, -offset), new Vector2(0, -offset), new Vector2(-offset, -offset), new Vector2(-offset, 0), new Vector2(-offset, offset) }; for (int i = 0; i < offsets.Length; i++) { Ray ray = ActiveCamera.ScreenPointToRay(screenPoint + offsets[i]); RaycastHit hit; if (Physics.Raycast(ray, out hit, 1000.0f)) { normal += hit.normal; hits++; } } return normal / hits; } }