001 #ifndef ENGINE_2D
002 #define ENGINE_2D
003 #include "Primtives3D.HC";
004 //2 dimensional object
005 extern class C2DWorld;
006 class C2DObject:CQue {
007   U64 ident;
008   //See replace_color/with_color
009   #define C2DOBJF_REPLACE_COLOR 1
010   #define C2DOBJF_MESH 2 
011   I64 flags;
012   union {
013     CDC *graphics;
014     U8 *sprite;
015     CD6 *mesh;
016   }; 
017   F64 x,y,z;
018   F64 center_x,center_y;
019   F64 center_z;
020   F64 rot;
021   F64 rotx,roty;
022   F64 scale;
023   F64 priority;
024   F64 width,height;
025 //This matrix includes all transofrmations
026   U64 mat[16];
027   Bool flipped;
028   Bool is_sprite;
029   Bool is_cd6_mesh;
030   U8 replace_color;
031   union {
032     U8 with_color;
033     U8 color;
034   }
035   U8 pad[4];
036   CTask *mem_task;
037   I64 user_data,lock;
038   U8 name[STR_LEN];
039 //Private
040   I64 _blot_at_x,_blot_at_y;
041   CDC *_blot_dc;
042   C2DWorld *world;
043 };
044 CTask *mem_task=Fs;
045 class C2DWorld {
046   I64 background_color;
047   CDC *scrn;
048   CQue objects;
049   I64 in_draw;
050 };
051 C2DWorld *C2DWorldNew(I64 background_color=BLACK) {
052   C2DWorld *ret=GCCAlloc(sizeof C2DWorld);
053   QueInit(&ret->objects);
054   CDC *scrn;
055   ret->scrn=scrn=DCNew(GR_WIDTH,GR_HEIGHT);
056   scrn->transform=&WorldTransform;
057   scrn->x=scrn->width>>1;
058   scrn->y=scrn->height>>1;
059   scrn->z=-1;
060   Mat4x4IdentEqu(ret->scrn->r);
061   ret->background_color=background_color;
062   DCDepthBufAlloc(ret->scrn);
063   return ret;
064 }
065 U0 C2DObjectUpdateMat(C2DObject *obj) {
066   //This sets us to the identity matrix
067   Mat4x4IdentEqu(obj->mat);
068 
069   //1: move center of graphics to (0,0)
070   //2: rotate
071   //3: scale
072   //4: move to (x,y)
073 
074   //
075   // Move center to 0,0
076   // Becasue rotations are rotated around (0,0)
077   //
078   Mat4x4TranslationAdd(obj->mat,
079     -obj->center_x, //Use negative cordnates to move towards 0,0
080     -obj->center_y,
081     -obj->center_z);
082   //
083   //Flip the image by rotationg around the Y axis
084   //
085   if(obj->flipped)
086     Mat4x4RotY(obj->mat,pi);
087   //
088   // We need to rotate around the center
089   //
090   Mat4x4RotX(obj->mat,obj->rotx);
091   Mat4x4RotY(obj->mat,obj->roty);
092   Mat4x4RotZ(obj->mat,obj->rot);
093   //
094   // Scale the image
095   //
096   Mat4x4Scale(obj->mat,obj->scale);
097   //
098   // Now we move 
099   //
100   Mat4x4TranslationAdd(obj->mat,obj->x,obj->y,obj->z);
101 }
102 U0 C2DObjectFinalize(C2DObject *obj) {
103   if(!obj->is_sprite&&!obj->is_cd6_mesh)
104     DCDel(obj->graphics);
105   else if(obj->is_sprite)
106     Free(obj->sprite);
107 }
108 C2DObject *C2DObjectNewFromSprite(U8 *sprite) {
109   I64 min_x,max_x,min_y,max_y;
110   C2DObject *new=GCCAlloc(sizeof(C2DObject));
111   GCFinalize(new,&C2DObjectFinalize);
112   new->ident='Obj';
113   new->mem_task=mem_task;
114   SpriteExtents(sprite,&min_x,&max_x,&min_y,&max_y);
115   new->width=max_x-min_x;
116   new->height=max_y-min_y;
117   new->sprite=CAlloc(SpriteSize(sprite),mem_task);
118   MemCpy(new->sprite,sprite,SpriteSize(sprite));
119   new->is_sprite=TRUE;
120   Mat4x4IdentEqu(new->mat);
121   new->scale=1;
122   QueInit(new);
123   return new;
124 }
125 
126 
127 U0 C2DObjectSetGraphicsFromSprite(C2DObject *obj,U8 *sprite) {
128   I64 min_x,max_x,min_y,max_y;
129   if(!obj->is_sprite&&!obj->is_cd6_mesh)
130     DCDel(obj->graphics);
131   else if(obj->sprite)
132    Free(obj->sprite);
133   obj->sprite=CAlloc(SpriteSize(sprite),obj->mem_task);
134   MemCpy(obj->sprite,sprite,SpriteSize(sprite));
135   sprite=obj->sprite;
136   SpriteExtents(sprite,&min_x,&max_x,&min_y,&max_y);  
137   obj->width=max_x-min_x;
138   obj->center_x=0;
139   obj->center_y=0;
140   obj->sprite=sprite;
141   obj->is_sprite=TRUE;
142   C2DObjectUpdateMat(obj);
143 }
144 
145 U0 C2DObjectSetMesh(C2DObject *obj,CD6 *mesh) {
146   if(!obj->is_sprite&&!obj->is_cd6_mesh)
147     DCDel(obj->graphics);
148   else if(obj->sprite)
149    Free(obj->sprite);
150   obj->is_cd6_mesh=TRUE;
151   obj->mesh=mesh;
152   obj->center_x=0;
153   obj->center_y=0;
154   obj->width=CD6Width(mesh);
155   obj->height=CD6Height(mesh);
156   C2DObjectUpdateMat(obj);
157 }
158 C2DObject *C2DObjectNewFromMesh(CD6 *mesh) {
159   C2DObject *new=GCCAlloc(sizeof C2DObject);
160   GCFinalize(new,&C2DObjectFinalize);
161   new->ident='Obj';
162   new->color=WHITE;
163   new->mem_task=mem_task;
164   Mat4x4IdentEqu(new->mat);
165   new->scale=1;
166   QueInit(new);
167   C2DObjectSetMesh(new,mesh);
168   return new;
169 }
170 
171 C2DObject *C2DObjectNewFromDC(CDC *img) {
172   C2DObject *new=GCCAlloc(sizeof C2DObject);
173   GCFinalize(new,&C2DObjectFinalize);
174   new->ident='Obj';
175   new->mem_task=mem_task;
176   new->graphics=DCCopy(img,mem_task);
177   new->center_x=img->width/2 ;
178   new->center_y=img->height/2;
179   new->width=img->width;
180   new->height=img->height;
181   Mat4x4IdentEqu(new->mat);
182   new->scale=1;
183   QueInit(new);
184   return new;
185 }
186 U0 C2DObjectMoveCenter(C2DObject *obj,F64 x,F64 y,F64 z=0) {
187   obj->center_x=x,obj->center_y=y;
188   obj->center_z=z;
189   C2DObjectUpdateMat(obj);  
190 }
191 U0 C2DObjectMove(C2DObject *obj,F64 x,F64 y,F64 z=0) {
192   obj->x=x,obj->y=y,obj->z=z;
193   C2DObjectUpdateMat(obj);
194 }
195 U0 C2DObjectRotate(C2DObject *obj,F64 rot=0) {
196   obj->rot=rot;
197   C2DObjectUpdateMat(obj);
198 }
199 U0 C2DObjectScale(C2DObject *obj,F64 s) {
200   obj->scale=s;
201   C2DObjectUpdateMat(obj);  
202 }
203 U0 C2DObjectSetGraphics(C2DObject *obj,CDC *gr) {
204   if(!obj->is_sprite&&!obj->is_cd6_mesh)
205     DCDel(obj->graphics);
206   else if(obj->sprite)
207    Free(obj->sprite);
208   obj->graphics=DCCopy(gr,obj->mem_task);
209   obj->center_x=gr->width/2 ;
210   obj->center_y=gr->height/2;
211   obj->width=gr->width;
212   obj->height=gr->height;
213   C2DObjectUpdateMat(obj);
214 }
215 
216 U0 PrepareForBlot(C2DObject *obj) {
217   I64 dummy,min_x,min_y,max_x,max_y;
218   CDC *sprite_dc;
219   CD2I64 a,b,c,d;
220   C2DObjectUpdateMat(obj);
221   if(!obj->is_sprite)
222     ; //GrBlot3(to,0,0,depth_offset,obj->graphics);
223   else if(!(obj->flags&C2DOBJF_MESH)) {
224     SpriteExtents(obj->sprite,&a.x,&b.x,&a.y,&b.y);
225     c.x=a.x;
226     c.y=b.y;
227     d.x=b.x;
228     d.y=a.y;
229     Mat4x4MulXYZ(obj->mat,&a.x,&a.y,&dummy);
230     Mat4x4MulXYZ(obj->mat,&b.x,&b.y,&dummy);
231     Mat4x4MulXYZ(obj->mat,&c.x,&c.y,&dummy);
232     Mat4x4MulXYZ(obj->mat,&d.x,&d.y,&dummy);
233     min_x=MinI64(a.x,b.x);
234     min_x=MinI64(min_x,c.x);
235     min_x=MinI64(min_x,d.x);
236 
237     min_y=MinI64(a.y,b.y);
238     min_y=MinI64(min_y,c.y);
239     min_y=MinI64(min_y,d.y);
240 
241     max_x=MaxI64(a.x,b.x);
242     max_x=MaxI64(max_x,c.x);
243     max_x=MaxI64(max_x,d.x);
244 
245     max_y=MaxI64(a.y,b.y);
246     max_y=MaxI64(max_y,c.y);
247     max_y=MaxI64(max_y,d.y);
248 
249     sprite_dc=DCNew(
250           Clamp(max_x-min_x+1,1,GR_WIDTH),
251           Clamp(max_y-min_y+1,1,GR_HEIGHT));
252     DCFill(sprite_dc);
253     Sprite3Mat4x4B(sprite_dc,-min_x,-min_y,0,obj->sprite,obj->mat);
254 //Compute center
255     c.x=0,c.y=0;
256     Mat4x4MulXYZ(obj->mat,&c.x,&c.y,&dummy);
257     obj->_blot_at_x=c.x-(c.x-min_x);
258     obj->_blot_at_y=c.y-(c.y-min_y);
259     obj->_blot_dc=sprite_dc;
260   }
261 }
262 U0 C2DObjectRemoveFromWorld(C2DObject *obj) {
263   QueRem(obj);
264   QueInit(obj);
265 }
266 U0 C2DObjectDel(C2DObject *obj) {
267   QueRem(obj);
268   if(!obj->is_sprite&&!obj->is_cd6_mesh)
269     DCDel(obj->graphics);
270   GCFree(obj);
271 }
272 U0 C2DWorldDel(C2DWorld *world) {
273   C2DObject *cur,*next;
274   I64 idx;
275   while(LBts(&world->in_draw,0))
276     Sleep(1);
277   for(cur=world->objects.next;cur!=&world->objects;) {
278     next=cur->next;
279     C2DObjectDel(cur);
280     cur=next;
281   }
282   GCFree(world);;
283 }
284 U0 AddObjectToWorld(C2DObject *obj,C2DWorld *world) {
285   while(LBts(&world->in_draw,0))
286     Yield;
287   C2DObject *head=&world->objects,*cur=head->next; 
288   if(!obj->priority)
289     obj->priority=QueCnt(head);
290   while(cur!=head) {
291     if(obj->priority<cur->priority) {
292       QueInsRev(obj,cur);
293       goto fin;
294     }
295     cur=cur->next;
296   }
297   obj->world=world;
298   QueIns(obj,head->last);
299 fin:
300   LBtr(&world->in_draw,0);
301 }
302 class CMPDrawItNugget {
303   CDC *to;
304   C2DWorld *world;
305   I64 start,end;
306   CMPDrawItNugget *last,*next;
307   I64 ready_to_merge;
308   I64 merged;
309   CTask *parent;
310   I64 done;
311 };
312 U0 MPDrawIt(CMPDrawItNugget *nugget) {
313   C2DObject *head=&nugget->world->objects,*cur=head->next;
314   I64 old_flags;
315   U8 *old_mat;
316   if(!nugget->to) {
317     nugget->to=DCNew(
318           nugget->world->scrn->width,
319           nugget->world->scrn->height,
320           nugget->world->scrn->mem_task);
321     DCFill(nugget->to,TRANSPARENT);
322     nugget->to->flags|=nugget->world->scrn->flags&DCF_TRANSFORMATION;
323     Mat4x4Equ(nugget->to->r,nugget->world->scrn->r);
324   }
325   I64 i;
326   for(i=0;i<nugget->end&&cur!=head;i++) {
327     if(nugget->start<=i) {
328       PrepareForBlot(cur);
329       if(cur->flags&C2DOBJF_MESH) {
330         C2DObjectUpdateMat(cur);
331         Sprite3Mat4x4B(nugget->to,0,0,0,cur->sprite,cur->mat);
332       } else if(!cur->is_sprite) {
333         old_flags=nugget->to->flags;
334         old_mat=nugget->to->r;
335         nugget->to->flags|=DCF_TRANSFORMATION;
336         nugget->to->r=cur->mat;
337         if(cur->graphics) {
338           if(cur->flags&C2DOBJF_REPLACE_COLOR)
339             DCColorChg(cur->graphics,cur->replace_color,cur->with_color);
340           GrBlot3(nugget->to,0,0,0,cur->graphics);
341         }
342         nugget->to->r=old_mat;
343         nugget->to->flags=old_flags;
344       } else {
345         if(cur->flags&C2DOBJF_REPLACE_COLOR)
346           DCColorChg(cur->_blot_dc,cur->replace_color,cur->with_color);
347         GrBlot(nugget->to,cur->_blot_at_x,cur->_blot_at_y,cur->_blot_dc);
348         DCDel(cur->_blot_dc);
349         cur->_blot_dc=NULL;
350       }
351     }
352     cur=cur->next;
353   }
354   nugget->parent->wake_jiffy=0;
355   LBts(&nugget->done,0);
356 }
357 I64 PrioritySort(C2DObject *a,C2DObject *b) {
358   if(b->priority>a->priority) return 1;
359   if(b->priority<a->priority) return -1;
360   return 0;
361 }
362 U0 WorldSortByPriority(C2DWorld *w) {
363   I64 cnt=QueCnt(&w->objects),i;
364   C2DObject **sorted=GCCAlloc(8*cnt);
365   C2DObject *head=&w->objects,*cur=head->next;
366   for(cnt=0;cur!=head;cnt++) {
367     sorted[cnt]=cur;
368     cur=cur->next;
369   }
370   QSortI64(sorted,cnt,&PrioritySort);
371   QueInit(&w->objects);
372   for(i=0;i!=cnt;i++) {
373     QueIns(sorted[i],&w->objects);
374   }
375   GCFree(sorted);
376 } 
377 U0 DrawWorld(CTask *t,C2DWorld *world) {
378   if(!t) t=Fs;
379   LBts(&world->in_draw,0);
380   I64 i,core_cnt,i2=0,cap;
381   I64 old_flags,*old_mat;
382   U8 *otrans;
383   CDC *scrn=world->scrn;
384   CMPDrawItNugget nuggets[mp_cnt];
385   MemSet(nuggets,0,sizeof(CMPDrawItNugget)*mp_cnt);
386   scrn->color=world->background_color;
387   DCDepthBufRst(scrn);
388   DCFill(scrn,TRANSPARENT);
389   TextRect(t->win_left,t->win_right,t->win_top,t->win_bottom,world->background_color<<12);
390 //head points to the head of the CQue
391   C2DObject *head=&world->objects,*cur=head->next;
392   WorldSortByPriority(world);
393 //Because CQue's wrap around,we check for head as the last element
394 
395   //  A is the head
396   // 
397   //   A->B->C-\
398   //   ^       |
399   //   |       |
400   //   \-------/
401   //
402   one:
403   cur=head->next;
404   while(cur!=head) {
405     PrepareForBlot(cur);
406     cur=cur->next;
407   }
408   i2=QueCnt(head);
409   cur=head->next;
410   while(i2--) {
411     if(cur->is_cd6_mesh) {
412       scrn->thick=1;
413       scrn->color=cur->color;
414       otrans=scrn->transform;
415       old_flags=scrn->flags;
416       scrn->flags|=DCF_TRANSFORMATION;
417       CD6Draw(cur->mesh,0,0,0,scrn,cur->mat);
418       scrn->transform=otrans;
419     } else if(cur->flags&C2DOBJF_MESH) {
420       C2DObjectUpdateMat(cur);
421       Sprite3Mat4x4B(scrn,0,0,0,cur->sprite,cur->mat);
422     } else if(!cur->is_sprite) {
423       old_flags=scrn->flags;
424       old_mat=scrn->r;
425       scrn->flags|=DCF_TRANSFORMATION;
426       scrn->r=cur->mat;
427       if(cur->graphics) {
428         if(cur->flags&C2DOBJF_REPLACE_COLOR)
429           DCColorChg(cur->graphics,cur->replace_color,cur->with_color);
430         GrBlot3(scrn,0,0,cur->z,cur->graphics);
431       }
432       scrn->r=old_mat;
433       scrn->flags=old_flags;
434     } else {
435       if(cur->flags&C2DOBJF_REPLACE_COLOR)
436         DCColorChg(cur->_blot_dc,cur->replace_color,cur->with_color);
437       GrBlot3(scrn,cur->_blot_at_x,cur->_blot_at_y,cur->z,cur->_blot_dc);
438       DCDel(cur->_blot_dc);
439     }
440     cur=cur->next;
441   }
442   LBtr(&world->in_draw,0);
443 }
444 U0 C2DObjectPointTo(C2DObject *obj,F64 x,F64 y) {
445   //We want to make the x,y relative  to the obj's cordnates
446   x-=obj->x;
447   y-=obj->y;
448   //https://en.wikipedia.org/wiki/Atan2
449   //ATan2 gets the angle that points to (x,y)
450   obj->rot=Arg(x,y);
451   C2DObjectUpdateMat(obj);
452 }
453 //
454 // Test section
455 //
456 class CD2F64 {
457   F64 x,y;
458 };
459 U0 CrossProduct(CD2F64 *dst,CD2F64 *a,CD2F64 *b) {
460   dst->x=a->y-b->y;
461   dst->y=a->x-b->x;
462 }
463 Bool InRange(F64 a,F64 v,F64 b) {
464   if(a>b)
465    return a>=v>=b;
466   return a<=v<=b;
467 }
468 class CD2F64 {
469   F64 x,y;
470 };
471 U0 CrossProduct(CD2F64 *dst,CD2F64 *a,CD2F64 *b) {
472   dst->x=a->y-b->y;
473   dst->y=a->x-b->x;
474 }
475 Bool InRange(F64 a,F64 v,F64 b) {
476   if(a>b)
477    return a>=v>=b;
478   return a<=v<=b;
479 }
480 Bool PlaneIntersect(CD2I64 *dst,CD2I64 *a,CD2I64 *b,CD2I64 *a2,CD2I64 *b2) {  
481 /*
482 * Nroot here,Heres the deal,I make a system of 2 linear equations and solve for an intersect
483 * I solve for the intersect and check if they are in bounds of points
484 
485 Check for intersection basically.
486 
487      /
488 ----*------
489    / 
490   /
491  /
492 */
493   F64 slope1,slope2,off1,off2;
494   CD2I64 dummy;
495   if(!dst) dst=&dummy;
496 //If the line points straight up,we can check to see if the other
497   //line goes through the y position,if so,it is a hit
498   if((b->x==a->x)||(b2->x==a2->x)) {
499     if(a->x==b->x&&a2->x==b2->x) {
500       if(a->x==a2->x) {
501         dst->x=a->x;
502         dst->y=a->y;
503         goto fin;
504       } else
505         return FALSE;
506     }
507     if(a->x==b->x) {
508       dst->x=a->x;
509       slope2=(ToF64(b2->y)-a2->y)/(ToF64(b2->x)-a2->x);
510       dst->y=slope2*(dst->x-a2->x)+a2->y;
511       goto fin;
512     }
513     if(a2->x==b2->x) {
514       dst->x=a2->x;
515       slope1=(ToF64(b->y)-a->y)/(ToF64(b->x)-a->x);
516       dst->y=slope1*(dst->x-a->x)+a->y;
517       goto fin;
518     }
519     goto fin;
520   }
521 //https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
522   slope2=(ToF64(b2->y)-a2->y)/(ToF64(b2->x)-a2->x);
523   slope1=(ToF64(b->y)-a->y)/(ToF64(b->x)-a->x);
524   off1=slope1*-a->x+a->y;
525   off2=slope2*-a2->x+a2->y;
526   dst->x=((off2-off1)/(slope1-slope2));
527   dst->y=slope1*(dst->x)+off1;
528 fin:
529   return InRange(a->x,dst->x,b->x)&&
530         InRange(a2->x,dst->x,b2->x)&&
531         InRange(a->y,dst->y,b->y)&&
532         InRange(a2->y,dst->y,b2->y);
533 }
534 Bool C2DObjectCollisionTest(C2DObject *o1,C2DObject *o2,CD2I64 *hit_at=NULL) {
535   I64 nul=1,i,i2;
536   I64 o1_mat[16],o2_mat[16];
537   CD2I64 *o1_edges[5];
538   CD2I64 *o2_edges[5];
539   CD2I64 a,b,c,d;
540   CD2I64 a2,b2,c2,d2;
541   if(!o1->is_sprite) {
542     a.x=0;
543     b.x=0+o1->width;
544     c.x=0+o1->width;
545     d.x=0;
546 
547     a.y=0;
548     b.y=0;
549     c.y=0+o1->height;
550     d.y=0+o1->height;
551   } else {
552     SpriteExtents(o1->sprite,&a.x,&b.x,&a.y,&c.y);
553     d.x=a.x; //min
554     c.x=b.x; //max
555     b.y=a.y; //min
556     d.y=c.y; //max
557   }
558 
559   if(!o2->is_sprite) {
560     a2.x=0;
561     b2.x=0+o2->width;
562     c2.x=0+o2->width;
563     d2.x=0;
564 
565     a2.y=0;
566     b2.y=0;
567     c2.y=0+o2->height;
568     d2.y=0+o2->height;
569   } else {
570     SpriteExtents(o2->sprite,&a2.x,&b2.x,&a2.y,&c2.y);
571     d2.x=a2.x; //min
572     c2.x=b2.x; //max
573     b2.y=a2.y; //min
574     d2.y=c2.y; //max
575   }
576 
577   C2DObjectUpdateMat(o1);
578   C2DObjectUpdateMat(o2);
579   Mat4x4Equ(o1_mat,o1->mat);
580   Mat4x4Equ(o2_mat,o2->mat);
581 //  Mat4x4Scale(o1_mat,1<<16);
582 //  Mat4x4Scale(o2_mat,1<<16);
583   nul=0;
584   Mat4x4MulXYZ(o1_mat,&a.x,&a.y,&nul);
585   nul=0;
586   Mat4x4MulXYZ(o1_mat,&b.x,&b.y,&nul);
587   nul=0;
588   Mat4x4MulXYZ(o1_mat,&c.x,&c.y,&nul);
589   nul=0;
590   Mat4x4MulXYZ(o1_mat,&d.x,&d.y,&nul);
591 
592   nul=0;
593   Mat4x4MulXYZ(o2_mat,&a2.x,&a2.y,&nul);
594   nul=0;
595   Mat4x4MulXYZ(o2_mat,&b2.x,&b2.y,&nul);
596   nul=0;
597   Mat4x4MulXYZ(o2_mat,&c2.x,&c2.y,&nul);
598   nul=0;
599   Mat4x4MulXYZ(o2_mat,&d2.x,&d2.y,&nul);
600 
601   o1_edges[0]=&a;
602   o1_edges[1]=&b;
603   o1_edges[2]=&c;
604   o1_edges[3]=&d;
605   o1_edges[4]=&a;
606 
607   o2_edges[0]=&a2;
608   o2_edges[1]=&b2;
609   o2_edges[2]=&c2;
610   o2_edges[3]=&d2;
611   o2_edges[4]=&a2;
612 
613   for(i=0;i!=4;i++)
614     for(i2=0;i2!=4;i2++) {
615       if(PlaneIntersect(hit_at,o1_edges[i],o1_edges[i+1],
616             o2_edges[i2],o2_edges[i2+1])) {
617         if(hit_at) hit_at->x/=(1<<16);
618         if(hit_at) hit_at->y/=(1<<16);
619         return TRUE;
620       }
621     }
622   return FALSE;
623 }
624 U0 C2DObjectSetFlip(C2DObject *sp,Bool flip) {
625   sp->flipped=flip;
626   C2DObjectUpdateMat(sp);
627 }
628 C2DObject *C2DObjectCopy(C2DObject *src) {
629   C2DObject *copy=GCMAllocIdent(src);
630   Mat4x4Equ(copy->mat,src->mat);
631   copy->mem_task=mem_task;
632   if(copy->is_sprite)
633     copy->sprite=src->sprite;
634   else if(copy->is_cd6_mesh)
635     copy->mesh=src->mesh;
636   else
637     copy->graphics=DCCopy(src->graphics,mem_task);
638   return copy;
639 }
640 #endif