1
2 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
3 * *
4 * FLEXIBLE.CPP *
5 * by Melinda Green *
6 * melinda@superliminal.com *
7 * Oct. 1996 *
8 * *
9 * DESCRIPTION: *
10 * Implements the Flexible class. *
11 * *
12 \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
13
14 #include <math.h>
15 #include "Flexible.h"
16 #include "Vec.h"
17 #include <assert.h>
18
19 /*
20 * UserVisual callback which directs control back to the Flexible
21 * object being rendered.
22 */
23 static int
FlexibleCallback(LPDIRECT3DRMUSERVISUAL uvis,void * arg,D3DRMUSERVISUALREASON reason,LPDIRECT3DRMDEVICE,LPDIRECT3DRMVIEWPORT view)24 FlexibleCallback(LPDIRECT3DRMUSERVISUAL uvis,
25 void *arg,
26 D3DRMUSERVISUALREASON reason, LPDIRECT3DRMDEVICE /* dev */ ,
27 LPDIRECT3DRMVIEWPORT view)
28 {
29 Flexible *flexi = (Flexible *)arg;
30
31 if (reason == D3DRMUSERVISUAL_CANSEE)
32 return TRUE; // could be smarter if given bounding volume
33
34 if (reason == D3DRMUSERVISUAL_RENDER)
35 {
36 if (flexi->Render(view))
37 return D3D_OK;
38 else
39 return DDERR_GENERIC;
40 }
41
42 return 0;
43 }
44
45
46 /*
47 * Called by D3D when the UserVisual's reference reaches zero.
48 * It deletes the containing Flexible object which will call
49 * any derived class' destructor allowing it to free any resources
50 * it may have allocated.
51 */
52 static void
FlexibleDestroyCB(LPDIRECT3DRMOBJECT obj,void * arg)53 FlexibleDestroyCB(LPDIRECT3DRMOBJECT obj, void *arg)
54 {
55 Flexible *flexi = (Flexible *)arg;
56 delete flexi;
57 }
58
59
60 /*
61 * The base class' destructor.
62 */
~Flexible()63 Flexible:: ~Flexible ()
64 {
65 RELEASE(eb);
66 if (mats)
67 {
68 for (int m = 0; m < n_mats; m++)
69 RELEASE(mats[m]);
70 delete[] mats;
71 }
72 RELEASE(m_d3ddev);
73 }
74
75
76 typedef struct
77 {
78 D3DINSTRUCTION op_set_status;
79 D3DSTATUS set_defaults;
80 D3DINSTRUCTION op_state_render;
81 D3DSTATE shade_mode;
82 D3DINSTRUCTION exit1;
83 }
84 OPData; // all but the vertex and triangle data
85
86 static HRESULT __stdcall
validate_cb(void * arg,DWORD offset)87 validate_cb(void *arg, DWORD offset)
88 {
89 *((DWORD *)arg) = offset;
90 return 0;
91 }
92
Flexible(LPDIRECT3DRMDEVICE dev,LPDIRECT3DRM lpd3drm,int n_verts,int verts_per_block,int n_blocks,int tris_per_block,const int tri_block[][3],int n_colors,const double colors[][3],int blocks_per_color,int flat_shade)93 Flexible::Flexible (LPDIRECT3DRMDEVICE dev, LPDIRECT3DRM lpd3drm,
94 int n_verts, int verts_per_block, int n_blocks,
95 int tris_per_block, const int tri_block[][3],
96 int n_colors, const double colors[][3],
97 int blocks_per_color, int flat_shade)
98 {
99 eb = NULL;
100 uvis = NULL;
101 n_mats = n_colors;
102 void *p;
103 void *p_start, *v_end;
104 long e_end;
105 DWORD error_offset = 0;
106 // DWORD cb_arg; // would have been used by Validate. See below
107
108 // Compute a few useful constants
109 //
110 int n_tris = n_blocks * tris_per_block;
111 int verts_per_color = verts_per_block * blocks_per_color;
112 int tris_per_color = tris_per_block * blocks_per_color;
113
114 D3DEXECUTEBUFFERDESC desc;
115 D3DEXECUTEDATA data;
116 LPDIRECT3D lpD3D = NULL;
117 int c, block, block_offset = 0;
118
119 dev->GetDirect3DDevice(&m_d3ddev);
120 if (!m_d3ddev)
121 goto generic_error;
122 if (FAILED(m_d3ddev->GetDirect3D(&lpD3D)))
123 goto generic_error;
124
125 // create the user visual object
126 //
127 if (FAILED
128 (lpd3drm->CreateUserVisual(FlexibleCallback, (void *)this, &uvis)))
129 goto generic_error;
130 if (FAILED(uvis->AddDestroyCallback(FlexibleDestroyCB, (void *)this)))
131 goto generic_error;
132
133 // Create the Execute Buffer
134 //
135 desc.dwSize = sizeof(desc);
136 desc.dwFlags = D3DDEB_BUFSIZE;
137 desc.dwBufferSize = sizeof(OPData) + // mode setting stuff, etc.
138 n_verts * sizeof(D3DVERTEX) + // the vertices
139 n_tris * sizeof(D3DTRIANGLE) + // the triangle indices
140 n_blocks * sizeof(D3DINSTRUCTION) + // triangle lists
141 n_colors * (sizeof(D3DINSTRUCTION) + sizeof(D3DSTATE)) +
142 n_colors * (sizeof(D3DINSTRUCTION) + sizeof(D3DPROCESSVERTICES));
143 if (FAILED(m_d3ddev->CreateExecuteBuffer(&desc, &eb, NULL)))
144 goto generic_error;
145
146 // Create the materials
147 //
148 mats = new LPDIRECT3DMATERIAL[n_colors];
149 for (c = 0; c < n_colors; c++)
150 {
151 if (FAILED(lpD3D->CreateMaterial(&mats[c], NULL)))
152 goto generic_error;
153 D3DMATERIAL color;
154 memset(&color, 0, sizeof(color));
155 color.dwSize = sizeof(color);
156 color.diffuse.r = color.ambient.r = D3DVAL (colors[c][0]);
157 color.diffuse.g = color.ambient.g = D3DVAL (colors[c][1]);
158 color.diffuse.b = color.ambient.b = D3DVAL (colors[c][2]);
159 color.dwRampSize = 32;
160 if (FAILED(mats[c]->SetMaterial(&color)))
161 goto generic_error;
162 }
163
164 // Lock the execute buffer and mark the following offsets:
165 // p_start = the beginning of the buffer
166 // p = the beginning of the non-vertex data (incremented)
167 // v_end = first address of the non-vertex data
168 // e_end = end of buffer I.E. expected ending value for p
169 //
170 if (FAILED(eb->Lock(&desc)))
171 goto generic_error;
172 p = (void *)((char *)desc.lpData + n_verts * sizeof(D3DVERTEX));
173 p_start = desc.lpData;
174 v_end = p;
175 e_end = (long)desc.lpData + desc.dwBufferSize; // expected end
176
177 // load defaults
178 //
179 OP_SET_STATUS(D3DSETSTATUS_ALL, D3DSTATUS_DEFAULT, 2048, 2048, 0, 0, p);
180
181 // load vertices
182 // note that they are colored in verts_per_color sets
183 //
184 for (c = 0; c < n_colors; c++)
185 {
186 D3DMATERIALHANDLE hMat;
187 if (FAILED(mats[c]->GetHandle(m_d3ddev, &hMat)))
188 goto generic_error;
189 OP_STATE_LIGHT(1, p);
190 STATE_DATA(D3DLIGHTSTATE_MATERIAL, hMat, p);
191 OP_PROCESS_VERTICES(1, p);
192 PROCESSVERTICES_DATA(D3DPROCESSVERTICES_TRANSFORMLIGHT,
193 c * verts_per_color, verts_per_color, p);
194 }
195
196 // set the shading mode
197 //
198 OP_STATE_RENDER(1, p);
199 STATE_DATA(D3DRENDERSTATE_SHADEMODE,
200 flat_shade ? D3DSHADE_FLAT : D3DSHADE_GOURAUD, p);
201
202 // casche some constants needed during picking
203 //
204 m_state_setup_size = (long)p - (long)v_end;
205 m_block_size = sizeof(D3DINSTRUCTION) +
206 tris_per_block * sizeof(D3DTRIANGLE);
207
208 // add all the triangle blocks
209 //
210 for (block = 0; block < n_blocks; block++)
211 {
212 // OP_BRANCH_FORWARD(... too bad this feature doesn't work
213 OP_TRIANGLE_LIST(tris_per_block, p);
214 for (int tri = 0; tri < tris_per_block; tri++)
215 {
216 D3DTRIANGLE *t = (D3DTRIANGLE *)p;
217 t->v1 = block * verts_per_block + tri_block[tri][0];
218 t->v2 = block * verts_per_block + tri_block[tri][1];
219 t->v3 = block * verts_per_block + tri_block[tri][2];
220 t->wFlags = D3DTRIFLAG_EDGEENABLETRIANGLE;
221 t++;
222 p = (char *)t;
223 }
224 }
225
226 OP_EXIT(p); // end of data marker
227
228 assert((long)p == e_end); // make sure buffer loaded as expected
229
230 // give the driver back control of the data contents
231 //
232 if (FAILED(eb->Unlock()))
233 goto generic_error;
234
235 // tell the driver how the buffer data is layed out
236 //
237 data.dwSize = sizeof(data);
238 data.dwVertexOffset = 0;
239 data.dwVertexCount = n_verts;
240 data.dwInstructionOffset = n_verts * sizeof(D3DVERTEX);
241 data.dwInstructionLength = desc.dwBufferSize - data.dwInstructionOffset;
242 data.dwHVertexOffset = 0;
243 if (FAILED(eb->SetExecuteData(&data)))
244 goto generic_error;
245
246 // The ExecuteBuffer::Validate call seems to always return
247 // garbage. Also, it won't accept a NULL callback argument
248 // like the documentation says it should.
249 //
250 /*
251 if (FAILED(eb->Validate(&error_offset, validate_cb, &cb_arg, 0)));
252 goto generic_error; if(error_offset == 0) goto generic_error;
253 if(cb_arg == 0) goto generic_error; */
254
255 RELEASE(lpD3D);
256 return;
257 generic_error:
258 RELEASE(lpD3D);
259 RELEASE(m_d3ddev);
260 // RELEASE(mat); need to release all mats and delete array
261 RELEASE(eb);
262 RELEASE(uvis);
263 return;
264 }
265
266 // Determines whether this object is under the given pixel in
267 // the given view, and if so, returns the index of the closest
268 // block under that point, and the index of the triangle hit
269 // within that block.
270 //
Pick(LPDIRECT3DRMVIEWPORT rmview,long x,long y,int * blockptr,int * triptr)271 int Flexible::Pick(LPDIRECT3DRMVIEWPORT rmview, long x, long y,
272 int *blockptr, int *triptr)
273 {
274 // get the immediate mode view from the retained mode viewport
275 //
276 LPDIRECT3DVIEWPORT view = NULL;
277 rmview->GetDirect3DViewport(&view);
278
279 // initialize an infinitely thin pick region
280 //
281 D3DRECT where;
282 where.x1 = where.x2 = x;
283 where.y1 = where.y2 = y;
284
285 // perform a pick on the object's execute buffer
286 //
287 DWORD dummy = 0; // initialization just stop the warning
288 m_d3ddev->Pick(eb, view, dummy, &where);
289
290 // make sure *some* part of the object was hit.
291 // Presumably the app did a retained mode pick first and
292 // is calling this routine for further information, but
293 // let's not make any assumptions.
294 //
295 unsigned long hits, i;
296 m_d3ddev->GetPickRecords(&hits, NULL);
297 if (0 == hits)
298 return 0;
299
300 // get all the hit data from the driver, and find
301 // the frontmost record. Seems like that would be
302 // a useful instruction to add to the driver API,
303 // but for now, this is the only way.
304 // NOTE: the speed of memory allocation and deallocation
305 // used here should not be a problem since most all
306 // picks are generated by some user action and is therefore
307 // probably not super time critical.
308 //
309 D3DPICKRECORD *recs = new D3DPICKRECORD[hits];
310 m_d3ddev->GetPickRecords(&hits, recs);
311 D3DVALUE front_z = D3DVAL (8888888.8);
312 int front_id;
313 for (i = 0; i < hits; i++)
314 if (recs[i].dvZ < front_z)
315 {
316 front_z = recs[i].dvZ;
317 front_id = i;
318 }
319 int eboff = recs[front_id].dwOffset;
320 delete[] recs;
321
322 // tri_offset is position of hit triangle from first trilist
323 //
324 int tri_offset = eboff - m_state_setup_size;
325 *blockptr = tri_offset / m_block_size;
326 *triptr = (tri_offset % m_block_size) - 1;
327 RELEASE(view);
328 return hits;
329 }
330
331
332 /*
333 * Called indirectly by the render driver during rendering,
334 * it first gives the derived class a chance to update it's vertices
335 * then it executes the buffer and flushes the results.
336 */
Render(LPDIRECT3DRMVIEWPORT view)337 BOOL Flexible::Render(LPDIRECT3DRMVIEWPORT view)
338 {
339 D3DVERTEX *v;
340 D3DEXECUTEBUFFERDESC desc;
341 D3DEXECUTEDATA data;
342 LPDIRECT3DVIEWPORT lpD3DView = NULL;
343
344 view->GetDirect3DViewport(&lpD3DView);
345 if (!lpD3DView)
346 goto ret_with_error;
347
348 desc.dwSize = sizeof(desc);
349 desc.dwFlags = 0;
350
351 if (FAILED(eb->Lock(&desc)))
352 goto ret_with_error;
353 v = (D3DVERTEX *)desc.lpData;
354
355 UpdateVertexData(v); // this is where derived classes move the
356 // verts
357
358 if (FAILED(eb->Unlock()))
359 goto ret_with_error;
360
361 if (FAILED(m_d3ddev->Execute(eb, lpD3DView, D3DEXECUTE_CLIPPED)))
362 goto ret_with_error;
363
364 data.dwSize = sizeof data;
365 if (FAILED(eb->GetExecuteData(&data)))
366 goto ret_with_error;
367 if (FAILED(view->ForceUpdate(data.dsStatus.drExtent.x1,
368 data.dsStatus.drExtent.y1,
369 data.dsStatus.drExtent.x2,
370 data.dsStatus.drExtent.y2)))
371 {
372 goto ret_with_error;
373 }
374
375 RELEASE(lpD3DView);
376 return TRUE;
377
378 ret_with_error:
379 RELEASE(lpD3DView);
380 return FALSE;
381 }
382
383
384 #define NORMALIZE_VEC3(norm, vec) \
385 { \
386 double len = sqrt(NORMSQRD3(norm)); \
387 VDS3(norm, norm, len); \
388 }
389
390 /*
391 * This is a simple utility method which can be called from UpdateVertexData
392 * to compute face normals when flat shading.
393 */
ComputeFlatNormals(D3DVERTEX v[],int verts_per_block,int n_blocks,int tris_per_block,const int tri_block[][3])394 void Flexible::ComputeFlatNormals(D3DVERTEX v[], int verts_per_block,
395 int n_blocks, int tris_per_block,
396 const int tri_block[][3])
397 {
398 int i0, last_i0 = -1;
399 for (int b = 0; b < n_blocks; b++)
400 {
401 int off = b * verts_per_block; // offset into vertex list
402 for (int t = 0; t < tris_per_block; t++)
403 {
404 i0 = tri_block[t][0];
405 if (i0 == last_i0)
406 continue; // test is just to reduce redundancy
407 int i1 = tri_block[t][1];
408 int i2 = tri_block[t][2];
409 double v0[3], v1[3], v2[3], v01[3], v02[3], norm[3];
410 v0[0] = v[off + i0].x;
411 v0[1] = v[off + i0].y;
412 v0[2] = v[off + i0].z;
413 v1[0] = v[off + i1].x;
414 v1[1] = v[off + i1].y;
415 v1[2] = v[off + i1].z;
416 v2[0] = v[off + i2].x;
417 v2[1] = v[off + i2].y;
418 v2[2] = v[off + i2].z;
419 VMV3(v01, v1, v0); // vector from 0 to 1
420 VMV3(v02, v2, v0); // vector from 0 to 2
421 VXV3(norm, v01, v02); // cross product is normal to both
422 NORMALIZE_VEC3(norm, v[v0]); // make into unit vector
423 v[off + i0].nx = D3DVAL (norm[0]);
424 v[off + i0].ny = D3DVAL (norm[1]);
425 v[off + i0].nz = D3DVAL (norm[2]);
426 last_i0 = i0;
427 }
428 }
429 }
430