<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      從0開發3D引擎(十一):使用領域驅動設計,從最小3D程序中提煉引擎(第二部分)

      大家好,本文根據領域驅動設計的成果,開始實現從最小的3D程序中提煉引擎。

      上一篇博文

      從0開發3D引擎(十):使用領域驅動設計,從最小3D程序中提煉引擎(第一部分)

      下一篇博文

      從0開發3D引擎(十二):使用領域驅動設計,從最小3D程序中提煉引擎(第三部分)

      本文流程

      我們根據上文的成果,按照下面的步驟開始實現從最小的3D程序中提煉引擎:
      1、建立代碼的文件夾結構
      2、index.html實現調用引擎API
      3、根據用例圖和API層的設計,用偽代碼實現index.html
      4、按照index.html從上往下的API調用順序,依次實現API:setCanvasById、setClearColor、addGLSL、createTriangleVertexData、addTriangle、setCamera

      回顧上文

      上文的領域驅動設計獲得了下面的成果:
      1、用戶邏輯和引擎邏輯
      2、分層架構視圖和每一層的設計
      3、領域驅動設計的戰略成果
      1)引擎子域和限界上下文劃分
      2)限界上下文映射圖
      3)流程圖
      4、領域驅動設計的戰術成果
      1)領域概念
      2)領域視圖
      5、數據視圖和PO的相關設計
      6、一些細節的設計
      7、基本的優化

      解釋基本的操作

      如何在瀏覽器上運行index.html

      1、在TinyWonder項目根目錄上執行start命令:

      yarn start
      

      2、在瀏覽器地址中輸入下面的url并回車,即可運行index.html頁面

      http://127.0.0.1:8080
      

      開始實現

      打開最小3D程序的TinyWonder項目,現在我們開始具體實現。

      準備

      我們要完全重寫src/的內容,因此在項目根目錄上新建mine/文件夾,將src/文件夾拷貝mine/中,并清空src/文件夾。

      通過備份src/文件夾,我們能容易地調出最小3D程序的代碼供我們參考。

      建立代碼的文件夾結構,約定模塊文件的命名規則

      模塊文件的命名原則

      • 加上所屬層級/模塊的后綴名
        這是為了減少重名的幾率
      • 盡量簡潔
        因此應該讓后綴名盡可能地短,只要幾乎不會出現重名的情況,那么不僅可以省略一些層級/模塊的后綴名,而且有些模塊文件甚至完全不加后綴名

      一級和二級文件夾

      如下圖所示:
      截屏2020-03-04上午8.28.11.png-16.5kB

      這是按照分層架構來劃分的文件夾:

      • 一級文件夾(xxx_layer/)對應每個層級
      • 二級文件夾(xxx_layer/的子文件夾)對應每層的對象

      api_layer的文件夾

      api_layer/api/放置API模塊文件,如SceneJsAPI.re等

      application_layer的文件夾

      application_layer/service/放置應用服務模塊文件,如SceneApService.re等

      domain_layer的文件夾

      domain_layer/domain/放置領域服務、實體和值對象的模塊文件

      domain_layer/repo/放置倉庫的模塊文件

      domain/的子文件夾對應引擎的各個子域,如下圖所示:
      截屏2020-03-04上午8.33.04.png-15.4kB

      引擎子域文件夾的子文件夾對應該子域的限界上下文,如下圖所示:
      截屏2020-03-04上午8.34.32.png-32.2kB

      限界上下文文件夾的子文件均為entity/、value_object/、service/,分別放置實體、值對象和領域服務的模塊文件。
      部分截圖如下圖所示:
      截屏2020-03-04上午8.37.22.png-22.3kB

      entity/、value_object/、service/文件夾的模塊文件的命名規則分別為:

      • 實體+限界上下文+Entity.re
        如SceneSceneGraphEntity.re
      • 值對象+限界上下文+VO.re
        如TriangleSceneGraphVO.re
      • 領域服務+限界上下文+DoService.re
        如RenderRenderDoService.re

      如果從這三個子文件夾的文件中提出公共代碼的模塊文件(如在后面,會從值對象ImmutableHashMap和值對象MutableHashMap中提出HashMap模塊),則該模塊文件的命名規則為:
      模塊名+限界上下文.re
      (如將HashMap模塊文件命名為HashMapContainer.re)

      infrastructure_layer的文件夾

      infrastructure_layer/data/的文件夾結構如下圖所示:
      截屏2020-03-04上午8.40.45.png-9.6kB

      ContainerManager.re負責實現“容器管理”

      container/放置PO Container相關的模塊文件

      po/放置PO類型定義的文件

      infrastructure_layer/external/文件夾結構如下圖所示:
      截屏2020-03-04上午8.41.13.png-5.6kB

      external_object/放置外部對象的FFI文件
      library/放置js庫的FFI文件

      index.html實現調用引擎API

      index.html需要引入引擎文件,調用它的API。

      我們首先考慮的實現方案是:
      與最小3D程序一樣,index.html以ES6 module的方式引入要使用的引擎的每個模塊文件(一個.re的引擎文件就是一個模塊文件),調用暴露的API函數。
      index.html的相關偽代碼如下:

      <script type="module">
      import { setCanvasById } from "./lib/es6_global/src/api_layer/api/CameraJsAPI.js";
      import { start } from "./lib/es6_global/src/api_layer/api/DirectorJsAPI.js";
      
      window.onload = () => {
          ...
          setCanvasById(canvasId);
          ...
          start();
          ...
      };
      </script>
      

      這個方案有下面的缺點:

      • 用戶訪問的權限過大
        用戶可以訪問非API的函數,如引擎的私有函數
      • 用戶需要知道要調用的引擎API在引擎的哪個模塊文件中,以及模塊文件的路徑,這樣會增加用戶的負擔
        如用戶需要知道setCanvasById在CameraJsAPI.js中,并且需要知道CameraJsAPI.js的路徑
      • 瀏覽器需要支持ES6 module import

      因此,我們使用下面的方案來實現,該方案可以解決上一個方案的缺點:

      • 把引擎所有的API模塊放到一個命名空間中,讓用戶通過它來調用API
        用戶只能訪問到API,從而讓引擎控制了用戶訪問權限;
        用戶只需要知道命名空間和API模塊的名字,減少了負擔。
      • 使用webpack,將與引擎API相關的文件打包成一個文件,在index.html中引入該文件
        這樣瀏覽器就不需要支持ES6 module import了

      我們通過下面的步驟來實現該方案:
      1、創建gulp任務
      該任務會創建src/Index.re文件,它引用了引擎所有的API模塊。
      通過下面的子步驟來實現該任務:
      1)在項目根目錄上,加入gulpfile.js文件
      2)在gulpfile.js中,加入gulp任務:generateIndex,該任務負責把引擎的所有API模塊放到Index.re文件中
      3)實現generateIndex任務
      因為我們希望用Reason而不是用js來實現,而且考慮到該任務屬于通用任務,多個Reason項目都需要它,所以我們通過下面的步驟來實現generateIndex任務:
      a)創建新項目:TinyWonderGenerateIndex
      b)進入新項目,用Reason代碼來實現generateIndex任務的邏輯
      其中src/Generate.re的generate函數是提供給用戶(如TinyWonder項目的generateIndex任務)使用的API,它的代碼如下:

      let generate =
          (
            globCwd: string,
            rootDir: string,
            sourceFileGlobArr: array(string),
            destDir: string,
            config,
          ) => {
        let excludeList = config##exclude |> Array.to_list;
        let replaceAPIModuleNameFunc =
          config##replaceAPIModuleNameFunc
          |> Js.Option.getWithDefault(moduleName =>
               moduleName |> Js.String.replace("JsAPI", "")
             );
      
        sourceFileGlobArr
        |> Array.to_list
        |> List.fold_left(
             (fileDataList, filePath) => {
               let fileName = Path.basename_ext(filePath, ".re");
               [
                 syncWithConfig(Path.join([|rootDir, filePath|]), {"cwd": globCwd})
                 |> Array.to_list
                 |> List.filter(filePath =>
                      excludeList
                      |> List.filter(exclude =>
                           filePath |> Js.String.includes(exclude)
                         )
                      |> List.length === 0
                    )
                 |> List.map(filePath =>
                      (
                        Path.basename_ext(filePath, ".re")
                        |> replaceAPIModuleNameFunc,
                        Path.basename_ext(filePath, ".re"),
                        Fs.readFileAsUtf8Sync(filePath) |> _findPublicFunctionList,
                      )
                    ),
                 ...fileDataList,
               ];
             },
             [],
           )
        |> List.flatten
        |> _buildContent
        |> _writeToIndexFile(destDir);
      };
      

      該函數使用glob庫遍歷sourceFileGlobArr數組,將“globCwd + rootDir + sourceFileGlob”路徑中的所有Reason文件的函數引入到destDir/Index.re的對應的模塊中。
      可在config.exclude中定義要排除的文件路徑的數組,在config.replaceAPIModuleNameFunc函數中定義如何重命名模塊名。

      該項目的完整代碼地址為:Tiny-Wonder-GenerateIndex

      舉個例子來說明用戶如何使用generate函數:
      假設用戶工作在TinyWonder項目(項目根目錄為/y/Github/TinyWonder/)上,創建了./src/api/AJsAPI.re,它的代碼為:

      let aFunc = v => 1;
      

      它的模塊名為“AJsAPI”。

      用戶還創建了./src/api/ddd/BJsAPI.re,它的代碼為:

      let bFunc = v => v * 2;
      

      它的模塊名為“BJsAPI”

      用戶可以在TinyWonder項目根目錄上,用js代碼調用Tiny-Wonder-GenerateIndex項目的generate函數:

      //將路徑為“/y/Github/TinyWonder/src/**/api/**/*.re”(排除包含“src/Config”的文件路徑)的所有API模塊引入到./src/Index.re中
      generate("/", "/y/Github/TinyWonder/src", ["**/api/**/*.re"], "./src/", {
              exclude: ["src/Config"],
              //該函數用于去掉API模塊名的“JsAPI”,如將“AJsAPI”重命名為“A”
              replaceAPIModuleNameFunc: (moduleName) => moduleName.replace("JsAPI", "")
          })
      

      用戶調用后,會在./src/中加入Index.re文件,它的代碼如下:

      module A = {
        let aFunc = AJsAPI.aFunc;
      };
      
      module B = {
        let bFunc = BJsAPI.bFunc;
      };
      

      現在我們清楚了如何使用generate函數,那么繼續在TinyWonderGenerateIndex項目上完成剩余工作:
      c)編譯該項目的Reason代碼為Commonjs模塊規范的js文件
      d)通過npm發布該項目
      package.json需要定義main字段:

      "main": "./lib/js/src/Generate.js",
      

      e)回到TinyWonder項目
      f)在TinyWonder項目的gulpfile.js->generateIndex任務中,引入TinyWonderGenerateIndex項目,調用它的generate函數
      TinyWonder項目的相關代碼為:
      gulpfile.js

      var gulp = require("gulp");
      var path = require("path");
      
      gulp.task("generateIndex", function (done) {
          var generate = require("tiny-wonder-generate-index");
          var rootDir = path.join(process.cwd(), "src"),
              destDir = "./src/";
      
          generate.generate("/", rootDir, ["**/api/**/*.re"], destDir, {
              exclude: [],
              replaceAPIModuleNameFunc: (moduleName) => moduleName.replace("JsAPI", "")
          });
      
          done();
      });
      

      2、創建gulp任務后,我們需要引入webpack,它將Index.re關聯的引擎文件打包成一個文件
      1)在項目的根目錄上,加入webpack.config.js文件:

      const path = require('path');
      
      const isProd = process.env.NODE_ENV === 'production';
      
      module.exports = {
        entry: {
            "wd": "./lib/es6_global/src/Index.js"
        },
        mode: isProd ? 'production' : 'development',
        output: {
            filename: '[name].js',
            path: path.join(__dirname, "dist"),
            library: 'wd',
            libraryTarget: 'umd'
        },
        target: "web"
      };
      

      2)在package.json中,加入script:

      "scripts": {
          ...
          "webpack:dev": "NODE_ENV=development webpack --config webpack.config.js",
          "webpack": "gulp generateIndex && npm run webpack:dev"
      }
      

      3、運行測試
      1)在src/api_layer/api/中加入一個用于測試的API模塊文件:TestJsAPI.re,定義兩個API函數:

      let aFunc = v => Js.log(v);
      
      let bFunc = v => Js.log(v * 2);
      

      2)安裝gulp后,在項目根目錄上執行generateIndex任務,進行運行測試:

      gulp generateIndex
      

      可以看到src/中加入了Index.re文件,Index.re代碼為:

      module Test = {
        let aFunc = TestJsAPI.aFunc;
        
        let bFunc = TestJsAPI.bFunc;
      };
      

      3)在安裝webpack后,在項目根目錄上執行下面命令,打包為dist/wd.js文件,其中命名空間為“wd”:

      yarn webpack
      

      4)index.html引入wd.js文件,調用引擎API

      index.html的代碼為:

        <script src="./dist/wd.js"></script>
      
        <script>
          var a = wd.Test.aFunc(1);
          var b = wd.Test.bFunc(2);
        </script>
      

      5)在瀏覽器上運行index.html,打開控制臺,可以看到打印了"1"和“4”

      用偽代碼實現index.html

      我們根據用例圖和設計的API來實現index.html:
      index.html需要使用最小3D程序的數據,來實現用例圖中“index.html”角色包含的用戶邏輯;
      index.html需要調用引擎API,來實現用例圖中的用例。

      index.html的偽代碼實現如下所示:

      <canvas id="webgl" width="400" height="400">
        Please use a browser that supports "canvas"
      </canvas>
      
      <script>
        //準備canvas的id
        var canvasId = "webgl";
        
        CanvasJsAPI.setCanvasById(canvasId);
        
        
        //準備清空顏色緩沖時的顏色值
        var clearColor = [0.0, 0.0, 0.0, 1.0];
        
        GraphicsJsAPI.setClearColor(clearColor);
        
        
        //準備兩組GLSL
        var vs1 = `
      precision mediump float;
      attribute vec3 a_position;
      uniform mat4 u_pMatrix;
      uniform mat4 u_vMatrix;
      uniform mat4 u_mMatrix;
      
      void main() {
      gl_Position = u_pMatrix * u_vMatrix * u_mMatrix * vec4(a_position, 1.0);
      }
      `;
        var fs1 = `
      precision mediump float;
      
      uniform vec3 u_color0;
      
      void main(){
      gl_FragColor = vec4(u_color0, 1.0);
      }
      `;
        var vs2 = `
      precision mediump float;
      attribute vec3 a_position;
      uniform mat4 u_pMatrix;
      uniform mat4 u_vMatrix;
      uniform mat4 u_mMatrix;
      
      void main() {
      gl_Position = u_pMatrix * u_vMatrix * u_mMatrix * vec4(a_position, 1.0);
      }
      `;
        var fs2 = `
      precision mediump float;
      
      uniform vec3 u_color0;
      uniform vec3 u_color1;
      
      void main(){
      gl_FragColor = vec4(u_color0 * u_color1, 1.0);
      }
      `;
        //準備兩個Shader的名稱
        var shaderName1 = "shader1";
        var shaderName2 = "shader2";
        
        ShaderJsAPI.addGLSL(shaderName1, [vs1, fs1]);
        ShaderJsAPI.addGLSL(shaderName2, [vs2, fs2]);
      
      
        //調用API,準備三個三角形的頂點數據
        var [vertices1, indices1] = SceneJsAPI.createTriangleVertexData();
        var [vertices2, indices2] = SceneJsAPI.createTriangleVertexData();
        var [vertices3, indices3] = SceneJsAPI.createTriangleVertexData();
        //準備三個三角形的位置數據
        var [position1, position2, position3] = [
          [0.75, 0.0, 0.0],
          [-0.0, 0.0, 0.5],
          [-0.5, 0.0, -2.0]
        ];
        //準備三個三角形的顏色數據
        var [colors1, colors2, colors3] = [
          [[1.0, 0.0, 0.0]],
          [[0.0, 0.8, 0.0], [0.0, 0.5, 0.0]],
          [[0.0, 0.0, 1.0]]
        ];
        
        SceneJsAPI.addTriangle(position1, [vertices1, indices1], [shaderName1, colors1]);
        SceneJsAPI.addTriangle(position2, [vertices2, indices2], [shaderName2, colors2]);
        SceneJsAPI.addTriangle(position3, [vertices3, indices3], [shaderName1, colors3]);
      
      
        //準備相機數據
        var [eye, center, up] = [
          [0.0, 0.0, 5.0],
          [0.0, 0.0, -100.0],
          [0.0, 1.0, 0.0]
        ];
        var [near, far, fovy, aspect] = [
          1.0,
          100.0,
          30.0,
          canvas.width / canvas.height
        ];
        
        SceneJsAPI.setCamera([eye, center, up], [near, far, fovy, aspect]);
      
      
        //準備webgl上下文的配置項
        var contextConfig = {
          "alpha": true,
          "depth": true,
          "stencil": false,
          "antialias": true,
          "premultipliedAlpha": true,
          "preserveDrawingBuffer": false,
        };
      
        DirectorJsAPI.init(contextConfig);
        
        
        DirectorJsAPI.start();
      </script>
      

      我們按照下面的步驟來具體實現index.html:
      1、按照index.html從上往下的API調用順序,確定要實現的API
      2、按照引擎的層級,從上層的API開始,實現每一層的對應模塊
      3、實現index.html的相關代碼
      4、運行測試

      現在我們按照順序,確定要實現API->“CanvasJsAPI.setCanvasById” 。

      現在來實現該API:

      實現“CanvasJsAPI.setCanvasById”

      1、在src/api_layer/api/中加入CanvasJsAPI.re,實現API
      CanvasJsAPI.re代碼為:

      //因為應用服務CanvasApService的setCanvasById函數輸入和輸出的DTO與這里(API)的setCanvasById函數輸入和輸出的VO相同,所以不需要在兩者之間轉換
      let setCanvasById = CanvasApService.setCanvasById;
      

      2、在src/application_layer/service/中加入CanvasApService.re,實現應用服務
      CanvasApService.re代碼為:

      let setCanvasById = canvasId => {
        CanvasCanvasEntity.setCanvasById(canvasId);
      
        //打印canvasId,用于運行測試
        Js.log(canvasId);
      };
      

      3、把最小3D程序的DomExtend.re放到src/instracture_layer/external/external_object/中,刪除目前沒用到的代碼(刪除requestAnimationFrame FFI)
      DomExtend.re代碼為:

      type htmlElement = {
        .
        "width": int,
        "height": int,
      };
      
      type body;
      
      type document = {. "body": body};
      
      [@bs.val] external document: document = "";
      
      [@bs.send] external querySelector: (document, string) => htmlElement = "";
      

      4、在src/domain_layer/domain/canvas/canvas/entity/中加入CanvasCanvasEntity.re,創建聚合根Canvas
      CanvasCanvasEntity.re代碼為:

      //這里定義Canvas DO的類型(Canvas DO為一個畫布)
      type t = DomExtend.htmlElement;
      
      let setCanvasById = canvasId =>
        Repo.setCanvas(
          DomExtend.querySelector(DomExtend.document, {j|#$canvasId|j}),
        );
      

      5、因為需要使用Result來處理錯誤,所以加入值對象Result
      1)在領域視圖的“容器”限界上下文中,加入值對象Result,負責操作錯誤處理的容器Result
      2)在src/domain_layer/domain/structure/container/value_object/中加入ResultContainerVO.re,創建值對象Result
      ResultContainerVO.re代碼為:

      type t('a, 'b) =
        | Success('a)
        | Fail('b);
      
      let succeed = x => Success(x);
      
      let fail = x => Fail(x);
      
      let _raiseErrorAndReturn = msg => Js.Exn.raiseError(msg);
      
      let failWith = x => (x |> _raiseErrorAndReturn)->Fail;
      
      let either = (successFunc, failureFunc, twoTrackInput) =>
        switch (twoTrackInput) {
        | Success(s) => successFunc(s)
        | Fail(f) => failureFunc(f)
        };
      
      let bind = (switchFunc, twoTrackInput) =>
        either(switchFunc, fail, twoTrackInput);
      
      let tap = (oneTrackFunc, twoTrackInput) =>
        either(
          result => {
            result |> oneTrackFunc |> ignore;
            result |> succeed;
          },
          fail,
          twoTrackInput,
        );
      
      let tryCatch = (oneTrackFunc: 'a => 'b, x: 'a): t('b, Js.Exn.t) =>
        try(oneTrackFunc(x) |> succeed) {
        | Js.Exn.Error(e) => fail(e)
        | err => {j|unknown error: $err|j} |> _raiseErrorAndReturn |> fail
        };
      
      let mapSuccess = (mapFunc, result) =>
        switch (result) {
        | Success(s) => mapFunc(s) |> succeed
        | Fail(f) => fail(f)
        };
      
      let handleFail = (handleFailFunc: 'f => unit, result: t('s, 'f)): unit =>
        switch (result) {
        | Success(s) => ()
        | Fail(f) => handleFailFunc(f)
        };
      

      6、在src/infrastructure_layer/data/po/中加入POType.re,定義PO的類型,目前PO只包含Canvas PO
      POType.re代碼為:

      //因為在創建PO時,Canvas PO(一個畫布)并不存在,所以它為option類型
      type po = {canvas: option(CanvasPOType.canvas)};
      

      7、在src/infrastructure_layer/data/po/中加入CanvasPOType.re,定義Canvas PO類型
      CanvasPOType.re代碼為:

      type canvas = DomExtend.htmlElement;
      

      8、因為定義了option類型,所以加入領域服務Option
      1)在領域視圖的“容器”限界上下文中,加入領域服務Option,負責操作Option容器
      2)在src/domain_layer/domain/structure/container/service/中加入OptionContainerDoService.re,創建領域服務Option
      OptionContainerDoService.re代碼為:

      //如果optionData為Some(v),返回v;否則拋出異常
      let unsafeGet = optionData => optionData |> Js.Option.getExn;
      
      //通過使用Result,安全地取出optionData的值
      let get = optionData => {
        switch (optionData) {
        | None => ResultContainerVO.failWith({|data not exist(get by getExn)|})
        | Some(data) => ResultContainerVO.succeed(data)
        };
      };
      

      9、在src/domain_layer/repo/中加入Repo.re,實現倉庫對Canvas PO的操作
      Repo.re代碼為:

      let getCanvas = () => {
        let po = ContainerManager.getPO();
      
        po.canvas;
      };
      
      let setCanvas = canvas => {
        let po = ContainerManager.getPO();
      
        {...po, canvas: Some(canvas)} |> ContainerManager.setPO;
      };
      

      10、在src/infrastructure_layer/data/中加入ContainerManager.re,實現PO Container中PO的讀寫
      ContainerManager.re代碼為:

      let getPO = () => {
        Container.poContainer.po;
      };
      
      let setPO = po => {
        Container.poContainer.po = po;
      };
      

      11、在src/infrastructure_layer/data/container/中加入ContainerType.re和Container.re,分別定義PO Container的類型和創建PO Container
      ContainerType.re代碼為:

      type poContainer = {mutable po: POType.po};
      

      Container.re代碼為:

      let poContainer: ContainerType.poContainer = {
        po: CreateRepo.create(),
      };
      

      12、在src/domain_layer/repo/中加入CreateRepo.re,實現創建PO
      CreateRepo.re代碼為:

      open POType;
      
      let create = () => {canvas: None};
      

      13、在項目根目錄上執行webpack命令,更新wd.js文件

      yarn webpack
      

      14、實現index.html相關代碼

      index.html代碼為:

      <!DOCTYPE html>
      <html lang="en">
      
      <head>
        <meta charset="utf-8" />
        <title>use engine</title>
      </head>
      
      <body>
        <canvas id="webgl" width="400" height="400">
          Please use a browser that supports "canvas"
        </canvas>
      
        <script src="./dist/wd.js"></script>
      
        <script>
          //準備canvas的id
          var canvasId = "webgl";
      
          //調用API
          wd.Canvas.setCanvasById(canvasId);
        </script>
      </body>
      
      </html>
      

      15、運行測試

      運行index.html頁面

      打開控制臺,可以看到打印了"webgl"

      實現“GraphicsJsAPI.setClearColor”

      1、在src/api_layer/api/中加入GraphicsJsAPI.re,實現API
      GraphicsJsAPI.re代碼為:

      let setClearColor = GraphicsApService.setClearColor;
      

      2、在src/application_layer/service/中加入GraphicsApService.re,實現應用服務
      GraphicsApService.re代碼為:

      let setClearColor = clearColor => {
        ContextContextEntity.setClearColor(Color4ContainerVO.create(clearColor));
      
        //用于運行測試
        Js.log(clearColor);
      };
      

      3、在src/domain_layer/domain/structure/container/value_object/中加入Color4ContainerVO.re,創建值對象Color4
      Color4ContainerVO.re代碼為:

      type r = float;
      type g = float;
      type b = float;
      type a = float;
      
      type t =
        | Color4(r, g, b, a);
      
      let create = ((r, g, b, a)) => Color4(r, g, b, a);
      
      let value = color =>
        switch (color) {
        | Color4(r, g, b, a) => (r, g, b, a)
        };
      

      4、在src/domain_layer/domain/webgl_context/context/entity/中加入ContextContextEntity.re,創建聚合根Context
      ContextContextEntity.re代碼為:

      let setClearColor = clearColor => {
        ContextRepo.setClearColor(clearColor);
      };
      

      5、在src/infrastructure_layer/data/po/中加入ContextPOType.re,定義Context PO類型
      ContextPOType.re代碼為:

      type context = {clearColor: (float, float, float, float)};
      

      6、修改POType.re
      POType.re相關代碼為:

      type po = {
        ...
        context: ContextPOType.context,
      };
      

      7、在src/domain_layer/repo/中加入ContextRepo.re,實現倉庫對Context PO的clearColor字段的操作
      ContextRepo.re代碼為:

      let getClearColor = () => {
        Repo.getContext().clearColor;
      };
      
      let setClearColor = clearColor => {
        Repo.setContext({
          ...Repo.getContext(),
          clearColor: Color4ContainerVO.value(clearColor),
        });
      };
      

      8、修改Repo.re,實現倉庫對Context PO的操作
      Repo.re相關代碼為:

      let getContext = () => {
        let po = ContainerManager.getPO();
      
        po.context;
      };
      
      let setContext = context => {
        let po = ContainerManager.getPO();
      
        {...po, context} |> ContainerManager.setPO;
      };
      

      9、修改CreateRepo.re,實現創建Context PO
      CreateRepo.re相關代碼為:

      let create = () => {
        ...
        context: {
          clearColor: (0., 0., 0., 1.),
        },
      };
      

      10、在項目根目錄上執行webpack命令,更新wd.js文件

      yarn webpack
      

      11、實現index.html相關代碼

      index.html代碼為:

        <script>
          ...
          //準備清空顏色緩沖時的顏色值
          var clearColor = [0.0, 0.0, 0.0, 1.0];
      
          wd.Graphics.setClearColor(clearColor);
        </script>
      

      12、運行測試

      運行index.html頁面

      打開控制臺,可以看到打印了數組:[0,0,0,1]

      實現“ShaderJsAPI.addGLSL”

      1、在src/api_layer/api/中加入ShaderJsAPI.re,實現API
      ShaderJsAPI.re代碼為:

      let addGLSL = ShaderApService.addGLSL;
      

      2、設計領域模型ShaderManager、Shader、GLSL的DO

      根據領域模型:
      此處輸入圖片的描述
      和識別的引擎邏輯:
      獲得所有Shader的Shader名稱和GLSL組集合

      我們可以設計聚合根ShaderManager的DO為集合list:

      type t = {glsls: list(Shader DO)};
      

      設計值對象GLSL的DO為:

      type t =
        | GLSL(string, string);
      

      設計實體Shader的DO為:

      type shaderName = string;
      
      type t =
        | Shader(shaderName, GLSL DO);
      

      3、在src/application_layer/service/中加入ShaderApService.re,實現應用服務
      ShaderApService.re代碼為:

      let addGLSL = (shaderName, glsl) => {
        ShaderManagerShaderEntity.addGLSL(
          ShaderShaderEntity.create(shaderName, GLSLShaderVO.create(glsl)),
        );
      
        //用于運行測試
        Js.log((shaderName, glsl));
      };
      

      4、在src/domain_layer/domain/shader/shader/entity/中加入ShaderShaderEntity.re,創建實體Shader
      ShaderShaderEntity.re代碼為:

      type shaderName = string;
      
      type t =
        | Shader(shaderName, GLSLShaderVO.t);
      
      let create = (shaderName, glsl) => Shader(shaderName, glsl);
      
      let getShaderName = shader =>
        switch (shader) {
        | Shader(shaderName, glsl) => shaderName
        };
      
      let getGLSL = shader =>
        switch (shader) {
        | Shader(shaderName, glsl) => glsl
        };
      

      5、在src/domain_layer/domain/shader/shader/value_object/中加入GLSLShaderVO.re,創建值對象GLSL
      GLSLShaderVO.re代碼為:

      type t =
        | GLSL(string, string);
      
      let create = ((vs, fs)) => GLSL(vs, fs);
      
      let value = glsl =>
        switch (glsl) {
        | GLSL(vs, fs) => (vs, fs)
        };
      

      6、在src/domain_layer/domain/shader/shader/entity/中加入ShaderManagerShaderEntity.re,創建聚合根ShaderManager
      ShaderManagerShaderEntity.re代碼為:

      type t = {glsls: list(ShaderShaderEntity.t)};
      
      let addGLSL = shader => {
        ShaderManagerRepo.addGLSL(shader);
      };
      

      7、在src/infrastructure_layer/data/po/中加入ShaderManagerPOType.re,定義ShaderManager PO的類型
      ShaderManagerPOType.re代碼為:

      //shaderId就是Shader的名稱
      type shaderId = string;
      
      type shaderManager = {glsls: list((shaderId, (string, string)))};
      

      8、修改POType.re
      POType.re相關代碼為:

      type po = {
        ...
        shaderManager: ShaderManagerPOType.shaderManager,
      };
      

      9、在src/domain_layer/repo/中加入ShaderManagerRepo.re,實現倉庫對ShaderManager PO的glsls字段的操作
      ShaderManagerRepo.re代碼為:

      open ShaderManagerPOType;
      
      let _getGLSLs = ({glsls}) => glsls;
      
      let addGLSL = shader => {
        Repo.setShaderManager({
          ...Repo.getShaderManager(),
          glsls: [
            (
              ShaderShaderEntity.getShaderName(shader),
              shader |> ShaderShaderEntity.getGLSL |> GLSLShaderVO.value,
            ),
            ..._getGLSLs(Repo.getShaderManager()),
          ],
        });
      };
      

      10、修改Repo.re,實現倉庫對ShaderManager PO的操作
      Repo.re相關代碼為:

      let getShaderManager = () => {
        let po = ContainerManager.getPO();
      
        po.shaderManager;
      };
      
      let setShaderManager = shaderManager => {
        let po = ContainerManager.getPO();
      
        {...po, shaderManager} |> ContainerManager.setPO;
      };
      

      11、修改CreateRepo.re,實現創建ShaderManager PO
      CreateRepo.re相關代碼為:

      let create = () => {
        ...
        shaderManager: {
          glsls: [],
        },
      };
      

      12、在項目根目錄上執行webpack命令,更新wd.js文件

      yarn webpack
      

      13、實現index.html相關代碼

      index.html代碼為:

        <script>
          ...
          //準備兩組GLSL
          var vs1 = `
      precision mediump float;
      attribute vec3 a_position;
      uniform mat4 u_pMatrix;
      uniform mat4 u_vMatrix;
      uniform mat4 u_mMatrix;
      
      void main() {
      gl_Position = u_pMatrix * u_vMatrix * u_mMatrix * vec4(a_position, 1.0);
      }
      `;
          var fs1 = `
      precision mediump float;
      
      uniform vec3 u_color0;
      
      void main(){
      gl_FragColor = vec4(u_color0, 1.0);
      }
      `;
          var vs2 = `
      precision mediump float;
      attribute vec3 a_position;
      uniform mat4 u_pMatrix;
      uniform mat4 u_vMatrix;
      uniform mat4 u_mMatrix;
      
      void main() {
      gl_Position = u_pMatrix * u_vMatrix * u_mMatrix * vec4(a_position, 1.0);
      }
      `;
          var fs2 = `
      precision mediump float;
      
      uniform vec3 u_color0;
      uniform vec3 u_color1;
      
      void main(){
      gl_FragColor = vec4(u_color0 * u_color1, 1.0);
      }
      `;
          //準備兩個Shader的名稱
          var shaderName1 = "shader1";
          var shaderName2 = "shader2";
      
          wd.Shader.addGLSL(shaderName1, [vs1, fs1]);
          wd.Shader.addGLSL(shaderName2, [vs2, fs2]);
        </script>
      

      14、運行測試

      運行index.html頁面

      打開控制臺,可以看到打印了兩個Shader的數據:
      截屏2020-02-29下午12.13.07.png-78.1kB

      實現“SceneJsAPI.createTriangleVertexData”

      1、在src/api_layer/api/中加入SceneJsAPI.re,實現API
      SceneJsAPI.re代碼為:

      let createTriangleVertexData = SceneApService.createTriangleVertexData;
      

      2、在src/application_layer/service/中加入SceneApService.re,實現應用服務
      SceneApService.re代碼為:

      let createTriangleVertexData = () => {
        //vertices和indices為DO數據,分別為值對象Vertices的DO和值對象Indices的DO
        let (vertices, indices) = GeometrySceneGraphVO.createTriangleVertexData();
      
        //將DO轉成DTO
        let data = (
          vertices |> VerticesSceneGraphVO.value,
          indices |> IndicesSceneGraphVO.value,
        );
      
        //用于運行測試
        Js.log(data);
        
        //將DTO返回給API層
        data;
      };
      

      3、在src/domain_layer/domain/scene/scene_graph/value_object/中加入GeometrySceneGraphVO.re,創建值對象Geometry
      GeometrySceneGraphVO.re代碼為:

      let createTriangleVertexData = () => {
        open Js.Typed_array;
      
        let vertices =
          Float32Array.make([|0., 0.5, 0.0, (-0.5), (-0.5), 0.0, 0.5, (-0.5), 0.0|])
          |> VerticesSceneGraphVO.create;
      
        let indices = Uint16Array.make([|0, 1, 2|]) |> IndicesSceneGraphVO.create;
      
        (vertices, indices);
      };
      

      4、在src/domain_layer/domain/scene/scene_graph/value_object/中加入VerticesSceneGraphVO.re和IndicesSceneGraphVO.re,創建值對象Vertices和值對象Indices
      VerticesSceneGraphVO.re代碼為:

      open Js.Typed_array;
      
      type t =
        | Vertices(Float32Array.t);
      
      let create = value => Vertices(value);
      
      let value = vertices =>
        switch (vertices) {
        | Vertices(value) => value
        };
      

      IndicesSceneGraphVO.re代碼為:

      open Js.Typed_array;
      
      type t =
        | Indices(Uint16Array.t);
      
      let create = value => Indices(value);
      
      let value = indices =>
        switch (indices) {
        | Indices(value) => value
        };
      

      5、在項目根目錄上執行webpack命令,更新wd.js文件

      yarn webpack
      

      6、實現index.html相關代碼

      index.html代碼為:

        <script>
          ...
          //調用API,準備三個三角形的頂點數據
          var [vertices1, indices1] = wd.Scene.createTriangleVertexData();
          var [vertices2, indices2] = wd.Scene.createTriangleVertexData();
          var [vertices3, indices3] = wd.Scene.createTriangleVertexData();
        </script>
      

      7、運行測試

      運行index.html頁面

      打開控制臺,可以看到打印了三次頂點數據

      實現“SceneJsAPI.addTriangle”

      1、修改SceneJsAPI.re,實現API
      SceneJsAPI.re相關代碼為:

      let addTriangle = (position, (vertices, indices), (shaderName, colors)) => {
        //這里的VO與DTO有區別:VO的colors的類型為array,而DTO的colors的類型為list,所以需要將colors的array轉換為list
        SceneApService.addTriangle(
          position,
          (vertices, indices),
          (shaderName, colors |> Array.to_list),
        );
      };
      

      2、設計聚合根Scene、值對象Triangle和它所有的值對象的DO

      根據領域模型:
      此處輸入圖片的描述

      我們按照Scene的聚合關系,從下往上開始設計:
      設計值對象Vector的DO為:

      type t =
        | Vector(float, float, float);
      

      設計值對象Position的DO為:

      type t =
        | Position(Vector.t);
      

      設計值對象Vertices的DO為:

      open Js.Typed_array;
      
      type t =
        | Vertices(Float32Array.t);
      

      設計值對象Indices的DO為:

      open Js.Typed_array;
      
      type t =
        | Indices(Uint16Array.t);
      

      設計值對象Color3的DO為:

      type r = float;
      type g = float;
      type b = float;
      
      type t =
        | Color3(r, g, b);
      

      設計值對象Transform的DO為:

      type t = {position: Position DO};
      

      設計值對象Geometry的DO為:

      type t = {
        vertices: Vertices DO,
        indices: Indices DO,
      };
      

      對于值對象Material的DO,我們需要思考:
      在領域模型中,Material組合了一個Shader,這應該如何體現到Material的DO中?

      解決方案:
      1)將Shader DO的Shader名稱和值對象GLSL拆開
      2)Shader DO只包含Shader名稱,它即為實體Shader的id
      3)Material DO包含一個Shader的id

      這樣就使Material通過Shader的id(Shader名稱),與Shader關聯起來了!

      因為Shader DO移除了值對象GLSL,所以我們需要重寫與Shader相關的代碼:
      1)重寫ShaderShaderEntity.re
      ShaderShaderEntity.re代碼為:

      type shaderName = string;
      type id = shaderName;
      
      type t =
        | Shader(id);
      
      let create = id => Shader(id);
      
      let getId = shader =>
        switch (shader) {
        | Shader(id) => id
        };
      

      2)重寫ShaderManagerShaderEntity.re
      ShaderManagerShaderEntity.re代碼為:

      type t = {glsls: list((ShaderShaderEntity.t, GLSLShaderVO.t))};
      
      let addGLSL = (shader, glsl) => {
        ShaderManagerRepo.addGLSL(shader, glsl);
      };
      

      3)重寫ShaderApService.re
      ShaderApService.re代碼為:

      let addGLSL = (shaderName, glsl) => {
        ShaderManagerShaderEntity.addGLSL(
          ShaderShaderEntity.create(shaderName),
          GLSLShaderVO.create(glsl),
        );
      
        //用于運行測試
        Js.log((shaderName, glsl));
      };
      

      4)重寫ShaderManagerRepo.re
      ShaderManagerRepo.re代碼為:

      open ShaderManagerPOType;
      
      let _getGLSLs = ({glsls}) => glsls;
      
      let addGLSL = (shader, glsl) => {
        Repo.setShaderManager({
          ...Repo.getShaderManager(),
          glsls: [
            (ShaderShaderEntity.getId(shader), GLSLShaderVO.value(glsl)),
            ..._getGLSLs(Repo.getShaderManager()),
          ],
        });
      };
      

      現在我們可以設計值對象Material的DO為:

      type t = {
        shader: Shader DO,
        colors: list(Color3 DO),
      };
      

      注意:這里的字段名是“shader”而不是“shaderName”或者“shaderId”,因為這樣才能直接體現Material組合了一個Shader,而不是組合了一個Shader名稱或Shader id

      我們繼續設計,設計值對象Triangle的DO為:

      type t = {
        transform: Transform DO,
        geometry: Geometry DO,
        material: Material DO,
      };
      

      設計聚合根Scene的DO為:

      type t = {triangles: list(Triangle DO)};
      

      3、修改SceneApService.re,實現應用服務
      SceneApService.re相關代碼為:

      let addTriangle = (position, (vertices, indices), (shaderName, colors)) => {
        SceneSceneGraphEntity.addTriangle(
          position |> VectorContainerVO.create |> PositionSceneGraphVO.create,
          (
            VerticesSceneGraphVO.create(vertices),
            IndicesSceneGraphVO.create(indices),
          ),
          (
            ShaderShaderEntity.create(shaderName),
            colors |> List.map(color => Color3ContainerVO.create(color)),
          ),
        );
      
        //用于運行測試
        Js.log(Repo.getScene());
      };
      

      4、加入值對象Triangle和它的所有值對象
      1)在src/domain_layer/domain/structure/math/value_object/中加入VectorMathVO.re,創建值對象Vector
      VectorMathVO.re代碼為:

      type t =
        | Vector(float, float, float);
      
      let create = ((x, y, z)) => Vector(x, y, z);
      
      let value = vec =>
        switch (vec) {
        | Vector(x, y, z) => (x, y, z)
        };
      

      2)在src/domain_layer/domain/scene/scene_graph/value_object/中加入PositionSceneGraphVO.re,創建值對象Position
      PositionSceneGraphVO.re代碼為:

      type t =
        | Position(VectorMathVO.t);
      
      let create = value => Position(value);
      
      let value = position =>
        switch (position) {
        | Position(pos) => pos
        };
      

      3)在src/domain_layer/domain/structure/container/value_object/中加入Color3ContainerVO.re,創建值對象Color3
      Color3ContainerVO.re代碼為:

      type r = float;
      type g = float;
      type b = float;
      
      type t =
        | Color3(r, g, b);
      
      let create = ((r, g, b)) => Color3(r, g, b);
      
      let value = color =>
        switch (color) {
        | Color3(r, g, b) => (r, g, b)
        };
      

      4)在src/domain_layer/domain/scene/scene_graph/value_object/中加入TransformSceneGraphVO.re,創建值對象Transform
      TransformSceneGraphVO.re代碼為:

      type t = {position: PositionSceneGraphVO.t};
      

      5)修改GeometrySceneGraphVO.re,定義DO
      GeometrySceneGraphVO.re相關代碼為:

      type t = {
        vertices: VerticesSceneGraphVO.t,
        indices: IndicesSceneGraphVO.t,
      };
      

      6)在src/domain_layer/domain/scene/scene_graph/value_object/中加入MaterialSceneGraphVO.re,創建值對象Material
      MaterialSceneGraphVO.re代碼為:

      type t = {
        shader: ShaderShaderEntity.t,
        colors: list(Color3ContainerVO.t),
      };
      

      7)在src/domain_layer/domain/scene/scene_graph/value_object/中加入TriangleSceneGraphVO.re,創建值對象Triangle
      TriangleSceneGraphVO.re代碼為:

      type t = {
        transform: TransformSceneGraphVO.t,
        geometry: GeometrySceneGraphVO.t,
        material: MaterialSceneGraphVO.t,
      };
      

      5、在src/domain_layer/domain/scene/scene_graph/entity/中加入SceneSceneGraphEntity.re,創建聚合根Scene
      SceneSceneGraphEntity.re代碼為:

      type t = {triangles: list(TriangleSceneGraphVO.t)};
      
      let addTriangle = (position, (vertices, indices), (shader, colors)) => {
        SceneRepo.addTriangle(position, (vertices, indices), (shader, colors));
      };
      

      6、在src/infrastructure_layer/data/po/中加入ScenePOType.re,定義Scene PO的類型
      ScenePOType.re代碼為:

      type transform = {position: (float, float, float)};
      
      type geometry = {
        vertices: Js.Typed_array.Float32Array.t,
        indices: Js.Typed_array.Uint16Array.t,
      };
      
      type material = {
        shader: string,
        colors: list((float, float, float)),
      };
      
      type triangle = {
        transform,
        geometry,
        material,
      };
      
      type scene = {triangles: list(triangle)};
      

      7、修改POType.re
      POType.re相關代碼為:

      type po = {
        ...
        scene: ScenePOType.scene,
      };
      

      8、實現Scene相關的倉庫

      我們按照倉庫依賴關系,從上往下開始實現:
      1)創建文件夾src/domain_layer/repo/scene/
      2)在src/domain_layer/repo/scene/中加入SceneRepo.re,實現倉庫對Scene PO的triangles字段的操作
      ShaderManagerRepo.re代碼為:

      open ScenePOType;
      
      let _getTriangles = ({triangles}) => triangles;
      
      let addTriangle = (position, (vertices, indices), (shader, colors)) => {
        Repo.setScene({
          ...Repo.getScene(),
          triangles: [
            TriangleSceneRepo.create(
              TransformSceneRepo.create(position),
              GeometrySceneRepo.create(vertices, indices),
              MaterialSceneRepo.create(shader, colors),
            ),
            ..._getTriangles(Repo.getScene()),
          ],
        });
      };
      

      3)在src/domain_layer/repo/scene/中加入TrianglerSceneRepo.re,實現創建Scene PO的一個Triangle數據
      TriangleSceneRepo.re代碼為:

      open ScenePOType;
      
      let create = (transform, geometry, material) => {
        transform,
        geometry,
        material,
      };
      

      4)在src/domain_layer/repo/scene/中加入TransformSceneRepo.re,實現創建Scene PO的一個Triangle的Transform數據
      TransformSceneRepo.re代碼為:

      open ScenePOType;
      
      let create = position => {
        position: position |> PositionSceneGraphVO.value |> VectorMathVO.value,
      };
      

      5)在src/domain_layer/repo/scene/中加入GeometrySceneRepo.re,實現創建Scene PO的一個Triangle的Geometry數據
      GeometrySceneRepo.re代碼為:

      open ScenePOType;
      
      let create = (vertices, indices) => {
        vertices: vertices |> VerticesSceneGraphVO.value,
        indices: indices |> IndicesSceneGraphVO.value,
      };
      

      6)在src/domain_layer/repo/scene/中加入MaterialSceneRepo.re,實現創建Scene PO的一個Triangle的Material數據
      MaterialSceneRepo.re代碼為:

      open ScenePOType;
      
      let create = (shader, colors) => {
        shader: shader |> ShaderShaderEntity.getId,
        colors: colors |> List.map(color => {color |> Color3ContainerVO.value}),
      };
      

      9、修改Repo.re,實現倉庫對Scene PO的操作
      Repo.re相關代碼為:

      let getScene = () => {
        let po = ContainerManager.getPO();
      
        po.scene;
      };
      
      let setScene = scene => {
        let po = ContainerManager.getPO();
      
        {...po, scene} |> ContainerManager.setPO;
      };
      

      10、修改CreateRepo.re,實現創建Scene PO
      CreateRepo.re相關代碼為:

      let create = () => {
        ...
        scene: {
          triangles: [],
        },
      };
      

      11、在項目根目錄上執行webpack命令,更新wd.js文件

      yarn webpack
      

      12、實現index.html相關代碼

      index.html代碼為:

        <script>
          ...
          //準備三個三角形的位置數據
          var [position1, position2, position3] = [
            [0.75, 0.0, 0.0],
            [-0.0, 0.0, 0.5],
            [-0.5, 0.0, -2.0]
          ];
          //準備三個三角形的顏色數據
          var [colors1, colors2, colors3] = [
            [[1.0, 0.0, 0.0]],
            [[0.0, 0.8, 0.0], [0.0, 0.5, 0.0]],
            [[0.0, 0.0, 1.0]]
          ];
      
          wd.Scene.addTriangle(position1, [vertices1, indices1], [shaderName1, colors1]);
          wd.Scene.addTriangle(position2, [vertices2, indices2], [shaderName2, colors2]);
          wd.Scene.addTriangle(position3, [vertices3, indices3], [shaderName1, colors3]);
        </script>
      

      13、運行測試

      運行index.html頁面

      打開控制臺,可以看到打印了三次Scene PO的數據

      實現“SceneJsAPI.setCamera”

      1、修改SceneJsAPI.re,實現API
      SceneJsAPI.re相關代碼為:

      let setCamera = SceneApService.setCamera;
      

      2、修改SceneApService.re,實現應用服務
      SceneApService.re相關代碼為:

      let setCamera = ((eye, center, up), (near, far, fovy, aspect)) => {
        SceneSceneGraphEntity.setCamera(
          (
            EyeSceneGraphVO.create(eye),
            CenterSceneGraphVO.create(center),
            UpSceneGraphVO.create(up),
          ),
          (
            NearSceneGraphVO.create(near),
            FarSceneGraphVO.create(far),
            FovySceneGraphVO.create(fovy),
            AspectSceneGraphVO.create(aspect),
          ),
        );
      
        //用于運行測試
        Js.log(Repo.getScene());
      };
      

      3、加入Camera的所有值對象
      1)在src/domain_layer/domain/scene/scene_graph/value_object/中加入EyeSceneGraphVO.re、CenterSceneGraphVO.re、UpSceneGraphVO.re,創建值對象Eye、Center、Up
      EyeSceneGraphVO.re代碼為:

      type t =
        | Eye(VectorMathVO.t);
      
      let create = value => Eye(value);
      
      let value = eye =>
        switch (eye) {
        | Eye(value) => value
        };
      

      CenterSceneGraphVO.re代碼為:

      type t =
        | Center(VectorMathVO.t);
      
      let create = value => Center(value);
      
      let value = center =>
        switch (center) {
        | Center(value) => value
        };
      

      UpSceneGraphVO.re代碼為:

      type t =
        | Up(VectorMathVO.t);
      
      let create = value => Up(value);
      
      let value = up =>
        switch (up) {
        | Up(value) => value
        };
      

      2)在src/domain_layer/domain/scene/scene_graph/value_object/中加入NearSceneGraphVO.re、FarSceneGraphVO.re、FovySceneGraphVO.re、AspectSceneGraphVO.re,創建值對象Near、Far、Fovy、Aspect
      NearSceneGraphVO.re代碼為:

      type t =
        | Near(float);
      
      let create = value => Near(value);
      
      let value = near =>
        switch (near) {
        | Near(value) => value
        };
      

      FarSceneGraphVO.re代碼為:

      type t =
        | Far(float);
      
      let create = value => Far(value);
      
      let value = far =>
        switch (far) {
        | Far(value) => value
        };
      

      FovySceneGraphVO.re代碼為:

      type t =
        | Fovy(float);
      
      let create = value => Fovy(value);
      
      let value = fovy =>
        switch (fovy) {
        | Fovy(value) => value
        };
      

      AspectSceneGraphVO.re代碼為:

      type t =
        | Aspect(float);
      
      let create = value => Aspect(value);
      
      let value = aspect =>
        switch (aspect) {
        | Aspect(value) => value
        };
      

      4、在src/domain_layer/domain/scene/scene_graph/value_object/中加入CameraSceneGraphVO.re,創建值對象Camera
      CameraSceneGraphVO.re代碼為:

      type t = {
        eye: EyeSceneGraphVO.t,
        center: CenterSceneGraphVO.t,
        up: UpSceneGraphVO.t,
        near: NearSceneGraphVO.t,
        far: FarSceneGraphVO.t,
        fovy: FovySceneGraphVO.t,
        aspect: AspectSceneGraphVO.t,
      };
      

      5、修改SceneSceneGraphEntity.re,將Camera DO作為Scene DO 的camera字段的數據,并實現setCamera函數:
      SceneSceneGraphEntity.re相關代碼為:

      type t = {
        ...
        camera: option(CameraSceneGraphVO.t),
      };
      
      ...
      
      let setCamera = ((eye, center, up), (near, far, fovy, aspect)) => {
        SceneRepo.setCamera((eye, center, up), (near, far, fovy, aspect));
      };
      

      6、修改ScenePOType.re,加入Scene PO的camera字段的數據類型
      ScenePOType.re相關代碼為:

      type camera = {
        eye: (float, float, float),
        center: (float, float, float),
        up: (float, float, float),
        near: float,
        far: float,
        fovy: float,
        aspect: float,
      };
      
      type scene = {
        ...
        camera: option(camera),
      };
      

      7、實現Scene->Camera相關的倉庫

      1)在src/domain_layer/repo/scene/中加入CameraSceneRepo.re,實現創建Scene PO的一個Camera數據
      CameraSceneRepo.re代碼為:

      open ScenePOType;
      
      let create = ((eye, center, up), (near, far, fovy, aspect)) => {
        eye: eye |> EyeSceneGraphVO.value |> VectorMathVO.value,
        center: center |> CenterSceneGraphVO.value |> VectorMathVO.value,
        up: up |> UpSceneGraphVO.value |> VectorMathVO.value,
        near: NearSceneGraphVO.value(near),
        far: FarSceneGraphVO.value(far),
        fovy: FovySceneGraphVO.value(fovy),
        aspect: AspectSceneGraphVO.value(aspect),
      };
      

      2)修改SceneRepo.re,實現倉庫對Scene PO的camera字段的操作
      SceneRepo.re相關代碼為:

      let setCamera = ((eye, center, up), (near, far, fovy, aspect)) => {
        Repo.setScene({
          ...Repo.getScene(),
          camera:
            Some(
              CameraSceneRepo.create(
                (eye, center, up),
                (near, far, fovy, aspect),
              ),
            ),
        });
      };
      

      8、修改CreateRepo.re,實現創建Scene PO的camera字段
      CreateRepo.re相關代碼為:

      let create = () => {
        ...
        scene: {
          ...
          camera: None,
        },
      };
      

      9、在項目根目錄上執行webpack命令,更新wd.js文件

      yarn webpack
      

      10、實現index.html相關代碼

      index.html代碼為:

        <script>
          ...
          //準備相機數據
          var [eye, center, up] = [
            [0.0, 0.0, 5.0],
            [0.0, 0.0, -100.0],
            [0.0, 1.0, 0.0]
          ];
          var canvas = document.querySelector("#webgl");
          var [near, far, fovy, aspect] = [
            1.0,
            100.0,
            30.0,
            canvas.width / canvas.height
          ];
      
          wd.Scene.setCamera([eye, center, up], [near, far, fovy, aspect]);
        </script>
      

      11、運行測試

      運行index.html頁面

      打開控制臺,可以看到打印了一次Scene PO的數據,它包含Camera的數據

      posted @ 2020-03-04 19:22  楊元超  閱讀(483)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲精品第一页中文字幕| 日本黄页网站免费观看| 麻阳| 国产超碰人人爽人人做| 精品一区二区亚洲国产| 热久久美女精品天天吊色| 最新精品露脸国产在线| 黄色亚洲一区二区三区四区| a男人的天堂久久a毛片| 免费A级毛片无码A∨蜜芽试看 | 白嫩人妻精品一二三四区| 国精偷拍一区二区三区| 亚洲第一成人网站| 午夜福利国产精品小视频| 亚洲精品人妻中文字幕| 日本久久久久久久做爰片日本| 欧洲码亚洲码的区别入口| 国产不卡一区二区四区| 67194熟妇人妻欧美日韩| 国产又色又爽又黄的| 国产成熟妇女性视频电影| 成人亚洲欧美成αⅴ人在线观看| 蜜臀视频在线观看一区二区| 中文字幕日韩有码第一页| 亚洲人成网站18禁止| 久99久热精品免费视频| 四虎在线中文字幕一区| 国产成人无码精品亚洲| 亚洲精品综合久中文字幕| 国产精品亚洲二区在线看| 日本免费一区二区三区最新vr| 超碰成人精品一区二区三| 国产精品中文第一字幕| 亚洲加勒比久久88色综合| 国产日韩av一区二区在线| 国产成AV人片久青草影院| 博罗县| 久久一日本道色综合久久| 欧美乱妇高清无乱码免费| 日本一道一区二区视频| 中文字幕日韩精品有码|