Beispiel #1
0
        public void Curl(PointF curlPos, PointF curlDir, double radius)
        {
            // First add some 'helper' lines used for development.
            if (DRAW_CURL_POSITION)
            {
                mBufCurlPositionLines.Position(0);

                mBufCurlPositionLines.Put(curlPos.X);
                mBufCurlPositionLines.Put(curlPos.Y - 1.0f);
                mBufCurlPositionLines.Put(curlPos.X);
                mBufCurlPositionLines.Put(curlPos.Y + 1.0f);
                mBufCurlPositionLines.Put(curlPos.X - 1.0f);
                mBufCurlPositionLines.Put(curlPos.Y);
                mBufCurlPositionLines.Put(curlPos.X + 1.0f);
                mBufCurlPositionLines.Put(curlPos.Y);

                mBufCurlPositionLines.Put(curlPos.X);
                mBufCurlPositionLines.Put(curlPos.Y);
                mBufCurlPositionLines.Put(curlPos.X + curlDir.X * 2);
                mBufCurlPositionLines.Put(curlPos.Y + curlDir.Y * 2);

                mBufCurlPositionLines.Position(0);
            }

            // Actual 'curl' implementation starts here.
            mBufVertices.Position(0);
            mBufColors.Position(0);
            if (DRAW_TEXTURE)
            {
                mBufTexCoords.Position(0);
            }

            // Calculate curl angle from direction.
            double curlAngle = Math.Acos(curlDir.X);

            curlAngle = curlDir.Y > 0 ? -curlAngle : curlAngle;

            // Initiate rotated rectangle which's is translated to curlPos and
            // rotated so that curl direction heads to right (1,0). Vertices are
            // ordered in ascending order based on x -coordinate at the same time.
            // And using y -coordinate in very rare case in which two vertices have
            // same x -coordinate.
            mArrTempVertices.AddAll(mArrRotatedVertices);
            mArrRotatedVertices.Clear();
            for (int i = 0; i < 4; ++i)
            {
                Vertex v = mArrTempVertices.Remove(0);
                v.Set(mRectangle[i]);
                v.Translate(-curlPos.X, -curlPos.Y);
                v.RotateZ(-curlAngle);
                int j = 0;
                for (; j < mArrRotatedVertices.Size(); ++j)
                {
                    Vertex v2 = mArrRotatedVertices.Get(j);
                    if (v.mPosX > v2.mPosX)
                    {
                        break;
                    }
                    if (v.mPosX == v2.mPosX && v.mPosY > v2.mPosY)
                    {
                        break;
                    }
                }
                mArrRotatedVertices.Add(j, v);
            }

            // Rotated rectangle lines/vertex indices. We need to find bounding
            // lines for rotated rectangle. After sorting vertices according to
            // their x -coordinate we don't have to worry about vertices at indices
            // 0 and 1. But due to inaccuracy it's possible vertex 3 is not the
            // opposing corner from vertex 0. So we are calculating distance from
            // vertex 0 to vertices 2 and 3 - and altering line indices if needed.
            // Also vertices/lines are given in an order first one has x -coordinate
            // at least the latter one. This property is used in getIntersections to
            // see if there is an intersection.
            int[][] lines = new int[][] { new int[] { 0, 1 }, new int[] { 0, 2 }, new int[] { 1, 3 }, new int[] { 2, 3 } };
            {
                // TODO: There really has to be more 'easier' way of doing this -
                // not including extensive use of sqrt.
                Vertex v0    = mArrRotatedVertices.Get(0);
                Vertex v2    = mArrRotatedVertices.Get(2);
                Vertex v3    = mArrRotatedVertices.Get(3);
                double dist2 = Math.Sqrt((v0.mPosX - v2.mPosX)
                                         * (v0.mPosX - v2.mPosX) + (v0.mPosY - v2.mPosY)
                                         * (v0.mPosY - v2.mPosY));
                double dist3 = Math.Sqrt((v0.mPosX - v3.mPosX)
                                         * (v0.mPosX - v3.mPosX) + (v0.mPosY - v3.mPosY)
                                         * (v0.mPosY - v3.mPosY));
                if (dist2 > dist3)
                {
                    lines[1][1] = 3;
                    lines[2][1] = 2;
                }
            }

            mVerticesCountFront = mVerticesCountBack = 0;

            if (DRAW_SHADOW)
            {
                mArrTempShadowVertices.AddAll(mArrDropShadowVertices);
                mArrTempShadowVertices.AddAll(mArrSelfShadowVertices);
                mArrDropShadowVertices.Clear();
                mArrSelfShadowVertices.Clear();
            }

            // Length of 'curl' curve.
            double curlLength = Math.PI * radius;

            // Calculate scan lines.
            // TODO: Revisit this code one day. There is room for optimization here.
            mArrScanLines.Clear();
            if (mMaxCurlSplits > 0)
            {
                mArrScanLines.Add((double)0);
            }
            for (int i = 1; i < mMaxCurlSplits; ++i)
            {
                mArrScanLines.Add((-curlLength * i) / (mMaxCurlSplits - 1));
            }
            // As mRotatedVertices is ordered regarding x -coordinate, adding
            // this scan line produces scan area picking up vertices which are
            // rotated completely. One could say 'until infinity'.
            mArrScanLines.Add(mArrRotatedVertices.Get(3).mPosX - 1);

            // Start from right most vertex. Pretty much the same as first scan area
            // is starting from 'infinity'.
            double scanXmax = mArrRotatedVertices.Get(0).mPosX + 1;

            for (int i = 0; i < mArrScanLines.Size(); ++i)
            {
                // Once we have scanXmin and scanXmax we have a scan area to start
                // working with.
                double scanXmin = mArrScanLines.Get(i);
                // First iterate 'original' rectangle vertices within scan area.
                for (int j = 0; j < mArrRotatedVertices.Size(); ++j)
                {
                    Vertex v = mArrRotatedVertices.Get(j);
                    // Test if vertex lies within this scan area.
                    // TODO: Frankly speaking, can't remember why equality check was
                    // added to both ends. Guessing it was somehow related to case
                    // where radius=0f, which, given current implementation, could
                    // be handled much more effectively anyway.
                    if (v.mPosX >= scanXmin && v.mPosX <= scanXmax)
                    {
                        // Pop out a vertex from temp vertices.
                        Vertex n = mArrTempVertices.Remove(0);
                        n.Set(v);
                        // This is done solely for triangulation reasons. Given a
                        // rotated rectangle it has max 2 vertices having
                        // intersection.
                        Array <Vertex> intersections2 = GetIntersections(
                            mArrRotatedVertices, lines, n.mPosX);
                        // In a sense one could say we're adding vertices always in
                        // two, positioned at the ends of intersecting line. And for
                        // triangulation to work properly they are added based on y
                        // -coordinate. And this if-else is doing it for us.
                        if (intersections2.Size() == 1 &&
                            intersections2.Get(0).mPosY > v.mPosY)
                        {
                            // In case intersecting vertex is higher add it first.
                            mArrOutputVertices.AddAll(intersections2);
                            mArrOutputVertices.Add(n);
                        }
                        else if (intersections2.Size() <= 1)
                        {
                            // Otherwise add original vertex first.
                            mArrOutputVertices.Add(n);
                            mArrOutputVertices.AddAll(intersections2);
                        }
                        else
                        {
                            // There should never be more than 1 intersecting
                            // vertex. But if it happens as a fallback simply skip
                            // everything.
                            mArrTempVertices.Add(n);
                            mArrTempVertices.AddAll(intersections2);
                        }
                    }
                }

                // Search for scan line intersections.
                Array <Vertex> intersections = GetIntersections(mArrRotatedVertices,
                                                                lines, scanXmin);

                // We expect to get 0 or 2 vertices. In rare cases there's only one
                // but in general given a scan line intersecting rectangle there
                // should be 2 intersecting vertices.
                if (intersections.Size() == 2)
                {
                    // There were two intersections, add them based on y
                    // -coordinate, higher first, lower last.
                    Vertex v1 = intersections.Get(0);
                    Vertex v2 = intersections.Get(1);
                    if (v1.mPosY < v2.mPosY)
                    {
                        mArrOutputVertices.Add(v2);
                        mArrOutputVertices.Add(v1);
                    }
                    else
                    {
                        mArrOutputVertices.AddAll(intersections);
                    }
                }
                else if (intersections.Size() != 0)
                {
                    // This happens in a case in which there is a original vertex
                    // exactly at scan line or something went very much wrong if
                    // there are 3+ vertices. What ever the reason just return the
                    // vertices to temp vertices for later use. In former case it
                    // was handled already earlier once iterating through
                    // mRotatedVertices, in latter case it's better to avoid doing
                    // anything with them.
                    mArrTempVertices.AddAll(intersections);
                }

                // Add vertices found during this iteration to vertex etc buffers.
                while (mArrOutputVertices.Size() > 0)
                {
                    Vertex v = mArrOutputVertices.Remove(0);
                    mArrTempVertices.Add(v);

                    // Local texture front-facing flag.
                    bool textureFront;

                    // Untouched vertices.
                    if (i == 0)
                    {
                        textureFront = true;
                        mVerticesCountFront++;
                    }
                    // 'Completely' rotated vertices.
                    else if (i == mArrScanLines.Size() - 1 || curlLength == 0)
                    {
                        v.mPosX      = -(curlLength + v.mPosX);
                        v.mPosZ      = 2 * radius;
                        v.mPenumbraX = -v.mPenumbraX;

                        textureFront = false;
                        mVerticesCountBack++;
                    }
                    // Vertex lies within 'curl'.
                    else
                    {
                        // Even though it's not obvious from the if-else clause,
                        // here v.mPosX is between [-curlLength, 0]. And we can do
                        // calculations around a half cylinder.
                        double rotY = Math.PI * (v.mPosX / curlLength);
                        v.mPosX       = radius * Math.Sin(rotY);
                        v.mPosZ       = radius - (radius * Math.Cos(rotY));
                        v.mPenumbraX *= Math.Cos(rotY);
                        // Map color multiplier to [.1f, 1f] range.
                        v.mColorFactor = (float)(.1f + .9f * Math.Sqrt(Math
                                                                       .Sin(rotY) + 1));

                        if (v.mPosZ >= radius)
                        {
                            textureFront = false;
                            mVerticesCountBack++;
                        }
                        else
                        {
                            textureFront = true;
                            mVerticesCountFront++;
                        }
                    }

                    // We use local textureFront for flipping backside texture
                    // locally. Plus additionally if mesh is in flip texture mode,
                    // we'll make the procedure "backwards". Also, until this point,
                    // texture coordinates are within [0, 1] range so we'll adjust
                    // them to final texture coordinates too.
                    if (textureFront != mFlipTexture)
                    {
                        v.mTexX *= mTextureRectFront.Right;
                        v.mTexY *= mTextureRectFront.Bottom;
                        v.mColor = mTexturePage.GetColor(CurlPage.SIDE_FRONT);
                    }
                    else
                    {
                        v.mTexX *= mTextureRectBack.Right;
                        v.mTexY *= mTextureRectBack.Bottom;
                        v.mColor = mTexturePage.GetColor(CurlPage.SIDE_BACK);
                    }

                    // Move vertex back to 'world' coordinates.
                    v.RotateZ(curlAngle);
                    v.Translate(curlPos.X, curlPos.Y);
                    AddVertex(v);

                    // Drop shadow is cast 'behind' the curl.
                    if (DRAW_SHADOW && v.mPosZ > 0 && v.mPosZ <= radius)
                    {
                        ShadowVertex sv = mArrTempShadowVertices.Remove(0);
                        sv.mPosX          = v.mPosX;
                        sv.mPosY          = v.mPosY;
                        sv.mPosZ          = v.mPosZ;
                        sv.mPenumbraX     = (v.mPosZ / 2) * -curlDir.X;
                        sv.mPenumbraY     = (v.mPosZ / 2) * -curlDir.Y;
                        sv.mPenumbraColor = v.mPosZ / radius;
                        int idx = (mArrDropShadowVertices.Size() + 1) / 2;
                        mArrDropShadowVertices.Add(idx, sv);
                    }
                    // Self shadow is cast partly over mesh.
                    if (DRAW_SHADOW && v.mPosZ > radius)
                    {
                        ShadowVertex sv = mArrTempShadowVertices.Remove(0);
                        sv.mPosX          = v.mPosX;
                        sv.mPosY          = v.mPosY;
                        sv.mPosZ          = v.mPosZ;
                        sv.mPenumbraX     = ((v.mPosZ - radius) / 3) * v.mPenumbraX;
                        sv.mPenumbraY     = ((v.mPosZ - radius) / 3) * v.mPenumbraY;
                        sv.mPenumbraColor = (v.mPosZ - radius) / (2 * radius);
                        int idx = (mArrSelfShadowVertices.Size() + 1) / 2;
                        mArrSelfShadowVertices.Add(idx, sv);
                    }
                }

                // Switch scanXmin as scanXmax for next iteration.
                scanXmax = scanXmin;
            }

            mBufVertices.Position(0);
            mBufColors.Position(0);
            if (DRAW_TEXTURE)
            {
                mBufTexCoords.Position(0);
            }

            // Add shadow Vertices.
            if (DRAW_SHADOW)
            {
                mBufShadowColors.Position(0);
                mBufShadowVertices.Position(0);
                mDropShadowCount = 0;

                for (int i = 0; i < mArrDropShadowVertices.Size(); ++i)
                {
                    ShadowVertex sv = mArrDropShadowVertices.Get(i);
                    mBufShadowVertices.Put((float)sv.mPosX);
                    mBufShadowVertices.Put((float)sv.mPosY);
                    mBufShadowVertices.Put((float)sv.mPosZ);
                    mBufShadowVertices.Put((float)(sv.mPosX + sv.mPenumbraX));
                    mBufShadowVertices.Put((float)(sv.mPosY + sv.mPenumbraY));
                    mBufShadowVertices.Put((float)sv.mPosZ);
                    for (int j = 0; j < 4; ++j)
                    {
                        double color = SHADOW_OUTER_COLOR[j]
                                       + (SHADOW_INNER_COLOR[j] - SHADOW_OUTER_COLOR[j])
                                       * sv.mPenumbraColor;
                        mBufShadowColors.Put((float)color);
                    }
                    mBufShadowColors.Put(SHADOW_OUTER_COLOR);
                    mDropShadowCount += 2;
                }
                mSelfShadowCount = 0;
                for (int i = 0; i < mArrSelfShadowVertices.Size(); ++i)
                {
                    ShadowVertex sv = mArrSelfShadowVertices.Get(i);
                    mBufShadowVertices.Put((float)sv.mPosX);
                    mBufShadowVertices.Put((float)sv.mPosY);
                    mBufShadowVertices.Put((float)sv.mPosZ);
                    mBufShadowVertices.Put((float)(sv.mPosX + sv.mPenumbraX));
                    mBufShadowVertices.Put((float)(sv.mPosY + sv.mPenumbraY));
                    mBufShadowVertices.Put((float)sv.mPosZ);
                    for (int j = 0; j < 4; ++j)
                    {
                        double color = SHADOW_OUTER_COLOR[j]
                                       + (SHADOW_INNER_COLOR[j] - SHADOW_OUTER_COLOR[j])
                                       * sv.mPenumbraColor;
                        mBufShadowColors.Put((float)color);
                    }
                    mBufShadowColors.Put(SHADOW_OUTER_COLOR);
                    mSelfShadowCount += 2;
                }
                mBufShadowColors.Position(0);
                mBufShadowVertices.Position(0);
            }
        }
Beispiel #2
0
        public void DrawGeometry(Microsoft.Xna.Framework.Graphics.GraphicsDevice device, Effect effect)
        {
            //Effect.CurrentTechnique = Effect.Techniques[0];
            if (SkelBones == null)
            {
                ReloadSkeleton();
            }
            effect.Parameters["SkelBindings"].SetValue(SkelBones);

            lock (Bindings)
            {
                foreach (var pass in effect.CurrentTechnique.Passes)
                {
                    foreach (var binding in Bindings)
                    {
                        if (binding.Texture != null)
                        {
                            var tex = binding.Texture.Get(device);
                            effect.Parameters["MeshTex"].SetValue(tex);
                        }
                        else
                        {
                            effect.Parameters["MeshTex"].SetValue((Texture2D)null);
                        }
                        pass.Apply();
                        binding.Mesh.Draw(device);
                    }
                }
            }

            //skip drawing shadows if we're drawing id
            if (LightPositions == null || effect.CurrentTechnique == effect.Techniques[1])
            {
                return;
            }

            if (ShadBuf == null)
            {
                var shadVerts = new ShadowVertex[]
                {
                    new ShadowVertex(new Vector3(-1, 0, -1), 25),
                    new ShadowVertex(new Vector3(-1, 0, 1), 25),
                    new ShadowVertex(new Vector3(1, 0, 1), 25),
                    new ShadowVertex(new Vector3(1, 0, -1), 25),

                    new ShadowVertex(new Vector3(-1, 0, -1), 19),
                    new ShadowVertex(new Vector3(-1, 0, 1), 19),
                    new ShadowVertex(new Vector3(1, 0, 1), 19),
                    new ShadowVertex(new Vector3(1, 0, -1), 19)
                };
                for (int i = 0; i < shadVerts.Length; i++)
                {
                    shadVerts[i].Position *= 1f;
                }
                int[] shadInd = new int[] { 2, 1, 0, 2, 0, 3, 6, 5, 4, 6, 4, 7 };

                ShadBuf = new VertexBuffer(device, typeof(ShadowVertex), shadVerts.Length, BufferUsage.None);
                ShadBuf.SetData(shadVerts);
                ShadIBuf = new IndexBuffer(device, IndexElementSize.ThirtyTwoBits, shadInd.Length, BufferUsage.None);
                ShadIBuf.SetData(shadInd);
            }

            foreach (var light in LightPositions)
            {
                //effect.Parameters["FloorHeight"].SetValue((float)(Math.Floor(Position.Y/2.95)*2.95 + 0.05));
                effect.Parameters["LightPosition"].SetValue(light);
                var oldTech = effect.CurrentTechnique;
                effect.CurrentTechnique = effect.Techniques[4];
                effect.CurrentTechnique.Passes[0].Apply();
                device.DepthStencilState = DepthStencilState.DepthRead;
                device.SetVertexBuffer(ShadBuf);
                device.Indices = ShadIBuf;
                device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 4);
                effect.CurrentTechnique  = oldTech;
                device.DepthStencilState = DepthStencilState.Default;
            }

            DrawHeadObject(device, effect);
        }