首先,进行Hololens项目的基本设置,并添加Spatial Mapping和Spatial Understanding 组件到Hierarchy面板中。Spatial Understanding模型主要暴露了三种类型的接口,简单平面的拓扑,空间查询,物体形状检测。要通过Spatial Understanding放置游戏对象到墙上或者椅子上,首先需要获取Spaital Understanding扫描到的房间数据。
勾选Spatial Understanding 组件的Auto Begin Scanning参数,应用就会在启动时开始扫描房间。或者通过RequestBeginScanning()方法来手动开始扫描。无论使用哪种方法开始扫描,都要通过RequsetFinishScan()方法来结束扫描。关于扫描的进度和状态,可以通过ScanStates来获取。
public enum ScanStates
{
//当前没有扫描进程
None,
//应用已就绪,可以开始扫描
ReadyToScan,
//正在扫描中
Scanning,
//已结束扫描
Finishing,
//扫描进程结束
Done
}
//如果当前扫描状态是没有扫描
if(SpatialUnderstanding.Instace.ScanState == SpatialUnderstanding.ScanState.Done){
//Do
}
SpatialUnderstandingDll.cs开始扫描后,SpatialUnderstandingDll.cs脚本就会被调用,扫描的主要逻辑都会在这个脚本中完成。此脚本中的功能主要被分成了三部分:
1 SpatialUnderstandingDllTopology
2 SpatialUnderstandingDllShapes
3 SpatialUnderstandingObjectPlacement
在使用SpatialUnderstandingDll中的功能前,必须首先调用SpatialUnderstanding_Init()方法进行初始化。以上三个部分都在单独的脚本中实现,SpatialUnderstandingDllTopology.cs、SpatialUnderstandingDllShape.cs、SpatialUnderstandingObjectPlacement.cs,每个脚本所实现的功能将在后文中介绍。SpatialUnderstandingDllTopology.cs此脚本中封装了关于当前环境的信息,它能分析出哪里是墙,哪里是地面,哪里是天花板等信息。这些信息都存储在PlaySpaceInfos结构体中。也可以根据参数指定数据,脚本中提供的方法有:
1 QueryTopology_FindPositionsOnWalls
2 QueryTopology_FindLargePositionsOnWalls
3 QueryTopology_FindLargestWall
4 QueryTopology_FindPositionsOnFloor
5 QueryTopology_FindLargestPositionsOnFloor
6 QueryTopology_FindPositionsSittable
具体参数可以查看脚本源码。以下是一个简单示例,查询环境中符合条件的墙的数量。
//定义存储查询结果的数组
private SpatialUnderstandingDllTopology.TopologyResult[] resultsTopology = new SpatialUnderstandingDllTopology.TopologyResult;
public void Query_Topology_FindPositionOnWall()
{
// 确定当前SpatialUnderstanding组件状态为可用。
if (!SpatialUnderstanding.Instance.AllowSpatialUnderstanding){
return;
}
// 定义参数,分别是墙面的最小高度,最小宽度,最高点和地面的距离,最后一个我也不知道..
float minHeightOfWallSpace = 0.5f;
float minWidthOfWallSpace = 0.75f;
float minHeightAboveFloor = 1.25f;
float minFacingClearance = 1.5f;
// 在内存中挂起resultTopology,避免内存地址变化
IntPtr resultsTopologyPtr = SpatialUnderstanding.Instance.UnderstandingDLL.PinObject(resultsTopology);
//执行查询操作并将数量返回给locationCount
int locationCount = SpatialUnderstandingDllTopology.QueryTopology_FindPositionsOnWalls(
minHeightOfWallSpace, minWidthOfWallSpace, minHeightAboveFloor, minFacingClearance,
resultsTopology.Length, resultsTopologyPtr);
}
SpaitalUnderstandingDllShapes.cs此脚本实现了查询某种形状的物体的功能。开发者可以指定各个参数,然后脚本会根据参数,找出环境中符合参数的地点。
//形状的组件
shapeComponents = new List<ShapeComponent>()
{
new ShapeComponent(
//形状组件约束
new List<ShapeComponentConstraint>()
{
//定义高度
ShapeComponentConstraint.Create_SurfaceHeight_Between(0.2f, 0.6f),
ShapeComponentConstraint.Create_SurfaceCount_Min(1),
//定义面积
ShapeComponentConstraint.Create_SurfaceArea_Min(0.035f),
}
),
};
AddShape("Sittable", shapeComponents);
以上代码定义了一个可以”坐”的区域。更多示例可以在ShapeDefinition.cs中找到。SpatialUnderstandingObjectPlacement.cs如果需要放置对象在指定地点,比如放置一个方块在椅子上,就需要使用Solver.PlaceObject()方法了。这个方法是一个查询语句,如果找到符合条件的地点,就会返回一个1(即true),否则返回0(false)。
//解算器_处对象
public static int Solver_PlaceObject(
//注意,此参数不是需要放置的对象的名字。而是此语句的名字。
string objectName,
//想要放置的地点,具体参数在ObjectPlacementDefinition结构体中定义
IntPtr placementDefinition,// ObjectPlacementDefinition
//规则的数量
int placementRuleCount,
//具体有哪些规则
IntPtr placementRules, // ObjectPlacementRule
//约束的数量
int constraintCount,
//具体的约束条件
IntPtr placementConstraints,// ObjectPlacementConstraint
//返回结果
IntPtr placementResult)
{
return 0;
}
以上语句会根据指定参数,在环境中找到符合要求的地点,并将结果返回到placementResult中。通过placementResult即可获得结果中的各个数据。关于放置地点的参数,ObjectPlacementDefinition结构体中定义了9种参数:
public enum PlacementType{
Place_OnFloor,
Place_OnWall,
Place_OnCeiling,
Place_OnShape,
Place_OnEdge,
Place_OnFloorAndCeiling,
Place_RandomInAir,
Place_InMidAir,
Place_UnderPlatformEdge,
};
规则有3种:
public enum ObjectPlacementRuleType{
Rule_AwayFromPosition,
Rule_AwayFromWalls,
Rule_AwayFromOtherObjects,
};
约束条件有6种:
public enum ObjectPlacementConstraintType{
Constraint_NearPoint,
Constraint_NearWall,
Constraint_AwayFromWalls,
Constraint_NearCenter,
Constraint_AwayFromOtherObjects,
Constraint_AwayFromPoint
};
下面是实际使用中的示例:
public void CreateOnFloor() {
//对象位置规则
List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementRule> placementRules = null;
//对象位置约束
List<SpatialUnderstandingDllObjectPlacement.ObjectPlacementConstraint> placementConstraints = null;
//一半的大小
float halfDimSize = UnityEngine.Random.Range(0.15f, 0.35f);
SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition myPlacementDefinition =
SpatialUnderstandingDllObjectPlacement.ObjectPlacementDefinition.Create_OnFloor(new Vector3(halfDimSize,
halfDimSize, halfDimSize * 2));
if (SpatialUnderstandingDllObjectPlacement.Solver_PlaceObject("place a chen on floor",
SpatialUnderstanding.Instance.UnderstandingDLL.PinObject(myPlacementDefinition),
(placementRules != null) ? placementRules.Count : 0,
((placementRules != null) && (placementRules.Count > 0)) ? SpatialUnderstanding.Instance.UnderstandingDLL.PinObject(placementRules.ToArray()) : IntPtr.Zero,
(placementConstraints != null) ? placementConstraints.Count : 0,
((placementConstraints != null) && (placementConstraints.Count > 0)) ? SpatialUnderstanding.Instance.UnderstandingDLL.PinObject(placementConstraints.ToArray()) : IntPtr.Zero,
SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticObjectPlacementResultPtr()) > 0
) {SpatialUnderstandingDllObjectPlacement.ObjectPlacementResult placementResult = SpatialUnderstanding.Instance.UnderstandingDLL.GetStaticObjectPlacementResult();
//Instantiate chan on result position
GameObject myChan =Instantiate(chan,placementResult.Position,Quaternion.LookRotation(holoCamera.transform.position));
if (myChan != null) {
nofityText.text = "已定位";
}else {
nofityText.text = "未找到合适地点";
}
}
}
以上代码中,首先设置规则和约束条件为null,即不进行约束。然后将查询条件设置为Create_OnFloor。随后通过If语句判断查询是否成功。如果查询到可用地点,则通过GetStaticObjectPlacementResult()方法取出查询到的结果。获取查询到的结果后,通过placementResult.Position获取符合条件的坐标点。再使用该坐标点生成游戏对象。以上代码没有设置规则和约束条件,只是指定为Floor,所以会在地面上生成对象,坐标为摄像机原点。更多详细的示例都在HoloToolkit-Examples/SpatialUnderstanding中。
查看完整版本: SpatialUnderstanding