예제 #1
0
        /*/// <summary>
         * /// attempt at adding this to the combatObject. it's not working too well though.
         * /// </summary>
         * /// <param name="battletick"></param>
         * /// <param name="attacker"></param>
         * /// <param name="weapon"></param>
         * /// <param name="Sector"></param>
         * /// <param name="IsReplay"></param>
         * /// <returns></returns>
         *
         * public CombatTakeFireEvent FireWeapon(int battletick, CombatObject attacker, CombatWeapon weapon, Space.Sector Sector, bool IsReplay)
         * {
         *  var wpninfo = weapon.weapon.Template.ComponentTemplate.WeaponInfo;
         *  Fix16 rangeForDamageCalcs = (Fix16)0;
         *  Fix16 rangetotarget = Trig.distance(attacker.cmbt_loc, this.cmbt_loc);
         *  int targettic = battletick;
         *
         *  //reset the weapon nextReload.
         *  weapon.nextReload = battletick + (int)(weapon.reloadRate * Battle_Space.TicksPerSecond); // TODO - round up, so weapons that fire more than 10 times per second don't fire at infinite rate
         *
         *  var target_icomobj = this.WorkingObject;
         *  //Vehicle defenderV = (Vehicle)target_icomobj;
         *
         *  if (!weapon.CanTarget(target_icomobj))
         *      return null;
         *
         *  // TODO - check range too
         *  var tohit =
         *      Mod.Current.Settings.WeaponAccuracyPointBlank // default weapon accuracy at point blank range
         + weapon.weapon.Template.WeaponAccuracy // weapon's intrinsic accuracy modifier
         + weapon.weapon.Container.Accuracy // firing ship's accuracy modifier
         +      - target_icomobj.Evasion // target's evasion modifier
         +      - Sector.GetAbilityValue(this.WorkingObject.Owner, "Sector - Sensor Interference").ToInt() // sector evasion modifier
         + Sector.GetAbilityValue(attacker.WorkingObject.Owner, "Combat Modifier - Sector").ToInt() // generic combat bonuses
         +      - Sector.GetAbilityValue(this.WorkingObject.Owner, "Combat Modifier - Sector").ToInt()
         + Sector.StarSystem.GetAbilityValue(attacker.WorkingObject.Owner, "Combat Modifier - System").ToInt()
         +      - Sector.StarSystem.GetAbilityValue(this.WorkingObject.Owner, "Combat Modifier - System").ToInt()
         + attacker.WorkingObject.Owner.GetAbilityValue("Combat Modifier - Empire").ToInt()
         +      - this.WorkingObject.Owner.GetAbilityValue("Combat Modifier - Empire").ToInt();
         +  // TODO - moddable min/max hit chances with per-weapon overrides
         +  if (tohit > 99)
         +      tohit = 99;
         +  if (tohit < 1)
         +      tohit = 1;
         +  if (weapon.weapon.Container.HasAbility("Weapons Always Hit"))
         +      tohit = 100;
         +
         +  //bool hit = RandomHelper.Range(0, 99) < tohit;
         +  PRNG dice = attacker.getDice();
         +  bool hit = dice.Range(0, 99) < tohit;
         +
         +  CombatTakeFireEvent target_event = null;
         +
         +  if (weapon.weaponType == "Seeker")
         +  {
         +
         +      //create seeker and node.
         +      CombatSeeker seeker = new CombatSeeker(attacker, weapon, -tempObjCounter);
         +      seeker.waypointTarget = new combatWaypoint(this);
         +      seeker.weaponTarget = new List<CombatObject>() { this };
         +      seeker.deathTick = battletick + weapon.maxRange_time;
         +      seeker.cmbt_head = attacker.cmbt_head;
         +      seeker.cmbt_att = attacker.cmbt_att;
         +      FreshNodes.Add(seeker);
         +
         +      foreach (var emp in Empires.Values)
         +      {
         +          if (emp.ownships.Contains(attacker))
         +              emp.ownships.Add(seeker);
         +          if (emp.friendly.Contains(attacker))
         +              emp.friendly.Add(seeker);
         +          if (emp.neutral.Contains(attacker))
         +              emp.neutral.Add(seeker);
         +          if (emp.hostile.Contains(attacker))
         +              emp.hostile.Add(seeker);
         +      }
         +
         +      if (IsReplay)
         +      {
         +          //read the event
         +          target_event = ReplayLog.EventsForObjectAtTick(this, targettic).OfType<CombatTakeFireEvent>().ToList<CombatTakeFireEvent>()[0];
         +          target_event.BulletNode = seeker;
         +      }
         +      else
         +      {
         +          //*write* the event
         +          target_event = new CombatTakeFireEvent(battletick, this, this.cmbt_loc, false);
         +          target_event.BulletNode = seeker;
         +          seeker.seekertargethit = target_event;
         +      }
         +  }
         +  //for bolt calc, need again for adding to list.
         +  else if (weapon.weaponType == "Bolt")
         +  {
         +
         +      rangeForDamageCalcs = rangeForDamageCalcs_bolt(attacker, weapon, this);
         +      Fix16 boltTTT = weapon.boltTimeToTarget(attacker, target);
         +      //set target tick for the future.
         +      targettic += (int)boltTTT;
         +
         +
         +
         +      if (IsReplay)
         +      {
         +          //read the event
         +          target_event = ReplayLog.EventsForObjectAtTick(this, targettic).OfType<CombatTakeFireEvent>().ToList<CombatTakeFireEvent>()[0];
         +
         +          //because bullets don't need to be created during processing
         +          Fix16 rThis_distance = (target_event.Location - target_event.fireOnEvent.Location).Length;
         +          PointXd bulletVector = Trig.intermediatePoint(attacker.cmbt_loc, target_event.Location, rThis_distance);
         +          if (!target_event.IsHit) //jitter it!
         +          {
         +              // TODO - take into account firing ship's accuracy and target's evasion
         +              int accuracy = target_event.fireOnEvent.Weapon.weapon.Template.WeaponAccuracy;
         +              int jitterAmount = 0;
         +              if (accuracy < 50)
         +                  jitterAmount = (int)System.Math.Pow(50 - accuracy, 2) / 50;
         +              if (jitterAmount < 5)
         +                  jitterAmount = 5;
         +              if (jitterAmount > 30)
         +                  jitterAmount = 30;
         +              //do *NOT* use ship prng here!!!! (since this is not done during normal processing, it'll cause differences, use any rand)
         +              Compass jitter = new Compass(RandomHelper.Range(-jitterAmount, jitterAmount), false);
         +              Compass bulletCompass = bulletVector.Compass;
         +              Compass offsetCompass = bulletCompass + jitter;
         +              bulletVector = offsetCompass.Point(bulletVector.Length);
         +          }
         +          CombatNode bullet = new CombatNode(attacker.cmbt_loc, bulletVector, -tempObjCounter, "BLT");
         +          target_event.BulletNode = bullet;
         +          FreshNodes.Add(bullet);
         +          if (target_event.IsHit)
         +          {
         +              bullet.deathTick = target_event.Tick;
         +          }
         +          else
         +          {
         +              bullet.deathTick = battletick + target_event.fireOnEvent.Weapon.maxRange;
         +          }
         +      }
         +      else
         +      {
         +          //*write* the event
         +          target_event = new CombatTakeFireEvent(targettic, this, this.cmbt_loc, hit);
         +          int nothing = tempObjCounter; //increase it just so processing has the same number of tempObjects created as replay will.
         +      }
         +
         +  }
         +  else //not bolt
         +  {
         +      if (IsReplay)
         +      { //read the replay... nothing to do if a beam.
         +      }
         +      else
         +      { //write the event.
         +          rangeForDamageCalcs = rangetotarget / (Fix16)1000;
         +          target_event = new CombatTakeFireEvent(targettic, this, this.cmbt_loc, hit);
         +      }
         +  }
         +
         +  rangeForDamageCalcs = Fix16.Max((Fix16)1, rangeForDamageCalcs); //don't be less than 1.
         +
         +  if (hit && !target_icomobj.IsDestroyed)
         +  {
         +      var shot = new Combat.Shot(weapon.weapon, target_icomobj, (int)rangeForDamageCalcs);
         +      //defender.TakeDamage(weapon.Template.ComponentTemplate.WeaponInfo.DamageType, shot.Damage, battle);
         +      int damage = shot.Damage;
         +      combatDamage(battletick, this, weapon, damage, attacker.getDice());
         +      if (target_icomobj.MaxNormalShields < target_icomobj.NormalShields)
         +          target_icomobj.NormalShields = target_icomobj.MaxNormalShields;
         +      if (target_icomobj.MaxPhasedShields < target_icomobj.PhasedShields)
         +          target_icomobj.PhasedShields = target_icomobj.MaxPhasedShields;
         +      //if (defender.IsDestroyed)
         +      //battle.LogTargetDeath(defender);
         +  }
         +  return target_event;
         + }
         */

        public void TakeDamage(Battle_Space battle, Hit hit, PRNG dice)
        {
            // special combat damage effects
            TakeSpecialDamage(battle, hit, dice);

            // basic damage effects
            WorkingObject.TakeDamage(hit, dice);
        }
예제 #2
0
 public void From(WorkingObject obj)
 {
     Name = obj.Name;
     m_mesh.From(obj.Mesh);
     m_materialIds = new List <string>();
     for (int i = 0; i < obj.Materials.Count; ++i)
     {
         m_materialIds.Add(obj.Materials[i].Guid);
     }
 }
예제 #3
0
        private static Vector2 clampHitCircleToPlayfield(WorkingObject workingObject)
        {
            var previousPosition = workingObject.PositionModified;

            workingObject.EndPositionModified = workingObject.PositionModified = clampToPlayfieldWithPadding(
                workingObject.PositionModified,
                (float)workingObject.HitObject.Radius
                );

            workingObject.HitObject.Position = workingObject.PositionModified;

            return(workingObject.PositionModified - previousPosition);
        }
예제 #4
0
        private void Combine(Vector3 rootPosition, HLODBuildInfo info)
        {
            var materialTable = new Dictionary <string, WorkingMaterial>();
            var combineInfos  = new Dictionary <string, List <MeshCombiner.CombineInfo> >();

            for (int i = 0; i < info.WorkingObjects.Count; ++i)
            {
                var materials = info.WorkingObjects[i].Materials;
                for (int m = 0; m < materials.Count; ++m)
                {
                    //var mat = materials[m];
                    MeshCombiner.CombineInfo combineInfo = new MeshCombiner.CombineInfo();

                    combineInfo.Transform      = info.WorkingObjects[i].LocalToWorld;
                    combineInfo.Transform.m03 -= rootPosition.x;
                    combineInfo.Transform.m13 -= rootPosition.y;
                    combineInfo.Transform.m23 -= rootPosition.z;
                    combineInfo.Mesh           = info.WorkingObjects[i].Mesh;
                    combineInfo.MeshIndex      = m;

                    if (combineInfos.ContainsKey(materials[m].Identifier) == false)
                    {
                        combineInfos.Add(materials[m].Identifier, new List <MeshCombiner.CombineInfo>());
                        materialTable.Add(materials[m].Identifier, materials[m]);
                    }

                    combineInfos[materials[m].Identifier].Add(combineInfo);
                }
            }

            using (var originWorkingObject = info.WorkingObjects)
            {
                DisposableList <WorkingObject> combinedObjects = new DisposableList <WorkingObject>();
                info.WorkingObjects = combinedObjects;

                MeshCombiner combiner = new MeshCombiner();
                foreach (var pair in combineInfos)
                {
                    WorkingMesh     combinedMesh   = combiner.CombineMesh(Allocator.Persistent, pair.Value);
                    WorkingObject   combinedObject = new WorkingObject(Allocator.Persistent);
                    WorkingMaterial material       = materialTable[pair.Key].Clone();

                    combinedMesh.name   = info.Name + "_Mesh" + pair.Key;
                    combinedObject.Name = info.Name;
                    combinedObject.SetMesh(combinedMesh);
                    combinedObject.Materials.Add(material);

                    combinedObjects.Add(combinedObject);
                }
            }
        }
예제 #5
0
        private static void computeModifiedPosition(WorkingObject current, WorkingObject?previous, WorkingObject?beforePrevious)
        {
            float previousAbsoluteAngle = 0f;

            if (previous != null)
            {
                if (previous.HitObject is Slider s)
                {
                    previousAbsoluteAngle = getSliderRotation(s);
                }
                else
                {
                    Vector2 earliestPosition = beforePrevious?.HitObject.EndPosition ?? playfield_centre;
                    Vector2 relativePosition = previous.HitObject.Position - earliestPosition;
                    previousAbsoluteAngle = MathF.Atan2(relativePosition.Y, relativePosition.X);
                }
            }

            float absoluteAngle = previousAbsoluteAngle + current.PositionInfo.RelativeAngle;

            var posRelativeToPrev = new Vector2(
                current.PositionInfo.DistanceFromPrevious * MathF.Cos(absoluteAngle),
                current.PositionInfo.DistanceFromPrevious * MathF.Sin(absoluteAngle)
                );

            Vector2 lastEndPosition = previous?.EndPositionModified ?? playfield_centre;

            posRelativeToPrev = RotateAwayFromEdge(lastEndPosition, posRelativeToPrev);

            current.PositionModified = lastEndPosition + posRelativeToPrev;

            if (!(current.HitObject is Slider slider))
            {
                return;
            }

            absoluteAngle = MathF.Atan2(posRelativeToPrev.Y, posRelativeToPrev.X);

            Vector2 centreOfMassOriginal = calculateCentreOfMass(slider);
            Vector2 centreOfMassModified = rotateVector(centreOfMassOriginal, current.PositionInfo.Rotation + absoluteAngle - getSliderRotation(slider));

            centreOfMassModified = RotateAwayFromEdge(current.PositionModified, centreOfMassModified);

            float relativeRotation = MathF.Atan2(centreOfMassModified.Y, centreOfMassModified.X) - MathF.Atan2(centreOfMassOriginal.Y, centreOfMassOriginal.X);

            if (!Precision.AlmostEquals(relativeRotation, 0))
            {
                RotateSlider(slider, relativeRotation);
            }
        }
예제 #6
0
        private void Combine(Vector3 rootPosition, TexturePacker packer, HLODBuildInfo info, dynamic options)
        {
            var atlas = packer.GetAtlas(info);

            if (atlas == null)
            {
                return;
            }

            List <TextureInfo> textureInfoList           = options.TextureInfoList;
            List <MeshCombiner.CombineInfo> combineInfos = new List <MeshCombiner.CombineInfo>();

            for (int i = 0; i < info.WorkingObjects.Count; ++i)
            {
                var obj = info.WorkingObjects[i];
                ConvertMesh(obj.Mesh, obj.Materials, atlas, textureInfoList[0].InputName);

                for (int si = 0; si < obj.Mesh.subMeshCount; ++si)
                {
                    var ci = new MeshCombiner.CombineInfo();
                    ci.Mesh      = obj.Mesh;
                    ci.MeshIndex = si;

                    ci.Transform      = obj.LocalToWorld;
                    ci.Transform.m03 -= rootPosition.x;
                    ci.Transform.m13 -= rootPosition.y;
                    ci.Transform.m23 -= rootPosition.z;

                    combineInfos.Add(ci);
                }
            }

            MeshCombiner combiner     = new MeshCombiner();
            WorkingMesh  combinedMesh = combiner.CombineMesh(Allocator.Persistent, combineInfos);

            WorkingObject   newObj = new WorkingObject(Allocator.Persistent);
            WorkingMaterial newMat = m_createdMaterials[atlas].Clone();

            combinedMesh.name = info.Name + "_Mesh";
            newObj.Name       = info.Name;
            newObj.SetMesh(combinedMesh);
            newObj.Materials.Add(newMat);

            info.WorkingObjects.Dispose();
            info.WorkingObjects = new DisposableList <WorkingObject>();
            info.WorkingObjects.Add(newObj);
        }
예제 #7
0
        private WorkingObject CreateBakedTerrain(string name, Bounds bounds, Heightmap heightmap, int distance)
        {
            WorkingObject wo = new WorkingObject(Allocator.Persistent);

            wo.Name = name;


            m_queue.EnqueueJob(() =>
            {
                WorkingMesh mesh = CreateBakedGeometry(name, heightmap, bounds, distance);
                wo.SetMesh(mesh);
            });

            m_queue.EnqueueJob(() =>
            {
                WorkingMaterial material = CreateBakedMaterial(name, bounds);
                wo.Materials.Add(material);
            });


            return(wo);
        }
예제 #8
0
        private WorkingObject CreateBakedTerrain(string name, Bounds bounds, Heightmap heightmap, int distance, bool isLeaf)
        {
            WorkingObject wo = new WorkingObject(Allocator.Persistent);

            wo.Name            = name;
            wo.LightProbeUsage = UnityEngine.Rendering.LightProbeUsage.Off;

            m_queue.EnqueueJob(() =>
            {
                WorkingMesh mesh = CreateBakedGeometry(name, heightmap, bounds, distance);
                wo.SetMesh(mesh);
            });

            m_queue.EnqueueJob(() =>
            {
                WorkingMaterial material = CreateBakedMaterial(name, bounds, isLeaf);
                wo.Materials.Add(material);
            });


            return(wo);
        }
예제 #9
0
        private static Vector2 clampSliderToPlayfield(WorkingObject workingObject)
        {
            var slider = (Slider)workingObject.HitObject;
            var possibleMovementBounds = calculatePossibleMovementBounds(slider);

            var previousPosition = workingObject.PositionModified;

            // Clamp slider position to the placement area
            // If the slider is larger than the playfield, at least make sure that the head circle is inside the playfield
            float newX = possibleMovementBounds.Width < 0
                ? Math.Clamp(possibleMovementBounds.Left, 0, OsuPlayfield.BASE_SIZE.X)
                : Math.Clamp(previousPosition.X, possibleMovementBounds.Left, possibleMovementBounds.Right);

            float newY = possibleMovementBounds.Height < 0
                ? Math.Clamp(possibleMovementBounds.Top, 0, OsuPlayfield.BASE_SIZE.Y)
                : Math.Clamp(previousPosition.Y, possibleMovementBounds.Top, possibleMovementBounds.Bottom);

            slider.Position = workingObject.PositionModified = new Vector2(newX, newY);
            workingObject.EndPositionModified = slider.EndPosition;

            shiftNestedObjects(slider, workingObject.PositionModified - workingObject.PositionOriginal);

            return(workingObject.PositionModified - previousPosition);
        }
        private static Vector2 clampSliderToPlayfield(WorkingObject workingObject)
        {
            var slider = (Slider)workingObject.HitObject;
            var possibleMovementBounds = calculatePossibleMovementBounds(slider);

            var previousPosition = workingObject.PositionModified;

            // Clamp slider position to the placement area
            // If the slider is larger than the playfield, force it to stay at the original position
            float newX = possibleMovementBounds.Width < 0
                ? workingObject.PositionOriginal.X
                : Math.Clamp(previousPosition.X, possibleMovementBounds.Left, possibleMovementBounds.Right);

            float newY = possibleMovementBounds.Height < 0
                ? workingObject.PositionOriginal.Y
                : Math.Clamp(previousPosition.Y, possibleMovementBounds.Top, possibleMovementBounds.Bottom);

            slider.Position = workingObject.PositionModified = new Vector2(newX, newY);
            workingObject.EndPositionModified = slider.EndPosition;

            shiftNestedObjects(slider, workingObject.PositionModified - workingObject.PositionOriginal);

            return(workingObject.PositionModified - previousPosition);
        }
        private static void computeModifiedPosition(WorkingObject current, WorkingObject?previous, WorkingObject?beforePrevious)
        {
            float previousAbsoluteAngle = 0f;

            if (previous != null)
            {
                Vector2 earliestPosition = beforePrevious?.HitObject.EndPosition ?? playfield_centre;
                Vector2 relativePosition = previous.HitObject.Position - earliestPosition;
                previousAbsoluteAngle = (float)Math.Atan2(relativePosition.Y, relativePosition.X);
            }

            float absoluteAngle = previousAbsoluteAngle + current.PositionInfo.RelativeAngle;

            var posRelativeToPrev = new Vector2(
                current.PositionInfo.DistanceFromPrevious * (float)Math.Cos(absoluteAngle),
                current.PositionInfo.DistanceFromPrevious * (float)Math.Sin(absoluteAngle)
                );

            Vector2 lastEndPosition = previous?.EndPositionModified ?? playfield_centre;

            posRelativeToPrev = RotateAwayFromEdge(lastEndPosition, posRelativeToPrev);

            current.PositionModified = lastEndPosition + posRelativeToPrev;
        }
예제 #12
0
        public IEnumerator CreateImpl()
        {
            try
            {
                using (m_queue = new JobQueue(8))
                {
                    Stopwatch sw = new Stopwatch();

                    AssetDatabase.Refresh();
                    AssetDatabase.SaveAssets();

                    sw.Reset();
                    sw.Start();

                    EditorUtility.DisplayProgressBar("Bake HLOD", "Initialize Bake", 0.0f);


                    TerrainData data = m_hlod.TerrainData;

                    m_size = data.size;

                    m_heightmap = new Heightmap(data.heightmapResolution, data.heightmapResolution, data.size,
                                                data.GetHeights(0, 0, data.heightmapResolution, data.heightmapResolution));

                    string materialPath = AssetDatabase.GUIDToAssetPath(m_hlod.MaterialGUID);
                    m_terrainMaterial = AssetDatabase.LoadAssetAtPath <Material>(materialPath);
                    if (m_terrainMaterial == null)
                    {
                        m_terrainMaterial = new Material(Shader.Find("Standard"));
                    }

                    m_terrainMaterialInstanceId = m_terrainMaterial.GetInstanceID();
                    m_terrainMaterialName       = m_terrainMaterial.name;

                    using (m_alphamaps = new DisposableList <WorkingTexture>())
                        using (m_layers = new DisposableList <Layer>())
                        {
                            for (int i = 0; i < data.alphamapTextures.Length; ++i)
                            {
                                m_alphamaps.Add(new WorkingTexture(Allocator.Persistent, data.alphamapTextures[i]));
                            }

                            for (int i = 0; i < data.terrainLayers.Length; ++i)
                            {
                                m_layers.Add(new Layer(data.terrainLayers[i]));
                            }


                            QuadTreeSpaceSplitter splitter =
                                new QuadTreeSpaceSplitter(0.0f);

                            SpaceNode rootNode = splitter.CreateSpaceTree(m_hlod.GetBounds(), m_hlod.ChunkSize * 2.0f,
                                                                          m_hlod.transform.position, null, progress => { });

                            EditorUtility.DisplayProgressBar("Bake HLOD", "Create mesh", 0.0f);

                            using (DisposableList <HLODBuildInfo> buildInfos = CreateBuildInfo(data, rootNode))
                            {
                                yield return(m_queue.WaitFinish());

                                //Write material & textures

                                for (int i = 0; i < buildInfos.Count; ++i)
                                {
                                    int curIndex = i;
                                    m_queue.EnqueueJob(() =>
                                    {
                                        ISimplifier simplifier = (ISimplifier)Activator.CreateInstance(m_hlod.SimplifierType,
                                                                                                       new object[] { m_hlod.SimplifierOptions });
                                        simplifier.SimplifyImmidiate(buildInfos[curIndex]);
                                    });
                                }

                                EditorUtility.DisplayProgressBar("Bake HLOD", "Simplify meshes", 0.0f);
                                yield return(m_queue.WaitFinish());

                                Debug.Log("[TerrainHLOD] Simplify: " + sw.Elapsed.ToString("g"));
                                sw.Reset();
                                sw.Start();
                                EditorUtility.DisplayProgressBar("Bake HLOD", "Make border", 0.0f);

                                for (int i = 0; i < buildInfos.Count; ++i)
                                {
                                    HLODBuildInfo info = buildInfos[i];
                                    m_queue.EnqueueJob(() =>
                                    {
                                        for (int oi = 0; oi < info.WorkingObjects.Count; ++oi)
                                        {
                                            WorkingObject o       = info.WorkingObjects[oi];
                                            int borderVertexCount = m_hlod.BorderVertexCount * Mathf.RoundToInt(Mathf.Pow(2.0f, (float)info.Distances[oi]));
                                            using (WorkingMesh m = MakeBorder(o.Mesh, info.Heightmap, borderVertexCount))
                                            {
                                                ReampUV(m, info.Heightmap);
                                                o.SetMesh(MakeFillHoleMesh(m));
                                            }
                                        }
                                    });
                                }
                                yield return(m_queue.WaitFinish());

                                Debug.Log("[TerrainHLOD] Make Border: " + sw.Elapsed.ToString("g"));
                                sw.Reset();
                                sw.Start();


                                for (int i = 0; i < buildInfos.Count; ++i)
                                {
                                    SpaceNode     node = buildInfos[i].Target;
                                    HLODBuildInfo info = buildInfos[i];
                                    if (node.HasChild() == false)
                                    {
                                        SpaceNode parent = node.ParentNode;
                                        node.ParentNode = null;

                                        GameObject go = new GameObject(buildInfos[i].Name);

                                        for (int wi = 0; wi < info.WorkingObjects.Count; ++wi)
                                        {
                                            WorkingObject wo       = info.WorkingObjects[wi];
                                            GameObject    targetGO = null;
                                            if (wi == 0)
                                            {
                                                targetGO = go;
                                            }
                                            else
                                            {
                                                targetGO = new GameObject(wi.ToString());
                                                targetGO.transform.SetParent(go.transform, false);
                                            }

                                            List <Material> materials = new List <Material>();
                                            for (int mi = 0; mi < wo.Materials.Count; ++mi)
                                            {
                                                WorkingMaterial wm = wo.Materials[mi];
                                                if (wm.NeedWrite() == false)
                                                {
                                                    materials.Add(wm.ToMaterial());
                                                    continue;
                                                }

                                                Material mat          = new Material(wm.ToMaterial());
                                                string[] textureNames = wm.GetTextureNames();
                                                for (int ti = 0; ti < textureNames.Length; ++ti)
                                                {
                                                    WorkingTexture wt  = wm.GetTexture(textureNames[ti]);
                                                    Texture2D      tex = wt.ToTexture();
                                                    tex.wrapMode = wt.WrapMode;
                                                    mat.name     = targetGO.name + "_Mat";
                                                    mat.SetTexture(textureNames[ti], tex);
                                                }
                                                mat.EnableKeyword("_NORMALMAP");
                                                materials.Add(mat);
                                            }

                                            targetGO.AddComponent <MeshFilter>().sharedMesh        = wo.Mesh.ToMesh();
                                            targetGO.AddComponent <MeshRenderer>().sharedMaterials = materials.ToArray();
                                        }

                                        go.transform.SetParent(m_hlod.transform, false);
                                        m_hlod.AddGeneratedResource(go);

                                        parent.Objects.Add(go);
                                        buildInfos.RemoveAt(i);
                                        i -= 1;
                                    }
                                }

                                //controller
                                IStreamingBuilder builder =
                                    (IStreamingBuilder)Activator.CreateInstance(m_hlod.StreamingType,
                                                                                new object[] { m_hlod, m_hlod.StreamingOptions });

                                builder.Build(rootNode, buildInfos, m_hlod.gameObject, m_hlod.CullDistance, m_hlod.LODDistance, true, false,
                                              progress =>
                                {
                                    EditorUtility.DisplayProgressBar("Bake HLOD", "Storing results.",
                                                                     0.75f + progress * 0.25f);
                                });

                                Debug.Log("[TerrainHLOD] Build: " + sw.Elapsed.ToString("g"));
                            }
                        }

                    EditorUtility.SetDirty(m_hlod.gameObject);
                }
            }
            finally
            {
                EditorUtility.ClearProgressBar();
                GC.Collect();
            }
        }