/// <summary> /// Computes the ratio between the area of the projected target inside frame, and the area inside viewport /// </summary> public float ComputeRatioInsideFrame(CLCameraMan camera, Rectangle frame) { if (this.screenArea < 0.00001) { // target is outside viewport or too small return(0.0f); } // clip screen representation by provided frame List <Vector2> partInFrame = new List <Vector2> (10); TargetUtils.Clip(frame, screenRepresentation, partInFrame); float framedArea = TargetUtils.ComputeScreenArea(partInFrame); return(framedArea / this.screenArea); }
/// <summary> /// Computes and internally stores a screen representation of the target given the current camera. The screen /// representation is in turn used to reason about properties like size, framing, ... /// </summary> /// <param name="currentCamera">camera from which rendering is performed</param> /// <param name="performClipping">if true, we clip against the viewport. If not, screenArea can </param> public void Render(CLCameraMan camera, bool performClipping = true) { screenRepresentation.Clear(); visibleBBVertices.Clear(); /** * ok, so ... using the world-space AABB of the game object is not ideal because it appears it is the world AABB * of the world-transformed local AABB of the mesh, see http://answers.unity3d.com/questions/292874/renderer-bounds.html * Also, using the AABB of the colliderMesh does not help (it appears to be identical to the renderer AABB) * * Could transform the camera to local space and use the AABB of the mesh ... */ // world position of the camera Vector3 eye = camera.unityCamera.transform.position; Bounds AABB = boundingBox; // World-space min-max corners of the target's AABB Vector3 minCorner = AABB.min; Vector3 maxCorner = AABB.max; /** Calculate bit-string position to perform lookup in the table, real * spatial relationship is not relevant, the relevant information is the * vertex ordering */ int pos = ((eye.x < minCorner.x ? 1 : 0) << (int)TargetUtils.RelativePositioning.LEFT) + ((eye.x > maxCorner.x ? 1 : 0) << (int)TargetUtils.RelativePositioning.RIGHT) + ((eye.y < minCorner.y ? 1 : 0) << (int)TargetUtils.RelativePositioning.BOTTOM) + ((eye.y > maxCorner.y ? 1 : 0) << (int)TargetUtils.RelativePositioning.TOP) + ((eye.z < minCorner.z ? 1 : 0) << (int)TargetUtils.RelativePositioning.FRONT) + ((eye.z > maxCorner.z ? 1 : 0) << (int)TargetUtils.RelativePositioning.BACK); // If camera inside bounding box return 0 numVisibleBBVertices = TargetUtils.Number(pos); if (numVisibleBBVertices == 0) { this.screenArea = 0.0f; screenAABB = new Rectangle(0.0f, 0.0f, 0.0f, 0.0f); screenRatio = 0.0f; return; } // Otherwise project vertices on screen // Array for storing projected vertices List <Vector2> projectedBBVertices = new List <Vector2> (10); bool behindCamera = false; // project each visibile vertex for (int i = 0; i < numVisibleBBVertices; i++) { Vector3 visibleVertex = TargetUtils.ReturnAABBVertex(TargetUtils.Vertex(i, pos), AABB); Vector3 projectedVertex = camera.unityCamera.WorldToViewportPoint(visibleVertex); if (projectedVertex.z >= 0) { projectedBBVertices.Add(projectedVertex); visibleBBVertices.Add(visibleVertex); //Debug.Log ( newPoint.ToString("F5")); } else { behindCamera = true; } } // clip them by viewport if (performClipping) { TargetUtils.Clip(camera.clipRectangle, projectedBBVertices, screenRepresentation); } // if there are less than three vertices on screen, area is zero if (screenRepresentation.Count < 3) { this.screenArea = 0; screenAABB = new Rectangle(0.0f, 0.0f, 0.0f, 0.0f); screenRatio = 0.0f; } else { // compute area this.screenArea = Mathf.Min(TargetUtils.ComputeScreenArea(screenRepresentation), 1.0f); // compute min and max vertices Vector2 minPoint = screenRepresentation [0]; Vector2 maxPoint = screenRepresentation [1]; for (int i = 0; i < screenRepresentation.Count; i++) { if (minPoint.x > screenRepresentation [i].x) { minPoint.x = screenRepresentation [i].x; } if (minPoint.y > screenRepresentation [i].y) { minPoint.y = screenRepresentation [i].y; } if (maxPoint.x < screenRepresentation [i].x) { maxPoint.x = screenRepresentation [i].x; } if (maxPoint.y < screenRepresentation [i].y) { maxPoint.y = screenRepresentation [i].y; } } screenAABB = new Rectangle(minPoint.x, maxPoint.x, minPoint.y, maxPoint.y); if (!behindCamera) { screenRatio = this.screenArea / TargetUtils.ComputeScreenArea(projectedBBVertices); } else { screenRatio = 0.5f; // this is just a hack since otherwise bb projected points behind camera } // are simply thrown away and the target, while partially on screen, // could be considered entirely on screen if (screenRatio > 1.0f && performClipping) { screenRatio = 0.0f; } else if (screenRatio > 1.0f) { // this means we have no clipping and the projected AABB is greater than the viewport screenRatio = 1.0f; } } }