2018年7月30日 星期一

從Blender裡萃取模組資料(3)

從Blender裡萃取模組資料(3)

前言

  續前篇從Blender裡萃取模組資料(2),這次說明物件的Hierarchy如何萃取,在此做個紀錄。

內容
  在Blender設定Hierarchy有3種常用的方法,一個是直接設定物件的Relations的Parent,如下圖
設定Relations的Parent
這個方法我認為是最好的方式,因為這個方法支援當Parent是Bone的狀況,第二種是"Set Parent To",用法很直覺,選取Child物件後再押住"Shift"選取Parent物件,然後按下Ctrl+p,選取需要的Parent選項即可,這方法很直覺而好用,還可以用來綁骨架,但這個方法有個缺陷,就是無法將物件的Parent設定到Bone,操作狀況如下圖
使用"Set Parent To"
接著是第三種方法,使用"Child Of Constraint",操作狀況如下圖
使用"Child Of Constraint"
這個方法雖然也可以達成Hierarchy效果,但有些相當棘手的問題,問題一,在操作狀況圖片可以看到物件的Hierarchy並沒有真正的改變,這會造成匯出模組時有些物件(Child Of Constraint的Parent物件)必須先匯出,問題二,這個方法並不會防止循環Hierarchy或是2個以上的Parent,如果先用第一或第二種方法設定Hierarchy為A物件的Child為B物件,B物件的Child為C物件,接著利用
"Child Of Constraint"將C物件的Parent設成A物件,造成循環Hierarchy且C物件具有2個Parent,Blender並不會阻止這件事的發生,所以這第三種方法就不打算支援了。
  接著就來看看由第一與第二種設定Hierarchy的資料如何萃取,萃取的範例如下
tagObj = bpy.data.objects["Cube"]
#
for obj in tagObj.children:
  print("child name:",obj.name)
#
if obj.parent != Nome:
  if obj.parent_type=='OBJECT':
    print("parent name:",obj.parent.name)
  elif obj.parent_type=='ARMATURE':
    print("parent name:",obj.parent.name)
  elif obj.parent_type=='Bone':  
    print("parent name:",obj.parent.name," bone name:",parent_bone)

只要存取物件的"Children"就可以得到該物件的Child物件,然後可以透過"Parent"來得到Parent物件,唯一要記住的特例是當物件的Parent是Bone的狀況,實際狀況會到萃取骨架資料時再詳細說明。

參考資料

Blender Documentation Contents

相關文章

從Blender裡萃取模組資料(2)
從Blender裡萃取模組資料(4)

2018年7月23日 星期一

從Blender裡萃取模組資料(2)

從Blender裡萃取模組資料(2)

前言

  續前篇從Blender裡萃取模組資料(1),這次會說明物件Transform的資料如何萃取,在此做個紀錄。

內容

  萃取Transform的Position與Scale相當容易,直接從object的Property取得即可,但Rotation的部分就不能直接取得了,相看下圖
Blender的旋轉模式
可以在編輯器看到旋轉模式的選項,在不同的旋轉模式要取不同的Property才能正確取到值,如下說明:
1.  Quaternion(WXYZ)模式下,取roation_quaternion
2.  XYZ Euler模式下,取roation_euler
3.  XZY Euler模式下,取roation_euler
4.  YXZ Euler模式下,取roation_euler
5.  YZX Euler模式下,取roation_euler
6.  ZXY Euler模式下,取roation_euler
7.  ZYX Euler模式下,取roation_euler
8.  Axis Angle模式下,取roation_axis_angle

旋轉模式在編輯器的預設值是"XYZ Euler",以我的需求來說,我會希望它是"Quaternion(WXYZ)",所以該怎麼轉換呢?只要將現在的旋轉模式轉到你想要的模式後,再去取對應的Property即可,整個Transform的資料萃取範例如下

import bpy

tagObj = bpy.data.objects["Cube"]
#
print('Object postion:',tagObj.location)

if obj.rotation_mode == 'QUATERNION':
  print('Object ratation(quaternion):',tagObj .rotation_quaternion)
else:
  #save old ratation mode for recover it.
  oldRotationMode = obj.rotation_mode
  #
  obj.rotation_mode = 'QUATERNION'
  print('Object ratation(quaternion):',tagObj .rotation_quaternion)
  #Recover ratation mode
  obj.rotation_mode = oldRotationMode 

#
print('Object scale:',tagObj.scale)

在取得非"Quaternion"時要把舊的模式記住,這只是為了恢復原本的操作模式,如果不介意旋轉模式被更改的話可以直接設成想要模式後取Property即可。

參考資料

Blender Documentation Contents

相關文章

從Blender裡萃取模組資料(1)
從Blender裡萃取模組資料(3)

2018年7月16日 星期一

從Blender裡萃取模組資料(1)

從Blender裡萃取模組資料(1)

前言

  從Blender裡匯出模型的完整範例很難搜尋到,都只能搜尋到片段的資料會出範例,所以這裡做個紀錄,由於目前並未研究透徹,只會萃取"位址"、"Normal"與"Index"的部分。

內容

  在之前的那一篇初探Blender的script裡有一個輸出的範例,那個範例輸出的資料恐怕是有問題的,如果用該範例輸出一個Cube的話,會得到36個vertex與36個index,這個和一般在Direcct3D與OpenGL的範例不一樣,Direcct3D與OpenGL的範例是24個vertex與36個index,其中的vertex是有Face的概念的,而不是將全部的Triangle一個一個輸出。
  這裡說一下輸出格式的問題,由於每個引擎有自己的格式,所以範例的用法並會包含輸出的格式,而是著重在從Blender裡將資料萃取出來,至於輸出格式的部分,相信並不困難,這裡就不記錄了。
  接著,就來看萃取的範例
import bpy
import bmesh

tagMesh = bpy.data.meshes["Cube"]
#Create bmesh from mesh
tagBMesh = bmesh.new()
tagBMesh.from_mesh(tagMesh)

#Start extract
baseIndex = 0
for face in tagBMesh.faces:
  for i in range(len(face.loops) ):
    print("Position:",face.loops[i].vert.co," ",end="")
    print("Normal:",face.normal," ",end="")
  print("Loop vertex end")
  loopLen = len(face.loops)
  for i in range( len(face.loops) - 2):
    print("index0:",baseIndex," ",end="")
    print("index1:",baseIndex + i + 1," ",end="")
    print("index2:",baseIndex + i + 2," ",end="")
  print("Loop index end")
  
#Free bmesh...
tagBMesh.free()

範例會去取"Cube"這個Mesh,如果取其它的Mesh的話,更換名稱即可。接著,會去從每個face裡取出loop的資料,這裡如果以"Cube"來說的話,loop就代表了"Cube"的面,如果用len(face.loops)取得的數值會是4,再依據每個loop的索引取得vertex的位址。Normal的部分可以直接從face拿到。接著會依據loop來輸出Index,每個面要輸出的Index應該有6個(兩個Triangle),輸出的過程如下圖
Index的輸出順序
至於BaseIndex是作為目前已第幾個Index為基準輸出Index,這樣就可以輸出6個Index了。
  輸出資料的時候要注意一些事,像是vertex的數值如果要取值時要經過round(),具體的範例如下
tagX = round(vert.co.x,3)
tagY = round(vert.co.y,3)
tagZ = round(vert.co.z,3)

取完後,才會是float的數值,原本的數值是double的。還有要注意Blender的坐標系是"右手坐標系",Index的順序是"順時針",如果需要轉換的話請自行轉換。

參考資料

Blender Documentation Contents

相關文章

從Blender裡萃取模組資料(2)

2018年7月9日 星期一

在Win32視窗程式中開啟console視窗

在Win32視窗程式中開啟console視窗

前言

  用Win32視窗程式寫出來的測試程式一直以來有個麻煩,就是切換測試項目(如測試光、測試影子...等)時會需要終止程式後,修改部分的code後再run一次,如果能有個debug的工具可以在不關閉程式的狀況下修改程式特定的參數不是很好嗎?可是如果自己了視窗的控件再交由C++來控制的話,成本實在有點高,如果有個console視窗可以輸入命令和輸出結果就可以解決上述的問題。在此把學習的過程做個紀錄。

內容

  使用的示範如下
  //Open console...
  AllocConsole();
  std::wstring strW = L"Dev Console";
  SetConsoleTitle( strW.c_str() );
  DrawMenuBar(GetConsoleWindow());
  //
  //call ReadConsole() or WriteConsole() to control console
  //...
  //Close console...
  FreeConsole();

開啟console視窗後,用ReadConsole()與WriteConsole()來控制輸入與輸出,但這兩個funciton的參數不是很用,如果可以將輸出入的結果導到stdin與stdout的話,就可以直接使用C++的IO function(scanf、printf...等),這樣用起來比較直接也比較好懂,所以導到stdin與stdout的範例如下
  //Open console...
  AllocConsole();
  std::wstring strW = L"Dev Console";
  SetConsoleTitle( strW.c_str() );
  DrawMenuBar(GetConsoleWindow());
  //Redirection IO to stdin and stdout 
  freopen( "CON", "r", stdin ) ;
  freopen( "CON", "w", stdout ) ;
  //call c++ IO function to control console
  //...
  //Close console...
  FreeConsole();

上述的範例如果要搭配Win32視窗程式使用的話,需要自己產生一個thread去控制輸入,為什麼呢?因為IO的輸入是block funciton,如果使用主thread去取得IO的輸入的話,整個程式都會停頓下來,這點請務必注意!
  接著還有一個麻煩是要將控制輸入的thread停下來的問題,當企圖停下一個thread時的標準做法是用WaitForSingleObject()來等待結束,可是scanf()或std::cin並不會自己中斷等待,所以需要給"假的"輸入來中斷IO的等待,範例如下

  //abort console inpurt
  HWND hWndConsole=GetConsoleWindow();
  INPUT ip[2];
  ip[0].type = INPUT_KEYBOARD;
  ip[0].ki.wVk = 0x41;;
  ip[0].ki.wScan = 0;
  ip[0].ki.dwFlags = 0;
  ip[0].ki.time = 0;
  ip[0].ki.dwExtraInfo = 0;
  ip[1].type = INPUT_KEYBOARD;
  ip[1].ki.wVk = VK_RETURN;
  ip[1].ki.wScan = 0;
  ip[1].ki.dwFlags = 0;
  ip[1].ki.time = 0;
  ip[1].ki.dwExtraInfo = 0;
  SendInput(2, ip, sizeof(INPUT) );

在範例中是輸入"a"和"enter"去中止console的blocking,要注意指輸入一個"enter"是無法中止blocking,必須要有一個有效的字元!

後記

  如果有看微軟的console使用說明Consoles的話,會發現還有其他的控制function可以使用,但由於本次的需求不需要用到所以都沒提到,有機會再來研究研究。

參考資料

Creation of a Console
Create window console inside main win32 window
Consoles

2018年7月2日 星期一

用Python寫Binary資料

用Python寫Binary資料

前言

  在之前的在Python中使用json,說到了要把Blender匯出的模組輸出成JSON,但如果要匯出模組到C++的話,就勢必要有parse JSON的Library,由於目前不打算引入JSON的Library,所以就想直接使用Python輸出Binary資料,在此把學習過程做個紀錄。

內容

  在學會寫Binary資料要先學會開Binary的檔案,範例如下
import os

f = open("testBinaryData.bin","wb+")
f.write(b"\x01\x02\x03\x04")
f.close()

開啟與寫入的方式和C++是相像的,但是如果要寫一個struct的資料的話,會發現只有write()是不夠用的,如果要寫入像是int、float、double...等資料時會需要一個可以把C++的基本資料型態轉成Python的Bytes這個資料型態的過程,而這個過程就是struct模組。

  接著來看struct模組的使用範例,如下
import struct

structFMT = "iIfd";
packData = struct.pack(structFMT,-1,123,3.14,-3.14 )
print("Pack data:", packData)
print("Pack data type:", type(packData) )
unpackData = struct.unpack(structFMT, packData)
print("Unpack data:", unpackData)
print("Unpack data type:", type(unpackData) )

#output
# Pack data: b'\xff\xff\xff\xff{\x00\x00\x00\xc3\xf5H@\x00\x00\x00\x00\x1f\x85\xebQ\xb8\x1e\t\xc0'
# Pack data type: <class bytes="">
# Unpack data: (-1, 123, 3.140000104904175, -3.14)
# Unpack data type: <class tuple="">

pack()是將資料編成Bytes,unpack()則是將Bytes資料轉回tuple。在這個範例中,pack()與unpack()的第一個參數都是format string,代表資料的格式,目前的格式依序如下
1.int(32位元)
2.unsigned int(32位元)
3.float
4.double
利用format string就可以自己規劃出自己的struct格式,如果想知道format string的詳細資料的話,可以到Interpret strings as packed binary data來了解。在撰寫時擔心float與double這兩個資料型態的Binary格式能不能被C++直接讀取,所以我寫了個C++的程式碼來測試,如下
#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"
#include "conio.h"
struct STestStruct
{
  long inum;
  unsigned long uinum;
  float fnum;
  double dnum;
};
int main()
{
  STestStruct data;
  FILE* f= fopen("testBinaryData.bin","rb");
  fread(&data,sizeof(STestStruct),1,f);
  printf("%d\n",data.inum);
  printf("%d\n",data.uinum);
  printf("%f\n",data.fnum);
  printf("%LF\n",data.dnum);
  fclose(f);
  system("PAUSE");
  return 0;
}
//output
//-1
//123
//3.140000
//-3.140000

結果非常好,遽然不需要任何轉換就可以讀回來,看來是我多慮了。

參考資料

Interpret strings as packed binary data