前端介面的呈現我們使用Vue+three.js。
Vue我們不選用vue-cli來建置專案,而是使用Vite,以求更好的性能。
然後再利用Vue 3D plugins - troisjs(based on Vite),來使用three.js library控制WebGL,呈現Unity的地圖與車子(物件)資料在瀏覽器上。
(成大學生念誠提供)
主要從troisjs中使用Camera, Renderer, Scene物件,協助畫面渲染。
import {
Camera,
Renderer,
Scene,
} from 'troisjs';
<Renderer ref="renderer" antialias orbit-ctrl resize="window">
<Camera ref="camera" />
<Scene ref="scene">
</Scene>
</Renderer>
左邊的icon是使用FontAwesomeIcon,並在css樣式上我們使用目前比較popular的atomic css - tailwind來協助畫面快速的樣式呈現。
<div class="w-12 grid grid-cols-1 gap-4 ">
<button id="hand" class="bg-pink-600 hover:bg-pink-500 text-white font-bold py-2 px-2 rounded drop-shadow">
<font-awesome-icon icon="hand" />
</button>
<button id="pointer" class="bg-pink-600 hover:bg-pink-500 text-white font-bold py-2 px-2 rounded drop-shadow">
<font-awesome-icon icon="arrow-pointer" />
</button>
<button id="Cube" class="bg-pink-600 hover:bg-pink-500 text-white font-bold py-2 px-2 rounded drop-shadow">
<font-awesome-icon icon="cube" />
</button>
<button id="Sphere" class="bg-pink-600 hover:bg-pink-500 text-white font-bold py-3 px-2 rounded drop-shadow flex justify-center items-center">
<svg-icon stroke-width="5px" name="sphere" class="w-fit h-4 icon-color"></svg-icon>
</button>
<button id="save" class="bg-pink-600 hover:bg-pink-500 text-white font-bold py-2 px-2 rounded drop-shadow">
<font-awesome-icon icon="floppy-disk" />
</button>
</div>
其中最後一個save button可以產生glTF(GL Transmission Format)格式的3D資料。
import { GLTFExporter } from 'three/examples/jsm/exporters/GLTFExporter.js';
const exporter = new GLTFExporter();
// scene 是上述透過troisjs所取得的scene object
exporter.parse(
scene,
function ( gltf ) {
const output = JSON.stringify( gltf, null, 2 );
saveString( output, 'scene.gltf' );
},
function ( error ) {
console.log( 'An error happened' );
}
);
然後再轉成Unity可使用的.fbx格式,類似像以下格式:
Definitions: {
Version: 100
Count: 4
ObjectType: "GlobalSettings" {
Count: 1
}
ObjectType: "Model" {
Count: 1
PropertyTemplate: "FbxNode" {
Properties70: {
P: "QuaternionInterpolate", "enum", "", "",0
P: "RotationOffset", "Vector3D", "Vector", "",0,0,0
P: "RotationPivot", "Vector3D", "Vector", "",0,0,0
P: "ScalingOffset", "Vector3D", "Vector", "",0,0,0
P: "ScalingPivot", "Vector3D", "Vector", "",0,0,0
P: "TranslationActive", "bool", "", "",0
P: "TranslationMin", "Vector3D", "Vector", "",0,0,0
P: "TranslationMax", "Vector3D", "Vector", "",0,0,0
P: "TranslationMinX", "bool", "", "",0
P: "TranslationMinY", "bool", "", "",0
P: "TranslationMinZ", "bool", "", "",0
P: "TranslationMaxX", "bool", "", "",0
P: "TranslationMaxY", "bool", "", "",0
P: "TranslationMaxZ", "bool", "", "",0
P: "RotationOrder", "enum", "", "",0
P: "RotationSpaceForLimitOnly", "bool", "", "",0
P: "RotationStiffnessX", "double", "Number", "",0
P: "RotationStiffnessY", "double", "Number", "",0
P: "RotationStiffnessZ", "double", "Number", "",0
P: "AxisLen", "double", "Number", "",10
P: "PreRotation", "Vector3D", "Vector", "",0,0,0
P: "PostRotation", "Vector3D", "Vector", "",0,0,0
P: "RotationActive", "bool", "", "",0
P: "RotationMin", "Vector3D", "Vector", "",0,0,0
P: "RotationMax", "Vector3D", "Vector", "",0,0,0
P: "RotationMinX", "bool", "", "",0
P: "RotationMinY", "bool", "", "",0
P: "RotationMinZ", "bool", "", "",0
P: "RotationMaxX", "bool", "", "",0
P: "RotationMaxY", "bool", "", "",0
P: "RotationMaxZ", "bool", "", "",0
P: "InheritType", "enum", "", "",0
P: "ScalingActive", "bool", "", "",0
P: "ScalingMin", "Vector3D", "Vector", "",0,0,0
P: "ScalingMax", "Vector3D", "Vector", "",1,1,1
P: "ScalingMinX", "bool", "", "",0
P: "ScalingMinY", "bool", "", "",0
P: "ScalingMinZ", "bool", "", "",0
P: "ScalingMaxX", "bool", "", "",0
P: "ScalingMaxY", "bool", "", "",0
P: "ScalingMaxZ", "bool", "", "",0
P: "GeometricTranslation", "Vector3D", "Vector", "",0,0,0
P: "GeometricRotation", "Vector3D", "Vector", "",0,0,0
P: "GeometricScaling", "Vector3D", "Vector", "",1,1,1
P: "MinDampRangeX", "double", "Number", "",0
P: "MinDampRangeY", "double", "Number", "",0
P: "MinDampRangeZ", "double", "Number", "",0
P: "MaxDampRangeX", "double", "Number", "",0
P: "MaxDampRangeY", "double", "Number", "",0
P: "MaxDampRangeZ", "double", "Number", "",0
P: "MinDampStrengthX", "double", "Number", "",0
P: "MinDampStrengthY", "double", "Number", "",0
P: "MinDampStrengthZ", "double", "Number", "",0
P: "MaxDampStrengthX", "double", "Number", "",0
P: "MaxDampStrengthY", "double", "Number", "",0
P: "MaxDampStrengthZ", "double", "Number", "",0
P: "PreferedAngleX", "double", "Number", "",0
P: "PreferedAngleY", "double", "Number", "",0
P: "PreferedAngleZ", "double", "Number", "",0
P: "LookAtProperty", "object", "", ""
P: "UpVectorProperty", "object", "", ""
P: "Show", "bool", "", "",1
P: "NegativePercentShapeSupport", "bool", "", "",1
P: "DefaultAttributeIndex", "int", "Integer", "",-1
P: "Freeze", "bool", "", "",0
P: "LODBox", "bool", "", "",0
P: "Lcl Translation", "Lcl Translation", "", "A",0,0,0
P: "Lcl Rotation", "Lcl Rotation", "", "A",0,0,0
P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1
P: "Visibility", "Visibility", "", "A",1
P: "Visibility Inheritance", "Visibility Inheritance", "", "",1
}
}
}
ObjectType: "Geometry" {
Count: 1
PropertyTemplate: "FbxMesh" {
Properties70: {
P: "Color", "ColorRGB", "Color", "",0.8,0.8,0.8
P: "BBoxMin", "Vector3D", "Vector", "",0,0,0
P: "BBoxMax", "Vector3D", "Vector", "",0,0,0
P: "Primary Visibility", "bool", "", "",1
P: "Casts Shadows", "bool", "", "",1
P: "Receive Shadows", "bool", "", "",1
}
}
}
ObjectType: "Material" {
Count: 1
PropertyTemplate: "FbxSurfaceLambert" {
Properties70: {
P: "ShadingModel", "KString", "", "", "Lambert"
P: "MultiLayer", "bool", "", "",0
P: "EmissiveColor", "Color", "", "A",0,0,0
P: "EmissiveFactor", "Number", "", "A",1
P: "AmbientColor", "Color", "", "A",0.2,0.2,0.2
P: "AmbientFactor", "Number", "", "A",1
P: "DiffuseColor", "Color", "", "A",0.8,0.8,0.8
P: "DiffuseFactor", "Number", "", "A",1
P: "Bump", "Vector3D", "Vector", "",0,0,0
P: "NormalMap", "Vector3D", "Vector", "",0,0,0
P: "BumpFactor", "double", "Number", "",1
P: "TransparentColor", "Color", "", "A",0,0,0
P: "TransparencyFactor", "Number", "", "A",0
P: "DisplacementColor", "ColorRGB", "Color", "",0,0,0
P: "DisplacementFactor", "double", "Number", "",1
P: "VectorDisplacementColor", "ColorRGB", "Color", "",0,0,0
P: "VectorDisplacementFactor", "double", "Number", "",1
}
}
}
}
這個.fbx檔案我們可以透過後端的api,將資料使用byte string insert進去dynamodb資料庫中。
API我們使用python的fastapi框架,並搭配uvicorn作為啟動的server。
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
from uuid import UUID
from datetime import datetime
class Item(BaseModel):
_id: UUID
topic: str
timestamp: datetime
payload: bytes
app = FastAPI()
@app.post("/fbx/")
async def create_fbxItem(item: Item):
return item
uvicorn main:app --reload
如之前所說,insert data到dynamodb在python的code中使用boto3。
data = {
"_id": "xxxx-xxxx-xxxx-xxxx",
"topic": "fbx",
"timestamp": int(round(time.time() * 1000)),
"payload": pickle.dump(fbxObject)
}
table.put_item(Item=item)
之後讀取資料使用
response = table.get_item(
Key={
'_id': "xxxx-xxxx-xxxx-xxxx",
}
)
item = response['Item']["payload"]
pickle.loads(bytes(item))
因此整個讀取檔案格式與顯示的架構改成如下: