1 
2 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *\
3  *                                                                         *
4  *                             FLEXIBLE.H                                  *
5  *                             by Melinda Green                            *
6  *                             melinda@superliminal.com                    *
7  *                             Oct. 1996                                   *
8  *                                                                         *
9  * RESTRICTIONS:                                                           *
10  *    None at all. Use this code for anything you like. Enjoy.             *
11  *                                                                         *
12  * DESCRIPTION:                                                            *
13  *    The "Flexible" class provides a clean wrapper over some D3D          *
14  *    immediate mode functionality that allows the programmer to easily    *
15  *    implement a retained mode object with dynamic vertices.              *
16  *    The resulting object is very nearly a first-class retained mode      *
17  *    object but for the fact that the real object needs to be created     *
18  *    within the driver. The difference then is that the real user-visual  *
19  *    object is maintained inside the flexible object via deligation.      *
20  *    The GetVisual method returns a pointer to that object which can be   *
21  *    placed in any D3DRM scene.                                           *
22  *                                                                         *
23  *    All one needs to do to create a custom flexible object is to         *
24  *    subclass from it and override the UpdateVertexData method. That      *
25  *    method is called durring display traversal and is passed an          *
26  *    array of D3DVERTEX into which it writes the vertex positions and     *
27  *    normals. All vertices must be loaded on the first call, but          *
28  *    subsequent calls only need to update any changed vertices.           *
29  *                                                                         *
30  *    Logically, the primative contains a list of triangle blocks of       *
31  *    identical topology. For example, the "fire" object in the original   *
32  *    "uvis" sample was easily reimplemented as a list of "flame" blocks   *
33  *    where each block contains 8 vertices and 4 triangles connected the   *
34  *    same way. They differ from each other only by the positions of their *
35  *    vertices. Blocks of blocks can also have different colors.           *
36  *                                                                         *
37  *    A Pick method is also provided which can be used to determine which  *
38  *    block and triangle lies under a given pixel in a view. This alone is *
39  *    is a very useful feature even if movable vertices are not needed.    *
40  *                                                                         *
41  *    A utility method CalculateFlatNormals is provided which will update  *
42  *    the normals for you for flat shaded objects as long as the vertices  *
43  *    are arranged according to these simple rules:                        *
44  *       1) No two non-parallel triangles within a block may begin with    *
45  *          the same index.                                                *
46  *       2) parallel triangles should be grouped consecutively.            *
47  *                                                                         *
48  *    The first rule is important because D3D takes face normals from      *
49  *    vertices instead of from the faces themselves. Don't ask me why.     *
50  *    That means that only one normal can be associated with a vertex.     *
51  *    For flat shading, it takes the face normal from the first vertex of  *
52  *    each triangle which is therefore what CalculateFlatNormals computes. *
53  *                                                                         *
54  *    The second rule is not strict, but is a good idea because            *
55  *    CalculateFlatNormals can trivially skip recalculating the same       *
56  *    normals over again which it would otherwise do.                      *
57  *                                                                         *
58  *    For smooth shaded objects you must generate your own normals. This   *
59  *    is because there is no "correct" way to generate vertex normals from *
60  *    face data alone. There are several good methods in the literature,   *
61  *    but the correct one for you will depend upon your data and           *
62  *    application. Hopefully, the correct normals will fall out of your    *
63  *    vertex computation such as happens with parametric surface           *
64  *    tesselation.                                                         *
65  *                                                                         *
66  * BUGS:                                                                   *
67  *    At least under the DxII final version, using a user-visual object    *
68  *    as a source of shadow does make a proper shadow, but seems to cause  *
69  *    the object itself not to render.                                     *
70  *                                                                         *
71  *    In some instances, clipping of these objects cause crashing even     *
72  *    though clipping is always enabled.                                   *
73  *                                                                         *
74  *    I was going to add a feature allowing the app to set a visibility    *
75  *    flag for each block, but the OP_BRANCH_FORWARD operation doesn't     *
76  *    seem to work. Therefore, the Flexible subclasses will have to        *
77  *    implement that effect themselves if desired. One reasonable way to   *
78  *    do that is to set the coordinates of the vertices of all blocks      *
79  *    meant to be invisible to be the same as the first visible vertex,    *
80  *    or perhaps any point within the visible bounding box of the object.  *
81  *    That might not be the cleanest solution, but if most blocks are      *
82  *    visible, the performance penalty should be very slight.              *
83  *                                                                         *
84 \* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
85 
86 #ifndef FLEXIBLE_H
87 #define FLEXIBLE_H
88 
89 #include "d3d.h"
90 #include "d3dmacs.h"
91 #include "d3drm.h"
92 
93 class Flexible
94 {
95   public:
96     Flexible (LPDIRECT3DRMDEVICE d, LPDIRECT3DRM lpd3drm, int n_verts,
97               int verts_per_block, int n_blocks, int tris_per_block,
98               const int tri_block[][3], int n_colors,
99               const double colors[][3], int blocks_per_color,
100               int flat_shade);
101     virtual ~ Flexible ();
GetVisual()102     IDirect3DRMUserVisual *GetVisual()
103         {
104             return uvis;
105         }
106     virtual int UpdateVertexData(D3DVERTEX v[]) = 0;
107     BOOL        Render(LPDIRECT3DRMVIEWPORT view);
108     int         Pick(LPDIRECT3DRMVIEWPORT view, long x, long y,
109                      int *blockptr, int *triptr);
110     void        ComputeFlatNormals(D3DVERTEX v[], int verts_per_block,
111                                    int n_blocks, int tris_per_block,
112                                    const int tri_block[][3]);
113 
114   private:
115     LPDIRECT3DDEVICE m_d3ddev;
116     LPDIRECT3DEXECUTEBUFFER eb;
117     int         n_mats, m_state_setup_size, m_block_size, m_color_block_size;
118     LPDIRECT3DMATERIAL *mats;
119     LPDIRECT3DRMUSERVISUAL uvis;
120 };
121 
122 #endif
123