Getting
WYSIWYG printing in your application typically means that
everything that is currently visible on the screen appears
on the printout. However because the aspect ratio (window
width versus window height) of the drawing area on a print
page and on your display window are typically different
we need to modify the camera field on the print page so
that it matches the world space width and height of the
display window. The other camera settings (position, target,
up-vector and projection) are set to the same values that
are used by the camera viewing the scene on the display
window.
To set
the camera correctly we simply need to determine the width
and height of the display window and set that as the camera
field. Determining the width and height of the display window
is relatively straight-forward and involves using the Compute_Coordinates(…)
function which helps you translate points between the various
coordinate systems. The lower-left corner of a window in
window-space is (-1,-1,0) while the upper-right corner is
(1,1,0). Thus, to determine the camera width and field the
pseudo code would look something like:
Note,
since the aspect ratio changes in many cases geometry
that was previously clips from the viewport will now be
visible.
Printing
my data with accurate scale
Though
HOOPS uses a Cartesian coordinate system it is a unitless
system. By this we mean that 1 unit in the 3dGS system
does not map over to a specific physical unit. It is up
to the user to decide whether one unit in their scene
graph is 1 mile, 1 kilometer, 1 foot, 1 inch or 1 centimeter.
Thus when a developer is outputting their data to a physical
device and they want 1 centimeter (say) to be 1 cm on
the printout then they must do some work to query the
physical size of the printer page and then set their camera
accordingly. Specifically, they must set the camera field
to match the exact physical size of the page. On Windows
you determine the physical size of a printable area via
CDC:: GetDeviceCaps(). To get accurate scale printing
all you should need to do is set the camera field so that
the width and height match over to the physical width
and height of the printable area. The pseudo code for
getting 1 to 1 printing on Windows looks like this:
// HORZRES and VERTRES return the width and height
// of the printable area in millimeters
int width =pDC->GetDeviceCaps(HORZRES);
int height =pDC->GetDeviceCaps(VERTRES);
// so now if 1 unit in our model is meant to be 1 centimeter
// then we would need to take this value into account when
// we set the camera field.
float scaleFactor = 10.0f;
HC_Open_Segment(/msw/printer/myPrintout);
HC_Set_Camera_Field(width*scaleFactor, height*scaleFactor);
// now you set the camera position, target and
// up_vector to get the required geometry drawn
// on the page
. . . .
HC_Include_Segment_By_Key(myData);
HC_Close_Segment()
Vector
versus Raster Printouts and how to create Printouts that
contain both
To
get high quality printing of text, edge and line data
a printer needs to be sent vector rather than raster data
and so when in wireframe or hidden line rendered scenes
developers set their hidden surface removal algorithm
to one of HOOPS’ vector based sorting algorithms.
In the wireframe case this can be either “z-sort”
or “painters” while in the hidden-line case
it is “hidden line”. Z-sort and painters could
both be used for all printing however they are extremely
slow when dealing with large scenes and since they send
all the geometry down to the printer they are pretty memory
intensive. Furthermore, printers do not have vector primitives
for lit geometry and consequently to ensure the printout
looks correct HOOPS must do the lighting calculations
and send this geometry down in a pixel-by-pixel fashion.
Thus, for scenes which contain a lot of lit geometry we
use a different, framebuffer-based approach to print them.
Using this approach results in fast printing which uses
the minimum amount of memory.
The
method is quite simple. We create an image of the scene
which has the same aspect ratio of the print page, set
this as the background image in the driver and then print
the page. The pseudo code for doing this is as follows:
int width = pDC->GetDeviceCaps(HORZRES);
int height = pDC->GetDeviceCaps(VERTRES);
// here is where we set the DPI of the image were going to blit
// to the printer
int image_dpi=150;
// there are 25.4 millimeters in a single inch
width *= image_dpi/25.4;
height *= image_dpi/25.4;
//prepare image for rendering
HC_Open_Segment ("?driver/null/1");
HC_KEY imageKey = HC_KInsert_Image(0.0,0.0,0.0, "rgb, name=background",
width, height, NULL);
HC_Close_Segment ();
bool useOpenGLImageGeneration = true;
long debug=0L;
if (useOpenGLImageGeneration)
{
sprintf(imageDriver, "/driver/opengl/image");
debug |= 0x00100000;
debug |= DEBUG_FORCE_SOFTWARE;
}
else
sprintf(imageDriver, "?driver/image/1");
// now lets create the image
HC_Open_Segment (imageDriver);
sprintf (buffer, "use window id = %s%p, isolated, debug=%d",
H_EXTRA_POINTER_FORMAT, imageKey, debug);
HC_Set_Driver_Options (buffer);
HC_Set_Camera (
);
if (useOpenGLImageGeneration)
HC_Set_Rendering_Options("hsra = hzb");
else
HC_Set_Rendering_Options("hsra = szb");
HC_Include_Segment_By_Key (view->GetSceneKey());
HC_Close_Segment();
HC_Update_Display();
// Now use this in our printout. Notice that we do not
// include any geometry into the scene since it is all
// contained in the background image
HC_Open_Segment (/driver/msw/printer);
HC_Set_Color("windows= background");
HC_Set_Driver_Options((char *)allThePrinterDriverSettings);
HC_Close_Segment ();
Note,
the DPI of the image does not have to match the DPI of
the output device since HOOPS will stretch the image so
that it extends across the complete page, all we have
to do is ensure that the aspect ratio of the image matches
the aspect ratio of the print page. HOOPS uses some internal
algorithms to eliminate white pixels from the image to
ensure that we’re sending as little data as possible
down to the printer.
In
many cases scenes contain both lit and unlit geometry
and users want the lit geometry to be printed quickly
with minimal memory usage and also want the vector data
(which typically consists of the edge, line and text data)
to be printed with vector commands. Being able to get
this kind of functionality is done by using a combination
of the image generation method above and using hidden
line algorithm to sort the vector data. What you do is
create the image containing only the lit geometry, use
that image as the background image for the print segment
and then include the scene in the print segment which
uses a “hidden line” hsra setting. The pseudo
code for doing this is:
// At this point we have already created the image of
// the appropriate size as is outlined above. We then
// create the image however notice how we turn off the
// visibility of the vector data and put an attribute
// lock on it so that it overrides any of the settings
// in the actual scene-graph
HC_Open_Segment (imageDriver);
sprintf (buffer, "use window id = %s%p, debug=%d", H_EXTRA_POINTER_FORMAT,
imageKey, debug);
HC_Set_Driver_Options (buffer);
HC_Set_Camera (
);
if (useOpenGLImageGeneration)
HC_Set_Rendering_Options("hsra = hzb");
else
HC_Set_Rendering_Options("hsra = szb");
HC_Set_Visibility(edges=off, lines=off, text=off, markers=off);
HC_Set_Rendering_Options(attribute lock=(visibility));
HC_Include_Segment_By_Key (myScene);
HC_Close_Segment();
HC_Update_Display();
// Now use the image we just generated as the background,
// include the scene and use the hidden line sorting
// algorithm to properly hidden surface remove the
// non-visible vector data
HC_Open_Segment (/driver/msw/printer);
HC_Set_Color("windows=background");
HC_Set_Driver_Options((char *)allThePrinterDriverSettings);
HC_Set_Rendering_Options(hsra=hidden line, hlro=(visibility=off));
HC_Include_Segment_By_Key (myScene);
HC_Close_Segment ();
How
do I get my HOOPS subwindows to be positioned correctly
in my printouts
Many
Developers have problems correctly positioning HOOPS subwindows
on a printout. The problem is that HOOPS subwindows are
defined in window space which means that a change in the
aspect ratio of the drawing area results in a corresponding
change in the shape of the subwindow. Thus each time we
are putting subwindows into a drawing area which has a
different aspect ratio we need to readjust the window
settings. To do this we convert the coordinates of the
subwindow in the original window into a coordinate system
which is independent of aspect ratio and then use that
position to re-calculate the location of the window on
the drawing area with a different aspect ratio. Pseudo
code for a function which does this is contained below:
// You would call this function for each windowed subsegment
// in your scene as the aspect ratio changes. This function
// only makes a difference if the aspect ratio between the
// two drivers is different. Consequently this version of the
// function will not work for repositioning subwindows in a
// window as it is being resized
void ResetWindows(HC_KEY currentDriver, HC_KEY newDriver, HC_KEY windowedSegment)
{
float left, right, top, bottom;
HC_Open_Segment_By_Key (windowedSegment);
HC_Show_Window(&left, &right, &top, &bottom);
HC_Close_Segment();
HPoint bottomLeft, topRight;
bottomLeft.Set(left, bottom, 0.0f);
topRight.Set(right, top, 0.0f);
HC_Open_Segment (currentDriver);
HC_Compute_Coordinates(., local window, bottomLeft, world, bottomLeft);
HC_Compute_Coordinates(., local window, topRight, world, topRight);
HC_Close_Segment();
HC_Open_Segment (newDriver);
HC_Compute_Coordinates(., world, bottomLeft, local window, bottomLeft);
HC_Compute_Coordinates(., world, topRight, local window, topRight);
HC_Close_Segment();
HC_Open_Segment (windowedSegment);
HC_Set_Window(bottomLeft.x, bottomLeft.y, topRight.x, topRight.y);
HC_Close_Segment();
}