Non-maintained solution
The proposed solution is not natively supported and may not work in all scenarios and versions.
Entity
To create a zoom invariant entity, meaning an entity that stays the same size on screen regardless of the camera position and zoom, you can use the following mesh-derived class.
class ZoomInvariantMesh : Mesh
{
private Point3D _objectRealPosition;
private bool _restoreMatrix;
public Design Design { get; set; }
public ZoomInvariantMesh(Mesh copy)
: base(copy)
{
}
public override bool IsInFrustum(FrustumParams data, Point3D center, double radius)
{
// not discarded when it goes out of the viewport
return true;
}
protected override void DrawSilhouettes(DrawSilhouettesParams data)
{
PreDraw(data);
double[] prevmvproj = data.ModelViewProj;
data.ModelViewProj = Utility.MultMatrixd(data.RenderContext.CurrentModelViewMatrix(), data.RenderContext.CurrentProjectionMatrix());
base.DrawSilhouettes(data);
PostDraw(data);
data.ModelViewProj = prevmvproj;
}
protected override void DrawForShadow(RenderParams data)
{
}
private void PreDraw(DrawParams data)
{
data.RenderContext.PushMatrices();
float multFactor = 50; // The multiplying factor depends on the model size
float screenToWorld = (float) ComputePreciseScreenToWorld(data) * multFactor;
//------------ For position invariance
var plane = data.Viewport.Camera.NearPlane;
plane.Origin = _objectRealPosition;
// Compute the world position of the screen point where we want to draw our entity (in this case 50, 50)
Design.ScreenToPlane(new System.Drawing.Point(50, 50), plane, out Point3D pt);
Translation transf = new Translation(pt.X - _objectRealPosition.X, pt.Y - _objectRealPosition.Y, pt.Z - _objectRealPosition.Z);
//-------------
var transformation =
transf * // Comment this line if the position invariance is not needed
new Translation(_objectRealPosition.X, _objectRealPosition.Y, _objectRealPosition.Z) * // move back to its position
new Scaling(screenToWorld, screenToWorld, screenToWorld) * // scale
new Translation(-_objectRealPosition.X, -_objectRealPosition.Y, -_objectRealPosition.Z); // Move to the Origin ;
data.RenderContext.MultMatrixModelView(transformation); // Move to the Origin
AdjustCameraPlanes(data, transformation);
// Comment this line if don't want the entity to go over the geometry in the scene
data.RenderContext.SetViewport(data.Viewport.GetViewFrame(), 0, (float)RenderContextBase.OverlayDepthRange);
}
/// <summary>
/// Adjusts the Camera planes to avoid the clipping of the scaled symbol.
/// </summary>
private void AdjustCameraPlanes(DrawParams data, Transformation transformation)
{
if (data.Viewport.Camera is MyCamera)
{
MyCamera camera = (MyCamera)data.Viewport.Camera;
var pts = Utility.GetBoundingBoxCorners(BoxMin, BoxMax);
for (int i = 0; i < pts.Length; i++)
pts[i] = transformation * pts[i];
double min = double.MaxValue;
double max = double.MinValue;
var normal = camera.ViewNormal;
for (int i = 0; i < pts.Length; i++)
{
var vec = Vector3D.Subtract(camera.Location, pts[i]);
double dist = Vector3D.Dot(vec, normal);
if (dist < min)
min = dist;
if (dist > max)
max = dist;
}
if (camera.Near > min || camera.Far < max)
{
if (camera.ProjectionMode == projectionType.Perspective && (min < 0 || max < 0))
return;
var projMat = camera.GetProjectionMatrix(min, max, CameraEyePosType.Center);
_restoreMatrix = true;
data.RenderContext.PushProjection();
data.RenderContext.SetProjectionMatrix(projMat);
}
else
_restoreMatrix = false;
}
}
double ComputePreciseScreenToWorld(DrawParams data)
{
_objectRealPosition = (BoxMin + BoxMax) / 2;
IViewport view = data.Viewport;
view.Project(Design.Height, _objectRealPosition.X, _objectRealPosition.Y, _objectRealPosition.Z, out _, out _, out double winZ);
Point3D[] pts = view.UnProject(new[] { new Point3D(0, 0, winZ), new Point3D(1, 0, winZ) }, Design.Height);
return Point3D.Distance(pts[0], pts[1]);
}
private void PostDraw(DrawParams data)
{
data.RenderContext.PopMatrices();
// Comment this line if don't want the entity to go over the geometry in the scene
data.RenderContext.SetViewport(data.Viewport.GetViewFrame(), (float) RenderContextBase.OverlayDepthRange, 1);
if (_restoreMatrix)
{
data.RenderContext.PopProjection();
_restoreMatrix = false;
}
}
protected override void DrawSelected(DrawParams data)
{
PreDraw(data);
base.DrawSelected(data);
PostDraw(data);
}
protected override void DrawForSelection(DrawForSelectionParams data)
{
PreDraw(data);
base.DrawForSelection(data);
PostDraw(data);
}
protected override void Draw(DrawParams data)
{
PreDraw(data);
base.Draw(data);
PostDraw(data);
}
protected override void DrawWireframe(DrawParams data)
{
PreDraw(data);
base.DrawWireframe(data);
PostDraw(data);
}
protected override void Render(RenderParams data)
{
if (data.PlanarReflections) // Requires Eyeshot >= 9.0.320
return;
PreDraw(data);
base.Render(data);
PostDraw(data);
}
protected override void DrawEdges(DrawParams data)
{
// not supported
}
protected override void DrawIsocurves(DrawParams data)
{
// not supported
}
}
The entity stays at a fixed position on-screen. See the line:
transf * // Comment this line if the position invariance is not needed
in the PreDraw() method.
Then you need to derive the Camera class in order to expose the GetProjectionMatrix() method:
class MyCamera : Camera
{
public MyCamera(Camera another) : base(another)
{
}
public new double[] GetProjectionMatrix(double nearDistance, double farDistance, CameraEyePosType cameraEyePos)
{
return base.GetProjectionMatrix(nearDistance, farDistance, cameraEyePos);
}
}
Then you can use it in this way:
design1.Viewports[0].Camera = new MyCamera(design1.Viewports[0].Camera);
Mesh arrow = Mesh.CreateArrow(0.2, 0.4, 0.4, 0.8, 16, Mesh.natureType.Smooth);
arrow.ColorMethod = colorMethodType.byEntity;
arrow.Color = Color.Blue;
arrow.Rotate(Math.PI / 2, Vector3D.AxisZ);
arrow.Translate(new Vector3D(0, 0, 0));
ZoomInvariantMesh aArrow = new ZoomInvariantMesh(arrow);
aArrow.Design = design1;
aArrow.Translate(1, 2, 3);
design1.Entities.Add(aArrow);
Note 1:
When deriving entities other than the Mesh or the Solid, since their Render() method actually calls the Draw(), it is important to not override their Render() method in order to avoid issues in the Rendered DisplayMode (due to the fact that the PreDraw() and PostDraw() would be called twice).
Note 2:
For the Line entity, the PostDraw() method must be like this:
void PostDraw(DrawParams data)
{
data.RenderContext.EndDrawBufferedLines();
data.RenderContext.PopMatrices();
// Comment this line if don't want the entity to go over the geometry in the scene
data.RenderContext.SetViewport(data.Viewport.GetViewFrame(), (float) RenderContextBase.OverlayDepthRange, 1);
}
Note 3:
The calls to SetViewport() in the PreDraw() and PostDraw() methods make the zoom invariant geometry go over the other geometry in the scene (like the origin symbol). If this is not desired they can be removed.
Note 4:
If you use Flat DisplayMode you have to override also this method:
protected override void DrawFlat(DrawParams data)
{
PreDraw(data);
base.DrawFlat(data);
PostDraw(data);
}
Note 5:
If you want to render text in Zoom/Location invariant method, use a Label object.
Comments
Hi Matteo,
I tried your zoom invariant code, commenting out the position invariant code. What I want to achieve is something like the CoordinateSystemIcon to represent a local system of reference. It should be invariant to zooming operations, but should move in respect to the camera.

I used your example code with the arrow positioned in (1, 0, 0). In the following to images you can see that the zooming moved the arrow in respect to the X axis.
Zoom in
This is the code used to create the entity:
public void MyFunc(devDept.Eyeshot.Model m) {
m.Viewports[0].Camera = new ZoomInvariantMesh.MyCamera(m.Viewports[0].Camera);
Mesh arrow = Mesh.CreateArrow(0.2, 0.4, 0.4, 0.8, 16, Mesh.natureType.Smooth);
arrow.ColorMethod = colorMethodType.byEntity;
arrow.Color = Color.Blue;
arrow.Rotate(Math.PI / 2, devDept.Geometry.Vector3D.AxisZ);
arrow.Translate(new devDept.Geometry.Vector3D(0, 0, 0));
ZoomInvariantMesh aArrow = new ZoomInvariantMesh(arrow);
aArrow.m = m;
aArrow.Translate(1, 0, 0);
m.Entities.Add(aArrow);
}
Thanks,
Stenio
Hi Stenio,
In Eyeshot 2020, you can add as many CoordinateSystemIcons you need and with different positions/orientations. Please check the Part1 sample.
Alberto
How to make the entity static too? i.e. entity doesn't rotate when the scene does.
You probably need to override the DrawOverlay() method instead.
Thank you.
How can we make a Label object location invariant
Please sign in to leave a comment.