From dd02e77c2651b3ee048bcf43b928ad61ed4b220b Mon Sep 17 00:00:00 2001 From: Taras Bereznyak Date: Mon, 14 Nov 2022 22:39:10 +0300 Subject: [PATCH] First commit --- ConvexDraw.dpr | 545 ++++++++++++++++++ ConvexTree.pas | 28 + Geometry.pas | 1488 ++++++++++++++++++++++++++++++++++++++++++++++++ Points.pas | 187 ++++++ Render.pas | 1081 +++++++++++++++++++++++++++++++++++ WinAPI.pas | 828 +++++++++++++++++++++++++++ fxMath.pas | 51 ++ 7 files changed, 4208 insertions(+) create mode 100644 ConvexDraw.dpr create mode 100644 ConvexTree.pas create mode 100644 Geometry.pas create mode 100644 Points.pas create mode 100644 Render.pas create mode 100644 WinAPI.pas create mode 100644 fxMath.pas diff --git a/ConvexDraw.dpr b/ConvexDraw.dpr new file mode 100644 index 0000000..d76cf24 --- /dev/null +++ b/ConvexDraw.dpr @@ -0,0 +1,545 @@ +program ConvexDraw; +{$APPTYPE CONSOLE} + +uses + Windows, + Messages, + WinAPI, + Points, + Geometry in 'Geometry.pas', + fxMath in 'fxMath.pas', + Render in 'Render.pas', + ConvexTree in 'ConvexTree.pas', + SectorGrid in 'SectorGrid.pas'; + +var + fMain: TWindow; + context1: RenderContext; + + rect: TRect; + p1: array of Point; + + viewAX, viewAZ: float; + viewC: Point; + + tree: array of HighNode; + v: array of Convex; + + nc: integer = 0; + na, nb: Point; + fps, fpsc: integer; + currentTime, prevTime: cardinal; + + keypressed: array [0..65535] of boolean; + +const + sz=8; + +function Perp(const v: Point): Point; +var + l: float; +begin + if (abs(v.x)0 then begin + while nc<$400 do begin + //if nc=0 then begin + a1 := random; + a2 := random*2*pi; + a3 := sqrt(1.0-sqr(a1)); + na.x := a1; + na.y := cos(a2)*a3; + na.z := sin(a2)*a3; + nb := perp(na); + na := cross(nb,na); {} { + na := toPoint(0.0, 0.0, 0.0); + nb := toPoint(0.0, 0.0, 0.0); + na.c[coord] := 1.0; + nb.c[(coord+1) mod 3] := 1.0; {} + //end; + //WriteLn('lap=', length(ap)); + + + normal := add(scale(na, cos(nc*2.0*pi/$400)), scale(nb, sin(nc*2.0*pi/$400))); + //normal := ToPoint(0.0,-0.6,0.8); + nshift := 1.3+0.2*normal.y; + //nshift := 0.2; + CutConvex(v, normal, nshift, true); + Writeln('cut'); + break; + end; {} + end; +end; + +procedure StartNew(context: pointer); +var + PP: array of PPoint; + i: integer; + vi: integer; +const detalisation=9; + + function CreateCave(x,y,z: float; size: float; cave: boolean; box: boolean): integer; + var + vc: integer; + i: integer; + dst,cx,cy,cz,r: float; + hasFloor: boolean; + + function max3(a,b,c: float): float; + begin + if (a>b) and (a>c) then result := a + else if b>c then result := b + else result := c; + end; + + function max2(a,b: float): float; + begin + if a>b then result := a + else result := b; + end; + + begin + vc := Length(v); + SetLength(v, vc+1); + SetLength(PP, 0); + cx := random; + cy := random*0.4; + cz := random; + r := max2( + abs((x*2.0+size)/sz-1.0), + abs((z*2.0+size)/sz-1.0) + ); + + hasFloor := (y=0) or (random(5)=0); + + if box then for i := Low(P1) to High(P1) do begin + dst := sqr(P1[i].x-0.5) + sqr(P1[i].y-0.5) + sqr(P1[i].z-0.5); + if (dst0.2)) {and (random(15)=0)} then begin + SetLength(PP, Length(PP)+1); + PP[Length(PP)-1] := @P1[i]; + end; + end else for i := Low(P1) to High(P1) do begin + dst := sqr(P1[i].y-0.5) + sqr(max(abs(P1[i].x-0.5), abs(P1[i].z-0.5))); + if (dst3); + v[vc] := GetConvex(PP); + for i := Low(v[vc].faces) to High(v[vc].faces) do + if box then v[vc].faces[i].tn := 2 + else if v[vc].faces[i].normal.y=-1.0 then v[vc].faces[i].tn := 1 + else v[vc].faces[i].tn := 0; + + if cave then ReverseConvex(v[vc]); + ScaleConvex(v[vc], size); + TranslateConvex(v[vc], toPoint(x,y,z)); + result := vc; + end; + + function CreateBoxes(x1,y1,z1,x2,y2,z2: integer; const bound: integer): integer; + var + cx,cy,cz: integer; + i: integer; + begin + result := Length(tree); + SetLength(tree, result+1); + + tree[result].pmin := toPoint(x1*0.125, y1*0.125, z1*0.125); + tree[result].pmax := toPoint(x2*0.125, y2*0.125, z2*0.125); + + if (x2-x1=1) or (random(3)=0) then begin + tree[result].leaf := TRUE; + if {(x1 and 7 in [2..5]) and (z1 and 7 in [2..5]) and} (random(5)=0) then begin + tree[result].walls := CreateCave(x1*0.125,y1*0.125,z1*0.125, (x2-x1)*0.125, FALSE, TRUE); + for i := low(v[bound].faces) to High(v[bound].faces) do begin + CutConvex(v[tree[result].walls], Scale(v[bound].faces[i].normal, 1.0), v[bound].faces[i].nshift, TRUE); + end; + end else + tree[result].walls := -1; + tree[result].subWalls := -1; + end else begin + tree[result].leaf := FALSE; + cx := (x1+x2) div 2; + cy := (y1+y2) div 2; + cz := (z1+z2) div 2; + tree[result].center := toPoint(cx*0.125, cy*0.125, cz*0.125); + tree[result].childs[0] := CreateBoxes(x1,y1,z1,cx,cy,cz, bound); + tree[result].childs[1] := CreateBoxes(cx,y1,z1,x2,cy,cz, bound); + tree[result].childs[2] := CreateBoxes(x1,cy,z1,cx,y2,cz, bound); + tree[result].childs[3] := CreateBoxes(cx,cy,z1,x2,y2,cz, bound); + tree[result].childs[4] := CreateBoxes(x1,y1,cz,cx,cy,z2, bound); + tree[result].childs[5] := CreateBoxes(cx,y1,cz,x2,cy,z2, bound); + tree[result].childs[6] := CreateBoxes(x1,cy,cz,cx,y2,z2, bound); + tree[result].childs[7] := CreateBoxes(cx,cy,cz,x2,y2,z2, bound); + end; + end; + + function CreateTree(x1,y1,z1,x2,y2,z2: integer; sub: boolean): integer; + var + cx,cy,cz: integer; + begin + result := Length(tree); + SetLength(tree, result+1); + + tree[result].pmin := toPoint(x1*1.0, y1*1.0, z1*1.0); + tree[result].pmax := toPoint(x2*1.0, y2*1.0, z2*1.0); + + if (x2-x1=sz) and not sub then begin + tree[result].leaf := TRUE; + tree[result].subtracting := FALSE; + tree[result].walls := CreateCave(x1,y1,z1, x2-x1, FALSE, FALSE); + tree[result].subWalls := CreateTree(x1,y1,z1,x2,y2,z2, TRUE); + end else begin + if sub and ((x2-x1=1) or ((x2-x1<=8) and (random(6)=0))) then begin + tree[result].leaf := TRUE; + tree[result].subtracting := TRUE; + if (y1>=0) then begin + tree[result].walls := CreateCave(x1,y1,z1, x2-x1, TRUE, FALSE); + if x2-x1>1 then + tree[result].subWalls := CreateBoxes(x1*8,y1*8,z1*8, x2*8,y2*8,z2*8, tree[result].walls) + else + tree[result].subWalls := -1; + end else begin + tree[result].walls := -1; + tree[result].subWalls := -1; + end; + end else begin + tree[result].leaf := FALSE; + cx := (x1+x2) div 2; + cy := (y1+y2) div 2; + cz := (z1+z2) div 2; + tree[result].center := toPoint(cx*1.0, cy*1.0, cz*1.0); + tree[result].childs[0] := CreateTree(x1,y1,z1,cx,cy,cz,sub); + tree[result].childs[1] := CreateTree(cx,y1,z1,x2,cy,cz,sub); + tree[result].childs[2] := CreateTree(x1,cy,z1,cx,y2,cz,sub); + tree[result].childs[3] := CreateTree(cx,cy,z1,x2,y2,cz,sub); + tree[result].childs[4] := CreateTree(x1,y1,cz,cx,cy,z2,sub); + tree[result].childs[5] := CreateTree(cx,y1,cz,x2,cy,z2,sub); + tree[result].childs[6] := CreateTree(x1,cy,cz,cx,y2,z2,sub); + tree[result].childs[7] := CreateTree(cx,cy,cz,x2,y2,z2,sub); + end; + end; + end; + + procedure AddSubConvexesBetween(tree1, tree2: integer; dim: integer); + + function GetChildOrSelf(t: integer; chi: integer): integer; + begin + if tree[t].leaf then result := t else result := tree[t].childs[chi]; + end; + + begin + if tree[tree1].leaf and tree[tree2].leaf then begin + if (tree[tree1].walls>=0) and (tree[tree2].walls>=0) then begin + AddSubConvex(v[tree[tree1].walls], v[tree[tree2].walls]); + end; + end else case dim of + 0: begin + AddSubConvexesBetween(GetChildOrSelf(tree1,1), GetChildOrSelf(tree2,0), 0); + AddSubConvexesBetween(GetChildOrSelf(tree1,3), GetChildOrSelf(tree2,2), 0); + AddSubConvexesBetween(GetChildOrSelf(tree1,5), GetChildOrSelf(tree2,4), 0); + AddSubConvexesBetween(GetChildOrSelf(tree1,7), GetChildOrSelf(tree2,6), 0); + end; + 1: begin + AddSubConvexesBetween(GetChildOrSelf(tree1,2), GetChildOrSelf(tree2,0), 1); + AddSubConvexesBetween(GetChildOrSelf(tree1,3), GetChildOrSelf(tree2,1), 1); + AddSubConvexesBetween(GetChildOrSelf(tree1,6), GetChildOrSelf(tree2,4), 1); + AddSubConvexesBetween(GetChildOrSelf(tree1,7), GetChildOrSelf(tree2,5), 1); + end; + 2: begin + AddSubConvexesBetween(GetChildOrSelf(tree1,4), GetChildOrSelf(tree2,0), 2); + AddSubConvexesBetween(GetChildOrSelf(tree1,5), GetChildOrSelf(tree2,1), 2); + AddSubConvexesBetween(GetChildOrSelf(tree1,6), GetChildOrSelf(tree2,2), 2); + AddSubConvexesBetween(GetChildOrSelf(tree1,7), GetChildOrSelf(tree2,3), 2); + end; + end; + end; + + procedure AddSubConvexes(t: integer); + begin + if not tree[t].leaf then begin + AddSubConvexes(tree[t].childs[0]); + AddSubConvexes(tree[t].childs[1]); + AddSubConvexes(tree[t].childs[2]); + AddSubConvexes(tree[t].childs[3]); + AddSubConvexes(tree[t].childs[4]); + AddSubConvexes(tree[t].childs[5]); + AddSubConvexes(tree[t].childs[6]); + AddSubConvexes(tree[t].childs[7]); + AddSubConvexesBetween(tree[t].childs[0], tree[t].childs[1], 0); + AddSubConvexesBetween(tree[t].childs[2], tree[t].childs[3], 0); + AddSubConvexesBetween(tree[t].childs[4], tree[t].childs[5], 0); + AddSubConvexesBetween(tree[t].childs[6], tree[t].childs[7], 0); + AddSubConvexesBetween(tree[t].childs[0], tree[t].childs[2], 1); + AddSubConvexesBetween(tree[t].childs[1], tree[t].childs[3], 1); + AddSubConvexesBetween(tree[t].childs[4], tree[t].childs[6], 1); + AddSubConvexesBetween(tree[t].childs[5], tree[t].childs[7], 1); + AddSubConvexesBetween(tree[t].childs[0], tree[t].childs[4], 2); + AddSubConvexesBetween(tree[t].childs[1], tree[t].childs[5], 2); + AddSubConvexesBetween(tree[t].childs[2], tree[t].childs[6], 2); + AddSubConvexesBetween(tree[t].childs[3], tree[t].childs[7], 2); + end; + end; + +begin + SetLength(P1, detalisation*detalisation*detalisation+0); + for i := 0 to detalisation*detalisation*detalisation-1 do begin + P1[i] := toPoint( + (i div (detalisation*detalisation))/(detalisation-1), + (i div detalisation mod detalisation)/(detalisation-1), + (i mod detalisation)/(detalisation-1) + ); + end; + + + setlength(tree, 0); + CreateTree(0,0,0,sz,sz,sz,FALSE); + + // for i := low(v[0].faces) to High(v[0].faces) do + // CutConvex(v[762], Scale(v[0].faces[i].normal, -1.0), -v[0].faces[i].nshift, TRUE); + + + for vi := 1 to High(v) do begin + for i := low(v[0].faces) to High(v[0].faces) do begin + CutConvex(v[vi], Scale(v[0].faces[i].normal, -1.0), -v[0].faces[i].nshift, TRUE); + end; + end; {} + + for vi := 1 to High(v) do begin + AddSubConvex(v[0],v[vi]); + end; + AddSubConvexes(tree[0].subWalls); {} + + SetFocus(fMain.Handle); +end; + +procedure Close(context: pointer); +begin + SendMessage(fMain.Handle, WM_CLOSE, 0, 0) +end; + +var firstDrawenConvex: integer = -1; + +procedure DrawTree(var context: RenderContext; const node: HighNode; depth: integer=0); +var + cubeIsVisible: boolean; + ox,oy,oz: integer; + mask: integer; + i: integer; +begin + cubeIsVisible := True; + + if cubeIsVisible then begin + if node.leaf then begin + Inc(counter8); + + if node.walls>=0 then begin + cubeIsVisible := FastCheckCube(context, node.pmin, node.pmax); + if cubeIsVisible then begin + if node.subtracting then begin + if node.subWalls>=0 then + DrawTree(context, tree[node.subWalls], depth) + else if firstDrawenConvex<0 then + firstDrawenConvex := node.walls; + DrawConvex(context, v[node.walls]); + end else begin + DrawConvex(context, v[node.walls]); + if node.subWalls>=0 then + DrawTree(context, tree[node.subWalls], depth) + else if firstDrawenConvex<0 then + firstDrawenConvex := node.walls; + end; + end; + end; + end else begin + if -viewC.x0 then begin + counter8 := 0; + firstDrawenConvex := -1; + dec(counter1, GetTimer); + DrawTree(context1, tree[0]); + inc(counter1, GetTimer); + //Counts(context); + + //ProcessCut(v1,0); + //ProcessCut(v2,1); + //ProcessCut(v3,2); + Inc(nc,1); + end; {} + + Inc(fpsc); + + if currentTime div 1000 <> prevTime div 1000 then begin + fps := fpsc; + fpsc := 0; + WriteLn('fps=', fps, + ' in ', firstDrawenConvex, + ' polyCount=', context1.polyCounter, + ' counter1(FULL)=', counter1, + ' counter2=', counter2/(counter1+1):0:3, + ' counter3=', counter3/(counter1+1):0:3, + ' counter4=', counter4/(counter1+1):0:3, + ' counter5=', counter5/(counter1+1):0:3, + ' counter6=', counter6/(counter1+1):0:3, + ' counter7=', counter7/(counter1+1):0:3, + ' counter8=', counter8); + end; + + InvalidateRect(Handle, nil, FALSE); + prevTime := currentTime; + end; +end; + +function MainWndProc(Handle: HWND; Message: UINT; WP: WParam; LP: LParam): longint; stdcall; +var + dc: HDC; + CR: TRect; +begin + case Message of + + WM_COMMAND: begin + if (HiWord(WP) = 1) and (LP = 0) then CheckControl(Handle, LoWord(WP), HiWord(DWORD(WP))) + else Click(Handle, LoWord(WP), HiWord(DWORD(WP))); + end; + WM_ERASEBKGND: begin + Result := 0; + Exit; + end; + WM_PAINT: begin + dc := GetDC(Handle); + GetClientRect(Handle, CR); + //BitBlt(dc, 0, 0, context.buffer.SizeX, context.buffer.SizeY, context.buffer.DC, 0, 0, SRCCOPY); + SetStretchBltMode(dc, COLORONCOLOR); + //SetStretchBltMode(dc, HALFTONE); + StretchBlt(dc, 0, 0, + CR.Right-CR.Left, CR.Bottom-CR.Top, context1.buffer.DC, 0, 0, context1.buffer.SizeX, context1.buffer.SizeY, SRCCOPY); + ReleaseDC(Handle, dc); + end; + WM_KEYDOWN: begin + if LoWord(WP) = byte('N') then StartNew(nil); + keypressed[LoWord(WP)] := TRUE; + end; + WM_KEYUP: begin + keypressed[LoWord(WP)] := FALSE; + end; + WM_TIMER: begin + end; + WM_DESTROY: begin + PostQuitMessage(0); + Result := 0; + Exit; + end; + end; + Result := DefWindowProc(Handle, Message, WP, LP); +end; + +begin + randseed := 2; + ChgFont('Courier New', 11); + WindowProc := MainWndProc; + CreateForm(fMain, 'ConvexDraw', 0, sFormFixedSize or WS_POPUP); + + //SetTimer(fMain.Handle, 0, 0, nil); + WinAPI.IdleProc := IdleProc; + viewC := toPoint(+0.1, -sz/2+0.1, +0.1); + ShowForm(fMain); + ShowWindow(fMain.Handle, SW_SHOWMAXIMIZED); + GetClientRect(fMain.Handle, rect); + CreateRenderContext(context1, (rect.Right-rect.Left) * 1, (rect.Bottom-rect.Top) * 1); + GetMessages; + DeleteRenderContext(context1); + +end. diff --git a/ConvexTree.pas b/ConvexTree.pas new file mode 100644 index 0000000..b2fcef5 --- /dev/null +++ b/ConvexTree.pas @@ -0,0 +1,28 @@ +unit ConvexTree; + +interface + +uses + Geometry, Points; + +type + PHighNode = ^HighNode; + + HighNode = record + pmin, pmax: Point; + subtracting: boolean; + + case leaf: boolean of + FALSE: ( + walls: integer; + subWalls: integer; + ); + TRUE: ( + center: Point; + childs: array [0..7] of integer + ); + end; + +implementation + +end. diff --git a/Geometry.pas b/Geometry.pas new file mode 100644 index 0000000..83195b2 --- /dev/null +++ b/Geometry.pas @@ -0,0 +1,1488 @@ +unit Geometry; + +interface + +uses Points; + +type + Vertex = record + p: Point; + icounter: integer; + fcounter: float; + end; + + PVertex = ^Vertex; + + PVertexArray = array of PVertex; + + Line = record + P1, P2: integer; // vertexIndex + icounter: integer; + ucounter: cardinal; + end; + + PLine = ^Line; + + IntegerArray = array of integer; + + Face = record + id: integer; + lines: IntegerArray; // lineIndex + vertexes: IntegerArray; // vertexIndex + normal: Point; + nshift: float; + pinside: Point; + sign: integer; + + tn: integer; + txn: Point; + txc: float; + tyn: Point; + tyc: float; + end; + + PFace = ^Face; + + Convex = record + lines: array of Line; + faces: array of Face; + vertexes: array of Vertex; + pmin, pmax: Point; + end; + + PConvex = ^Convex; + +function GetConvex(var vertexes: array of PPoint): Convex; +procedure CutConvex(var v: Convex; const cutNormal: Point; cutNShift: float; closed: boolean); +procedure ReverseConvex(var v: Convex); +procedure AddSubConvex(var outv, inv: Convex); +procedure TranslateConvex(var v: Convex; const b: Point); +procedure ScaleConvex(var v: Convex; s: float); + +implementation + +function GetSign(s: float): integer; +begin + if s > 0.000001 then result := 1 + else if s > -0.000001 then result := 0 + else result := -1; +end; + +function GetSignWithEps(s: float; eps: float): integer; +begin + if s > eps then result := 1 + else if s > -eps then result := 0 + else result := -1; +end; + +procedure DeleteUnusedLines(var v: Convex); +var + i, j: integer; + lineni: array of integer; +begin + for i := Low(v.faces) to High(v.faces) do with v.faces[i] do begin + for j := Low(lines) to High(lines) do begin + v.lines[lines[j]].icounter := 1; + end; + end; + + SetLength(lineni, Length(v.lines)); + j := 0; + for i := Low(v.lines) to High(v.lines) do begin + lineni[i] := j; + if v.lines[i].icounter>0 then begin + if i>j then v.lines[j] := v.lines[i]; + v.lines[j].icounter := 0; + Inc(j); + end; + end; + SetLength(v.lines, j); + + for i := Low(v.faces) to High(v.faces) do with v.faces[i] do begin + for j := Low(lines) to High(lines) do begin + lines[j] := lineni[lines[j]]; + end; + end; +end; + +procedure DeleteUnusedVertexes(var v: Convex); +var + i, j: integer; + vertexesi: array of integer; +begin + for i := Low(v.vertexes) to High(v.vertexes) do v.vertexes[i].icounter := 0; + + for i := Low(v.lines) to High(v.lines) do with v.lines[i] do begin + v.vertexes[P1].icounter := 1; + v.vertexes[P2].icounter := 1; + end; + + SetLength(vertexesi, Length(v.vertexes)); + j := 0; + for i := Low(v.vertexes) to High(v.vertexes) do begin + vertexesi[i] := j; + if v.vertexes[i].icounter>0 then begin + if i>j then v.vertexes[j] := v.vertexes[i]; + v.vertexes[j].icounter := 0; + Inc(j); + end; + end; + SetLength(v.vertexes, j); + + for i := Low(v.lines) to High(v.lines) do with v.lines[i] do begin + P1 := vertexesi[P1]; + P2 := vertexesi[P2]; + end; + + for i := Low(v.faces) to High(v.faces) do with v.faces[i] do begin + for j := Low(vertexes) to High(vertexes) do begin + vertexes[j] := vertexesi[vertexes[j]]; + end; + end; +end; + +procedure DeleteUnusedFromConvex(var v: Convex); +begin + DeleteUnusedLines(v); + DeleteUnusedVertexes(v); +end; + +function GetConvex(var vertexes: array of PPoint): Convex; +var + center: Point; + + type T = PPoint; + function LESS(p1,p2: PPoint): boolean; + begin + result := sqrLen(Sub(p1^, center)) > sqrLen(Sub(p2^,center)); + end; + {$I qsort.inc} + + procedure MoveFirst4Points; + procedure FindPoint1; + var + i: integer; + s: float; + tmp: PPoint; + begin + for i := Low(vertexes)+1 to High(vertexes) do begin + s := sqrlen(sub(vertexes[i]^, vertexes[0]^)); + if s > 0.0001 then begin + if i>1 then begin + tmp := vertexes[1]; + vertexes[1] := vertexes[i]; + vertexes[i] := tmp; + end; + break; + end; + end; + end; + + procedure FindPoint2; + var + i: integer; + s: float; + tmp: PPoint; + begin + for i := Low(vertexes)+2 to High(vertexes) do begin + s := sqrlen(cross(sub(vertexes[0]^,vertexes[i]^), sub(vertexes[1]^,vertexes[i]^))); + + if s > 0.0000000001 then begin + if i>2 then begin + tmp := vertexes[2]; + vertexes[2] := vertexes[i]; + vertexes[i] := tmp; + end; + break; + end; + end; + end; + + procedure FindPoint3; + var + i: integer; + s: float; + tmp: PPoint; + begin + for i := Low(vertexes)+3 to High(vertexes) do begin + s := volume(sub(vertexes[0]^,vertexes[i]^), sub(vertexes[1]^,vertexes[i]^), sub(vertexes[2]^,vertexes[i]^)); + + if abs(s) > 0.00000001 then begin + if i>3 then begin + tmp := vertexes[3]; + vertexes[3] := vertexes[i]; + vertexes[i] := tmp; + end; + if s<0 then begin + tmp := vertexes[3]; + vertexes[3] := vertexes[2]; + vertexes[2] := tmp; + end; + break; + end; + end; + end; + + begin + FindPoint1; + FindPoint2; + FindPoint3; + end; + + function CreateFirstTetraedr: Convex; + var + i: integer; + begin + SetLength(result.vertexes, 4); + result.vertexes[0].p := vertexes[0]^; + result.vertexes[1].p := vertexes[1]^; + result.vertexes[2].p := vertexes[2]^; + result.vertexes[3].p := vertexes[3]^; + + SetLength(result.lines, 6); + result.lines[0].P1 := 0; + result.lines[0].P2 := 1; + result.lines[1].P1 := 0; + result.lines[1].P2 := 2; + result.lines[2].P1 := 1; + result.lines[2].P2 := 2; + result.lines[3].P1 := 0; + result.lines[3].P2 := 3; + result.lines[4].P1 := 1; + result.lines[4].P2 := 3; + result.lines[5].P1 := 2; + result.lines[5].P2 := 3; + for i := Low(result.lines) to High(result.lines) do begin + result.lines[i].icounter := 0; + result.lines[i].ucounter := 0; + end; + SetLength(result.faces, 4); + + SetLength(result.faces[0].lines, 3); + result.faces[0].lines[0] := 2; + result.faces[0].lines[1] := 4; + result.faces[0].lines[2] := 5; + result.faces[0].normal := norm(cross(sub(vertexes[1]^,vertexes[2]^), sub(vertexes[3]^,vertexes[2]^))); + result.faces[0].nshift := -dot(vertexes[1]^, result.faces[0].normal); + result.faces[0].pinside := Scale(add(add(vertexes[1]^, vertexes[2]^), vertexes[3]^), 1.0/3.0); + + SetLength(result.faces[1].lines, 3); + result.faces[1].lines[0] := 1; + result.faces[1].lines[1] := 3; + result.faces[1].lines[2] := 5; + result.faces[1].normal := norm(cross(sub(vertexes[0]^,vertexes[3]^), sub(vertexes[2]^,vertexes[3]^))); + result.faces[1].nshift := -dot(vertexes[2]^, result.faces[1].normal); + result.faces[1].pinside := scale(add(add(vertexes[2]^, vertexes[3]^), vertexes[0]^), 1.0/3.0); + + SetLength(result.faces[2].lines, 3); + result.faces[2].lines[0] := 0; + result.faces[2].lines[1] := 3; + result.faces[2].lines[2] := 4; + result.faces[2].normal := norm(cross(sub(vertexes[3]^,vertexes[0]^), sub(vertexes[1]^,vertexes[0]^))); + result.faces[2].nshift := -dot(vertexes[3]^, result.faces[2].normal); + result.faces[2].pinside := scale(add(add(vertexes[3]^, vertexes[0]^), vertexes[1]^), 1.0/3.0); + + SetLength(result.faces[3].lines, 3); + result.faces[3].lines[0] := 0; + result.faces[3].lines[1] := 1; + result.faces[3].lines[2] := 2; + result.faces[3].normal := norm(cross(sub(vertexes[2]^,vertexes[1]^), sub(vertexes[0]^,vertexes[1]^))); + result.faces[3].nshift := -dot(vertexes[0]^, result.faces[3].normal); + result.faces[3].pinside := scale(add(add(vertexes[0]^, vertexes[1]^), vertexes[2]^), 1.0/3.0); + for i := Low(result.faces) to High(result.faces) do result.faces[i].id := i; + end; + + procedure AddPoint(var v: Convex; P: PPoint); + var + newP: integer; + i, j, k: integer; + f, df: integer; + pl: PLine; + pv1, pv2: PVertex; + + pfaces: array of integer; + zfaces: array of integer; + blines: array of integer; + + procedure ProcessBorderFace(facei: integer; ormask: integer); + var + j,k: integer; + pv1, pv2: PVertex; + begin + with v.faces[facei] do for j := Low(lines) to High(lines) do begin + pl := @v.lines[lines[j]]; + if pl.icounter=1 then begin + k := length(blines); + SetLength(blines, k+1); + blines[k] := lines[j]; + pv1 := @v.vertexes[pl.P1]; + pv2 := @v.vertexes[pl.P2]; + pv1.icounter := pv1.icounter xor id or ormask; + pv2.icounter := pv2.icounter xor id or ormask; + end; + end; + end; + + function CreateLine(P1, P2: integer): integer; + begin + result := length(v.lines); + SetLength(v.lines, result+1); + v.lines[result].P1 := P1; + v.lines[result].P2 := P2; + v.lines[result].icounter := 0; + v.lines[result].ucounter := 0; + // result cannot be zero we already have lines + end; + + begin + for i := Low(v.faces) to High(v.faces) do with v.faces[i] do begin + sign := GetSign(dot(normal, P^) + nshift); + + if sign=0 then begin + j := Length(zfaces); + SetLength(zfaces, j+1); + zfaces[j] := i; + end else if sign=1 then begin + j := Length(pfaces); + SetLength(pfaces, j+1); + pfaces[j] := i; + end; + + if sign>=0 then for j := Low(lines) to High(lines) do begin + pl := @v.lines[lines[j]]; + pl.icounter := 1 - pl.icounter; + end; + end; + + if Length(pfaces)=0 then begin + for i := Low(zfaces) to High(zfaces) do with v.faces[zfaces[i]] do begin + for j := Low(lines) to High(lines) do begin + pl := @v.lines[lines[j]]; + pl.icounter := 0; + end; + end; + end else begin + newP := Length(v.vertexes); + SetLength(v.vertexes, newP+1); + v.vertexes[newP].p := P^; + v.vertexes[newP].icounter := 0; + v.vertexes[newP].fcounter := 0; + + for i := Low(zfaces) to High(zfaces) do ProcessBorderFace(zfaces[i], 0); + for i := Low(pfaces) to High(pfaces) do ProcessBorderFace(pfaces[i], -$7FFFFFFF-1); // 0x80 00 00 00 + + for i := Low(blines) to High(blines) do begin + pl := @v.lines[blines[i]]; + pv1 := @v.vertexes[pl.P1]; + pv2 := @v.vertexes[pl.P2]; + if pv1.icounter<>0 then pv1.icounter := -1; + if pv2.icounter<>0 then pv2.icounter := -1; + end; + + for i := Low(blines) to High(blines) do begin + pl := @v.lines[blines[i]]; + pv1 := @v.vertexes[pl.P1]; + pv2 := @v.vertexes[pl.P2]; + + if pv1.icounter<0 then begin + k := createLine(newP, pl.P1); + pl := @v.lines[blines[i]]; + pv1.icounter := k; + end; + + if pv2.icounter<0 then begin + k := createLine(newP, pl.P2); + pl := @v.lines[blines[i]]; + pv2.icounter := k; + end; + end; + + for i := Low(zfaces) to High(zfaces) do with v.faces[zfaces[i]] do begin + k := Length(lines); + SetLength(lines, k+2); + for j := Low(lines) to High(lines)-2 do begin + pl := @v.lines[lines[j]]; + if pl.icounter=1 then begin + pv1 := @v.vertexes[pl.P1]; + pv2 := @v.vertexes[pl.P2]; + if pv1.icounter<>0 then begin + lines[k] := pv1.icounter; + Inc(k); + end; + if pv2.icounter<>0 then begin + lines[k] := pv2.icounter; + Inc(k); + end; + end; + end; + + k := 0; + for j := Low(lines) to High(Lines) do begin + if (v.lines[lines[j]].icounter=1) or (j>High(lines)-2) then begin + lines[k] := lines[j]; + Inc(k); + end; + end; + SetLength(lines, k); + end; + + df := 0; + for i := Low(pfaces) to High(pfaces) do with v.faces[pfaces[i]] do begin + for j := Low(lines) to High(lines) do begin + pl := @v.lines[lines[j]]; + if pl.icounter=1 then Inc(df); + end; + end; + + f := Length(v.faces); + SetLength(v.faces, f+df); + + for i := Low(pfaces) to High(pfaces) do with v.faces[pfaces[i]] do begin + for j := Low(lines) to High(lines) do begin + pl := @v.lines[lines[j]]; + if pl.icounter=1 then begin + pv1 := @v.vertexes[pl.P1]; + pv2 := @v.vertexes[pl.P2]; + + SetLength(v.faces[f].lines, 3); + v.faces[f].id := f; + v.faces[f].lines[0] := pv1.icounter; + v.faces[f].lines[1] := pv2.icounter; + v.faces[f].lines[2] := lines[j]; + v.faces[f].normal := norm(cross(sub(pv1.p,P^), sub(pv2.p,P^))); + v.faces[f].nshift := -dot(P^, v.faces[f].normal); + if dot(pinside, v.faces[f].normal)+v.faces[f].nshift>0 then begin + v.faces[f].normal := scale(v.faces[f].normal, -1.0); + v.faces[f].nshift := -v.faces[f].nshift; + end; + v.faces[f].pinside := scale(add(add(pv1.p, pv2.p), P^), 1.0/3.0); + v.faces[f].sign := 0; + Inc(f); + end; + end; + end; + + for i := Low(blines) to High(blines) do begin + pl := @v.lines[blines[i]]; + pv1 := @v.vertexes[pl.P1]; + pv2 := @v.vertexes[pl.P2]; + pv1.icounter := 0; + pv2.icounter := 0; + pl.icounter := 0; + end; + + j := 0; + for i := Low(v.faces) to High(v.faces) do begin + if v.faces[i].sign<=0 then begin + if j0 then pv1.icounter := lines[j] else pv2.icounter := lines[j]; + end; + + SetLength(vertexes, Length(lines)); + SetLength(orderedLines, Length(lines)); + vertexes[0] := v.lines[lines[0]].P1; + orderedLines[0] := v.vertexes[vertexes[0]].icounter; + for j := Low(vertexes) to High(vertexes)-1 do begin + pl := @v.lines[v.vertexes[vertexes[j]].icounter]; + if vertexes[j] = pl.P1 then vertexes[j+1] := pl.P2 else vertexes[j+1] := pl.P1; + orderedLines[j+1] := v.vertexes[vertexes[j+1]].icounter; + end; + + for j := Low(vertexes) to High(vertexes) do v.vertexes[vertexes[j]].icounter := 0; + lines := orderedLines; + end; + end; + + procedure CountMinMax(var v: Convex); + var + i: integer; + begin + v.pmin := v.vertexes[0].p; + v.pmax := v.vertexes[0].p; + for i := 1 to Length(v.vertexes)-1 do begin + if v.vertexes[i].p.xv.pmax.x then v.pmax.x:=v.vertexes[i].p.x; + if v.vertexes[i].p.y>v.pmax.y then v.pmax.y:=v.vertexes[i].p.y; + if v.vertexes[i].p.z>v.pmax.z then v.pmax.z:=v.vertexes[i].p.z; + end; + end; + +var + i: integer; +begin // GetConvex + center.x := 0.0; + center.y := 0.0; + center.z := 0.0; + for i := Low(vertexes) to High(vertexes) do begin + center.x := center.x + vertexes[i].x; + center.y := center.y + vertexes[i].y; + center.z := center.z + vertexes[i].z; + end; + + center := scale(center, 1.0/Length(vertexes)); + + Sort(vertexes, Low(vertexes), High(vertexes)); + MoveFirst4Points; + result := CreateFirstTetraedr; + + for i := 4 to High(vertexes) do begin + AddPoint(result, vertexes[i]); + if i mod 10000=0 then + WriteLn('processed ', i, ' of ', Length(vertexes)); + end; + DeleteUnusedFromConvex(result); + AdjustFacePoints(result); + CountMinMax(result); +end; + +function Complete(const v: Convex; vIndex,lIndex: IntegerArray): boolean; +var + i,pi: integer; +begin + result := FALSE; + for i := Low(lIndex) to High(lIndex) do for pi := i+1 to High(lIndex) do + if vIndex[i]=vIndex[pi] then exit; + + pi := High(lIndex); + for i := Low(lIndex) to High(lIndex) do begin + if (v.lines[lIndex[pi]].P1=vIndex[pi]) and (v.lines[lIndex[pi]].P2=vIndex[i]) then + else if (v.lines[lIndex[pi]].P1=vIndex[i]) and (v.lines[lIndex[pi]].P2=vIndex[pi]) then + else exit; + pi := i; + end; + + result := TRUE; +end; + +procedure CutConvex(var v: Convex; const cutNormal: Point; cutNShift: float; closed: boolean); +var + i, j: integer; + f, l, fl, rl, al: integer; + plc: integer; + hg, hp, fb: boolean; + b1, b2: cardinal; + t: float; + pl: PLine; + pv1, pv2: PVertex; + pc: array [0..1] of integer; + alines: IntegerArray; + + newLines: IntegerArray; + newVertexes: IntegerArray; +begin + hp := false; + + for i := Low(v.vertexes) to High(v.vertexes) do begin + v.vertexes[i].fcounter := dot(v.vertexes[i].p, cutNormal) + cutNshift; + case GetSign(v.vertexes[i].fcounter) of + -1: v.vertexes[i].icounter := 0; + 0: v.vertexes[i].icounter := 1; + 1: v.vertexes[i].icounter := 3; + end; + end; + + for i := Low(v.lines) to High(v.lines) do begin + pl := @v.lines[i]; + pv1 := @v.vertexes[pl.P1]; + pv2 := @v.vertexes[pl.P2]; + + b1 := cardinal(pv1.icounter); + b2 := cardinal(pv2.icounter); + + if (b1 and b2) > 0 then begin + if b1=1 then begin + v.lines[i].ucounter := b1 or (b2 shl 2); + j := pl.P1; + pl.P1 := pl.P2; + pl.P2 := j; + end else + v.lines[i].ucounter := b2 or (b1 shl 2); + end else if (b1 xor b2) = 3 then begin + plc := length(v.vertexes); + SetLength(v.vertexes, plc+1); + pv1 := @v.vertexes[pl.P1]; + pv2 := @v.vertexes[pl.P2]; + + t := pv1.fcounter / (pv1.fcounter-pv2.fcounter); + v.vertexes[plc].p := add(scale(pv1.p, 1.0-t), scale(pv2.p, t)); + v.vertexes[plc].icounter := 0; + v.vertexes[plc].fcounter := 0.0; + + if b1=0 then begin + v.lines[i].P1 := v.lines[i].P2; + v.lines[i].ucounter := b1 or (b2 shl 2); + end else begin + v.lines[i].ucounter := b2 or (b1 shl 2); + end; + v.lines[i].P2 := plc; + end; + end; + + al := 0; + + for i := Low(v.faces) to High(v.faces) do with v.faces[i] do begin + hg := false; + fl := -1; + fb := closed; + for j := Low(lines) to High(lines) do begin + pl := @v.lines[lines[j]]; + + if pl.ucounter and $F<>$5 then fb := false; // fullBorder + if pl.ucounter and $A<>0 then hg := true; // hasGood + if pl.P1=vertexes[j] then begin + if pl.ucounter and $C=0 then fl := j; // first line for process + end else begin + if pl.ucounter and $3=0 then fl := j; + end; + end; + + if fb then begin + hg := true; + assert(fl<0); + end; + + if hg then begin + if fl<0 then begin // full in good side + for j := Low(lines) to High(lines) do begin + pl := @v.lines[lines[j]]; + + if closed and not fb then begin + if pl.ucounter=$5 then begin + SetLength(alines, al+1); + alines[al] := lines[j]; + Inc(al); + end; + end; + end; + end else begin + hp := true; + l := 0; + rl := Length(v.lines); + for j := Low(lines) to High(lines) do begin + pl := @v.lines[lines[fl]]; + plc := pl.ucounter; + + if pl.P1<>vertexes[fl] then plc := plc shr 2 + (plc and $03) shl 2; + case plc of + $0,$1,$4,$5:; + $3,$7: begin + if plc=3 then pc[0] := pl.P2 else pc[0] := vertexes[fl]; + SetLength(newLines, l+1); + SetLength(newVertexes, l+1); + newLines[l] := lines[fl]; + newVertexes[l] := pc[0]; + Inc(l); + end; + $C, $D: begin + if plc=$C then pc[1] := pl.P2 else if fl=High(lines) then pc[1] := vertexes[Low(lines)] else pc[1] := vertexes[fl+1]; + + SetLength(v.lines, rl+1); + v.lines[rl].P1 := pc[1]; + v.lines[rl].P2 := pc[0]; + v.lines[rl].icounter := 0; + + SetLength(newLines, l+2); + SetLength(newVertexes, l+2); + newLines[l] := lines[fl]; + newLines[l+1] := rl; + newVertexes[l] := vertexes[fl]; + newVertexes[l+1] := pc[1]; + + if closed then begin + SetLength(alines, al+1); + alines[al] := rl; + Inc(al); + end; + break; + end; + $F: begin + SetLength(newLines, l+1); + SetLength(newVertexes, l+1); + newLines[l] := lines[fl]; + newVertexes[l] := vertexes[fl]; + Inc(l); + end; + else begin + assert(False); + end; + end; + + if fl=High(lines) then fl := Low(lines) else Inc(fl); + end; + lines := newLines; + vertexes := newVertexes; + end; + + sign := 1; + end else begin + sign := -1; + end; + end; + + if closed and hp then begin + assert(Length(alines)>=3); + + f := Length(v.faces); + SetLength(v.faces, f+1); + with v.faces[f] do begin + id := f; + sign := 1; + pinside.x := 0.0; + pinside.y := 0.0; + pinside.z := 0.0; + for i := Low(alines) to High(alines) do begin + pl := @v.lines[alines[i]]; + pv1 := @v.vertexes[pl.P1]; + pv2 := @v.vertexes[pl.P2]; + pinside := add(pinside, add(pv1.p, pv2.p)); + end; + pinside := scale(pinside, 0.5/Length(alines)); + normal := scale(cutNormal, -1.0); + nshift := -cutNshift; + + for i := Low(alines) to High(alines) do begin + pl := @v.lines[alines[i]]; + pv1 := @v.vertexes[pl.P1]; + pv2 := @v.vertexes[pl.P2]; + if dot(cross(sub(pv1.p, pinside), sub(pv2.p, pinside)), normal)>0 then + pv1.icounter := alines[i] + else + pv2.icounter := alines[i]; + end; + + SetLength(vertexes, Length(alines)); + SetLength(lines, Length(alines)); + vertexes[0] := v.lines[alines[0]].P1; + lines[0] := v.vertexes[vertexes[0]].icounter; + for j := Low(vertexes) to High(vertexes)-1 do begin + pl := @v.lines[v.vertexes[vertexes[j]].icounter]; + if vertexes[j] = pl.P1 then vertexes[j+1] := pl.P2 else vertexes[j+1] := pl.P1; + lines[j+1] := v.vertexes[vertexes[j+1]].icounter; + end; + + for j := Low(vertexes) to High(vertexes) do v.vertexes[vertexes[j]].icounter := 0; + + assert(Complete(v, vertexes, lines)); + end; + end; + + j := 0; + for i := Low(v.faces) to High(v.faces) do begin + if v.faces[i].sign>0 then begin + if j0 then hasPositive := TRUE; + + if (s>0) and (ns<=0) then begin + lc[i].o1 := o; + lc[i].w1 := d/(d-nd); + if lc[i].w1<0.0 then lc[i].w1:=0.0 else if lc[i].w1>1.0 then lc[i].w1:=1.0; + lc[i].ow1 := lc[i].o1+lc[i].w1; + end else if (s<=0) and (ns>0) then begin + lc[i].o2 := o; + lc[i].w2 := d/(d-nd); + if lc[i].w2<0.0 then lc[i].w2:=0.0 else if lc[i].w2>1.0 then lc[i].w2:=1.0; + lc[i].ow2 := lc[i].o2+lc[i].w2; + end; + + d := nd; + s := ns; + end; + + assert ((lc[i].o1<0) = (lc[i].o2<0)); + if allPositive then exit; + + pi := i; + end; + + if not hasPositive then SetLength(lc,0); + + result := TRUE; + end; + + function CountLineConnection: boolean; + var + i,pi,ni: integer; + o,no: integer; + d1,d2: float; + s1,s2: integer; + + function SeqOk: boolean; + var + i,pi: integer; + begin + result := FALSE; + pi := High(inf.vertexes); + for i := Low(inf.vertexes) to High(inf.vertexes) do begin + if lc[i].o1>=0 then begin + if lc[i].ip1<>-2 then begin + if lc[pi].o1<0 then Exit; + if lc[pi].ip2=-2 then Exit; + end; + end else if lc[pi].o1>=0 then begin + if lc[pi].ip2<>-2 then begin + if lc[i].o1<0 then Exit; + if lc[i].ip1=-2 then Exit; + end; + end; + pi := i; + end; + result := TRUE; + end; + + begin + result := FALSE; + for i := Low(inf.vertexes) to High(inf.vertexes) do if lc[i].o1>=0 then lc[i].li := 0 else lc[i].li := -1; + + for i := Low(inf.vertexes) to High(inf.vertexes) do begin + if lc[i].o1>=0 then begin + if i=0 then pi := High(inf.vertexes) else pi := i-1; + if i=High(inf.vertexes) then ni := 0 else ni := i+1; + + o := lc[i].o1; + if o=High(outf.vertexes) then no:=0 else no:=o+1; + d1 := Dot(Cross(Sub(inv.vertexes[inf.vertexes[pi]].p, outv.vertexes[outf.vertexes[o]].p), Sub(inv.vertexes[inf.vertexes[pi]].p, outv.vertexes[outf.vertexes[no]].p)), outf.normal); + s1 := GetSign(d1); + o := lc[i].o2; + if o=High(outf.vertexes) then no:=0 else no:=o+1; + d2 := Dot(Cross(Sub(inv.vertexes[inf.vertexes[pi]].p, outv.vertexes[outf.vertexes[o]].p), Sub(inv.vertexes[inf.vertexes[pi]].p, outv.vertexes[outf.vertexes[no]].p)), outf.normal); + s2 := GetSign(d2); + assert((s1>=0) or (s2>=0)); + if lc[pi].o1<0 then s1 := -1; + if s1<=0 then lc[i].ip1 := -2 + else if s2<=0 then lc[i].ip1 := -1 + else lc[i].ip1 := 0; + + o := lc[i].o1; + if o=High(outf.vertexes) then no:=0 else no:=o+1; + d1 := Dot(Cross(Sub(inv.vertexes[inf.vertexes[i]].p, outv.vertexes[outf.vertexes[o]].p), Sub(inv.vertexes[inf.vertexes[i]].p, outv.vertexes[outf.vertexes[no]].p)), outf.normal); + s1 := GetSign(d1); + o := lc[i].o2; + if o=High(outf.vertexes) then no:=0 else no:=o+1; + d2 := Dot(Cross(Sub(inv.vertexes[inf.vertexes[i]].p, outv.vertexes[outf.vertexes[o]].p), Sub(inv.vertexes[inf.vertexes[i]].p, outv.vertexes[outf.vertexes[no]].p)), outf.normal); + s2 := GetSign(d2); + assert((s1>=0) or (s2>=0)); + if lc[ni].o1<0 then s2 := -1; + if s2<=0 then lc[i].ip2 := -2 + else if s1<=0 then lc[i].ip2 := -1 + else lc[i].ip2 := 0; + end; + end; + + pi := High(inf.vertexes); + for i := Low(inf.vertexes) to High(inf.vertexes) do begin + if (lc[i].o1>=0) and (lc[pi].o1>=0) then begin + if lc[pi].ip2=0 then lc[i].ip1 := 0; + if lc[i].ip1=0 then lc[pi].ip2 := 0; + end; + pi := i; + end; + + pi := High(inf.vertexes); + for i := Low(inf.vertexes) to High(inf.vertexes) do begin + if (lc[i].o1>=0) and (lc[pi].o1>=0) then begin + if (lc[pi].ip2=-1) and (lc[i].ip1=-1) then Exit; + end; + pi := i; + end; + + for i := Low(inf.vertexes) to High(inf.vertexes) do begin + if (lc[i].o1>=0) then begin + if (lc[i].ip1=-1) and (lc[i].ip2=-2) then lc[i].li := -1; + if (lc[i].ip1=-2) and (lc[i].ip2=-1) then lc[i].li := -1; + end; + end; + + for i := Low(inf.vertexes) to High(inf.vertexes) do begin + if (lc[i].o1>=0) and (lc[i].li=-1) then begin + lc[i].o1 := -1; + end; + end; + + for i := Low(inf.vertexes) to High(inf.vertexes) do begin + if (lc[i].o1>=0) then begin + if lc[i].ip1<>-2 then lc[i].ip1 := -1; + if lc[i].ip2<>-2 then lc[i].ip2 := -1; + end; + end; + + Assert(seqOk); // corrupted order of inv + result := TRUE; + end; + + procedure CreateLciIndexes; + var + i,ni,pi: integer; + lci: ^LineCutInfo; + o,no,nno,po: integer; + + vci: array of integer; + tmp: CutInfo; + + const eps=1.0e-5; + + procedure NewVCI(i: integer); + var + cvci: integer; + begin + cvci := Length(vci); + SetLengtH(vci, cvci+1); + vci[cvci] := i; + end; + + begin + for i := Low(inf.vertexes) to High(inf.vertexes) do if lc[i].o1>=0 then begin + lc[i].hasTriangle := FALSE; + if lc[i].ip1=-2 then begin + o := lc[i].o1; + if o = High(outf.vertexes) then no := 0 else no := o+1; + + if lc[i].w11.0-eps then begin + lc[i].ip1 := outf.vertexes[no]; + lc[i].il1 := outf.lines[o]; + lc[i].op1 := o; + end else begin + lci := @vCutInfo[outf.lines[o]]; + if lci.cutIn.pIndex < 0 then begin + lci.cutIn.pIndex := NewVertex( + Add(outv.vertexes[outf.vertexes[o]].p, Scale(Sub(outv.vertexes[outf.vertexes[no]].p, outv.vertexes[outf.vertexes[o]].p), lc[i].w1)) + ); + lci.cutIn.lIndex := NewLine(lci.cutIn.pIndex, outf.vertexes[o]); + lc[i].ip1 := lci.cutIn.pIndex; + lc[i].il1 := lci.cutIn.lIndex; + NewVCI(outf.lines[o]); + end else begin + Assert(outv.lines[lci.cutIn.lIndex].p2=outf.vertexes[o]); + lc[i].ip1 := lci.cutIn.pIndex; + lc[i].il1 := lci.cutIn.lIndex; + end; + lc[i].op1 := o; + end; + end else begin + if i=0 then pi:=High(inf.vertexes) else pi := i-1; + if lc[i].ip1<0 then lc[i].ip1 := NewVertex(inv.vertexes[inf.vertexes[pi]].p); + end; + + if lc[i].ip2=-2 then begin + o := lc[i].o2; + if o = High(outf.vertexes) then no := 0 else no := o+1; + + if lc[i].w21.0-eps then begin + if no = High(outf.vertexes) then nno := 0 else nno := no+1; + lc[i].ip2 := outf.vertexes[no]; + lc[i].il2 := outf.lines[no]; + lc[i].op2 := nno; + end else begin + lci := @vCutInfo[outf.lines[o]]; + if lci.cutOut.pIndex < 0 then begin + lci.cutOut.pIndex := NewVertex( + Add(outv.vertexes[outf.vertexes[o]].p, Scale(Sub(outv.vertexes[outf.vertexes[no]].p, outv.vertexes[outf.vertexes[o]].p), lc[i].w2)) + ); + lci.cutOut.lIndex := NewLine(lci.cutOut.pIndex, outf.vertexes[no]); + lc[i].ip2 := lci.cutOut.pIndex; + lc[i].il2 := lci.cutOut.lIndex; + NewVCI(outf.lines[o]); + end else begin + Assert(outv.lines[lci.cutOut.lIndex].p2=outf.vertexes[no]); + lc[i].ip2 := lci.cutOut.pIndex; + lc[i].il2 := lci.cutOut.lIndex; + end; + lc[i].op2 := no; + end; + end else begin + // add corner triangles if we need it + if i=High(inf.vertexes) then ni := 0 else ni := i+1; + if lc[i].ip2<0 then begin + if lc[ni].ip1<0 then lc[ni].ip1 := NewVertex(inv.vertexes[inf.vertexes[i]].p); + lc[i].ip2 := lc[ni].ip1; + end; + + if lc[i].o2 = lc[ni].o1 then begin + if lc[ni].w1>1-eps then begin + // single + if lc[ni].o1 = High(outf.vertexes) then lc[i].op2 := 0 else lc[i].op2 := lc[ni].o1+1; + lc[i].il2 := NewLine(lc[i].ip2, outf.vertexes[lc[i].op2]); + lc[ni].il1 := lc[i].il2; + lc[ni].op1 := lc[i].op2; + end else if (lc[i].w2=0) and ((lc[i].ip1=outf.vertexes[lc[i].op2]) or (lc[i].ip2=outf.vertexes[lc[i].op1]) or (lc[i].ip1=lc[i].ip2)) then + lc[i].li := -1; + end; + end; + + procedure UseLciIndexes; + var + o,i,ni: integer; + vIndex, lIndex: IntegerArray; + + procedure AddVertex(v: integer); + var + vc: integer; + begin + vc := Length(vIndex); + SetLength(vIndex, vc+1); + vIndex[vc] := v; + end; + + procedure AddLine(l: integer); + var + lc: integer; + begin + lc := Length(lIndex); + SetLength(lIndex, lc+1); + lIndex[lc] := l; + end; + + begin + for i := Low(inf.vertexes) to High(inf.vertexes) do if lc[i].li>=0 then begin + if i=High(inf.vertexes) then ni:=0 else ni:=i+1; + if lc[i].hasTriangle then begin + assert((lc[i].o1>=0) and (lc[ni].ip1=lc[i].ip2) and (lc[ni].op1<>lc[i].op2)); + SetLength(vIndex,0); + SetLength(lIndex,0); + + AddVertex(lc[i].ip2); + AddVertex(outf.vertexes[lc[ni].op1]); + AddVertex(outf.vertexes[lc[i].op2]); + + AddLine(lc[ni].il1); + AddLine(outf.lines[lc[ni].op1]); + AddLine(lc[i].il2); + + Assert(Complete(outv, vIndex, lIndex)); + AddFace(vIndex, lIndex); + end; + + SetLength(vIndex,0); + SetLength(lIndex,0); + AddVertex(lc[i].ip1); + AddVertex(lc[i].ip2); + AddLine(lc[i].li); + AddLine(lc[i].il2); + o := lc[i].op2; + while o<>lc[i].op1 do begin + AddVertex(outf.vertexes[o]); + AddLine(outf.lines[o]); + if o=High(outf.vertexes) then o:=0 else Inc(o); + end; + AddVertex(outf.vertexes[o]); + AddLine(lc[i].il1); + + + Assert(Complete(outv, vIndex, lIndex)); + AddFace(vIndex, lIndex); + end; + end; + +begin + + outf := @outv.faces[outfIndex]; + inf := @inv.faces[infIndex]; + + result := FALSE; + + if not InitLineConnection then Exit; + if length(lc)>0 then begin + if not CountLineConnection then Exit; + CreateLciIndexes; + UseLciIndexes; + end; + result := TRUE; +end; + +procedure AddSubConvex(var outv, inv: Convex); +var + o,i,j: integer; + initialOfc, initialIfc: integer; + goodFaceIn, goodFaceOut: array of integer; + oldSize,g: integer; + vCutInfoIn, vCutInfoOut: LineCutInfoArray; +begin + initialIfc := Length(inv.faces); + initialOfc := Length(outv.faces); + SetLength(goodFaceIn, initialIfc); + SetLength(goodFaceOut, initialOfc); + for i := Low(inv.faces) to High(inv.faces) do goodFaceIn[i] := 0; + for i := Low(outv.faces) to High(outv.faces) do goodFaceOut[i] := 0; + + SetLength(vCutInfoIn, Length(inv.lines)); + SetLength(vCutInfoOut, Length(outv.lines)); + + for i := Low(vCutInfoIn) to High(vCutInfoIn) do begin + vCutInfoIn[i].cutIn.pIndex := -1; + vCutInfoIn[i].cutOut.pIndex := -1; + vCutInfoIn[i].swaped := FALSE; + end; + + for i := Low(vCutInfoOut) to High(vCutInfoOut) do begin + vCutInfoOut[i].cutIn.pIndex := -1; + vCutInfoOut[i].cutOut.pIndex := -1; + vCutInfoOut[i].swaped := FALSE; + end; + + o := 0; + while o=0 then begin + if i>j then inv.faces[j]:=inv.faces[i]; + Inc(j); + end; + SetLength(inv.faces, j); + + j := 0; + for i := Low(outv.faces) to High(outv.faces) do if goodFaceOut[i]>=0 then begin + if i>j then outv.faces[j]:=outv.faces[i]; + Inc(j); + end; + SetLength(outv.faces, j); +end; + +procedure TranslateConvex(var v: Convex; const b: Point); +var + i: integer; +begin + for i := Low(v.vertexes) to High(v.vertexes) do begin + v.vertexes[i].p := Add(v.vertexes[i].p, b); + end; + + for i := Low(v.faces) to High(v.faces) do begin + v.faces[i].nshift := v.faces[i].nshift - Dot(v.faces[i].normal, b); + v.faces[i].pinside := Add(v.faces[i].pinside, b); + end; + + v.pmin := Add(v.pmin, b); + v.pmax := Add(v.pmax, b); +end; + +procedure ScaleConvex(var v: Convex; s: float); +var + i: integer; +begin + for i := Low(v.vertexes) to High(v.vertexes) do begin + v.vertexes[i].p := Scale(v.vertexes[i].p, s); + end; + + for i := Low(v.faces) to High(v.faces) do begin + v.faces[i].nshift := v.faces[i].nshift * s; + v.faces[i].pinside := Scale(v.faces[i].pinside, s); + end; + + v.pmin := Scale(v.pmin, s); + v.pmax := Scale(v.pmax, s); +end; + +end. + diff --git a/Points.pas b/Points.pas new file mode 100644 index 0000000..d6bc196 --- /dev/null +++ b/Points.pas @@ -0,0 +1,187 @@ +unit Points; + +interface + +type + float = single; + Point = record case boolean of true: (x,y,z:float); false: (c: array [0..2] of float) end; + PPoint = ^Point; + + function ToPoint (x,y,z: float): Point; + function Dot (const p1,p2: Point): float; + function Cross (const p1,p2: Point): Point; + function Volume (const p1,p2,p3: Point): float; + function SqrLen (const p: Point): float; + function LengthP (const p: Point): float; + function Norm (const p: Point): Point; + function Add (const p1,p2: Point): Point; + function Sub (const p1,p2: Point): Point; + function Scale (const p: Point; s: float): Point; + function Mid (const p1: Point; f1: float; const p2: Point; f2: float) : Point; + function Atan2 (y,x:float):float; + +type + Matrix = array [0..3] of Point; + PMatrix = ^Matrix; + + procedure SetID (var M: Matrix); + procedure Translate (var M: Matrix; const P: Point); + procedure Rotate (var M: Matrix; coord: integer; a: float); + procedure RotateToNew (const M: Matrix; var result: Matrix; coord: integer; a: float); + procedure ScaleM (var M: Matrix; S : Point); + function RotateP (const M: Matrix; const P: Point; OnlyRot: boolean = false): Point; + +implementation + + uses Math; + + function ToPoint (x,y,z: float): Point; + begin + Result.x := x; + Result.y := y; + Result.z := z; + end; + + function Dot (const p1,p2: Point): float; + begin + Result := p1.x*p2.x + p1.y*p2.y + p1.z*p2.z; + end; + + function Cross (const p1,p2: Point): Point; + begin + Result.x := p1.y*p2.z - p1.z*p2.y; + Result.y := p1.z*p2.x - p1.x*p2.z; + Result.z := p1.x*p2.y - p1.y*p2.x; + end; + + function Volume (const p1,p2,p3: Point): float; + begin + Result := Dot(Cross(p1,p2),p3); + end; + + function SqrLen(const p: Point): float; + begin + Result := sqr(p.x)+sqr(p.y)+sqr(p.z); + end; + + function LengthP (const p: Point): float; + begin + Result := sqrt(sqr(p.x)+sqr(p.y)+sqr(p.z)); + end; + + function Norm (const p: Point): Point; + var + l: float; + begin + l := 1.0/sqrt(sqr(p.x)+sqr(p.y)+sqr(p.z)); + Result.x := p.x*l; + Result.y := p.y*l; + Result.z := p.z*l; + end; + + function Add (const p1,p2: Point): Point; + begin + Result.x := p1.x+p2.x; + Result.y := p1.y+p2.y; + Result.z := p1.z+p2.z; + end; + + function Sub (const p1,p2: Point): Point; + begin + Result.x := p1.x-p2.x; + Result.y := p1.y-p2.y; + Result.z := p1.z-p2.z; + end; + + function Scale (const p: Point; s: float): Point; + begin + Result.x := p.x*s; + Result.y := p.y*s; + Result.z := p.z*s; + end; + + function Mid (const p1: Point; f1: float; const p2: Point; f2: float) : Point; + begin + Result := Add(p1, Scale(Sub(p2,p1), f1/(f1+f2))); + end; + + procedure SetID (var M: Matrix); + begin + M[0] := ToPoint(1,0,0); + M[1] := ToPoint(0,1,0); + M[2] := ToPoint(0,0,1); + M[3] := ToPoint(0,0,0); + end; + + procedure Translate (var M: Matrix; const P: Point); + begin + M[3] := Add(M[3], P); + end; + + procedure Rotate(var M: Matrix; coord: integer; a: float); + // умножение справа + var + c,s,t: float; + i : integer; + c1,c2 : integer; + begin + c := cos(a); + s := sin(a); + c1 := (coord+1) mod 3; + c2 := (coord+2) mod 3; + for i := 0 to 3 do begin + t := M[i].c[c1]*c - M[i].c[c2]*s; + M[i].c[c2] := M[i].c[c2]*c + M[i].c[c1]*s; + M[i].c[c1] := t; + end; + end; + + procedure RotateToNew(const M: Matrix; var result: Matrix; coord: integer; a: float); + var + c,s: float; + i : integer; + c1,c2 : integer; + begin + c := cos(a); + s := sin(a); + c1 := (coord+1) mod 3; + c2 := (coord+2) mod 3; + for i := 0 to 3 do begin + result[i].c[c1] := M[i].c[c1]*c - M[i].c[c2]*s; + result[i].c[c2] := M[i].c[c2]*c + M[i].c[c1]*s; + end; + end; + + procedure ScaleM (var M: Matrix; S : Point); + var + j, i : integer; + begin + for j := 0 to 2 do + for i := 0 to 3 do + M[i].c[j] := M[i].c[j] * S.c[j]; + end; + + function RotateP (const M: Matrix; const P: Point; OnlyRot : boolean = false) : Point; + var + i : integer; + begin + if OnlyRot then begin + for i := 0 to 2 do + Result.c[i] := M[0].c[i]*P.c[0] + M[1].c[i]*P.c[1] + M[2].c[i]*P.c[2]; + end else begin + for i := 0 to 2 do + Result.c[i] := M[0].c[i]*P.c[0] + M[1].c[i]*P.c[1] + M[2].c[i]*P.c[2] + M[3].c[i]; + end; + end; + + function Atan2(y,x:float):float; + asm + FLD Y + FLD X + FPATAN + FWAIT + end; + +end. + + diff --git a/Render.pas b/Render.pas new file mode 100644 index 0000000..e160f9e --- /dev/null +++ b/Render.pas @@ -0,0 +1,1081 @@ +unit Render; + +{$IFOPT Q+} +{$DEFINE HASQ} +{$ENDIF} + +interface + +uses Windows, Messages, WinAPI, Points, Geometry; + +const + STENCIL_ORD = 4; + STENCIL_SIZE = 1 shl STENCIL_ORD; + STENCIL_FULL = cardinal(1 shl ((1 shl STENCIL_ORD) - 1)) - cardinal(1) + cardinal(1 shl ((1 shl STENCIL_ORD) - 1)); + +type + T32Colors = array [0..STENCIL_SIZE-1] of TColor; + P32Colors = ^T32Colors; + + StencilCell = record + count: integer; + bits: array [0..STENCIL_SIZE-1] of cardinal; + pixels: array [0..STENCIL_SIZE-1] of P32Colors; + end; + + PStencilCell = ^StencilCell; + + StencilBuffer = record + sizeX, sizeY: integer; + cells: array of StencilCell; + end; + + RenderContext = record + buffer: TBitmap; + stencil: StencilBuffer; + viewC: Point; + view: Matrix; + viewSX, viewSY: float; + polyCounter: integer; + end; + +procedure CreateRenderContext(var context: RenderContext; sizeX, sizeY: integer); +procedure SetRenderContext(var context: RenderContext; const viewC: Point; viewAX, viewAZ: float; viewSX, viewSY: float); +procedure DeleteRenderContext(var context: RenderContext); +procedure DrawConvex(var context: RenderContext; const v: Convex); +function FastCheckCube(var context: RenderContext; const pmin, pmax: Point): boolean; + +procedure Counts(var context: RenderContext); + +var + dt: TLargeInteger = 0; + counter1: TLargeInteger = 0; + counter2: TLargeInteger = 0; + counter3: TLargeInteger = 0; + counter4: TLargeInteger = 0; + counter5: TLargeInteger = 0; + counter6: TLargeInteger = 0; + counter7: TLargeInteger = 0; + counter8: TLargeInteger = 0; + + texture, texture2: array [0..$30000] of integer; + palette: array [0..15] of TColor; + basePalette: array [0..15] of TColor; + +implementation + +uses fxMath; + +function GetBitCount(c: cardinal): integer; +begin + c := (c and $AAAAAAAA) shr 1 + (c and $55555555); + c := (c and $CCCCCCCC) shr 2 + (c and $33333333); + c := (c and $F0F0F0F0) shr 4 + (c and $0F0F0F0F); + c := (c and $FF00FF00) shr 8 + (c and $00FF00FF); + c := (c and $FFFF0000) shr 16 + (c and $0000FFFF); + result := integer(c); +end; + +var bitCount: array [0..$FFFF] of integer; + +procedure CreateRenderContext(var context: RenderContext; sizeX, sizeY: integer); +var + i,j,k: integer; + //r,g,b: integer; + pc: PColor; +begin + CreateBitmap(context.buffer, sizeX, sizeY, 32); + context.stencil.sizeX := (sizeX+STENCIL_SIZE-1) div STENCIL_SIZE; + context.stencil.sizeY := (sizeY+STENCIL_SIZE-1) div STENCIL_SIZE; + SetLength(context.stencil.cells, context.stencil.sizeX*context.stencil.sizeY); + for j := 0 to context.stencil.sizeY-1 do for i := 0 to context.stencil.sizeX-1 do begin + for k := 0 to STENCIL_SIZE-1 do begin + pc := context.buffer.Mem; + Inc(pc, (j*STENCIL_SIZE+k)*context.buffer.sizeX+(i*STENCIL_SIZE)); + context.stencil.cells[j*context.stencil.sizeX+i].pixels[k] := P32Colors(pc); + end; + end; + + for i := Low(bitCount) to High(bitCount) do bitCount[i] := getBitCount(i); + + for i := Low(texture) to High(texture) do begin + texture[i] := random(16); + end; + + for i := 0 to 5 do basePalette[i] := (random(8)) * $20100 + $402000; + for i := 6 to 9 do basePalette[i] := (random(64)) * $1 + $303030; + for i := 10 to 15 do basePalette[i] := $204000 + (random(8)) * $10200; + + for i := 0 to 255 do for j := 0 to 255 do begin + texture[i shl 8 + j] := random(6); + texture[(i+256) shl 8 + j] := random(6)+10; + texture[(i+512) shl 8 + j] := random(4)+6; + end; + + for k := 0 to 0 do begin + for i := 0 to 255 do for j := 0 to 255 do begin + texture2[i shl 8 + j] := ( + texture[(i and $F0 + $05) and $FF shl 8 + (j and $F0 + $05) and $FF] * (16 - i and $F) * (16 - j and $F) + + texture[(i and $F0 + $15) and $FF shl 8 + (j and $F0 + $05) and $FF] * ( i and $F) * (16 - j and $F) + + texture[(i and $F0 + $05) and $FF shl 8 + (j and $F0 + $15) and $FF] * (16 - i and $F) * ( j and $F) + + texture[(i and $F0 + $15) and $FF shl 8 + (j and $F0 + $15) and $FF] * ( i and $F) * ( j and $F) + 128) div (16*16); + end; + + for i := 0 to 255 do for j := 0 to 255 do begin + texture2[i shl 8 + j + $10000] := ( + texture[(i and $F0 + $05) and $FF shl 8 + (j and $F0 + $05) and $FF + $10000] * (16 - i and $F) * (16 - j and $F) + + texture[(i and $F0 + $15) and $FF shl 8 + (j and $F0 + $05) and $FF + $10000] * ( i and $F) * (16 - j and $F) + + texture[(i and $F0 + $05) and $FF shl 8 + (j and $F0 + $15) and $FF + $10000] * (16 - i and $F) * ( j and $F) + + texture[(i and $F0 + $15) and $FF shl 8 + (j and $F0 + $15) and $FF + $10000] * ( i and $F) * ( j and $F) + 128) div (16*16); + end; + + for i := 0 to 255 do for j := 0 to 255 do begin + texture2[i shl 8 + j + $20000] := ( + texture[(i and $F0 + $05) and $FF shl 8 + (j and $F0 + $05) and $FF + $20000] * (16 - i and $F) * (16 - j and $F) + + texture[(i and $F0 + $15) and $FF shl 8 + (j and $F0 + $05) and $FF + $20000] * ( i and $F) * (16 - j and $F) + + texture[(i and $F0 + $05) and $FF shl 8 + (j and $F0 + $15) and $FF + $20000] * (16 - i and $F) * ( j and $F) + + texture[(i and $F0 + $15) and $FF shl 8 + (j and $F0 + $15) and $FF + $20000] * ( i and $F) * ( j and $F) + 128) div (16*16); + end; + + texture := texture2; + end; +end; + +type + AInteger = array [0 .. MaxInt div sizeof(integer)-1] of integer; + PAInteger = ^AInteger; + + ProjPoint = record + p: Point; + planeDist: array [0..3] of float; + hasProj: integer; // -1 hz, 0 out, 1 in + sx, sy: float; + end; + + PProjPoint = ^ProjPoint; + AProjPoint = array [0 .. MaxInt div sizeof(ProjPoint)-1] of ProjPoint; + PAProjPoint = ^AProjPoint; + + CutedLine = record + processed: boolean; + cuted: boolean; + failed: boolean; + reversed: boolean; + w1, w2: float; + zeroDist: float; + x1, x2: integer; + y1, y2: integer; + i1, i2: integer; // indexes of frustum border planes + x: PAInteger; + end; + + PCutedLine = ^CutedLine; + ACutedLine = array [0 .. MaxInt div sizeof(CutedLine)-1] of CutedLine; + PACutedLine = ^ACutedLine; + + Zone = record + //x1, x2: integer; + y1, y2: integer; + lx, rx: PAInteger; + end; + + PZone = ^Zone; + AZone = array [0 .. MaxInt div sizeof(Zone)-1] of Zone; + PAZone = ^AZone; + + FaceTexturingInfo = record + processed: boolean; + w0,wdx,wdy,tx0,txdx,txdy,ty0,tydx,tydy: float; + end; + +var + intBuffer: array [0..$FFFFF] of integer; + intTop: integer = 0; + projPointBuffer: array [0..$FFFF] of ProjPoint; + projPointTop: integer = 0; + cutedLineBuffer: array [0..$FFFF] of CutedLine; + cutedLineTop: integer = 0; + zoneBuffer: array [0..$FFFF] of Zone; + zoneTop: integer = 0; + +procedure SetRenderContext(var context: RenderContext; const viewC: Point; viewAX, viewAZ: float; viewSX, viewSY: float); +var + i, j, k: integer; + Px, Py: PColor; + pc: PStencilCell; + cx, cy: integer; +begin + SetId(context.view); + Translate(context.view, viewC); + Rotate(context.view, 1, viewAX); + Rotate(context.view, 0, viewAZ); + + context.viewC := viewC; + context.viewSX := viewSX; + context.viewSY := viewSY; + context.polyCounter := 0; + + Py := context.buffer.Mem; + for j := 0 to context.buffer.SizeY-1 do begin + Px := Py; + for i := 0 to context.buffer.SizeX-1 do begin + Px^ := $002244; + Inc(Px); + end; + Inc(Py, context.buffer.SizeX); + end; + + for j := 0 to context.stencil.sizeY-1 do for i := 0 to context.stencil.sizeX - 1 do begin + pc := @context.stencil.cells[j*context.stencil.sizeX+i]; + if j=context.stencil.sizeY-1 then cy := context.buffer.SizeY-(j*STENCIL_SIZE) else cy := STENCIL_SIZE; + if i=context.stencil.sizeX-1 then cx := context.buffer.SizeX-(i*STENCIL_SIZE) else cx := STENCIL_SIZE; + pc.count := cx*cy; + for k := 0 to STENCIL_SIZE-1 do pc.bits[k] := 0; + end; +end; + +procedure DeleteRenderContext(var context: RenderContext); +begin + DeleteBitmap(context.buffer); +end; + +procedure DrawConvex(var context: RenderContext; const v: Convex); +var + i,j: integer; + proj: PAProjPoint; + pp: PProjPoint; + fc1, fc2: float; + lc: PACutedLine; + zones: PAZone; + vnormal: Point; + vshift: float; + f: float; + + procedure ProjectPoint(pindex: integer); + begin + pp := @proj[pindex]; + + pp.hasProj := 0; + pp.p := rotateP(context.view, v.vertexes[pindex].p); + pp.planeDist[0] := +pp.p.x*context.viewSx*0.999 + pp.p.z; + pp.planeDist[1] := +pp.p.y*context.viewSy*0.999 + pp.p.z; + pp.planeDist[2] := -pp.p.x*context.viewSx*0.999 + pp.p.z; + pp.planeDist[3] := -pp.p.y*context.viewSy*0.999 + pp.p.z; + + if pp.p.z>0.0001 then begin + pp.sx := (pp.p.x/pp.p.z*context.viewSX+1.0) * context.buffer.SizeX * 0.5; + pp.sy := (pp.p.y/pp.p.z*context.viewSY+1.0) * context.buffer.SizeY * 0.5; + pp.hasProj := 1; + end; + end; + + procedure PrerenderLine(index: integer); + var + pl: PLine; + pp1, pp2: PProjPoint; + x1,y1,x2,y2: float; + ix1,iy1,ix2,iy2: integer; + mp: Point; + i, j: integer; + x,cx,dx: integer; + + + begin + lc[index].processed := TRUE; + lc[index].cuted := FALSE; + lc[index].failed := FALSE; + lc[index].w1 := 0.0; + lc[index].w2 := 1.0; + lc[index].i1 := -1; + lc[index].i2 := -1; + + pl := @v.lines[index]; + pp1 := @proj[pl.P1]; + pp2 := @proj[pl.P2]; + + if pp1.hasProj<0 then ProjectPoint(pl.P1); + if pp2.hasProj<0 then ProjectPoint(pl.P2); + + lc[index].zeroDist := pp1.p.x*pp2.p.y - pp1.p.y*pp2.p.x; + + for j := 0 to 3 do begin + pl := @v.lines[index]; + fc1 := proj[pl.P1].planeDist[j]; + fc2 := proj[pl.P2].planeDist[j]; + + if (fc1>=0.0) then begin + if (fc2>=0.0) then begin + //nothing, line is fully correct + end else begin + f := fc1/(fc1-fc2); + if f=0.0) then begin + f := fc1/(fc1-fc2); + if f>lc[index].w1 then begin + lc[index].w1 := f; + lc[index].i1 := j; + end; + end else begin + // totally incorrect line + lc[index].cuted := TRUE; + end; + end; + end; + + if lc[index].w2<=lc[index].w1 then lc[index].cuted := TRUE; + + if not lc[index].cuted then begin + x1 := 0; + y1 := 0; + x2 := 0; + y2 := 0; + pl := @v.lines[index]; + pp1 := @proj[pl.P1]; + pp2 := @proj[pl.P2]; + + if lc[index].w1=0.0 then begin + if pp1.hasProj>0 then begin + x1 := pp1.sx; + y1 := pp1.sy; + end else begin + lc[index].failed := TRUE; + end; + end else begin + Inc(projPointTop); + + mp := Add(pp1.p, Scale(Sub(pp2.p, pp1.p), lc[index].w1)); + x1 := (mp.x/mp.z*context.viewSX+1.0) * context.buffer.SizeX * 0.5; + y1 := (mp.y/mp.z*context.viewSY+1.0) * context.buffer.SizeY * 0.5; + end; + + if lc[index].w2=1.0 then begin + if pp2.hasProj>0 then begin + x2 := pp2.sx; + y2 := pp2.sy; + end else begin + lc[index].failed := TRUE; + end; + end else begin + Inc(projPointTop); + mp := Add(pp1.p, Scale(Sub(pp2.p, pp1.p), lc[index].w2)); + x2 := (mp.x/mp.z*context.viewSX+1.0) * context.buffer.SizeX * 0.5; + y2 := (mp.y/mp.z*context.viewSY+1.0) * context.buffer.SizeY * 0.5; + end; + + if not lc[index].failed then begin + if y1>y2 then begin + ix1 := round(x2*$10000); + iy1 := round(y2*$10000); + ix2 := round(x1*$10000); + iy2 := round(y1*$10000); + lc[index].reversed := TRUE; + end else begin + ix1 := round(x1*$10000); + iy1 := round(y1*$10000); + ix2 := round(x2*$10000); + iy2 := round(y2*$10000); + lc[index].reversed := FALSE; + end; + lc[index].y1 := smallint(iy1 shr 16); + lc[index].y2 := smallint(iy2 shr 16); + lc[index].x1 := context.buffer.sizeX; + lc[index].x2 := 0; + + if lc[index].y1=lc[index].y2 then begin + if lc[index].y1<0 then lc[index].y1 := 0; + if lc[index].y2>=context.buffer.SizeY then lc[index].y2 := context.buffer.SizeY; + end else if lc[index].y1+1=lc[index].y2 then begin + if lc[index].y1<0 then lc[index].y1 := 0; + if lc[index].y2>=context.buffer.SizeY then lc[index].y2 := context.buffer.SizeY; + if lc[index].y1=context.buffer.SizeX then lc[index].x[0] := context.buffer.SizeX else lc[index].x[0] := cx; + lc[index].x1 := lc[index].x[0]; + lc[index].x2 := lc[index].x[0]; + end; + end else begin + x := ix1+MulDiv((lc[index].y1+1) shl 16-iy1, ix2-ix1, iy2-iy1); + dx := fxDiv(ix2-ix1, iy2-iy1); + if lc[index].y1<0 then begin + Inc(x, -dx*lc[index].y1); + lc[index].y1 := 0; + end; + + if lc[index].y2 >= context.buffer.SizeY then begin + lc[index].y2 := context.buffer.SizeY; + end; + + if lc[index].y2>lc[index].y1 then begin + lc[index].x := PAInteger(@intBuffer[intTop]); + Inc(intTop, lc[index].y2-lc[index].y1); + + for i := 0 to lc[index].y2-lc[index].y1-1 do begin + cx := smallint(x shr 16); + if cx<0 then lc[index].x[i] := 0 else if cx >= context.buffer.SizeX then lc[index].x[i] := context.buffer.SizeX else lc[index].x[i] := cx; + Inc(x, dx); + end; + + lc[index].x1 := lc[index].x[0]; + lc[index].x2 := lc[index].x[lc[index].y2-lc[index].y1-1]; + if lc[index].x1>lc[index].x2 then begin + cx := lc[index].x1; + lc[index].x1 := lc[index].x2; + lc[index].x2 := cx; + end; + end; + end; + end; + end; + end; + + procedure FillPixel(x,y: integer); + var + pc: PColor; + begin + pc := context.buffer.Mem; + Inc(pc, y*context.buffer.SizeX + x); + pc^ := $FFFFFF; + end; + + {$Q-} + procedure FillLine(y, x1, x2: integer; var fti: FaceTexturingInfo; const currentFace: Face); + var + celly: integer; + incell: integer; + inx1, inx2: integer; + bc: integer; + cellx1, cellx2: integer; + ps, lastps: PStencilCell; + pci: PCardinal; + + w1,w2,dw1,dw2, + tx1,tx2,dtx, + ty1,ty2,dty: integer; + + textureShift: integer; + ltx1,ltx2,lty1,lty2: integer; + + procedure CountDeltas; + begin + if inx2>inx1 then begin + dtx := (tx2-tx1) div (inx2+1-inx1); + dty := (ty2-ty1) div (inx2+1-inx1); + end else begin + dtx := (tx2-tx1); + dty := (ty2-ty1); + end; + end; + + procedure CountBordersAndIterate; + begin + tx1 := ltx1 + muldiv(ltx2-ltx1, w2, w1+w2); + ty1 := lty1 + muldiv(lty2-lty1, w2, w1+w2); + w1 := w1 + dw1*(inx2+1-inx1); + w2 := w2 + dw2*(inx2+1-inx1); + tx2 := ltx1 + muldiv(ltx2-ltx1, w2, w1+w2); + ty2 := lty1 + muldiv(lty2-lty1, w2, w1+w2); + CountDeltas; + end; {} + + procedure Loop(pixelLine: P32Colors; tx1,dtx,ty1,dty: integer); + var + j: integer; + begin + if pci^ = 0 then begin + for j := inx1 to inx2 do begin + {$I loop.inc} + tx1 := tx1 + dtx; + ty1 := ty1 + dty; + end; + end else if pci^ and (pci^+1) = 0 then begin // last free + bc := bsr(pci^)+1; + if bcinx2 then bc := inx2; + if inx1<=bc then begin + for j := inx1 to bc do begin + {$I loop.inc} + tx1 := tx1 + dtx; + ty1 := ty1 + dty; + end; + end; + end else begin + for j := inx1 to inx2 do begin + if ((pci^ shr j) and 1) = 0 then begin + {$I loop.inc} + end; + tx1 := tx1 + dtx; + ty1 := ty1 + dty; + end; + end; + end; + + function truncFactored (f : single; factor: integer): integer; + // for floor(f*2**factor) + // FUCKING HACK + var + d : integer absolute f; + e,m : integer; + begin + e := (d shr 23) and $FF; + if e<>0 then + m := (d and $7FFFFF) or $800000 + else + m := (d and $7FFFFF) shr 1; + e := 150-factor-e; // 127 + 23 - 16 - e + if e>=32 then result := 0 + else if e>0 then result := m shr e else result := m shl (-e); + if d<0 then result := -1-result; + end; {} + + procedure CountFTI(var fti: FaceTexturingInfo; const currentFace: Face); + var + MN, MNx, MNy, corner: Point; + den: float; + colorFactor: TColor; + lw1, lw2: float; + i,r,g,b: integer; + begin + with fti do begin + if not processed then begin + processed := TRUE; + MN := RotateP(context.view, currentFace.normal, true); + MNx := RotateP(context.view, currentFace.txn, true); + MNy := RotateP(context.view, currentFace.tyn, true); + + den := 1.0 / (dot(MN, context.view[3]) - currentFace.nshift); + corner.x := -1.0/context.viewSX; + corner.y := -1.0/context.viewSY; + corner.z := 1.0; + w0 := dot(MN, corner)*den; + tx0 := dot(MNx, corner) + w0*(currentFace.txc-dot(MNx, context.view[3])); + ty0 := dot(MNy, corner) + w0*(currentFace.tyc-dot(MNy, context.view[3])); + + corner.x := +1.0/context.viewSX; + corner.y := -1.0/context.viewSY; + corner.z := 1.0; + wdx := dot(MN, corner)*den; + txdx := dot(MNx, corner) + wdx*(currentFace.txc-dot(MNx, context.view[3])); + tydx := dot(MNy, corner) + wdx*(currentFace.tyc-dot(MNy, context.view[3])); + wdx := (wdx-w0) / (context.buffer.SizeX); + txdx := (txdx-tx0) / (context.buffer.SizeX); + tydx := (tydx-ty0) / (context.buffer.SizeX); + + corner.x := -1.0/context.viewSX; + corner.y := +1.0/context.viewSY; + corner.z := 1.0; + wdy := dot(MN, corner)*den; + textureShift := currentFace.tn * 65536; + txdy := dot(MNx, corner) + wdy*(currentFace.txc-dot(MNx, context.view[3])); + tydy := dot(MNy, corner) + wdy*(currentFace.tyc-dot(MNy, context.view[3])); + wdy := (wdy-w0) / (context.buffer.SizeY); + txdy := (txdy-tx0) / (context.buffer.SizeY); + tydy := (tydy-ty0) / (context.buffer.SizeY); + Inc(context.polyCounter); + + den := SqrLen(Add(context.viewC, currentFace.pinside)); + colorFactor := round(255/(den*0.3+1)) * $10101; + + for i := 0 to 15 do begin + r := (basePalette[i] shr 16) * (colorFactor shr 16) shr 7; + if r>255 then r:=255; + g := (basePalette[i] shr 8 and $FF) * (colorFactor shr 8 and $FF) shr 7; + if g>255 then g:=255; + b := (basePalette[i] and $FF) * (colorFactor and $FF) shr 7; + if b>255 then b:=255; + palette[i] := r shl 16 + g shl 8 + b; + end; + end; + + lw1 := w0 + wdx*(x1+1.0) + wdy*(y+1.0); + lw2 := w0 + wdx*(x2 ) + wdy*(y+1.0); + + dw1 := truncFactored(lw1, 24) and $3fffffff; + dw2 := truncFactored(lw2, 24) and $3fffffff; + ltx1 := truncFactored((tx0+txdx*(x1+1)+txdy*(y+1))/lw1, 16); + ltx2 := truncFactored((tx0+txdx*(x2 )+txdy*(y+1))/lw2, 16); + lty1 := truncFactored((ty0+tydx*(x1+1)+tydy*(y+1))/lw1, 16); + lty2 := truncFactored((ty0+tydx*(x2 )+tydy*(y+1))/lw2, 16); + + w1 := dw1+1; + w2 := 1; + dw1 := -dw1; + dw2 := dw2; + + if x2>x1+2 then begin + dw1 := dw1 div (x2-x1-1); + dw2 := dw2 div (x2-x1-1); + end; + end; + end; + + begin + inc(counter2, GetTimer); + { {} + if x2>x1 then begin + celly := y shr STENCIL_ORD; + incell := y and (STENCIL_SIZE-1); + cellx1 := x1 shr STENCIL_ORD; + cellx2 := (x2-1) shr STENCIL_ORD; + + ps := @context.stencil.cells[celly*context.stencil.sizeX + cellx1]; + lastps := @context.stencil.cells[celly*context.stencil.sizeX + cellx2]; + pci := @ps.bits[incell]; + + if (cellx1=cellx2) then begin + if pci^ <> STENCIL_FULL then begin + inx1 := x1 and (STENCIL_SIZE-1); + inx2 := (x2-1) and (STENCIL_SIZE-1)-1; + CountFTI(fti, currentFace); + CountBordersAndIterate; + Inc(inx2); + Loop(ps.pixels[incell],tx1,dtx,ty1,dty); + end; + end else begin + inx1 := x1 and (STENCIL_SIZE-1); + inx2 := STENCIL_SIZE-1; + if pci^ <> STENCIL_FULL then begin + CountFTI(fti, currentFace); + CountBordersAndIterate; + Loop(ps.pixels[incell],tx1,dtx,ty1,dty); + Inc(ps); + pci := @ps.bits[incell]; + tx1 := tx2; + ty1 := ty2; + // here we know, we has borders + end else begin + while true do begin + Inc(ps); + pci := @ps.bits[incell]; + x1 := x1 and not (STENCIL_SIZE - 1) + STENCIL_SIZE; + if pci^ <> STENCIL_FULL then begin + CountFTI(fti, currentFace); + break; + end; + if ps=lastps then exit; + end; + tx1 := ltx1 + muldiv(ltx2-ltx1, w2, w1+w2); + ty1 := lty1 + muldiv(lty2-lty1, w2, w1+w2); + // here we know, ps now good + end; + + while true do begin + // here we has border1 + if ps=lastps then begin + if pci^ <> STENCIL_FULL then begin + inx1 := 0; + inx2 := (x2-1) and (STENCIL_SIZE-1)-1; + w1 := w1 + dw1*(inx2+1-inx1); + w2 := w2 + dw2*(inx2+1-inx1); + tx2 := ltx1 + muldiv(ltx2-ltx1, w2, w1+w2); + ty2 := lty1 + muldiv(lty2-lty1, w2, w1+w2); + CountDeltas; + Inc(inx2); + Loop(ps.pixels[incell],tx1,dtx,ty1,dty); + end; + + exit; + end; + + inx1 := 0; + inx2 := STENCIL_SIZE-1; + if pci^ <> STENCIL_FULL then begin + w1 := w1 + dw1*STENCIL_SIZE; + w2 := w2 + dw2*STENCIL_SIZE; + tx2 := ltx1 + muldiv(ltx2-ltx1, w2, w1+w2); + ty2 := lty1 + muldiv(lty2-lty1, w2, w1+w2); + dtx := (tx2-tx1) div STENCIL_SIZE; + dty := (ty2-ty1) div STENCIL_SIZE; + Loop(ps.pixels[incell],tx1,dtx,ty1,dty); + Inc(ps); + pci := @ps.bits[incell]; + tx1 := tx2; + ty1 := ty2; + end else begin + while true do begin + w1 := w1 + dw1*STENCIL_SIZE; + w2 := w2 + dw2*STENCIL_SIZE; + Inc(ps); + pci := @ps.bits[incell]; + if pci^ <> STENCIL_FULL then break; + if ps=lastps then exit; + end; + tx1 := ltx1 + muldiv(ltx2-ltx1, w2, w1+w2); + ty1 := lty1 + muldiv(lty2-lty1, w2, w1+w2); + // here we know, ps now good + end; + end; + end; + end; + end; + {$IFDEF HASQ} {$Q+} {$ENDIF} + + procedure AdjustLineInfo(y, x1, x2: integer); + var + i: integer; + celly: integer; + incell: integer; + cellx1, cellx2: integer; + ps: PStencilCell; + pci: PCardinal; + newpci: Cardinal; + begin + if x2>x1 then begin + celly := y shr STENCIL_ORD; + incell := y and (STENCIL_SIZE-1); + cellx1 := x1 shr STENCIL_ORD; + cellx2 := (x2-1) shr STENCIL_ORD; + + ps := @context.stencil.cells[celly*context.stencil.sizeX + cellx1]; + + for i := cellx1 to cellx2 do begin + newpci := STENCIL_FULL; + if i=cellx1 then newpci := newpci - cardinal(1 shl ( x1 and (STENCIL_SIZE-1)) - 1); + if i=cellx2 then newpci := (newpci + cardinal(1 shl ((x2-1) and (STENCIL_SIZE-1) + 1))) and STENCIL_FULL; + + pci := @ps.bits[incell]; + newpci := newpci and not pci^; + ps.count := ps.count - bitcount[newpci and $FFFF] - bitcount[newpci shr 16]; + + pci^ := pci^ or newpci; + Inc(ps); + end; + end; + end; + + procedure DrawFace(var currentFace: Face; var zone: Zone); + var + i, j: integer; + pl: PLine; + pcl: PCutedLine; + failed: boolean; + zinside: boolean; + hl,lin,lout,rin,rout: boolean; + //eleft, eright: boolean; + center: Point; + + fti: FaceTexturingInfo; + begin + with currentFace do begin + failed := FALSE; + for i := Low(lines) to High(lines) do begin + pcl := @lc[lines[i]]; + if not pcl.processed then PrerenderLine(lines[i]); + if pcl.failed then failed := true; + end; + + center := toPoint(0.0, 0.0, 0.0); + for i := Low(vertexes) to High(vertexes) do + center := Add(center, proj[vertexes[i]].p); + center := Scale(center, 1/Length(vertexes)); + + zone.rx := nil; + zone.lx := nil; + zone.y1 := context.buffer.SizeY; + zone.y2 := 0; + + if not failed then begin + + lin := FALSE; + lout := FALSE; + rin := FALSE; + rout := FALSE; + hl := FALSE; + zinside := TRUE; + + for j := Low(lines) to High(lines) do begin + pl := @v.lines[lines[j]]; + pcl := @lc[lines[j]]; + if ((pl.P2 = vertexes[j]) and (pcl.zeroDist>0)) or ((pl.P1 = vertexes[j]) and (pcl.zeroDist<0)) then begin + end else begin + zinside := FALSE; + end; + + if not pcl.cuted then begin + + if pl.P2 = vertexes[j] then begin + if pcl.i1=0 then lout := TRUE; + if pcl.i2=0 then lin := TRUE; + if pcl.i1=2 then rout := TRUE; + if pcl.i2=2 then rin := TRUE; + end else begin + if pcl.i2=0 then lout := TRUE; + if pcl.i1=0 then lin := TRUE; + if pcl.i2=2 then rout := TRUE; + if pcl.i1=2 then rin := TRUE; + end; + + hl := TRUE; + if pcl.y1zone.y2 then zone.y2:=pcl.y2; + end; + end; + + + if hl then begin + //zone.x1 := context.buffer.sizeX; + //zone.x2 := 0; + + if lin and not lout then + zone.y1 := 0; + if rout and not rin then + zone.y1 := 0; + if rin and not rout then + zone.y2 := context.buffer.SizeY; + if lout and not lin then + zone.y2 := context.buffer.SizeY; + + zone.rx := PAInteger(@intBuffer[intTop]); + Inc(intTop, zone.y2-zone.y1); + zone.lx := PAInteger(@intBuffer[intTop]); + Inc(intTop, zone.y2-zone.y1); + for i := 0 to zone.y2-zone.y1-1 do begin + zone.rx[i] := context.buffer.sizeX; + zone.lx[i] := 0; + end; + + //eleft := TRUE; + //eright := TRUE; + + for j := Low(lines) to High(lines) do begin + pl := @v.lines[lines[j]]; + pcl := @lc[lines[j]]; + if not pcl.cuted then begin + if (pl.P2 = vertexes[j]) xor (pcl.reversed) then begin + for i := pcl.y1 to pcl.y2-1 do zone.rx[i-zone.y1] := pcl.x[i-pcl.y1]; + //eright := FALSE; + end else begin + for i := pcl.y1 to pcl.y2-1 do zone.lx[i-zone.y1] := pcl.x[i-pcl.y1]; + //eleft := FALSE; + end; + end; + + //if zone.x1 > pcl.x1 then zone.x1 := pcl.x1; + //if zone.x2 < pcl.x2 then zone.x2 := pcl.x2; + end; + + //if lin or lout or eleft then zone.x1 := 0; + //if rin or rout or eright then zone.x2 := context.buffer.sizeX -1; + end else if zinside then begin + zone.y1 := 0; + zone.y2 := context.buffer.SizeY; + + //zone.x1 := 0; + //zone.x2 := context.buffer.sizeX-1; + + zone.rx := PAInteger(@intBuffer[intTop]); + Inc(intTop, zone.y2-zone.y1); + zone.lx := PAInteger(@intBuffer[intTop]); + Inc(intTop, zone.y2-zone.y1); + for i := 0 to zone.y2-zone.y1-1 do begin + zone.rx[i] := context.buffer.sizeX; + zone.lx[i] := 0; + end; + end else begin + zone.y1 := context.buffer.sizeY; + zone.y2 := 0; + end; + + if (abs(currentFace.normal.x)>abs(currentFace.normal.y)) and (abs(currentFace.normal.x)>abs(currentFace.normal.z)) then begin + txn.x := 0.0; + txn.y := 600.0; + txn.z := 0.0; + txc := 0; + + tyn.x := 0.0; + tyn.y := 0.0; + tyn.z := -600.0; + tyc := 0; + end else if (abs(currentFace.normal.y)>abs(currentFace.normal.z)) then begin + txn.x := -600.0; + txn.y := 0.0; + txn.z := 0.0; + txc := 0; + + tyn.x := 0.0; + tyn.y := 0.0; + tyn.z := 600.0; + tyc := 0; + end else begin + txn.x := 600.0; + txn.y := 0.0; + txn.z := 0.0; + txc := 0; + + tyn.x := 0.0; + tyn.y := -600.0; + tyn.z := 0.0; + tyc := 0; + end; + { + txn := toPoint(0.0, 0.0, 0.0); + txc := currentFace.id; + tyn := toPoint(0.0, 0.0, 0.0); + tyc := 0; {} + + fti.processed := FALSE; + + for i := 0 to zone.y2-zone.y1-1 do if zone.lx[i] 0.0 then begin + DrawFace(v.faces[i], zones[i]); + end else begin + zones[i].y1 := 0; + zones[i].y2 := -1; + end; + end; + + for j := Low(v.faces) to High(v.faces) do begin + for i := 0 to zones[j].y2-zones[j].y1-1 do begin + AdjustLineInfo(i+zones[j].y1, zones[j].lx[i], zones[j].rx[i]); + end; + end; + + for i := 0 to Length(v.vertexes)-1 do begin + v.vertexes[i].icounter := 0; + v.vertexes[i].fcounter := 0.0; + end; +end; + +function FastCheckCube(var context: RenderContext; const pmin, pmax: Point): boolean; +var + middle: Point; + rp: array [0..7] of Point; + x, y: integer; + x1,y1,x2,y2: integer; + i,j: integer; + dx,dy,dz: Point; +begin + //if SqrLen(context.view[3]) < SqrLen(Sub(pmin, pmax)) * 20.0 then begin + //if true then begin + // result := true; + //end else begin + middle := RotateP(context.view, Scale(Add(pmin, pmax), 0.5)); + dx := Scale(context.view[0], pmax.x-pmin.x); + dy := Scale(context.view[1], pmax.y-pmin.y); + dz := Scale(context.view[2], pmax.z-pmin.z); + + rp[0] := Sub(middle, Scale(Add(dx, Add(dy, dz)), 0.5)); + rp[1] := Add(rp[0], dx); + rp[2] := Add(rp[0], dy); + rp[3] := Add(rp[0], dz); + rp[4] := Add(rp[1], dy); + rp[5] := Add(rp[2], dz); + rp[6] := Add(rp[3], dx); + rp[7] := Add(rp[4], dz); + x1 := 0; + y1 := 0; + x2 := 0; + y2 := 0; + + result := FALSE; + for i := 0 to 7 do if rp[i].z>rp[i].x*context.viewSX then result := TRUE; + if not result then exit; + + result := FALSE; + for i := 0 to 7 do if rp[i].z>-rp[i].x*context.viewSX then result := TRUE; + if not result then exit; + + result := FALSE; + for i := 0 to 7 do if rp[i].z>rp[i].y*context.viewSY then result := TRUE; + if not result then exit; + result := FALSE; + for i := 0 to 7 do if rp[i].z>-rp[i].y*context.viewSY then result := TRUE; + if not result then exit; + + result := FALSE; + for i := 0 to 7 do if rp[i].z<=0.5*abs(rp[i].x)*context.viewSX+0.0001 then result := TRUE; + if not result then for i := 0 to 7 do if rp[i].z<=0.5*abs(rp[i].y)*context.viewSY+0.0001 then result := TRUE; + + if not result then begin + for i := 0 to 7 do begin + x := trunc((rp[i].x/rp[i].z*context.viewSX+1.0) * context.buffer.SizeX * 0.5); + y := trunc((rp[i].y/rp[i].z*context.viewSY+1.0) * context.buffer.SizeY * 0.5); + + x := (x + ($10000 shl STENCIL_ORD)) shr STENCIL_ORD - $10000; + y := (y + ($10000 shl STENCIL_ORD)) shr STENCIL_ORD - $10000; + + if i=0 then begin + x1 := x; y1 := y; x2 := x; y2 := y; + end else begin + if xx2 then x2:=x; + if y>y2 then y2:=y; + end; + end; + + if x1<0 then x1:=0; + if y1<0 then y1:=0; + if x2>=context.stencil.sizeX then x2:=context.stencil.sizeX-1; + if y2>=context.stencil.sizeY then y2:=context.stencil.sizeY-1; + + for j := y1 to y2 do begin + for i := x1 to x2 do begin + if context.stencil.cells[i+j*context.stencil.sizeX].count>0 then begin + result := true; + break; + end; + end; + end; + end; + //end; +end; + +procedure Counts(var context: RenderContext); +var + i,j: integer; + pc: PColor; +begin + for j := 0 to context.stencil.sizeY-1 do for i := 0 to context.stencil.sizeX - 1 do begin + pc := context.buffer.Mem; + Inc(pc, i+j*context.buffer.SizeX); + pc^ := context.stencil.cells[j*context.stencil.sizeX+i].count*49; + end; +end; + +end. diff --git a/WinAPI.pas b/WinAPI.pas new file mode 100644 index 0000000..178de1c --- /dev/null +++ b/WinAPI.pas @@ -0,0 +1,828 @@ +unit WinAPI; + +interface + +{$IFOPT D+} + {$DEFINE DEBUG} +{$ENDIF} + +uses + Windows, Messages; + +type + TColor = cardinal; + + PColor = ^TColor; + + TKind = (kForm, kGroup, kDefault); + + TBitmap = record + Handle: HWND; + DC: HDC; + Mem: PColor; + SizeX, SizeY: integer; + end; + + PFormStack = ^TFormStack; + + TFormStack = record + Handle: hWnd; + Accels: THandle; + Next: PFormStack; + end; + + TFileOfByte = file of byte; + + TWndProc = function(Handle: HWND; Message: UINT; WP: WParam; LP: LParam): longint; stdcall; + THWNDFunction = function: HWND; stdcall; + + TPProc = procedure (d : pointer = nil); + + TEvent = record + p : TPProc; + d : pointer; + end; + + PWindow = ^TWindow; + TWindow = record + Handle: HWND; + //DC: HDC; + ID: Word; + Tag, TabOrder: integer; + Event: TEvent; + case TKind of + kForm: ( + Accels: THandle; + ); + kGroup: ( + MinID, MaxID: integer; + ); + end; + + TRadioGroup = array of TWindow; + +const + sChild = WS_CHILD or WS_VISIBLE or WS_CLIPSIBLINGS; + sFormDef = WS_OVERLAPPEDWINDOW or WS_CLIPCHILDREN; + sFormModal = WS_CLIPCHILDREN; + sFormFixedSize = WS_SYSMENU or WS_MINIMIZEBOX or WS_CLIPCHILDREN; + sButtonDef = sChild or BS_PUSHBUTTON; + sEdit = sChild or ES_AUTOHSCROLL or WS_TABSTOP or WS_BORDER; + sMemo = sChild or ES_MULTILINE or WS_HSCROLL or ES_AUTOHSCROLL or WS_VSCROLL or ES_AUTOVSCROLL or WS_BORDER; + sEdOut = sChild or ES_MULTILINE or WS_HSCROLL or ES_AUTOHSCROLL or WS_VSCROLL or ES_AUTOVSCROLL or ES_READONLY or WS_BORDER; + sStatic = sChild or ES_AUTOHSCROLL or ES_READONLY; + +var + WindowProc: TWndProc; + + MainPath: string; + + ExceptionCode: integer; + + MainForm: hWnd; + MainIcon: hIcon; + IdleProc: procedure; + + aTag: integer; + + Wnds: array [word] of PWindow; + TabList: array [word] of PWindow; + GetFocusEx: THWNDFunction = GetFocus; + + Font: hFont; + +function ToEvent(p : TPProc; d : pointer = nil) : TEvent; +function NoEvent : TEvent; + +function ToWide(s: string): WideString; +procedure GetMessages; +procedure CreateForm(var Window: TWindow; Name: string; Parent: HWND; Style: Cardinal); +procedure SetClientSize(H: hWnd; X, Y: integer); +procedure CorrectSize(H: hWnd; Mode: integer; minX, minY, maxX, maxY: integer; var R: TRect); + +procedure CreateAnyWindow(var Window: TWindow; ClassName, Name: string; X, Y, sizeX, sizeY: integer; Parent: HWND; Style: cardinal; + aTabOrder: boolean = False; aEvent: TPProc = nil; dEvent : pointer = nil); +procedure CreateRadioGroup(var Window: TRadioGroup; Names: array of string; X, Y, sizeX, sizeY: integer; Parent: HWND; + aTabOrder: boolean = False); +function RadioGroupChoise(Window: TRadioGroup): integer; +procedure MoveRadioGroup(var Window : TRadioGroup; x,y,sizex,sizey: integer); +procedure CreateMainMenu(var Window: TWindow; Parent: HWND); +procedure CreateSubMenu(var Window: TWindow; Name: string; Parent: HWND); +procedure CreateMenuItem(var Window: TWindow; Name: string; Parent: HWND; aEvent: TPProc = nil; dEvent : pointer = nil; Radio : boolean = false); +procedure CreateBitmap(var Bitmap: TBitmap; sizeX, sizeY: integer; Bits: byte); +function LoadBitmap(var Bitmap: TBitmap; FileName: string): boolean; +procedure ChgFont(Name: string; Size: integer); + +function GetClassName(H: HWND): string; + +procedure CheckControl(H: hWnd; ID, HiWP: word); +procedure Click(H: hWnd; ID, HiWP: word); +procedure DoTab; + +procedure ShowForm(var Window: TWindow; Maximized: boolean = false); +procedure ShowFormByPtr (p : pointer); +procedure HideTopForm; + +procedure DeleteBitmap(var Bitmap: TBitmap); +procedure DeleteWindow(var Window: TWindow); + +function GetCaption(H: HWND): string; +procedure SetCaption(H: HWND; S: string); +function MsgBox(hWnd: HWND; text, caption: string; uType: UINT): Integer; +procedure ShowError(Text: string); +function WaitingMessageBox: boolean; +procedure LoadStruct(var F: TFileOfByte; var V; Size: cardinal); + +function ToAccels(A: array of word): THandle; + +function IntToStr(N: integer): string; +function IntToHex(N: integer): string; +function FloatToStr(E: extended; W, D: integer): string; +function FloatToStrE(E: extended; W: integer): string; +function FloatToStrEE(E: extended; W, ED: integer): string; + +function GetTimer: int64; + +implementation + +{$IFDEF DEBUG} +uses SysUtils; +{$ENDIF} + +var + LastID, LastTab: integer; + FormStack: PFormStack; + + HList: array of array of array of array of PWindow; + FlagWaitingMessageBox : boolean = false; + + +function GetTimer: int64; +begin + QueryPerformanceCounter(result); +end; + +function IntToStr; +begin + Str(N, Result); +end; + +function IntToHex; +const + S = '0123456789ABCDEF'; +var + i: integer; +begin + SetLength(Result, 8); + for i := 0 to 7 do Result[8 - i] := S[N shr (i shl 2) and $0F + 1]; +end; + +function FloatToStr(E: extended; W, D: integer): string; +begin + Str(E: W: D, Result); +end; + +function FloatToStrE(E: extended; W: integer): string; +begin + Str(E: W, Result); +end; + +function FloatToStrEE(E: extended; W, ED: integer): string; +begin + Str(E: W+(4-ED), Result); + Assert(Result[Length(Result)-5]='E'); + Delete(Result, Length(Result)-3, 4-ED); +end; + +function ToEvent(p : TPProc; d : pointer = nil) : TEvent; +begin + Result.p := p; + Result.d := d; +end; + +function NoEvent : TEvent; +begin + Result.p := nil; + Result.d := nil; +end; + +function ToWide; +var + cchw: integer; +const CP_THREAD_ACP = 3; +begin + cchw := MultiByteToWideChar(CP_THREAD_ACP, 0, PChar(s), Length(s), nil, 0); + if cchw <> 0 then begin + SetLength(Result, cchw); + MultiByteToWideChar (CP_THREAD_ACP, 0, PChar(s), Length(s), PWideChar(Result), cchw); + end; +end; + +function GetAcc(H: hWnd): THandle; +var + bt: array [0 .. 3] of byte absolute H; + p: pointer; +begin + Result := 0; + p := HList; + if p = nil then Exit; + p := ppointer(integer(p) + bt[0] shl 2)^; + if p = nil then Exit; + p := ppointer(integer(p) + bt[1] shl 2)^; + if p = nil then Exit; + p := ppointer(integer(p) + bt[2] shl 2)^; + if p = nil then Exit; + p := ppointer(integer(p) + bt[3] shl 2)^; + if p = nil then Exit; + Result := PWindow(p).Accels; +end; + +procedure GetMessages; +var + Message: TMsg; + AW: hWnd; +begin + repeat + try + if not PeekMessage(Message, 0, 0, 0, 0) and (@IdleProc <> nil) then IdleProc + else begin + if not GetMessage(Message, 0, 0, 0) then Break; + AW := GetActiveWindow; + if TranslateAccelerator(AW, GetAcc(AW), Message) = 0 then begin + TranslateMessage(Message); + DispatchMessage(Message); + end; + end; + except + {$IFDEF DEBUG} + on E: exception do + MsgBox(MainForm, E.Message, 'ERROR', mb_OK or mb_IconError); + {$ELSE} + MsgBox(MainForm, 'Runtime error ' + IntToHex(ExceptionCode), 'ERROR', mb_OK or mb_IconError); + {$ENDIF} + end; + until False; +end; + +procedure DB (DC: HDC); +// толку нихуя +var + PFD : TPixelFormatDescriptor; + N: integer; +begin + FillChar(PFD, SizeOf(PFD), 0); + PFD.nSize := SizeOf(PFD); + PFD.dwFlags := PFD_DOUBLEBUFFER; + N := ChoosePixelFormat(DC, @PFD); + SetPixelFormat(DC, N, @PFD); +end; + +procedure CreateForm; +var + Sx, Sy, X, Y: integer; + bt: array [0 .. 3] of byte absolute Window.Handle; + + procedure RegClass(Name: string); + var + WindowClass: TWndClass; + begin + with WindowClass do begin + Style := 0;//cs_HRedraw or cs_VRedraw or cs_OwnDC; + lpfnWndProc := @WindowProc; + cbClsExtra := 0; + cbWndExtra := 0; + hInstance := MainInstance; + MainIcon := LoadIcon(MainInstance, 'MAINICON'); + hIcon := MainIcon; + hCursor := LoadCursor(0, idc_Arrow); + hbrBackground := COLOR_WINDOW; + lpszMenuName := nil; + lpszClassName := PChar(Name); + end; + RegisterClass(WindowClass); + end; + +begin + X := 600; + Y := 400; + RegClass(PChar('T' + Name)); + Sx := GetDeviceCaps(GetDC(0), HorzRes); + Sy := GetDeviceCaps(GetDC(0), VertRes); + with Window do begin + ID := LastId; + Inc(LastID); + Handle := CreateWindow(PChar('T' + Name), PChar(Name), Style, + (Sx - X) div 2, (Sy - Y) div 2, X, Y, + Parent, 0, MainInstance, nil); + + Wnds[ID] := @Window; + if MainForm = 0 then MainForm := Handle; + + //DC := GetDC(Handle); + //DB(DC); + + MinID := 0; + MaxID := 0; + Accels := 0; + + Event := NoEvent; + + SetLength(HList, 256); + SetLength(HList[bt[0]], 256); + SetLength(HList[bt[0], bt[1]], 256); + SetLength(HList[bt[0], bt[1], bt[2]], 256); + + HList[bt[0], bt[1], bt[2], bt[3]] := @Window; + + Tag := aTag; + end; +end; + +procedure SetClientSize; +var + WR, CR: TRect; +begin + GetWindowRect(H, WR); + GetClientRect(H, CR); + if X = -1 then X := CR.Right - CR.Left; + if Y = -1 then Y := CR.Bottom - CR.Top; + MoveWindow(H, + WR.Left - (X - (CR.Right - CR.Left)) div 2, + WR.Top - (Y - (CR.Bottom - CR.Top)) div 2, + X + (WR.Right - WR.Left) - (CR.Right - CR.Left), + Y + (WR.Bottom - WR.Top) - (CR.Bottom - CR.Top), True); +end; + +procedure CorrectSize; +var + D, D1, D2: integer; + WR, CR: TRect; +begin + GetWindowRect(H, WR); + GetClientRect(H, CR); + D1 := MinX + (WR.Right - WR.Left) - (CR.Right - CR.Left); + if MaxX = 0 then D2 := MaxInt else D2 := MaxX + (WR.Right - WR.Left) - (CR.Right - CR.Left); + D := R.Right - R.Left; + if D < D1 then D := D1 else if D > D2 then D := D2; + case Mode of + wmsz_TopLeft, wmsz_Left, wmsz_BottomLeft: R.Left := R.Right - D; + else R.Right := R.Left + D; + end; + D1 := MinY + (WR.Bottom - WR.Top) - (CR.Bottom - CR.Top); + if MaxY = 0 then D2 := MaxInt else D2 := MaxY + (WR.Bottom - WR.Top) - (CR.Bottom - CR.Top); + D := R.Bottom - R.Top; + if D < D1 then D := D1 else if D > D2 then D := D2; + case Mode of + wmsz_TopRight, wmsz_Top, wmsz_TopLeft: R.Top := R.Bottom - D; + else R.Bottom := R.Top + D; + end; +end; + +procedure CreateAnyWindow (var Window: TWindow; ClassName, Name: string; X, Y, sizeX, sizeY: integer; Parent: HWND; Style: cardinal; + aTabOrder: boolean = False; aEvent: TPProc = nil; dEvent : pointer = nil); +var + ExStyle: DWORD; +begin + with Window do begin + ID := LastId; + Inc(LastID); + + if Style and WS_BORDER <> 0 then begin + ExStyle := WS_EX_CLIENTEDGE; + Style := Style and not WS_BORDER; + end else ExStyle := 0; + + if aTabOrder then Style := Style or WS_TABSTOP; + + Handle := CreateWindowExW(ExStyle, PWideChar(ToWide(ClassName)), PWideChar(ToWide(Name)), Style, + X, Y, sizeX, sizeY, + Parent, ID, hInstance, nil); + + SetCaption(Handle, Name); + Wnds[ID] := @Window; + //DC := GetDC(Handle); + //DB(DC); + SendMessage(Handle, wm_SetFont, WParam(Font), 0); + MinID := 0; + MaxID := 0; + Tag := aTag; + Event := ToEvent(aEvent, dEvent); + if aTaborder then begin + TabOrder := LastTab; + Inc(LastTab); + if (TabOrder >= 0) and (TabOrder < $10000) then TabList[TabOrder] := @Window; + end else TabOrder := -10 + end; +end; + +procedure MoveRadioGroup(var Window : TRadioGroup; x,y,sizex,sizey: integer); +var + i:integer; + sy : integer; +begin + sy := (sizey-2) div Length(Window); + for i := 0 to Length(Window) - 1 do with Window[i] do begin + if i = 0 then begin + if Window[i].Handle <> 0 then MoveWindow(Window[i].Handle, x,y, sizex,sizey, true) + end else MoveWindow(Window[i].Handle, x+2, y + sy*i, sizex - 4, sy, true); + end; +end; + +procedure CreateRadioGroup; +var + i: integer; + sY: integer; + Min, Max: integer; +begin + SetLength(Window, Length(Names)); + sY := (sizeY - 2) div Length(Names); + Min := LastId + 1; + Max := LastId + Length(Window) - 1; + for i := 0 to Length(Window) - 1 do with Window[i] do begin + if i = 0 then begin + if Names[i] = '' then Window[i].Handle := 0 + else CreateAnyWindow(Window[i], 'button', Names[i], + X, Y, sizeX, sizeY, Parent, ws_Visible or ws_Child or bs_GroupBox) + end else CreateAnyWindow(Window[i], 'button', Names[i], + X + 2, Y + sY * i, sizeX - 4, sY, Parent, ws_Visible or ws_Child or bs_AutoRadioButton or (ws_Group * integer(i = 1))); + Window[i].MinID := Min; + Window[i].MaxID := Max; + if i = 0 then begin + Tag := aTag; + if aTaborder then begin + TabOrder := LastTab; + Inc(LastTab); + if (TabOrder >= 0) and (TabOrder < $10000) then TabList[TabOrder] := @Window[0]; + end else TabOrder := -10; + end; + end; +end; + +function GetClassName(H: HWND): string; +var + i: integer; +begin + SetLength(Result, 255); + if Windows.GetClassName(H, @Result[1], Length(Result)) = 0 then Result := '' else begin + i := 0; while Result[i+1]<>#0 do Inc(i); + SetLength(Result, i); + end; +end; + +procedure Click; +begin + if Wnds[ID] <> nil then with Wnds[ID]^ do begin + if @Event.p <> nil then begin + if (GetClassName(Handle)='ComboBox') and (HiWP <> CBN_SELCHANGE) then Exit; + Event.p(Event.d); + end; + end; +end; + +procedure CheckControl; +begin + if Wnds[ID] <> nil then with Wnds[ID]^ do begin + SetFocus(Handle); + if MinID > 0 then CheckRadioButton(H, MinID, MaxID, ID); + if (GetClassName(Handle) = 'Button') and (GetWindowLong(Handle, GWL_STYLE) and BS_AUTOCHECKBOX <> 0) then begin + SendMessage(Handle, WM_LBUTTONDOWN, 0, 0); + SendMessage(Handle, WM_LBUTTONUP , 0, 0); + end else if GetClassName(Handle) = 'ComboBox' then + else Click(H, ID, HiWP); + end; +end; + +procedure DoTab; // народ, как это нормально делается?! +var + i, N, sN: integer; + GF: hWnd; +begin + GF := GetFocusEx; + N := -1; + for i := 0 to Length(TabList) - 1 do if (TabList[i] <> nil) then begin + if TabList[i].Handle = GF then begin + N := i; + Break; + end; + end else Break; + sN := N; + repeat + Inc(N); + if (N > High(TabList)) or (TabList[N] = nil) then N := Low(TabList)-1; + if (N >= Low(TabList)) and (TabList[N] <> nil) and IsWindowVisible(TabList[N].Handle) and IsWindowEnabled(TabList[N].Handle) then begin + SetFocus(TabList[N].Handle); + if GetFocusEx = TabList[N].Handle then + Break; + end; + until N = sN; +end; + +function RadioGroupChoise; +var + i: integer; +begin + Result := 0; + for i := 1 to Length(Window) - 1 do if SendMessage(Window[i].Handle, bm_GetCheck, 0, 0) = 1 then begin + Result := i; + Break; + end; +end; + +procedure CreateMainMenu; +begin + with Window do begin + ID := LastId; + Inc(LastID); + Handle := CreateMenu; + Wnds[ID] := @Window; + //DC := 0; + SetMenu(Parent, Handle); + MinID := 0; + MaxID := 0; + end; +end; + +function Ins(Menu, SubMenu: HWND; Name: string; ID: uint; Radio: boolean): boolean; +var + MenuItem: MenuItemInfo; +begin + FillChar(MenuItem, SizeOf(MenuItem), 0); + with MenuItem do begin + cbSize := SizeOf(MenuItem); + fMask := miim_State or miim_Type or miim_SubMenu or miim_ID;// or MIIM_CHECKMARKS; + if Name = '-' then fType := mft_Separator + else if Radio then fType := mft_RadioCheck + else fType := mft_String; + fState := mfs_Enabled; + wID := ID; + hSubMenu := SubMenu; + dwItemData := 0; + dwTypeData := PAnsiChar(Name); + cch := Length(Name); + end; + Result := InsertMenuItem(Menu, 0, false, MenuItem); +end; + +procedure CreateSubMenu; +begin + with Window do begin + ID := LastId; + Inc(LastID); + Handle := CreatePopupMenu; + Wnds[ID] := @Window; + //DC := 0; + Ins(Parent, Handle, Name, ID, false); + MinID := 0; + MaxID := 0; + Event := noEvent; + end; +end; + +procedure CreateMenuItem(var Window: TWindow; Name: string; Parent: HWND; aEvent: TPProc = nil; dEvent : pointer = nil; Radio : boolean = false); +begin + with Window do begin + ID := LastId; + Inc(LastID); + Handle := 0; + Wnds[ID] := @Window; + //DC := 0; + Ins(Parent, 0, Name, ID, Radio); + MinID := 0; + MaxID := 0; + Event := ToEvent(aEvent, dEvent); + end; +end; + +procedure CreateBitmap; +var + BI: TBitmapInfo; + ScreenDC: HDC; +begin + FillChar(BI, SizeOf(BI), 0); + Bitmap.SizeX := SizeX; + Bitmap.SizeY := SizeY; + with BI.bmiHeader do begin + biSize := SizeOf(BI.bmiHeader); + biWidth := sizeX; + biHeight := sizeY; + biPlanes := 1; + biBitCount := Bits; + end; + ScreenDC := GetDC(0); + with Bitmap do begin + DC := CreateCompatibleDC(ScreenDC); + Handle := CreateDIBSection(DC, BI, DIB_RGB_COLORS, pointer(Mem), 0, 0); + SelectObject(DC, Handle); + SelectObject(DC, Font); + ReleaseDC(0, ScreenDC); + end; +end; + +function LoadBitmap; +var + BI: packed record + bmiHeader: TBitmapInfoHeader; + bmiColors: array[0 .. 255] of TRGBQuad; + end; + Header: TBitmapFileHeader; + ScreenDC: HDC; + F: TFileOfByte; +begin + Result := False; + IOResult; + Assign(F, FileName); + Reset(F); + if IOResult = 0 then begin + BlockRead(F, Header, SizeOf(Header)); + LoadStruct(F, BI.bmiHeader, SizeOf(BI.bmiHeader)); + Bitmap.SizeX := BI.bmiHeader.biWidth; + Bitmap.SizeY := BI.bmiHeader.biHeight; + if BI.bmiHeader.biBitCount <= 8 then BlockRead(F, BI.bmiColors, 1 shl (BI.bmiHeader.biBitCount + 2)); + ScreenDC := GetDC(0); + with Bitmap do begin + DC := CreateCompatibleDC(ScreenDC); + Handle := CreateDIBSection(DC, PBitmapInfo(@BI)^, DIB_RGB_COLORS, pointer(Mem), 0, 0); + Seek(F, Header.bfOffBits); + BlockRead(F, Mem^, BI.bmiHeader.biSizeImage); + SelectObject(DC, Handle); + ReleaseDC(0, ScreenDC); + end; + Close(F); + Result := True; + end; +end; + +procedure DeleteBitmap; +begin + with Bitmap do if Handle <> 0 then begin + DeleteDC(DC); + DeleteObject(Handle); + Handle := 0; + end; +end; + +procedure DeleteWindow; +begin + with Window do begin + //ReleaseDC(Handle, DC); + //DeleteDC(DC); + DestroyWindow(Handle); + end; +end; + +procedure ShowForm(var Window: TWindow; Maximized: boolean = false); +var + Tmp: PFormStack; +begin + if FormStack = nil then begin + MainForm := Window.Handle; + if Maximized then ShowWindow(Window.Handle, SW_SHOWMAXIMIZED) + else ShowWindow(Window.Handle, CMDSHOW); + UpdateWindow(Window.Handle); + end else begin + if Maximized then ShowWindow(Window.Handle, SW_SHOWMAXIMIZED) + else ShowWindow(Window.Handle, SW_NORMAL); + EnableWindow(FormStack.Handle, False); + end; + New(Tmp); + Tmp.Handle := Window.Handle; + Tmp.Accels := Window.Accels; + Tmp.Next := FormStack; + FormStack := Tmp; + MainForm := FormStack.Handle; +end; + +procedure ShowFormByPtr (p : pointer); +begin + ShowForm(PWindow(p)^); +end; + +procedure HideTopForm; +var + Tmp: PFormStack; +begin + if FormStack = nil then Exit; + Tmp := FormStack.Next; + if Tmp = nil then begin + // хз, надо выйти нафиг, наверное + end else begin + EnableWindow(Tmp.Handle, True); + BringWindowToTop(Tmp.Handle); + ShowWindow(FormStack.Handle, sw_Hide); + Dispose(FormStack); + FormStack := Tmp; + MainForm := FormStack.Handle; + end; +end; + +function MsgBox(hWnd: HWND; text, caption: string; uType: UINT): Integer; +begin + if flagWaitingMessageBox then begin + DebugBreak; + MessageBox(MainForm, 'Незакрытая рекурсия с диалоговыми окнами!', 'ERROR', mb_OK or mb_IconError); // рекурсия и хуй знает что делать + Halt; + end; + flagWaitingMessageBox := True; + Result := MessageBox (hWnd, PChar(text), PChar(caption), uType); + flagWaitingMessageBox := False; +end; + +procedure ShowError; +begin + MsgBox(MainForm, Text, 'ERROR', mb_OK or mb_IconError); +end; + +function WaitingMessageBox: boolean; +begin + result := flagWaitingMessageBox; +end; + +function GetCaption; +begin + SetLength(Result, SendMessage(H, wm_GetTextLength, 0, 0)); + if Result <> '' then SendMessage(H, wm_GetText, Length(Result)+1, LPARAM(Result)); +end; + +procedure SetCaption; +begin + SendMessage(H, WM_SETTEXT, 0, LPARAM(S)); +end; + +function ToAccels; +begin + Result := CreateAcceleratorTable(A[0], Length(A) div 3); +end; + +procedure LoadStruct; +var + sz: cardinal absolute V; +begin + BlockRead(F, sz, 4); + if sz = Size then begin + BlockRead(F, pointer(cardinal(@V) + 4)^, sz - 4); + end else if sz > Size then begin + BlockRead(F, pointer(cardinal(@V) + 4)^, Size - 4); + Seek(F, FilePos(F) + integer(sz - Size)); + end else begin + BlockRead(F, pointer(cardinal(@V) + 4)^, sz - 4); + FillChar(pointer(cardinal(@V) + sz)^, Size - sz, 0); + end; +end; + +procedure ChgFont; +begin + Font := CreateFont(-Size, 0, 0, 0, + 0, 0, 0, 0, RUSSIAN_CHARSET, OUT_DEFAULT_PRECIS, + CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH or FF_DONTCARE, + PChar(Name)); +end; + +function Exception(Code: PInt): TObject; +begin + ExceptionCode := Code^; + Result := TObject.Create; +end; + +procedure AddEsp16; +asm + ret 16 +end; + +procedure AssertProc (const Message, Filename: string; LineNumber: Integer; ErrorAddr: Pointer); +begin + ShowError('Вышибает на ассерте "' + Message + + '" в файле "' + Filename + + '" на линии ' + IntToStr(LineNumber) + + ' по адресу ' + IntToHex(Integer(ErrorAddr)) + '!'); + DebugBreak; +end; + +var + i: integer; + OldException: pointer; + +initialization + OldException := ExceptObjProc; + {$IFNDEF DEBUG} + ExceptObjProc := @Exception; + RaiseExceptionProc := @AddEsp16; + {$ENDIF} + MainForm := 0; + LastID := 1; + LastTab := 0; + // FormStack := nil; + MainPath := Paramstr(0); + i := Length(MainPath); + while (i > 0) and (MainPath[i] <> '\') do Dec(i); + SetLength(MainPath, i); + aTag := 0; + Font := GetStockObject(Default_Gui_Font); + + {$IFDEF DEBUG} + AssertErrorProc := AssertProc; + {$ENDIF} + +finalization + ExceptObjProc := OldException; + +end. diff --git a/fxMath.pas b/fxMath.pas new file mode 100644 index 0000000..6151421 --- /dev/null +++ b/fxMath.pas @@ -0,0 +1,51 @@ +unit fxMath; + +interface + + const k16 = $10000; + function fxMul(a, b: integer): integer; + function fxDiv(a, b: integer): integer; + function mulDiv(a, b, c: integer): integer; + function bsf(a: cardinal): integer; + function bsr(a: cardinal): integer; + +implementation + + function fxMul(a, b: integer): integer; + asm + imul edx + test edx, edx + jns @ + add eax, $FFFF + jnb @ + inc edx + @: + shrd eax, edx, 16 + end; + + function fxDiv(a, b: integer): integer; + asm + mov ecx, edx + mov edx, eax + sar edx, 16 + sal eax, 16 + idiv ecx + end; + + function mulDiv(a, b, c: integer): integer; + asm + imul edx + idiv ecx + end; + + function bsf(a: cardinal): integer; + asm + bsf eax, eax + end; + + function bsr(a: cardinal): integer; + asm + bsr eax, eax + end; + +end.