| 1 |
TRANSLATING TO MGF FROM OTHER FORMATS
|
| 2 |
RCSid "$Id$"
|
| 3 |
|
| 4 |
The description of the parser and the MGF specification should provide
|
| 5 |
enough information to get you started using MGF scene files, but we
|
| 6 |
thought it would be helpful to also provide some hints and
|
| 7 |
suggestions for translating to MGF from other formats.
|
| 8 |
Specifically, we will discuss several issues that come up repeatedly
|
| 9 |
when converting from more usual computer graphics scene formats to
|
| 10 |
MGF, most of them having to do with materials. First, let's look at
|
| 11 |
some geometry-related issues.
|
| 12 |
|
| 13 |
Vertex Naming
|
| 14 |
=============
|
| 15 |
Many scene formats do not name vertices; many do not even share
|
| 16 |
vertices. Does it matter what names are given to vertices in MGF?
|
| 17 |
Not a lot, but it can affect memory and file size. In a way, vertex
|
| 18 |
sharing is nothing more than a form of file compression, and the
|
| 19 |
better you are at sharing vertex information, the smaller your file
|
| 20 |
will be. (Vertex sharing is also important for some rendering
|
| 21 |
algorithms, which depend on it for computing surface adjacency.)
|
| 22 |
|
| 23 |
If you are translating from a format that shares unnamed vertices,
|
| 24 |
such as Wavefront's .OBJ format, you will want to name your MGF
|
| 25 |
vertices according to some simple pattern. In most cases, a name
|
| 26 |
such as "v%d" will do, where %d is replaced by an incremented
|
| 27 |
integer.
|
| 28 |
|
| 29 |
If, on the other hand, you are translating from a format that does
|
| 30 |
not share vertices, you should do one of two things. You should
|
| 31 |
either select your MGF vertex names from a small, recycled pool of
|
| 32 |
names, or figure out some way to share vertices that were not shared
|
| 33 |
before. In the first case, you will just allocate as many vertex
|
| 34 |
names as you need for any given object, then reuse these names and
|
| 35 |
therefore the parser's memory for other objects. In the second case,
|
| 36 |
you will cache vertex names and values in some LRU table of
|
| 37 |
predetermined size, and use this table to merge vertices in the
|
| 38 |
file. (See rad2mgf.c as an example of how this can be done.)
|
| 39 |
|
| 40 |
For some objects, there may be little point in merging vertices, and
|
| 41 |
you may want to treat these surfaces separately. For example,
|
| 42 |
putting out an MGF ring means putting out a central vertex, which
|
| 43 |
must have both a position point and a normal direction. It is somewhat
|
| 44 |
unlikely that any other MGF entity will share this point, and quite
|
| 45 |
unlikely that it will share the normal direction, so there is little
|
| 46 |
sense in trying to merge or otherwise reuse it.
|
| 47 |
|
| 48 |
Points and Lines
|
| 49 |
================
|
| 50 |
Although points and lines are really 3-d surfaces, many CAD
|
| 51 |
systems include them in their models. The question then is,
|
| 52 |
what do we do with these in MGF? If the idea is to produce a point
|
| 53 |
or line on the final display that is one or two pixels wide, there
|
| 54 |
is little one can do to guarantee such a thing will happen because
|
| 55 |
the pixel size is dependent on view and display parameters as well
|
| 56 |
as object location.
|
| 57 |
|
| 58 |
There are two ways of dealing with points and lines in MGF. The
|
| 59 |
first is to say, "Hey, these are 0 and 1 dimensional entities, so
|
| 60 |
they won't appear in 3 dimensions," and get rid of them. The second
|
| 61 |
approach is to assign some user-specified dimension for the "width"
|
| 62 |
of points and lines, and turn them into spheres and cylinders. It
|
| 63 |
might be best to instead create minimal polyhedron analogs, such as
|
| 64 |
tetrahedra for points and triangular prisms for lines. That way, an
|
| 65 |
itty-bitty point won't be converted into 200 polygons because the
|
| 66 |
translator reading in the MGF file can't handle curved surfaces.
|
| 67 |
|
| 68 |
Polygons with Holes
|
| 69 |
===================
|
| 70 |
There is no explicit representation of holes in MGF. A hole must be
|
| 71 |
represented implicitly by connecting vertices to form "seams." For
|
| 72 |
example, a wall with a window in it might look like this:
|
| 73 |
|
| 74 |
v1.-----------------------------------------------.v4
|
| 75 |
| |
|
| 76 |
| v8.---------------.v5 |
|
| 77 |
| | | |
|
| 78 |
| | | |
|
| 79 |
| v7.---------------.v6 |
|
| 80 |
| |
|
| 81 |
| |
|
| 82 |
v2.-----------------------------------------------.v3
|
| 83 |
|
| 84 |
In many systems, the wall itself would be represented with the first
|
| 85 |
list of vertices, (v1,v2,v3,v4) and the hole associated with that
|
| 86 |
wall as a second set of vertices (v5,v6,v7,v8). In MGF, we must
|
| 87 |
give the whole thing as a single polygon, connecting the vertices so
|
| 88 |
as to create a "seam," thus:
|
| 89 |
|
| 90 |
v1.----------------------<------------------------.v4
|
| 91 |
| _____--><---'|
|
| 92 |
| v8.------->-------.v5 |
|
| 93 |
| | v |
|
| 94 |
v ^ | ^
|
| 95 |
| v7.-------<-------.v6 |
|
| 96 |
| |
|
| 97 |
| |
|
| 98 |
v2.---------------------->------------------------.v3
|
| 99 |
|
| 100 |
which could be written in MGF as "f v1 v2 v3 v4 v5 v6 v7 v8 v5 v4".
|
| 101 |
|
| 102 |
It is very important that the order of the hole be opposite to the
|
| 103 |
order of the outer perimeter, otherwise the polygon will be
|
| 104 |
"twisted" on top of itself. Note also that the seam was traversed
|
| 105 |
in both directions, once going from v4 to v5, and again returning
|
| 106 |
from v5 to v4. This is a necessary condition for a proper seam.
|
| 107 |
(The final edge from v4 back to v1 is implied in MGF.)
|
| 108 |
|
| 109 |
The choice of vertices to make into a seam is somewhat arbitrary, but
|
| 110 |
some rendering systems may not give sane results if you cross over a
|
| 111 |
hole with part of your seam. If we had chosen to create the seam
|
| 112 |
between v2 and v5 in the above example instead of v4 and v5, the seam
|
| 113 |
would cross our hole and may not render correctly. (For systems that
|
| 114 |
are sensitive to this, it is probably safest for their MGF
|
| 115 |
loader/translator re-expresses seams in terms of holes again, which can
|
| 116 |
be done easily so long as vertices are shared in the above fashion.)
|
| 117 |
|
| 118 |
Non-planar Polygons
|
| 119 |
===================
|
| 120 |
Polygons in MGF should be planar. There is nothing about the format
|
| 121 |
that enforces this, but the rendering or modeling software on the other
|
| 122 |
end may have real problems if this requirement is violated. The parser
|
| 123 |
itself does not test for non-planar polygons, so when in doubt about a
|
| 124 |
model, it is safest to test for planarity and break a polygon into triangles
|
| 125 |
if it is even slightly non-planar.
|
| 126 |
|
| 127 |
NURBS, CSG, Blobbies, Etc.
|
| 128 |
==========================
|
| 129 |
Sorry, folks, this is just plain hard. If and until MGF supports these
|
| 130 |
higher-order entities, it will be necessary for you to convert them to
|
| 131 |
smoothed triangle meshes. Fortunately, a lot of modeling software
|
| 132 |
already knows how to do this, so if you wrote the modeler, you probably
|
| 133 |
have access to the necessary code. (By the way, if you ever want to see
|
| 134 |
these primitives in MGF, you might just think about sharing the wealth,
|
| 135 |
because the MGF parser needs to mesh every primitive it supports.)
|
| 136 |
|
| 137 |
Materials
|
| 138 |
=========
|
| 139 |
The MGF material model was designed to accommodate most common
|
| 140 |
physical surfaces. Included are reasonable models for plastic
|
| 141 |
and metal, thin glass and translucent surfaces. Not included at
|
| 142 |
this time are surfaces with anisotropic reflection, refraction and/or
|
| 143 |
surface textures. These were deemed either unnecessary or too
|
| 144 |
difficult to standardize for the initial format. Also, light
|
| 145 |
sources are known only by the emissive nature of their surface(s),
|
| 146 |
and MGF itself only provides for diffuse emission. (As MGF is
|
| 147 |
destined to be part of the IES luminaire data standard, it was
|
| 148 |
assumed that this combined format would be used for such purposes as
|
| 149 |
describing light source output and geometry.)
|
| 150 |
|
| 151 |
The "sides" entity is used to control the number of sides a surface
|
| 152 |
should have. In the real world, a surface can have only one side,
|
| 153 |
defining the interface between one volume and another. Many
|
| 154 |
object-space rendering packages (e.g. z-buffer algorithms) take
|
| 155 |
advantage of this fact by culling back-facing polygons and thus saving
|
| 156 |
roughly 50% of the calculation time. However, many models rely on an
|
| 157 |
approximation whereby a single surface is used to represent a very thin
|
| 158 |
volume, such as a pane of glass, and this also can provide significant
|
| 159 |
calculational savings in an image-space algorithm (such as
|
| 160 |
ray-tracing). Since both types of surfaces are useful and both types
|
| 161 |
of rendering algorithms may ultimately be applied, MGF provides a way
|
| 162 |
to specify sidedness rather than picking one interpretation or the other.
|
| 163 |
|
| 164 |
So-called specular reflection and transmission are modeled using a
|
| 165 |
Gaussian distribution of surface normals. The "alpha_r" and
|
| 166 |
"alpha_t" parameters to the respective "rs" and "ts" entities specify
|
| 167 |
the root-mean-squared (RMS) surface facet slope, which varies from 0
|
| 168 |
for a perfectly smooth surface to around .2 for a fairly rough one.
|
| 169 |
The effect this will have on the reflected component distribution is
|
| 170 |
well-defined, but predicting the behavior of the transmitted
|
| 171 |
component requires further assumptions. We assume that the surface
|
| 172 |
scatters light passing through it just as much as it scatters
|
| 173 |
reflected light. This assumption is approximately correct for a
|
| 174 |
two-sided transparent material with an index of refraction of 1.5
|
| 175 |
(about that of glass) and both sides having the given RMS facet
|
| 176 |
slope.
|
| 177 |
|
| 178 |
Oftentimes, one is translating from a Phong exponent on the cosine
|
| 179 |
of the half-vector-to-normal angle to the more physical but less
|
| 180 |
familiar Gaussian model of MGF. The hardest part is translating
|
| 181 |
the specular power to a roughness value. For this, we recommend
|
| 182 |
the following approximation:
|
| 183 |
|
| 184 |
roughness = 0.6/sqrt(specular_power)
|
| 185 |
|
| 186 |
It's not a perfect correlation, but it's about as good as you can get.
|
| 187 |
|
| 188 |
Colors
|
| 189 |
======
|
| 190 |
Unlike most graphics languages, MGF does not use an RGB color model,
|
| 191 |
simply because there is no recognized definition for this model.
|
| 192 |
It is based on computer monitor phosphors, which vary from one
|
| 193 |
CRT to the next. (There is an RGB standard defined in the TV
|
| 194 |
industry, but this has a rather poor correlation to most computer
|
| 195 |
monitors.)
|
| 196 |
|
| 197 |
MGF uses two alternative, well-defined standards. The first is the CIE
|
| 198 |
standard xy chromaticity coordinates. With this standard, any viewable
|
| 199 |
color may be exactly reproduced. Unfortunately, the interaction between
|
| 200 |
colors (i.e. colored light sources and interreflections) cannot be
|
| 201 |
specified exactly with any finite coordinate set, including CIE
|
| 202 |
chromaticities. So, MGF offers the ability to give reflectance,
|
| 203 |
transmittance or emittance as a function of wavelength over the visible
|
| 204 |
spectrum. This function is still discretized, but at a user-selectable
|
| 205 |
resolution. Furthermore, spectral colors may be mixed, providing (nearly)
|
| 206 |
arbitrary basis functions, which can produce more accurate results in
|
| 207 |
some cases and are merely a convenience for translation in others.
|
| 208 |
|
| 209 |
Conversion back and forth between CIE chromaticity coordinates and spectral
|
| 210 |
samples is provided within the MGF parser. Unfortunately, conversion
|
| 211 |
to and from RGB values depends on a particular RGB definition, and as we
|
| 212 |
have said, there is no recognized standard. We therefore recommend that
|
| 213 |
you decide yourself what chromaticity values to use for each RGB primary,
|
| 214 |
and adopt the following code to convert between CIE and RGB coordinates.
|
| 215 |
|
| 216 |
#ifdef NTSC
|
| 217 |
#define CIE_x_r 0.670 /* standard NTSC primaries */
|
| 218 |
#define CIE_y_r 0.330
|
| 219 |
#define CIE_x_g 0.210
|
| 220 |
#define CIE_y_g 0.710
|
| 221 |
#define CIE_x_b 0.140
|
| 222 |
#define CIE_y_b 0.080
|
| 223 |
#define CIE_x_w 0.3333 /* monitor white point */
|
| 224 |
#define CIE_y_w 0.3333
|
| 225 |
#else
|
| 226 |
#define CIE_x_r 0.640 /* nominal CRT primaries */
|
| 227 |
#define CIE_y_r 0.330
|
| 228 |
#define CIE_x_g 0.290
|
| 229 |
#define CIE_y_g 0.600
|
| 230 |
#define CIE_x_b 0.150
|
| 231 |
#define CIE_y_b 0.060
|
| 232 |
#define CIE_x_w 0.3333 /* monitor white point */
|
| 233 |
#define CIE_y_w 0.3333
|
| 234 |
#endif
|
| 235 |
|
| 236 |
#define CIE_D ( CIE_x_r*(CIE_y_g - CIE_y_b) + \
|
| 237 |
CIE_x_g*(CIE_y_b - CIE_y_r) + \
|
| 238 |
CIE_x_b*(CIE_y_r - CIE_y_g) )
|
| 239 |
#define CIE_C_rD ( (1./CIE_y_w) * \
|
| 240 |
( CIE_x_w*(CIE_y_g - CIE_y_b) - \
|
| 241 |
CIE_y_w*(CIE_x_g - CIE_x_b) + \
|
| 242 |
CIE_x_g*CIE_y_b - CIE_x_b*CIE_y_g ) )
|
| 243 |
#define CIE_C_gD ( (1./CIE_y_w) * \
|
| 244 |
( CIE_x_w*(CIE_y_b - CIE_y_r) - \
|
| 245 |
CIE_y_w*(CIE_x_b - CIE_x_r) - \
|
| 246 |
CIE_x_r*CIE_y_b + CIE_x_b*CIE_y_r ) )
|
| 247 |
#define CIE_C_bD ( (1./CIE_y_w) * \
|
| 248 |
( CIE_x_w*(CIE_y_r - CIE_y_g) - \
|
| 249 |
CIE_y_w*(CIE_x_r - CIE_x_g) + \
|
| 250 |
CIE_x_r*CIE_y_g - CIE_x_g*CIE_y_r ) )
|
| 251 |
|
| 252 |
#define CIE_rf (CIE_y_r*CIE_C_rD/CIE_D)
|
| 253 |
#define CIE_gf (CIE_y_g*CIE_C_gD/CIE_D)
|
| 254 |
#define CIE_bf (CIE_y_b*CIE_C_bD/CIE_D)
|
| 255 |
|
| 256 |
float xyz2rgbmat[3][3] = { /* XYZ to RGB */
|
| 257 |
{(CIE_y_g - CIE_y_b - CIE_x_b*CIE_y_g + CIE_y_b*CIE_x_g)/CIE_C_rD,
|
| 258 |
(CIE_x_b - CIE_x_g - CIE_x_b*CIE_y_g + CIE_x_g*CIE_y_b)/CIE_C_rD,
|
| 259 |
(CIE_x_g*CIE_y_b - CIE_x_b*CIE_y_g)/CIE_C_rD},
|
| 260 |
{(CIE_y_b - CIE_y_r - CIE_y_b*CIE_x_r + CIE_y_r*CIE_x_b)/CIE_C_gD,
|
| 261 |
(CIE_x_r - CIE_x_b - CIE_x_r*CIE_y_b + CIE_x_b*CIE_y_r)/CIE_C_gD,
|
| 262 |
(CIE_x_b*CIE_y_r - CIE_x_r*CIE_y_b)/CIE_C_gD},
|
| 263 |
{(CIE_y_r - CIE_y_g - CIE_y_r*CIE_x_g + CIE_y_g*CIE_x_r)/CIE_C_bD,
|
| 264 |
(CIE_x_g - CIE_x_r - CIE_x_g*CIE_y_r + CIE_x_r*CIE_y_g)/CIE_C_bD,
|
| 265 |
(CIE_x_r*CIE_y_g - CIE_x_g*CIE_y_r)/CIE_C_bD}
|
| 266 |
};
|
| 267 |
|
| 268 |
float rgb2xyzmat[3][3] = { /* RGB to XYZ */
|
| 269 |
{CIE_x_r*CIE_C_rD/CIE_D,CIE_x_g*CIE_C_gD/CIE_D,CIE_x_b*CIE_C_bD/CIE_D},
|
| 270 |
{CIE_y_r*CIE_C_rD/CIE_D,CIE_y_g*CIE_C_gD/CIE_D,CIE_y_b*CIE_C_bD/CIE_D},
|
| 271 |
{(1.-CIE_x_r-CIE_y_r)*CIE_C_rD/CIE_D,
|
| 272 |
(1.-CIE_x_g-CIE_y_g)*CIE_C_gD/CIE_D,
|
| 273 |
(1.-CIE_x_b-CIE_y_b)*CIE_C_bD/CIE_D}
|
| 274 |
};
|
| 275 |
|
| 276 |
|
| 277 |
cie_rgb(rgbcolor, ciecolor) /* convert CIE to RGB */
|
| 278 |
register float *rgbcolor, *ciecolor;
|
| 279 |
{
|
| 280 |
register int i;
|
| 281 |
|
| 282 |
for (i = 0; i < 3; i++) {
|
| 283 |
rgbcolor[i] = xyz2rgbmat[i][0]*ciecolor[0] +
|
| 284 |
xyz2rgbmat[i][1]*ciecolor[1] +
|
| 285 |
xyz2rgbmat[i][2]*ciecolor[2] ;
|
| 286 |
if (rgbcolor[i] < 0.0)
|
| 287 |
rgbcolor[i] = 0.0;
|
| 288 |
}
|
| 289 |
}
|
| 290 |
|
| 291 |
|
| 292 |
rgb_cie(ciecolor, rgbcolor) /* convert RGB to CIE */
|
| 293 |
register float *ciecolor, *rgbcolor;
|
| 294 |
{
|
| 295 |
register int i;
|
| 296 |
|
| 297 |
for (i = 0; i < 3; i++)
|
| 298 |
ciecolor[i] = rgb2xyzmat[i][0]*rgbcolor[0] +
|
| 299 |
rgb2xyzmat[i][1]*rgbcolor[1] +
|
| 300 |
rgb2xyzmat[i][2]*rgbcolor[2] ;
|
| 301 |
}
|
| 302 |
|
| 303 |
An alternative to adopting the above code is to use the MGF "cmix"
|
| 304 |
entity to convert from RGB directly by naming the three primaries in
|
| 305 |
terms of their chromaticities, e.g:
|
| 306 |
|
| 307 |
c r =
|
| 308 |
cxy 0.640 0.330
|
| 309 |
c g =
|
| 310 |
cxy 0.290 0.600
|
| 311 |
c b =
|
| 312 |
cxy 0.150 0.060
|
| 313 |
|
| 314 |
Then, converting from RGB to MGF colors is as simple as multiplying each
|
| 315 |
component by its relative luminance in a cmix statement, for instance:
|
| 316 |
|
| 317 |
c white =
|
| 318 |
cmix 0.265 r 0.670 g 0.065 b
|
| 319 |
|
| 320 |
For the chosen RGB standard, the above specification would result a pure
|
| 321 |
white. The reason the coefficients are not all 1 as you might expect is
|
| 322 |
that cmix uses relative luminance as the standard for its weights. Since
|
| 323 |
blue is less luminous for the same energy than red, which is in turn
|
| 324 |
less luminous than green, the weights cannot be the same to achieve an
|
| 325 |
even spectral balance. Unfortunately, computing these relative weights
|
| 326 |
is not straightforward, though it is given in the above macros as CIE_rf,
|
| 327 |
CIE_gf and CIE_bf. (The common factors in these macros may of course
|
| 328 |
be removed for simplification purposes.)
|