;***********************************************************************************************************
; Урок номер 3. Выбор режима работы приложения посредством диалога
;               Работа с полигонами. Простая вертексная анимация.
; Разработал Пономарев Михаил ака keYMax
; По всем вопросам мыльте mybox@aib.ru 
;***********************************************************************************************************
   .686                  ; Распознавать при компиляции команды процессора 686
   .MMX                  ; Распознавать при компиляции MMX инструкции
   .XMM                  ; Распознавать при компиляции SSE инструкции
	
   .MODEL FLAT, STDCALL  ; Модель памяти плоская как в Win32
                         ; STDCALL - метод вызова через стек

   OPTION CASEMAP:none   ; Эта строка указывает компилятору что надо различать
                         ; заглавные и прописные буквы. ;)
					
;===========================================================================================================
; Включаем прототипы функций которые мы используем в нашей программе 
;===========================================================================================================
   INCLUDE       \masm32\include\windows.inc     ; Структуры, константы ...    
   INCLUDE       \masm32\include\kernel32.inc    ; Системные функции приложения ...
   INCLUDE       \masm32\include\user32.inc      ; Интерфейс ...
   INCLUDE       \masm32\include\gdi32.inc       ; Графический вывод ...
             
   INCLUDE       include\d3d8.inc                ; Главный заголовочный файл Direct3D8
   INCLUDE       include\d3dx8math.inc           ; Работа с матрицами
                
   INCLUDELIB    \masm32\lib\kernel32.lib
   INCLUDELIB    \masm32\lib\user32.lib
   INCLUDELIB    \masm32\lib\gdi32.lib

   INCLUDELIB    include\d3d8.lib                ; Главная библиотека Direct3D8
   INCLUDELIB    include\d3dx8d.lib

;ПРОТОТИПЫ ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

   Init_Direct3D         PROTO                               ; Текст этих функций можно найти ниже
   Destroy_Direct3D      PROTO                               ; 
   Set_Render_Parameters PROTO                               ;
   
   Init_Scene            PROTO :DWORD                        ; 
   Render_Scene          PROTO                               ;
   Animate_Scene         PROTO                               ;    
    
   SetupDialog           PROTO :DWORD,:DWORD,:DWORD,:DWORD   ; Прототипы функций из файла Dialog.asm
   DW2A                  PROTO :DWORD,:DWORD
        
.CONST ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    
.DATA ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    szClassName    db  "MyClass", 0                          ; Имя класса нашего окна
    szAppName      db  "Direct X 8.1 в MASM32: Урок 4 !", 0  ; Заголовок нашего окна ( то что будет в заголовке и на панели задач )
    szDialogName   db  "SetupDialog", 0
       
   ;WNDCLASSEX ------------------------------------------------ 
    cbSize         dd  12*4                       ; Размер структуры
    style          dd  CS_HREDRAW or CS_VREDRAW   ; Стиль нашего окна
    lpfnWndProc    dd  OFFSET WndProc             ; Адрес процедуры обработки событий
    cbClsExtra     dd  0                          ; Эта хрень нам не нужна
    cbWndExtra     dd  0                          ; и эта тоже
    hInstance      dd  400000h                    ; Адрес нашей проги в памяти
    hIcon          dd  0                          ; Иконка окна по умолчанию ( большая )
    hCursor        dd  65553                      ; Курсор окна по умолчанию ( здесь я указал ID обычной стрелки )
    hbrBackground  dd  BLACK_PEN                  ; Фон нашего окна черный
    lpszMenuName   dd  0                          ; Меню у нас отсутствует
    lpszClassName  dd  OFFSET szClassName         ; Указатель на имя нашего класса
    hIconSm        dd  0                          ; Иконка окна по умолчанию ( маленькая - отображается в заголовке )
                         
.DATA? ;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    msg            MSG  <?>                       ; Структура сообщения
    hwnd           HWND ?                         ; ID нашего окна    
        
.CODE ;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

Start:                                                        ; Точка входа в нашу программу                 
   invoke  LoadCursor, NULL, IDC_ARROW			
   mov     hCursor, eax
		
   invoke  DialogBoxParam, hInstance , ADDR szDialogName , \  ; Вызываем наш диалог
           NULL , OFFSET SetupDialog, NULL 
    
   invoke  ShowWindow, hwnd, SW_NORMAL                        ; Показывем окно
   invoke  UpdateWindow, hwnd                                 ; Обновляем его
   invoke  SetFocus, hwnd                                     ; Устанавливаем на него фокус

   invoke  Init_Direct3D                                      ; Инициализируем Direct3D
           
 msg_loop:
   invoke  PeekMessageA, ADDR msg, 0 , 0 , 0 , PM_NOREMOVE	  ;	
   test    eax, eax                                           ;
   jne     message                                            ; Если у окна нет сообщений... 

   invoke  Render_Scene                                       ; ... то мы рисуем нашу сцену
   jmp     msg_loop                                           ;
   
 message:                                                     ;
   invoke  GetMessage, ADDR msg , NULL , 0 ,  0 
   test    eax, eax				
   je      end_loop

   invoke  TranslateMessage, ADDR msg     												
   invoke  DispatchMessage, ADDR msg      
   jmp     msg_loop

 end_loop:
   mov     eax, msg.wParam            ; Если цикл обработки сообщений прерван, то код выхода из программы
                                      ; передается в поле wParam сообщения MSG. 
   invoke  ExitProcess, eax
   ret
;===========================================================================================================                                     
 WndProc proc hWnd:HWND , uMsg:UINT, wParam:WPARAM, lParam:LPARAM                                          
;=========================================================
   
   cmp     uMsg, WM_KEYDOWN           ; Это сообщение посылается окну, после того как нажимают кнопку на клавиатуре.
   je      wmKeydown
                   
   cmp     uMsg, WM_CREATE            ; Это сообщение посылается окну, когда оно создается
   je    wmCreate
            
   cmp     uMsg, WM_DESTROY           ; Это сообщение посылается окну, когда оно должно быть уничтожено.
   je    wmDestroy	
         			                 
   invoke  DefWindowProc, hWnd, uMsg, wParam, lParam  ; Если Мы НЕ ХОТИМ обрабатывать полученное сообщение, то
   ret                                                ; нам надо вызвать DefWindowProc  с параметрами, которые Мы получили.
                                                      ; Эта функция API обрабатывает не интересующие Нас события.
;-----------------------------------------------------------------------------------------------------------------------------------------                                                               
  wmDestroy:
;------------    
   invoke  Destroy_Direct3D                           ; Уничтожаем все что мы создали для работы с DirectX

   invoke  PostQuitMessage, NULL     
   xor     eax,eax                            
   ret                                         
;-----------------------------------------------------------------------------------------------------------------------------------------                                                               
  wmCreate:                                       
;-----------    
   xor     eax, eax 
   ret 
;-----------------------------------------------------------------------------------------------------------------------------------------    
  wmKeydown:
;-----------
   mov     eax, wParam
   cmp     eax, 27                                    ; 27 - код клавиши ESC
   jne     noExit
   invoke  SendMessage, hWnd , WM_CLOSE , NULL, NULL  ; Посылаем сообщение о закрытии самим себе 
   noExit:
   xor     eax,eax
   ret     
       
 WndProc endp
;===========================================================================================================
; Начинаются всякие вспомогательные процедуры
;===========================================================================================================

INCLUDE  Dialog.asm  ; Подключаем файл содержащий текст функций SetupDialog и DW2A
;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Init_Scene proc pd3dDev:DWORD
      
  .CONST
    
   D3DFVF_CUSTOMVERTEX equ D3DFVF_XYZ OR D3DFVF_DIFFUSE   ; вершина будет содержать координаты и цвет
   
  .DATA                                                  
                                                         
   xplus           dd 0.105f                              ; Начальная позиция по X                             
   yplus           dd 0.0f                                ; Начальная позиция по Y
   shiftx          dd 0.105f                              ; Добавка по координате X
   shifty          dd -0.105f                             ; Добавка по координате Y
   
   Text            dd 10001001100011001000100011001001b   ; Всего 6*4 байт 
                   dd 10001010010100001101100010101001b   ; значение бита используется как закрашивать или нет
                   dd 10101010010011001010100010101001b   
                   dd 10101010010000101000100011001001b
                   dd 10101011110100101000100010101001b                   
                   dd 01010010010011001000101010100110b                    
                   
   ;CountVertex     dd 24*360                             ; Число вершин
   CountData       dd (24*4)*360+24*4                     ; Всего данных в байтах
   CountPrimitive  dd 360*2+2                             ; Число треугольников
   
   Vertices        dd 0.0f ,  0.0f , 0.0f , 0d1d0e7h      ; Исходные данные ( образец для клонирования )
                   dd 0.0f ,  0.1f , 0.0f , 0d1d0e7h      ; 4*4 байт на точку
                   dd 0.1f , 0.1f , 0.0f , 0d1d0e7h       ; 12*4 байт на полигон
                                                          ; 24*4 байт на квадрат
                   dd 0.0f , 0.0f , 0.0f , 0d1d0e7h
                   dd 0.1f ,  0.1f , 0.0f , 0d1d0e7h
                   dd 0.1f , 0.0f , 0.0f , 0d1d0e7h
   
   Fon             dd -1.0f ,  -6.0f , 1.5f , 0f1f0e7h    ; Фон - 2 полигона с цветом
                   dd -1.0f ,  2.0f , 1.5f , 0b1b0a7h     ; для градиентной заливки
                   dd 9.0f , 2.0f , 1.5f , 0f1f0e7h 

                   dd -1.0f , -6.0f , 1.5f , 0f1f0e7h 
                   dd 9.0f ,  2.0f , 1.5f , 0f1f0e7h 
                   dd 9.0f , -6.0f , 1.5f , 0FFFFFFh
  .DATA?
   pd3dVertexBuffer  dd  ?                                ; Указатель на VertexBuffer
   pLockBuffer       dd  ?                                ; Указатель на блокированный Vertex буфер
          
  .CODE               
      
   d3dev8  CreateVertexBuffer, pd3dDev, CountData, D3DUSAGE_WRITEONLY, D3DFVF_CUSTOMVERTEX , \
           D3DPOOL_MANAGED, ADDR pd3dVertexBuffer
            
   d3dvb8  Lock1, pd3dVertexBuffer, NULL, NULL, ADDR pLockBuffer, NULL
   
   lea     esi, Fon                             ; Копируем в буфер 2 полигона фона
   mov     edi, pLockBuffer           
   mov     ecx, 24
   rep     movsd   

   push    edi
   mov     esi, edi                             ; Клонируем квадрат в количестве 36 по горизонтали                    
   push    10                                   ; и 10 по вертикали
   pop     ecx                                  ; В итоге получится сетка 36*10 квадратов
   nextVertices3:                               ; В каждом квадрате по 2 полигона
   xor     ebx, ebx     
   push    ecx
   push    36
   pop     ecx
   nextVertices2:
   push    ecx
   lea     edi, Vertices
   push    6
   pop     ecx
   nextVertex:
   fld     DWORD PTR [edi]
   fadd    xplus
   fstp    DWORD PTR [esi]  
   fld     DWORD PTR [edi+4]
   fadd    yplus
   fstp    DWORD PTR [esi+4]
   push    DWORD PTR [edi+8]
   pop     DWORD PTR [esi+8]
   mov     eax, DWORD PTR [edi+12]   
   sub     eax, ebx
   mov     DWORD PTR [esi+12], eax     
   add     esi, 16
   add     edi, 16
   loop    nextVertex
   add     ebx, 00030303h
   fld     xplus
   fadd    shiftx
   fstp    xplus
   pop     ecx
   loop    nextVertices2
   push    shiftx
   pop     xplus
   fld     yplus
   fadd    shifty
   fstp    yplus   
   pop     ecx
   dec     ecx
   jnz     nextVertices3
   
   pop     esi                              ; Рисуем надпись
   add     esi, 96*74
   lea     edi, Text
   push    6
   pop     ecx
   nextVert2:
   mov     ebx, 00FF0000h
   push    ecx
   mov     eax, [edi]
   push    32
   pop     ecx
   nextVert:
   shl     eax, 1 
   jnc     noMovColor
   mov     DWORD PTR [esi+12], ebx
   mov     DWORD PTR [esi+28], ebx
   mov     DWORD PTR [esi+44], ebx
   mov     DWORD PTR [esi+60], ebx
   mov     DWORD PTR [esi+76], ebx 
   mov     DWORD PTR [esi+92], ebx
   noMovColor:
   add     esi, 96
   sub     ebx, 00040000h
   loop    nextVert
   add     esi, 96*4
   add     edi, 4
   pop     ecx
   loop    nextVert2
       
   d3dvb8  Unlock, pd3dVertexBuffer
    
   ret
Init_Scene endp
;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Init_Direct3D proc    ; Создавать Direct3DDevice в сообщении WM_Create нельзя ! ( проверено опытным путем :) )    			

  .DATA                
   ;------------------------------------------- Параметры камеры
   
   EyeVector    D3DVECTOR  <0.0f, 0.0f, -2.6f>  ; Координаты камеры в пространстве
   LookAtVector D3DVECTOR  <3.0f, -1.0f, 3.0f>  ; Указывает куда смотрит камера
   UpVector     D3DVECTOR  <0.0f, 2.0f, -0.8f>  ; Верх камеры. Обычно берется 0.0,1.0,0.0
   
   FieldOfView    dd  0.7853981635f             ; Поле обзора камеры в радианах обычно берется (D3DX_PI/4)
   AspectRatio    dd  NULL                      ; AspectRatio
   NearViewPlanZ  dd  1.0f                      ; Передняя отсекающая плоскость
   FarViewPlanZ   dd  10.0f                     ; Задняя отсекающая плоскость
         
  .DATA?
   pd3dDevice        dd  ?                      ; Указатель на Direct3DDevice
   
   WorldMatrix       D3DMATRIX  <?>             ; Мировая матрица
   ViewMatrix        D3DMATRIX  <?>             ; Матрица вида 
   ProjectionMatrix  D3DMATRIX  <?>             ; Матрица проекции
   
  .CODE    ; До вызова этого метода у нас уже будет создан Direct3D в SetupDialog  
           
   d3d8    CreateDevice, pd3d, D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd , \  ; Создаем устройство Direct3DDevice8
           D3DCREATE_SOFTWARE_VERTEXPROCESSING, ADDR d3dpp, ADDR pd3dDevice             
   
   fild    d3dpp.BackBufferWidth    ; Ширину делим на высоту
   fild    d3dpp.BackBufferHeight
   fdiv    
   fstp    AspectRatio
     
   invoke  D3DXMatrixRotationY, ADDR WorldMatrix, NULL   
   invoke  D3DXMatrixLookAtLH, ADDR ViewMatrix, ADDR EyeVector, ADDR LookAtVector, ADDR UpVector
   invoke  D3DXMatrixPerspectiveFovLH, ADDR ProjectionMatrix, FieldOfView, AspectRatio, NearViewPlanZ, FarViewPlanZ
   
   invoke  Init_Scene, pd3dDevice         
   invoke  Set_Render_Parameters
         
   ret
Init_Direct3D endp
;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Destroy_Direct3D proc

   d3dev8  Release, pd3dVertexBuffer  ; Освобождаем Vertex буфер
   d3dev8  Release, pd3dDevice        ; Освобождаем Direct3DDevice8  
   d3d8    Release, pd3d              ; Освобождаем Direct3D8   
    
   ret    
Destroy_Direct3D endp
;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Set_Render_Parameters proc
   
   d3dev8  SetTransform, pd3dDevice, D3DTS_WORLD, ADDR WorldMatrix            ; Подключаем матрицы  
   d3dev8  SetTransform, pd3dDevice, D3DTS_VIEW, ADDR ViewMatrix   
   d3dev8  SetTransform, pd3dDevice, D3DTS_PROJECTION, ADDR ProjectionMatrix
   
   d3dev8  SetRenderState, pd3dDevice, D3DRS_CULLMODE, D3DCULL_NONE           ; Откл. проверку следования вершин
   d3dev8  SetRenderState, pd3dDevice, D3DRS_LIGHTING, NULL                   ; Откл. освещение
   d3dev8  SetRenderState, pd3dDevice, D3DRS_ZENABLE, D3DZB_TRUE              ; Вкл. Z буфер
   
   d3dev8  SetVertexShader, pd3dDevice, D3DFVF_CUSTOMVERTEX                   ; Устанавливаем формат вершины
   d3dev8  SetStreamSource, pd3dDevice, 0 , pd3dVertexBuffer, 16              ; Подключаем поток
                                                                              ; 16 - размер данных для 1 вершины    
   ret   
Set_Render_Parameters endp
;::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Render_Scene proc
  
  .DATA
   Zvalue      dd  1.0f
   clearcolor  dd  0                                       ; Цвет для очистки буфера Direct3D
    
  .CODE
   d3dev8  TestCooperativeLevel, pd3dDevice                ; Проверка кооперации
   cmp     eax, D3DERR_DEVICELOST                          ; Если устройство потеряно то выходим
   jne     noreset                                         ;
   ret                                                     ;
   noreset:                                                ;
   cmp     eax, D3DERR_DEVICENOTRESET                      ; Если устройство не потеряно и не сброшено то сбрасываем
   jne     noreset2                                        ;
   d3dev8  Reset, pd3dDevice, ADDR d3dpp.BackBufferWidth   ;
   invoke  Set_Render_Parameters
   noreset2:                                               ;
   
   invoke  Animate_Scene
   
   d3dev8  Clear, pd3dDevice, 0 , NULL , D3DCLEAR_TARGET or D3DCLEAR_ZBUFFER , 0 , Zvalue , 0  ; Очищаем буфер
   d3dev8  DrawPrimitive, pd3dDevice, D3DPT_TRIANGLELIST, 0 , CountPrimitive                   ; Рисуем флаг
   d3dev8  Present, pd3dDevice, NULL, NULL, NULL, NULL                                         ; Показываем то что получилось    

   ret
Render_Scene	endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
Animate_Scene proc
   
   .DATA    
    x                 dd 0.1f
    x2                dd 0.0f
    z                 dd 0.0f
    zcoeff            dd 0.3f    
    zcorrect          dd 10.0f    
    sinx              dd 0.0f
   
   .DATA?
    checktime         dd ?
    color             db ?
    colorshift        db ?
    flag              dd ?
       
   .CODE

   invoke  GetTickCount             ; Получаем число тиков
   mov     ebx, checktime           ;
   add     ebx, 50                  ; Добавляем к нему коэффициент
   cmp     eax, ebx                 ;
   jb      noColor                  ; Если не наступило время обновления, то выход
   mov     checktime, eax           ;
   
   cmp     flag, 1                  ; Здесь происходит проверка, которая позволяет
   je      goDec                    ; узнать какой цвет был и какой нужно
   inc     color                    ; использовать далее
   cmp     color, 164
   jb      noxorFlag                ; Нужно для циклического изменения цвета фона
   xor     flag, 1                  ;от синего до красного по кругу
   noxorFlag:                       ;
   jmp     exitt                    ;
   goDec:                           ;
   dec     color                    ;
   cmp     color, 0                 ;
   ja      exitt                    ;
   xor     flag, 1                  ;
   add     colorshift, 8            ;
   cmp     colorshift, 24           ;
   jb      noincshift               ;
   mov     colorshift, 0            ;
   noincshift:

   exitt:

   d3dvb8  Lock1, pd3dVertexBuffer, NULL, CountData, ADDR pLockBuffer, NULL  ; Блокируем Vertex буфер
   
   mov     esi, pLockBuffer          ; Обновляем цвет фона
   add     esi, 28
   movzx   eax, color
   movzx   ecx, colorshift
   shl     eax, cl
   mov     [esi], eax
   
   mov     edi, pLockBuffer          ; Обновляем координату Z для флага
   add     edi, 104
   fld     x2
   fadd    x
   fst     x2
   fstp    z
   push    36
   pop     ecx
   column:
   push    ecx   
   fld     z                        ; Вычисляем нужное значение посредством Sin
   fsub    zcoeff
   fst     z
   fsin    
   fdiv    zcorrect
   fstp    sinx
   push    sinx
   pop     eax
   push    edi                      ; Обновляем координату Z
   push    10
   pop     ecx
   row:
   mov     DWORD PTR [edi], eax
   mov     DWORD PTR [edi+16], eax
   mov     DWORD PTR [edi+32], eax
   mov     DWORD PTR [edi+48], eax
   mov     DWORD PTR [edi+64], eax
   mov     DWORD PTR [edi+80], eax   
   add     edi, 96*36 
   loop    row
   pop     edi
   add     edi, 96   
   pop     ecx
   loop    column

   d3dvb8  Unlock, pd3dVertexBuffer        ; разблокируем Vertex буфер
   noColor:
   ret
Animate_Scene endp
;:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
end Start 
