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