Esempio n. 1
0
    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.Z))
        {
            SceneManager.LoadScene(0);
        }
        RaycastHit hit;

        if (!Input.GetMouseButtonDown(0) || !Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit))
        {
            return;
        }
        var building = hit.transform.name == "Cottage" ||
                       hit.transform.name == "house1" ||
                       hit.transform.name == "house2";
        var explosionRange = building ? 2 : 0.5f;
        var sw             = Stopwatch.StartNew();

        var commands = new ScamScatter.ScatterCommands();

        commands.Add(hit.transform.gameObject);
        new ScamScatter.Scatter {
            TargetArea = 0.8f, MaxTimeMs = 40
        }
        .Run(this, _ =>
        {
            _info = _.NewGameObjects > 0
                ? $"Scattered {hit.transform.name} ({_.SourceTriangles} triangles) into {_.NewGameObjects} new game objects in {sw.ElapsedMilliseconds} ms."
                : "Nothing to scatter.";
            ScamScatter.Explode.Run(hit.point, 1.5f, 2);
        },
             commands);
    }
Esempio n. 2
0
 /// <summary>
 /// Creates a number of new game objects with meshes that will resemble the original mesh.
 /// The new parts may then be moved around separately.
 /// </summary>
 /// <param name="gameObject">The game object to scatter. Must have a read/write enabled mesh and must not be marked as static.</param>
 /// <param name="parts">Aim at creating this number of new parts. Depending on maxArea, the result may be much larger.</param>
 /// <param name="maxArea">The approx area of the new parts. This is the area of the front side.</param>
 /// <param name="thicknessMin">Min thickness of the newmeshes (random range)</param>
 /// <param name="thicknessMax">Max thickness of the new meshes(random range)</param>
 /// <param name="parentTransform">Attach the new objects to this transform.</param>
 /// <param name="destroyOriginal">If the original object shall be destroyed automatically</param>
 /// <returns></returns>
 public void Run(
     MonoBehaviour mb,
     Action <Stats> whenDone,
     ScatterCommands commands)
 {
     mb.StartCoroutine(run(commands, whenDone));
 }
Esempio n. 3
0
 public void PrepareScatter(ScatterCommands commands)
 {
 }
Esempio n. 4
0
 public static void Run(
     ScatterCommands commands)
 {
     new Scatter().run(commands, null).MoveNext();
 }
Esempio n. 5
0
        private IEnumerator run(
            ScatterCommands commands,
            Action <Stats> whenDone)
        {
            var sw          = Stopwatch.StartNew();
            var stats       = new Stats();
            var objectCount = 0;

            foreach (var cmd in commands)
            {
                var gameObject = cmd.GameObject;
                // already scattered?
                if (gameObject == null ||
                    gameObject.name.StartsWith(FragmentNamePrefix) ||
                    gameObject.name.StartsWith(DebrisNamePrefix))
                {
                    continue;
                }

                var targetPartCount = cmd.TargetPartCount.GetValueOrDefault(TargetPartCount);
                var targetArea      = cmd.TargetArea.GetValueOrDefault(TargetArea);
                var newThicknessMin = cmd.NewThicknessMin.GetValueOrDefault(NewThicknessMin);
                var newThicknessMax = cmd.NewThicknessMax.GetValueOrDefault(NewThicknessMax);
                Debug.Log(targetArea);

                var meshData = new meshData(cmd.Mesh, cmd.MeshScale);
                stats.SourceTriangles += meshData.TotalTriangleCount;
                for (var submeshIndex = 0; submeshIndex < cmd.Mesh.subMeshCount; submeshIndex++)
                {
                    meshData.SelectSubmesh(submeshIndex);
                    var maxTris = Mathf.Max(2, meshData.TotalTriangleCount / targetPartCount);
                    while (meshData.Triangles.Any())
                    {
                        var newTriangles = new List <triangle>();
                        var newVerticies = new List <vertex>();

                        var quota = Mathf.Min(meshData.Triangles.Count, Random.Range(maxTris - 1, maxTris + 2));
                        extractTrianglesAndVerticies(newVerticies, newTriangles, quota, meshData, targetArea);

                        // at this point, we have a continous surface in newTriangles+newVerticies which
                        // are a subset of the whole mesh
                        // now let's create some SCAM!

                        // first, calculate the average normal of the surface and the midpoint
                        var frontNormal   = Vector3.zero;
                        var frontMidpoint = Vector3.zero;
                        foreach (var t in newTriangles)
                        {
                            var p1 = newVerticies[t.I0].Pos;
                            var p2 = newVerticies[t.I1].Pos;
                            var p3 = newVerticies[t.I2].Pos;

                            frontMidpoint += p1 + p2 + p3;
                            frontNormal   += Vector3.Cross(p2 - p1, p3 - p1);
                        }
                        var backVector   = -frontNormal.normalized * Random.Range(newThicknessMin, newThicknessMax);
                        var backMidpoint = frontMidpoint / (newTriangles.Count * 3) + backVector;

                        // now we create a backside by creating a flipped backside, pushed backward by
                        // the user's specified thickness. we also contract it somewhat, to avoid z-fighting
                        // with orthogonal surfaces
                        var vertLength = newVerticies.Count;
                        for (var k = 0; k < vertLength; k++)
                        {
                            newVerticies.Add(new vertex
                            {
                                Pos     = Vector3.Lerp(newVerticies[k].Pos + backVector, backMidpoint, 0.2f),
                                Uv      = Vector2.one - newVerticies[k].Uv,
                                Normal  = -newVerticies[k].Normal,
                                Tangent = newVerticies[k].Tangent
                            });
                        }

                        // create all the new scam triangles
                        var tlen = newTriangles.Count;
                        for (var ti = 0; ti < tlen; ti++)
                        {
                            var t = newTriangles[ti];
                            // this is triangle for the backside (only verticies has been created this far)
                            newTriangles.Add(new triangle(t.I0 + vertLength, t.I2 + vertLength, t.I1 + vertLength));

                            // for each side of the front triangle, investigate if it is an outer
                            // side, and if so, create two triangles connecting the front with the back
                            buildSideRect(newVerticies, vertLength, newTriangles, ti, t.I0, t.I1);
                            buildSideRect(newVerticies, vertLength, newTriangles, ti, t.I1, t.I2);
                            buildSideRect(newVerticies, vertLength, newTriangles, ti, t.I2, t.I0);
                            // the new triangles created have completely wrong normals. fixing that would
                            // slow things down. just sayin'
                        }

                        // a micro optimizition here would be to stop using LINQ
                        var theNewMesh = new Mesh
                        {
                            vertices  = newVerticies.Select(_ => _.Pos).ToArray(),
                            normals   = newVerticies.Select(_ => _.Normal).ToArray(),
                            uv        = newVerticies.Select(_ => _.Uv).ToArray(),
                            tangents  = newVerticies.Select(_ => _.Tangent).ToArray(),
                            triangles = newTriangles.SelectMany(_ => new[] { _.I0, _.I1, _.I2 }).ToArray()
                        };

                        var newFragment = new GameObject($"{FragmentNamePrefix}{++objectCount}");
                        newFragment.transform.parent   = cmd.NewTransformParent;
                        newFragment.transform.position = cmd.Renderer.transform.position;
                        newFragment.transform.rotation = cmd.Renderer.transform.rotation;
#if UNITY_EDITOR
                        newFragment.AddComponent <MeshRenderer>().sharedMaterial = cmd.Renderer.sharedMaterials[submeshIndex % cmd.Renderer.sharedMaterials.Length];
#else
                        newFragment.AddComponent <MeshRenderer>().material = cmd.Renderer.materials[submeshIndex % cmd.Renderer.materials.Length];
#endif
                        newFragment.AddComponent <MeshFilter>().mesh = theNewMesh;
                        newFragment.AddComponent <BoxCollider>();

                        stats.NewGameObjects++;
                        stats.NewTriangles += newTriangles.Count;

                        if (MaxTimeMs > 0 && sw.ElapsedMilliseconds > MaxTimeMs)
                        {
                            Debug.Log("Yield");
                            yield return(null);

                            sw.Restart();
                        }
                    }
                }

                if (cmd.DestroyMesh)
                {
                    Object.Destroy(cmd.Mesh);
                }
                if (cmd.Destroy)
                {
                    Object.Destroy(gameObject);
                }
            }
            whenDone?.Invoke(stats);
        }