Start a new topic

Billboard Script Question

im guessing I need to use mgSetAttList to set Render Both Sides Visible? can someone help and explain how that function works or which function to use? I also need to set Billboard Type to Fixed with Alpha, is this possible via script? Thx, Joe

If you have the OpenFlight API installed, there are lots of help docs (and samples) included that show you how to use functions in the API, includng mgSetAttList. The most useful (IMO) is the OpenFlight API Reference Set. It has a page for each function and (equally important) includes the OpenFlight Data Dictionary. The OpenFlight Data Dictionary lists all the attributes (names and types) for every node type in the OpenFlight Scene Graph. Included in the OFAPI Reference Set is a FAQ which really dives into mgSetAttList and mgGetAttList.

BTW, if you just want to skip ahead, here is the script to set a polygon to Render Both Sides Visible and Billboard Type to Fixed with Alpha: 

# set both attributes at the same time...
mgSetAttList(poly, fltPolyDrawType, 1, fltPolyTemplate, 1)

# ...or set each separately, either way works
mgSetAttList(poly, fltPolyDrawType, 1)
mgSetAttList(poly, fltPolyTemplate, 1)

  In C/C++, this would be: 

// set both attributes at the same time...
mgSetAttList(poly, fltPolyDrawType, 1, fltPolyTemplate, 1, MG_NULL);

# ...or set each separately, either way works
mgSetAttList(poly, fltPolyDrawType, 1, MG_NULL);
mgSetAttList(poly, fltPolyTemplate, 1, MG_NULL);

 where poly is the polygon (face) node whose attributes you want to set.


The key to using mgSetAttList (or mgGetAttList) is knowing how to navigate the OpenFlight Data Dictionary to learn the field names and their types. If you have specific questions, let us know.

ok so i gave pieced together this script and it works for the most part, but there is something i can't figure out. in some cases, someone has created billboards that are rotated in the xy axis, as well as rotated in the z axis. this script works great on things rotated in the z axis. the AlignBillboard() was working until I added the RotateBillboard() .. but obviously it would squash the texture quite a bit. So I need to use a reference axis of some kind? And I just can't wrap my head around that In essence, all this should need is a way to rotate the plane to face the Y axis, but ignore any xy rotation so, rotating around its center, but with the world axis? # this script will turn single plane trees into plus style billboard trees. # it will align planes that are rotated off axis and will align planes to face in the Y direction # to run this script, select all of the polygons and run the script # setup db = mgGetCurrentDb() selectList = mgGetSelectList (db) num = mgGetRecListCount (selectList) # this function rotates the billboard to face in the Y direction def RotateBillboard (rec): ok, box = mgGetBounds (rec) sum = mgCoord3dAdd (box.min, box.max) center = mgCoord3dDivide (sum,2) target = mgMakeCoord3d(center.x, center.y + 1, center.z) ok, i,j,k = mgGetPolyNormal(rec) ref = mgMakeCoord3d (center.x+i, center.y+j, center.z+k) norm = mgMakeUnitVectord (center, ref) dir = mgMakeUnitVectord (center, target) cross = mgVectordCross (norm, mgVectordZAxis()) dot = mgVectordDot (cross, dir) # get the angle to rotate by norm = mgMakeCoord3d (norm.i, norm.j, norm.k) dir = mgMakeCoord3d (dir.i, dir.j, dir.k) angle = mgCoord3dAngle (norm, dir) if (dot >= 0): angle = -angle ok, translateto = mgMatrixFormTranslate (-center.x, -center.y, -center.z) ok, rotatematrix = mgMatrixFormRotateZ (angle) ok, translatefrom = mgMatrixFormTranslate (center.x, center.y, center.z) ok, matrix = mgMatrixMultiply (translateto, rotatematrix) ok, matrix = mgMatrixMultiply (matrix, translatefrom) vert = mgGetChild (rec) while (vert): ok, x,y,z = mgGetVtxCoord (vert) pos = mgMakeCoord3d (x,y,z) newpos = mgCoord3dTransform (matrix,pos) mgSetVtxCoord (vert, newpos.x, newpos.y, newpos.z) mgSetVtxNormal (vert, dir.x, dir.y, dir.z) vert = mgGetNext (vert) def AlignBillboard(rec): vertCount = mgCountChild(rec) v = mgGetChild(rec) ok, a, b, c = mgGetVtxCoord(v) for j in range (1, vertCount + 1): t = mgGetChildNth(rec, j) ok, x, y, z = mgGetVtxCoord(t) mgSetVtxCoord(t, x, b, z) def DuplicateAndRotateBillboard(rec): ok, bounds = mgGetBounds (rec) center = mgCoord3dDivide (mgCoord3dAdd (bounds.max, bounds.min), 2.0) paramBlock = mgGetParamBlock ("Duplicate") mgParamSetDouble3 (paramBlock, "From Point", 0, 0, 0) mgParamSetDouble3 (paramBlock, "To Point", 0, 0, 0) mgExecute ("Duplicate", paramBlock) paramBlock = mgGetParamBlock ("Rotate About Point") mgParamSetDouble3 (paramBlock, "Center Point", center.x, center.y, center.z) mgParamSetDouble (paramBlock, "Angle", 90) mgExecute ("Rotate About Point", paramBlock) for i in range (0, num): rec,m = mgGetNextRecInList (selectList) RotateBillboard(rec) mgDeselectAll(db) mgSelectOne (rec) mgSetAttList(rec, fltPolyDrawType, 1, fltPolyTemplate, 1) AlignBillboard(rec) DuplicateAndRotateBillboard(rec)

Your script did not come out formatted in your last post. When using the forum there is a way to embed "code snippets" in your posts. When you do this the "formatting" of the code is maintained. Use the little icon on the top (right most one) that contains three dots surrounded by curly braces. Click this and paste your code in the window that pops up. Choose Python as language.

I tried to reconstruct your script in the OpenFlight Script editor. Below is what I believe it is. Make sure I got all the indents right.

# this script will turn single plane trees into plus style billboard trees. 
# it will align planes that are rotated off axis and will align planes to face in the Y direction 
# to run this script, select all of the polygons and run the script 
# setup
db = mgGetCurrentDb()
selectList = mgGetSelectList (db)
num = mgGetRecListCount (selectList)

# this function rotates the billboard to face in the Y direction 
def RotateBillboard (rec):
	ok, box = mgGetBounds (rec)
	sum = mgCoord3dAdd (box.min, box.max)
	center = mgCoord3dDivide (sum,2)
	target = mgMakeCoord3d(center.x, center.y + 1, center.z)
	ok, i,j,k = mgGetPolyNormal(rec)
	ref = mgMakeCoord3d (center.x+i, center.y+j, center.z+k)
	norm = mgMakeUnitVectord (center, ref)
	dir = mgMakeUnitVectord (center, target)
	cross = mgVectordCross (norm, mgVectordZAxis())
	dot = mgVectordDot (cross, dir)
	# get the angle to rotate by 
	norm = mgMakeCoord3d (norm.i, norm.j, norm.k)
	dir = mgMakeCoord3d (dir.i, dir.j, dir.k)
	angle = mgCoord3dAngle (norm, dir)
	if (dot >= 0): 
		angle = -angle
	ok, translateto = mgMatrixFormTranslate (-center.x, -center.y, -center.z)
	ok, rotatematrix = mgMatrixFormRotateZ (angle)
	ok, translatefrom = mgMatrixFormTranslate (center.x, center.y, center.z)
	ok, matrix = mgMatrixMultiply (translateto, rotatematrix)
	ok, matrix = mgMatrixMultiply (matrix, translatefrom)
	vert = mgGetChild (rec)
	while (vert): 
		ok, x,y,z = mgGetVtxCoord (vert)
		pos = mgMakeCoord3d (x,y,z)
		newpos = mgCoord3dTransform (matrix,pos)
		mgSetVtxCoord (vert, newpos.x, newpos.y, newpos.z)
		mgSetVtxNormal (vert, dir.x, dir.y, dir.z)
		vert = mgGetNext (vert)

def AlignBillboard(rec):
	vertCount = mgCountChild(rec)
	v = mgGetChild(rec)
	ok, a, b, c = mgGetVtxCoord(v)
	for j in range (1, vertCount + 1):
		t = mgGetChildNth(rec, j)
		ok, x, y, z = mgGetVtxCoord(t)
		mgSetVtxCoord(t, x, b, z)
		
def DuplicateAndRotateBillboard(rec):
	ok, bounds = mgGetBounds (rec)
	center = mgCoord3dDivide (mgCoord3dAdd (bounds.max, bounds.min), 2.0)
	paramBlock = mgGetParamBlock ("Duplicate") 
	mgParamSetDouble3 (paramBlock, "From Point", 0, 0, 0)
	mgParamSetDouble3 (paramBlock, "To Point", 0, 0, 0)
	mgExecute ("Duplicate", paramBlock)
	paramBlock = mgGetParamBlock ("Rotate About Point") 
	mgParamSetDouble3 (paramBlock, "Center Point", center.x, center.y, center.z) 
	mgParamSetDouble (paramBlock, "Angle", 90) 
	mgExecute ("Rotate About Point", paramBlock) 
	
for i in range (0, num):
	rec,m = mgGetNextRecInList (selectList)
	RotateBillboard(rec) 
	mgDeselectAll(db) 
	mgSelectOne (rec)
	mgSetAttList(rec, fltPolyDrawType, 1, fltPolyTemplate, 1) 
	AlignBillboard(rec) 
	DuplicateAndRotateBillboard(rec)

If I understand your question, you are trying to re-orient an arbitrary polygon such that it's normal is the Y axis. Instead of just rotating it around the Z axis, you might try using a Quad to Quad transformation or LCS transformation with translate (mgMatrixFormQuadToQuad or mgMatrixFormLCS).

Quick update... looks like there is a bug in mgMatrixFormLCS which stops it from working in OpenFlight Script properly. You can still use mgMatrixFormQuadToQuad.

Here is a script that uses mgMatrixFormQuadToQuad to re-orient an arbitrary polygon so it lies in the XZ plane. In other words after you run this script on a selected polygon, the normal of the polygon will align to the positive Y axis. You can use the function AlignToXZ in this script to replace your function RotateBillboard. If you do this, you no longer need your function AlignBillboard.

# this script will re-orient the selected polygons such that they lie within the XZ plane
# to run this script, select all of the polygons and run the script 

class undoData():
	def __init__(self):
		self.undoList = []
		
class undoVtxPos():
	def __init__(self, vtx):
		self.vtx = vtx
		b,self.x,self.y,self.z = mgGetVtxCoord (vtx)
		self.hasNormal,self.i,self.j,self.k = mgGetVtxNormal (vtx)
	def swap(self):
		b,x,y,z = mgGetVtxCoord (self.vtx)
		mgSetVtxCoord (self.vtx, self.x, self.y, self.z)
		self.x = x
		self.y = y
		self.z = z
		b,i,j,k = mgGetVtxNormal (self.vtx)
		if (self.hasNormal):
			mgSetVtxNormal (self.vtx, self.i, self.j, self.k)
		else:
			mgRemoveVtxNormal(self.vtx)
		self.hasNormal = b
		self.i = i
		self.j = j
		self.k = k

def RedoFunc (db, undoData):
	for i in undoData.undoList:
		i.swap()
	mgEditorAddUndoForRedo (UndoFunc, UndoCleanupFunc, undoData)

def UndoFunc (db, undoData):
	for i in undoData.undoList:
		i.swap()
	mgEditorAddRedo (RedoFunc, UndoCleanupFunc, undoData)

def UndoCleanupFunc (db, reason, undoData):
	pass

class AlignPolyToXZPlaneData():
	def __init__(self, db):
		self.undoData = undoData ()
	def appendUndoVtx(self, vtx):
		undoOne = undoVtxPos (vtx)
		self.undoData.undoList.append (undoOne)
	def appendUndoFace(self, face):
		vert = mgGetChild (face)
		while (vert):
			self.appendUndoVtx(vert)
			vert = mgGetNext(vert)

def SetVertexNormals(poly, dir):
	vert = mgGetChild (poly)
	while (vert): 
		mgSetVtxNormal (vert, dir.i, dir.j, dir.k)
		vert = mgGetNext(vert)

def TransformPoly(poly, matrix):
	vert = mgGetChild (poly)
	while (vert): 
		ok, x,y,z = mgGetVtxCoord (vert)
		pos = mgMakeCoord3d (x,y,z)
		newpos = mgCoord3dTransform (matrix, pos)
		mgSetVtxCoord (vert, newpos.x, newpos.y, newpos.z)
		vert = mgGetNext(vert)

def UnitVectorFromV1ToV2(v1,v2):
	c1 = mgcoord3d()
	c2 = mgcoord3d()
	b, c1.x,c1.y,c1.z = mgGetVtxCoord(v1)
	b, c2.x,c2.y,c2.z = mgGetVtxCoord(v2)
	return mgMakeUnitVectord (c1, c2)

def BestXAxis(rec):
	v1 = mgGetChild(rec)
	v2 = mgGetNext(v1)
	v3 = mgGetNext(v2)
	vec1 = UnitVectorFromV1ToV2(v1,v2)
	vec2 = UnitVectorFromV1ToV2(v2,v3)
	if (abs(vec2.k) < abs(vec1.k)):
		return vec2
	else:
		return vec1

def AlignToXZ(rec):
	norm = mgvectord()
	ok, norm.i,norm.j,norm.k = mgGetPolyNormal(rec)
	ok, box = mgGetBounds (rec)
	origin = mgBoxGetCenter (box)
	xAxis = BestXAxis(rec)
	yAxis = mgVectordCross(norm, xAxis)
	from2 = mgCoord3dMoveAlongVectord(origin, xAxis, 100)
	from3 = mgCoord3dMoveAlongVectord(from2, yAxis, 100)
	from4 = mgCoord3dMoveAlongVectord(origin, yAxis, 100)
	
	to2 = mgCoord3dMoveAlongVectord(origin, mgVectordNegativeXAxis(), 100)
	to3 = mgCoord3dMoveAlongVectord(to2, mgVectordZAxis(), 100)
	to4 = mgCoord3dMoveAlongVectord(origin, mgVectordZAxis(), 100)
		
	b, matrix = mgMatrixFormQuadToQuad(origin, from2, from3, from4, origin, to2, to3, to4)
	TransformPoly(rec, matrix)
	SetVertexNormals(rec, mgVectordYAxis())

def AlignPolyToXZPlane():
	toolName = "Align Poly To XZ Plane"
	
	editorContext = mgNewEditorContext (toolName)

	if (not editorContext): 
		mgSendMessage (MMSG_ERROR, "Failed creating Editor Context")
		return

	db = mgEditorGetDbRec (editorContext)
	
	selectList = mgGetSelectList (db)
	num = mgGetRecListCount (selectList)

	if (num == 0):
		mgSendMessage (MMSG_ERROR, "Nothing Selected")
	else:
		faceList = []

		for i in range (0, num):
			rec,m = mgGetNextRecInList (selectList)
			code = mgGetCode (rec)
			if (code == fltPolygon):
				faceList.append (rec)
							
		numFaces = len(faceList)
		if (numFaces > 0):
			data = AlignPolyToXZPlaneData (db)
			for face in faceList:
				data.appendUndoFace (face)
				AlignToXZ(face)			
			
			mgEditorAddUndo (editorContext, toolName,
									UndoFunc, UndoCleanupFunc, data.undoData) 
		else:
			mgSendMessage (MMSG_ERROR, "Select at face level.")

AlignPolyToXZPlane ()

 


Oh, the script I posted also shows you how to implement Undo/Redo in your scripts if that is something you're interested in adding to yours.

Also it works best on rectangular polygons. If you try it with other shapes, the polygon will face the right direction (Y) but may be rotated about the Y axis in a way you don't expect. You can tweak this by changing the function BestXAxis to return better choices for the Quad To Quad orientation.

Login to post a comment