Notifications
Clear all

[Closed] Writing to Realflow Bin Mesh – UVWs No Worky

Hey all,

I’ve been using some code from martin briedt’s site (that used some original code by Ben Lipman so thanks to you both) to write meshes to the bin mesh format. This sort of works, the only thing I cant get working is the UVWs. I’ve tried a couple of methods in case I was getting the faces wrong or something but cant see why. I’ve looked at the RF file specification for bin mesh too, I’m pretty sure it is correct, but Im supplying the wrong data from the UVs. I know it must be possible as the native RF exporter in max works with UVWs, its just not customisable enough for my purposes. Any thoughts as to what might be wrong?

	---------------------------
--Convert obj sequence to to Realflowbin
--based on .bin writer by Ben Lipman
--
--Martin Breidt 
--www.breidt.net/scripts/
--06/18/2005
--early test version. no interface
---------------------------

--
-- This code is released under "Quote ware" license:
--      If you use this tool in a production environment with a group of more than two people,
--      or have used it in the past under such conditions, then you are obliged to tell 
--      me (martin@breidt.net) about it and allow me to list that project title and your 
--      company name as a reference on my website  http://scripts.breidt.net 

	fn writeBin4 tmesh fileName Zup:true useTx:true useVelocity:false = (

	tmesh = snapshotAsMesh obj
	num_verts = tmesh.numverts 
	num_faces = tmesh.numfaces
	f=fopen fileName "wb"
	--Prefixes
	WriteShort f 0xDADA #unsigned
	WriteShort f 0xDADA #unsigned
	version = 4
	Writelong f version #unsigned
	WriteShort f 0xCCCC #unsigned
	WriteShort f 0xCCCC #unsigned
	
	--vertices
	WriteLong f num_verts  #signed --[int] number of vertices
	for v = 1 to num_verts do
	(
		vert = getVert tmesh v
		WriteFloat f vert.x
		if Zup then WriteFloat f vert.z else WriteFloat f vert.y
		if Zup then WriteFloat f vert.y else WriteFloat f vert.z
	)
		
	--faces
	WriteLong f num_faces #signed --[int] number of faces
	
	
	for fc = 1 to num_faces do
	(
		face = getFace tmesh fc
		WriteLong f ((face[1] as integer)-1) #signed
		WriteLong f ((face[2] as integer)-1) #signed
		WriteLong f ((face[3] as integer)-1) #signed
	)
	
	
		--texture coords
	if (useTx == true) do 
	(
		
		
		print "writing UVW"
		
		--WriteLong f 0xCCCCCC00 #unsigned	
		WriteShort f 0xCC00 #unsigned
		WriteShort f 0xCCCC #unsigned		
		
		flCount = 1
		WriteLong f flCount #signed --[int] number of fluids
		
		
		-- this method not great either, trying to get the texture vert order from the face
		/*
		
		for face = 1 to tmesh.numfaces do
		(
			texFace = getTVFace tmesh face	
			tarray = #(texFace.x, texFace.y, texFace.z)
			
			UVWVerts = for tx in tarray collect			
			(
				tvert = getTVert tmesh tx
			)			
						
			for i in uvwverts do 
			(
		--	WriteFloat f 0.0
					
				tx = ((i.x as float))		
				ty = ((i.z as float))
				tz = ((i.y as float))	
				format "uvwverts % % % % % %
" obj.name tx ty tz texFace tarray
 			
				WriteFloat f tx		
				WriteFloat f ty
				WriteFloat f tz			
			)
				
			
		)	
		*/
 		
		--for tx = 1 to num_verts do
		for tx = 1 to (getNumTVerts tmesh) do
		(
			tvert = getTVert tmesh tx
			
		        WriteFloat f tvert.x 
			if Zup then WriteFloat f tvert.z else WriteFloat f tvert.y
			if Zup then WriteFloat f tvert.y else WriteFloat f tvert.z
			
		)
		
	)

	--velocities
	if (useVelocity == true) do
	(
		WriteShort f 0xCC11 #unsigned
		WriteShort f 0xCCCC #unsigned
		for vel = 1 to num_verts do
		(
			velocity = 0.0
			WriteFloat f velocity
			WriteFloat f velocity
			WriteFloat f velocity 
		)
	)	
		
	--end code	
	WriteShort f 0xDEDE #unsigned
	WriteShort f 0xDEDE #unsigned
	
	fclose f
	
)
11 Replies

Heh, thought this one might be a bit of a fringe ask. I’ll have to see exactly where it’s placing the UVW verts and see if there’s a common relationship in the screwyness.

i guess this methodology will give you what you want. you can dump the normal collection which is the big slow down.

it’s may be overkill and i’ve no way of testing but something like…


     --*****************************************************************************************
 
 fn hashPoint3 n = 
 (
 	bit.xor (bit.xor ((n.x  * 73856093) as integer) ((n.y  * 19349663) as integer )) ((n.z  * 83492791) as integer);
 )
 
 --*****************************************************************************************
 
 struct vertex
 (
 	pos,
 	uvw,
 	hash,
 
 	fn hashit =	
 	(
 		hash = bit.xor ((hashPoint3 pos) * 93944371) ((hashPoint3 uvw) * 19393541);
 	),
 	fn printf = (format "% % %
" pos uvw hash)
 )	
 
 --**************************************************************************************
 -- sort and search compares,
 
 fn compareVertexfn i j values: =
 (
 	v1 = values[i].hash; 
 	v2 = values[j].hash;
 	if v1 < v2 then 1 else if v1 > v2 then -1 else 0;
 )	
 
 --***********************************************************************************
 -- compares the raw against the optimized
 
 fn compareVertexBinfn i j raw: opt: =
 (
 	v1 = raw[i].hash; 
 	v2 = opt[j].hash;
 	if v1 < v2 then 1 else if v1 > v2 then -1 else 0;
 )	
 
 --***********************************************************************************
 -- collects all the unique material IDs within a mesh
 
 fn GetMeshMatIDs mObj =
 (
 	matid = #();
 	for f in mObj.faces as bitarray	do appendifunique matid (getfaceMatID mObj f);
 	matid;
 )	
 
 --***********************************************************************************
 -- collects all the faces with the same material ID
 
 fn GetFacesWithMatID mObj matid =
 (
 	faces = #{}
 	faces.count = mObj.numfaces;
 	for f in mObj.faces as bitarray do faces[f] = ((getfaceMatID mObj f) == matid);
 	faces;
 )	
 
 --**************************************************************************************
 -- generate the worst case scenerio, 3 verts per face every face
 
 fn CollectRawVerts msh faces tm =
 (
 	verts = #();
 	verts.count = faces.numberSet * 3;
 	vi = 1;
 	for f in faces do
 	(
 		geo_verts = getface msh f;
 		tex_verts = getTVFace msh f;
 		for v = 1 to 3 do
 		(
 			gvert = (getvert msh geo_verts[v]) * tm;
 			tvert = getTVert msh tex_verts[v];
 			
 -- create the vertex and generate it's hash
 			
 			verts[vi] = vertex gvert tvert;
 			verts[vi].hashit();
 			vi += 1;
 		)	
 	)	
 	verts;
 )
 
 --**************************************************************************************
 -- create the optimized version of the raw verts
 
 fn OptimizeVerts raw =
 (
 	numverts = raw.count;
 	copylist = #();
 	copylist.count = numverts;
 	
 -- create the index array	
 	
 	rawi = #{1..numverts} as array;
 
 -- sort int indexes based on the raw data	
 	
 	qsort rawi compareVertexfn values:raw;
 	
 -- collect the indices of all the "unique" verts	
 	
 	copycount = 0;
 	for i = 1 to numverts do 
 	(
 		copylist[i] = -1;	
 		if i == bsearch i rawi compareVertexfn values:raw then
 		(	
 			copycount += 1;
 			copylist[copycount] = i;
 		)	
 	)	
 	
 -- extract them from the raw verts	
 	
 	optimized = #();
 	optimized.count = copycount;
 	copycount = 1;
 	for i = 1 to numverts do 
 	(
 		if i == copylist[copycount] then
 		(	
 			optimized[copycount] = raw[i];
 			copycount += 1;
 		)
 	)
 	optimized;	
 )	
 
 --***********************************************************************************
 -- reconstruct the face indexing, the raw verts are in the correct order so we find where
 -- each raw vert is in the optimised array and thats our index.
 
 fn CreateFaces raw optimized =
 (
 	opti = #{1..optimized.count} as array;
 	qsort opti compareVertexfn values:optimized;
 	for i = 1 to raw.count collect (bsearch i opti compareVertexBinfn raw:raw opt:optimized;)
 )
 
 --***********************************************************************************
 
 fn ExportAsRealFlowBinMesh objName verts faces flipYZ =
 (	
 	numverts = verts.count;
 	numindices = faces.count;
 	fname = (GetDir #export) + "/" + objName + ".bin"; -- i don't know the file type :/
 	
 	fstream = fopen fname "wb";
 	if fstream != undefined then
 	(	
 		WriteLong fstream 0xDADADADA #unsigned;
 		WriteLong fstream 4 #unsigned;
 		WriteLong fstream 0xCCCCCCCC #unsigned;
 
 		for i = 1 to numverts do 
 		(
 			WriteFloat fstream verts[i].pos.x;
 			if flipYZ then
 			(
 				WriteFloat fstream verts[i].pos.z;
 				WriteFloat fstream verts[i].pos.y;
 			)	
 			else
 			(
 				WriteFloat fstream verts[i].pos.y;
 				WriteFloat fstream verts[i].pos.z;
 			)
 		)
 		for i = 1 to numindices do 	WriteLong fstream faces[i] #unsigned;
 			
 		numfluids = 1;
 		WriteLong fstream 0xCCCCCC00 #unsigned;
 		WriteLong fstream numfluids #unsigned;
 		
 		for i = 1 to numverts do 
 		(
 			for j = 1 to numfluids do  WriteFloat fstream 1.0;	
 			
 			WriteFloat fstream verts[i].uvw.x;
 			WriteFloat fstream verts[i].uvw.y;
 			WriteFloat fstream verts[i].uvw.z;
 		)
 		-- no velocities
 		
 		WriteLong fstream 0xDEDEDEDE #unsigned;
 		
 
 		fclose fstream;
 	)		
 	
 )	
 	
 --***********************************************************************************
 -- main export loop
 
 fn ExportMesh mObj = if canConvertTo mObj Editable_Mesh then
 (
 -- grab the mesh	
 	
 	msh = snapshot mObj;
 	
 -- compute the inverse tm so we can get positions in local space	
 	
 	localTM = (inverse (msh.transform));
 		
 -- get the number of mat id's	
 	
 	matids = GetMeshMatIDs msh;
 	
 	for m in matids do
 	(
 		faces = GetFacesWithMatID msh m;
 		raw = CollectRawVerts msh faces localTM;
 		opt = OptimizeVerts raw;
 		indices = CreateFaces raw opt;
 		ExportAsRealFlowBinMesh (mObj.name + "_" + m as string) opt indices true;
 	)	
 	delete msh;
 )	
 	
 --**************************************************************************************
 
 delete objects
 sph = sphere segs:32  mapcoords:on pos:[75,0,0] 
 clearlistener() 
 ts = timestamp();
 ExportMesh sph;
 print ((timestamp() - ts) * 0.001);
     

Hey Klunk,

Thanks a million for weighing in on this. Unfortunately, your attempt doesn’t appear to write the bin file correctly. But I thank you as it has given me an alternate workflow for extracting the the texture vertices than I was currently using. I am going to see if I can use the functions you have provided to yield any further results. The original code was working, with the exception of what looked like the UVW vertex order being wrong.

The bin format specification is as follows, so there were a couple of lines missing that may have been important

Thanks again for your input.


This is the specification for RealFlow BIN mesh files 

(Don't confuse with BIN particle files) 

[bool] is 1 byte long. 

(Begin of file) 

[unsigned int] ; ID code = 0xDADADADA 
[unsigned int] ; version = 4 
[unsigned int] ; geometry chunk code = 0xCCCCCCCC (*) 
[int] ; number of vertices 

	loop for [number of vertices]
		[float] ; X coordinate 
		[float] ; Y coordinate 
		[float] ; Z coordinate
	endloop 

[int] ; number of faces 

	loop for [number of faces] 
		[int] ; vertex index 
		[int] ; vertex index 
		[int] ; vertex index
	endloop 

[unsigned int] ; texture chunk code = 0xCCCCCC00 (**) 
[int] ; number of fluids 

loop for [number of vertices] 
	loop for [number of fluids-1] ; version>=3 () 
		[float] ; texture weight () 
	endloop 


	[float] ; X texture coordinate 
	[float] ; Y texture coordinate 
	[float] ; Z texture coordinate

 endloop 

[unsigned int] ; velocity chunk code = 0xCCCCCC11 (**) 

loop for [number of vertices] 
	[float] ; X vertex velocity 
	[float] ; Y vertex velocity 
	[float] ; Z vertex velocity
endloop 

[unsigned int] ; code = 0xDEDEDEDE (end of file mark) 
(End of file) 

Notes: 

(*) geometry chunk always exists 

(**) chunk may not be available 

(***) Weight information stores, for every mesh vertex, the contribution of each fluid.
All the contributions sum 1.0 

In version>=3 the weight information stores (number of fluids – 1) values for 
every mesh vertex. Therefore, for just one fluid, the weights are not stored. For 
two fluids, we store just one value, since the other value is calculated as (1.0 –
value). And so on for more fluids. 

For compatibility purposes, version<3 stored (number of fluids) values for every

mesh vertex. 




sorry, Pete… i can’t really understand the problem. the code that you showed above is absolutely correct (actually both of them). as i understood some built-in max plugin doesn’t read it right. correct? where can i get a definition of the Bin Mesh format?

this was the one I was using

there was a error in the code writing the fluid weight try this, have added the option of velocity on the vertex color (though editable normals would probably make more sense and easier to author ).


  --*****************************************************************************************
  
  fn hashPoint3 n = 
  (
  	bit.xor (bit.xor ((n.x * 73856093) as integer) ((n.y * 19349663) as integer)) ((n.z * 83492791) as integer);
  )
  
  --*****************************************************************************************
  
  struct vertex
  (
  	pos,
  	uvw,
  	vel,
  	hash,
  
  	fn hashit =	
  	(
  		hash = bit.xor (bit.xor ((hashPoint3 pos) * 93944371) ((hashPoint3 uvw) * 19393541)) ((hashPoint3 vel) * 82895123);
  	),
  	fn printf = (format "% % %
" pos uvw hash)
  )	
  
  --**************************************************************************************
  -- sort and search compares,
  
  fn compareVertexfn i j values: =
  (
  	v1 = values[i].hash; 
  	v2 = values[j].hash;
  	if v1 < v2 then 1 else if v1 > v2 then -1 else 0;
  )	
  
  --***********************************************************************************
  -- compares the raw against the optimized
  
  fn compareVertexBinfn i j raw: opt: =
  (
  	v1 = raw[i].hash; 
  	v2 = opt[j].hash;
  	if v1 < v2 then 1 else if v1 > v2 then -1 else 0;
  )	
  
  --***********************************************************************************
  -- collects all the unique material IDs within a mesh
  
  fn GetMeshMatIDs mObj =
  (
  	matid = #();
  	for f in mObj.faces as bitarray	do appendifunique matid (getfaceMatID mObj f);
  	matid;
  )	
  
  --***********************************************************************************
  -- collects all the faces with the same material ID
  
  fn GetFacesWithMatID mObj matid =
  (
  	faces = #{}
  	faces.count = mObj.numfaces;
  	for f in mObj.faces as bitarray do faces[f] = ((getfaceMatID mObj f) == matid);
  	faces;
  )	
  
  --**************************************************************************************
  -- generate the worst case scenerio, 3 verts per face every face
  
  fn CollectRawVerts msh faces tm =
  (
  	verts = #();
  	verts.count = faces.numberSet * 3;
  	vi = 1;
  	
  	getvel = meshop.getMapSupport msh 0; 
  	
  	for f in faces do
  	(
  		geo_verts = getface msh f;
  		tex_verts = getTVFace msh f;
  		if getvel then vel_verts = meshop.getmapface msh 0 f;
  		for v = 1 to 3 do
  		(
  			gvert = (getvert msh geo_verts[v]) * tm;
  			tvert = getTVert msh tex_verts[v];
  			vvert = [1,1,1];
  			if getvel then vvert = meshop.getmapface msh 0 f;
  			
  -- create the vertex and generate it's hash
  			
  			verts[vi] = vertex gvert tvert vvert;
  			verts[vi].hashit();
  			vi += 1;
  		)	
  	)	
  	verts;
  )
  
  --**************************************************************************************
  -- create the optimized version of the raw verts
  
  fn OptimizeVerts raw =
  (
  	numverts = raw.count;
  	copylist = #();
  	copylist.count = numverts;
  	
  -- create the index array	
  	
  	rawi = #{1..numverts} as array;
  
  -- sort int indexes based on the raw data	
  	
  	qsort rawi compareVertexfn values:raw;
  	
  -- collect the indices of all the "unique" verts	
  	
  	copycount = 0;
  	for i = 1 to numverts do 
  	(
  		copylist[i] = -1;	
  		if i == bsearch i rawi compareVertexfn values:raw then
  		(	
  			copycount += 1;
  			copylist[copycount] = i;
  		)	
  	)	
  	
  -- extract them from the raw verts	
  	
  	optimized = #();
  	optimized.count = copycount;
  	copycount = 1;
  	for i = 1 to numverts do 
  	(
  		if i == copylist[copycount] then
  		(	
  			optimized[copycount] = raw[i];
  			copycount += 1;
  		)
  	)
  	optimized;	
  )	
  
  --***********************************************************************************
  -- reconstruct the face indexing, the raw verts are in the correct order so we find where
  -- each raw vert is in the optimised array and thats our index.
  
  fn CreateFaces raw optimized =
  (
  	opti = #{1..optimized.count} as array;
  	qsort opti compareVertexfn values:optimized;
  	for i = 1 to raw.count collect (bsearch i opti compareVertexBinfn raw:raw opt:optimized;)
  )
  
  --***********************************************************************************
  
  fn ExportAsRealFlowBinMesh objName verts faces flipYZ writeUVW writeVel =
  (	
  	numverts = verts.count;
  	numindices = faces.count;
  	fname = (GetDir #export) + "/" + objName + ".bin"; -- i don't know the file type :/
  	
  	fstream = fopen fname "wb";
  	if fstream != undefined then
  	(	
  		WriteLong fstream 0xDADADADA #unsigned;
  		WriteLong fstream 4 #unsigned;
  		WriteLong fstream 0xCCCCCCCC #unsigned;
  
  		for i = 1 to numverts do 
  		(
  			WriteFloat fstream verts[i].pos.x;
  			if flipYZ then
  			(
  				WriteFloat fstream verts[i].pos.z;
  				WriteFloat fstream verts[i].pos.y;
  			)	
  			else
  			(
  				WriteFloat fstream verts[i].pos.y;
  				WriteFloat fstream verts[i].pos.z;
  			)
  		)
  		for i = 1 to numindices do 	WriteLong fstream faces[i] #unsigned;
  			
  		
  		if writeUVW then
  		(	
  			numfluids = 1;
  			WriteLong fstream 0xCCCCCC00 #unsigned;
  			WriteLong fstream numfluids #unsigned;
  			
  			for i = 1 to numverts do 
  			(
  				for j = 1 to (numfluids - 1) do  WriteFloat fstream 1.0;	-- fixed it
  				
  				WriteFloat fstream verts[i].uvw.x;
  				WriteFloat fstream verts[i].uvw.y;
  				WriteFloat fstream verts[i].uvw.z;
  			)
  		)	
  		
  		if writeVel then
  		(	
  			WriteLong fstream 0xCCCCCC11 #unsigned;
  			for i = 1 to numverts do 
  			(
  				WriteFloat fstream verts[i].vel.x;
  				if flipYZ then
  				(
  					WriteFloat fstream verts[i].vel.z;
  					WriteFloat fstream verts[i].vel.y;
  				)	
  				else
  				(
  					WriteFloat fstream verts[i].vel.y;
  					WriteFloat fstream verts[i].vel.z;
  				)
  			)
  		)		
  		WriteLong fstream 0xDEDEDEDE #unsigned;
  		fclose fstream;
  	)		
  )	
  	
  --***********************************************************************************
  -- main export loop
  
  fn ExportMesh mObj = if canConvertTo mObj Editable_Mesh then
  (
  -- grab the mesh	
  	
  	msh = snapshot mObj;
  	
  -- compute the inverse tm so we can get positions in local space	
  	
  	localTM = (inverse (msh.transform));
  		
  -- get the number of mat id's	
  	
  	matids = GetMeshMatIDs msh;
  	
  	for m in matids do
  	(
  		faces = GetFacesWithMatID msh m;
  		raw = CollectRawVerts msh faces localTM;
  		opt = OptimizeVerts raw;
  		indices = CreateFaces raw opt;
  		ExportAsRealFlowBinMesh (mObj.name + "_" + m as string) opt indices true true true;
  	)	
  	delete msh;
  )	
  	
  --**************************************************************************************
  
  delete objects
  sph = sphere segs:32  mapcoords:on pos:[75,0,0] 
  clearlistener() 
  ts = timestamp();
  ExportMesh sph;
  print ((timestamp() - ts) * 0.001);
  

Thanks again for your input Klunk,

I’m sorry to say that the bin file generated gets the following error in the realflow log window –

Error: Invalid ID Signature found in the file <blah blah> It looks like it has an invalid filetype and cant be loaded

Denis – I’ll try to explain a little better, I am using the code in the initial post to write meshes from max into the realflow .bin mesh file specification. I am doing this to have a little more control over the next limit bin mesh exporter (which has very few or limited paramters exposed to maxscript from what I can make out) The original code was for version 3 of .bin, I changed it a little to match the specification of the version 4 format, which I posted just before your reply. I want to then load these bin files in max using the RFMeshLoader plugin which is provided with realflow as a suite of connectivity plugins. So im not actually using anything to do with fluids, but I want to use the mesh handling functionality of the .bin mesh loader.

When I use the original code, I get a valid .bin mesh file, but there is something wrong with the way I am writing the texture vertices, and they are present, but all messed up. If I use the nextlimit exporter, the UVs work correctly. I thought it might be something simple like axis inversion but it wasn’t. However, the original code was working on some level in that it rebuilt the meshes correctly, and detected UVs were present, albeit incorrect.

Thanks again

try this, tricky working blind


  fn ExportAsRealFlowBinMesh objName verts faces flipYZ writeUVW writeVel =
  (	
  	numverts = verts.count;
  	numindices = faces.count;
  	
  	fname = (GetDir #export) + "/" + objName + ".bin"; -- i don't know the file type :/
  	
  	fstream = fopen fname "wb";
  	if fstream != undefined then
  	(	
  		WriteLong fstream 0xDADADADA #unsigned;
  		WriteLong fstream 4 #unsigned;
  		WriteLong fstream 0xCCCCCCCC #unsigned;
  		WriteLong fstream numverts #unsigned;
  		for i = 1 to numverts do 
  		(
  			WriteFloat fstream verts[i].pos.x;
  			if flipYZ then
  			(
  				WriteFloat fstream verts[i].pos.z;
  				WriteFloat fstream verts[i].pos.y;
  			)	
  			else
  			(
  				WriteFloat fstream verts[i].pos.y;
  				WriteFloat fstream verts[i].pos.z;
  			)
  		)
  		WriteLong fstream (numindices/3) #unsigned;
  		for i = 1 to numindices do 	WriteLong fstream (faces[i] - 1) #unsigned;
  			
  		
  		if writeUVW then
  		(	
  			numfluids = 1;
  			WriteLong fstream 0xCCCCCC00 #unsigned;
  			WriteLong fstream numfluids #unsigned;
  			
  			for i = 1 to numverts do 
  			(
  				for j = 1 to (numfluids - 1) do  WriteFloat fstream 1.0;	-- fixed it
  				
  				WriteFloat fstream verts[i].uvw.x;
  				WriteFloat fstream verts[i].uvw.y;
  				WriteFloat fstream verts[i].uvw.z;
  			)
  		)	
  		
  		if writeVel then
  		(	
  			WriteLong fstream 0xCCCCCC11 #unsigned;
  			for i = 1 to numverts do 
  			(
  				WriteFloat fstream verts[i].vel.x;
  				if flipYZ then
  				(
  					WriteFloat fstream verts[i].vel.z;
  					WriteFloat fstream verts[i].vel.y;
  				)	
  				else
  				(
  					WriteFloat fstream verts[i].vel.y;
  					WriteFloat fstream verts[i].vel.z;
  				)
  			)
  		)		
  		WriteLong fstream 0xDEDEDEDE #unsigned;
  		fclose fstream;
  	)		
  )	

oops bracket in the wrong place fixed now

the shown format looks incomplete for me. because more than one map vertex can be corresponded to a geo vertex the format should store the loop of number of map vertices.
but in this case it also needs a loop of map faces.

Page 1 / 2