Files
TempleOS-Rijn/TempleOSCD/Demo/Graphics/LightTable.HC
T

429 lines
9.4 KiB
HolyC

/*
There is a coarse and a fine-grained.
The coarse gets flood-filled but the
fine grained is only outlines.
*/
class Photon
{
Photon *next,*last;
CD3 p,v,p_snell_inhibit;
} p_root[mp_cnt];
I64 p_root_locks;
#define ANIMATE_JIFFIES (JIFFY_FREQ*0.01)
I64 master_sleep_jiffy;
CTask *animate_tasks[mp_cnt];
CDC *map;
I64 photon_cnt,snell_cnt;
Bool full_speed;
U8 *bmp;
F64 bmp_scale,bmp_scale_sqr;
I64 bmp_mem,bmp_width,bmp_height,bmp_norm_radius;
I64 BmpPeek(I64 x,I64 y)
{
if (0<=x<bmp_width && 0<=y<bmp_height) {
if (Bt(bmp,y*bmp_width+x))
return WHITE;
else
return BLACK;
} else
return -1;
}
U0 BmpPlot(U8 *bmp,I64 x,I64 y,I64)
{
if (0<=x<bmp_width && 0<=y<bmp_height)
Bts(bmp,y*bmp_width+x);
}
Photon *PhotonNew()
{
I64 num=photon_cnt++%mp_cnt;
Photon *res=CAlloc(sizeof(Photon));
while (LBts(&p_root_locks,num))
Yield;
QueIns(res,p_root[num].last);
LBtr(&p_root_locks,num);
return res;
}
#define TAIL 20
U0 DrawIt(CTask *,CDC *dc)
{
I64 i;
Photon *tmpp;
GrBlot(dc,0,0,map);
dc->color=WHITE;
GrPrint(dc,0,0,"Mem:0x%X %,dMeg Scale:%0.3f (%d,%d)-->(%d,%d)",
bmp_mem,bmp_mem/1024/1024,bmp_scale,
map->width,map->height,bmp_width,bmp_height);
GrPrint(dc,0,FONT_HEIGHT,"PhotonCnt:%d SnellCnt:%d",photon_cnt,snell_cnt);
for (i=0;i<mp_cnt;i++) {
while (LBts(&p_root_locks,i))
Yield;
tmpp=p_root[i].next;
while (tmpp!=&p_root[i]) {
dc->color=LTRED;
GrLine(dc,tmpp->p.x-TAIL*tmpp->v.x,tmpp->p.y-TAIL*tmpp->v.y,
tmpp->p.x,tmpp->p.y);
tmpp=tmpp->next;
}
LBtr(&p_root_locks,i);
}
}
U0 RayBurst(I64 x1,I64 y1,I64 x2,I64 y2)
{
CD3 p,v,n,n2;
I64 i;
Photon *tmpp;
D3Equ(&p,x2,y2);
D3Equ(&v,x2-x1,y2-y1);
D3Unit(&v);
D3Equ(&n,v.y,-v.x);
tmpp=PhotonNew;
D3Copy(&tmpp->p,&p);
D3Copy(&tmpp->v,&v);
for (i=2;i<=9;i+=3) {
D3Mul(&n2,i,&n);
tmpp=PhotonNew;
D3Add(&tmpp->p,&p,&n2);
D3Copy(&tmpp->v,&v);
tmpp=PhotonNew;
D3Sub(&tmpp->p,&p,&n2);
D3Copy(&tmpp->v,&v);
}
}
U0 RandomBurst()
{
I64 i;
F64 é;
Photon *tmpp;
for (i=0;i<256;i++) {
tmpp=PhotonNew;
D3Equ(&tmpp->p,Fs->pix_width*Rand,Fs->pix_height*Rand);
é=2*ã*Rand;
D3Equ(&tmpp->v,Cos(é),Sin(é));
}
}
U0 FindNormal(Photon *tmpp,CD3 *_normal)
{
CD3 p,p1,p2,v,s;
F64 é=Arg(tmpp->v.x,tmpp->v.y),è;
D3Copy(&tmpp->p_snell_inhibit,&tmpp->p);
//Coarse grains has black and white filled-in BSplines.
//Fine grained has only white outline without being filled-in.
//Back-up a step and move fwd to get a fined-grained value
//for the point of contact.
D3SubEqu(&tmpp->p,&tmpp->v);
D3Mul(&p,bmp_scale,&tmpp->p);
D3Copy(&p1,&p);
while (BmpPeek(p1.x,p1.y)==BLACK && D3DistSqr(&p,&p1)<bmp_scale_sqr*2)
D3AddEqu(&p1,&tmpp->v);
D3Copy(&p,&p1);
D3Div(&tmpp->p,&p,bmp_scale);
//Draw an arc one direction, finding point of contact.
for (è=0;è<ã/2;è+=ã/bmp_norm_radius) {
D3Equ(&s,Cos(é+ã-ã/4-è),Sin(é+ã-ã/4-è));
D3Mul(&v,bmp_norm_radius,&s);
D3Add(&p1,&p,&v);
if (BmpPeek(p1.x,p1.y)!=BLACK)
break;
}
if (BmpPeek(p1.x,p1.y)!=WHITE)
D3Copy(&p1,&tmpp->p);
//Draw an arc other direction, finding point of contact.
for (è=0;è<ã/2;è+=ã/bmp_norm_radius) {
D3Equ(&s,Cos(é+ã+ã/4+è),Sin(é+ã+ã/4+è));
D3Mul(&v,bmp_norm_radius,&s);
D3Add(&p2,&p,&v);
if (BmpPeek(p2.x,p2.y)!=BLACK)
break;
}
if (BmpPeek(p2.x,p2.y)!=WHITE)
D3Copy(&p2,&tmpp->p);
D3Sub(&s,&p1,&p2);
D3Equ(_normal,s.y,-s.x);
if (D3Dot(_normal,&tmpp->v)<0)
D3Equ(_normal,-s.y,s.x);
D3Unit(_normal);
}
U0 SnellsLaw(Photon *tmpp,I64 last,I64 next)
{
//n1 and n2 are refraction index.
//n1 Sin(é1) == n2 Sin(é2)
F64 é=Arg(tmpp->v.x,tmpp->v.y),én,n1,n2,é1,é2,éa,éb;
CD3 normal;
if (last==WHITE)
n1=1.5;
else
n1=1.0;
if (next==WHITE)
n2=1.5;
else
n2=1.0;
FindNormal(tmpp,&normal);
én=Arg(normal.x,normal.y);
//dot=m1m2Cos(é);
é1=ACos(D3Dot(&normal,&tmpp->v));
é2=ASin(n1*Sin(é1)/n2);
//é --> (én+é1)
//é <-- (én+é2)
éa=én+é2;
éb=én-é2;
if (Abs(Wrap(éa-é))<Abs(Wrap(éb-é)))
é=éa;
else
é=éb;
D3Equ(&tmpp->v,Cos(é),Sin(é));
lock {snell_cnt++;}
}
U0 AnimateTask(I64)
{
while (TRUE) {
master_sleep_jiffy+=ANIMATE_JIFFIES;
if (cnts.jiffies>=master_sleep_jiffy)
master_sleep_jiffy=cnts.jiffies+ANIMATE_JIFFIES;
SleepUntil(master_sleep_jiffy);
}
}
U0 MPAnimateTask(I64)
{
I64 last_master_jiffy=0,
timeout_jiffy=master_sleep_jiffy+ANIMATE_JIFFIES,
last,next;
Photon *tmpp,*root=&p_root[Gs->num];
while (TRUE) {
while (LBts(&p_root_locks,Gs->num))
Yield;
tmpp=root->next;
while (tmpp!=root) {
last=GrPeek(map,tmpp->p.x,tmpp->p.y);
D3AddEqu(&tmpp->p,&tmpp->v);
if (tmpp->p.x<0) {
tmpp->p.x=-tmpp->p.x;
tmpp->v.x=-tmpp->v.x;
}
if (tmpp->p.x>=map->width) {
tmpp->p.x-=tmpp->p.x-map->width;
tmpp->v.x=-tmpp->v.x;
}
if (tmpp->p.y<0) {
tmpp->p.y=-tmpp->p.y;
tmpp->v.y=-tmpp->v.y;
}
if (tmpp->p.y>=map->height) {
tmpp->p.y-=tmpp->p.y-map->height;
tmpp->v.y=-tmpp->v.y;
}
next=GrPeek(map,tmpp->p.x,tmpp->p.y);
if (last!=next && (last==BLACK && next==WHITE) ||
(last==WHITE && next==BLACK) &&
//Don't do a snell operation too close to the last. Just ignore it.
D3DistSqr(&tmpp->p_snell_inhibit,&tmpp->p)>=4.0)
SnellsLaw(tmpp,last,next);
tmpp=tmpp->next;
if (cnts.jiffies>=timeout_jiffy)
break;
}
LBtr(&p_root_locks,Gs->num);
if (cnts.jiffies>=timeout_jiffy) {
Sleep(1);
timeout_jiffy=master_sleep_jiffy+ANIMATE_JIFFIES;
}
if (!full_speed) {
while (master_sleep_jiffy==last_master_jiffy)
Sleep(1);
last_master_jiffy=master_sleep_jiffy;
SleepUntil(master_sleep_jiffy);
timeout_jiffy=master_sleep_jiffy+ANIMATE_JIFFIES;
}
}
}
U0 Init()
{
I64 i;
master_sleep_jiffy=cnts.jiffies;
full_speed=FALSE;
photon_cnt=snell_cnt=0;
map=DCNew(Fs->pix_width,Fs->pix_height);
for (i=0;i<mp_cnt;i++) {
while (LBts(&p_root_locks,i))
Yield;
QueInit(&p_root[i]);
LBtr(&p_root_locks,i);
}
//x*y=bmp_mem*8
//x/y=640/480
//x=640/480*y
//640/480*y^2=bmp_mem*8
//y=Sqrt(bmp_mem*8*480/640)
//bmp_scale=Sqrt(bmp_mem*8*480/640)/480
bmp_scale=Sqrt(bmp_mem*8*Fs->pix_height/Fs->pix_width)/Fs->pix_height;
bmp_scale_sqr=Sqr(bmp_scale);
bmp_width =bmp_scale*Fs->pix_width;
bmp_height=bmp_scale*Fs->pix_height;
bmp=CAlloc(bmp_width*bmp_height/8);
bmp_norm_radius=Min(10*bmp_scale,250);
}
U0 CleanUp()
{
I64 i;
for (i=0;i<mp_cnt;i++) {
while (LBts(&p_root_locks,i))
Yield;
QueDel(&p_root[i],TRUE);
LBtr(&p_root_locks,i);
}
DCDel(map);
Free(bmp);
}
#define PTS_NUM 1024
U0 LightTable()
{
I64 msg_code,i,cnt,arg1,arg2,ray_x1,ray_y1,ray_x2,ray_y2;
CD3I32 *c=MAlloc(PTS_NUM*sizeof(CD3I32));
p_root_locks=0;
MenuPush(
"File {"
" Restart(,'\n');"
" Abort(,CH_SHIFT_ESC);"
" Exit(,CH_ESC);"
"}"
"Edit {"
" Fill(,CH_SPACE);"
"}"
"Play {"
" RandomBurst(,'r');"
" ElapseTime(,'e');"
"}"
);
MemBIOSRep;
bmp_mem=GetI64("\n\n\nHow much memory for the high resolution\n"
"shadow bitmap that helps improve the\n"
"accuracy of the normal vector estimate?\n"
"You can choose up to the largest\n"
"contiguous chunk of physical memory.\n\n"
"Mem (0x%0X):",1024*1024*16);
PopUpOk(
"Draw splines with $$GREEN$${Left-click}$$FG$$.\n\n"
"Fill lens with $$GREEN$$<SPACE>$$FG$$.\n\n"
"$$GREEN$${Right-drag}$$FG$$ to fire rays.\n"
);
SettingsPush; //See $LK,"SettingsPush",A="MN:SettingsPush"$
Fs->win_inhibit=WIG_TASK_DFT-WIF_SELF_FOCUS-
WIF_SELF_BORDER-WIF_FOCUS_TASK_MENU;
Fs->text_attr=BLACK<<4+WHITE; //Current $LK,"CTask",A="MN:CTask"$ is Fs segment register.
AutoComplete;
WinBorder;
WinMax;
DocCursor;
DocClear;
Init;
Fs->draw_it=&DrawIt;
Fs->animate_task=Spawn(&AnimateTask,NULL,"Animate",,Fs);
for (i=0;i<mp_cnt;i++)
animate_tasks[i]=Spawn(&MPAnimateTask,NULL,"MPAnimate",i);
try {
while (TRUE) {
msg_code=GetMsg(&arg1,&arg2,
1<<MSG_KEY_DOWN+1<<MSG_MS_L_DOWN+1<<MSG_MS_R_DOWN+1<<MSG_MS_R_UP);
lt_restart:
switch (msg_code) {
case MSG_MS_R_DOWN:
ray_x1=arg1; ray_y1=arg2;
break;
case MSG_MS_R_UP:
ray_x2=arg1; ray_y2=arg2;
RayBurst(ray_x1,ray_y1,ray_x2,ray_y2);
break;
case MSG_MS_L_DOWN:
cnt=0;
map->color=ROP_XOR+WHITE;
do {
c[cnt].x=arg1; c[cnt].y=arg2; c[cnt].z=0;
Gr2BSpline(map,c,cnt+1);
msg_code=GetMsg(&arg1,&arg2,
1<<MSG_KEY_DOWN+1<<MSG_MS_L_UP+1<<MSG_MS_MOVE+1<<MSG_MS_R_UP);
Gr2BSpline(map,c,cnt+1);
if (msg_code==MSG_KEY_DOWN)
goto lt_restart;
else if (msg_code==MSG_MS_L_UP) {
Sweep(100,70,100);
cnt++;
}
} while (cnt<PTS_NUM-1 && msg_code!=MSG_MS_R_UP);
map->color=WHITE;
Gr2BSpline3(map,c,cnt);
for (i=0;i<cnt;i++) {
c[i].x*=bmp_scale;
c[i].y*=bmp_scale;
}
BSpline2(bmp,c,cnt,&BmpPlot);
break;
case MSG_KEY_DOWN:
switch (arg1) {
case CH_SPACE:
GrFloodFill(map,ms.pos.x-Fs->pix_left,ms.pos.y-Fs->pix_top);
break;
case '\n':
CleanUp;
Init;
break;
case 'r':
RandomBurst;
break;
case 'e':
full_speed=TRUE;
Sleep(1500);
FlushMsgs;
full_speed=FALSE;
break;
case CH_ESC:
case CH_SHIFT_ESC:
goto lt_done;
}
break;
}
}
lt_done:
GetMsg(,,1<<MSG_KEY_UP);
} catch
PutExcept;
Free(c);
SettingsPop;
for (i=0;i<mp_cnt;i++)
Kill(animate_tasks[i]);
CleanUp;
MenuPop;
}
LightTable;