/******************/ /*** Actor Bank ***/ /******************/ #include "system\vid.h" #include "system\global.h" #include "mem\memory.h" #include "fileio\fileio.h" #include "utils\utils.h" #include "gfx\prim.h" #include "gfx\actor.h" #include "gfx\otpos.h" #include "gfx\animtex.h" #include /*****************************************************************************/ const int BBOX_ADJ=4; /*****************************************************************************/ CActorCache CActorPool::Cache; sActorPool *CActorPool::ActorList,*CActorPool::LastActor; /*****************************************************************************/ /*** Cache *******************************************************************/ /*****************************************************************************/ CActorCache::CActorCache() { for (int i=0;i=CACHE_TYPE_MAX) { // No free slots, grab an existing one, and alter size to fit Slot=ReAllocSlot(W,H); } else { Slot=SlotCount; SlotList[Slot].Width=W; SlotList[Slot].Height=H; SlotCount++; } } // SlotList[Slot].RefCount++; return(Slot); } /*****************************************************************************/ sPoolNode *CActorCache::RemoveHeadNode(sNodeList *RootNode) { sPoolNode *ThisNode=RootNode->Head; sPoolNode *NextNode=ThisNode->Next; RootNode->Head=NextNode; ThisNode->Next=0; if (NextNode) { NextNode->Prev=0; } else { // Its Tail Node RootNode->Tail=0; } return(ThisNode); } /*****************************************************************************/ void CActorCache::RemoveNode(sPoolNode *ThisNode,sNodeList *RootNode) { sPoolNode *PrevNode=ThisNode->Prev; sPoolNode *NextNode=ThisNode->Next; if (PrevNode) { // Not Head Node PrevNode->Next=NextNode; } else { // Head Node RootNode->Head=NextNode; } if (NextNode) { // Not Tail Node NextNode->Prev=PrevNode; } else { // Tail Node RootNode->Tail=PrevNode; } ThisNode->Next=0; ThisNode->Prev=0; } /*****************************************************************************/ // Add node to end of list void CActorCache::AddNode(sPoolNode *ThisNode,sNodeList *RootNode) { sPoolNode *TailNode=RootNode->Tail; if (TailNode) { TailNode->Next=ThisNode; } else { // List is empty RootNode->Head=ThisNode; } ThisNode->Prev=TailNode; ThisNode->Next=0; ASSERT(ThisNode); RootNode->Tail=ThisNode; } /*****************************************************************************/ // Add node list to end of list void CActorCache::AddNodeList(sNodeList *Src,sNodeList *Dst) { sPoolNode *SrcHead=Src->Head; sPoolNode *SrcTail=Src->Tail; sPoolNode *DstHead=Dst->Head; sPoolNode *DstTail=Dst->Tail; /* if (!SrcHead) return; if (DstTail) { DstTail->Next=SrcHead; } else { // List is empty Dst->Head=SrcHead; } SrcHead->Prev=DstTail; Dst->Tail=SrcTail; Src->Head=0; Src->Tail=0; */ while(SrcHead) { sPoolNode *Next=SrcHead->Next; AddNode(SrcHead,Dst); SrcHead=Next; } } /*****************************************************************************/ int CActorCache::ReserveSlot(int W,int H,int FrameCount) { int Slot=GetSlot(W,H); // SlotList[Slot].FrameCount+=FrameCount; return(Slot); } /*****************************************************************************/ void CActorCache::AllocCache() { int TPW=CACHE_W/SlotCount; int MaxW=0; int MaxH=0; TPW=1; ASSERT(SlotCount<=CACHE_W); for (int i=0; iWidth; int H=(TPAGE_H)/ThisSlot->Height; int Total=W*H; sPoolNode *List; // Init List ThisSlot->SlotCount=Total; ThisSlot->ListMem=(u8*)MemAlloc(Total*sizeof(sPoolNode),"CacheNodeList"); List=(sPoolNode*)ThisSlot->ListMem; // Create List Entries for (int Y=0; YWidth); int V=(Y*ThisSlot->Height); int TexX=(CACHE_X+CurrentTPX+(U>>2)); int TexY=(CACHE_Y+V); List->Frame=0; List->DstRect.x=TexX; List->DstRect.y=TexY; List->U=U&255; List->V=V&255; List->TPage=getTPage(0,0,TexX,TexY); AddNode(List,&ThisSlot->NodeList); List++; } } CurrentTPX+=(TPW*TPAGE_W)>>2; } /*****************************************************************************/ void CActorCache::Reset() { // Free and init lists for (int i=0;iActorGfx->Clut) { RECT R; int PalNo; if (Actor->Filename==ACTORS_SPONGEBOB_SBK) { PalNo=0; } else { PalNo=CurrentPalette+1; } int X=PalNo%CACHE_W; int Y=PalNo/CACHE_W; R.x=CACHE_PALX+(X*CACHE_PALW); R.y=CACHE_PALY-Y; R.w=CACHE_PALW; R.h=CACHE_PALH; DrawSync(0); LoadImage( &R, (u32*)Actor->ActorGfx->Palette); Actor->ActorGfx->Clut=getClut(R.x,R.y); CurrentPalette++; } } /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ void CActorPool::Init() { ActorList=0; Cache.Reset(); } /*****************************************************************************/ void CActorPool::Reset() { sActorPool *List=ActorList; while (List) { sActorPool *Next=List->Next; if (List->Filename!=ACTORS_SPONGEBOB_SBK) { MemFree(List->ActorGfx); delete List; } else { List->Next=0; } List=Next; } LastActor=ActorList; Cache.Reset(); } /*****************************************************************************/ void CActorPool::SetUpCache() { Cache.AllocCache(); // SetUp Actors sActorPool *List=ActorList; while (List) { List->GlobalCache=Cache.GetSlotList(List->CacheSlot); List->LocalCache.Head=0; List->LocalCache.Tail=0; List->LastCache.Head=0; List->LastCache.Tail=0; List=List->Next; } } /*****************************************************************************/ sActorPool *CActorPool::FindActor(FileEquate Filename) { sActorPool *List=ActorList; while (List) { if (List->ActorGfx && List->Filename==Filename) return(List); List=List->Next; } return(0); } /*****************************************************************************/ CActorGfx *CActorPool::GetActor(FileEquate Filename) { sActorPool *ThisActor; CActorGfx *NewActor; int TotalFrames=0; bool NewFlag=false; // Find Actor in Pool ThisActor=FindActor(Filename); if (!ThisActor) { ThisActor=LoadActor(Filename); NewFlag=true; } NewActor=new ("CActorGfx") CActorGfx(ThisActor); if (NewFlag) { TotalFrames=NewActor->GetTotalFrameCount(); } ThisActor->CacheSlot=Cache.ReserveSlot(ThisActor->ActorGfx->MaxW,ThisActor->ActorGfx->MaxH,TotalFrames); return(NewActor); } /*****************************************************************************/ sActorPool *CActorPool::LoadActor(FileEquate Filename) { int i; int TotalFrames=0; sSpriteAnimBank *Spr=(sSpriteAnimBank*)CFileIO::loadFile(Filename,"ActorGfx"); Spr->AnimList=(sSpriteAnim*) MakePtr(Spr,(int)Spr->AnimList); Spr->FrameList=(sSpriteFrameGfx*) MakePtr(Spr,(int)Spr->FrameList); Spr->Palette=(u8*) MakePtr(Spr,(int)Spr->Palette); // FixUp AnimList for (i=0; iAnimCount; i++) { sSpriteAnim *ThisAnim=&Spr->AnimList[i]; ThisAnim->Anim=(sSpriteFrame*) MakePtr(Spr,(int)ThisAnim->Anim); TotalFrames+=ThisAnim->FrameCount; } // FixUp FrameList (not blank ones) for (i=0; iFrameCount; i++) { sSpriteFrameGfx *ThisFrame=&Spr->FrameList[i]; if (ThisFrame->PAKSpr) { ThisFrame->PAKSpr=(u8*) MakePtr(Spr,(int)ThisFrame->PAKSpr); } } // Store it sActorPool *NewActor; NewActor=new ("ActorPool") sActorPool(); NewActor->Filename=Filename; NewActor->ActorGfx=Spr; AddActor(NewActor); return(NewActor); } /*****************************************************************************/ void CActorPool::AddActor(sActorPool *NewActor) { // Insert into list if (!LastActor) { // Empty List ActorList=NewActor; } else { LastActor->Next=NewActor; } NewActor->Next=0; Cache.LoadPalette(NewActor); LastActor=NewActor; } /*****************************************************************************/ void CActorPool::CleanUpCache() { sActorPool *Actor=ActorList; while (Actor) { Actor->LastCache.Head=Actor->LocalCache.Head; Actor->LastCache.Tail=Actor->LocalCache.Tail; CActorCache::AddNodeList(&Actor->LocalCache ,Actor->GlobalCache); Actor->LocalCache.Head=0; Actor->LocalCache.Tail=0; ASSERT(Actor->GlobalCache->Head); ASSERT(Actor->GlobalCache->Tail); Actor=Actor->Next; } } /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ CActorGfx::CActorGfx(sActorPool *ThisActor) { setActor(ThisActor); } /*****************************************************************************/ CActorGfx::~CActorGfx() { } /*****************************************************************************/ void CActorGfx::setActor(sActorPool *ThisActor) { PoolEntry=ThisActor; OtPos=OTPOS__ACTOR_POS; } /*****************************************************************************/ POLY_FT4 *CActorGfx::Render(DVECTOR &Pos,int Anim,int Frame,bool XFlip,bool YFlip) { sPoolNode *ThisNode,*FindNode; POLY_FT4 *Ft4; // Calc Frame Ptrs sSpriteAnimBank *SpriteBank=PoolEntry->ActorGfx; sSpriteAnim *ThisAnim=SpriteBank->AnimList+Anim; CurrentFrame=&ThisAnim->Anim[Frame]; CurrentFrameGfx=&SpriteBank->FrameList[CurrentFrame->FrameIdx]; if (!CurrentFrameGfx->PAKSpr) return(0); // Blank Frame // Try to find Pre-cached sprite ThisNode=0; // Check Local Cache FindNode=PoolEntry->LocalCache.Head; while (FindNode) { // Try local Cache (From Head forward) if (FindNode->Frame==CurrentFrameGfx) { ThisNode=FindNode; break; } FindNode=FindNode->Next; } // Check Last Cache if (!ThisNode) { FindNode=PoolEntry->LastCache.Head; while (FindNode) { if (FindNode->Frame==CurrentFrameGfx) { ThisNode=FindNode; CActorCache::RemoveNode(ThisNode,PoolEntry->GlobalCache); CActorCache::AddNode(ThisNode,&PoolEntry->LocalCache); break; } if (FindNode==PoolEntry->LastCache.Tail) break; FindNode=FindNode->Next; } } // Check Global Cache (From Tail back) if (!ThisNode) { FindNode=PoolEntry->GlobalCache->Tail; while (FindNode) { if (FindNode->Frame==CurrentFrameGfx) { ThisNode=FindNode; CActorCache::RemoveNode(ThisNode,PoolEntry->GlobalCache); CActorCache::AddNode(ThisNode,&PoolEntry->LocalCache); break; } FindNode=FindNode->Prev; } } // Could not find it, get new if (!ThisNode) { // Not cached frame ThisNode=CActorCache::RemoveHeadNode(PoolEntry->GlobalCache); ASSERT(ThisNode); CActorCache::AddNode(ThisNode,&PoolEntry->LocalCache); ThisNode->Frame=CurrentFrameGfx; ThisNode->DstRect.w=CurrentFrameGfx->W>>2; // div 4 cos 16 color ThisNode->DstRect.h=CurrentFrameGfx->H; CPakTex::Add(CurrentFrameGfx->PAKSpr,&ThisNode->DstRect); } Ft4=GetPrimFT4(); SetUpFT4(Ft4,ThisNode,Pos.vx,Pos.vy,XFlip,YFlip); Ft4->tpage=ThisNode->TPage; Ft4->clut=PoolEntry->ActorGfx->Clut; setShadeTex(Ft4,1); addPrim(OtPtr+OtPos,Ft4); // Set BBox // Sizes now depend on aspect corrected sizes, so get sizes back from poly int BBoxW=Ft4->x1-Ft4->x0; int BBoxH=Ft4->y2-Ft4->y0; int HalfW=BBoxW>>1; BBox.XMin=-HalfW+BBOX_ADJ; BBox.XMax=+HalfW-BBOX_ADJ; BBox.YMin=-BBoxH+BBOX_ADJ; BBox.YMax=0-BBOX_ADJ; return(Ft4); } /*****************************************************************************/ POLY_FT4 *CActorGfx::RotateScale(POLY_FT4 *Ft4,DVECTOR &Pos,int Angle,int XScale,int YScale) { int dX,dY; int CosAngle,SinAngle; int CosX,CosY,SinX,SinY; sBBox SBox,CBox; int W=Ft4->x1-Ft4->x0; int H=Ft4->y2-Ft4->y0; Angle&=4095; // dX=(CurrentFrameGfx->W*XScale)>>(12+1); // +1 for half // dY=(CurrentFrameGfx->H*YScale)>>(12); dX=(W*XScale)>>(12+1); // +1 for half dY=(H*YScale)>>(12); CosAngle=mcos(Angle); SinAngle=msin(Angle); SBox.XMin=(SinAngle*-dX)>>12; SBox.XMax=(SinAngle*+dX)>>12; SBox.YMin=(SinAngle*-dY)>>12; SBox.YMax=0; CBox.XMin=(CosAngle*-dX)>>12; CBox.XMax=(CosAngle*+dX)>>12; CBox.YMin=(CosAngle*-dY)>>12; CBox.YMax=0; int x0,x1,x2,x3; int y0,y1,y2,y3; int XMin,XMax; int YMin,YMax; x0=CBox.XMin-SBox.YMin; y0=SBox.XMin+CBox.YMin; x1=CBox.XMax-SBox.YMin; y1=SBox.XMax+CBox.YMin; x2=CBox.XMin+SBox.YMax; y2=SBox.XMin-CBox.YMax; x3=CBox.XMax+SBox.YMax; y3=SBox.XMax-CBox.YMax; XMin=x0; if (XMin>x1) XMin=x1; if (XMin>x2) XMin=x2; if (XMin>x3) XMin=x3; XMax=x0; if (XMaxy1) YMin=y1; if (YMin>y2) YMin=y2; if (YMin>y3) YMin=y3; YMax=y0; if (YMaxx0=Pos.vx+x0; Ft4->y0=Pos.vy+y0; Ft4->x1=Pos.vx+x1; Ft4->y1=Pos.vy+y1; Ft4->x2=Pos.vx+x2; Ft4->y2=Pos.vy+y2; Ft4->x3=Pos.vx+x3; Ft4->y3=Pos.vy+y3; return(Ft4); } /*****************************************************************************/ void CActorGfx::getFrameOffsets(int _anim,int _frame,int *_x,int *_y) { sSpriteAnimBank *SpriteBank; sSpriteAnim *ThisAnim; sSpriteFrame *pFrame; SpriteBank=PoolEntry->ActorGfx; ThisAnim=SpriteBank->AnimList+_anim; pFrame=&ThisAnim->Anim[_frame]; *_x=pFrame->XOfs; *_y=pFrame->YOfs; } /*****************************************************************************/ void CActorGfx::SetUpFT4(POLY_FT4 *Ft4,sPoolNode *Node,int X,int Y,bool XFlip,bool YFlip) { u8 W=CurrentFrameGfx->W-1; u8 H=CurrentFrameGfx->H-1; u8 U=Node->U; u8 V=Node->V; if (XFlip) { X-=CurrentFrame->XOfs; X-=W; Ft4->u0=U+W; Ft4->u1=U;//-1;// Ft4->u2=U+W; Ft4->u3=U;//-1;// } else { X+=CurrentFrame->XOfs; Ft4->u0=U; Ft4->u1=U+W; Ft4->u2=U; Ft4->u3=U+W; } if (YFlip) { Y-=CurrentFrame->YOfs; Y-=H; Ft4->v0=V+H; Ft4->v1=V+H; Ft4->v2=V-1;// Ft4->v3=V-1;// } else { Y+=CurrentFrame->YOfs; Ft4->v0=V; Ft4->v1=V; Ft4->v2=V+H; Ft4->v3=V+H; } setXYWH(Ft4,X,Y,W,H); #if INGAME_SCREENW==512 // Correct Aspect Ft4->x0-=CurrentFrameGfx->AspectX0; Ft4->x1+=CurrentFrameGfx->AspectX1; Ft4->x2-=CurrentFrameGfx->AspectX0; Ft4->x3+=CurrentFrameGfx->AspectX1; #endif } /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ /*****************************************************************************/ sModel *CModelGfx::ModelTable; sElem3d *CModelGfx::ElemBank; sTri *CModelGfx::TriList; sQuad *CModelGfx::QuadList; sVtx *CModelGfx::VtxList; u16 *CModelGfx::VtxIdxList; /*****************************************************************************/ void CModelGfx::SetData(sLevelHdr *LevelHdr) { ModelTable=LevelHdr->ModelList; ElemBank=LevelHdr->ElemBank3d; TriList=LevelHdr->TriList; QuadList=LevelHdr->QuadList; VtxList=LevelHdr->VtxList; VtxIdxList=LevelHdr->VtxIdxList; } /*****************************************************************************/ void CModelGfx::SetModel(int Type) { Model=&CModelGfx::ModelTable[Type]; Elem=&ElemBank[Model->ElemID]; } /*****************************************************************************/ static const int ElemXMin=-(16/2); static const int ElemXMax=+(16/2); static const int ElemYMin=-(16/2); static const int ElemYMax=+(16/2); static const int ElemZMin=-(16*4); static const int ElemZMax=+(16*4); static VECTOR VtxTable[8]= { {ElemXMin,ElemYMin,ElemZMin}, // FLU {ElemXMax,ElemYMin,ElemZMin}, // FRU {ElemXMin,ElemYMax,ElemZMin}, // FLD {ElemXMax,ElemYMax,ElemZMin}, // FRD {ElemXMin,ElemYMin,ElemZMax}, // BLU {ElemXMax,ElemYMin,ElemZMax}, // BRU {ElemXMin,ElemYMax,ElemZMax}, // BLD {ElemXMax,ElemYMax,ElemZMax}, // BRD }; /*****************************************************************************/ void CModelGfx::RenderTile(DVECTOR &Pos,int TileID) { sElem3d *ThisElem=&ElemBank[TileID]; u32 *XYList=(u32*)SCRATCH_RAM; u32 *OutVtx=XYList; VECTOR *V0,*V1,*V2,*InVtx=VtxTable; VECTOR RenderPos; MATRIX Mtx; SetIdentNoTrans(&Mtx); RenderPos.vx=(INGAME_SCREENOFS_X)+Pos.vx; RenderPos.vy=(INGAME_SCREENOFS_Y)+Pos.vy; gte_SetRotMatrix(&Mtx); CMX_SetTransMtxXY(&RenderPos); V0=InVtx++; V1=InVtx++; V2=InVtx++; gte_ldv3(V0,V1,V2); for (int i=0; i<(int)((sizeof(VtxTable)/sizeof(VECTOR))+1); i++) { u32 *OutPtr; gte_rtpt(); // 22 cycles V0=InVtx++; V1=InVtx++; V2=InVtx++; gte_ldv3(V0,V1,V2); OutPtr=OutVtx; OutVtx+=3; gte_stsxy3c(OutPtr); // read XY back } XYList+=8; RenderElem(ThisElem,Pos,0,0,0,XYList); } /*****************************************************************************/ void CModelGfx::RenderElem(sElem3d *ThisElem,DVECTOR &Pos,SVECTOR *Angle,VECTOR *Scale,s32 ClipFlag,u32 *TransBuffer) { u8 *PrimPtr=GetPrimPtr(); u32 T0,T1,T2,T3; u32 P0,P1,P2,P3; s32 ClipZ; sOT *ThisOT; VECTOR RenderPos; MATRIX Mtx; u32 const *XYList=(u32*)SCRATCH_RAM; // If has scale && angle then need to use PSX scale matrix, otherwise, force values if (Angle) { RotMatrix_gte(Angle,&Mtx); if (Scale) { ScaleMatrix(&Mtx,Scale); } } else { if (Scale) { SetIdentNoTrans(&Mtx,Scale); } else { SetIdentNoTrans(&Mtx); } } RenderPos.vx=(INGAME_SCREENOFS_X)+Pos.vx; RenderPos.vy=(INGAME_SCREENOFS_Y)+Pos.vy; gte_SetRotMatrix(&Mtx); CMX_SetTransMtxXY(&RenderPos); // --- Cache Vtx ---------- { int Count=ThisElem->VtxTriCount; sVtx *V0,*V1,*V2; u16 *IdxTable=&VtxIdxList[ThisElem->VtxIdxStart]; V0=&VtxList[*IdxTable++]; V1=&VtxList[*IdxTable++]; V2=&VtxList[*IdxTable++]; gte_ldv3(V0,V1,V2); while (Count--) { u32 *OutPtr; gte_rtpt_b(); // 22 cycles // Preload next (when able) - Must check this V0=&VtxList[*IdxTable++]; V1=&VtxList[*IdxTable++]; V2=&VtxList[*IdxTable++]; OutPtr=TransBuffer; TransBuffer+=3; gte_ldv3(V0,V1,V2); gte_stsxy3c(OutPtr); // read XY back } } // --- Render Tri's ------------- int TriCount=ThisElem->TriCount; sTri *TList=&TriList[ThisElem->TriStart]; while (TriCount--) { POLY_FT3 *ThisPrim=(POLY_FT3*)PrimPtr; P0=XYList[TList->P0]; P1=XYList[TList->P1]; P2=XYList[TList->P2]; gte_ldsxy0(P0); gte_ldsxy1(P1); gte_ldsxy2(P2); setlen(ThisPrim, GPU_PolyFT3Tag); ThisPrim->code=TList->PolyCode; gte_nclip_b(); // 8 cycles setShadeTex(ThisPrim,1); T0=*(u32*)&TList->uv0; // Get UV0 & TPage T1=*(u32*)&TList->uv1; // Get UV1 & Clut T2=*(u32*)&TList->uv2; // Get UV2 *(u32*)&ThisPrim->u0=T0; // Set UV0 *(u32*)&ThisPrim->u1=T1; // Set UV1 *(u32*)&ThisPrim->u2=T2; // Set UV2 gte_stopz(&ClipZ); ThisOT=OtPtr+TList->OTOfs; ClipZ|=ClipFlag; // <-- Evil!! TList++; if (ClipZ<0) { *(u32*)&ThisPrim->x0=P0; // Set XY0 *(u32*)&ThisPrim->x1=P1; // Set XY1 *(u32*)&ThisPrim->x2=P2; // Set XY2 addPrim(ThisOT,ThisPrim); PrimPtr+=sizeof(POLY_FT3); } } // --- Render Quads ----------- int QuadCount=ThisElem->QuadCount; sQuad *QList=&QuadList[ThisElem->QuadStart]; while (QuadCount--) { POLY_FT4 *ThisPrim=(POLY_FT4*)PrimPtr; P0=XYList[QList->P0]; P1=XYList[QList->P1]; P2=XYList[QList->P2]; P3=XYList[QList->P3]; gte_ldsxy0(P0); gte_ldsxy1(P1); gte_ldsxy2(P2); setlen(ThisPrim, GPU_PolyFT4Tag); ThisPrim->code=QList->PolyCode; gte_nclip_b(); // 8 cycles setShadeTex(ThisPrim,1); T0=*(u32*)&QList->uv0; // Get UV0 & TPage T1=*(u32*)&QList->uv1; // Get UV1 & Clut T2=*(u32*)&QList->uv2; // Get UV2 T3=*(u32*)&QList->uv3; // Get UV2 *(u32*)&ThisPrim->u0=T0; // Set UV0 *(u32*)&ThisPrim->u1=T1; // Set UV1 *(u32*)&ThisPrim->u2=T2; // Set UV2 *(u32*)&ThisPrim->u3=T3; // Set UV2 gte_stopz(&ClipZ); ThisOT=OtPtr+QList->OTOfs; ClipZ|=ClipFlag; // <-- Evil!! QList++; if (ClipZ<0) { *(u32*)&ThisPrim->x0=P0; // Set XY0 *(u32*)&ThisPrim->x1=P1; // Set XY1 *(u32*)&ThisPrim->x2=P2; // Set XY2 *(u32*)&ThisPrim->x3=P3; // Set XY3 addPrim(ThisOT,ThisPrim); PrimPtr+=sizeof(POLY_FT4); } } SetPrimPtr(PrimPtr); }