]> 4ch.mooo.com Git - 16.git/blobdiff - 16/xw__/modex/THREED.ASM
refresh wwww
[16.git] / 16 / xw__ / modex / THREED.ASM
diff --git a/16/xw__/modex/THREED.ASM b/16/xw__/modex/THREED.ASM
new file mode 100755 (executable)
index 0000000..5ecd3ba
--- /dev/null
@@ -0,0 +1,872 @@
+COMMENT /\r
+        Fixed-point math functions and 3D transforms\r
+        Copyright (c) 1993,94 by Alessandro Scotti\r
+/\r
+WARN    PRO\r
+P386\r
+JUMPS\r
+LOCALS\r
+\r
+INCLUDE MATH.INC\r
+\r
+PUBLIC  tdFixedMul\r
+PUBLIC  tdGetNormal\r
+PUBLIC  tdRotate\r
+PUBLIC  tdGetSurfaceLight\r
+PUBLIC  tdSetLight\r
+PUBLIC  tdSetRotation\r
+PUBLIC  tdSetTranslation\r
+PUBLIC  tdTransform\r
+PUBLIC  tdTransformToImage\r
+PUBLIC  tdTransformLight\r
+PUBLIC  tdBackPlaneCull\r
+PUBLIC  tdSetPerspective\r
+\r
+;-----------------------------------------------------------\r
+;\r
+; Data segment\r
+;\r
+MATH_DATA       SEGMENT USE16 PARA PUBLIC 'DATA'\r
+                ASSUME ds:MATH_DATA\r
+\r
+INCLUDE         SINCOS.INC              ; Fixed 8:24 sin/cos table\r
+\r
+XRotation       TPOINT  <>              ; 3x3 rotation matrix\r
+YRotation       TPOINT  <>\r
+ZRotation       TPOINT  <>\r
+\r
+Translation     TPOINT  <>              ; Translation vector\r
+\r
+Light           TPOINT  <>              ; Light vector\r
+AmbientLight    DW      00              ; Ambient light\r
+\r
+XScale                  DD      10000h  ; Scaling factor for X coordinate\r
+YScale                  DD      10000h  ; Scaling factor for Y coordinate\r
+PerspectiveDistance     DD      20000000h\r
+\r
+MATH_DATA       ENDS\r
+\r
+;-----------------------------------------------------------\r
+;\r
+; Code segment\r
+;\r
+MATH_TEXT       SEGMENT USE16 PARA PUBLIC 'CODE'\r
+                ASSUME cs:MATH_TEXT, es:NOTHING, fs:NOTHING\r
+\r
+tdSetPerspective        PROC PASCAL FAR\r
+        ARG     Perspective:DWORD,      \\r
+                ScaleX:DWORD,           \\r
+                ScaleY:DWORD\r
+        USES    ds\r
+\r
+        mov     ax, SEG MATH_DATA\r
+        mov     ds, ax\r
+        ASSUME  ds:MATH_DATA\r
+\r
+        mov     eax, [Perspective]\r
+        mov     [PerspectiveDistance], eax\r
+        mov     eax, [ScaleX]\r
+        mov     [XScale], eax\r
+        mov     eax, [ScaleY]\r
+        mov     [YScale], eax\r
+\r
+        ret\r
+tdSetPerspective        ENDP\r
+\r
+\r
+;-----------------------------------------------------------\r
+;\r
+; Sets the rotation matrix.\r
+;\r
+; Input:\r
+;       RX      = X-axis rotation angle\r
+;       RY      = X-axis rotation angle\r
+;       RZ      = X-axis rotation angle\r
+; Output:\r
+;       none\r
+;\r
+tdSetRotation   PROC PASCAL FAR\r
+        ARG     RX:WORD,        \\r
+                RY:WORD,        \\r
+                RZ:WORD\r
+        USES    ds, si, di\r
+\r
+        mov     ax, SEG MATH_DATA\r
+        mov     ds, ax\r
+        ASSUME  ds:MATH_DATA\r
+\r
+        mov     bx, [RZ]\r
+        mov     si, [RY]\r
+        mov     di, [RX]\r
+        shl     bx, 2\r
+        shl     si, 2\r
+        shl     di, 2\r
+\r
+        push    ebp                     ; We use EBP as a scratch register\r
+\r
+; Set X rotation\r
+        mov     eax, tblCos[bx]\r
+        imul    tblCos[si]\r
+        mov     [XRotation.X], edx\r
+\r
+        mov     eax, tblSin[bx]\r
+        imul    tblCos[si]\r
+        mov     [XRotation.Y], edx\r
+\r
+        mov     eax, tblSin[si]\r
+        sar     eax, 8                  ; Convert fixed 8:24 to fixed 16:16\r
+        mov     [XRotation.Z], eax\r
+\r
+; Set Y rotation\r
+        mov     eax, tblCos[bx]\r
+        imul    tblSin[si]              ; EDX:EAX = fixed 16:48\r
+        shrd    eax, edx, 24            ; EAX = fixed 8:24\r
+        imul    tblSin[di]              ; EDX:EAX = fixed 16:48\r
+        mov     ebp, eax\r
+        mov     ecx, edx\r
+        mov     eax, tblSin[bx]\r
+        imul    tblCos[di]\r
+        add     eax, ebp\r
+        adc     edx, ecx                ; EDX:EAX = fixed 16:48\r
+        neg     edx\r
+        mov     [YRotation.X], edx\r
+\r
+        mov     eax, tblSin[bx]\r
+        imul    tblSin[si]\r
+        shrd    eax, edx, 24\r
+        imul    tblSin[di]\r
+        mov     ebp, eax\r
+        mov     ecx, edx\r
+        mov     eax, tblCos[bx]\r
+        imul    tblCos[di]\r
+        sub     eax, ebp\r
+        sbb     edx, ecx\r
+        mov     [YRotation.Y], edx\r
+\r
+        mov     eax, tblCos[si]\r
+        imul    tblSin[di]\r
+        mov     [YRotation.Z], edx\r
+\r
+; Set Z rotation\r
+        mov     eax, tblCos[bx]\r
+        imul    tblSin[si]\r
+        shrd    eax, edx, 24\r
+        imul    tblCos[di]\r
+        mov     ebp, eax\r
+        mov     ecx, edx\r
+        mov     eax, tblSin[bx]\r
+        imul    tblSin[di]\r
+        sub     eax, ebp\r
+        sbb     edx, ecx\r
+        mov     [ZRotation.X], edx\r
+\r
+        mov     eax, tblSin[bx]\r
+        imul    tblSin[si]\r
+        shrd    eax, edx, 24\r
+        imul    tblCos[di]\r
+        mov     ebp, eax\r
+        mov     ecx, edx\r
+        mov     eax, tblCos[bx]\r
+        imul    tblSin[di]\r
+        add     eax, ebp\r
+        add     edx, ecx\r
+        neg     edx\r
+        mov     [ZRotation.Y], edx\r
+\r
+        mov     eax, tblCos[si]\r
+        imul    tblCos[di]\r
+        mov     [ZRotation.Z], edx\r
+\r
+        pop     ebp                     ; Restore EBP\r
+\r
+        ret\r
+tdSetRotation   ENDP\r
+\r
+;-----------------------------------------------------------\r
+;\r
+; Sets the translation vector.\r
+;\r
+; Input:\r
+;       TV      = pointer to translation vector\r
+; Output:\r
+;       none\r
+;\r
+tdSetTranslation        PROC PASCAL FAR\r
+        ARG     TV:DWORD\r
+        USES    ds, es, di\r
+\r
+        mov     ax, SEG MATH_DATA\r
+        mov     ds, ax\r
+        ASSUME  ds:MATH_DATA\r
+\r
+        les     di, [TV]\r
+        mov     eax, es:[di].X\r
+        mov     [Translation.X], eax\r
+        mov     eax, es:[di].Y\r
+        mov     [Translation.Y], eax\r
+        mov     eax, es:[di].Z\r
+        mov     [Translation.Z], eax\r
+\r
+        ret\r
+tdSetTranslation        ENDP\r
+\r
+;-----------------------------------------------------------\r
+;\r
+; Transforms an array of TPOINT.\r
+;\r
+; Input:\r
+;       Source  = pointer to source array of TPOINT\r
+;       Dest    = pointer to destination array of TPOINT\r
+;       Count   = number of entries to transform\r
+; Output:\r
+;       none\r
+;\r
+tdTransform     PROC PASCAL FAR\r
+        ARG     Source:DWORD,   \\r
+                Dest:DWORD,     \\r
+                Count:WORD\r
+        LOCAL   Adjust:DWORD\r
+        USES    ds, si, es, di, fs\r
+\r
+        mov     ax, SEG MATH_DATA\r
+        mov     ds, ax\r
+        ASSUME  ds:MATH_DATA\r
+\r
+        lfs     si, [Source]\r
+        les     di, [Dest]\r
+\r
+        ALIGN   DWORD\r
+@@Loop:\r
+; Transform Z coordinate\r
+        mov     eax, fs:[si].X\r
+        imul    [ZRotation.X]\r
+        mov     ecx, eax\r
+        mov     ebx, edx\r
+        mov     eax, fs:[si].Y\r
+        imul    [ZRotation.Y]\r
+        add     ecx, eax\r
+        adc     ebx, edx\r
+        mov     eax, fs:[si].Z\r
+        imul    [ZRotation.Z]\r
+        add     eax, ecx\r
+        adc     edx, ebx\r
+        mov     ebx, eax\r
+        shrd    eax, edx, 16\r
+        add     eax, [Translation.Z]    ; EAX = new Z coord (fixed 16:16)\r
+        mov     es:[di].Z, eax\r
+; Get perspective factor\r
+        mov     ebx, [PerspectiveDistance]\r
+        sub     eax, ebx\r
+        neg     eax                     ; EAX = PD - Z\r
+        xor     edx, edx\r
+        shld    edx, eax, 16\r
+        shl     eax, 16\r
+        idiv    ebx                     ; EAX = fixed 16:16 result\r
+        mov     [Adjust], eax\r
+\r
+; Transform X coordinate\r
+        mov     eax, fs:[si].X\r
+        imul    [XRotation.X]\r
+        mov     ecx, eax\r
+        mov     ebx, edx\r
+        mov     eax, fs:[si].Y\r
+        imul    [XRotation.Y]\r
+        add     ecx, eax\r
+        adc     ebx, edx\r
+        mov     eax, fs:[si].Z\r
+        imul    [XRotation.Z]\r
+        add     eax, ecx\r
+        adc     edx, ebx\r
+        shrd    eax, edx, 16\r
+        add     eax, [Translation.X]\r
+        imul    [Adjust]\r
+        shrd    eax, edx, 16\r
+        mov     es:[di].X, eax\r
+\r
+; Transform Y coordinate\r
+        mov     eax, fs:[si].X\r
+        imul    [YRotation.X]\r
+        mov     ecx, eax\r
+        mov     ebx, edx\r
+        mov     eax, fs:[si].Y\r
+        imul    [YRotation.Y]\r
+        add     ecx, eax\r
+        adc     ebx, edx\r
+        mov     eax, fs:[si].Z\r
+        imul    [YRotation.Z]\r
+        add     eax, ecx\r
+        adc     edx, ebx\r
+        shrd    eax, edx, 16\r
+        add     eax, [Translation.Y]\r
+        imul    [Adjust]\r
+        shrd    eax, edx, 16\r
+        mov     es:[di].Y, eax\r
+\r
+        add     si, SIZE TPOINT\r
+        add     di, SIZE TPOINT\r
+        dec     [Count]\r
+        jnz     @@Loop\r
+\r
+        ret\r
+tdTransform     ENDP\r
+\r
+;-----------------------------------------------------------\r
+;\r
+; Transforms an array of TPOINT into an array of TIMAGEPOINT.\r
+;\r
+; Input:\r
+;       Source  = pointer to source array of TPOINT\r
+;       Dest    = pointer to destination array of TIMAGEPOINT\r
+;       Count   = number of entries to transform\r
+;       DeltaX  = translation distance for the X coordinate\r
+;       DeltaY  = translation distance for the Y coordinate\r
+; Output:\r
+;       the maximum Z value\r
+;\r
+tdTransformToImage      PROC PASCAL FAR\r
+        ARG     Source:DWORD,   \\r
+                Dest:DWORD,     \\r
+                Count:WORD,     \\r
+                DeltaX:WORD,    \\r
+                DeltaY:WORD\r
+        LOCAL   Adjust:DWORD,   \\r
+                Max:DWORD\r
+        USES    ds, si, es, di, fs\r
+\r
+        mov     ax, SEG MATH_DATA\r
+        mov     ds, ax\r
+        ASSUME  ds:MATH_DATA\r
+\r
+        lfs     si, [Source]\r
+        les     di, [Dest]\r
+        mov     [Max], 80000000h\r
+\r
+@@Loop:\r
+; Check max Z\r
+        mov     eax, fs:[si].Z\r
+        cmp     eax, [Max]\r
+        jle     @@1\r
+        mov     [Max], eax\r
+@@1:\r
+\r
+; Transform X coordinate\r
+        mov     ax, WORD PTR fs:[si].X[2]\r
+        add     ax, [DeltaX]\r
+        mov     es:[di].IX, ax\r
+\r
+; Transform Y coordinate\r
+        mov     ax, WORD PTR fs:[si].Y[2]\r
+        add     ax, [DeltaY]\r
+        mov     es:[di].IY, ax\r
+\r
+        add     si, SIZE TPOINT\r
+        add     di, SIZE TIMAGEPOINT\r
+        dec     [Count]\r
+        jnz     @@Loop\r
+\r
+        mov     eax, [Max]\r
+        shld    edx, eax, 16\r
+        ret\r
+tdTransformToImage      ENDP\r
+\r
+;-----------------------------------------------------------\r
+;\r
+; Sets the light source.\r
+;\r
+; Input:\r
+;       Light   = pointer to light vector\r
+; Output:\r
+;       none\r
+;\r
+tdSetLight      PROC PASCAL FAR\r
+        ARG     L:DWORD\r
+        USES    ds, es, di\r
+\r
+        mov     ax, SEG MATH_DATA\r
+        mov     ds, ax\r
+        ASSUME  ds:MATH_DATA\r
+\r
+        les     di, [L]\r
+        mov     eax, es:[di].X\r
+        mov     [Light.X], eax\r
+        mov     eax, es:[di].Y\r
+        mov     [Light.Y], eax\r
+        mov     eax, es:[di].Z\r
+        mov     [Light.Z], eax\r
+\r
+        ret\r
+tdSetLight      ENDP\r
+\r
+;-----------------------------------------------------------\r
+;\r
+; Computes light intensity for an array of surfaces.\r
+;\r
+; Input:\r
+;       Normals = pointer to an array of surface normals\r
+;       Lights  = pointer to an array of integer to be filled with\r
+;                 light intensity\r
+;       Count   = number of elements to transform\r
+; Output:\r
+;       none\r
+;\r
+tdTransformLight        PROC PASCAL FAR\r
+        ARG     Normals:DWORD,  \\r
+                Lights:DWORD,   \\r
+                Count:WORD\r
+        USES    ds, si, es, di, fs\r
+\r
+        mov     ax, SEG MATH_DATA\r
+        mov     fs, ax\r
+        ASSUME  fs:MATH_DATA\r
+\r
+        lds     si, [Normals]\r
+        les     di, [Lights]\r
+        ASSUME  ds:NOTHING\r
+\r
+; Intensity is given by the dot product between the Light vector and\r
+; the surface normal\r
+@@Loop:\r
+        mov     eax, ds:[si].Z\r
+        imul    [Light.Z]\r
+        mov     ebx, eax\r
+        mov     ecx, edx\r
+        mov     eax, ds:[si].Y\r
+        imul    [Light.Y]\r
+        add     ebx, eax\r
+        adc     ecx, edx\r
+        mov     eax, ds:[si].X\r
+        imul    [Light.X]\r
+        add     eax, ebx\r
+        adc     edx, ecx                ; EDX:EAX = fixed 32:32 intensity\r
+        add     dx, [AmbientLight]\r
+        test    dx, dx\r
+        jg      @@1\r
+        xor     dx, dx                  ; Return 0 for no light\r
+@@1:\r
+        mov     es:[di], dx\r
+        inc     di\r
+        inc     di\r
+        add     si, SIZE TPOINT\r
+        dec     [Count]\r
+        jnz     @@Loop\r
+\r
+        ASSUME  fs:NOTHING\r
+        ret\r
+tdTransformLight        ENDP\r
+\r
+;-----------------------------------------------------------\r
+;\r
+; Returns the light value given the normal to a surface.\r
+;\r
+; Input:\r
+;       Normal  = pointer to TPOINT surface normal vector\r
+; Output:\r
+;       AX      = light intensity (>=0)\r
+; Notes:\r
+;       the normal is rotated according to the current setting.\r
+;\r
+tdGetSurfaceLight       PROC PASCAL FAR\r
+        ARG     Normal:DWORD\r
+        USES    ds, esi, es, di\r
+\r
+        mov     ax, SEG MATH_DATA\r
+        mov     ds, ax\r
+        ASSUME  ds:MATH_DATA\r
+\r
+        les     di, [Normal]\r
+\r
+; Transform Z coordinate\r
+        mov     eax, es:[di].X\r
+        imul    [ZRotation.X]\r
+        mov     ecx, eax\r
+        mov     ebx, edx\r
+        mov     eax, es:[di].Y\r
+        imul    [ZRotation.Y]\r
+        add     ecx, eax\r
+        adc     ebx, edx\r
+        mov     eax, es:[di].Z\r
+        imul    [ZRotation.Z]\r
+        add     eax, ecx\r
+        adc     edx, ebx\r
+        shrd    eax, edx, 16\r
+        imul    [Light.Z]\r
+        shrd    eax, edx, 16\r
+        mov     esi, eax\r
+\r
+; Transform X coordinate\r
+        mov     eax, es:[di].X\r
+        imul    [XRotation.X]\r
+        mov     ecx, eax\r
+        mov     ebx, edx\r
+        mov     eax, es:[di].Y\r
+        imul    [XRotation.Y]\r
+        add     ecx, eax\r
+        adc     ebx, edx\r
+        mov     eax, es:[di].Z\r
+        imul    [XRotation.Z]\r
+        add     eax, ecx\r
+        adc     edx, ebx\r
+        shrd    eax, edx, 16\r
+        imul    [Light.X]\r
+        shrd    eax, edx, 16\r
+        add     esi, eax\r
+\r
+; Transform Y coordinate\r
+        mov     eax, es:[di].X\r
+        imul    [YRotation.X]\r
+        mov     ecx, eax\r
+        mov     ebx, edx\r
+        mov     eax, es:[di].Y\r
+        imul    [YRotation.Y]\r
+        add     ecx, eax\r
+        adc     ebx, edx\r
+        mov     eax, es:[di].Z\r
+        imul    [YRotation.Z]\r
+        add     eax, ecx\r
+        adc     edx, ebx\r
+        shrd    eax, edx, 16\r
+        imul    [Light.X]\r
+        shrd    eax, edx, 16\r
+        add     eax, esi\r
+        shr     eax, 16\r
+\r
+; Add ambient light\r
+        add     ax, [AmbientLight]\r
+        test    ax, ax\r
+        jge     @@Exit\r
+        xor     ax, ax\r
+\r
+@@Exit:\r
+        ret\r
+tdGetSurfaceLight       ENDP\r
+\r
+;-----------------------------------------------------------\r
+;\r
+; Rotates an array of TPOINT.\r
+;\r
+; Input:\r
+;       Source  = pointer to source array of TPOINT\r
+;       Dest    = pointer to destination array of TPOINT\r
+;       Count   = number of entries to transform\r
+; Output:\r
+;       none\r
+;\r
+tdRotate        PROC PASCAL FAR\r
+        ARG     Source:DWORD,   \\r
+                Dest:DWORD,     \\r
+                Count:WORD\r
+        USES    ds, si, es, di, fs\r
+\r
+        mov     ax, SEG MATH_DATA\r
+        mov     ds, ax\r
+        ASSUME  ds:MATH_DATA\r
+\r
+        lfs     si, [Source]\r
+        les     di, [Dest]\r
+\r
+@@Loop:\r
+; Transform Z coordinate\r
+        mov     eax, fs:[si].X\r
+        imul    [ZRotation.X]\r
+        mov     ecx, eax\r
+        mov     ebx, edx\r
+        mov     eax, fs:[si].Y\r
+        imul    [ZRotation.Y]\r
+        add     ecx, eax\r
+        adc     ebx, edx\r
+        mov     eax, fs:[si].Z\r
+        imul    [ZRotation.Z]\r
+        add     eax, ecx\r
+        adc     edx, ebx\r
+        shrd    eax, edx, 16\r
+        mov     es:[di].Z, eax\r
+\r
+; Transform X coordinate\r
+        mov     eax, fs:[si].X\r
+        imul    [XRotation.X]\r
+        mov     ecx, eax\r
+        mov     ebx, edx\r
+        mov     eax, fs:[si].Y\r
+        imul    [XRotation.Y]\r
+        add     ecx, eax\r
+        adc     ebx, edx\r
+        mov     eax, fs:[si].Z\r
+        imul    [XRotation.Z]\r
+        add     eax, ecx\r
+        adc     edx, ebx\r
+        shrd    eax, edx, 16\r
+        mov     es:[di].X, eax\r
+\r
+; Transform Y coordinate\r
+        mov     eax, fs:[si].X\r
+        imul    [YRotation.X]\r
+        mov     ecx, eax\r
+        mov     ebx, edx\r
+        mov     eax, fs:[si].Y\r
+        imul    [YRotation.Y]\r
+        add     ecx, eax\r
+        adc     ebx, edx\r
+        mov     eax, fs:[si].Z\r
+        imul    [YRotation.Z]\r
+        add     eax, ecx\r
+        adc     edx, ebx\r
+        shrd    eax, edx, 16\r
+        mov     es:[di].Y, eax\r
+\r
+        add     si, SIZE TPOINT\r
+        add     di, SIZE TPOINT\r
+        dec     [Count]\r
+        jnz     @@Loop\r
+\r
+        ret\r
+tdRotate        ENDP\r
+\r
+tdFixedMul      PROC PASCAL FAR\r
+        ARG     F1:DWORD,       \\r
+                F2:DWORD\r
+\r
+        mov     eax, [F1]\r
+        imul    [F2]\r
+        shr     eax, 16\r
+\r
+        ret\r
+tdFixedMul      ENDP\r
+\r
+;-----------------------------------------------------------\r
+;\r
+; Returns in EAX the square root of EDX:EAX.\r
+;\r
+subSqrt PROC NEAR\r
+        push    esi\r
+        push    edi\r
+\r
+        add     eax, eax\r
+        adc     edx, 0\r
+        mov     eax, edx                ; Just discard the low bits\r
+\r
+        mov     esi, eax\r
+        xor     edi, edi\r
+        shld    edi, esi, 16\r
+        shl     esi, 16\r
+@@Loop:\r
+        mov     ebx, eax\r
+        mul     eax\r
+        add     eax, esi\r
+        adc     edx, edi\r
+        shrd    eax, edx, 1\r
+        shr     edx, 1\r
+        div     ebx\r
+        cmp     eax, ebx\r
+        jne     @@Loop\r
+\r
+; Adjust EAX\r
+        shl     eax, 8\r
+\r
+        pop     edi\r
+        pop     esi\r
+        ret\r
+subSqrt ENDP\r
+\r
+;-----------------------------------------------------------\r
+;\r
+; Finds the unitary normal to a given surface.\r
+;\r
+; Input:\r
+;       Dest            = pointer to TPOINT (vector) result\r
+;       P1, P2, P3      = pointer to TPOINT points on surface\r
+; Output:\r
+;       none\r
+; Notes:\r
+;       the normal is given by the cross-product between (P3-P1) and\r
+;       (P2-P1), so its orientation depends on the parameters order.\r
+;\r
+tdGetNormal     PROC PASCAL FAR\r
+        ARG     Dest:DWORD,     \\r
+                P1:DWORD,       \\r
+                P2:DWORD,       \\r
+                P3:DWORD\r
+        LOCAL   V1:TPOINT,      \\r
+                V2:TPOINT,      \\r
+                N:TPOINT\r
+        USES    ds, si, es, di\r
+\r
+; Get vector V1\r
+        lds     si, [P1]\r
+        les     di, [P3]\r
+        mov     eax, es:[di].X\r
+        sub     eax, ds:[si].X\r
+        mov     [V1.X], eax\r
+        mov     eax, es:[di].Y\r
+        sub     eax, ds:[si].Y\r
+        mov     [V1.Y], eax\r
+        mov     eax, es:[di].Z\r
+        sub     eax, ds:[si].Z\r
+        mov     [V1.Z], eax\r
+\r
+; Get vector V2\r
+        les     di, [P2]\r
+        mov     eax, es:[di].X\r
+        sub     eax, ds:[si].X\r
+        mov     [V2.X], eax\r
+        mov     eax, es:[di].Y\r
+        sub     eax, ds:[si].Y\r
+        mov     [V2.Y], eax\r
+        mov     eax, es:[di].Z\r
+        sub     eax, ds:[si].Z\r
+        mov     [V2.Z], eax\r
+\r
+; Get normal vector (V1 x V2)\r
+        mov     eax, [V1.Z]\r
+        imul    [V2.Y]\r
+        mov     ebx, eax\r
+        mov     ecx, edx\r
+        mov     eax, [V1.Y]\r
+        imul    [V2.Z]\r
+        sub     eax, ebx\r
+        sbb     edx, ecx\r
+        shrd    eax, edx, 16\r
+        mov     [N.X], eax\r
+\r
+        mov     eax, [V1.X]\r
+        imul    [V2.Z]\r
+        mov     ebx, eax\r
+        mov     ecx, edx\r
+        mov     eax, [V1.Z]\r
+        imul    [V2.X]\r
+        sub     eax, ebx\r
+        sbb     edx, ecx\r
+        shrd    eax, edx, 16\r
+        mov     [N.Y], eax\r
+\r
+        mov     eax, [V1.Y]\r
+        imul    [V2.X]\r
+        mov     ebx, eax\r
+        mov     ecx, edx\r
+        mov     eax, [V1.X]\r
+        imul    [V2.Y]\r
+        sub     eax, ebx\r
+        sbb     edx, ecx\r
+        shrd    eax, edx, 16\r
+        mov     [N.Z], eax\r
+\r
+; Get normal length\r
+        mov     eax, [N.X]\r
+        imul    eax\r
+        mov     ebx, eax\r
+        mov     ecx, edx\r
+        mov     eax, [N.Y]\r
+        imul    eax\r
+        add     ebx, eax\r
+        adc     ecx, edx\r
+        mov     eax, [N.Z]\r
+        imul    eax\r
+        add     eax, ebx\r
+        adc     edx, ecx                ; EDX:EAX = N.X*N.X + N.Y*N.Y + N.Z*N.Z\r
+        call    subSqrt                 ; EAX = normal length\r
+        mov     ebx, eax\r
+\r
+; Adjust vector and save it\r
+        les     di, [Dest]\r
+        mov     eax, [N.X]\r
+        cdq\r
+        shld    edx, eax, 16\r
+        shl     eax, 16\r
+        idiv    ebx\r
+        mov     es:[di].X, eax\r
+        mov     eax, [N.Y]\r
+        cdq\r
+        shld    edx, eax, 16\r
+        shl     eax, 16\r
+        idiv    ebx\r
+        mov     es:[di].Y, eax\r
+        mov     eax, [N.Z]\r
+        cdq\r
+        shld    edx, eax, 16\r
+        shl     eax, 16\r
+        idiv    ebx\r
+        mov     es:[di].Z, eax\r
+\r
+        ret\r
+tdGetNormal     ENDP\r
+\r
+TPOLY   STRUC\r
+        Vtx     DW      4 DUP(?)\r
+TPOLY   ENDS\r
+\r
+;-----------------------------------------------------------\r
+;\r
+; Performs surface removal on an array of polygons.\r
+;\r
+; Input:\r
+;       Poly    = pointer to an array of TPOLY\r
+;       Vertex  = pointer to an array of TPOINT\r
+;       Dest    = pointer to an array of integer\r
+;       Count   = number of polygons to check\r
+;       Step    = size of TPOLY structure\r
+; Output:\r
+;       if the n-th polygon is invisible the n-th entry of the\r
+;       Dest array is set to -1, other entries are not modified\r
+;       (so it's possible to use the Light array for Dest, because\r
+;       the light intensity is always >= 0)\r
+;\r
+tdBackPlaneCull PROC PASCAL FAR\r
+        ARG     Step:WORD,      \\r
+                Poly:DWORD,     \\r
+                Vertex:DWORD,   \\r
+                Dest:DWORD,     \\r
+                Count:WORD\r
+        USES    ds, si, es, di, fs\r
+        ASSUME  ds:NOTHING\r
+\r
+        mov     ds, WORD PTR Vertex[2]\r
+        les     di, [Poly]\r
+        mov     fs, WORD PTR Dest[2]\r
+\r
+@@Loop:\r
+        mov     ax, es:[di].Vtx[2]      ; Index of 2nd vertex\r
+        shl     ax, 2\r
+        mov     bx, ax\r
+        shl     ax, 1\r
+        add     bx, ax                  ; BX = index*SIZE TPOINT\r
+        add     bx, WORD PTR [Vertex]   ; BX = offset of 2nd vertex\r
+        mov     ax, es:[di].Vtx[4]      ; Index of 3rd vertex\r
+        shl     ax, 2\r
+        mov     si, ax\r
+        shl     ax, 1\r
+        add     si, ax\r
+        add     si, WORD PTR [Vertex]   ; SI = offset of 3rd vertex\r
+        mov     ecx, ds:[si].X\r
+        sub     ecx, ds:[bx].X          ; ECX = V3.X-V2.X\r
+        mov     edx, ds:[si].Y\r
+        sub     edx, ds:[bx].Y          ; EDX = V3.Y-V2.Y\r
+        mov     ax, es:[di].Vtx[0]      ; Index of 1st vertex\r
+        shl     ax, 2\r
+        mov     si, ax\r
+        shl     ax, 1\r
+        add     si, ax\r
+        add     si, WORD PTR [Vertex]   ; SI = offset of 1st vertex\r
+        mov     eax, ds:[si].X\r
+        sub     eax, ds:[bx].X          ; EAX = V1.X-V2.X\r
+        mov     esi, ds:[si].Y\r
+        sub     esi, ds:[bx].Y          ; ESI = V1.Y-V2.Y\r
+        imul    edx\r
+        mov     ebx, eax\r
+        xchg    ecx, edx                ; ECX:EBX = (V1.X-V2.X)*(V3.Y-V2.Y)\r
+        mov     eax, esi\r
+        imul    edx                     ; EDX:EAX = (V1.Y-V2.Y)*(V3.X-V2.X)\r
+        sub     eax, ebx\r
+        sbb     edx, ecx\r
+        jl      @@Next                  ; Polygon is visible\r
+        mov     bx, WORD PTR [Dest]     ; FS:BX -> current Dest entry\r
+        mov     WORD PTR fs:[bx], -1    ; Remove polygon\r
+@@Next:\r
+        add     WORD PTR [Dest], 2      ; Next entry for dest\r
+        add     di, [Step]              ; Next polygon\r
+        dec     [Count]\r
+        jnz     @@Loop\r
+\r
+        ret\r
+tdBackPlaneCull ENDP\r
+\r
+MATH_TEXT       ENDS\r
+END\r