[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
)
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?
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.