Design Automation API for 3ds Max:MAXScript レンダリング フロー で生成されるレンダリング画像は、シーンファイル Table Fan.max の第 4 ビューポートになっていました。
今回は、Forge Viewer 上で表示しているビュー(カメラ)を 3ds Max シーンに反映して、レンダリング画像を得る方法を考察してみます。
Forge Viewer:シードファイルのビュー復元 で触れたとおり、Forge では,、シードファイル(元のデザインファイルが持つビューを Forge Viewer で表示するための情報を得る方法が用意されています。
逆に Forge Viewer からシードファイルへのビューの反映については、シードファイルへの書き込みをしない「Viewer」としての位置づけから、特に一貫した方法が用意されていない状況です。ただ、Forge ポータルへの過去の問い合わせ実績から、一定の需要があることも知られています。
英語のブログ記事になってしまいますが、Map Forge Viewer Camera back to Revit や Map Forge Viewer Camera back to Navisworks では、Forge Viewer:State API でビューを更新 でもご紹介した State API からのビュー情報を利用する方法が説明されています。
3ds Max では、少し前の Autodesk University クラス、3ds Max Design Automation: Add Beautiful Renders to Your Web Site で、少し違った方法が紹介されています。いずれも、あらゆるケースで試行したものではありませんので、ここでご紹介する内容も、調整が必要になる可能性があります。その点は事前にご了承ください。
前述の Autodesk University クラスの手法では、State API ではなく、次のようにビュー情報を得ています。
getCameraCoordinates() {let cam = this.viewer.getCamera()let matrix = cam.matrixWorldlet position = new THREE.Vector3();let rotation = new THREE.Quaternion();let scale = new THREE.Vector3();matrix.decompose(position, rotation, scale)//TODO: replace magic numbers with derived oneslet offset = new THREE.Vector3(60.5, -24.5, 60.5)position.addVectors(position, offset);let aspect = cam.aspect;//TODO: derive the 1.67 multiplicatorlet new_fov = cam.fov*1.67;return {position: position.toArray(),rotation: rotation.toArray(),fov: new_fov,renderingSize: [this.viewer.canvas.width, this.viewer.canvas.height]}}
青字で記した部分は、シーン毎に調整が必要な値であることを意味しています。
Forge Viewer は、シードファイルから得た 3D モデルを、カンバス領域の中心に表示するようになっています。この際にシードファイル時の位置との差を補正しているのが、Global Offset(グローバル オフセット)と呼ばれる位置合わせ用の(X、Y、Z)値です。上記コードの最初の // TO DO には、この Global Offset を当てはめることが出来ます。Global Offset については、過去に Forge Viewer シーンへの複数モデルの表示(一部改定・追記) のブログ記事でも触れたことがあります。
2 つめの // TO DO には、Web ブラウザで表示している Forge Viewer 上の 3D モデルと、レンダリングした画像を、オーバーラップした領域に同じ大きさで表示させる、FOV(Field Of View、視野角)の値に乗算させる画像表示用の任意の係数が含まれます。
下記は、前述の getCameraCoordinates() に Global Offset と表示係数を設定し、JSON パラメータしてWeb サーバーへ投げかける箇所の抜粋です。
function getCameraCoordinates() {let cam = _viewer.getCamera();let matrix = cam.matrixWorldlet position = new THREE.Vector3();let rotation = new THREE.Quaternion();let scale = new THREE.Vector3();matrix.decompose(position, rotation, scale);//TODO: replace magic numbers with derived oneslet element = JSON.stringify(_viewer.model.getData().globalOffset);let offset = new THREE.Vector3(JSON.parse(element).x, JSON.parse(element).y, JSON.parse(element).z);position.addVectors(position, offset);let aspect = cam.aspect;//TODO: derive the 1.67 multiplicatorlet new_fov = cam.fov * 2.4; // Table Fan.maxreturn {position: position.toArray(),rotation: rotation.toArray(),fov: new_fov,renderingSize: [_viewer.canvas.width, _viewer.canvas.height]}}// Render button$(document).on("click", "[id^='start']", function () {var vp = getCameraCoordinates();var params = '&color=' + _colorIndex +'&quantity=' + $("#quantity").val() +'&leaf=' + _leafFlag +'&width=' + JSON.parse(JSON.stringify(vp)).renderingSize[0] +'&height=' + JSON.parse(JSON.stringify(vp)).renderingSize[1] +'&fov=' + JSON.parse(JSON.stringify(vp)).fov +'&position=' + JSON.parse(JSON.stringify(vp)).position +'&rotation=' + JSON.parse(JSON.stringify(vp)).rotation;var uri = '/api/process/' + params;$.ajax({url: uri,type: 'POST',contentType: 'text/plain'}).done(function (res) {...
{"color": "3","quantity": "1","leaf": "true","width": "1920","height": "942","fov": "54.28767677618733","position": "-241.98164558410645,-447.09261417388916,179.48493644408882","rotation": "0.6990782139317431,-0.16547652955068382,-0.1602335643066114,0.6769286269720552"}
次のコードは、Web サーバー実装でルーティングに用意した /process endpoint から呼び出した WorkItem が処理する MAXScript です。青字部分が前回の MAXScript から拡張された部分です。レンダリングするビューを、JSON パラメータを元に作成したカメラに設定していることがわかります。
/* JSON 読み込み関数参照:https://forums.cgsociety.org/t/json-and-maxscript/1552038/11*/fn getJsonFileAsString filePath=(local jsonString = ""fs=openFile filePathwhile not eof fs do(jsonString += readchar fs)close fsreturn jsonString)
/* JSON 読み込み */paramsFilePath = "params.json"jsonString = getJsonFileAsString paramsFilePathmyJObject = dotNetObject "Newtonsoft.Json.Linq.JObject"myJObject = myJObject.parse jsonString
/* パラメータ取得 */global col = myJObject.item["color"].value as integerglobal leaf = myJObject.item["leaf"].value as booleanClassglobal width = myJObject.item["width"].value as integerglobal height = myJObject.item["height"].value as integerglobal fov = myJObject.item["fov"].value as doubleglobal temp = myJObject.item["position"].valueglobal pos = FilterString temp ","temp = myJObject.item["rotation"].valueglobal rot = FilterString temp ","
/* ColorX 画層オフ */global layfor i = 1 to 6 do(if col != i then (lay = LayerManager.getLayerFromName ( "Color" + i as string )lay.on = false))
/* Leaf 画層オン/オフ */lay = LayerManager.getLayerFromName ( "Leaf" )lay.on = leaf
/* ART レンダラー設定 */global art = ART_Renderer()art.quality_db = 20art.render_method = 1art.anti_aliasing_filter_diameter = 2.0art.enable_noise_filter = trueart.noise_filter_strength = 1renderers.current = art
/* カメラ設定 */x = rot[1] as floaty = rot[2] as floatz = rot[3] as floatw = rot[4] as floatglobal cam = freecamera rotation:(quat x y z w) position:[pos[1] as float, pos[2] as float, pos[3] as float] fov:fov
/* レンダリング & 生成画像保存 */fname = sysInfo.currentdir + "/result.jpg"undisplay ( render camera:cam outputwidth:width outputheight:height outputfile:fname )
Web ブラウザのズーム設定(Web ページの表示スケール)やウィンドウ サイズ(カンバスのサイズ)だけでなく、Windows 側のデスクトップ表示スケールの要素もあり、条件に合わせて表示状態を動的に変化させてしまう Forge Viewer との表示の一致はチャレンジな部分があります。
ただし、前提条件を固定化出来るのであれば、このシナリオを別のシーンで利用することも可能かと思います。
By Toshiaki Isezaki
コメント
コメントフィードを購読すればディスカッションを追いかけることができます。