예제 #1
0
        /// <summary>
        /// Intersects the specified client point with the scene, knowing that the given
        /// HitRecords were the result of having called Dispatch with these same client points</summary>
        /// <param name="x">Client x coordinate</param>
        /// <param name="y">Client y coordinate</param>
        /// <param name="hits">The hits</param>
        /// <param name="pt">The intersection point</param>
        /// <param name="surfaceNormal">The surface normal of the target object at the intersection
        /// point, or the zero vector if the surface normal could not be found. Check against
        /// Vec3F.ZeroVector before using.</param>
        /// <returns>True iff client point intersects scene</returns>
        protected bool Intersect(int x, int y, HitRecord[] hits, ref Vec3F pt, out Vec3F surfaceNormal)
        {
            surfaceNormal = new Vec3F();

            if (hits.Length == 0)
            {
                return(false);
            }

            HitRecord hit = hits[0];

            if (hit.HasNormal)
            {
                surfaceNormal = hit.Normal;
            }

            if (hit.HasWorldIntersection)
            {
                pt = hit.WorldIntersection;
                return(true);
            }

            return(false);
        }
예제 #2
0
        /// <summary>
        /// Performs actions during control drag</summary>
        /// <param name="hit">Hit record</param>
        /// <param name="x">Mouse x position</param>
        /// <param name="y">Mouse y position</param>
        /// <param name="action">Render action</param>
        /// <param name="camera">Camera</param>
        /// <param name="transform">Transform</param>
        /// <returns>Translation, in world coordinates</returns>
        public Vec3F OnDrag(HitRecord hit, float x, float y, IRenderAction action, Camera camera, Matrix4F transform)
        {
            float    a1, a2;
            Matrix4F W = new Matrix4F();

            W.Mul(transform, camera.ViewMatrix);

            // Setup rays, in view space. (-z goes into the screen.)
            Ray3F ray0 = camera.CreateRay(m_iX, m_iY);
            Ray3F ray  = camera.CreateRay(x, y);

            // Build axis and origin in view space
            Vec3F xAxis  = W.XAxis;
            Vec3F yAxis  = W.YAxis;
            Vec3F zAxis  = W.ZAxis;
            Vec3F origin = W.Translation;

            Vec3F trans = new Vec3F();

            // Choose the best projection plane according to the projection angle
            switch ((HitElement)hit.RenderObjectData[1])
            {
            case HitElement.X_ARROW:
            {
                a1 = Math.Abs(Vec3F.Dot(ray0.Direction, yAxis));
                a2 = Math.Abs(Vec3F.Dot(ray0.Direction, zAxis));
                Vec3F axis       = (a1 > a2 ? yAxis : zAxis);
                Vec3F p0         = ray0.IntersectPlane(axis, -Vec3F.Dot(axis, origin));
                Vec3F p1         = ray.IntersectPlane(axis, -Vec3F.Dot(axis, origin));
                float dragAmount = Vec3F.Dot(xAxis, p1 - p0);
                Vec3F xLocal     = transform.XAxis;
                trans = dragAmount * xLocal;
            }
            break;

            case HitElement.Y_ARROW:
            {
                a1 = Math.Abs(Vec3F.Dot(ray0.Direction, zAxis));
                a2 = Math.Abs(Vec3F.Dot(ray0.Direction, xAxis));
                Vec3F axis       = (a1 > a2 ? zAxis : xAxis);
                Vec3F p0         = ray0.IntersectPlane(axis, -Vec3F.Dot(axis, origin));
                Vec3F p1         = ray.IntersectPlane(axis, -Vec3F.Dot(axis, origin));
                float dragAmount = Vec3F.Dot(yAxis, p1 - p0);
                Vec3F yLocal     = transform.YAxis;
                trans = dragAmount * yLocal;
            }
            break;

            case HitElement.Z_ARROW:
            {
                a1 = Math.Abs(Vec3F.Dot(ray0.Direction, xAxis));
                a2 = Math.Abs(Vec3F.Dot(ray0.Direction, yAxis));
                Vec3F axis       = (a1 > a2 ? xAxis : yAxis);
                Vec3F p0         = ray0.IntersectPlane(axis, -Vec3F.Dot(axis, origin));
                Vec3F p1         = ray.IntersectPlane(axis, -Vec3F.Dot(axis, origin));
                float dragAmount = Vec3F.Dot(zAxis, p1 - p0);
                Vec3F zLocal     = transform.ZAxis;
                trans = dragAmount * zLocal;
            }
            break;

            case HitElement.XY_SQUARE:
            {
                Vec3F p0         = ray0.IntersectPlane(zAxis, -Vec3F.Dot(zAxis, origin));
                Vec3F p1         = ray.IntersectPlane(zAxis, -Vec3F.Dot(zAxis, origin));
                Vec3F deltaLocal = p1 - p0;
                float dragX      = Vec3F.Dot(xAxis, deltaLocal);
                float dragY      = Vec3F.Dot(yAxis, deltaLocal);
                Vec3F xLocal     = transform.XAxis;
                Vec3F yLocal     = transform.YAxis;
                trans = dragX * xLocal + dragY * yLocal;
            }
            break;

            case HitElement.YZ_SQUARE:
            {
                Vec3F p0         = ray0.IntersectPlane(xAxis, -Vec3F.Dot(xAxis, origin));
                Vec3F p1         = ray.IntersectPlane(xAxis, -Vec3F.Dot(xAxis, origin));
                Vec3F deltaLocal = p1 - p0;
                float dragY      = Vec3F.Dot(yAxis, deltaLocal);
                float dragZ      = Vec3F.Dot(zAxis, deltaLocal);
                Vec3F yLocal     = transform.YAxis;
                Vec3F zLocal     = transform.ZAxis;
                trans = dragY * yLocal + dragZ * zLocal;
            }
            break;

            case HitElement.XZ_SQUARE:
            {
                Vec3F p0         = ray0.IntersectPlane(yAxis, -Vec3F.Dot(yAxis, origin));
                Vec3F p1         = ray.IntersectPlane(yAxis, -Vec3F.Dot(yAxis, origin));
                Vec3F deltaLocal = p1 - p0;
                float dragX      = Vec3F.Dot(xAxis, deltaLocal);
                float dragZ      = Vec3F.Dot(zAxis, deltaLocal);
                Vec3F xLocal     = transform.XAxis;
                Vec3F zLocal     = transform.ZAxis;
                trans = dragX * xLocal + dragZ * zLocal;
            }
            break;
            }
            return(trans);
        }
예제 #3
0
 /// <summary>
 /// Returns whether the control was hit during a picking operation</summary>
 /// <param name="hit">Hit record</param>
 /// <returns>True if the control was hit during a picking operation</returns>
 public bool IsHit(HitRecord hit)
 {
     return(hit.RenderObjectData[0] == m_nameBase);
 }
예제 #4
0
        private HitRecord[] PopulateOpenGlSelection(ICollection <TraverseNode> traverseList)
        {
            // Ensure that OpenGL is in correct state.
            double[] viewMat       = null;
            double[] projectionMat = null;
            int[]    viewport      = null;
            if (m_frustumPick == false)
            {
                Gl.glViewport(0, 0, m_width, m_height);
                Gl.glMatrixMode(Gl.GL_PROJECTION);
                Gl.glLoadIdentity();
                Util3D.glMultMatrixf(m_projectionMatrix);

                Gl.glMatrixMode(Gl.GL_MODELVIEW);
                Gl.glLoadIdentity();
                Util3D.glMultMatrixf(m_viewMatrix);

                viewMat = new double[16];
                Gl.glGetDoublev(Gl.GL_MODELVIEW_MATRIX, viewMat);

                projectionMat = new double[16];
                Gl.glGetDoublev(Gl.GL_PROJECTION_MATRIX, projectionMat);

                viewport    = new int[4];
                viewport[0] = viewport[1] = 0;
                viewport[2] = m_width;
                viewport[3] = m_height;
            }

            // Construct traverse array
            HitRecord[]      selection     = null;
            List <HitRecord> selectionList = new List <HitRecord>();

            TraverseNode[] travArray = new TraverseNode[traverseList.Count];
            traverseList.CopyTo(travArray, 0);

            uint start = 0;

            for (int i = 0; i < m_openGlHits; ++i)
            {
                uint nameCount = (uint)m_selectionBuffer[start];

                if (nameCount > 0)
                {
                    uint         travNodeIndex = (uint)m_selectionBuffer[start + 3];
                    TraverseNode travNode      = travArray[travNodeIndex];
                    HitRecord    hitRecord;
                    if (m_frustumPick)
                    {
                        hitRecord = new HitRecord(
                            travNode.GraphPath,
                            travNode.RenderObject,
                            new Matrix4F(travNode.Transform),
                            new uint[nameCount - 1]);
                    }
                    else
                    {
                        hitRecord = new HitRecord(
                            travNode.GraphPath,
                            travNode.RenderObject,
                            new Matrix4F(travNode.Transform),
                            new uint[nameCount - 1]);

                        // Transform screen to world and record world-space intersection point.
                        float zMin           = ((float)((uint)m_selectionBuffer[start + 1])) / 0xFFFFFFFF;
                        Vec3F intersectionPt = GetWorldIntersectionFromScreen(
                            m_x, m_y, zMin,
                            viewMat, projectionMat, viewport);
                        hitRecord.WorldIntersection = intersectionPt;
                    }

                    // Populate object data
                    for (uint j = 0; j < nameCount - 1; j++)
                    {
                        hitRecord.RenderObjectData[j] = (uint)m_selectionBuffer[start + 4 + j];
                    }

                    selectionList.Add(hitRecord);

                    start += 3 + nameCount;
                }
            }
            selection = new HitRecord[selectionList.Count];
            selectionList.CopyTo(selection, 0);
            return(selection);
        }
예제 #5
0
        /// <summary>
        /// Dispatches untyped items. Replaces DispatchNotTyped(). To get the same behavior as
        /// the old DispatchNotTyped(), set the TypeFilter property to null prior to calling.</summary>
        /// <param name="traverseList">The traverse list</param>
        /// <param name="camera">The camera</param>
        protected void DispatchTraverseList(ICollection <TraverseNode> traverseList, Camera camera)
        {
            // Prepare for geometric picking -- create the ray in world space and reset geometric hit-list.
            // First create the ray in viewing coordinates and transform to world coordinates.
            float    nx          = (m_x / (float)m_width) - 0.5f;  //normalized x
            float    ny          = 0.5f - (m_y / (float)m_height); //normalized y
            Ray3F    rayWorld    = camera.CreateRay(nx, ny);
            Matrix4F worldToView = camera.ViewMatrix;
            Matrix4F viewToWorld = new Matrix4F();

            viewToWorld.Invert(worldToView);
            rayWorld.Transform(viewToWorld);
            ClearHitList();

            // for geometric picking. will be cleared for each HitRecord.
            List <uint> userData = new List <uint>(1);

            // Dispatch traverse list
            int index = 0;

            foreach (TraverseNode node in traverseList)
            {
                // First test for filtering.
                IRenderObject renderObject = node.RenderObject;
                if (FilterByType(renderObject))
                {
                    IIntersectable intersectable = renderObject.GetIntersectable();
                    IGeometricPick geometricPick = intersectable as IGeometricPick;
                    if (geometricPick != null)
                    {
                        // Picking by geometry.
                        Matrix4F objToWorld = new Matrix4F(node.Transform);
                        Matrix4F worldToObj = new Matrix4F();
                        worldToObj.Invert(objToWorld);
                        Matrix4F viewToObj = Matrix4F.Multiply(viewToWorld, worldToObj);

                        if (m_frustumPick)
                        {
                            //The pick frustum is in view space. Transform to world space then object space.
                            Frustum frustumObj = new Frustum(m_viewFrust0);
                            frustumObj.Transform(viewToObj);

                            //Multi-pick. Get everything in the pick frustum (m_viewFrust0).
                            Vec3F eyeObj;
                            worldToObj.Transform(camera.Eye, out eyeObj);
                            userData.Clear();

                            if (geometricPick.IntersectFrustum(frustumObj, eyeObj, node.RenderState, userData))
                            {
                                // Prepare a multi-pick HitRecord, as if OpenGL had calculated this.
                                HitRecord hit = new HitRecord(
                                    node.GraphPath,
                                    renderObject,
                                    objToWorld,
                                    userData.ToArray());

                                m_geoHitList.Add(hit);
                            }
                        }
                        else
                        {                            //Single pick. We care about distance from camera eye.
                            //Make a copy of the ray in world-space and tranform it to object space.
                            Ray3F rayObj = rayWorld; //remember, Ray3F is a value type, not a reference type.
                            rayObj.Transform(worldToObj);

                            // Do the intersection test in object space.
                            userData.Clear();
                            Vec3F intersectionPt, surfaceNormal;
                            Vec3F nearestVert;
                            bool  intersected;
                            intersected = geometricPick.IntersectRay(
                                rayObj, camera, node.RenderState, objToWorld, this,
                                out intersectionPt, out nearestVert, out surfaceNormal, userData);

                            if (intersected)
                            {
                                // Transform to world space and then to screen space.
                                objToWorld.Transform(intersectionPt, out intersectionPt);
                                objToWorld.Transform(nearestVert, out nearestVert);
                                // Prepare a single-pick HitRecord, as if OpenGL had calculated this.
                                HitRecord hit = new HitRecord(
                                    node.GraphPath,
                                    renderObject,
                                    objToWorld,
                                    userData.ToArray());

                                // This is the one difference from OpenGL pick. We have the world pt already.
                                hit.WorldIntersection = intersectionPt;
                                hit.NearestVert       = nearestVert;

                                // Another difference is that it's possible to get the surface normal.
                                if (surfaceNormal != Vec3F.ZeroVector)
                                {
                                    objToWorld.TransformNormal(surfaceNormal, out surfaceNormal);
                                    surfaceNormal.Normalize();
                                    hit.Normal = surfaceNormal;
                                }

                                m_geoHitList.Add(hit);
                            }
                        }
                    }
                    else
                    {
                        // Picking by "rendering", using OpenGL pick.
                        PushMatrix(node.Transform, false);
                        Gl.glPushName(index);
                        IRenderPick pickInterface = renderObject as IRenderPick;
                        if (pickInterface != null)
                        {
                            pickInterface.PickDispatch(node.GraphPath, node.RenderState, this, camera);
                        }
                        else
                        {
                            renderObject.Dispatch(node.GraphPath, node.RenderState, this, camera);
                        }
                        Gl.glPopName();
                        PopMatrix();
                    }
                }

                index++;
            }
        }
예제 #6
0
        /// <summary>
        /// Gets the HitRecord array from a given traverse list</summary>
        /// <param name="traverseList">The traverse list</param>
        /// <param name="multiPick">True to return all available HitRecords. False to return just the closest</param>
        /// <returns>HitRecord array</returns>
        /// <remarks>Must be called after Init() and Dispatch(). There can potentially be duplicate
        /// HitRecords returned for two reasons: 1) Proxy objects, like the cube by default, render
        /// themselves twice, once with solid fill and once with the outline. Both renderings can
        /// yield a HitRecord, sometimes with slightly different z values. 2) Models are made up of
        /// multiple TransformNodes and each one can generate a HitRecord.</remarks>
        public HitRecord[] GetHits(ICollection <TraverseNode> traverseList, bool multiPick)
        {
            if (m_frustumPick == true && multiPick == false)
            {
                throw new InvalidOperationException("results can't be sorted front-to-back with frustum picking");
            }

            // First get the hits from OpenGL pick. Gl.glRenderMode resets this value when it is called,
            // so consecutive calls to GetHits() will fail. Thus, we need to cache glRenderMode(GL_RENDER).
            if (m_openGlHits == 0)
            {
                m_openGlHits = Gl.glRenderMode(Gl.GL_RENDER);
            }
            HitRecord[] renderSelect = null;

            if (m_openGlHits > 0 && traverseList.Count > 0)
            {
                renderSelect = PopulateOpenGlSelection(traverseList);
            }
            else
            {
                renderSelect = s_emptyHitRecordArray;
            }

            // now integrate the hits from geometrical picking.
            HitRecord[] select;
            if (m_geoHitList.Count == 0)
            {
                select = renderSelect;
            }
            else
            {
                int total = renderSelect.Length + m_geoHitList.Count;
                select = new HitRecord[total];
                renderSelect.CopyTo(select, 0);
                m_geoHitList.CopyTo(select, renderSelect.Length);
            }

            // sort the results by distance from camera eye, near-to-far
            if (m_frustumPick == false)
            {
                HitRecord.Sort(select, m_eye);
            }

            // if the caller only wants one result, just give them one.
            if (multiPick == false && select.Length > 1)
            {
                HitRecord[] singleHit = new HitRecord[1];
                singleHit[0] = select[0];
                select       = singleHit;
            }

            // useful debug output
            //Console.WriteLine("multiPick:{0}, m_frustumPick:{1}", multiPick, m_frustumPick);
            //foreach (HitRecord hit in select)
            //{
            //    Console.WriteLine("  hit: {0} at z {1}", hit.RenderObject.InternalObject.Id, hit.ZMin);
            //}

            // return the results
            return(select);
        }