The exporter: http://www.box.net/shared/r5fqthmusy
Example "how to" map: http://www.box.net/shared/u2zm53r7i4.
Dear Sirs,
I've tried to use common Blender plugin (export_map.py) to use with NetRadiant. I've found a few drawbacks.
1) It export AS IS only cubes or cube like meshes. All other geometry is splitted into triangles and is extruded into prisms.
2) In doesn't work with textures at all.
3) It has no options concerning light properties Blender doesn't have but NetRadiant has.
4) Sometimes exported meshes appear to have strange artifacts.
5) It's impossible to see the result right in Blender. One is to open file in NetRadiant to check the export results.
I've spent a week and designed my one exporter form Blender to MAP format. From my point of view it has several fixes of problems met in current one. And these are the following.
1) It does check if mesh is convex or not and split it into triangles only if it doesn't And doesn't depend on mesh complexity.
2) It uses Game Logic Blender menu for storing properties Blender object doesn't have. For example for light it's "sun" "target" and "radius" properties.
3) It export textures and texture coordinates for each face in mesh!!!!!! (It was really hard to transform uv pairs into MAP formap).
4) It shows the export result right in Blender. So it's possible to check it and to change slightly right in Blender.
I would be very appreciate to everyone who test my plugin. It's code is the following:
- Code: Select all
#!BPY
"""
Name: 'Nexuiz (.map)'
Blender: 243
Group: 'Export'
Tooltip: 'Export to Nexuiz map format'
"""
__author__ = 'sergey bashkirov'
__version__ = '0.1'
__email__ = "bashkirov.sergey@gmail.com"
__bpydoc__ = """\
This script Exports a Nexuiz map format.
Supports meshes, lights and nurbs patch surfaces
"""
from Blender import *
import BPyMesh
import os
import math
import string
from os import path
# export options.
opts = []
g_nexuiz_path = "c:/programs/nexuiz"
g_fname = "mymap.map"
g_scale = 32
g_extrudeHeight = 0.3
def nexifyVector( x, y, z ):
global g_scale
a = round( x * g_scale )
b = round( y * g_scale )
c = round( z * g_scale )
return a, b, c
def faceNormal( face, scaleVals=1, roundVals=1 ):
# Calculating normal:
x1, y1, z1 = face.v[0].co.x, \
face.v[0].co.y, \
face.v[0].co.z
x2, y2, z2 = face.v[1].co.x, \
face.v[1].co.y, \
face.v[1].co.z
x3, y3, z3 = face.v[2].co.x, \
face.v[2].co.y, \
face.v[2].co.z
if scaleVals:
global g_scale
x1, y1, z1 = x1 * g_scale, y1 * g_scale, z1 * g_scale
x2, y2, z2 = x2 * g_scale, y2 * g_scale, z2 * g_scale
x3, y3, z3 = x3 * g_scale, y3 * g_scale, z3 * g_scale
if roundVals:
x1, y1, z1 = round( x1 ), round( y1 ), round( z1 )
x2, y2, z2 = round( x2 ), round( y2 ), round( z2 )
x3, y3, z3 = round( x3 ), round( y3 ), round( z3 )
nx = (y2-y1)*(z3-z1)-(y3-y1)*(z2-z1)
ny = (x3-x1)*(z2-z1)-(x2-x1)*(z3-z1)
nz = (x2-x1)*(y3-y1)-(x3-x1)*(y2-y1)
l = math.sqrt( nx * nx + ny * ny + nz * nz )
nx = nx / l
ny = ny / l
nz = nz / l
return nx, ny, nz
def main():
if ( nexify() ):
showGui()
writeFile( g_nexuiz_path + "/data/maps/" + g_fname )
else:
warningGui()
def showGui():
global g_nexuiz_path
global g_fname
global g_scale
global g_extrudeHeight
path = Get( "datadir" )
fname = path + "/nexify.txt"
if ( os.path.exists( fname ) ):
file = open( fname, 'r' )
if ( file ):
g_nexuiz_path = file.readline()
if ( g_nexuiz_path[ len(g_nexuiz_path) - 1 ] == "\n" ):
g_nexuiz_path = g_nexuiz_path[ :(len(g_nexuiz_path) - 1) ]
g_fname = file.readline()
if ( g_fname[ len(g_fname) - 1 ] == "\n" ):
g_fname = g_fname[ :(len(g_fname) - 1) ]
g_scale = int( file.readline() )
g_extrudeHeight = float( file.readline() )
file.close()
guiNexuizPath = Draw.Create( g_nexuiz_path )
guiFileName = Draw.Create( g_fname )
guiNexuizScale = Draw.Create( g_scale )
guiNexuizExtrudeHeight = Draw.Create( g_extrudeHeight )
pup_block = [\
( 'Nexuiz path:', guiNexuizPath, 1, 128, 'Path to nexuiz root directory.'),\
( 'Map file:', guiFileName, 1, 128, 'Map file name.'),\
( 'Grid scale', guiNexuizScale, 1, 128, 'Grid scale (32).'),\
( 'Extrude height', guiNexuizExtrudeHeight, 1, 128, 'Extrude height'),\
]
if not Draw.PupBlock( 'Nexuiz map export', pup_block ):
return
Window.WaitCursor(1)
g_nexuiz_path = guiNexuizPath.val
guiFileName = guiFileName.val
g_scale = guiNexuizScale.val
g_extrudeHeight = guiNexuizExtrudeHeight.val
file = open( fname, 'w' )
if ( file ):
file.write( g_nexuiz_path + "\n" )
file.write( g_fname + "\n" )
file.write( str( g_scale ) + "\n" )
file.write( str( g_extrudeHeight ) + "\n" )
file.close()
def warningGui():
global g_nexuiz_path
guiNexuizPath = Draw.Create( g_nexuiz_path )
pup_block = [ ( 'Nexuiz path:', guiNexuizPath, 1, 128, 'Path to nexuiz root directory.') ]
if not Draw.PupBlock( 'Some concave meshes were modified, \ncheck changes and launch again!', pup_block ):
return
Window.WaitCursor(1)
def nexify():
print( "*************************************************************" )
print( "*************************************************************" )
print( "*************************************************************" )
res = 1
# Disable edit mode!!!!
if Window.EditMode():
Window.EditMode( 0 )
scene = Scene.GetCurrent()
#List copy because I modify it inside the loop.
objList = scene.objects
for obj in objList:
print( "\n____________________________________________" )
print( "inspecting object: " )
print( " type = " + obj.getType() )
print( " name = " + obj.getName() )
if ( obj.getType() == 'Mesh' ):
propsList = obj.getAllProperties()
print( " object properties:" )
for prop in propsList:
print( " " + prop.getName() + " = " + str( prop.getData() ) )
skipObject = 0
for prop in propsList:
if prop.getName() == 'ignore':
if prop.getData():
skipObject = 1
break
if ( skipObject ):
print( "skipping this object due to 'ignore' property" )
continue
res = checkConvex( obj )
obj.removeProperty( "convex" )
if ( res ):
print( " shape = convex" )
obj.addProperty( "convex", 1, 'BOOL' )
else:
print( " shape = concave" )
extrudeMesh( obj, g_extrudeHeight )
obj.addProperty( "convex", 0, 'BOOL' )
obj.addProperty( "ignore", 1, 'BOOL' )
res = 0
obj.makeDisplayList()
elif ( obj.getType() == "Lamp" ):
setLightDefaultFlags( obj );
Window.RedrawAll()
Redraw()
return res
def checkConvex( obj ):
mesh = NMesh.GetRawFromObject( obj.getName() )
# First, check if each edge belongs to exactly two different faces.
# Each edge belongs to exactly two different faces.
for edge in mesh.edges:
v1 = edge.v1
v2 = edge.v2
n = 0
for face in mesh.faces:
if ( ( face.v.count( v1 ) == 1 ) and ( face.v.count( v2 ) == 1 ) ):
n = n + 1
if ( n != 2 ):
print( " faces cnt doesn't match, expected 2, appeared " + str( n ) )
return 0
for edge in mesh.edges:
v1 = edge.v1
v2 = edge.v2
nextEdge = 0
for face_t in mesh.faces:
if ( face_t.v.count( v1 ) == 1 ) and ( face_t.v.count( v2 ) == 1 ):
# Looking for second face with
for face_n in mesh.faces:
if ( face_n.v.count( v1 ) == 1 ) and ( face_n.v.count( v2 ) == 1 ) and ( face_n != face_t ):
# *************************************************************************************
# Faces desc:
# print( "face_t %d\n" % ( len( face_t.v ) ) )
# for v in face_t.v:
# print( "v[%d] = %.4f, %.4f, %.4f\n" % (v.index, v.co.x, v.co.y, v.co.z) )
# print( "face_n %d\n" % ( len( face_n.v ) ) )
# for v in face_n.v:
# print( "v[%d] = %.4f, %.4f, %.4f\n" % (v.index, v.co.x, v.co.y, v.co.z) )
# print( "n = %.4f, %.4f, %.4f\n" % (n[0], n[1], n[2]) )
# *************************************************************************************
# For all edges must not be positive!
d = normal_x_tangent( v1, v2, face_t, face_n )
# if ( d <= 0 ):
# print( "convex\n" )
if ( d > 0 ):
return 0
nextEdge = 1
break
if ( nextEdge ):
break
return 1
def normal_x_tangent( v1, v2, face_t, face_n ):
global g_scale
# Calculating normal:
nx, ny, nz = faceNormal( face_n )
x1, y1, z1 = nexifyVector( v1.co.x, v1.co.y, v1.co.z * g_scale )
# x2, y2, z2 = round( v2.co.x * g_scale ), \
# round( v2.co.y * g_scale ), \
# round( v2.co.z * g_scale )
for v in face_t.v:
if ( v != v1 ) and ( v != v2 ):
x3, y3, z3 = nexifyVector( v.co.x, v.co.y, v.co.z )
# Looking for tangent vector:
# t = ((z2-z1)*z3-z1*z2+z1*z1+(y2-y1)*y3-y1*y2+y1*y1+(x2-x1)*x3-x1*x2+x1*x1)/(z2*z2-2*z1*z2+z1*z1+y2*y2-2*y1*y2+y1*y1+x2*x2-2*x1*x2+x1*x1)
# x = ( x2 - x1 ) * t + x1
# y = ( y2 - y1 ) * t + y1
# z = ( z2 - z1 ) * t + z1
# vx = x3 - x
# vy = y3 - y
# vz = z3 - z
vx, vy, vz = x3 - x1, y3 - y1, z3 - z1
d = vx * nx + vy * ny + vz * nz
# print( "v1: %.4f, %.4f, %.4f\n" % ( x1, y1, z1 ) )
# print( "v2: %.4f, %.4f, %.4f\n" % ( x2, y2, z2 ) )
# print( "v3: %.4f, %.4f, %.4f\n" % ( x3, y3, z3 ) )
# print( "v4: %.4f, %.4f, %.4f\n" % ( x4, y4, z4 ) )
# print( "n2: %.4f, %.4f, %.4f\n" % ( nx, ny, nz ) )
# print( "v: %.4f, %.4f, %.4f\n" % ( vx, vy, vz ) )
return d
def checkConvexEx( obj ):
global g_scale
mesh = NMesh.GetRawFromObject( obj.getName() )
# All points from one size of the mesh.
for face in mesh.faces:
nx, ny, nz = nexifyVector( face.no[0], face.no[1], face.no[2] )
x0, y0, z0 = nexifyVector( face.v[0].co.x, face.v[0].co.y, face.v[0].co.z )
for v in mesh.verts:
x, y, z = nexifyVector( v.co.x, v.co.y, v.co.z )
dot = nx * (x - x0) + ny * (y - y0) + nz * (z - z0)
if ( dot > 0 ):
print( " positive dot product means concave region, data is the following:" )
print( " nx, ny, nz: %.4f, %.4f, %.4f\n" % ( nx, ny, nz ) )
print( " dx, dy, dz: %.4f, %.4f, %.4f\n" % ( x-x0, y-y0, z-z0 ) )
print( " x0, y0, z0: %.4f, %.4f, %.4f\n" % ( x0, y0, z0 ) )
print( " x, y, z: %.4f, %.4f, %.4f\n" % ( x, y, z ) )
return -1
# Each edge belongs to exactly two different faces.
for edge in mesh.edges:
v1 = edge.v1
v2 = edge.v2
n = 0
for face in mesh.faces:
if ( ( face.v.count( v1 ) == 1 ) and ( face.v.count( v2 ) == 1 ) ):
n = n + 1
if ( n != 2 ):
print( " faces cnt doesn't match, expected 2, appeared " + str( n ) )
return 0
return 1
def extrudeMesh( obj, h ):
global g_scale
mesh = NMesh.GetRawFromObject( obj.getName() )
name = mesh.name
propsList = obj.getAllProperties()
for prop in propsList:
if ( prop.getName() == 'height' ):
h_self = prop.getData()
if ( h_self != 0 ):
h = h_self
r0 = obj.getLocation( 'worldspace' )
i = 0
for face in mesh.faces:
newname = name + "." + str( i )
newmesh = Mesh.New( newname )
scene = Scene.GetCurrent()
newobject = scene.objects.new( newmesh, newname )
# Fill newmesh with vertices and faces.
# Calculating normal:
nx, ny, nz = faceNormal( face )
nx = nx * h
ny = ny * h
nz = nz * h
verts = []
for v in face.v:
verts.append( [ v.co.x + r0[0], v.co.y + r0[1], v.co.z + r0[2] ] )
for v in face.v:
verts.append( [ v.co.x + r0[0] + nx, v.co.y + r0[1] + ny, v.co.z + r0[2] + nz ] )
n = len( face.v )
faces = []
if ( h < 0 ):
# Initial face.
faces.append( range(n) )
# New face
faces.append( range(2*n)[ (2*n-1):(n-1):(-1) ] )
else:
# Initial face.
faces.append( range(n)[n::-1] )
# New face
faces.append( range(2*n)[ n:(2*n) ] )
# walls depend on if h > 0 or not.
if ( h > 0 ):
for i in range(n):
faces.append( [ i, (i+1) % n, n + (i+1)%n, i+n ] )
else:
for i in range(n):
faces.append( [ i, i+n, n + (i+1)%n, (i+1) % n ] )
# Extend new mesh with verts and faces.
print( "verts: " + str( len(verts) ) )
print( verts )
print( "faces: " + str( len(faces) ) )
print( faces )
newmesh.verts.extend( verts )
newmesh.faces.extend( faces )
newmesh.calcNormals()
newobject.addProperty( "convex", 1, 'BOOL' )
# Updating properties and mesh data.
newmesh.update()
newobject.makeDisplayList()
i = i + 1
def setLightDefaultFlags( obj ):
propsList = obj.getAllProperties()
# Currently I add special props: sun, has_target, target_x, target_y, target_z, radius.
propsToAdd = [ "sun", "has_target", "target_x", "target_y", "target_z", "radius" ]
propsAlreadyExist = []
for prop in propsList:
if ( propsToAdd.count( prop.getName() ) > 0 ):
propsAlreadyExist.append( prop.getName() )
for prop in propsToAdd:
# If not exist
if ( propsAlreadyExist.count( prop ) == 0 ):
if ( prop == "sun" ):
obj.addProperty( prop, 0, 'BOOL' )
elif ( prop == "has_target" ):
obj.addProperty( prop, 0, 'BOOL' )
elif ( prop == "target_x" ):
obj.addProperty( prop, 0.0, 'FLOAT' )
elif ( prop == "target_y" ):
obj.addProperty( prop, 0.0, 'FLOAT' )
elif ( prop == "target_z" ):
obj.addProperty( prop, 0.0, 'FLOAT' )
elif ( prop == "radius" ):
obj.addProperty( prop, 0.0, 'FLOAT' )
obj.makeDisplayList()
def writeFile( fname ):
print 'Exporting:'
file = open( fname, 'w' )
objList = Object.Get()
# Writing meshes.
file.write( "// entity 0\n" )
file.write( "{\n" )
file.write( ' "classname" "worldspawn"\n' )
for obj in objList:
if ( obj.getType() == 'Mesh' ):
skipObject = 0
propList = obj.getAllProperties()
for prop in propList:
if prop.getName() == 'ignore':
if prop.getData():
skipObject = 1
break
elif prop.getName() == 'convex':
if not prop.getData():
skipObject = 1
break
if ( skipObject ):
print( "skipping this object due to 'ignore' property" )
continue
writeMesh( file, obj )
file.write( '}\n' )
# Writing lamps.
for obj in objList:
if ( obj.getType() == "Lamp" ):
propsList = obj.getAllProperties()
skipObject = 0
for prop in propsList:
if prop.getName() == 'ignore':
if prop.getData():
skipObject = 1
break
if ( skipObject ):
print( "skipping this object due to 'ignore' property" )
continue
writeLamp( file, obj )
file.close()
def writeMesh( file, obj ):
global g_scale
mesh = NMesh.GetRawFromObject( obj.getName() )
r0 = obj.getLocation( 'worldspace' )
#Composing a unique planes list.
planes = []
faces = []
for face in mesh.faces:
nx, ny, nz = faceNormal( face )
nx, ny, nz = nexifyVector( nx, ny, nz )
r0x, r0y, r0z = nexifyVector( face.v[0].co.x + r0[0], face.v[0].co.y + r0[1], face.v[0].co.z + r0[2] )
a, b, c = nx, ny, nz
d = round( -( nx * r0x + ny * r0y + nz * r0z ) )
pl = [ a, b, c, d ]
if ( planes.count( pl ) == 0 ):
planes.append( pl )
faces.append( face )
# Writing planes
stri = ' // mesh "' + mesh.name + '"\n'
stri = stri + ' {\n'
for face in faces:
stri = stri + ' '
for v in face.v[2::-1]:
x, y, z = nexifyVector( v.co.x + r0[0], v.co.y + r0[1], v.co.z + r0[2] )
stri = stri + ( '( %d %d %d ) ' % ( x, y, z ) )
# Shift x, shift y, angle, scale x, scale y, ......
texture, x, y, ang, w, h = calcFaceUv( face )
stri = stri + ( '%s %.8f %.8f %.8f %.8f %.8f 0 0 0\n' % ( texture, x, y, ang, w, h ) )
stri = stri + ' }\n'
file.write( stri )
def writeLamp( file, obj ):
global g_scale
lamp = obj.data
stri = '{\n "classname" "light"\n'
stri = stri + ' "light" %.6f\n' % ( lamp.dist * g_scale )
r = obj.getLocation( 'worldspace' )
x, y, z = nexifyVector( r[0], r[1], r[2] )
stri = stri + ( ' "origin" "%d %d %d"\n' % (x, y, z) )
stri = stri + ( ' "_color" "%.6f %.6f %.6f"\n' % tuple(lamp.col) )
stri = stri + ( ' "style" "0"\n' )
propsList = obj.getAllProperties()
has_target = 0
for prop in propsList:
name = prop.getName()
if ( name == "sun" ):
if ( prop.getData() ):
stri = stri + ' "sun" "1"\n'
elif ( name == "has_target" ):
if ( prop.getData() ):
has_target = 1
target_x, target_y, targer_z = 0, 0, 0
radius = 0
elif ( name == "target_x" ):
target_x = prop.getData()
elif ( name == "target_y" ):
target_y = prop.getData()
elif ( name == "target_z" ):
target_z = prop.getData()
elif ( name == "radius" ):
radius = prop.getData()
if ( has_target ):
stri = stri + ( ' "target" "%.8f %.8f %.8f"' % ( target_x, target_y, target_z ) )
stri = stri + ( ' "radius" "%.8f"\n' % ( radius ) )
stri = stri + "}\n"
file.write( stri )
def calcFaceUv( face ):
if ( len(face.v) == len(face.uv) ):
# I don't know how it works exactly. And the following code is just
# my guess on this.
# Find the most like plane by comparing dot product with face normal.
# Calculating normal:
nx, ny, nz = faceNormal( face )
nxa, nya, nza = abs(nx), abs(ny), abs(nz)
# (Oxy, Oxz, Oyz)
ox0 = [ 1, 0, 0 ]
n0 = [ 0, 0, 1 ]
if ( nxa >= nya ) and ( nxa >= nza ):
ox0 = [ 0, 1, 0 ]
n0 = [ 1, 0, 0 ]
elif ( nya >= nxa ) and ( nya >= nza ):
ox0 = [ 1, 0, 0 ]
n0 = [ 0, 1, 0 ]
elif ( nza >= nxa ) and ( nza >= nya ):
ox0 = [ 1, 0, 0 ]
n0 = [ 0, 0, 1 ]
# Looking for point 0 by intersecting face plane with n0.
r0x, r0y, r0z = nexifyVector( face.v[0].co.x, face.v[0].co.y, face.v[0].co.z )
t = ( r0x * nx + r0y * ny + r0z * nz ) / ( n0[0] * nx + n0[1] * ny + n0[2] * nz )
Ox, Oy, Oz = n0[0] * t, n0[1] * t, n0[2] * t
# Project ox0 on face plane to get ox using not face normal, but n0.
ox = [ 0, 0, 0 ]
d = 0
for i in range(3):
d = d + n0[i] * ox0[i]
for i in range(3):
ox[i] = ox0[i] - d * n0[i]
# make ox unit length.
l = math.sqrt( ox[0] * ox[0] + ox[1] * ox[1] + ox[2] * ox[2] )
for i in range(3):
ox[i] = ox[i] / l
# Looking for oy as cross product (n x ox)
oy = [ 0, 0, 0 ]
oy[0], oy[1], oy[2] = cross( nx, ny, nz, ox[0], ox[1], ox[2] )
# make oy unit length.
l = math.sqrt( oy[0] * oy[0] + oy[1] * oy[1] + oy[2] * oy[2] )
for i in range(3):
oy[i] = oy[i] / l
# Rename some variables to fit external soft output.
oxx, oxy, oxz = ox[0], ox[1], ox[2]
oyx, oyy, oyz = oy[0], oy[1], oy[2]
# Matrix for transferring x,y,z to u0,v0.
d = nx*(oxy*oyz-oxz*oyy)+oxx*(nz*oyy-ny*oyz)+(ny*oxz-nz*oxy)*oyx
A = [ [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0] ]
A[0][0] = (nz*oyy-ny*oyz) / d
A[0][1] = (nx*oyz-nz*oyx) / d
A[0][2] = (ny*oyx-nx*oyy) / d
A[0][3] = (nx*(oyy*Oz-oyz*Oy)-oyx*(ny*Oz-nz*Oy)-(nz*oyy-ny*oyz)*Ox) / d
A[1][0] = (ny*oxz-nz*oxy) / d
A[1][1] = (nz*oxx-nx*oxz) / d
A[1][2] = (nx*oxy-ny*oxx) / d
A[1][3] = (-nx*(oxy*Oz-oxz*Oy)+oxx*(ny*Oz-nz*Oy)+(nz*oxy-ny*oxz)*Ox) / d
A[2][0] = (oxy*oyz-oxz*oyy) / d
A[2][1] = (oxz*oyx-oxx*oyz) / d
A[2][2] = (oxx*oyy-oxy*oyx) / d
A[2][3] = (-oxx*(oyy*Oz-oyz*Oy)+oyx*(oxy*Oz-oxz*Oy)-(oxy*oyz-oxz*oyy)*Ox) / d
A[3][3] = (nx*(oxy*oyz-oxz*oyy)+oxx*(nz*oyy-ny*oyz)-(nz*oxy-ny*oxz)*oyx) / d
# Calculating initial uv0
xyz = []
for i in range(3):
x, y, z = nexifyVector( face.v[i].co.x, face.v[i].co.y, face.v[i].co.z )
xyz.append( [ x, y, z, 1 ] )
uv0 = [ [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ], [ 0, 0, 0, 0 ] ]
for i in range(3): # Vector number
for j in range(4): # Coordinate number
for k in range(4): # Summing index
# print( "A[%d][%d] * xyz[%d][%d] = " % ( j, k, i, k ) )
# print( " %.8f * %.8f" % ( A[j][k], xyz[i][k] ) )
uv0[i][j] = uv0[i][j] + A[j][k] * xyz[i][k]
# **********************************************
print( "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" )
print( " checking coordinates transformation:" )
print( "ox: %.8f, %.8f, %.8f" % (ox[0], ox[1], ox[2]) )
print( "oy: %.8f, %.8f, %.8f" % (oy[0], oy[1], oy[2]) )
print( "O: %.8f, %.8f, %.8f" % (Ox, Oy, Oz) )
print( "coordinates xyz:" )
for i in range(3):
print( "xyz[%d][:]: %.8f, %.8f, %.8f, %.8f" % ( i, xyz[i][0], xyz[i][1], xyz[i][2], xyz[i][3] ) )
print( "coordinates uv:" )
for i in range(3):
print( "uv0[%d][:]: %.8f, %.8f, %.8f, %.8f" % ( i, uv0[i][0], uv0[i][1], uv0[i][2], uv0[i][3] ) )
# it should be uv0[:][2] == 0 and uv0[:][3] == 1!!!!!
# **********************************************
# Initial UV coordinates after transferring xyz to uv according
# to initial guess.
u0, v0 = uv0[0][0], uv0[0][1]
u1, v1 = uv0[1][0], uv0[1][1]
u2, v2 = uv0[2][0], uv0[2][1]
# Real uv coordinates (it's necessary to multiply them on image size, I suppose):
w, h = 32, 32 # Now I take just some common texture resolution.
if ( face.image ):
sz = face.image.getSize()
w = sz[0]
h = sz[1]
U0, V0 = face.uv[0][0] * w, face.uv[0][1] * h
U1, V1 = face.uv[1][0] * w, face.uv[1][1] * h
U2, V2 = face.uv[2][0] * w, face.uv[2][1] * h
for i in range(3):
print( "uv[%d][:]: %.8f, %.8f" % ( i, face.uv[i][0] * w, face.uv[i][1] * h ) )
# matrix for transferring uv to UV.
a = [ [0, 0, 0], [0, 0, 0], [0, 0, 1] ]
d = (u1-u0)*v2+(u0-u2)*v1+(u2-u1)*v0
a[0][0] = -((v1-v0)*U2+(v0-v2)*U1+(v2-v1)*U0) / d
a[0][1] = ((u1-u0)*U2+(u0-u2)*U1+(u2-u1)*U0) / d
a[0][2] = ((u0*v1-u1*v0)*U2+(u2*v0-u0*v2)*U1+(u1*v2-u2*v1)*U0) / d
a[1][0] = -((v1-v0)*V2+(v0-v2)*V1+(v2-v1)*V0) / d
a[1][1] = ((u1-u0)*V2+(u0-u2)*V1+(u2-u1)*V0) / d
a[1][2] = ((u0*v1-u1*v0)*V2+(u2*v0-u0*v2)*V1+(u1*v2-u2*v1)*V0) / d
print( " matrix from uv0->uv:" )
for i in range(2):
print( " a[%d][0], a[%d][1], a[%d][2] = %.8f, %.8f, %.8f" % ( i, i, i, a[i][0], a[i][1], a[i][2] ) )
# To find scale, shift and rotation angle let's transform unit vectors.
r00x, r00y = a[0][2], a[1][2]
a10x, a10y = a[0][0], a[1][0]
a01x, a01y = a[0][1], a[1][1]
# New reference frame orientation. (It's cross product third component.)
d = a10x * a01y - a10y * a01x
if d > 0:
signScaleY = 1
else:
signScaleY = -1
a01x, a01y = -a01x, -a01y
scaleX = math.sqrt( a10x * a10x + a10y * a10y )
scaleY = signScaleY * math.sqrt( a01x * a01x + a01y * a01y )
print( "scaleX, scaleY = %.8f, %.8f" % ( scaleX, scaleY ) )
# Calculating angle.
angle = math.acos( a10x / scaleX ) * 57.295779513082320876798154814105
if ( a10y < 0 ):
angle = 360 - angle
print( "angle = %.8f" % ( angle ) )
ang = angle / 57.295779513082320876798154814105
c = math.cos( ang )
s = math.sin( ang )
inv_ang_sc = [ [c/scaleX, -s/scaleX, 0 ], [s/scaleY, c/scaleY, 0], [0, 0, 1] ]
shift = [ [0, 0, 0], [0, 0, 0], [0, 0, 0] ]
for i in range(3):
for j in range(3):
for k in range(3):
shift[i][j] = shift[i][j] + inv_ang_sc[i][k] * a[k][j]
shiftX, shiftY = shift[0][2], shift[1][2]
else:
shiftX, shiftY, angle, scaleX, scaleY = 0, 0, 0, 1, 1
# Texture name
if ( face.image ):
texture = face.image.getFilename().lower()
# I need to cut off file extension and the
# beginning "c:\programs\nexuiz\data\textures\".
global g_nexuiz_path
tex_path = g_nexuiz_path.replace( '\\', '/' )
if ( tex_path[ len(path)-1 ] == '/' ):
tex_path = tex_path[:(len(tex_path)-1)]
tex_path = ( tex_path + "/data/textures/" ).lower()
os.path.relpath( tex_path, texture )
# Texture in on correct path.
if ( texture.find( tex_path ) == 0 ):
texture = texture[ len(tex_path): ]
texture, ext = os.path.splitext( texture )
print( "texture is: '" + texture + "', file type is: " + ext[ 1: ] )
else:
print( "Some misterious texture file path: '" + texture + "'" )
texture = 'common/caulk'
else:
texture = 'common/caulk'
return texture, shiftX, shiftY, angle, scaleX, scaleY
def cross( ax, ay, az, bx, by, bz ):
cx = ay * bz - az * by
cy = az * bx - ax * bz
cz = ax * by - ay * bx
return cx, cy, cz
if __name__ == '__main__':
main()
It's necessary just to save the code to a file with "py" extension and copy to ".../blender/.blender/plugins" folder. It should appear as "Nexify" menu option in export file menu directory.