void PlaceOccludingObjects( GameObject[] objectsToPlace, Camera camera, PlacementStatics statics, NativeList <PlacedObject> placedObjects) { var textures = statics.BackgroundImages; if (m_OccludingObjectCache == null) { m_OccludingObjectCache = new GameObjectOneWayCache(m_ParentOccluding.transform, objectsToPlace); } m_OccludingObjectCache.ResetAllObjects(); var occludingObjectBounds = ComputeObjectBounds(objectsToPlace); var materialPropertyBlock = new MaterialPropertyBlock(); var placedOccludingObjects = new NativeArray <PlacedObject>(placedObjects.Length, Allocator.TempJob); var placementRegion = ObjectPlacementUtilities.ComputePlacementRegion(camera, k_OccludingLayerDistance); using (s_ComputePlacements.Auto()) { var job = new ComputeOccludingObjectPlacements() { OccludingObjectBounds = occludingObjectBounds, ImageCoordinates = placementRegion, Transformer = new WorldToScreenTransformer(camera), PlacedForegroundObjects = placedObjects, RandomSeed = m_Rand.NextUInt(), PlacedOccludingObjects = placedOccludingObjects, ScalingMin = statics.ScalingMin, ScalingSize = statics.ScalingSize }; job.Schedule(placedObjects.Length, 10).Complete(); } using (s_SetupObjects.Auto()) { foreach (var placedOccludingObject in placedOccludingObjects) { if (placedOccludingObject.PrefabIndex < 0) { continue; } var prefab = objectsToPlace[placedOccludingObject.PrefabIndex]; var objectToPlace = m_OccludingObjectCache.GetOrInstantiate(prefab); objectToPlace.layer = m_OccludingLayer; var meshRenderer = objectToPlace.GetComponentInChildren <MeshRenderer>(); meshRenderer.GetPropertyBlock(materialPropertyBlock); ObjectPlacementUtilities.CreateRandomizedHue(materialPropertyBlock, statics.OccludingHueMaxOffset, ref m_Rand); materialPropertyBlock.SetTexture("_BaseMap", textures[m_Rand.NextInt(textures.Length)]); meshRenderer.SetPropertyBlock(materialPropertyBlock); objectToPlace.transform.localPosition = placedOccludingObject.Position; objectToPlace.transform.localRotation = placedOccludingObject.Rotation; objectToPlace.transform.localScale = Vector3.one * placedOccludingObject.Scale; } } placedOccludingObjects.Dispose(); occludingObjectBounds.Dispose(); }
protected override JobHandle OnUpdate(JobHandle inputDeps) { if (!initialized) { Initialize(); } if (!initialized) { return(inputDeps); } if (m_CurriculumQuery.CalculateEntityCount() != 1) { return(inputDeps); } using (s_ResetBackgroundObjects.Auto()) { objectCache.ResetAllObjects(); } var entity = m_CurriculumQuery.GetSingletonEntity(); var curriculumState = EntityManager.GetComponentData <CurriculumState>(entity); var statics = EntityManager.GetComponentObject <PlacementStatics>(entity); if (curriculumState.ScaleIndex >= statics.ScaleFactors.Length) { return(inputDeps); } var meshInfos = new NativeArray <MeshInfo>(statics.BackgroundPrefabs.Length, Allocator.TempJob); for (int i = 0; i < statics.BackgroundPrefabs.Length; i++) { // ReSharper disable once UnusedVariable ObjectPlacementUtilities.GetMeshAndMaterial(statics.BackgroundPrefabs[i], out var material, out var meshToDraw); meshInfos[i] = new MeshInfo() { Bounds = meshToDraw.bounds }; } var foregroundObject = statics.ForegroundPrefabs[curriculumState.PrefabIndex]; var foregroundBounds = ObjectPlacementUtilities.ComputeBounds(foregroundObject); var foregroundRotation = ObjectPlacementUtilities.ComposeForegroundRotation(curriculumState, statics.OutOfPlaneRotations, statics.InPlaneRotations); var foregroundScale = ObjectPlacementUtilities.ComputeForegroundScaling( foregroundBounds, statics.ScaleFactors[curriculumState.ScaleIndex]); var transformer = new WorldToScreenTransformer(camera); // NOTE: For perspective projection, size will depend on position within the viewport, but we're approximating // by computing size for the center var foregroundSizePixels = ObjectPlacementUtilities.ComputeProjectedArea( transformer, m_ForegroundCenter, foregroundRotation, foregroundBounds, foregroundScale); var placementRegion = ObjectPlacementUtilities.ComputePlacementRegion(camera, k_PlacementDistance); var areaPlacementRegion = placementRegion.width * placementRegion.height; var cameraSquarePixels = camera.pixelHeight * camera.pixelWidth; var foregroundSizeUnits = foregroundSizePixels * areaPlacementRegion / cameraSquarePixels; // Lazy approximation of how many subdivisions we need to achieve the target density var numCellsSqrt = math.sqrt(m_objectDensity * areaPlacementRegion / foregroundSizeUnits); var numCellsHorizontal = (int)math.round(numCellsSqrt * m_aspectRatio); var numCellsVertical = (int)math.round(numCellsSqrt / m_aspectRatio); var verticalStep = placementRegion.height / numCellsVertical; var horizontalStep = placementRegion.width / numCellsHorizontal; var scale0 = m_Rand.NextFloat(0.9f, 1.5f); var scale1 = m_Rand.NextFloat(0.9f, 1.5f); var scaleMin = math.min(scale0, scale1); var scaleMax = math.max(scale0, scale1); var cameraTf = camera.transform; var placementOrigin = new Vector3(placementRegion.x, placementRegion.y, k_PlacementDistance + cameraTf.position.z); DatasetCapture.ReportMetric(m_ScaleRangeMetric, $@"[ {{ ""scaleMin"": {scaleMin}, ""scaleMax"": {scaleMax} }}]"); var meshesToDraw = new NativeArray <MeshDrawInfo>(numCellsHorizontal * numCellsVertical * numFillPasses, Allocator.TempJob); using (s_PlaceBackgroundObjects.Auto()) { // XXX: Rather than placing a large collection and then looking for gaps, we simply assume that a sufficiently // dense background will not have gaps - rendering way more objects than necessary is still substantially // faster than trying to read the render texture multiple times per frame new PlaceBackgroundObjectsJob() { PlacementOrigin = placementOrigin, Transformer = new WorldToScreenTransformer(camera), NumCellsHorizontal = numCellsHorizontal, NumCellsVertical = numCellsVertical, HorizontalStep = horizontalStep, VerticalStep = verticalStep, ForegroundSize = foregroundSizePixels, MeshInfos = meshInfos, MeshDrawInfos = meshesToDraw, TextureCount = statics.BackgroundImages.Length, Seed = m_Rand.NextUInt(), MinScale = scaleMin, MaxScale = scaleMax }.Schedule(numFillPasses, 1, inputDeps).Complete(); } using (s_DrawMeshes.Auto()) { var properties = new MaterialPropertyBlock(); foreach (var meshDrawInfo in meshesToDraw) { var prefab = statics.BackgroundPrefabs[meshDrawInfo.MeshIndex]; var sceneObject = objectCache.GetOrInstantiate(prefab); ObjectPlacementUtilities.CreateRandomizedHue(properties, backgroundHueMaxOffset, ref m_Rand); // ReSharper disable once Unity.PreferAddressByIdToGraphicsParams properties.SetTexture("_BaseMap", statics.BackgroundImages[meshDrawInfo.TextureIndex]); sceneObject.GetComponentInChildren <MeshRenderer>().SetPropertyBlock(properties); sceneObject.transform.SetPositionAndRotation(meshDrawInfo.Position, meshDrawInfo.Rotation); sceneObject.transform.localScale = meshDrawInfo.Scale; } } // We have finished drawing the meshes in the camera view, but the engine itself will call Render() // at the end of the frame meshInfos.Dispose(); meshesToDraw.Dispose(); var numObjectsExpected = numCellsHorizontal * numCellsVertical * numFillPasses; if (numObjectsExpected != objectCache.NumObjectsActive) { Debug.LogWarning($"BackgroundGenerator should have placed {numObjectsExpected} but is only using " + $"{objectCache.NumObjectsActive} from the cache."); } return(inputDeps); }
NativeList <PlacedObject> PlaceObjects(Camera camera, PlacementStatics statics, ref CurriculumState curriculumState) { var placedObjectBoundingBoxes = new NativeList <PlacedObject>(500, Allocator.TempJob); var objectBounds = ComputeObjectBounds(statics.ForegroundPrefabs); var localCurriculumState = curriculumState; var curriculumStatePtr = (CurriculumState *)UnsafeUtility.AddressOf(ref localCurriculumState); var localRandom = m_Rand; var randomPtr = (Random *)UnsafeUtility.AddressOf(ref localRandom); var placementRegion = ObjectPlacementUtilities.ComputePlacementRegion(camera, k_ForegroundLayerDistance); using (s_ComputePlacements.Auto()) { var computePlacementsJob = new ComputePlacementsJob() { CurriculumStatePtr = curriculumStatePtr, Transformer = new WorldToScreenTransformer(camera), ImageCoordinates = placementRegion, ObjectBounds = objectBounds, PlaceObjects = placedObjectBoundingBoxes, RandomPtr = randomPtr, NativePlacementStatics = new NativePlacementStatics { ForegroundPrefabCount = statics.ForegroundPrefabs.Length, MaxForegroundObjects = statics.MaxForegroundObjectsPerFrame, InPlaneRotations = statics.InPlaneRotations, OutOfPlaneRotations = statics.OutOfPlaneRotations, ScaleFactors = statics.ScaleFactors } }; computePlacementsJob.Run(); curriculumState = *computePlacementsJob.CurriculumStatePtr; m_Rand = *computePlacementsJob.RandomPtr; } using (s_SetupObjects.Auto()) { int objectGroupIndex = 0; foreach (var placedObject in placedObjectBoundingBoxes) { EnsureObjectGroupsExist(statics, objectGroupIndex); var gameObject = m_ParentForeground.transform.GetChild(placedObject.PrefabIndex + objectGroupIndex * statics.ForegroundPrefabs.Length).gameObject; gameObject.transform.localRotation = placedObject.Rotation; gameObject.transform.localScale = Vector3.one * placedObject.Scale; gameObject.transform.localPosition = placedObject.Position; ObjectPlacementUtilities.SetMeshRenderersEnabledRecursive(gameObject, true); if (placedObject.PrefabIndex == statics.ForegroundPrefabs.Length - 1) { objectGroupIndex++; } } } objectBounds.Dispose(); return(placedObjectBoundingBoxes); }
NativeList <PlacedObject> PlaceObjects(Camera camera, PlacementStatics statics, NativeArray <Bounds> occludingObjectBounds, ref CurriculumState curriculumState) { var placedObjectBoundingBoxes = new NativeList <PlacedObject>(500, Allocator.TempJob); var objectBounds = ComputeObjectBounds(statics.ForegroundPrefabs); var localCurriculumState = curriculumState; var curriculumStatePtr = (CurriculumState *)UnsafeUtility.AddressOf(ref localCurriculumState); var localRandom = m_Rand; var randomPtr = (Random *)UnsafeUtility.AddressOf(ref localRandom); var placementRegion = ObjectPlacementUtilities.ComputePlacementRegion(camera, k_ForegroundLayerDistance); using (s_ComputePlacements.Auto()) { var computePlacementsJob = new ComputePlacementsJob() { CurriculumStatePtr = curriculumStatePtr, Transformer = new WorldToScreenTransformer(camera), ImageCoordinates = placementRegion, ObjectBounds = objectBounds, OccludingObjectBounds = occludingObjectBounds, PlaceObjects = placedObjectBoundingBoxes, RandomPtr = randomPtr, NativePlacementStatics = new NativePlacementStatics { ForegroundPrefabCount = statics.ForegroundPrefabs.Length, MaxForegroundObjects = statics.MaxForegroundObjectsPerFrame, InPlaneRotations = statics.InPlaneRotations, OutOfPlaneRotations = statics.OutOfPlaneRotations, ScaleFactors = statics.ScaleFactors, BackgroundObjectInForegroundChance = statics.BackgroundObjectInForegroundChance, } }; computePlacementsJob.Run(); curriculumState = *computePlacementsJob.CurriculumStatePtr; m_Rand = *computePlacementsJob.RandomPtr; } using (s_SetupObjects.Auto()) { var materialPropertyBlock = new MaterialPropertyBlock(); int objectGroupIndex = 0; foreach (var placedObject in placedObjectBoundingBoxes) { GameObject gameObject; if (placedObject.IsOccluding) { gameObject = m_BackgroundInForegroundObjectCache.GetOrInstantiate(statics.BackgroundPrefabs[placedObject.PrefabIndex]); var meshRenderer = gameObject.GetComponentInChildren <MeshRenderer>(); meshRenderer.GetPropertyBlock(materialPropertyBlock); ObjectPlacementUtilities.CreateRandomizedHue(materialPropertyBlock, statics.OccludingHueMaxOffset, ref m_Rand); materialPropertyBlock.SetTexture("_BaseMap", statics.BackgroundImages[m_Rand.NextInt(statics.BackgroundImages.Length)]); meshRenderer.SetPropertyBlock(materialPropertyBlock); } else { EnsureObjectGroupsExist(statics, objectGroupIndex); gameObject = m_ParentForeground.transform.GetChild(placedObject.PrefabIndex + objectGroupIndex * statics.ForegroundPrefabs.Length).gameObject; } gameObject.transform.localRotation = placedObject.Rotation; gameObject.transform.localScale = Vector3.one * placedObject.Scale; gameObject.transform.localPosition = placedObject.Position; ObjectPlacementUtilities.SetMeshRenderersEnabledRecursive(gameObject, true); if (placedObject.PrefabIndex == statics.ForegroundPrefabs.Length - 1) { objectGroupIndex++; } } } objectBounds.Dispose(); return(placedObjectBoundingBoxes); }