1 |
greg |
1.1 |
TRANSLATING TO MGF FROM OTHER FORMATS |
2 |
greg |
1.3 |
RCSid "$Id$" |
3 |
greg |
1.1 |
|
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 |
greg |
1.2 |
therefore the parser's memory for other objects. In the second case, |
36 |
greg |
1.1 |
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 |
greg |
1.2 |
as object location. |
57 |
greg |
1.1 |
|
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 |
greg |
1.2 |
are sensitive to this, it is probably safest for their MGF |
115 |
greg |
1.1 |
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 |
greg |
1.2 |
model, it is safest to test for planarity and break a polygon into triangles |
125 |
greg |
1.1 |
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 |
greg |
1.2 |
to specify sidedness rather than picking one interpretation or the other. |
163 |
greg |
1.1 |
|
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 |
greg |
1.2 |
industry, but this has a rather poor correlation to most computer |
195 |
|
|
monitors.) |
196 |
greg |
1.1 |
|
197 |
|
|
MGF uses two alternative, well-defined standards. The first is the CIE |
198 |
greg |
1.2 |
standard xy chromaticity coordinates. With this standard, any viewable |
199 |
greg |
1.1 |
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 |
greg |
1.2 |
some cases and are merely a convenience for translation in others. |
208 |
greg |
1.1 |
|
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 |
greg |
1.2 |
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 |
greg |
1.1 |
|
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 |
greg |
1.2 |
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.) |