public VPaintLayer NewLayer() { VPaintLayer layer = new VPaintLayer(); layers.Add(layer); return(layer); }
public void ApplyTo(VPaintLayer layer, params VPaintActionType[] types) { foreach (VPaintVertexData data in layer.paintData) { Color[] colors = data.colors; float[] transparency = data.transparency; for (int i = 0; i < colors.Length; i++) { foreach (var t in types) { Apply(ref colors[i], ref transparency[i], t); } } data.colors = colors; } }
public VPaintLayer Clone() { VPaintLayer layer = new VPaintLayer(); foreach (var vpd in paintData) { var v = vpd.Clone(); layer.paintData.Add(v); } layer.blendMode = blendMode; layer.name = name; layer.tag = tag; layer.opacity = opacity; layer.enabled = enabled; layer.maskR = maskR; layer.maskG = maskG; layer.maskB = maskB; layer.maskA = maskA; return(layer); }
public void Merge(VPaintLayer layer, Color baseColor = new Color()) { foreach (VPaintVertexData data in layer.paintData) { var rootData = Get(data.identifier); Color[] cols = null; float[] trans = null; if (rootData != null) { cols = rootData.colors; trans = rootData.transparency; } else { cols = new Color[data.colors.Length]; for (int i = 0; i < cols.Length; i++) { cols[i] = baseColor; } trans = new float[data.transparency.Length]; rootData = new VPaintVertexData(); rootData.colors = cols; rootData.transparency = trans; rootData.colorer = data.colorer; paintData.Add(rootData); } VPaintUtility.MergeColors( cols, trans, data.colors, data.transparency, layer.blendMode, layer.opacity, layer.maskR, layer.maskG, layer.maskB, layer.maskA ); } }
public VPaintLayer GetMergedLayer(VPaintLayer baseLayer) { VPaintLayer merged = null; if (baseLayer == null) { merged = new VPaintLayer(); } else { merged = baseLayer.Clone(); } for (int i = 0; i < layers.Count; i++) { VPaintLayer layer = layers[i]; if (!layer.enabled) { continue; } merged.Merge(layer); } return(merged); }
public static IEnumerator <VPaintLayerProgress> CalculateAmbientOcclusionAsync( VPaintObject[] objects, float radius, float intensity, int sampleCount, Color darkColor, Color lightColor, Bounds?bounds = null) { VPaintLayer layer = new VPaintLayer(); //Used for valid targets to sample through (avoid realloc) List <VPaintObject> targets = new List <VPaintObject>(); for (int i = 0; i < objects.Length; i++) { var vc = objects[i]; //Grab the bounds of this object var checkBounds = vc.GetBounds(); //Expand the bounds by radius, so account for sample length checkBounds.Expand(radius); //Does this object even intersect with the given bounds? (if any) if (bounds != null && !bounds.Value.Intersects(checkBounds)) { continue; } //Grab mesh and duplicate arrays var mesh = vc.GetMeshInstance(); var verts = mesh.vertices; var norms = mesh.normals; //grab another normals array copy, // so that if we recalculate, we can reset the mesh's normals back the way they were var originalNorms = mesh.normals; //AO needs normals to work, so if they don't exist then recalculate them if (norms.Length != verts.Length) { mesh.RecalculateNormals(); norms = mesh.normals; } //Color buffer to fill with AO data var colors = new Color[verts.Length]; //Cull out objects which don't intersect with this one targets.Clear(); foreach (var targ in objects) { //must have an editor collider! if (Application.isPlaying && !targ.isDynamic) { continue; } if (checkBounds.Intersects(targ.GetBounds())) { targets.Add(targ); } } //Message for the async process string msg = "Sampling AO on " + vc.name + " for vert "; //Loop through all verts and sample AO for (int v = 0; v < verts.Length; v++) { var localVert = verts[v]; //Grab vert and transform it to the world position var vert = vc.transform.TransformPoint(localVert); //don't do calculations on vert not within the bounds! if (bounds != null && !bounds.Value.Contains(vert)) { continue; } //grab normal var norm = norms[v]; //Displace the vert by its normal var vertDisp = vc.transform.TransformPoint(localVert + norm); //calc world normal var worldNormal = (vertDisp - vert).normalized; //collated occlusion factor float occlusion = 0f; //sample each object for (int h = 0; h < sampleCount; h++) { //randomness for sample // (is there a better way to do this?) var rayDir = Quaternion.FromToRotation(Vector3.up, worldNormal) * Quaternion.Euler(UnityEngine.Random.Range(-90f, 90f), UnityEngine.Random.Range(-90f, 90f), UnityEngine.Random.Range(-90f, 90f)) * Vector3.up; //world normal reflection var offset = Vector3.Reflect(rayDir, worldNormal); //make sure the ray length lines up with the input radius rayDir = rayDir * (radius / rayDir.magnitude); //build ray Ray ray = new Ray(vert - (offset * 0.1f), rayDir); //vert + sample, (vert - sample).normalized); //initialize empty raycast hit so we can do a champion test RaycastHit closestHit = new RaycastHit(); closestHit.distance = Mathf.Infinity; bool used = false; RaycastHit hit; foreach (var targ in targets) { var collider = targ.editorCollider; //cast the ray if (collider.Raycast(ray, out hit, radius)) { //if the ray hit, only use it if it's closer than the most recent hit if (hit.distance < closestHit.distance) { closestHit = hit; used = true; } } } if (used) { //if we found something, add to the occlusion occlusion += Mathf.Clamp01(1 - (closestHit.distance / radius)); } } //normalize occlusion occlusion = Mathf.Clamp01(1 - ((occlusion * intensity) / sampleCount)); //lerp in the color based on the occlusion factor colors[v] = Color.Lerp(darkColor, lightColor, occlusion); if (v % 10 == 0) { //yield out a message for the async op yield return(new VPaintLayerProgress() { progress = (float)v / verts.Length, message = msg + v + "/" + verts.Length, result = null }); } } // Create paint data in the layer for this object var pd = layer.GetOrCreate(vc); pd.colors = colors; pd.transparency = new float[pd.colors.Length]; for (int v = 0; v < pd.transparency.Length; v++) { pd.transparency[v] = 1f; } mesh.normals = originalNorms; } yield return(new VPaintLayerProgress() { progress = 1, message = "Finished", result = layer }); }