[Closed] Maps and UVs
I am trying to write an exporter and I am reading in the docs about meshop
I’ve gotten vertices using
getVert tmesh index
I’ve gotten normals using
getFaceNormal tmesh index
I’ve gotten UVs using
meshop getMapVert tmesh mapIndex index
I’ve gotten faces using
getFace tmesh index
Since the faces I get at the end only seem to hold indices to position vertices, how do I match those up to the map vertices and the normals? Are all the indices the same for these sets?
Can I store all this parsed data in an array of some sort? (I’m not used to these typeless variables) and then match them up? I guess I would need a 2D array for the UVs so the first could be the map channel
Also, is there a way to name a map channel and to name a map channel?
I’d also like to get information about what is mapped to it, like what the texture name is, but materials can wait.
have a peek through the MaxScript help topic: Understanding Texture Coordinates and Vertex Colors
It’s a bit of a read, but it explains how mesh vertices and texture vertices relate.
I’ve read the entire thing from the top of the edible meshes expanded topic to the bottom, including “Understnading Texture Coordinates and Vertex Colors.”
There isn’t anything there about “How do I get the material assigned to this mesh, or this face” nor “How do I get the texture that is mapped to this map channel”
Additonally, the documentation claims Texture Vertices are legacy, yet it constantly refers you to sections that talk about texture vertices.
At this point I have UVWs for each map channel in use by a particular vertex, but do not see a way to see what is mapped to those UVWs, or get any information about the material.
All I’ve seen is a a “get material ID”, but from what I’ve read a material id corresponds to a “sub material” and not the root material.
But it answers your question about which mapping vertex corresponds to a mesh vertex – the mapping faces and the mesh faces have a one-to-one correspondence, and the I-th vertex inside the faces definition corresponds to the I-th vertex of the map face
s definition.
Mapping coordinates have little to do with what material or texture is assigned to a face. That chapter does not discuss material IDs because they are not directly related to mapping coordinates.
The system (as you probably know already) works like this:
*The object (node level) gets a material assigned.
*If the material is NOT of a Multi-Sub Material class, all faces of the object (if any) get that material.
*If the material is a Multi-Sub Material, the Material ID of a face defines which of the sub-materials will be used. If an ID is higher than the available sub-materials, a Modulo operation is used to wrap around the ID so if you have Mat.ID 10 but only 9 sub-materials, it will use number 1.
*A texture within that material set to explicit texture coordinates will use the mapping face with the same index as the mesh face to access the mapping coordinates at the 3 corners of the face and pin down the texture based on the UVW values stored there.
Regarding your note about Legacy, yes, Texture Coordinates (Mapping Channel 1) and Vertex Color (Mapping Channel 0) are considered Legacy because they were the only channels that were there when MAXScript was added in Max 2, thus they use a separate set of functions to access the data. When Max 3 added 98 more channels, Simon Feltman who was then a developer at Kinetix released a free DLX extension to access all mapping data using a unified set of functions. These then made it into Max 4 as the meshop. functions we now have. “Legacy” does not mean “obsolete” though. The “legacy” methods are still valid when dealing with Channel 0 and 1, they are just not as universal as the meshop methods. You can use either one.
That being said, these methods are supposed to be used with an Editable Mesh or a TriMesh value. If you have modifiers on the stack like UVW Unwrap and you want to access the mapping data from there, you have several options – snapshot the top of the stack TriMesh state of the geometry using SnapshotAsMesh(), collapse the stack and work with the Editable_Mesh, or deal with the UVW Unwrap methods.
Each of these approaches has its drawbacks: SnapshotAsMesh gives you the world state of the mesh so all vertices are already in world space, taking into account all transforms and space warps. Collapsing the stack would probably require creating a temp. copy of the original object to avoid destroying the original one. And working with the UVW Unwrap methods requires dealing with Polygons instead of Triangle faces, which complicates things a lot and does not use the meshop. stuff at all.
I am not sure this helps if the “Understanding…” chapter didn’t, but it was worth a try.
True. I am getting absorbed with the problem I now face instead of remembering the problems I conquered . I totally got which post I was replying to confused. I’ve got an explorer window with about 10 tabs open on various forums trying to work my way through an export script. Sorry bout that.
Ok, lets deal with this scenario for now. We’ll worry about sub materials later.
So, if I understand right. When I am not dealing with multi-sub materials I do not have to worry about material IDs at all? For example, I assigned a “standard material” to a cube, the standard material is of type blinn and has a texture mapped to the diffuse from a file. (my current scenario)
Makes sense, except for the word explicit. What is the difference between a texture within a material set to use explicit coordinates and inexplicit? Explain how I would know if coordinates are explicitly set.
Ok, I did that in my script. I have a TriMesh value from a snapshot
It does, I am getting further.
I am still missing the key ingredient though:
I have a tri mesh
—I don’t know how to get the material!!—
I know how to associate the UVWs from map faces with a texture in a material.
All I need is a way to query the trimesh and ask “hey guy, what material is assigned to you?”
Let’s assume the cube with a standard material assigned with a texture mapped to diffuse. I want to get the filename of that texture from my tri mesh object, amonst other things.
Here is my script so far (output to file isn’t the concenr, getting the data is)
-----------------------------------------------------------------------------------------------------------
-- IsGeometry
--
-- @return - true if the superclass of the parameter is a Geometry class and is not a TargetObject
-- In other words, make sure the selected onject has a mesh
fn IsGeometry selected =
(
Superclassof selected == Geometryclass and classof selected != TargetObject
)
------------------------------------------------------------------------------------------------------------
global positions = #() -- array of all positions
global normals =#() -- array of normals, one per face
global UVs =#(#()) -- 2D array, index 1 - map channel, index 2 - corresponding position index
global faces =#() -- array of all faces
global maxToDirectXVert = matrix3 [1, 0, 0] [0, 0, 1] [0, 1, 0] [0, 0, 0]
global maxToDirectXUVW = matrix3 [1, 1, 0] [0, 0, 0] [0, 0, 0] [0, 0, 0]
-- Check all objects below the mouse pointer and allow objects to be picked that match the filter function
obj = pickobject filter:IsGeometry
if isValidNode obj then
(
-- Take a snapshot of the selected mesh
tmesh = snapshotAsMesh obj
-- get the vertex data
for vertexIndex = 1 to tmesh.numverts do
(
positions[vertexIndex] = getVert tmesh vertexIndex
)
-- get the normals
for f = 1 to getNumFaces tmesh do
(
normals[f] = getFaceNormal tmesh f
)
-- get the UVs
for mapChannel = 1 to meshop.getNumMaps tmesh do
(
if meshop.getMapSupport tmesh mapChannel then
(
for mapFace = 1 to meshop.getNumMapFaces tmesh mapChannel do
(
face = meshop.getMapFace tmesh mapChannel mapFace
for i = 1 to 3 do
(
mapVertIndex = face[i]
UVs[mapChannel][mapVertIndex] = meshop.getMapVert tmesh mapChannel mapVertIndex
)
)
)
)
for materialIndex = 1 to sceneMaterials.count do
(
print sceneMaterials[materialIndex]
print sceneMaterials[materialIndex].shaderName
)
-- How do I get what material is assigned to my mesh????
-- open an output file to write to
out_name = GetSaveFileName()
if out_name != undefined then
(
out_file = createfile out_name
-- write out the data
for faceIndex = 1 to tmesh.numfaces do
(
getFaceMatId tmesh faceIndex
faceVertexIndices = (getFace tmesh faceIndex)
for vertexIndex = 1 to 3 do
(
position = positions[faceVertexIndices[vertexIndex]] * maxToDirectXVert
normal = normals[faceIndex] * maxToDirectXVert
format "
f[%][%] v% n%" faceIndex vertexIndex position normal to:out_file
for uvSetIndex = 1 to UVs.count do
(
uvw = UVs[uvSetIndex][vertexIndex]
format " u[%] %" uvSetIndex uvw to:out_file
)
)
format "
" to:out_file
)
-- close the file
close out_file
-- clean up
delete tmesh
-- open the file for viewing
edit out_name
)
)
I think I might have it.
It looks like I can get a material as a property from the object before I take a snapshot of it as a trimesh.
The UVs that resultes from my export script dont look correct.
I made a unit cube, applied a standard material, mapped a file to the diffuse channel and got these results:
Material
{
Name: 01 - Default
Type: Standard Material
Shader: Max_Blinn
Diffuse Map: C:\Users\cpisz.HOMENETWORK\Documents\evil.png
}
f[1][1] v[-0.5,-0.5,-0.5] n[0,0,-1] u[1] [0,0,0]
f[1][2] v[-0.5,0.5,-0.5] n[0,0,-1] u[1] [1,0,0]
f[1][3] v[0.5,0.5,-0.5] n[0,0,-1] u[1] [0,1,0]
f[2][1] v[0.5,0.5,-0.5] n[0,0,-1] u[1] [0,0,0]
f[2][2] v[0.5,-0.5,-0.5] n[0,0,-1] u[1] [1,0,0]
f[2][3] v[-0.5,-0.5,-0.5] n[0,0,-1] u[1] [0,1,0]
f[3][1] v[-0.5,-0.5,0.5] n[0,0,-1] u[1] [0,0,0]
f[3][2] v[0.5,-0.5,0.5] n[0,0,-1] u[1] [1,0,0]
f[3][3] v[0.5,0.5,0.5] n[0,0,1] u[1] [0,1,0]
f[4][1] v[0.5,0.5,0.5] n[0,0,1] u[1] [0,0,0]
f[4][2] v[-0.5,0.5,0.5] n[0,0,1] u[1] [1,0,0]
f[4][3] v[-0.5,-0.5,0.5] n[0,0,-1] u[1] [0,1,0]
f[5][1] v[-0.5,-0.5,-0.5] n[0,0,-1] u[1] [0,0,0]
f[5][2] v[0.5,-0.5,-0.5] n[0,0,-1] u[1] [1,0,0]
f[5][3] v[0.5,-0.5,0.5] n[0,0,-1] u[1] [0,1,0]
f[6][1] v[0.5,-0.5,0.5] n[0,0,-1] u[1] [0,0,0]
f[6][2] v[-0.5,-0.5,0.5] n[0,0,-1] u[1] [1,0,0]
f[6][3] v[-0.5,-0.5,-0.5] n[0,0,-1] u[1] [0,1,0]
f[7][1] v[0.5,-0.5,-0.5] n[0,0,-1] u[1] [0,0,0]
f[7][2] v[0.5,0.5,-0.5] n[0,0,-1] u[1] [1,0,0]
f[7][3] v[0.5,0.5,0.5] n[0,0,1] u[1] [0,1,0]
f[8][1] v[0.5,0.5,0.5] n[0,0,1] u[1] [0,0,0]
f[8][2] v[0.5,-0.5,0.5] n[0,0,-1] u[1] [1,0,0]
f[8][3] v[0.5,-0.5,-0.5] n[0,0,-1] u[1] [0,1,0]
f[9][1] v[0.5,0.5,-0.5] n[0,0,-1] u[1] [0,0,0]
f[9][2] v[-0.5,0.5,-0.5] n[0,0,-1] u[1] [1,0,0]
f[9][3] v[-0.5,0.5,0.5] n[0,0,1] u[1] [0,1,0]
f[10][1] v[-0.5,0.5,0.5] n[0,0,1] u[1] [0,0,0]
f[10][2] v[0.5,0.5,0.5] n[0,0,1] u[1] [1,0,0]
f[10][3] v[0.5,0.5,-0.5] n[0,0,-1] u[1] [0,1,0]
f[11][1] v[-0.5,0.5,-0.5] n[0,0,-1] u[1] [0,0,0]
f[11][2] v[-0.5,-0.5,-0.5] n[0,0,-1] u[1] [1,0,0]
f[11][3] v[-0.5,-0.5,0.5] n[0,0,-1] u[1] [0,1,0]
f[12][1] v[-0.5,-0.5,0.5] n[0,0,-1] u[1] [0,0,0]
f[12][2] v[-0.5,0.5,0.5] n[0,0,1] u[1] [1,0,0]
f[12][3] v[-0.5,0.5,-0.5] n[0,0,-1] u[1] [0,1,0]
Looking at face 1, it appears to be a triangle on the bottom of the cube. So I went to my perspective viewport and orientate the view to look at the bottom:
(The insert image button isn’t working for me on these forums! Here is a link)
ftp://christopherpisz.is-a-geek.com/pub/images/3dsmax/uvs_01.jpg
Looking at the outputs position, face[1][1] looks to be the bottom right vertex.
Looking at the outputs UV, face[1][1] is 0 0 0, which would be the bottom left of the texture.
However, looking at the viewport screenshow, the bottom right vertex is mapped to the bottom right of the texture.
Why arent these matching up?
Here is my script so far
-----------------------------------------------------------------------------------------------------------
-- IsGeometry
--
-- @return - true if the superclass of the parameter is a Geometry class and is not a TargetObject
-- In other words, make sure the selected onject has a mesh
fn IsGeometry selected =
(
Superclassof selected == Geometryclass and classof selected != TargetObject
)
-----------------------------------------------------------------------------------------------------------
-- WriteMaterialData
--
-- @param rootMaterial - The root material to be written out to file
-- @param outFile - The opened file to write to
fn WriteMaterialData rootMaterial outFile =
(
format "Material
{
" to:outFile
format "Name: %
" rootMaterial.name to:outFile
unsupported = false
-- Standard Material
if classOf rootMaterial == standardMaterial then
(
format "Type: Standard Material
" to:outFile
-- Blinn
if rootMaterial.shaderType == 1 then
(
format "Shader: Max_Blinn
" to:outFile
if rootMaterial.mapEnables[1] then
(
texMap = rootMaterial.maps[1]
if classOf texMap == BitmapTexture then
(
format "Ambient Map: %
" texMap.filename to:outFile
)
)
if rootMaterial.mapEnables[2] then
(
texMap = rootMaterial.maps[2]
if classOf texMap == BitmapTexture then
(
format "Diffuse Map: %
" texMap.filename to:outFile
)
)
if rootMaterial.mapEnables[3] then
(
texMap = rootMaterial.maps[3]
if classOf texMap == BitmapTexture then
(
format "Specular Map: %
" texMap.filename to:outFile
)
)
if rootMaterial.mapEnables[4] then
(
texMap = rootMaterial.maps[4]
if classOf texMap == BitmapTexture then
(
format "Specular Level Map: %
" texMap.filename to:outFile
)
)
)
)
if unsupported then
(
messageBox "Unsupported Material found, skipping..." "Error"
)
format "}
" to:outFile
)
------------------------------------------------------------------------------------------------------------
-- Main
--
global rootMaterial -- material assigned to the mesh
global positions = #() -- array of all positions
global normals =#() -- array of normals, one per face
global UVs =#(#()) -- 2D array, index 1 - map channel, index 2 - corresponding position index
global faces =#() -- array of all faces, with each face being 3 vertex indices
global maxToDirectXVert = matrix3 [1, 0, 0] [0, 0, 1] [0, 1, 0] [0, 0, 0]
global maxToDirectXUVW = matrix3 [1, 1, 0] [0, 0, 0] [0, 1, 1] [0, 0, 0]
-- Check all objects below the mouse pointer and allow objects to be picked that match the filter function
obj = pickobject filter:IsGeometry
if isValidNode obj then
(
-- Take a snapshot of the selected mesh
tmesh = convertToMesh (snapshot obj)
-- Get the material
rootMaterial = obj.material
-- Get vertices data
for vertexIndex = 1 to tmesh.numverts do
(
position = getVert tmesh vertexIndex
positions[vertexIndex] = position
)
for faceIndex = 1 to tmesh.numFaces do
(
normal = getFaceNormal tmesh faceIndex
for vertexIndex = 1 to 3 do
(
normals[(faceIndex - 1) * 3 + vertexIndex] = normal
)
)
-- Get the UVs
for mapChannel = 1 to meshop.getNumMaps tmesh do
(
if meshop.getMapSupport tmesh mapChannel then
(
for mapFace = 1 to meshop.getNumMapFaces tmesh mapChannel do
(
face = meshop.getMapFace tmesh mapChannel mapFace
for i = 1 to 3 do
(
mapVertIndex = face[i]
texCoord = meshop.getMapVert tmesh mapChannel mapVertIndex
UVs[mapChannel][mapVertIndex] = texCoord
)
)
)
)
-- Get the faces
for faceIndex = 1 to tmesh.numfaces do
(
faceVertexIndices = (getFace tmesh faceIndex)
faces[faceIndex] = faceVertexIndices
)
-- Convert to DirectX coordinate system and draw order
-- v0 v1 v2 -> v0 v2 v3, to draw in clockwise order for DirectX
-- temp = faceVertexIndices[2]
-- faceVertexIndices[2] = faceVertexIndices[3]
-- faceVertexIndices[3] = temp
-- Open an output file to write to
out_name = GetSaveFileName()
if out_name != undefined then
(
out_file = createfile out_name
-- Material info
WriteMaterialData rootMaterial out_file
-- Write out the data
for faceIndex = 1 to faces.count do
(
faceVertexIndices = faces[faceIndex]
for vertexIndex = 1 to 3 do
(
position = positions[faceVertexIndices[vertexIndex]]
normal = normals[faceVertexIndices[vertexIndex]]
format "
f[%][%] v% n%" faceIndex vertexIndex position normal to:out_file
for uvSetIndex = 1 to UVs.count do
(
uvw = UVs[uvSetIndex][vertexIndex]
format " u[%] %" uvSetIndex uvw to:out_file
)
)
format "
" to:out_file
)
-- close the file
close out_file
-- clean up
delete tmesh
-- open the file for viewing
edit out_name
)
)