Notifications
Clear all

[Closed] Viewport Statistics Improvement

Using the dennisT version with support for objects arrays, gives 0.017s for 10212 objects and a total of 49017600 tris, using GetTriMeshFaceCount gives 0.539s. No type check is done on the object.


def_visible_primitive_debug_ok(numVertsAndTris, "numVertsAndTris");
Value* numVertsAndTris_cf(Value** arg_list, int count)
{
	check_arg_count(numVertsAndTris, 1, count);
	auto arr = static_cast<Array*>(arg_list[0]);
	if(!is_array(arr))
	{
		arr = new Array(1);
		arr->append(arg_list[0]);
	}
	one_typed_value_local(Array *result);
	vl.result = new Array(2);
	ulong vertices = 0;
	ulong faces = 0;
	for (int i = 1; i <= arr->size; i++)
	{
		INode* node = arr->get(i)->to_node();
		TimeValue t = MAXScript_time();
		const ObjectState& os = node->EvalWorldState(t); // Get the object that is referenced at the time
		Object* ob = os.obj;
		if (ob && (ob->SuperClassID() == GEOMOBJECT_CLASS_ID || ob->SuperClassID() == SHAPE_CLASS_ID))
		{
			BOOL needDel;
			NullView nullView;	// Set up a null viewport so that we get the right mesh..
			Mesh *mesh = static_cast<GeomObject*>(ob)->GetRenderMesh(t, node, nullView, needDel); // Type cast to a geometric object and then retrieve
			int numv = mesh->numVerts;
			int numf = mesh->numFaces;
			if (needDel) delete mesh;
			vertices += numv;
			faces += numf;
		}
	}
	vl.result->append(new Integer(vertices));
	vl.result->append(new Integer(faces));
	return_value(vl.result);
}

and finally! universal GET_VERTS_AND_FACES method:

for some situations 200! times faster then buil-in GetTriMeshFaceCount

def_visible_primitive(getNumVertsAndFaces, "getNumVertsAndFaces");
Value* getNumVertsAndFaces_cf(Value** arg_list, int count)
{
	check_arg_count_with_keys(getNumVertsAndFaces, 1, count);
	INode* node = arg_list[0]->to_node();
	TimeValue t = MAXScript_time();
 	Object* obj = node->GetObjectRef();

	int id = -3;
	if (obj)
	{
		int numv, numf;

		TimeValue t = MAXScript_time();
		Object* osobj = obj->Eval(t).obj;
		
		BOOL renderMesh = key_arg_or_default(render, &false_value)->to_bool();
		BOOL faces = key_arg_or_default(faces, &true_value)->to_bool();
		
		if (renderMesh)
		{
			if ((id = 1, osobj->SuperClassID() == GEOMOBJECT_CLASS_ID) || (id = 2, osobj->ClassID() == EPOLYOBJ_CLASS_ID) || (id = 3, osobj->SuperClassID() == SHAPE_CLASS_ID))
			{
				BOOL needDel;
				NullView nullView;	// Set up a null viewport so that we get the right mesh..
				Mesh *mesh = ((GeomObject*)osobj)->GetRenderMesh(t, node, nullView, needDel); // Type cast to a geometric object and then retriev
				numv = mesh->numVerts;
				numf = mesh->numFaces;
				if (needDel) delete mesh;
				return new Point3Value(Point3(numv, numf, id));
			}
		}

		if (id = 1, osobj->ClassID() == Class_ID(EDITTRIOBJ_CLASS_ID, 0))
		{
			Mesh* mesh = &((TriObject*)osobj)->mesh; 
			numv = mesh->numVerts;
			numf = mesh->numFaces;
			return new Point3Value(Point3(numv, numf, id));
		}

		if ((id = -2, osobj->ClassID() == Class_ID(POLYOBJ_CLASS_ID, 0)) || (id = 2, osobj->ClassID() == EPOLYOBJ_CLASS_ID))
		{ 
			if (faces)
			{
				MNMesh* mesh = &((PolyObject*)osobj)->mm;
				numv = mesh->numv;
				numf = mesh->TriNum();
				return new Point3Value(Point3(numv, numf, id));
			}
			osobj->PolygonCount(t, numf, numv);
			return new Point3Value(Point3(numv, numf, id));
		}

		GetTriMeshFaceCount (t, osobj, numf, numv);
		return new Point3Value(Point3(numv, numf, 0));
	}
	return new Point3Value(Point3(-1,-1,id));
}

Ok updated code with local bounding box and using the getNumVertsAndFaces if available, with fallback to GetTriMeshFaceCount when not available.


/*
Advanced Viewport Stats
(c) 2015 Dave Wortley & Daniel Santana
*/
try(unregisterRedrawViewsCallback drawStatsInfo.drawObjNameInfo)catch()
gc light:true
global drawStatsInfo
try(dw_ds_callback.enabled = false)catch()
dw_ds_callback = undefined
global dw_ds_callback	

fn dw_ds_formatNumber val =
(
	local l = val.count
	while (l - 2) > 1 do (val = replace val (l - 2) 0 ","; l -= 3)
	val
)


struct drawStatsInfo
(
	lastObjectHandle,
	lastObjectName,
	lastObjectLayer,
	lastObjectClass, 
	lastObjectSuperClass, 
	lastObjectParent,
	lastObjectChilds,
	lastObjectMat,
	lastObjectFaceCount,
	lastObjectVertCount,
	lastObjectSize,
	lastObjectDebug = "",
	totalCounts,
	totalFaceCount,
	totalVertCount,
	totalDebug = "",
	useDlx = getNumVertsAndFaces != undefined,
	updateObjectSizeDebug ="
",
	disableTotalCount = true,
	debug = false,
	cfg = "$plugcfg/drawstatsinfo.ini",
	ver = 0.8,
	enabled = false,
	
	fn updateObjectStats force:false =
	(
		st = timestamp()
		if selection.count != 1 do return()
		obj = selection[1]
		if force == false and drawStatsInfo.lastObjectHandle == GetHandleByAnim obj do return()
		drawStatsInfo.lastObjectHandle = GetHandleByAnim obj
		drawStatsInfo.lastObjectName = obj.name
		drawStatsInfo.lastObjectClass = (classof obj) as string
		drawStatsInfo.lastObjectSuperClass = (superclassof obj) as string
		drawStatsInfo.lastObjectLayer = obj.layer.name
		drawStatsInfo.lastObjectParent = (if obj.parent == undefined then "sceneRootNode" else obj.parent.name)
		drawStatsInfo.lastObjectChilds = dw_ds_formatNumber (obj.children.count as string)
		drawStatsInfo.lastObjectMat = (if obj.material != undefined then classof obj.material as string + ": " + obj.material.name as string else "Unassigned")
		drawStatsInfo.lastObjectFaceCount = (if superclassof obj == geometryclass then "
Faces: " + (dw_ds_formatNumber (obj.mesh.faces.count as string)) else "")
		drawStatsInfo.lastObjectVertCount = (if superclassof obj == geometryclass then "
Vertices: " + (dw_ds_formatNumber (obj.mesh.vertices.count as string)) else "")
		bbox = nodeLocalBoundingBox obj
		s = bbox[2] - bbox[1]
		f = units.formatValue
		drawStatsInfo.lastObjectSize = "
Size: X=" + f(s.x) + " Y=" + f(s.y) + " Z:" + f(s.z)
		--if drawStatsInfo.debug do 
		(
			ss = stringstream ""
			format "

[DEBUG]
updateObject took: %s
" ((timestamp()-st)/1000.0) to:ss
			drawStatsInfo.lastObjectDebug = ss as string
			--format (ss as string)
		)
	),
	
	fn drawObjNameInfo =
	(
		try
		(
		textColour = (colorMan.getColor #activeViewportBorder) * 255.0
		textColour = color 196 196 196
			
		--tt = fileProperties.getItems "Mesh Totals"
		--st = fileProperties.getItems "Scene Totals"
		drawStatsInfo.updateObjectStats()
		dtext = case of
		(
			(selection.count == 0): (
				""
			)
			(selection.count == 1): (		
				"Object: " + drawStatsInfo.lastObjectName + \
				"
Class: " + drawStatsInfo.lastObjectClass + "  (" + drawStatsInfo.lastObjectSuperClass + ")" + \
				"
Layer: " + drawStatsInfo.lastObjectLayer + \
				"
Parent: " + drawStatsInfo.lastObjectParent + \
				"
Children: " + drawStatsInfo.lastObjectChilds + \
				"
Material: " + drawStatsInfo.lastObjectMat + \
				drawStatsInfo.lastObjectFaceCount + \
				drawStatsInfo.lastObjectVertCount + \
				drawStatsInfo.lastObjectSize + \
				(if drawStatsInfo.debug then drawStatsInfo.lastObjectDebug else "") + \
				(if drawStatsInfo.debug then drawStatsInfo.totalDebug else "") + \
				(if drawStatsInfo.debug then drawStatsInfo.updateObjectSizeDebug as string else "") + \
				(if not drawStatsInfo.disableTotalCount then "

Total Faces: "  + drawStatsInfo.totalFaceCount else "
") + \
				--"
Total Vertices: "  + drawStatsInfo.totalVertCount  + \
				"
Scene Object Count: "  + objects.count as string + \
				"
FPS: " + viewport.getFPS() as string
				
			)
			(selection.count > 1): (
				types = #()
				for o in selection do appendifunique types ((classof o))
				
				(selection.count as string) + " Objects Selected" + \
				"
" + (types.count as string) + " classes " + (types as string) + \
				"
Geometry: " + (for o in selection where superclassof o == geometryclass collect o).count as string + \
				"
Shapes: " + (for o in selection where superclassof o == shape collect o).count as string + \
				"
Lights: " + (for o in selection where superclassof o == light collect o).count as string + \
				"
Cameras: " + (for o in selection where superclassof o == camera collect o).count as string + \
				"
Helpers: " + (for o in selection where superclassof o == helper collect o).count as string + \
				"
Space Warps: " + (for o in selection where superclassof o == spacewarpObject collect o).count as string + \
				"
Particle Systems: " + (for o in selection where superclassof o == particlesystem collect o).count as string + \
				"
Bone Objects: " + (for o in selection where superclassof o == geometryclass collect o).count as string
				
				
			)
		)
		
		
		local pos = [10,38]
			
		gw.setTransform(Matrix3 1)
		gw.wText [pos.x, pos.y, 0] dtext color:textColour
		)
		catch(print ("issue" + (getCurrentException())))
	),
	fn loadSettings =
	(
		if not doesfileexist cfg do return false
		if hasINISetting  drawStatsInfo.cfg "Options" "Debug" do
			drawStatsInfo.debug = getIniSetting drawStatsInfo.cfg "Options" "Debug" as BooleanClass
		if hasINISetting  drawStatsInfo.cfg "Options" "TotalCount" do
			drawStatsInfo.disableTotalCount = getIniSetting drawStatsInfo.cfg "Options" "TotalCount" as BooleanClass
		if hasINISetting  drawStatsInfo.cfg "Options" "Enabled" do
			drawStatsInfo.enabled = getIniSetting drawStatsInfo.cfg "Options" "Enabled" as BooleanClass
	),
	fn show =
	(
		global dw_ds_DrawStatsInfoUI
		try(destroydialog dw_ds_DrawStatsInfoUI)catch()
		rollout dw_ds_DrawStatsInfoUI "Viewport Stat Options" width:245
		(
			group "Options"
			(
				checkbox ckbDebug "Debug mode" checked:drawStatsInfo.debug
				on ckbDebug changed val do drawStatsInfo.debug = ckbDebug.checked
				checkbox ckbTotal "Enable total face count" checked:(not drawStatsInfo.disableTotalCount)
			)
			checkbutton ckbEnable "Enabled" checked:drawStatsInfo.enabled
			button btnClose "Close" align:#right
			on btnClose pressed do try(destroydialog dw_ds_DrawStatsInfoUI)catch()
			label lblVer "Version: 0.000" align:#left
			hyperLink lblAbout1 "© 2015 Dave Wortley" address:"http://www.taylorjames.com/" across:3 align:#left
			label lblAboutM "and" align:#middle offset:[15,0]
			hyperLink lblAbout2 "Daniel Santana" address:"http://www.ycdivfx.com/" align:#left
			
			on dw_ds_DrawStatsInfoUI open do lblVer.text = "Version: " + drawStatsInfo.ver as string
			
			on dw_ds_DrawStatsInfoUI close do
			(
				setIniSetting drawStatsInfo.cfg "Options" "Debug" (drawStatsInfo.debug as string)
				setIniSetting drawStatsInfo.cfg "Options" "TotalCount" (drawStatsInfo.disableTotalCount as string)
				setIniSetting drawStatsInfo.cfg "Options" "Enabled" (drawStatsInfo.enabled as string)
			)
			
			on ckbTotal changed val do
			(
				drawStatsInfo.disableTotalCount = not val
				if not drawStatsInfo.disableTotalCount do ::dw_ds_updateTotals ok ok
			)
			
			on ckbEnable changed val do
			(
				::drawStatsInfo.enabled = val
				::drawStatsInfo.enable()
			)

		)
		createdialog dw_ds_DrawStatsInfoUI
	),
	fn createMacros =
	(
		files = getfiles (pathConfig.appendPath (getdir #usermacros) "*.mcr")
		macr = for f in files where findstring (tolower (getfilenamefile f)) "dwdsviewportstats" != undefined collect f
		if macr.count == 0 do
		(
			macros.new "Views" "Macro_DwDsViewportStats" "DrawStatsInfoUI" "DrawStatsInfoUI" "
on execute do
(
::drawStatsInfo.show();
)
"
			format "DrawStatsInfo Macro created.
"
		)
	),
	fn enable =
	(
		try(unregisterRedrawViewsCallback drawStatsInfo.drawObjNameInfo)catch()
		if drawStatsInfo.enabled do registerRedrawViewsCallback (drawStatsInfo.drawObjNameInfo)
	),
	fn setup =
	(
		drawStatsInfo.loadSettings()
		drawStatsInfo.createMacros()
		drawStatsInfo.enable()
	)--,
	--init = setup()
)
if not IsNetServer() do
(
drawStatsInfo = drawStatsInfo()
drawStatsInfo.setup()
global dw_ds_updateTotals
global dw_ds_updateObject
global dw_ds_updateObjectSize

fn dw_ds_updateTotals evt nds =
(
	try(
	st = timestamp()
	drawStatsInfo.totalCounts = objects.count as string
	--tv = 0
	tf = 0
	--for o in objects where superclassof o == geometryclass do (try(tv += o.mesh.numvertices)catch())
	if not drawStatsInfo.disableTotalCount and drawStatsInfo.useDlx then tf = (getnumVertsAndFaces (objects as array))[2]
	else if not drawStatsInfo.disableTotalCount do for o in objects where superclassof o == geometryclass do (try(tf += (GetTriMeshFaceCount o)[1]/*o.mesh.numfaces*/)catch())
	drawStatsInfo.totalFaceCount = dw_ds_formatNumber (tf as string)
	--drawStatsInfo.totalVertCount = tv as string
	drawStatsInfo.updateObjectStats force:true
	--if drawStatsInfo.debug do
	(
		ss = stringstream ""
		format "updateTotals took: %s" ((timestamp()-st)/1000.0) to:ss
		drawStatsInfo.totalDebug = ss as string
		--format (ss as string)
	)
	)catch()
)
fn dw_ds_updateObject evt nds =
(
	try
	(
		drawStatsInfo.updateObjectStats force:true
	)
	catch()
)
fn dw_ds_updateObjectSize evt nds =
(
	try
	(
		st = timestamp()
		if selection.count != 1 do return()
		obj = selection[1]
		s = obj.max - obj.min
		f = units.formatValue
		drawStatsInfo.lastObjectSize = "
Size: X=" + f(s.x) + " Y=" + f(s.y) + " Z:" + f(s.z)
		--if drawStatsInfo.debug do
		(
			ss = stringstream ""
			format "
updateObjectSize took: %s" ((timestamp()-st)/1000.0) to:ss
			drawStatsInfo.updateObjectSizeDebug = ss as string
			--format (ss as string)
		)
	)
	catch(print "ooops")
)
dw_ds_updateTotals undefined undefined
dw_ds_callback = NodeEventCallback enabled:true mouseup:true delay:50 topologyChanged:(dw_ds_updateTotals) added:(dw_ds_updateTotals) deleted:(dw_ds_updateTotals) materialStructured:(dw_ds_updateObject) nameChanged:(dw_ds_updateObject) geometryChanged:(dw_ds_updateObjectSize) controllerOtherEvent:(dw_ds_updateObjectSize)
)

modified dennisT extension code (returned id only makes sense for single objects)


def_visible_primitive_debug_ok(getNumVertsAndFaces, "getNumVertsAndFaces");
Value* getNumVertsAndFaces_cf(Value** arg_list, int count)
{
	check_arg_count_with_keys(numVertsAndTris, 1, count);
	auto arr = static_cast<Array*>(arg_list[0]);
	if(!is_array(arr))
	{
		arr = new Array(1);
		arr->append(arg_list[0]);
	}
	
	int id = -3;
	one_typed_value_local(Array *result);
	vl.result = new Array(3);
	ulong total_vertices = 0;
	ulong total_faces = 0;
	int numf, numv = 0;
	BOOL renderMesh = key_arg_or_default(render, &false_value)->to_bool();
	BOOL faces = key_arg_or_default(faces, &true_value)->to_bool();
	
	for (int i = 1; i <= arr->size; i++)
	{
		auto node = arr->get(i)->to_node();
		auto t = MAXScript_time();
		const auto& os = node->EvalWorldState(t); // Get the object that is referenced at the time
		auto ob = os.obj;
		if (!ob) continue;
		if (renderMesh)
		{
			if ((id = 1, ob->SuperClassID() == GEOMOBJECT_CLASS_ID) || (id = 2, ob->ClassID() == EPOLYOBJ_CLASS_ID) || (id = 3, ob->SuperClassID() == SHAPE_CLASS_ID))
			{
				BOOL needDel;
				NullView nullView;	// Set up a null viewport so that we get the right mesh..
				auto mesh = static_cast<GeomObject*>(ob)->GetRenderMesh(t, node, nullView, needDel); // Type cast to a geometric object and then retriev
				total_vertices += mesh->numVerts;
				total_faces += mesh->numFaces;
				if (needDel) delete mesh;
				continue;
			}
		}
		if (id = 1, ob->ClassID() == Class_ID(EDITTRIOBJ_CLASS_ID, 0))
		{
			auto mesh = &static_cast<TriObject*>(ob)->mesh;
			total_vertices += mesh->numVerts;
			total_faces += mesh->numFaces;
			continue;
		}
		if ((id = -2, ob->ClassID() == Class_ID(POLYOBJ_CLASS_ID, 0)) || (id = 2, ob->ClassID() == EPOLYOBJ_CLASS_ID))
		{
			if (faces)
			{
				auto mesh = &static_cast<PolyObject*>(ob)->mm;
				total_vertices += mesh->numv;
				total_faces += mesh->TriNum();
				continue;
			}
			ob->PolygonCount(t, numf, numv);
			total_vertices += numv;
			total_faces += numf;
			continue;
		}
		GetTriMeshFaceCount(t, ob, numf, numv);
		total_vertices += numv;
		total_faces += numf;
	}
	vl.result->append(new Integer(total_vertices));
	vl.result->append(new Integer(total_faces));
	vl.result->append(new Integer(id));
	return_value(vl.result);
}

Page 2 / 2