//when drawing, the instances will call this method public void AddInstance(DrawState state) { //store the instance matrix state.GetWorldMatrix(out instanceMatrices[instanceCount]); instanceCount++; }
/// <summary> /// <para>Call this method before drawing/culling an item. Match this method call with a call to TryGetPosition after the cull/draw is complete</para> /// <para>The <see cref="ResetPreCullItem"/> method may also be used for subsequent items that are culled, provided the world matrix hasn't changed.</para> /// </summary> /// <param name="state"></param> public void BeginPreCullItem(DrawState state) { this.count = 0; this.countI = 0; this.position = new Vector3(); state.GetWorldMatrix(out this.matrix, out this.isIdentity); }
/// <summary> /// <para>Call this method before drawing/culling an item. Match this method call with a call to TryGetBounds after the cull/draw is complete</para> /// <para>The <see cref="ResetPreCullItem"/> method may also be used for subsequent items that are culled, provided the world matrix hasn't changed.</para> /// </summary> /// <param name="state"></param> public void BeginPreCullItem(DrawState state) { this.reset = true; state.GetWorldMatrix(out matrix, out isIdentity); scale = 1; minBound = new Vector3(); maxBound = new Vector3(); if (!isIdentity) { Matrix.Invert(ref matrix, out matrix); scale = (float)Math.Sqrt( Math.Max(Math.Max( (matrix.M11 * matrix.M11 + matrix.M21 * matrix.M21 + matrix.M31 * matrix.M31), (matrix.M12 * matrix.M12 + matrix.M22 * matrix.M22 + matrix.M32 * matrix.M32)), (matrix.M13 * matrix.M13 + matrix.M23 * matrix.M23 + matrix.M33 * matrix.M33))); } }
/// <summary></summary> /// <param name="state"></param> protected override void DrawItems(DrawState state) { //draw all items in the tree minBuffer[0] = boundsMin.X; minBuffer[1] = boundsMin.Y; minBuffer[2] = boundsMin.Z; maxBuffer[0] = boundsMax.X; maxBuffer[1] = boundsMax.Y; maxBuffer[2] = boundsMax.Z; if (this.count > 64 && state.Application.ThreadPool.ThreadCount > 0) { //if there are lots of items and free threads, then traverse the tree on multiple threads DrawItemsThread(state); return; } Matrix world; state.GetWorldMatrix(out world); allNodes[0].Draw(state, minBuffer, maxBuffer, 0, allChildren, allNodes, world == Matrix.Identity, this.IsOptimizedState); }
//the child isn't drawn right now, but for every bit of geometry that is visible, the world matrix is stored internal void DrawChild(DrawState state) { if (modelData == null) { throw new InvalidOperationException("ModelData is null"); } if (geometry == null) { SetupGeometry(); } ContainmentType cullModel = ContainmentType.Contains; //if there is just one geometry object, then the ICullable.CullTest() call will have been suficient. bool skipCullTest = this.modelData != null && this.modelData.meshes.Length == 1 && this.modelData.meshes[0].geometry.Length == 1; if (!skipCullTest) { cullModel = state.Culler.IntersectBox(ref modelData.staticBounds.minimum, ref modelData.staticBounds.maximum); } int geometryIndex = 0; bool drawn = false; //loop through the model data if (cullModel != ContainmentType.Disjoint) { for (int m = 0; m < modelData.meshes.Length; m++) { MeshData mesh = modelData.meshes[m]; ContainmentType cullMesh = cullModel; //cull testing along the way if (cullModel == ContainmentType.Intersects && modelData.meshes.Length > 1) { cullMesh = state.Culler.IntersectBox(ref mesh.staticBounds.minimum, ref mesh.staticBounds.maximum); } if (cullMesh != ContainmentType.Disjoint) { for (int g = 0; g < mesh.geometry.Length; g++) { GeometryData geom = mesh.geometry[g]; bool cullTest = true; if (cullMesh == ContainmentType.Intersects && mesh.geometry.Length > 1) { cullTest = state.Culler.TestBox(ref geom.staticBounds.minimum, ref geom.staticBounds.maximum); } //finally, is the geometry visible? if (cullTest) { //add the world matrix to the geometry set GeometrySet set = this.geometry[geometryIndex]; if (set.count == set.instances.Length) { Array.Resize(ref set.instances, set.instances.Length * 2); } state.GetWorldMatrix(out set.instances[set.count++]); drawn = true; } geometryIndex++; } } else { geometryIndex += mesh.geometry.Length; } } } if (drawn) { drawCount++; } }
/// <summary></summary> /// <param name="state"></param> /// <param name="maskOnly"></param> protected override sealed void BindShader(DrawState state, bool maskOnly) { if (this.vertices == null) { this.vertices = state.UserValues[GetType().FullName + ".vertices"] as IVertices; this.indices = state.UserValues[GetType().FullName + ".indices"] as Indices <ushort>; this.verticesSI = state.UserValues[GetType().FullName + ".verticesSI"] as IVertices; this.indicesSI = state.UserValues[GetType().FullName + ".indicesSI"] as Indices <ushort>; if (this.vertices == null) { //still null, create the global vertices this.vertices = new Vertices <Vector4>( new Vector4(0, 0, 0, 1), new Vector4(1, 0, 0, 1), new Vector4(1, 1, 0, 1), new Vector4(0, 1, 0, 1)); this.indices = new Indices <ushort>(0, 2, 1, 0, 3, 2); //shader instancing.. List <InstanceVertex> verts = new List <InstanceVertex>(); List <ushort> inds = new List <ushort>(); for (int i = 0; i < NonInstancingRenderCount; i++) { verts.Add(new InstanceVertex(new Vector3(0, 0, 0), (float)i)); verts.Add(new InstanceVertex(new Vector3(1, 0, 0), (float)i)); verts.Add(new InstanceVertex(new Vector3(1, 1, 0), (float)i)); verts.Add(new InstanceVertex(new Vector3(0, 1, 0), (float)i)); inds.Add((ushort)(0 + i * 4)); inds.Add((ushort)(2 + i * 4)); inds.Add((ushort)(1 + i * 4)); inds.Add((ushort)(0 + i * 4)); inds.Add((ushort)(3 + i * 4)); inds.Add((ushort)(2 + i * 4)); } this.verticesSI = new Vertices <InstanceVertex>(verts.ToArray()); this.indicesSI = new Indices <ushort>(inds.ToArray()); state.UserValues[GetType().FullName + ".vertices"] = vertices; state.UserValues[GetType().FullName + ".indices"] = indices; state.UserValues[GetType().FullName + ".verticesSI"] = verticesSI; state.UserValues[GetType().FullName + ".indicesSI"] = indicesSI; } } if (state.SupportsHardwareInstancing && instanceCount > HardwareInstancingMinimum) { Graphics2D.InstancingSprite shader = state.GetShader <Graphics2D.InstancingSprite>(); Matrix world; state.GetWorldMatrix(out world); shader.SetSpriteWorldMatrix(ref world); shader.CustomTexture = texture ?? Xen.Ex.Material.WhiteTexture.GetTexture(state); shader.Bind(state); } else { Graphics2D.NonInstancingSprite shader = state.GetShader <Graphics2D.NonInstancingSprite>(); shader.CustomTexture = texture ?? Xen.Ex.Material.WhiteTexture.GetTexture(state); shader.Bind(state); } }
//traverse the tree on multiple threads... private void DrawItemsThread(DrawState state) { minBuffer[0] = boundsMin.X; minBuffer[1] = boundsMin.Y; minBuffer[2] = boundsMin.Z; maxBuffer[0] = boundsMax.X; maxBuffer[1] = boundsMax.Y; maxBuffer[2] = boundsMax.Z; Threading.ThreadPool pool = state.Application.ThreadPool; if (this.threads == null) { //create the threads. //must be a power-of-two number of thread tasks threadLevel = 0; int threadCount = 2; while (pool.ThreadCount >= threadCount) { threadCount *= 2; threadLevel++; } this.threads = new ThreadDrawer[threadCount]; for (int i = 0; i < threadCount; i++) { this.threads[i] = new ThreadDrawer(); this.threads[i].axis = threadLevel % 3; } } Matrix world; state.GetWorldMatrix(out world); bool isIdentity = world == Matrix.Identity; for (int i = 0; i < this.threads.Length; i++) { this.threads[i].children = this.allChildren; this.threads[i].nodes = this.allNodes; this.threads[i].idenityMatrix = isIdentity; this.threads[i].cullTestInstanceCount = 0; this.threads[i].instanceCount = 0; this.threads[i].treeIsOptimized = this.IsOptimizedState; } int index = 0; //traverse the tree, when getting to 'threadLevel' child depth, process on a thread DrawItemsThread(state, 0, 0, ref index, minBuffer, maxBuffer); //now wait for everything to finish... for (int i = 0; i < this.threads.Length; i++) { threads[i].callback.WaitForCompletion(); } //iterate through the items to draw for (int t = 0; t < this.threads.Length; t++) { //some do not require cull tests if (this.IsOptimizedState) { for (int i = 0; i < threads[t].instanceCount; i++) { ThreadDrawnInstance inst = threads[t].instances[i]; uint child = inst.firstChild; child <<= ChildCountShift; for (ushort c = 0; c < inst.childCount; c++) { IDraw item = allChildren[child++]; if (item != null) { item.Draw(state); } } } } else { for (int i = 0; i < threads[t].instanceCount; i++) { ThreadDrawnInstance inst = threads[t].instances[i]; uint child = inst.firstChild; child <<= ChildCountShift; for (ushort c = 0; c < inst.childCount; c++) { allChildren[child++].Draw(state); } } } //some do. for (int i = 0; i < threads[t].cullTestInstanceCount; i++) { ThreadDrawnInstance inst = threads[t].cullTestInstances[i]; uint child = inst.firstChild; child <<= ChildCountShift; for (ushort c = 0; c < inst.childCount; c++) { if (allChildren[child].CullTest(state)) { allChildren[child].Draw(state); } child++; } } } }