THREED.C
[目次 | 関数 | マクロ]
1|/***********************************************************************
2|* 1. <<< 浮動小数版・三次元グラフィック関連 [ThreeD] >>>
3|*
4|*
5|*
6|*・「視線」は、視点から物体への半直線(片方が無限遠)です。
7|*・「三次元ワールド座標」は、三次元モデルの世界の座標です。
8|*・「視点座標」は、視点の位置を原点(0,0)とする座標です。
9|*・「二次元ワールド座標」は、視点座標の x,y を透視変換した座標です。
10|* z は、視点座標と同じです。視線上にあるモデルは (0,0) になります。
11|*・三次元ワールド座標から視点座標の変換を、一般に「三次元座標変換」と呼びます。
12|*・視点座標から二次元ワールド座標の変換を、一般に「透視変換」といい、これに
13|* よって、遠近法が表現されます。
14|************************************************************************/
15|#define STDLIBS_INCLUDE
16|#define STDLIBS_INCLUDE_STDIO_H
17|#define STDLIBS_INCLUDE_LIMITS_H
18|#define STDLIBS_INCLUDE_MATH_H
19|#ifdef USES_MXP_AUTOINC
20| #define MATH_NAMESPC_PREFIX 0
21| #include "threed.ah" /* Auto include header, Look at mixer-... folder */
22|#else
23| #include <all.h>
24|#endif
25|
26|
27|
28|/***********************************************************************
29|* 2. <<< まるめ端数 >>>
30|************************************************************************/
31|double ThreeD_err = 0.001;
32|
33|
34|
35|/*-----------------------------------------------------------------------*/
36|/* 3. <<< ◆三次元の座標値(double型) (ThreeD_XYZ) >>> */
37|/*-----------------------------------------------------------------------*/
38|
39|
40|
41|/***********************************************************************
42|* 4. <<< [ThreeD_XYZ_init] 初期化する >>>
43|************************************************************************/
44|void ThreeD_XYZ_init( ThreeD_XYZ* m, double x, double y, double z )
45|{
46| m->x = x;
47| m->y = y;
48| m->z = z;
49|}
50|
51|
52|
53|/* #ifdef USES_THREED_VIEWL */
54|/***********************************************************************
55|* 5. <<< [ThreeD_XYZ_get2D] 二次元ワールド座標へ変換する ★>>>
56|*【引数】
57|* ・ThreeD_XYZ* m; ある点の三次元ワールド座標値(入力)
58|* ・ThreeD_ViewL* view; 視線
59|* ・ThreeD_XYZ* out; ある点を二次元ワールド座標に変換した値(出力)
60|*【補足】
61|*・m と out が同じアドレスでも構いません。
62|*・透視変換は、以下のように改良しています。
63|*
64|*・仮想視点は、仮想ユーザ(画面の中にいる仮の人)の視点です。
65|*・ユーザ視点は、ユーザ(画面を見ている人)の視点です。
66|*・もし遠近の歪みがひどい場合、視点を遠く( diffU を大きく)すること。
67|************************************************************************/
68|void ThreeD_XYZ_get2D( ThreeD_XYZ* m, ThreeD_ViewL* view, ThreeD_XYZ* out )
69|{
70| ThreeD_XYZ p;
71|
72| p = *m;
73|
74| /* 三次元座標変換(三次元ワールド座標 p を視点座標 out に変換する) */
75| /* 投影変換とも言う*/
76| {
77| /* 視点が原点になるように平行移動変換する */
78| p.x -= view->p.x;
79| p.y -= view->p.y;
80| p.z -= view->p.z;
81|
82| /* p を視線の方向に回転変換する */
83| ThreeD_XYZ_rotR( &p, &view->dir, &p );
84|
85| /* x,y,z の関係を合わせる */
86| out->x = -p.y; out->y = -p.z; out->z = p.x;
87| }
88|
89| /* 透視変換(遠近法、視点座標を二次元ワールド座標に変換する) */
90| /*(比率を定数にして、乗算をシフト演算にすれば高速にできる) */
91| if ( out->z > ThreeD_err || out->z < -ThreeD_err ) {
92| out->x = out->x * view->diffU / (out->z + view->diffV + view->diffU);
93| out->y = out->y * view->diffU / (out->z + view->diffV + view->diffU);
94| }
95| else {
96| #ifndef NDEBUG
97| error();
98| #endif
99| out->x = 0.0;
100| out->y = 0.0;
101| }
102|}
103|/* #endif USES_THREED_VIEWL */
104|
105|
106|
107|/* #ifdef USES_THREED_VIEWL */
108|/*************************************************************************
109|* 6. <<< [ThreeD_XYZ_get3D] 三次元ワールド座標へ変換する >>>
110|*【機能】
111|*・二次元ワールド座標から三次元ワールド座標に変換します。
112|*【補足】
113|*・この関数とは別に、スクリーン座標から二次元ワールド座標に変換(または
114|* オフセットを修正)する必要があります。
115|*【内部補足】
116|*・実装は、ThreeD_XYZ_get2D 関数とまったく逆の手順をしています。
117|**************************************************************************/
118|void ThreeD_XYZ_get3D( ThreeD_XYZ* m, ThreeD_ViewL* view, ThreeD_XYZ* out )
119|{
120| ThreeD_XYZ p;
121|
122| #ifdef _CHECKER
123| if ( view->diffU == 0 ) error();
124| #endif
125|
126| /* 透視変換の逆(遠近法、二次元ワールド座標を視点座標に変換する) */
127| p.x = m->x * (m->z + view->diffV + view->diffU) / view->diffU;
128| p.y = m->y * (m->z + view->diffV + view->diffU) / view->diffU;
129| p.z = m->z;
130|
131| /* 三次元座標変換の逆(視点座標 p を三次元ワールド座標 out に変換する) */
132| {
133| /* x,y,z の関係を合わせる */
134| out->x = p.z; out->y = -p.x; out->z = -p.y;
135|
136| /* p を視線の方向に逆回転変換する */
137| ThreeD_XYZ_rot( out, &view->dir, out );
138|
139| /* 視点が原点になっているので平行移動変換する */
140| out->x += view->p.x;
141| out->y += view->p.y;
142| out->z += view->p.z;
143| }
144|}
145|/* #endif USES_THREED_VIEWL */
146|
147|
148|
149|/* #ifdef USES_THREED_DIR */
150|/***********************************************************************
151|* 7. <<< [ThreeD_XYZ_move] 指定された方向を基準に平行移動する >>>
152|*【機能】
153|*・方向 dir を基準に、座標値 m を前後上下左右に move だけ平行移動して
154|* out に格納します。
155|*【補足】
156|*・move は、視点座標で指定します。
157|*・m == out でも構いません。
158|************************************************************************/
159|void ThreeD_XYZ_move( ThreeD_XYZ* m, ThreeD_XYZ* move,
160| ThreeD_Dir* dir, ThreeD_XYZ* out )
161|{
162| ThreeD_XYZ move2; /* 平行移動量 */
163|
164| /* 三次元座標変換(視点座標 move を三次元ワールド座標 move2 に変換する) */
165| {
166| /* x,y,z の関係を合わせる */
167| move2.x = move->z; move2.y = -move->x; move2.z = -move->y;
168|
169| /* p を視線の方向に回転変換する */
170| ThreeD_XYZ_rot( &move2, dir, &move2 );
171| }
172|
173| /* 平行移動する */
174| out->x = m->x + move2.x;
175| out->y = m->y + move2.y;
176| out->z = m->z + move2.z;
177|}
178|/* #endif USES_THREED_DIR */
179|
180|
181|
182|/* #ifdef USES_THREED_DIR */
183|/***********************************************************************
184|* 8. <<< [ThreeD_XYZ_rot] 原点を中心に回転する >>>
185|*【機能】
186|*・方向 dir の角度成分だけ、座標値 m を回転させ out に格納します。
187|*【補足】
188|*・m == out でも構いません。
189|************************************************************************/
190|void ThreeD_XYZ_rot( ThreeD_XYZ* m, ThreeD_Dir* dir, ThreeD_XYZ* out )
191|{
192| double x1,y1,z1;
193| double x2,y2,z2;
194| double x3,y3,z3;
195| double x4,y4,z4;
196|
197| x1 = m->x; y1 = m->y; z1 = m->z;
198|
199| /* 角度 α(視線軸反時計周り) だけ座標値を回転する */
200| x2 = x1;
201| y2 = y1 * dir->cos_af - z1 * dir->sin_af;
202| z2 = y1 * dir->sin_af + z1 * dir->cos_af;
203|
204| /* 角度 φ(xy→z) だけ座標値を回転する */
205| x3 = x2 * dir->cos_fy - z2 * dir->sin_fy;
206| y3 = y2;
207| z3 = x2 * dir->sin_fy + z2 * dir->cos_fy;
208|
209| /* 角度 θ(x→y) だけ座標値を回転する */
210| x4 = x3 * dir->cos_th - y3 * dir->sin_th;
211| y4 = x3 * dir->sin_th + y3 * dir->cos_th;
212| z4 = z3;
213|
214| out->x = x4; out->y = y4; out->z = z4;
215|}
216|/* #ifdef USES_THREED_DIR */
217|
218|
219|
220|/* #ifdef USES_THREED_DIR */
221|/***********************************************************************
222|* 9. <<< [ThreeD_XYZ_rotR] 原点を中心に逆回転(座標変換)する >>>
223|*【機能】
224|*・方向 dir の角度成分だけ、座標値 m を逆回転させ out に格納します。
225|*・または、dir 方向の座標に回転変換して out に格納します。
226|*【補足】
227|*・m == out でも構いません。
228|*【内部補足】
229|*・座標値をθ回転させる行列は次のようになります。
230|* X = x*cosθ - y*sinθ
231|* Y = x*sinθ + y*cosθ
232|*・座標変換するときは、座標値を逆回転させます。
233|* ただし、cos-θ = cosθ, sin-θ = -sinθ
234|************************************************************************/
235|void ThreeD_XYZ_rotR( ThreeD_XYZ* m, ThreeD_Dir* dir, ThreeD_XYZ* out )
236|{
237| double x1,y1,z1;
238| double x2,y2,z2;
239| double x3,y3,z3;
240| double x4,y4,z4;
241|
242| x1 = m->x; y1 = m->y; z1 = m->z;
243|
244| /* 角度 θ(x→y) だけ回転するように座標変換する */
245| x2 = x1 * dir->cos_th + y1 * dir->sin_th;
246| y2 = -x1 * dir->sin_th + y1 * dir->cos_th;
247| z2 = z1;
248|
249| /* 角度 φ(xy→z) だけ回転するように座標変換する */
250| x3 = x2 * dir->cos_fy + z2 * dir->sin_fy;
251| y3 = y2;
252| z3 = -x2 * dir->sin_fy + z2 * dir->cos_fy;
253|
254| /* 角度 α(視線軸反時計周り) だけ回転するように座標変換する */
255| x4 = x3;
256| y4 = y3 * dir->cos_af + z3 * dir->sin_af;
257| z4 = -y3 * dir->sin_af + z3 * dir->cos_af;
258|
259| out->x = x4; out->y = y4; out->z = z4;
260|}
261|/* #ifdef USES_THREED_DIR */
262|
263|
264|
265|/***********************************************************************
266|* 10. <<< [ThreeD_XYZ_rotDir] 任意軸を中心に点を回転する >>>
267|*【引数】
268|* ・ThreeD_XYZ* in; 回転前の点の座標
269|* ・ThreeD_XYZ* axis_xyz; 回転軸上の任意の点の座標
270|* ・ThreeD_Dir* axis_dir; 回転軸の方向と回転角度
271|* ・ThreeD_XYZ* out; 回転後の点の座標を格納するアドレス
272|*【内部補足】
273|*・座標値をθ回転させる行列は次のようになります。
274|* X = x*cosθ - y*sinθ
275|* Y = x*sinθ + y*cosθ
276|*・変換行列を用いれば、キャッシュが効くと考えられます。(未対応)
277|************************************************************************/
278|#if 0
279|void ThreeD_XYZ_rotDir( ThreeD_XYZ* in, ThreeD_XYZ* axis_xyz,
280| ThreeD_Dir* axis_dir, ThreeD_XYZ* out )
281|{
282| ThreeD_XYZ p1, p2;
283|
284| /* 回転軸を Z軸に合わせる */
285| /* in := { in, axis_xyz }, out := { p1->x, p2->y, p2->z } */
286| {
287| /* axiz_xyz が原点になるように平行移動する */
288| p1->x = in->x - axis->x;
289| p1->y = in->y - axis->y;
290| p1->z = in->z - axis->z;
291|
292| /* Z 軸 -θ回転する */
293| p2->x = p1->x * axis_dir->cos_th + p1->y * axis_dir->sin_th;
294| p2->y = - p1->x * axis_dir->sin_th + p1->y * axis_dir->cos_th;
295|
296| /* Y 軸 90度−φ回転する */
297| /* sin(90-f)==cos(f), cos(90-f)==sin(f) */
298| p1->x = p2->x * axis_dir->sin_fy - p1->z * axis_dir->cos_fy;
299| p2->z = p2->x * axis_dir->cos_fy + p1->z * axis_dir->sin_fy;
300| }
301|
302| /* -α回転する(Z 軸) */
303| p2->x = p1->x * axis_dir->cos_af + p2->y * axis_dir->sin_af;
304| p1->y = - p1->x * axis_dir->sin_af + p2->y * axis_dir->cos_af;
305|
306| /* 回転軸を戻す */
307| /* in := { p2->x, p1->y, p2->z }, out := { out } */
308| {
309| /* Y 軸 φ−90度回転する */
310| /* sin(f-90)== -cos(f), cos(f-90)==sin(f) */
311| p1->x = p2->x * axis_dir->sin_fy + p2->z * axis_dir->cos_fy;
312| p1->z = - p2->x * axis_dir->cos_fy + p2->z * axis_dir->sin_fy;
313|
314| /* Z 軸 θ回転する */
315| p2->x = p1->x * axis_dir->cos_th - p1->y * axis_dir->sin_th;
316| p2->y = p1->x * axis_dir->sin_th + p1->y * axis_dir->cos_th;
317|
318| /* 原点が axiz_xyz になるように平行移動する */
319| out->x = p2->x + axis->x;
320| out->y = p2->y + axis->y;
321| out->z = p1->z + axis->z;
322| }
323|}
324|#endif
325|
326|
327|
328|/***********************************************************************
329|* 11. <<< [ThreeD_XYZ_getDistance] 2点の距離を返す >>>
330|************************************************************************/
331|double ThreeD_XYZ_getDistance( ThreeD_XYZ* a, ThreeD_XYZ* b )
332|{
333| double dx = a->x - b->x;
334| double dy = a->y - b->y;
335| double dz = a->z - b->z;
336|
337| return sqrt( dx*dx + dy*dy + dz*dz );
338|}
339|
340|
341|
342|/***********************************************************************
343|* 12. <<< [ThreeD_XYZ_getZCross] 2点からなる直線と Z=z の面との交点を取得する >>>
344|*【機能】
345|*・点 p1 と点 p2 からなる直線と Z=z の面の交点を p に格納します。
346|************************************************************************/
347|void ThreeD_XYZ_getZCross( ThreeD_XYZ* p1, ThreeD_XYZ* p2,
348| double z, ThreeD_XYZ* p )
349|{
350| p->x = p1->x + (p2->x - p1->x) * (z - p1->z) / (p2->z - p1->z);
351| p->y = p1->y + (p2->y - p1->y) * (z - p1->z) / (p2->z - p1->z);
352| p->z = z;
353|}
354|
355|
356|
357|/*************************************************************************
358|* 13. <<< [ThreeD_XYZ_getSurfDistanceL] 点から平面までの直線上の長さを求める >>>
359|*【引数】
360|* ・ThreeD_Line* line; 直線(長さを測るところ)
361|* ・double 返り値; 点から平面までの直線上の長さ
362|*【補足】
363|*
364|**************************************************************************/
365|double ThreeD_XYZ_getSurfDistanceL( ThreeD_XYZ* p, ThreeD_Surf* surf,
366| ThreeD_Line* line )
367|{
368| double w;
369|
370| w = surf->a * line->xcos + surf->b * line->ycos + surf->c * line->zcos;
371| if ( w == 0.0 ) w = 0.001;
372| return -(surf->a * p->x + surf->b * p->y + surf->c * p->z + surf->d) / w;
373|}
374|
375|
376|
377|#ifdef USES_TWOD
378|/*************************************************************************
379|* 14. <<< [ThreeD_XYZ_isInPoly] 点がポリゴンの内部にあるかどうか判定する >>>
380|*【引数】
381|* ・TwoD_XY* poly2_xy; ワーク領域。ポリゴンの頂点数だけ格納できる配列
382|* ・size_t poly2_xy_sizeof; xy_work のメモリサイズ
383|**************************************************************************/
384|bool ThreeD_XYZ_isInPoly( ThreeD_XYZ* p, ThreeD_Poly* poly,
385| TwoD_XY* poly2_xy, size_t poly2_xy_sizeof )
386|{
387| ThreeD_TwoD f;
388| TwoD_Poly poly2;
389| TwoD_XY p2;
390|
391| #ifdef _CHECKER
392| if ( poly2_xy_sizeof / sizeof( TwoD_XY ) < (unsigned)poly->n ) error();
393| #endif
394|
395| ThreeD_TwoD_initByPoly( &f, poly );
396| TwoD_Poly_initByThreeD2( &poly2, poly2_xy, poly2_xy_sizeof, &f, poly );
397| TwoD_XY_initBy3D( &p2, &f, p );
398|
399| return TwoD_XY_isInPoly( &p2, &poly2 ); /*★*/
400|}
401|#endif /* USES_TWOD */
402|
403|
404|
405|/***********************************************************************
406|* 15. <<< [ThreeD_XYZ_print] 属性を表示する >>>
407|************************************************************************/
408|#ifndef ERRORS_CUT_DEBUG_TOOL
409|void ThreeD_XYZ_print( ThreeD_XYZ* m, const char* title )
410|{
411| Errors_printf( "%s:ThreeD_XYZ(%p): (%lf, %lf, %lf)", title, m,
412| m->x, m->y, m->z );
413|}
414|#endif
415|
416|
417|
418|/*-----------------------------------------------------------------------*/
419|/* 16. <<< ◆三次元の座標値(float型) (ThreeD_XYZ2) >>> */
420|/*-----------------------------------------------------------------------*/
421|
422|
423|
424|/***********************************************************************
425|* 17. <<< [ThreeD_XYZ2_getDistance] 2点の距離を返す >>>
426|************************************************************************/
427|float ThreeD_XYZ2_getDistance( ThreeD_XYZ2* a, ThreeD_XYZ2* b )
428|{
429| double dx = (double)a->x - b->x;
430| double dy = (double)a->y - b->y;
431| double dz = (double)a->z - b->z;
432|
433| return (float)sqrt( dx*dx + dy*dy + dz*dz );
434|}
435|
436|
437|
438|/***********************************************************************
439|* 18. <<< [ThreeD_XYZ2_print] 属性を表示する >>>
440|************************************************************************/
441|#ifndef ERRORS_CUT_DEBUG_TOOL
442|void ThreeD_XYZ2_print( ThreeD_XYZ2* m, const char* title )
443|{
444| Errors_printf( "%s:ThreeD_XYZ2(%p):(%f, %f, %f)",
445| title, m, m->x, m->y, m->z );
446|}
447|#endif
448|
449|
450|
451|/*-----------------------------------------------------------------------*/
452|/* 19. <<< ◆方向, 角度 (ThreeD_Dir) >>> */
453|/*-----------------------------------------------------------------------*/
454|
455|
456|
457|/***********************************************************************
458|* 20. <<< [ThreeD_Dir_initDef] デフォルトで初期化する >>>
459|*【機能】
460|*・X 軸方向に設定します。
461|************************************************************************/
462|void ThreeD_Dir_initDef( ThreeD_Dir* m )
463|{
464| m->th = 0.0;
465| m->fy = 0.0;
466| m->af = 0.0;
467|
468| m->sin_th = 0.0;
469| m->cos_th = 1.0;
470| m->sin_fy = 0.0;
471| m->cos_fy = 1.0;
472| m->sin_af = 0.0;
473| m->cos_af = 1.0;
474|}
475|
476|
477|
478|/***********************************************************************
479|* 21. <<< [ThreeD_Dir_init] 初期化する(角度指定)>>>
480|*【補足】
481|*・引数は、ラジアン(〜0〜2π〜)です。
482|************************************************************************/
483|void ThreeD_Dir_init( ThreeD_Dir* m, double th, double fy, double af )
484|{
485| m->th = th;
486| m->fy = fy;
487| m->af = af;
488|
489| m->sin_th = sin(th);
490| m->cos_th = cos(th);
491| m->sin_fy = sin(fy);
492| m->cos_fy = cos(fy);
493| m->sin_af = sin(af);
494| m->cos_af = cos(af);
495|}
496|
497|
498|
499|/***********************************************************************
500|* 22. <<< [ThreeD_Dir_init2] 初期化する(ベクトル成分指定)>>>
501|*【補足】
502|*・引数 af は、ラジアン(〜0〜2π〜)です。
503|************************************************************************/
504|void ThreeD_Dir_init2( ThreeD_Dir* m, double dx, double dy,
505| double dz, double af )
506|{
507|#if 0
508| double a;
509|
510| a = dy / dx; ???
511| m->th = asin(a);
512| m->sin_th = a;
513| m->cos_th = dy/dx; ???
514|
515| a = dy / dx; ???
516| m->fy = ;
517| m->sin_fy = sin(fy);
518| m->cos_fy = cos(fy);
519| m->sin_af = sin(af);
520| m->cos_af = cos(af);
521|
522| m->af = af;
523|#endif
524|Errors_notSupport();
525|m, dx, dy, dz, af;
526|}
527|
528|
529|
530|/***********************************************************************
531|* 23. <<< [ThreeD_Dir_print] 方向の成分を表示する>>>
532|************************************************************************/
533|#ifndef ERRORS_CUT_DEBUG_TOOL
534|void ThreeD_Dir_print( ThreeD_Dir* m, const char* title )
535|{
536| Errors_printf( "%s:ThreeD_Dir(%p):(%f,%f,%f:radian)(%f,%f,%f:degree)",
537| title, m,
538| m->th, m->fy, m->af,
539| m->th * 180 / 3.14, m->fy * 180 / 3.14, m->af * 180 / 3.14 );
540|
541| Errors_printf( "%s: (%f,%f,%f:sin)(%f,%f,%f:cos)",
542| title,
543| m->sin_th, m->sin_fy, m->sin_af,
544| m->cos_th, m->cos_fy, m->cos_af );
545|}
546|#endif
547|
548|
549|
550|/*-----------------------------------------------------------------------*/
551|/* 24. <<< ◆三次元ベクトル (ThreeD_Vect) >>> */
552|/*-----------------------------------------------------------------------*/
553|
554|
555|
556|/*************************************************************************
557|* 25. <<< [ThreeD_Vect_chgToUnit] 単位ベクトルにする >>>
558|**************************************************************************/
559|void ThreeD_Vect_chgToUnit( ThreeD_Vect* m )
560|{
561| double len = ThreeD_Vect_getLen( m );
562| m->x /= len; m->y /= len; m->z /= len;
563|}
564|
565|
566|
567|/*************************************************************************
568|* 26. <<< [ThreeD_Vect_getLen] ベクトルの長さ(|A|)>>>
569|**************************************************************************/
570|double ThreeD_Vect_getLen( ThreeD_Vect* a )
571|{
572| #if ERRORS_DEBUG_FALSE
573| Errors_printf( "--- ThreeD_Vect_getLen" );
574| ThreeD_Vect_print( a, "" );
575| Errors_printf( "len^2 = %f", a->x * a->x + a->y * a->y + a->z * a->z );
576| Errors_printf( "sqrt = %f", sqrt( a->x * a->x + a->y * a->y + a->z * a->z ) );
577| #endif
578|
579| return sqrt( a->x * a->x + a->y * a->y + a->z * a->z );
580|}
581|
582|
583|
584|/*************************************************************************
585|* 27. <<< [ThreeD_Vect_getDirVect] ベクトルの方向成分のベクトルを取得する >>>
586|*【機能】
587|*・ベクトル in の dir 方向成分を out に格納します。
588|*【補足】
589|*・dir は、単位ベクトルを指定してください。
590|**************************************************************************/
591|void ThreeD_Vect_getDirVect( ThreeD_Vect* in, ThreeD_Vect* dir,
592| ThreeD_Vect* out )
593|{
594| double dir_len;
595|
596| #ifdef _CHECKER
597| if ( !MathX_equal( ThreeD_Vect_getLen( dir ), 1.0, 0.001) ) error();
598| #endif
599|
600| dir_len = ThreeD_Vect_getDirLen( in, dir );
601| out->x = dir_len * dir->x;
602| out->y = dir_len * dir->y;
603| out->z = dir_len * dir->z;
604|}
605|
606|
607|
608|/*************************************************************************
609|* 28. <<< [ThreeD_Vect_getDirLen] ベクトルの方向成分の長さを取得する >>>
610|*【補足】
611|*・dir には、単位ベクトルを指定してください。
612|*・_CHECKER マクロが定義されていないと、マクロになります
613|**************************************************************************/
614|#ifdef _CHECKER
615|double ThreeD_Vect_getDirLen( ThreeD_Vect* a, ThreeD_Vect* dir )
616|{
617| #ifdef _CHECKER
618| if ( !MathX_equal( ThreeD_Vect_getLen( dir ), 1.0, 0.001) ) error();
619| #endif
620|
621| return a->x * dir->x + a->y * dir->y + a->z * dir->z;
622|}
623|#endif
624|
625|
626|
627|/***********************************************************************
628|* 29. <<< [ThreeD_Vect_getCos] 2ベクトルのなす角度のcosを計算する >>>
629|*【引数】
630|* ・double 返り値; 2ベクトルのなす角度の cos
631|*【内部補足】
632|*・cosθ = (a・b)/(|a||b|) の公式から計算します。ただし、a,b はベクトル。
633|************************************************************************/
634|double ThreeD_Vect_getCos( ThreeD_Vect* v1, ThreeD_Vect* v2 )
635|{
636| ASSERT( ThreeD_Vect_getLen( v1 ) != 0.0 );
637| ASSERT( ThreeD_Vect_getLen( v2 ) != 0.0 );
638|
639| return ThreeD_Vect_getInnerProd( v1, v2 ) /
640| ( ThreeD_Vect_getLen( v1 ) * ThreeD_Vect_getLen( v2 ) );
641|}
642|
643|
644|
645|/*-------------------------------------------------------------------------*/
646|/* 30. <<< ◆視線, 視点 (ThreeD_ViewL) ★>>> */
647|/*-------------------------------------------------------------------------*/
648|
649|
650|
651|/***********************************************************************
652|* 31. <<< [ThreeD_ViewL_initDef] デフォルトで初期化する >>>
653|************************************************************************/
654|void ThreeD_ViewL_initDef( ThreeD_ViewL* m )
655|{
656| m->p.x = 0.0;
657| m->p.y = 0.0;
658| m->p.z = 0.0;
659| ThreeD_Dir_initDef( &m->dir );
660| m->diffV = 10.0;
661| m->diffV = 10.0;
662| m->diffU = 600.0;
663|}
664|
665|
666|
667|/***********************************************************************
668|* 32. <<< [ThreeD_ViewL_init] 初期化する >>>
669|************************************************************************/
670|void ThreeD_ViewL_init( ThreeD_ViewL* m,
671| double x, double y, double z,
672| double th, double fy, double af,
673| double diffV, double diffC, double diffU )
674|{
675| m->p.x = x;
676| m->p.y = y;
677| m->p.z = z;
678| ThreeD_Dir_init( &m->dir, th, fy, af );
679| m->diffV = diffV;
680| m->diffC = diffC;
681| m->diffU = diffU;
682|}
683|
684|
685|
686|/***********************************************************************
687|* 33. <<< [ThreeD_ViewL_setXYZ] 視点の位置を設定する >>>
688|************************************************************************/
689|void ThreeD_ViewL_setXYZ( ThreeD_ViewL* m, double x, double y,
690| double z )
691|{
692| ThreeD_XYZ_init( &m->p, x, y, z );
693|}
694|
695|
696|/***********************************************************************
697|* 34. <<< [ThreeD_ViewL_setDir] 視線の方向を設定する(角度指定)>>>
698|************************************************************************/
699|void ThreeD_ViewL_setDir( ThreeD_ViewL* m, double th, double fy,
700| double af )
701|{
702| ThreeD_Dir_init( &m->dir, th, fy, af );
703|}
704|
705|
706|/***********************************************************************
707|* 35. <<< [ThreeD_ViewL_setDirAndMove] 視線の方向を設定する(角度&目標点指定)>>>
708|*【引数】
709|* ・double th, fy, af; 角度
710|* ・double tx, ty, tz; 目標点の座標
711|*【補足】
712|*・tx,ty,tz の点がスクリーンの中央に来るように視点を移動します。
713|* ↓次の図は無くしてしまいました (^^;
714|*
715|************************************************************************/
716|void ThreeD_ViewL_setDirAndMove( ThreeD_ViewL* m,
717| double th, double fy, double af,
718| double tx, double ty, double tz )
719|{
720|#if 0
721| double diff;
722| double dx, dy, dz;
723|
724| dx = tx - m->p.x; dy = ty - m->p.y; dz = tz - m->p.z;
725|/* diff = sqrt( dx*dx + dy*dy + dz*dz ); */
726|
727|/* 作成中 */
728|#endif
729|
730|Errors_notSupport();
731|m, th, fy, af, tx, ty, tz;
732|}
733|
734|/***********************************************************************
735|* 36. <<< [ThreeD_ViewL_setTarget] 視線の方向を設定する(目標点指定) >>>
736|************************************************************************/
737|void ThreeD_ViewL_setTarget( ThreeD_ViewL* m, double x, double y,
738| double z, double af )
739|{
740| ThreeD_Dir_init2( &m->dir,
741| x - m->p.x, y - m->p.y, z - m->p.z, af );
742|}
743|
744|
745|/***********************************************************************
746|* 37. <<< [ThreeD_ViewL_getUserViewP] ユーザ視点の座標を取得する >>>
747|************************************************************************/
748|void ThreeD_ViewL_getUserViewP( ThreeD_ViewL* m, ThreeD_XYZ* userViewP )
749|{
750| ThreeD_XYZ vect;
751|
752| ThreeD_XYZ_init( &vect, 0, 0, -m->diffU );
753| ThreeD_XYZ_move( &m->p, &vect, &m->dir, userViewP );
754|}
755|
756|
757|
758|/***********************************************************************
759|* 38. <<< [ThreeD_ViewL_move] 視点を移動する >>>
760|*【引数】
761|* ・double dx,dy,dz; 移動量(視線方向をZ正方向とした差分値)
762|************************************************************************/
763|void ThreeD_ViewL_move( ThreeD_ViewL* m,
764| double dx, double dy, double dz )
765|{
766| ThreeD_XYZ d;
767|
768| d.x = dx; d.y = dy; d.z = dz;
769| ThreeD_XYZ_move( &m->p, &d, &m->dir, &m->p );
770|}
771|
772|
773|
774|/***********************************************************************
775|* 39. <<< [ThreeD_ViewL_rot] 視線の角度を変える >>>
776|************************************************************************/
777|void ThreeD_ViewL_rot( ThreeD_ViewL* m,
778| double d_th, double d_fy, double d_af )
779|{
780| ThreeD_Dir_init( &m->dir, m->dir.th + d_th,
781| m->dir.fy + d_fy, m->dir.af + d_af );
782|}
783|
784|
785|
786|/***********************************************************************
787|* 40. <<< [ThreeD_ViewL_initS] スクリーン位相で視線を指定して初期化する >>>
788|*【引数】
789|* ・double ax; スクリーン X軸方向の位相(Y軸回転)
790|* ・double ay; スクリーン Y軸方向の位相(X軸回転)
791|************************************************************************/
792|#if 0
793|void ThreeD_ViewL_initS( ThreeD_ViewL* m, double ax, double* fy2 )
794|{
795|}
796|#endif
797|
798|
799|
800|/**************************************************************************
801|* 41. <<< [ThreeD_ViewL_print] パラメータを表示する >>>
802|***************************************************************************/
803|#ifndef ERRORS_CUT_DEBUG_TOOL
804|void ThreeD_ViewL_print( ThreeD_ViewL* m, const char* title )
805|{
806| Errors_printf( "%s:ThreeD_ViewL(%p):", title, m );
807| ThreeD_XYZ_print( &m->p, title );
808| ThreeD_Dir_print( &m->dir, title );
809| Errors_printf( "%s: diffV = %f, diffC = %f, diffU = %f",
810| title, m->diffV, m->diffC, m->diffU );
811|}
812|#endif
813|
814|
815|
816|/*----------------------------------------------------------------------*/
817|/* 42. <<< ◆直線 (ThreeD_Line) >>> */
818|/*----------------------------------------------------------------------*/
819|
820|
821|
822|/*************************************************************************
823|* 43. <<< [ThreeD_Line_init] 2点の座標から初期化する >>>
824|**************************************************************************/
825|void ThreeD_Line_init( ThreeD_Line* m, ThreeD_XYZ* p1, ThreeD_XYZ* p2 )
826|{
827| double dx = p2->x - p1->x;
828| double dy = p2->y - p1->y;
829| double dz = p2->z - p1->z;
830| double dist = sqrt( dx*dx + dy*dy + dz*dz );
831|
832| m->p1 = *p1;
833| m->xcos = dx / dist;
834| m->ycos = dy / dist;
835| m->zcos = dz / dist;
836|}
837|
838|
839|
840|/*----------------------------------------------------------------------*/
841|/* 44. <<< ◆平面 (ThreeD_Surf) >>> */
842|/*----------------------------------------------------------------------*/
843|
844|
845|
846|/**************************************************************************
847|* 45. <<< [ThreeD_Surf_initByP3] 3点から平面を初期化する >>>
848|*【引数】
849|* ・ThreeD_XYZ p[]; 平面上にある3点の座標の配列の先頭アドレス
850|*【補足】
851|*・p の配列要素数は3です。
852|*・3点の順番は、その3点からできる三角形を表から見て左回りに指定します。
853|*・3点は1つの直線上に並ばないようにしてください。
854|***************************************************************************/
855|void ThreeD_Surf_initByP3( ThreeD_Surf* m, ThreeD_XYZ p[] )
856|{
857| double w,w1,w2,w3;
858|
859| w1 = p[0].y * (p[1].z - p[2].z)
860| + p[1].y * (p[2].z - p[0].z)
861| + p[2].y * (p[0].z - p[1].z);
862| w2 = p[0].z * (p[1].x - p[2].x)
863| + p[1].z * (p[2].x - p[0].x)
864| + p[2].z * (p[0].x - p[1].x);
865| w3 = p[0].x * (p[1].y - p[2].y)
866| + p[1].x * (p[2].y - p[0].y)
867| + p[2].x * (p[0].y - p[1].y);
868|
869| w = w1*w1 + w2*w2 + w3*w3;
870| if ( w < ThreeD_err ) w = ThreeD_err;
871|
872| w = sqrt( w );
873|
874| m->a = w1/w;
875| m->b = w2/w;
876| m->c = w3/w;
877| m->d = - ( (m->a * p[0].x) + (m->b * p[0].y) +
878| (m->c * p[0].z) );
879|}
880|
881|
882|
883|/**************************************************************************
884|* 46. <<< [ThreeD_Surf_initByP3_2] 3点から平面を初期化する >>>
885|*【引数】
886|* ・ThreeD_XYZ2 p[]; 平面上にある3点の座標の配列の先頭アドレス
887|*【補足】
888|*・ThreeD_Surf_initByP3 との違いは、引数の型のみです。
889|***************************************************************************/
890|void ThreeD_Surf_initByP3_2( ThreeD_Surf* m, ThreeD_XYZ2 p[] )
891|{
892| float w,w1,w2,w3;
893|
894| w1 = p[0].y * (p[1].z - p[2].z)
895| + p[1].y * (p[2].z - p[0].z)
896| + p[2].y * (p[0].z - p[1].z);
897| w2 = p[0].z * (p[1].x - p[2].x)
898| + p[1].z * (p[2].x - p[0].x)
899| + p[2].z * (p[0].x - p[1].x);
900| w3 = p[0].x * (p[1].y - p[2].y)
901| + p[1].x * (p[2].y - p[0].y)
902| + p[2].x * (p[0].y - p[1].y);
903|
904| w = w1*w1 + w2*w2 + w3*w3;
905| if ( w < (float)ThreeD_err ) w = (float)ThreeD_err;
906|
907| w = (float)sqrt( w );
908|
909| m->a = w1/w;
910| m->b = w2/w;
911| m->c = w3/w;
912| m->d = - ( (m->a * p[0].x) + (m->b * p[0].y) +
913| (m->c * p[0].z) );
914|}
915|
916|
917|
918|/**************************************************************************
919|* 47. <<< [ThreeD_Surf_initByP3_3] 3点から平面を初期化する >>>
920|*【補足】
921|*・ThreeD_Surf_initByP3 との違いは、引数の型のみです。
922|***************************************************************************/
923|void ThreeD_Surf_initByP3_3( ThreeD_Surf* m,
924| double p0x, double p0y, double p0z,
925| double p1x, double p1y, double p1z,
926| double p2x, double p2y, double p2z )
927|{
928| double w,w1,w2,w3;
929|
930| w1 = p0y * (p1z - p2z)
931| + p1y * (p2z - p0z)
932| + p2y * (p0z - p1z);
933| w2 = p0z * (p1x - p2x)
934| + p1z * (p2x - p0x)
935| + p2z * (p0x - p1x);
936| w3 = p0x * (p1y - p2y)
937| + p1x * (p2y - p0y)
938| + p2x * (p0y - p1y);
939|
940| w = w1*w1 + w2*w2 + w3*w3;
941| if ( w < ThreeD_err ) w = ThreeD_err;
942|
943| w = sqrt( w );
944|
945| m->a = w1/w;
946| m->b = w2/w;
947| m->c = w3/w;
948| m->d = - ( (m->a * p0x) + (m->b * p0y) +
949| (m->c * p0z) );
950|}
951|
952|
953|
954|/**************************************************************************
955|* 48. <<< [ThreeD_Surf_getZ] Z 軸に平行な直線と平面の交点を返す >>>
956|*【引数】
957|* ・double x,y; Z 軸に平行な直線の (x,y) 座標
958|* ・double 返り値; 交点の Z 座標
959|***************************************************************************/
960|double ThreeD_Surf_getZ( ThreeD_Surf* m, double x, double y )
961|{
962| #ifdef _CHECKER
963| if ( m->c == 0.0 ) error();
964| #endif
965| return -( m->a *x + m->b *y + m->d ) / m->c;
966|}
967|
968|
969|
970|/*************************************************************************
971|* 49. <<< [ThreeD_Surf_getLineCross] 平面と直線の交点を求める >>>
972|*【引数】
973|* ・ThreeD_XYZ* out; 交点を格納するアドレス
974|*【補足】
975|*
976|**************************************************************************/
977|void ThreeD_Surf_getLineCross( ThreeD_Surf* surf, ThreeD_Line* line,
978| ThreeD_XYZ* out )
979|{
980| double dist = ThreeD_XYZ_getSurfDistanceL( &line->p1, surf, line );
981|
982| out->x = line->xcos * dist + line->p1.x;
983| out->y = line->ycos * dist + line->p1.y;
984| out->z = line->zcos * dist + line->p1.z;
985|}
986|
987|
988|
989|/*************************************************************************
990|* 50. <<< [ThreeD_Surf_cmpFront] 平面が表側かどうか判断する ★>>>
991|*【機能】
992|*・ある視点 viewP から平面 m を見たとき、平面の表側かどうかを判断します。
993|*【引数】
994|* ・ThreeD_XYZ* viewP; 視点座標
995|* ・int 返り値; 正の数 = 表、負の数 = 裏、0 = 平行
996|*【補足】
997|*・返り値が0の場合は、平面に viewP が含まれているため、表か裏か判断でき
998|* ない場合です。
999|**************************************************************************/
1000|int ThreeD_Surf_cmpFront( ThreeD_Surf* m, ThreeD_XYZ* viewP )
1001|{
1002| return (int)( (m->a * viewP->x) + (m->b * viewP->y) +
1003| (m->c * viewP->z) + m->d );
1004|}
1005|
1006|
1007|
1008|/**************************************************************************
1009|* 51. <<< [ThreeD_Surf_print] パラメータを表示する >>>
1010|***************************************************************************/
1011|#ifndef ERRORS_CUT_DEBUG_TOOL
1012|void ThreeD_Surf_print( ThreeD_Surf* m )
1013|{
1014| Errors_printf( "%fx + %fy + %fz + %f = 0\n", m->a, m->b, m->c, m->d );
1015|}
1016|#endif
1017|
1018|
1019|
1020|/*----------------------------------------------------------------------*/
1021|/* 52. <<< ◆ポリゴン (ThreeD_Poly) >>>
1022|/*----------------------------------------------------------------------*/
1023|
1024|
1025|
1026|/*************************************************************************
1027|* 53. <<< [ThreeD_Poly_init] ポリゴンを初期化する >>>
1028|*【引数】
1029|* (構造体を参照)
1030|**************************************************************************/
1031|void ThreeD_Poly_init( ThreeD_Poly* m, ThreeD_XYZ* p_array, int n,
1032| int color )
1033|{
1034| m->p_array = p_array;
1035| m->n = n;
1036| ThreeD_Surf_initByP3( &m->surf, p_array );
1037| m->color = color;
1038|}
1039|
1040|
1041|
1042|/*************************************************************************
1043|* 54. <<< [ThreeD_Poly_initShadow] 影ポリゴンを初期化する ★>>>
1044|*【機能】
1045|*・光源(点)light に対して、影付けポリゴン shadowing が、
1046|* 影映りポリゴン shadowed に映す、影ポリゴン m を初期化します。
1047|*
1048|*【引数】
1049|* ・ThreeD_Poly* m; 影ポリゴン
1050|* ・ThreeD_Poly* shadowed; 影映りポリゴン
1051|* ・ThreeD_Poly* shadowing; 影付けポリゴン
1052|* ・ThreeD_XYZ* light; 光源(点)
1053|* ・ThreeD_XYZ* xyzs; m の頂点を格納する領域の先頭アドレス
1054|* ・size_t xyzs_sizeof; xyzs のメモリサイズ
1055|*【補足】
1056|*・ポリゴンの影は複数のポリゴンに映るので、それぞれのポリゴンに映る
1057|* 影ポリゴンを作らないと正しく描画されません。
1058|*・1st step では影映りポリゴンからはみ出る仮の影ポリゴンが作成されます。
1059|* 2nd step では仮の影ポリゴンと影映りポリゴンの各辺の交点が追加され、
1060|* 3rd step では頂点をたどりながら最終的な影ポリゴンを決定します。
1061|*
1062|*・2ポリゴンの関係において、以下の関係を考慮しています。
1063|* ・内部に頂点を持つ場合、持たない場合
1064|* ・1辺に対し、2つの交点を持つ場合
1065|* ・頂点が他方のポリゴンに接する場合(凸型ポリゴンに限る)
1066|* ・頂点が他方のポリゴンとの交点の場合
1067|* ・頂点が他方のポリゴンの頂点と重なる場合
1068|* ・交点を持たない場合
1069|*
1070|**************************************************************************/
1071|void ThreeD_Poly_initShadow( ThreeD_Poly* m, ThreeD_Poly* shadowed,
1072| ThreeD_Poly* shadowing, ThreeD_XYZ* light,
1073| ThreeD_XYZ* xyzs, size_t xyzs_sizeof )
1074|{
1075| int i;
1076| ThreeD_Line lightLine; /* 光源(点)と影付けポリゴンの頂点を結ぶ直線 */
1077| ThreeD_XYZ* xyzs_over = (ThreeD_XYZ*)((char*)xyzs + xyzs_sizeof);
1078|
1079| /* 影ポリゴンの初期化 */
1080| m->p_array = xyzs;
1081| m->n = shadowing->n;
1082| m->surf = shadowed->surf;
1083| m->color = 0;
1084|
1085| /* 影ポリゴンの頂点を求める */
1086| for ( i = 0; i < shadowing->n && xyzs < xyzs_over; i++ ) {
1087|
1088| /* lightLine を初期化 */
1089| ThreeD_Line_init( &lightLine, light, &shadowing->p_array[i] );
1090|
1091| /* lightLine と、影映りポリゴンを含む平面との、交点を xyzs に格納する */
1092| ThreeD_Surf_getLineCross( &shadowed->surf, &lightLine, xyzs );
1093| xyzs ++;
1094| }
1095|}
1096|
1097|
1098|
1099|#if 0
1100|/*************************************************************************
1101|* 55. <<< [ThreeD_Poly_initByFrontClip] セクショニングしたポリゴンに初期化する >>>
1102|*【引数】
1103|* ・ThreeD_XYZ* p_array; 頂点座標を格納するアドレス
1104|* ・int n_max; p_array に格納できる最大の頂点数
1105|* ・ThreeD_Poly* org; セクショニングする前のポリゴン
1106|* ・ThreeD_Surf* sect; セクショニング面
1107|*【補足】
1108|*・org ポリゴンから sect 面の表側にある部分を取り除いたポリゴンになります。
1109|**************************************************************************/
1110|void ThreeD_Poly_initBySect( ThreeD_Poly* m, ThreeD_XYZ* p_array,
1111| int n_max, ThreeD_Poly* org, ThreeD_Surf* clip )
1112|{
1113| int i;
1114| int cmp1, cmp2;
1115|
1116| cmp1 = ThreeD_Surf_cmpFront( clip, base->p_array[i] );
1117| cmp2 = ThreeD_Surf_cmpFront( clip, base->p_array[i+1] );
1118| #error now implementing
1119|}
1120|#endif
1121|
1122|
1123|
1124|/*************************************************************************
1125|* 56. <<< [ThreeD_Poly_getCenter] ポリゴンの重心を取得する >>>
1126|*【引数】
1127|* ・ThreeD_XYZ* center; ポリゴンの重心、三次元ワールド座標(出力)
1128|**************************************************************************/
1129|void ThreeD_Poly_getCenter( ThreeD_Poly* m, ThreeD_XYZ* center )
1130|{
1131| ThreeD_XYZ* p; /* ある頂点の座標 */
1132| ThreeD_XYZ* p_over = m->p_array + m->n; /* 最後の次の頂点 */
1133| double x = 0.0, y = 0.0, z = 0.0; /* 座標値の和 */
1134|
1135| for ( p = m->p_array; p < p_over; p++ )
1136| { x += p->x; y += p->y; z += p->z; }
1137|
1138| center->x = x/m->n; center->y = y/m->n; center->z = z/m->n;
1139|}
1140|
1141|
1142|
1143|/*************************************************************************
1144|* 57. <<< [ThreeD_Poly_cmpFront] ポリゴンが表側かどうか判断する >>>
1145|*【補足】
1146|*(ThreeD_Surf_cmpFront 関数を参照してください)
1147|**************************************************************************/
1148|int ThreeD_Poly_cmpFront( ThreeD_Poly* m, ThreeD_XYZ* viewP )
1149|{
1150| return ThreeD_Surf_cmpFront( &m->surf, viewP );
1151|}
1152|
1153|
1154|
1155|/*************************************************************************
1156|* 58. <<< [ThreeD_Poly_getZVal] 指定したスクリーン座標の Z 値を返す ★>>>
1157|*【機能】
1158|*・Z バッファに値を格納するために、スクリーン上のある点の Z 値を返します。
1159|* ここで言う Z 値とは、視点からポリゴン上のある点までの距離のことです。
1160|*【引数】
1161|* ・int sx, sy; スクリーン座標
1162|* ・int 返り値; Z 値、または INT_MAX
1163|*【補足】
1164|*・指定したスクリーン座標にポリゴンが無い場合、INT_MAX が返ります。
1165|**************************************************************************/
1166|#ifdef USES_SCALE
1167|#ifdef USES_TWOD
1168|int ThreeD_Poly_getZVal( ThreeD_Poly* m, int sx, int sy,
1169| ThreeD_ViewL* view, Scale* scale )
1170|{
1171| ThreeD_XYZ viewP; /* ユーザ視点の座標・三次元ワールド座標 */
1172| ThreeD_XYZ scrP; /* 投影面の座標・三次元ワールド座標 */
1173| ThreeD_Line line; /* ユーザ視点から仮想面上の点をむすぶ直線 */
1174| TwoD_XY scrP2; /* 投影面の座標・二次元ワールド座標 */
1175| TwoD_Poly poly2; /* ポリゴン・二次元ワールド座標 */
1176|
1177| static TwoD_XY poly2_ps[10];
1178| #ifdef _CHECKER
1179| if ( m->n > 10 ) error(); /* poly2_ps[] */
1180| #endif
1181|
1182| /* 二次元ワールド座標で、ポリゴンの内部でないなら INT_MAX を返す */
1183| TwoD_XY_init( &scrP2, Scale_toLX( scale, sx ), Scale_toLY( scale, sy ) );
1184| TwoD_Poly_initByThreeD( &poly2, poly2_ps, sizeof(poly2_ps), m, view );
1185| if ( ! TwoD_XY_isInPoly( &scrP2, &poly2 ) ) return INT_MAX;
1186|
1187| /* 投影面の座標 scrP を設定する */
1188| scrP.x = scrP2.x;
1189| scrP.y = scrP2.y;
1190| scrP.z = 0;
1191| ThreeD_XYZ_get3D( &scrP, view, &scrP );
1192|
1193| /* ユーザ視点 viewP を設定し、viewP と scrP をむすぶ直線 line を設定する */
1194| ThreeD_ViewL_getUserViewP( view, &viewP );
1195| ThreeD_Line_init( &line, &viewP, &scrP );
1196|
1197| /* ユーザ視点 viewP からポリゴン m までの直線 line 上の長さを返す */
1198| return (int)ThreeD_XYZ_getSurfDistanceL( &viewP, &m->surf, &line );
1199|}
1200|#endif /* USES_TWOD */
1201|#endif /* USES_SCALE */
1202|
1203|
1204|
1205|#ifdef USES_DRAW
1206|#ifdef USES_SCALE
1207|#ifdef USES_XORPAINT
1208|
1209|/*************************************************************************
1210|* 59. <<< [ThreeD_Poly_draw] ポリゴンを描画する >>>
1211|*【補足】
1212|*・paint は、画面のサイズで初期化&クリアしてあること。そして、この関数から
1213|* 抜けると、paint のバッファにはポリゴンの図形(ごみ)が残ります。
1214|*・ポリゴンの色・模様は paint->paintPattern に設定しておいてください。
1215|**************************************************************************/
1216|void ThreeD_Poly_draw( ThreeD_Poly* m, ThreeD_ViewL* view, Draw* draw,
1217| Scale* scale, XorPaint* paint )
1218|{
1219| ThreeD_XYZ* p; /* ある頂点の座標 */
1220| ThreeD_XYZ* p_over = m->p_array + m->n; /* 最後の次の頂点 */
1221| ThreeD_XYZ pp; /* p の二次元ワールド座標 */
1222| int x, y; /* p のスクリーン座標 */
1223| int x1, y1; /* 前の p のスクリーン座標 */
1224| int x0, y0; /* 最初の p のスクリーン座標 */
1225|
1226| /* 最初の頂点 p のスクリーン座標 x0, y0 を取得する */
1227| p = m->p_array;
1228| ThreeD_XYZ_get2D( p, view, &pp );
1229| x0 = x1 = (int)Scale_toGX( scale, pp.x );
1230| y0 = y1 = (int)Scale_toGY( scale, pp.y );
1231| p++;
1232|
1233| /* 頂点をたどりながら、塗りつぶす領域を設定する */
1234| for ( ; p < p_over; p++ ) {
1235| ThreeD_XYZ_get2D( p, view, &pp );
1236| x = (int)Scale_toGX( scale, pp.x );
1237| y = (int)Scale_toGY( scale, pp.y );
1238| XorPaint_setBound( paint, x1, y1, x, y );
1239| x1 = x; y1 = y;
1240| }
1241| XorPaint_setBound( paint, x1, y1, x0, y0 );
1242|
1243| /* 塗りつぶす */
1244| XorPaint_flushTo( paint, draw->drawScr, 0, 0 );
1245|}
1246|
1247|#endif /* USES_XORPAINT */
1248|#endif /* USES_SCALE */
1249|#endif /* USES_DRAW */
1250|
1251|
1252|
1253|#ifdef USES_DRAW
1254|#ifdef USES_SCALE
1255|#ifdef USES_XORPAINT
1256|
1257|/*************************************************************************
1258|* 60. <<< [ThreeD_Poly_draw2] 前面クリッピングしてポリゴンを描画する ★>>>
1259|*【補足】
1260|*・前面クリッピングは、投影面に平行な前面クリッピング面 Z=-view->diffC より
1261|* 視点側をクリッピングすることです。
1262|*・前面クリッピングする以外は、ThreeD_Poly_draw 関数と同じです。
1263|**************************************************************************/
1264|void ThreeD_Poly_draw2( ThreeD_Poly* m, ThreeD_ViewL* view, Draw* draw,
1265| Scale* scale, XorPaint* paint )
1266|{
1267| ThreeD_XYZ* p; /* ある頂点の座標 */
1268| ThreeD_XYZ* p_over = m->p_array + m->n; /* 最後の次の頂点 */
1269| ThreeD_XYZ pp; /* p の二次元ワールド座標 */
1270| ThreeD_XYZ pp1; /* 前の p の二次元ワールド座標 */
1271| ThreeD_XYZ pp0; /* 最初の p の二次元ワールド座標 */
1272| int x, y; /* p のスクリーン座標 */
1273| int x1, y1; /* 前に描いた p のスクリーン座標 */
1274| int x0, y0; /* 最初に描いた p のスクリーン座標 */
1275| bool bExist; /* 前面クリッピングされない最初の p が決まったか */
1276|
1277| /* 最初の頂点 p のスクリーン座標 pp0 を取得する */
1278| p = m->p_array;
1279| ThreeD_XYZ_get2D( p, view, &pp0 );
1280| if ( pp0.z >= -view->diffC ) {
1281| x0 = x1 = (int)Scale_toGX( scale, pp0.x );
1282| y0 = y1 = (int)Scale_toGY( scale, pp0.y );
1283| bExist = true;
1284| }
1285| else
1286| bExist = false;
1287| pp1 = pp0;
1288| p++;
1289|
1290| /* 頂点をたどりながら、塗りつぶす領域を設定する */
1291| for ( ; p < p_over; p++ ) {
1292|
1293| /* 頂点 p のスクリーン座標 pp を取得する */
1294| ThreeD_XYZ_get2D( p, view, &pp );
1295|
1296| /* 前に描いた点から pp まで境界線を描画する、ただし ... */
1297| /* [case1] pp1 も pp も前面クリッピングされないなら、そのまま描画する */
1298| /* [case2] pp のみ前面クリッピングされる(入)なら、交点Aまでを描画する */
1299| /* [case3] pp1 も pp も前面クリッピングされるなら、何もしない */
1300| /* [case4] pp1 のみ前面クリッピングされた(出)なら、交点Aから */
1301| /* 交点Bまでと、交点Bから pp までを描画する */
1302| /*
*/
1303| if ( pp1.z >= -view->diffC || pp.z >= -view->diffC ) {
1304| /* 前に描いた点から交点まで描画する [case2][case4] */
1305| if ( pp.z < -view->diffC || pp1.z < -view->diffC ) {
1306| ThreeD_XYZ_getZCross( &pp1, &pp, -view->diffC, &pp1 );
1307| x = (int)Scale_toGX( scale, pp1.x );
1308| y = (int)Scale_toGY( scale, pp1.y );
1309| if ( bExist ) {
1310| XorPaint_setBound( paint, x1, y1, x, y );
1311| }
1312| else {
1313| x0 = x; y0 = y; bExist = true;
1314| }
1315| x1 = x; y1 = y;
1316| }
1317| /* 前に描いた点から点 pp まで描画する [case1][case4] */
1318| if ( pp.z >= -view->diffC ) {
1319| x = (int)Scale_toGX( scale, pp.x );
1320| y = (int)Scale_toGY( scale, pp.y );
1321| XorPaint_setBound( paint, x1, y1, x, y );
1322| x1 = x; y1 = y;
1323| }
1324| }
1325| pp1 = pp;
1326| }
1327| /* もし1本でも線が描画されていたら、閉じるように線を描画する */
1328| /* もし、その線がクリッピング領域を超える場合、2本描画する */
1329| if ( bExist ) {
1330| /* 前に描いた点から最初に描いた点まで描画する [case1][case3] */
1331| if ( (pp1.z - -view->diffC)*(pp0.z - -view->diffC ) >= 0 ) {
1332| XorPaint_setBound( paint, x1, y1, x0, y0 );
1333| }
1334| /* 交点の両側に2本描画する [case2][case4] */
1335| else {
1336| ThreeD_XYZ_getZCross( &pp1, &pp0, -view->diffC, &pp );
1337| x = (int)Scale_toGX( scale, pp.x );
1338| y = (int)Scale_toGY( scale, pp.y );
1339| XorPaint_setBound( paint, x1, y1, x, y );
1340| XorPaint_setBound( paint, x0, y0, x, y );
1341| }
1342| }
1343|
1344| /* 塗りつぶす */
1345| XorPaint_flushTo( paint, draw->drawScr, 0, 0 );
1346|}
1347|
1348|#endif /* USES_XORPAINT */
1349|#endif /* USES_SCALE */
1350|#endif /* USES_DRAW */
1351|
1352|
1353|
1354|/*----------------------------------------------------------------------*/
1355|/* 61. <<< ◆影映りポリゴン (ThreeD_ShPoly) >>> */
1356|/*----------------------------------------------------------------------*/
1357|
1358|
1359|
1360|/*************************************************************************
1361|* 62. <<< [ThreeD_ShPoly_init] 影映りポリゴンを初期化する >>>
1362|*【補足】
1363|*・機能は、ThreeD_Poly_init 関数と同じです。
1364|*・影は ThreeD_ShPoly_shadowing 関数や ThreeD_ShPoly_shadowingAll 関数で
1365|* 付けます。
1366|**************************************************************************/
1367|void ThreeD_ShPoly_init( ThreeD_ShPoly* m, ThreeD_XYZ* p_array, int n,
1368| int color )
1369|{
1370| ThreeD_Poly_init( &m->poly, p_array, n, color );
1371| m->shadow[0] = NULL; /*(まだ)影なし */
1372| m->lightFlag = 0; /* 「不明」という値 */
1373|}
1374|
1375|
1376|
1377|#ifdef USES_ARRX
1378|/*************************************************************************
1379|* 63. <<< [ThreeD_ShPoly_shadowing] 影映りポリゴンに影ポリゴンを付ける >>>
1380|*【機能】
1381|*・指定した影映りポリゴンに、ワールドにあるすべてのポリゴンが作る
1382|* 影ポリゴンを付けます。
1383|*【引数】
1384|* ・ArrX* shPolys; ワールドにあるすべてのポリゴン(ThreeD_ShPoly 型)
1385|* ・ThreeD_XYZ* light; 光源(点)
1386|* ・ArrX_Buf* shadowBuf; 影ポリゴン・バッファ領域(ThreeD_Poly 型)
1387|* ・ArrX_Buf* xyzBuf 頂点・バッファ領域(ThreeD_XYZ 型)
1388|*【補足】
1389|*・光の位置が変わったりポリゴンが移動したら、影ポリゴンを付け直す必要が
1390|* あります。
1391|*・これまで付いていた影ポリゴンは、失います。
1392|**************************************************************************/
1393|void ThreeD_ShPoly_shadowing( ThreeD_ShPoly* m, ArrX* shPolys,
1394| ThreeD_XYZ* light, ArrX_Buf* shadowBuf, ArrX_Buf* xyzBuf )
1395|{
1396| ThreeD_ShPoly* shPoly; /* 影付けポリゴン */
1397| ThreeD_ShPoly* shPoly_over = shPolys->last;
1398| ThreeD_Poly** pNextShadow = &m->shadow[0]; /* 次の影ポリゴン */
1399|
1400| /* 光源の方向を記録する */
1401| m->lightFlag = ThreeD_Poly_cmpFront( &m->poly, light );
1402|
1403| /* すべてのポリゴンを影付けポリゴンとして */
1404| for ( shPoly = shPolys->first; shPoly < shPoly_over; shPoly++ ) {
1405| ThreeD_XYZ center;
1406|
1407| ThreeD_Poly_getCenter( &shPoly->poly, ¢er );
1408|
1409| /* もし、影付けポリゴンが光源の方向にあったら */
1410| if ( m != shPoly &&
1411| ThreeD_Poly_cmpFront( &m->poly, ¢er ) * m->lightFlag > 0 ) {
1412|
1413| /* 影ポリゴンを付ける */
1414| ThreeD_Poly* shadow = ArrX_Buf_alloc( shadowBuf, ThreeD_Poly );
1415| ThreeD_XYZ* xyzs = ArrX_Buf_allocs( xyzBuf, ThreeD_XYZ,
1416| shPoly->poly.n );
1417| ThreeD_Poly_initShadow( shadow, &m->poly, &shPoly->poly, light,
1418| xyzs, sizeof(ThreeD_XYZ) * shPoly->poly.n );
1419| *pNextShadow = shadow;
1420| pNextShadow ++;
1421| }
1422| }
1423| *pNextShadow = NULL;
1424|}
1425|#endif /* USES_ARRX */
1426|
1427|
1428|
1429|#ifdef USES_DRAW
1430|#ifdef USES_SCALE
1431|#ifdef USES_XORPAINT
1432|#ifdef USES_ARRX
1433|#ifdef USES_MASK
1434|/*************************************************************************
1435|* 64. <<< [ThreeD_ShPoly_draw2] 影映りポリゴンを描画する >>>
1436|*【補足】
1437|*・paint は、画面のサイズで初期化&クリアしてあること。そして、この関数から
1438|* 抜けると、paint のバッファにはポリゴンの図形(ごみ)が残ります。
1439|*・内部で ThreeD_Poly_draw2 関数を呼び出しているので、前面クリッピングが
1440|* 有効です。
1441|*・ポリゴンの色・模様は paint->paintPattern に設定しておいてください。
1442|**************************************************************************/
1443|void ThreeD_ShPoly_draw2( ThreeD_ShPoly* m, ThreeD_ViewL* view, Draw* draw,
1444| Scale* scale, XorPaint* paint )
1445|{
1446| ThreeD_XYZ viewP;
1447|
1448| /* ユーザ視点を求める */
1449| ThreeD_ViewL_getUserViewP( view, &viewP );
1450|
1451| /* 影映りポリゴン(本体)を描画する */
1452| ThreeD_Poly_draw2( &m->poly, view, draw, scale, paint );
1453|
1454| /* もし、光源と視点が、ポリゴンに対して同じ方向なら、*/
1455| if ( ThreeD_Poly_cmpFront( &m->poly, &viewP ) * m->lightFlag > 0 ) {
1456| ThreeD_Poly** pShadow;
1457|
1458| /* すべての影ポリゴンを描画する */
1459| for ( pShadow = m->shadow; *pShadow != NULL; pShadow++ ) {
1460| XorPaint_clear( paint );
1461| Mask_A_initByPlain( paint->paintPattern, 0 );
1462| ThreeD_Poly_draw2( *pShadow, view, draw, scale, paint );
1463| }
1464| }
1465|}
1466|#endif /* USES_MASK */
1467|#endif /* USES_ARRX */
1468|#endif /* USES_XORPAINT */
1469|#endif /* USES_SCALE */
1470|#endif /* USES_DRAW */
1471|
1472|
1473|
1474|#ifdef USES_ARRX
1475|/*************************************************************************
1476|* 65. <<< [ThreeD_ShPoly_shadowingAll] すべての影映りポリゴンに影ポリゴンを付ける >>>
1477|*【引数】
1478|* ・ArrX* shPolys; ワールドにあるすべてのポリゴン(ThreeD_ShPoly 型)
1479|* ・ThreeD_XYZ* light; 光源(点)
1480|*【補足】
1481|*・光の位置が変わったりポリゴンが移動したら、影ポリゴンを付け直す必要が
1482|* あります。
1483|*・これまで付いていた影ポリゴンは、失います。
1484|*・内部で影ポリゴンなどのバッファを用意しています。
1485|**************************************************************************/
1486|void ThreeD_ShPoly_shadowingAll( ArrX* shPolys, ThreeD_XYZ* light )
1487|{
1488| ArrX_Buf shadowBuf;
1489| static ThreeD_Poly shadowBuf_mem[500];
1490| ArrX_Buf xyzBuf;
1491| static ThreeD_Poly xyzBuf_mem[1000];
1492| ThreeD_ShPoly* shPoly;
1493| ThreeD_ShPoly* shPoly_over = shPolys->last;
1494|
1495| ArrX_Buf_init( &shadowBuf, shadowBuf_mem, sizeof(shadowBuf_mem) );
1496| ArrX_Buf_init( &xyzBuf, xyzBuf_mem, sizeof(xyzBuf_mem) );
1497|
1498| for ( shPoly = shPolys->first; shPoly < shPoly_over; shPoly++ ) {
1499| ThreeD_ShPoly_shadowing( shPoly, shPolys, light, &shadowBuf, &xyzBuf );/*★*/
1500| }
1501|}
1502|#endif /* USES_ARRX */
1503|
1504|
1505|
1506|/*----------------------------------------------------------------------*/
1507|/* 66. <<< ◆ソート用ポリゴン (ThreeD_PolyS) >>> */
1508|/*----------------------------------------------------------------------*/
1509|
1510|
1511|
1512|#ifdef USES_DRAW
1513|#ifdef USES_SCALE
1514|#ifdef USES_XORPAINT
1515|#ifdef USES_ARRX
1516|#ifdef USES_MASK
1517|
1518|/*************************************************************************
1519|* 67. <<< [ThreeD_PolyS_draw] すべてのポリゴンを描画する(ソーティング)★>>>
1520|*【機能】
1521|*・ソーティング、前面クリッピングして、全てのポリゴンを描画します。
1522|*【引数】
1523|* ・ArrX* polys; ポリゴン ThreeD_PolyS 型の配列を ArrX_init したもの
1524|* ・void** sortBuf; ソート用バッファ、void* 型の配列の先頭ポインタ
1525|* ・size_t sortBuf_sizeof; sortBuf のメモリサイズ
1526|*【補足】
1527|*・polys と sortBuf の要素数は同じであること。
1528|*・paint は、画面のサイズで初期化&クリアしてあること。そして、この関数から
1529|* 抜けると、paint のバッファにはポリゴンの図形(ごみ)が残ります。
1530|**************************************************************************/
1531|void ThreeD_PolyS_draw( ArrX* polys, void** sortBuf, size_t sortBuf_sizeof,
1532| ThreeD_ViewL* view, Draw* draw, Scale* scale, XorPaint* paint )
1533|{
1534| ArrX_Sort sort; /* ソート用配列 */
1535| ArrX_Key key; /* キー */
1536| ThreeD_XYZ userViewP; /* ユーザ視点の座標 */
1537| ThreeD_XYZ centerP; /* ポリゴンの重心の座標 */
1538| ThreeD_PolyS* poly; /* 処理中のポリゴン */
1539| ThreeD_PolyS* poly_over;
1540|
1541| /* ソート用配列に格納 ArrX_Sort_pushPQ する */
1542| {
1543| ThreeD_ViewL_getUserViewP( view, &userViewP );
1544| ArrX_Key_init( &key, OFFSET( poly, dist ), ArrX_Key_Double, ArrX_Key_Down );
1545| ArrX_Sort_init( &sort, sortBuf, sortBuf_sizeof, &key );
1546|
1547| /* ユーザ視点からポリゴンの重心までの距離を求め、ソート用配列に格納する */
1548| poly_over = polys->last;
1549| for ( poly = polys->first; poly < poly_over; poly++ ) {
1550| ThreeD_Poly_getCenter( poly->poly, ¢erP );
1551| poly->dist = ThreeD_XYZ_getDistance( ¢erP, &userViewP );
1552| ArrX_Sort_pushPQ( &sort, poly );
1553| }
1554| }
1555|
1556| /* ソート用配列から取り出しながら描画 ThreeD_Poly_draw2 する */
1557| {
1558| poly = ArrX_Sort_popPQ( &sort );
1559| while ( poly != NULL ) {
1560| XorPaint_clear( paint );
1561| Mask_A_initByPlain( paint->paintPattern, poly->poly->color );
1562| ThreeD_Poly_draw2( poly->poly, view, draw, scale, paint ); /*★*/
1563| poly = ArrX_Sort_popPQ( &sort );
1564| }
1565| }
1566|}
1567|
1568|#endif /* USES_MASK */
1569|#endif /* USES_ARRX */
1570|#endif /* USES_XORPAINT */
1571|#endif /* USES_SCALE */
1572|#endif /* USES_DRAW */
1573|
1574|
1575|
1576|#ifdef USES_DRAW
1577|#ifdef USES_SCALE
1578|#ifdef USES_XORPAINT
1579|#ifdef USES_ARRX
1580|#ifdef USES_MASK
1581|
1582|/*************************************************************************
1583|* 68. <<< [ThreeD_PolyS_drawSh] すべての影映りポリゴンを描画する(ソーティング)>>>
1584|*【補足】
1585|*・polys が ThreeD_ShPoly 型の配列である以外は、ThreeD_PolyS_draw 関数と
1586|* 同じです。
1587|**************************************************************************/
1588|void ThreeD_PolyS_drawSh( ArrX* polys, void** sortBuf, size_t sortBuf_sizeof,
1589| ThreeD_ViewL* view, Draw* draw, Scale* scale, XorPaint* paint )
1590|{
1591| ArrX_Sort sort; /* ソート用配列 */
1592| ArrX_Key key; /* キー */
1593| ThreeD_XYZ userViewP; /* ユーザ視点の座標 */
1594| ThreeD_XYZ centerP; /* ポリゴンの重心の座標 */
1595| ThreeD_PolyS* poly; /* 処理中のポリゴン */
1596| ThreeD_PolyS* poly_over;
1597|
1598| /* ソート用配列に格納 ArrX_Sort_pushPQ する */
1599| {
1600| ThreeD_ViewL_getUserViewP( view, &userViewP );
1601| ArrX_Key_init( &key, OFFSET( poly, dist ), ArrX_Key_Double, ArrX_Key_Down );
1602| ArrX_Sort_init( &sort, sortBuf, sortBuf_sizeof, &key );
1603|
1604| /* ユーザ視点からポリゴンの重心までの距離を求め、ソート用配列に格納する */
1605| poly_over = polys->last;
1606| for ( poly = polys->first; poly < poly_over; poly++ ) {
1607| ThreeD_Poly_getCenter( poly->poly, ¢erP );
1608| poly->dist = ThreeD_XYZ_getDistance( ¢erP, &userViewP );
1609| ArrX_Sort_pushPQ( &sort, poly );
1610| }
1611| }
1612|
1613| /* ソート用配列から取り出しながら描画 ThreeD_Poly_draw2 する */
1614| {
1615| poly = ArrX_Sort_popPQ( &sort );
1616| while ( poly != NULL ) {
1617| XorPaint_clear( paint );
1618| Mask_A_initByPlain( paint->paintPattern, poly->poly->color );
1619| ThreeD_ShPoly_draw2( (ThreeD_ShPoly*)poly->poly,
1620| view, draw, scale, paint ); /*★*/
1621| poly = ArrX_Sort_popPQ( &sort );
1622| }
1623| }
1624|}
1625|
1626|#endif /* USES_MASK */
1627|#endif /* USES_ARRX */
1628|#endif /* USES_XORPAINT */
1629|#endif /* USES_SCALE */
1630|#endif /* USES_DRAW */
1631|
1632|
1633|
1634|/*-----------------------------------------------------------------------*/
1635|/* 69. <<< ◆三次元空間内の二次元座標 (ThreeD_TwoD) >>> */
1636|/*-----------------------------------------------------------------------*/
1637|
1638|
1639|
1640|/*************************************************************************
1641|* 70. <<< [ThreeD_TwoD_initByP3] 3点からなる平面を二次元座標として初期化する >>>
1642|*【補足】
1643|*・X, Y 座標単位ベクトルは、次の図のように求めています。
1644|*
(y1, y0 が逆です[ミス])
1645|**************************************************************************/
1646|void ThreeD_TwoD_initByP3( ThreeD_TwoD* m, ThreeD_XYZ p[] )
1647|{
1648| ThreeD_Vect y0; /* Y 座標単位ベクトルの起点 */
1649| ThreeD_Vect y1; /* Y 座標単位ベクトルの終点 */
1650|
1651| /* X 座標単位ベクトルの初期化 */
1652| ThreeD_Vect_initBy2XYZ( &m->x_coord, &p[1], &p[0] );
1653| ThreeD_Vect_chgToUnit( &m->x_coord );
1654|
1655| /* Y 座標単位ベクトルの初期化 */
1656| ThreeD_Vect_initBy2XYZ( &y1, &p[2], &p[0] );
1657| ThreeD_Vect_getDirVect( &y1, &m->x_coord, &y0 );
1658| ThreeD_Vect_initBy2XYZ( &m->y_coord, &y1, &y0 ); /*★*/
1659| ThreeD_Vect_chgToUnit( &m->y_coord );
1660|}
1661|
1662|
1663|
1664|/*************************************************************************
1665|* 71. <<< [ThreeD_TwoD_print] 属性を表示する >>>
1666|**************************************************************************/
1667|#ifndef ERRORS_CUT_DEBUG_TOOL
1668|void ThreeD_TwoD_print( ThreeD_TwoD* m )
1669|{
1670| ThreeD_Vect_print( &m->x_coord, "X" );
1671| ThreeD_Vect_print( &m->y_coord, "Y" );
1672|}
1673|#endif
1674|
1675|
1676|