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); }