private AABB RotateBounds(ref float4x4 tx, ref AABB b) { WorldBounds wBounds; Culling.AxisAlignedToWorldBounds(ref tx, ref b, out wBounds); // now turn those bounds back to axis aligned.. AABB aab; Culling.WorldBoundsToAxisAligned(ref wBounds, out aab); return(aab); }
private void EncodeBox(RendererBGFXInstance *sys, bgfx.Encoder *encoder, Entity ePass, ref float4x4 tx, float3 cMin, float3 cMax, float width, float4 color) { var pass = EntityManager.GetComponentData <RenderPass>(ePass); float4x4 adjustedProjection = sys->GetAdjustedProjection(ref pass); float2 normWidth = new float2(width / pass.viewport.w, width / pass.viewport.h); for (int j = 0; j < Culling.EdgeTable.Length; j++) { float3 p0 = Culling.SelectCoordsMinMax(cMin, cMax, Culling.EdgeTable[j] & 7); float3 p1 = Culling.SelectCoordsMinMax(cMin, cMax, Culling.EdgeTable[j] >> 3); SubmitHelper.EncodeLine(sys, encoder, pass.viewId, p0, p1, color, normWidth, ref tx, ref pass.viewTransform, ref adjustedProjection); } }
public unsafe void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex) { var chunkLocalToWorld = chunk.GetNativeArray(LocalToWorldType); var chunkMeshRenderer = chunk.GetNativeArray(MeshRendererType); var chunkMeshReference = chunk.GetNativeArray(LitMeshType); var chunkWorldBoundingSphere = chunk.GetNativeArray(WorldBoundingSphereType); var boundingSphere = chunk.GetChunkComponentData(ChunkWorldBoundingSphereType).Value; var bounds = chunk.GetChunkComponentData(ChunkWorldBoundsType).Value; Entity lighte = SharedLightingRef[chunkIndex]; var lighting = ComponentLightingBGFX[lighte]; Entity rtpe = SharedRenderToPass[chunkIndex]; Assert.IsTrue(ThreadIndex >= 0 && ThreadIndex < MaxPerThreadData); bgfx.Encoder *encoder = PerThreadData[ThreadIndex].encoder; if (encoder == null) { encoder = bgfx.encoder_begin(true); Assert.IsTrue(encoder != null); PerThreadData[ThreadIndex].encoder = encoder; } DynamicBuffer <RenderToPassesEntry> toPasses = BufferRenderToPassesEntry[rtpe]; // we can do this loop either way, passes first or renderers first. // TODO: profile what is better! for (int i = 0; i < toPasses.Length; i++) { Entity ePass = toPasses[i].e; Frustum frustum = default; if (ComponentFrustum.Exists(ePass)) { frustum = ComponentFrustum[ePass]; // TODO, just make frustum a member of pass? if (Culling.Cull(ref boundingSphere, ref frustum) == Culling.CullingResult.Outside) { continue; // nothing to do for this pass } } Assert.IsTrue(encoder != null); var pass = ComponentRenderPass[ePass]; for (int j = 0; j < chunk.Count; j++) { var wbs = chunkWorldBoundingSphere[j]; var meshRenderer = chunkMeshRenderer[j]; var meshRef = chunkMeshReference[j]; var mesh = ComponentSimpleMeshBGFX[meshRef.mesh]; var tx = chunkLocalToWorld[j].Value; if (Culling.Cull(ref wbs, ref frustum) == Culling.CullingResult.Outside) // TODO: fine cull only if rough culling was !Inside { continue; } switch (pass.passType) // TODO: we can hoist this out of the loop { case RenderPassType.ZOnly: SubmitHelper.EncodeZOnly(BGFXSystem, encoder, pass.viewId, ref mesh, ref tx, meshRenderer.startIndex, meshRenderer.indexCount, pass.flipCulling); break; case RenderPassType.ShadowMap: float4 bias = new float4(0); SubmitHelper.EncodeShadowMap(BGFXSystem, encoder, pass.viewId, ref mesh, ref tx, meshRenderer.startIndex, meshRenderer.indexCount, (byte)(pass.flipCulling ^ 0x3), bias); break; case RenderPassType.Transparent: case RenderPassType.Opaque: var material = ComponentLitMaterialBGFX[meshRenderer.material]; SubmitHelper.EncodeLit(BGFXSystem, encoder, pass.viewId, ref mesh, ref tx, ref material, ref lighting, ref pass.viewTransform, meshRenderer.startIndex, meshRenderer.indexCount, pass.flipCulling, ref PerThreadData[ThreadIndex].viewSpaceLightCache); break; default: Assert.IsTrue(false); break; } } } }
protected override void OnUpdate() { var sys = World.GetExistingSystem <RendererBGFXSystem>(); if (!sys.Initialized) { return; } // get all MeshRenderer, cull them, and add them to graph nodes that need them // any mesh renderer MUST have a shared component data that has a list of passes to render to // this list is usually very shared - all opaque meshes will render to all ZOnly and Opaque passes // this shared data is not dynamically updated - other systems are responsible to update them if needed // simple Entities.ForEach((Entity e, ref MeshRenderer mr, ref SimpleMeshReference meshRef, ref LocalToWorld tx, ref WorldBounds wb, ref WorldBoundingSphere wbs) => { if (!EntityManager.HasComponent <RenderToPasses>(e)) { return; } RenderToPasses toPassesRef = EntityManager.GetSharedComponentData <RenderToPasses>(e); DynamicBuffer <RenderToPassesEntry> toPasses = EntityManager.GetBufferRO <RenderToPassesEntry>(toPassesRef.e); for (int i = 0; i < toPasses.Length; i++) { Entity ePass = toPasses[i].e; var pass = EntityManager.GetComponentData <RenderPass>(ePass); if (EntityManager.HasComponent <Frustum>(ePass)) { var frustum = EntityManager.GetComponentData <Frustum>(ePass); if (Culling.Cull(ref wbs, ref frustum) == Culling.CullingResult.Outside) { continue; } // double cull as example only if (Culling.IsCulled(ref wb, ref frustum)) { continue; } } var mesh = EntityManager.GetComponentData <SimpleMeshBGFX>(meshRef.mesh); switch (pass.passType) { case RenderPassType.ZOnly: SubmitHelper.SubmitZOnlyDirect(sys, pass.viewId, ref mesh, ref tx.Value, mr.startIndex, mr.indexCount, pass.flipCulling); break; case RenderPassType.ShadowMap: SubmitHelper.SubmitZOnlyDirect(sys, pass.viewId, ref mesh, ref tx.Value, mr.startIndex, mr.indexCount, (byte)(pass.flipCulling ^ 0x3)); break; case RenderPassType.Transparent: case RenderPassType.Opaque: var material = EntityManager.GetComponentData <SimpleMaterialBGFX>(mr.material); SubmitHelper.SubmitSimpleDirect(sys, pass.viewId, ref mesh, ref tx.Value, ref material, mr.startIndex, mr.indexCount, pass.flipCulling); break; default: Assert.IsTrue(false); break; } } }); }
protected override void OnUpdate() { // check if we need bounds bool needBounds = false; Entities.ForEach((Entity e, ref AutoMovingDirectionalLight amdl) => { if (amdl.autoBounds) { needBounds = true; } }); // compute bounds if needed // TODO: separate bounds for shadow casters and receivers? AABB autoBounds = default; if (needBounds) { // TODO: use chunk bounds instead! float3 bbMin = new float3(float.MaxValue); float3 bbMax = new float3(-float.MaxValue); Entities.ForEach((Entity e, ref WorldBounds wb) => { Culling.GrowBounds(ref bbMin, ref bbMax, wb); }); autoBounds = new AABB { Center = (bbMin + bbMax) * .5f, Extents = (bbMax - bbMin) * .5f }; if (math.any(autoBounds.Extents < 0.0f)) { autoBounds.Center = new float3(0); autoBounds.Extents = new float3(0); } } // do assignment Entities.ForEach((Entity e, ref AutoMovingDirectionalLight amdl, ref DirectionalLight dl, ref Light l, ref LocalToWorld tx) => { if (amdl.autoBounds) { amdl.boundsCasters = autoBounds; amdl.boundsReceivers = autoBounds; } // transform bounds into light space rotation float4x4 rotOnlyTx = tx.Value; rotOnlyTx.c3 = new float4(0, 0, 0, 1); float4x4 rotOnlyTxInv = math.inverse(rotOnlyTx); AABB aabCasters = RotateBounds(ref rotOnlyTx, ref amdl.boundsCasters); AABB aabReceivers = RotateBounds(ref rotOnlyTx, ref amdl.boundsReceivers); l.clipZFar = l.clipZNear + aabReceivers.Extents.z + aabCasters.Extents.z; // do not change near clip float3 posls; posls.x = aabCasters.Center.x; posls.y = aabCasters.Center.y; posls.z = aabCasters.Center.z - aabCasters.Extents.z - l.clipZNear; tx.Value.c3.xyz = math.transform(rotOnlyTx, posls); // back to world space dl.size = math.max(aabCasters.Extents.x, aabCasters.Extents.y); }); }