/// <summary>
        /// This method is here because we can only send a limited number of particles at a time, and the current API
        /// insists that the source of the variables are at the beginning of an array. We must therefore copy a slice
        /// of the array into a buffer and use the buffer instead.
        /// The EffectParameter.SetArrayRange sets array slice written to, but there seems to be no such function for
        /// setting the array slice read from.
        /// </summary>
        /// <param name="cloud">The particle cloud to get particle data from.</param>
        /// <param name="renderN">The current render pass.</param>
        private void PrepareParticleBuffers(ParticleCloud cloud, int renderN)
        {
            int numParticlesToRender = cloud.NumberOfParticles - MaxParticlesPerRender * renderN;

            if (numParticlesToRender > MaxParticlesPerRender)
            {
                numParticlesToRender = MaxParticlesPerRender;
            }

            for (int i = 0; i < numParticlesToRender; i++)
            {
                int cloudIndex = renderN * MaxParticlesPerRender + i;
                positionBuffer[i]    = cloud.Positions[cloudIndex];
                colorBuffer[i]       = cloud.Colors[cloudIndex];
                orientationBuffer[i] = cloud.Orientations[cloudIndex];
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Generates a new tree using the loaded tree profile. This method is <i>not</i> thread-safe.
        /// </summary>
        /// <param name="seed">Random seed to generate the tree. Using the same seed twice will generate the same tree twice.</param>
        /// <param name="radialSegments">Number of radials segments at the tree's trunk. Typical values are between 8 and 12.</param>
        /// <param name="addLeaves">If true, leaf positions will be calculated and added to the tree mesh. Otherwise, leaves are ignored.</param>
        /// <param name="cutoffLevel">Branches lower than this level are abruptly removed from the mesh, but otherwise the tree is the same. Typically you want this to be 0, but for LOD you can increase it.</param>
        /// <returns>A TreeModel object containing the trunk's mesh, the leaves, and the bounding box. Null is returned if no tree profile is loaded.</returns>
        public void GenerateTreeMesh(ref XMain X, int seed, int radialSegments, bool addLeaves, int cutoffLevel, out Mesh trunk, out BoundingBox boundingbox, out ParticleCloud leaves)
        {
            //if (branches.Count == 0)
            //    return;

            cloud = new ParticleCloud(cloudSystem);
            treeVertices.Clear();
            treeIndices.Clear();
            boundingBox.Min = boundingBox.Max = Vector3.Zero;

            this.seed           = seed;
            leafSeed            = seed;
            this.radialSegments = radialSegments;
            this.cutoffLevel    = cutoffLevel;
            this.addLeaves      = addLeaves;

            int branchId = GetBranchIndexFromId(rootIdRef);

            if (branchId != -1)
            {
                AppendBranch(Matrix.Identity, branchId, 1f, 1f, levels);
            }


            //TODO: I added in some processing to the scales of the trees after the fact.
            //its dirty and adds time, could be integrated into the function where the vertices are created but
            //I didn't want to read through the code to find out where that is!
            BoundingBox bound = new BoundingBox();

            //scale
            for (int i = 0; i < treeVertices.Count; i++)
            {
                VertexPositionNormalTexture vert = treeVertices[i];
                vert.Position   = Vector3.Multiply(vert.Position, 0.037f);
                treeVertices[i] = vert;
                MeshUtil.AddPointToBoundingBox(vert.Position, ref bound);
            }


            for (int i = 0; i < cloud.Positions.Length; i++)
            {
                Vector3 pos = cloud.Positions[i];
                pos = Vector3.Multiply(pos, 0.037f);
                cloud.Positions[i] = pos;
            }

            for (int i = 0; i < cloud.Orientations.Length; i++)
            {
                Vector4 orient = cloud.Orientations[i];
                orient = Vector4.Multiply(orient, 0.037f);
                cloud.Orientations[i] = orient;
            }


            // Convert the result to a tree mesh
            //XTreeModel mesh = new XTreeModel(ref X);
            //mesh.Trunk = new Mesh(graphicsDevice, treeVertices.ToArray(), treeIndices.ToArray());
            //mesh.Trunk.Effect = trunkEffect;
            //mesh.boundingBox = bound;//boundingBox;
            //mesh.Leaves = cloud;
            //mesh.Leaves.Texture = leafTexture;
            //cloud = null;

            //set out parameters
            trunk          = new Mesh(graphicsDevice, treeVertices.ToArray(), treeIndices.ToArray());
            trunk.Effect   = trunkEffect;
            boundingbox    = bound;
            leaves         = cloud;
            leaves.Texture = leafTexture;


            //return mesh;
        }
        /// <summary>
        /// Draws all the particles in a cloud. The cloud system must be initialized, and its projection matrix must be set
        /// before this is called.
        /// The texture used to draw the particles is specified by cloud.Texture.
        /// </summary>
        /// <param name="cloud">The particle cloud to render.</param>
        /// <param name="world">The world matrix, indicating the position, rotation, and scale of the particle cloud.</param>
        /// <param name="view">The view matrix, indicating the camera's current point of view.</param>
        public void DrawParticleCloud(ParticleCloud cloud, Matrix world, Matrix view)
        {
            // Verify that we have been properly initialized.
            Debug.Assert(isInitialized, "ParticleCloudSystem has not been initialized. "
                         + "You must call Initialize() before it can draw anything.");
            Debug.Assert(isProjectionMatrixSet, "ParticleCloudSystem has not been given a projection matrix. "
                         + "You must write to Projection before it can draw anything.");

            // Remember if ZWrite was enabled.
            bool wasZWriteEnabled = device.RenderState.DepthBufferWriteEnable;

            // Get the number of particles to draw
            int numParticles = cloud.NumberOfParticles;

            // If there is nothing to draw, just skip right now.
            if (numParticles == 0)
            {
                return;
            }

            // How many render calls must be use to draw these particles?
            int numRenders = 1 + numParticles / (MaxParticlesPerRender + 1);

            // Set the correct effect technique
            if (cloud.DepthMapRendering)
            {
                particleEffect.CurrentTechnique = depthTechnique;
            }
            else
            {
                particleEffect.CurrentTechnique = cloud.SortingEnabled ? sortedTechnique : unsortedTechnique;
            }

            // Set vertex and index stuff
            device.VertexDeclaration = vertexDeclaration;
            device.Vertices[0].SetSource(vertexBuffer, 0, ParticleCloudVertex.SizeInBytes);
            device.Indices = indexBuffer;

            // Assign shader uniforms
            if (obsoleteProjection)
            {
                projectionParam.SetValue(projection);
                obsoleteProjection = false;
            }
            Matrix worldView = world * view;

            worldParam.SetValue(world);
            worldViewParam.SetValue(worldView);
            textureParam.SetValue(cloud.Texture);

            Vector3 right = Vector3.Right;
            Vector3 up    = Vector3.Up;

            // Are the particle billboards axis-aligned?
            if (cloud.AxisEnabled)
            {
                // Get the forward vector from the view matrix. Remember that the view matrix's rotation is
                // inverted, so we cannot use view.Forward.
                Vector3 forward = new Vector3(view.M13, view.M23, view.M33);

                right = Vector3.Cross(forward, cloud.Axis);
                right.Normalize();
                up = cloud.Axis;

                Vector3.TransformNormal(ref right, ref view, out right);
                Vector3.TransformNormal(ref up, ref view, out up);
            }

            // Scale the right and up vectors according to the X-scale of the WorldView matrix
            // Billboards cannot be scaled non-uniformly, so we have to assume that it is a uniform scale
            float sizeScale = worldView.Right.Length();

            right = right * sizeScale;
            up    = up * sizeScale;

            // Sends the vectors to the shader
            billboardRightParam.SetValue(right);
            billboardUpParam.SetValue(up);


            // Start rendering the particles
            particleEffect.Begin(SaveStateMode.SaveState);
            for (int i = 0; i < particleEffect.CurrentTechnique.Passes.Count; i++)
            {
                EffectPass pass = particleEffect.CurrentTechnique.Passes[i];

                for (int renderN = 0; renderN < numRenders; renderN++)
                {
                    // Calculate the number of particles to render in this render call
                    int numParticlesToRender = numParticles - renderN * MaxParticlesPerRender;
                    if (numParticlesToRender > MaxParticlesPerRender)
                    {
                        numParticlesToRender = MaxParticlesPerRender;
                    }

                    // Get the array start and end indices
                    int arrayStart = renderN * MaxParticlesPerRender;
                    int arrayEnd   = arrayStart + numParticlesToRender - 1;

                    // Assign the parameters accordingly
                    if (renderN == 0)
                    {
                        // The first render pass can use the arrays directly. No need to waste time setting up the buffers.
                        positionsParam.SetValue(cloud.Positions);
                        colorsParam.SetValue(cloud.Colors);
                        orientationsParam.SetValue(cloud.Orientations);
                    }
                    else
                    {
                        // We need to construct a new array where the next 80 particles are first in the array.
                        PrepareParticleBuffers(cloud, renderN);
                        positionsParam.SetValue(positionBuffer);
                        colorsParam.SetValue(colorBuffer);
                        orientationsParam.SetValue(orientationBuffer);
                    }

                    pass.Begin();
                    device.DrawIndexedPrimitives(
                        PrimitiveType.TriangleList,
                        0,
                        0,
                        4 * numParticlesToRender,
                        0,
                        2 * numParticlesToRender);
                    pass.End();
                }
            }
            particleEffect.End();

            // It seems that ZWriteEnable propagates to other drawing calls in XNA, so we better clean up
            // after ourselves.
            if (cloud.SortingEnabled && wasZWriteEnabled)
            {
                device.RenderState.DepthBufferWriteEnable = true;
            }
        }