public VPaintLayer NewLayer()
        {
            VPaintLayer layer = new VPaintLayer();

            layers.Add(layer);
            return(layer);
        }
Beispiel #2
0
 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;
     }
 }
Beispiel #3
0
        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);
        }
Beispiel #4
0
        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);
        }
Beispiel #6
0
        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
            });
        }