2014-01-17 23:14:40 +01:00
# -*- coding: utf-8 -*-
###########################################################################
# Glest Model / Texture / UV / Animation Importer and Exporter
# for the Game Glest that u can find http://www.glest.org
# copyright 2005 By Andreas Becker (seltsamuel@yahoo.de)
#
# 2011/05/25: v0.1 alpha1
# modified by William Zheng for Blender 2.57(loveheaven_zhengwei@hotmail.com)
#
# corrected by MrPostiga for Blender 2.58
#
# extended by Yggdrasil
#
# Started Date: 07 June 2005 Put Public 20 June 2005
# Distributed under the GNU PUBLIC LICENSE
#"""
#Here an explanation of the V4 Format found at www.glest.org
#================================
#1. DATA TYPES
#================================
#G3D files use the following data types:
#uint8: 8 bit unsigned integer
#uint16: 16 bit unsigned integer
#uint32: 32 bit unsigned integer
#float32: 32 bit floating point
#================================
#2. OVERALL STRUCTURE
#================================
#- File header
#- Model header
#- Mesh header
#- Texture names
#- Mesh data
#================================
#2. FILE HEADER
#================================
#Code:
#struct FileHeader{
# uint8 id[3];
# uint8 version;
#};
#This header is shared among all the versions of G3D, it identifies this file as a G3D model and provides information of the version.
#id: must be "G3D"
#version: must be 4, in binary (not '4')
#================================
#3. MODEL HEADER
#================================
#Code:
#struct ModelHeader{
# uint16 meshCount;
# uint8 type;
#};
#meshCount: number of meshes in this model
#type: must be 0
#================================
#4. MESH HEADER
#================================
#There is a mesh header for each mesh, there must be "meshCount" headers in a file but they are not consecutive, texture names and mesh data are stored in between.
#Code:
#struct MeshHeader{
# uint8 name[64];
# uint32 frameCount;
# uint32 vertexCount;
# uint32 indexCount;
# float32 diffuseColor[3];
# float32 specularColor[3];
# float32 specularPower;
# float32 opacity;
# uint32 properties;
# uint32 textures;
#};
#name: name of the mesh
#frameCount: number of keyframes in this mesh
#vertexCount: number of vertices in each frame
#indexCount: number of indices in this mesh (the number of triangles is indexCount/3)
#diffuseColor: RGB diffuse color
#specularColor: RGB specular color (currently unused)
#specularPower: specular power (currently unused)
##properties: property flags
#Code:
#enum MeshPropertyFlag{
# mpfCustomColor= 1,
# mpfTwoSided= 2,
# mpfNoSelect= 4
#};
#mpfTwoSided: meshes in this mesh are rendered by both sides, if this flag is not present only "counter clockwise" faces are rendered
#mpfCustomColor: alpha in this model is replaced by a custom color, usually the player color
#textures: texture flags
#Code:
#enum MeshTexture{
# diffuse = 1,
# specular = 2,
# normal = 4
#};
#================================
#4. TEXTURE NAMES
#================================
#A list of uint8[64] texture name values. One for each texture in the mesh. If there are no textures in the mesh no texture names are present.
#================================
#5. MESH DATA
#================================
#After each mesh header and texture names the mesh data is placed.
#vertices: frameCount * vertexCount * 3, float32 values representing the x, y, z vertex coords for all frames
#normals: frameCount * vertexCount * 3, float32 values representing the x, y, z normal coords for all frames
#texture coords: vertexCount * 2, float32 values representing the s, t tex coords for all frames (only present if the mesh has 1 texture at least)
#indices: indexCount, uint32 values representing the indices
###########################################################################
bl_info = {
" name " : " G3D Mesh Import/Export " ,
" description " : " Import/Export .g3d file (Glest 3D) " ,
" author " : " various, see head of script " ,
" version " : ( 0 , 11 , 1 ) ,
" blender " : ( 2 , 65 , 0 ) ,
" location " : " File > Import-Export " ,
" warning " : " always keep .blend files " ,
" wiki_url " : " http://glest.wikia.com/wiki/G3D_support " ,
" tracker_url " : " https://forum.megaglest.org/index.php?topic=6596 " ,
" category " : " Import-Export " }
###########################################################################
# Importing Structures needed (must later verify if i need them really all)
###########################################################################
import bpy
from bpy . props import StringProperty
from bpy_extras . image_utils import load_image
from bpy_extras . io_utils import ImportHelper , ExportHelper
import bmesh
import sys , struct , string , types
from types import *
import os
from os import path
from os . path import dirname , abspath
import subprocess
from mathutils import Matrix
from math import radians
###########################################################################
# Variables that are better Global to handle
###########################################################################
imported = [ ] #List of all imported Objects
toexport = [ ] #List of Objects to export (actually only meshes)
sceneID = None #Points to the active Blender Scene
def unpack_list ( list_of_tuples ) :
l = [ ]
for t in list_of_tuples :
l . extend ( t )
return l
###########################################################################
# Declaring Structures of G3D Format
###########################################################################
class G3DHeader : #Read first 4 Bytes of file should be G3D + Versionnumber
binary_format = " <3cB "
def __init__ ( self , fileID ) :
temp = fileID . read ( struct . calcsize ( self . binary_format ) )
data = struct . unpack ( self . binary_format , temp )
self . id = str ( data [ 0 ] + data [ 1 ] + data [ 2 ] , " utf-8 " )
self . version = data [ 3 ]
class G3DModelHeaderv3 : #Read Modelheader in V3 there is only the number of Meshes in file
binary_format = " <I "
def __init__ ( self , fileID ) :
temp = fileID . read ( struct . calcsize ( self . binary_format ) )
data = struct . unpack ( self . binary_format , temp )
self . meshcount = data [ 0 ]
class G3DModelHeaderv4 : #Read Modelheader: Number of Meshes and Meshtype (must be 0)
binary_format = " <HB "
def __init__ ( self , fileID ) :
temp = fileID . read ( struct . calcsize ( self . binary_format ) )
data = struct . unpack ( self . binary_format , temp )
self . meshcount = data [ 0 ]
self . mtype = data [ 1 ]
class G3DMeshHeaderv3 : #Read Meshheader
binary_format = " <7I64c "
def __init__ ( self , fileID ) :
temp = fileID . read ( struct . calcsize ( self . binary_format ) )
data = struct . unpack ( self . binary_format , temp )
self . framecount = data [ 0 ] #Framecount = Number of Animationsteps
self . normalframecount = data [ 1 ] #Number of Normal Frames actualli equal to Framecount
self . texturecoordframecount = data [ 2 ] #Number of Frames of Texturecoordinates seems everytime to be 1
self . colorframecount = data [ 3 ] #Number of Frames of Colors seems everytime to be 1
self . vertexcount = data [ 4 ] #Number of Vertices in each Frame
self . indexcount = data [ 5 ] #Number of Indices in Mesh (Triangles = Indexcount/3)
self . properties = data [ 6 ] #Property flags
if self . properties & 1 : #PropertyBit is Mesh Textured ?
self . hastexture = False
self . diffusetexture = None
else :
self . diffusetexture = " " . join ( [ str ( x , " ascii " ) for x in data [ 7 : - 1 ] if x [ 0 ] < 127 ] )
self . hastexture = True
if self . properties & 2 : #PropertyBit is Mesh TwoSided ?
self . istwosided = True
else :
self . istwosided = False
if self . properties & 4 : #PropertyBit is Mesh Alpha Channel custom Color in Game ?
self . customalpha = True
else :
self . customalpha = False
class G3DMeshHeaderv4 : #Read Meshheader
binary_format = " <64c3I8f2I "
texname_format = " <64c "
def _readtexname ( self , fileID ) :
temp = fileID . read ( struct . calcsize ( self . texname_format ) )
data = struct . unpack ( self . texname_format , temp )
return " " . join ( [ str ( x , " ascii " ) for x in data [ 0 : - 1 ] if x [ 0 ] < 127 ] )
def __init__ ( self , fileID ) :
temp = fileID . read ( struct . calcsize ( self . binary_format ) )
data = struct . unpack ( self . binary_format , temp )
self . meshname = " " . join ( [ str ( x , " ascii " ) for x in data [ 0 : 64 ] if x [ 0 ] < 127 ] ) #Name of Mesh every Char is a String on his own
self . framecount = data [ 64 ] #Framecount = Number of Animationsteps
self . vertexcount = data [ 65 ] #Number of Vertices in each Frame
self . indexcount = data [ 66 ] #Number of Indices in Mesh (Triangles = Indexcount/3)
self . diffusecolor = data [ 67 : 70 ] #RGB diffuse color
self . specularcolor = data [ 70 : 73 ] #RGB specular color (unused)
self . specularpower = data [ 73 ] #Specular power (unused)
self . opacity = data [ 74 ] #Opacity
self . properties = data [ 75 ] #Property flags
self . textures = data [ 76 ] #Texture flags
self . customalpha = bool ( self . properties & 1 )
self . istwosided = bool ( self . properties & 2 )
self . noselect = bool ( self . properties & 4 )
2015-03-18 20:21:59 +01:00
self . glow = bool ( self . properties & 8 )
2014-01-17 23:14:40 +01:00
self . hastexture = False
self . diffusetexture = None
self . speculartexture = None
self . normaltexture = None
if self . textures : #PropertyBit is Mesh Textured ?
if self . textures & 1 : # diffuse
self . diffusetexture = self . _readtexname ( fileID )
if self . textures & 2 : # specular
self . speculartexture = self . _readtexname ( fileID )
if self . textures & 4 : # normal
self . normaltexture = self . _readtexname ( fileID )
self . hastexture = True
# read all texture slots, otherwise it's read as data
tex = self . textures >> 3
while tex :
tex & = tex - 1 # set rightmost 1-bit to 0
# discard texture name, as we don't know what to do with it
fileID . seek ( struct . calcsize ( self . texname_format ) )
print ( " warning: ignored texture in undefined texture slot " )
class G3DMeshdataV3 : #Calculate and read the Mesh Datapack
def __init__ ( self , fileID , header ) :
#Calculation of the Meshdatasize to load because its variable
#Animationframes * Vertices per Animation * 3 (Each Point are 3 Float X Y Z Coordinates)
vertex_format = " < %i f " % int ( header . framecount * header . vertexcount * 3 )
#The same for Normals
normals_format = " < %i f " % int ( header . normalframecount * header . vertexcount * 3 )
#Same here but Textures are 2D so only 2 Floats needed for Position inside Texture Bitmap
texturecoords_format = " < %i f " % int ( header . texturecoordframecount * header . vertexcount * 2 )
#Colors in format RGBA
colors_format = " < %i f " % int ( header . colorframecount * 4 )
#Indices
indices_format = " < %i I " % int ( header . indexcount )
#Load the Meshdata as calculated above
self . vertices = struct . unpack ( vertex_format , fileID . read ( struct . calcsize ( vertex_format ) ) )
self . normals = struct . unpack ( normals_format , fileID . read ( struct . calcsize ( normals_format ) ) )
self . texturecoords = struct . unpack ( texturecoords_format , fileID . read ( struct . calcsize ( texturecoords_format ) ) )
self . colors = struct . unpack ( colors_format , fileID . read ( struct . calcsize ( colors_format ) ) )
self . indices = struct . unpack ( indices_format , fileID . read ( struct . calcsize ( indices_format ) ) )
class G3DMeshdataV4 : #Calculate and read the Mesh Datapack
def __init__ ( self , fileID , header ) :
#Calculation of the Meshdatasize to load because its variable
#Animationframes * Points (Vertex) per Animation * 3 (Each Point are 3 Float X Y Z Coordinates)
vertex_format = " < %i f " % int ( header . framecount * header . vertexcount * 3 )
#The same for Normals
normals_format = " < %i f " % int ( header . framecount * header . vertexcount * 3 )
#Same here but Textures are 2D so only 2 Floats needed for Position inside Texture Bitmap
texturecoords_format = " < %i f " % int ( header . vertexcount * 2 )
#Indices
indices_format = " < %i I " % int ( header . indexcount )
#Load the Meshdata as calculated above
self . vertices = struct . unpack ( vertex_format , fileID . read ( struct . calcsize ( vertex_format ) ) )
self . normals = struct . unpack ( normals_format , fileID . read ( struct . calcsize ( normals_format ) ) )
if header . hastexture :
self . texturecoords = struct . unpack ( texturecoords_format , fileID . read ( struct . calcsize ( texturecoords_format ) ) )
self . indices = struct . unpack ( indices_format , fileID . read ( struct . calcsize ( indices_format ) ) )
#Create a Mesh inside Blender
def createMesh ( filename , header , data , toblender , operator ) :
mesh = bpy . data . meshes . new ( header . meshname ) #New Mesh
meshobj = bpy . data . objects . new ( header . meshname + ' Object ' , mesh ) #New Object for the new Mesh
scene = bpy . context . scene
scene . objects . link ( meshobj )
scene . update ( )
uvcoords = [ ]
img_diffuse = None
img_specular = None
img_normal = None
if header . hastexture : #Load Texture when assigned
try :
texturefile = dirname ( abspath ( filename ) ) + os . sep + header . diffusetexture
img_diffuse = bpy . data . images . load ( texturefile )
for x in range ( 0 , len ( data . texturecoords ) , 2 ) : #Prepare the UV
uvcoords . append ( [ data . texturecoords [ x ] , data . texturecoords [ x + 1 ] ] )
if header . isv4 :
if header . speculartexture :
texturefile = dirname ( abspath ( filename ) ) + os . sep + header . speculartexture
img_specular = bpy . data . images . load ( texturefile )
if header . normaltexture :
texturefile = dirname ( abspath ( filename ) ) + os . sep + header . normaltexture
img_normal = bpy . data . images . load ( texturefile )
except :
import traceback
traceback . print_exc ( )
header . hastexture = False
operator . report ( { ' WARNING ' } , " Couldn ' t load texture. See console for details. " )
vertsCO = [ ]
vertsNormal = [ ]
for x in range ( 0 , header . vertexcount * 3 , 3 ) : #Get the Vertices and Normals into empty Mesh
vertsCO . extend ( [ ( data . vertices [ x ] , data . vertices [ x + 1 ] , data . vertices [ x + 2 ] ) ] )
vertsNormal . extend ( [ ( data . normals [ x ] , data . normals [ x + 1 ] , data . normals [ x + 2 ] ) ] )
#vertsCO.extend([(data.vertices[x+(header.framecount-1)*header.vertexcount*3],data.vertices[x+(header.framecount-1)*header.vertexcount*3+1],data.vertices[x+(header.framecount-1)*header.vertexcount*3+2])])
#vertsNormal.extend([(data.normals[x+(header.framecount-1)*header.vertexcount*3],data.normals[x+(header.framecount-1)*header.vertexcount*3+1],data.normals[x+(header.framecount-1)*header.vertexcount*3+2])])
mesh . vertices . add ( len ( vertsCO ) )
mesh . vertices . foreach_set ( " co " , unpack_list ( vertsCO ) )
mesh . vertices . foreach_set ( " normal " , unpack_list ( vertsNormal ) )
faces = [ ]
faceuv = [ ]
for i in range ( 0 , len ( data . indices ) , 3 ) : #Build Faces into Mesh
faces . extend ( [ data . indices [ i ] , data . indices [ i + 1 ] , data . indices [ i + 2 ] , 0 ] )
if header . hastexture :
uv = [ ]
u0 = uvcoords [ data . indices [ i ] ] [ 0 ]
v0 = uvcoords [ data . indices [ i ] ] [ 1 ]
uv . append ( [ u0 , v0 ] )
u1 = uvcoords [ data . indices [ i + 1 ] ] [ 0 ]
v1 = uvcoords [ data . indices [ i + 1 ] ] [ 1 ]
uv . append ( [ u1 , v1 ] )
u2 = uvcoords [ data . indices [ i + 2 ] ] [ 0 ]
v2 = uvcoords [ data . indices [ i + 2 ] ] [ 1 ]
uv . append ( [ u2 , v2 ] )
faceuv . append ( [ uv , 0 , 0 , 0 ] )
else :
uv = [ ]
uv . append ( [ 0 , 0 ] )
uv . append ( [ 0 , 0 ] )
uv . append ( [ 0 , 0 ] )
faceuv . append ( [ uv , 0 , 0 , 0 ] )
mesh . tessfaces . add ( len ( faces ) / / 4 )
mesh . tessfaces . foreach_set ( " vertices_raw " , faces )
mesh . tessfaces . foreach_set ( " use_smooth " , [ True ] * len ( mesh . tessfaces ) )
mesh . g3d_customColor = header . customalpha
mesh . show_double_sided = header . istwosided
if header . isv4 :
mesh . g3d_noSelect = header . noselect
2015-03-18 20:21:59 +01:00
mesh . g3d_glow = header . glow
2014-01-17 23:14:40 +01:00
else :
mesh . g3d_noSelect = False
2015-03-18 20:21:59 +01:00
mesh . glow = False
2014-01-17 23:14:40 +01:00
mesh . g3d_fullyOpaque = False
#===================================================================================================
#Material Setup
#===================================================================================================
def addtexslot ( matdata , index , name , img ) :
texture = bpy . data . textures . new ( name = name , type = ' IMAGE ' )
texture . image = img
slot = matdata . texture_slots . create ( index )
slot . texture = texture
slot . texture_coords = ' UV '
if header . hastexture :
materialname = " pskmat "
materials = [ ]
matdata = bpy . data . materials . new ( materialname + ' 1 ' )
addtexslot ( matdata , 0 , ' diffusetexture ' , img_diffuse )
if img_specular :
addtexslot ( matdata , 1 , ' speculartexture ' , img_specular )
if img_normal :
addtexslot ( matdata , 2 , ' normaltexture ' , img_normal )
if header . isv4 :
matdata . diffuse_color = ( header . diffusecolor [ 0 ] , header . diffusecolor [ 1 ] , header . diffusecolor [ 2 ] )
matdata . alpha = header . opacity
matdata . specular_color = ( header . specularcolor [ 0 ] , header . specularcolor [ 1 ] , header . specularcolor [ 2 ] )
materials . append ( matdata )
for material in materials :
#add material to the mesh list of materials
mesh . materials . append ( material )
countm = 0
psktexname = " psk " + str ( countm )
mesh . tessface_uv_textures . new ( name = psktexname )
if ( len ( faceuv ) > 0 ) :
for countm in range ( len ( mesh . tessface_uv_textures ) ) :
uvtex = mesh . tessface_uv_textures [ countm ] #add one uv texture
for i , face in enumerate ( mesh . tessfaces ) :
blender_tface = uvtex . data [ i ] #face
mfaceuv = faceuv [ i ]
if countm == faceuv [ i ] [ 1 ] :
face . material_index = faceuv [ i ] [ 1 ]
blender_tface . uv1 = mfaceuv [ 0 ] [ 0 ] #uv = (0,0)
blender_tface . uv2 = mfaceuv [ 0 ] [ 1 ] #uv = (0,0)
blender_tface . uv3 = mfaceuv [ 0 ] [ 2 ] #uv = (0,0)
blender_tface . image = img_diffuse
else :
blender_tface . uv1 = [ 0 , 0 ]
blender_tface . uv2 = [ 0 , 0 ]
blender_tface . uv3 = [ 0 , 0 ]
imported . append ( meshobj ) #Add to Imported Objects
sk = meshobj . shape_key_add ( )
for x in range ( 1 , header . framecount ) : #Put in Vertex Positions for Keyanimation
sk = meshobj . shape_key_add ( )
for i in range ( 0 , header . vertexcount * 3 , 3 ) :
sk . data [ i / / 3 ] . co [ 0 ] = data . vertices [ x * header . vertexcount * 3 + i ]
sk . data [ i / / 3 ] . co [ 1 ] = data . vertices [ x * header . vertexcount * 3 + i + 1 ]
sk . data [ i / / 3 ] . co [ 2 ] = data . vertices [ x * header . vertexcount * 3 + i + 2 ]
# activate one shapekey per frame
for i in range ( 1 , header . framecount ) :
shape = mesh . shape_keys . key_blocks [ i ]
shape . value = 0.0
shape . keyframe_insert ( " value " , frame = i )
shape . value = 1.0
shape . keyframe_insert ( " value " , frame = ( i + 1 ) )
shape . value = 0.0
shape . keyframe_insert ( " value " , frame = ( i + 2 ) )
meshobj . active_shape_key_index = 0
if toblender :
# rotate from glest to blender orientation
#mesh.transform( Matrix( ((1,0,0,0),(0,0,-1,0),(0,1,0,0),(0,0,0,1)) ) )
# doesn't work, maybe because of shape keys
# use object transformation instead
meshobj . rotation_euler = ( radians ( 90 ) , 0 , 0 )
# update polygon structures from tessfaces
mesh . update ( )
mesh . update_tag ( )
# remove duplicates
bm = bmesh . new ( )
bm . from_mesh ( mesh )
bmesh . ops . remove_doubles ( bm , verts = bm . verts , dist = 0.0001 )
bm . to_mesh ( mesh )
bm . free ( )
return
###########################################################################
# Import
###########################################################################
def G3DLoader ( filepath , toblender , operator ) : #Main Import Routine
global imported , sceneID
print ( " \n Now Importing File: " + filepath )
fileID = open ( filepath , " rb " )
header = G3DHeader ( fileID )
print ( " \n Header ID : " + header . id )
print ( " Version : " + str ( header . version ) )
if header . id != " G3D " :
print ( " ERROR: This is Not a G3D Model File " )
operator . report ( { ' ERROR ' } , " This is Not a G3D Model File " )
fileID . close
return
if header . version not in ( 3 , 4 ) :
print ( " ERROR: The Version of this G3D File is not Supported " )
operator . report ( { ' ERROR ' } , " The Version of this G3D File is not Supported " )
fileID . close
return
#in_editmode = Blender.Window.EditMode() #Must leave Editmode when active
#if in_editmode: Blender.Window.EditMode(0)
sceneID = bpy . context . scene #Get active Scene
#scenecontext=sceneID.getRenderingContext() #To Access the Start/Endframe its so hidden i searched till i got angry :-)
basename = os . path . basename ( filepath ) . split ( ' . ' ) [ 0 ] #Generate the Base Filename without Path + extension
imported = [ ]
maxframe = 0
if header . version == 3 :
modelheader = G3DModelHeaderv3 ( fileID )
print ( " Number of Meshes : " + str ( modelheader . meshcount ) )
for x in range ( modelheader . meshcount ) :
meshheader = G3DMeshHeaderv3 ( fileID )
meshheader . isv4 = False
print ( " \n Mesh Number : " + str ( x + 1 ) )
print ( " framecount : " + str ( meshheader . framecount ) )
print ( " normalframecount : " + str ( meshheader . normalframecount ) )
print ( " texturecoordframecount: " + str ( meshheader . texturecoordframecount ) )
print ( " colorframecount : " + str ( meshheader . colorframecount ) )
print ( " pointcount : " + str ( meshheader . vertexcount ) )
print ( " indexcount : " + str ( meshheader . indexcount ) )
print ( " texturename : " + str ( meshheader . diffusetexture ) )
print ( " hastexture : " + str ( meshheader . hastexture ) )
print ( " istwosided : " + str ( meshheader . istwosided ) )
print ( " customalpha : " + str ( meshheader . customalpha ) )
meshheader . meshname = basename + str ( x + 1 ) #Generate Meshname because V3 has none
if meshheader . framecount > maxframe : maxframe = meshheader . framecount #Evaluate the maximal animationsteps
meshdata = G3DMeshdataV3 ( fileID , meshheader )
createMesh ( filepath , meshheader , meshdata , toblender , operator )
fileID . close
bpy . context . scene . frame_start = 1
bpy . context . scene . frame_end = maxframe
bpy . context . scene . frame_current = 1
anchor = bpy . data . objects . new ( ' Empty ' , None )
anchor . select = True
bpy . context . scene . objects . link ( anchor )
for ob in imported :
ob . parent = anchor
bpy . context . scene . update ( )
return
if header . version == 4 :
modelheader = G3DModelHeaderv4 ( fileID )
print ( " Number of Meshes : " + str ( modelheader . meshcount ) )
for x in range ( modelheader . meshcount ) :
meshheader = G3DMeshHeaderv4 ( fileID )
meshheader . isv4 = True
print ( " \n Mesh Number : " + str ( x + 1 ) )
print ( " meshname : " + str ( meshheader . meshname ) )
print ( " framecount : " + str ( meshheader . framecount ) )
print ( " vertexcount : " + str ( meshheader . vertexcount ) )
print ( " indexcount : " + str ( meshheader . indexcount ) )
print ( " diffusecolor : %1.6f %1.6f %1.6f " % meshheader . diffusecolor )
print ( " specularcolor : %1.6f %1.6f %1.6f " % meshheader . specularcolor )
print ( " specularpower : %1.6f " % meshheader . specularpower )
print ( " opacity : %1.6f " % meshheader . opacity )
print ( " properties : " + str ( meshheader . properties ) )
print ( " textures : " + str ( meshheader . textures ) )
print ( " texturename : " + str ( meshheader . diffusetexture ) )
if len ( meshheader . meshname ) == 0 : #When no Meshname in File Generate one
meshheader . meshname = basename + str ( x + 1 )
if meshheader . framecount > maxframe : maxframe = meshheader . framecount #Evaluate the maximal animationsteps
meshdata = G3DMeshdataV4 ( fileID , meshheader )
createMesh ( filepath , meshheader , meshdata , toblender , operator )
fileID . close
bpy . context . scene . frame_start = 1
bpy . context . scene . frame_end = maxframe
bpy . context . scene . frame_current = 1
anchor = bpy . data . objects . new ( ' Empty ' , None )
anchor . select = True
bpy . context . scene . objects . link ( anchor )
for ob in imported :
ob . parent = anchor
bpy . context . scene . update ( )
print ( " Created a empty Object as ' Grip ' where all imported Objects are parented to " )
print ( " To move the complete Meshes only select this empty Object and move it " )
print ( " All Done, have a good Day :-) \n \n " )
return
def G3DSaver ( filepath , context , toglest , operator ) :
print ( " \n Now Exporting File: " + filepath )
objs = context . selected_objects
if len ( objs ) == 0 :
objs = bpy . data . objects
#get real meshcount as len(bpy.data.meshes) holds also old meshes
meshCount = 0
for obj in objs :
if obj . type == ' MESH ' :
meshCount + = 1
if obj . mode != ' OBJECT ' : # we want to be in object mode
print ( " ERROR: mesh not in object mode " )
operator . report ( { ' ERROR ' } , " mesh not in object mode " )
return - 1
if meshCount == 0 :
print ( " ERROR: no meshes found " )
operator . report ( { ' ERROR ' } , " no meshes found " )
return - 1
fileID = open ( filepath , " wb " )
# G3DHeader v4
fileID . write ( struct . pack ( " <3cB " , b ' G ' , b ' 3 ' , b ' D ' , 4 ) )
# G3DModelHeaderv4
fileID . write ( struct . pack ( " <HB " , meshCount , 0 ) )
# meshes
#for mesh in bpy.data.meshes:
for obj in objs :
if obj . type != ' MESH ' :
continue
mesh = obj . data . copy ( )
diffuseColor = [ 1.0 , 1.0 , 1.0 ]
specularColor = [ 0.9 , 0.9 , 0.9 ]
opacity = 1.0
textures = 0
if len ( mesh . materials ) > 0 :
# we have a texture, hopefully
material = mesh . materials [ 0 ]
slot = material . texture_slots [ 0 ]
# only look for other textures when we have diffuse
if slot and slot . texture . type == ' IMAGE ' and len ( mesh . uv_textures ) > 0 :
diffuseColor = material . diffuse_color
specularColor = material . specular_color
opacity = material . alpha
textures = 1
texnames = [ ]
texnames . append ( bpy . path . basename ( slot . texture . image . filepath ) )
# specular and normal
for i in range ( 1 , 3 ) :
slot = material . texture_slots [ i ]
if slot and slot . texture . type == ' IMAGE ' :
texnames . append ( bpy . path . basename ( slot . texture . image . filepath ) )
textures | = 1 << i
else :
print ( " WARNING: first texture slot in first material isn ' t of type IMAGE or it ' s not unwrapped, texture ignored " )
operator . report ( { ' WARNING ' } , " first texture slot in first material isn ' t of type IMAGE or it ' s not unwrapped, texture ignored " )
#continue without texture
meshname = mesh . name
frameCount = context . scene . frame_end - context . scene . frame_start + 1
realFaceCount = 0 # real face count (triangles)
indices = [ ] # list of indices
newverts = [ ] # list of vertex indices which need to be duplicated
uvlist = [ ] # list of texcoords
mesh . update ( calc_tessface = True ) # tesselate n-polygons to triangles & quads
if textures :
uvtex = mesh . tessface_uv_textures [ 0 ]
uvlist [ : ] = [ [ 0 ] * 2 for i in range ( len ( mesh . vertices ) ) ]
# blender allows to have multiple texcoords per vertex,
# in g3d format every vertex can only have one texcoord
# -> duplicate vertex
# the dictionary/map vdict collects all the stuff
# index to "unique" vertices from blender
# -> tuple( list of texcoords, list of indices to the duplicated vertex )
vdict = dict ( )
nextIndex = len ( mesh . vertices )
for face in mesh . tessfaces :
# when a vertex is duplicated it gets a new index, so the
# triple of indices describing the face is different too
faceindices = [ ]
realFaceCount + = 1
uvdata = uvtex . data [ face . index ]
for i in range ( 3 ) :
#closure, got rid of copy&paste, still looking weird
def getTexCoords ( ) :
nonlocal nextIndex , vdict , uvlist , newverts
vindex = face . vertices [ i ]
if vindex not in vdict : # new vertex -> add it
vdict [ vindex ] = [ uvdata . uv [ i ] ] , [ vindex ]
uvlist [ vindex ] = uvdata . uv [ i ] # that's a (s,t)-pair
else :
found = False
idx = 0
for ele in vdict [ vindex ] [ 0 ] :
if uvdata . uv [ i ] [ 0 ] == ele [ 0 ] and uvdata . uv [ i ] [ 1 ] == ele [ 1 ] :
found = True
break
idx + = 1
if found : # same vertex and texcoord before
# it could be a different index now, the index of a new
# duplicated vertex
#vindex = vdict[vindex][1][ vdict[vindex][0].index(uvdata.uv[i]) ]
vindex = vdict [ vindex ] [ 1 ] [ idx ]
else : # same vertex as before but with different texcoord -> duplicate
vdict [ vindex ] [ 0 ] . append ( uvdata . uv [ i ] )
vdict [ vindex ] [ 1 ] . append ( nextIndex )
# duplicate vertex because it takes part in different faces
# with different texcoords
newverts . append ( vindex )
uvlist . append ( uvdata . uv [ i ] )
# new index for the duplicated vertex
vindex = nextIndex
nextIndex + = 1
faceindices . append ( vindex )
getTexCoords ( )
indices . extend ( faceindices )
if len ( face . vertices ) == 4 :
faceindices = [ ]
realFaceCount + = 1
for i in [ 0 , 2 , 3 ] :
getTexCoords ( )
indices . extend ( faceindices )
else :
for face in mesh . tessfaces :
realFaceCount + = 1
indices . extend ( face . vertices [ 0 : 3 ] )
if len ( face . vertices ) == 4 :
realFaceCount + = 1
# new face because quad got split
indices . append ( face . vertices [ 0 ] )
indices . append ( face . vertices [ 2 ] )
indices . append ( face . vertices [ 3 ] )
# abort when no triangles as it crashs g3dviewer
if realFaceCount == 0 :
print ( " ERROR: no triangles found " )
operator . report ( { ' ERROR ' } , " no triangles found " )
fileID . close ( )
return - 1
indexCount = realFaceCount * 3
vertexCount = len ( mesh . vertices ) + len ( newverts )
specularPower = 9.999999 # unused, same as old exporter
properties = 0
if mesh . g3d_customColor :
properties | = 1
if mesh . show_double_sided :
properties | = 2
if mesh . g3d_noSelect :
properties | = 4
2015-03-18 20:21:59 +01:00
if mesh . g3d_glow :
properties | = 8
2014-01-17 23:14:40 +01:00
#MeshData
vertices = [ ]
normals = [ ]
fcurrent = context . scene . frame_current
for i in range ( context . scene . frame_start , context . scene . frame_end + 1 ) :
context . scene . frame_set ( i )
#FIXME: not sure what's better: PREVIEW or RENDER settings
m = obj . to_mesh ( context . scene , True , ' RENDER ' )
m . transform ( obj . matrix_world ) # apply object-mode transformations
if toglest :
# rotate from blender to glest orientation
m . transform ( Matrix ( ( ( 1 , 0 , 0 , 0 ) , ( 0 , 0 , 1 , 0 ) , ( 0 , - 1 , 0 , 0 ) , ( 0 , 0 , 0 , 1 ) ) ) )
# transform normals too
m . calc_normals ( )
for vertex in m . vertices :
vertices . extend ( vertex . co )
normals . extend ( vertex . normal )
# duplicate vertices and corresponding normals, for every frame
for nv in newverts :
vertices . extend ( m . vertices [ nv ] . co )
normals . extend ( m . vertices [ nv ] . normal )
context . scene . frame_set ( fcurrent )
if mesh . g3d_fullyOpaque :
opacity = 1.0
# MeshHeader
fileID . write ( struct . pack ( " <64s3I8f2I " ,
bytes ( meshname , " ascii " ) ,
frameCount , vertexCount , indexCount ,
diffuseColor [ 0 ] , diffuseColor [ 1 ] , diffuseColor [ 2 ] ,
specularColor [ 0 ] , specularColor [ 1 ] , specularColor [ 2 ] ,
specularPower , opacity ,
properties , textures
) )
#Texture names
if textures : # only when we have textures
for i in range ( len ( texnames ) ) :
fileID . write ( struct . pack ( " <64s " , bytes ( texnames [ i ] , " ascii " ) ) )
# see G3DMeshdataV4
vertex_format = " < %i f " % int ( frameCount * vertexCount * 3 )
normals_format = " < %i f " % int ( frameCount * vertexCount * 3 )
texturecoords_format = " < %i f " % int ( vertexCount * 2 )
indices_format = " < %i I " % int ( indexCount )
fileID . write ( struct . pack ( vertex_format , * vertices ) )
fileID . write ( struct . pack ( normals_format , * normals ) )
# texcoords
if textures : # only when we have textures
texcoords = [ ]
for uv in uvlist :
texcoords . extend ( uv )
fileID . write ( struct . pack ( texturecoords_format , * texcoords ) )
fileID . write ( struct . pack ( indices_format , * indices ) )
fileID . close ( )
return 0
#---=== Register ===
class G3DPanel ( bpy . types . Panel ) :
#bl_idname = "OBJECT_PT_G3DPanel"
bl_label = " G3D properties "
bl_space_type = ' PROPERTIES '
bl_region_type = ' WINDOW '
bl_context = " data "
@classmethod
def poll ( cls , context ) :
return ( context . object is not None and context . object . type == ' MESH ' )
def draw ( self , context ) :
self . layout . prop ( context . object . data , " g3d_customColor " )
self . layout . prop ( context . object . data , " show_double_sided " , text = " double sided " )
self . layout . prop ( context . object . data , " g3d_noSelect " )
self . layout . prop ( context . object . data , " g3d_fullyOpaque " )
2015-03-18 20:21:59 +01:00
self . layout . prop ( context . object . data , " g3d_glow " )
2014-01-17 23:14:40 +01:00
class ImportG3D ( bpy . types . Operator , ImportHelper ) :
''' Load a G3D file '''
bl_idname = " importg3d.g3d "
bl_label = " Import G3D "
filename_ext = " .g3d "
filter_glob = StringProperty ( default = " *.g3d " , options = { ' HIDDEN ' } )
toblender = bpy . props . BoolProperty (
name = " rotate to Blender orientation " ,
description = " Rotate meshes from Glest to Blender orientation " ,
default = True )
def execute ( self , context ) :
try :
G3DLoader ( self . filepath , self . toblender , self )
except :
import traceback
traceback . print_exc ( )
return { ' CANCELLED ' }
return { ' FINISHED ' }
class ExportG3D ( bpy . types . Operator , ExportHelper ) :
''' Save a G3D file '''
bl_idname = " exportg3d.g3d "
bl_label = " Export G3D "
filename_ext = " .g3d "
filter_glob = StringProperty ( default = " *.g3d " , options = { ' HIDDEN ' } )
#export options
showg3d = bpy . props . BoolProperty (
name = " show G3D afterwards " ,
description = ( " Run g3dviewer to show G3D after export. "
" g3dviewer needs to be in the scripts directory, "
" otherwise the associated program of .g3d is run. " ) ,
default = False )
toglest = bpy . props . BoolProperty (
name = " rotate to glest orientation " ,
description = " Rotate meshes from Blender to Glest orientation " ,
default = True )
def execute ( self , context ) :
try :
res = G3DSaver ( self . filepath , context , self . toglest , self )
if res == 0 and self . showg3d :
print ( " opening g3dviewer with " + self . filepath )
scriptsdir = bpy . utils . script_path_user ( )
dname = os . path . dirname ( self . filepath )
found = False
for f in os . listdir ( scriptsdir ) :
if " g3dviewer " in f :
f = os . path . join ( scriptsdir , f )
if os . path . isfile ( f ) and os . access ( f , os . X_OK ) :
cmd = [ f , self . filepath ]
print ( cmd )
subprocess . Popen ( cmd , cwd = dname )
found = True
# try default associated program
if not found :
if os . name == ' posix ' :
# xdg-open is only a shell script which delegates the job to a
# desktop specific program, e.g. if DE=kde than kde-open
# needs DE environment variable set, otherwise it just throws it
# at the browser, which is not very helpful
print ( " running xdg-open " + self . filepath )
subprocess . Popen ( [ ' xdg-open ' , self . filepath ] , cwd = dname )
elif os . name == ' mac ' :
subprocess . Popen ( [ ' open ' , self . filepath ] , cwd = dname )
elif os . name == ' nt ' :
#os.startfile(self.filepath) # no way to change dir
subprocess . Popen ( [ ' cmd ' , ' /C ' , ' start ' , self . filepath ] , cwd = dname )
except :
import traceback
traceback . print_exc ( )
return { ' CANCELLED ' }
return { ' FINISHED ' }
def menu_func_import ( self , context ) :
self . layout . operator ( ImportG3D . bl_idname , text = " Glest 3D File (.g3d) " )
def menu_func_export ( self , context ) :
self . layout . operator ( ExportG3D . bl_idname , text = " Glest 3D File (.g3d) " )
def register ( ) :
# custom mesh properties
bpy . types . Mesh . g3d_customColor = bpy . props . BoolProperty (
name = " team color " ,
description = " replace alpha channel of texture with team color " )
bpy . types . Mesh . g3d_noSelect = bpy . props . BoolProperty (
name = " non-selectable " ,
description = " click on mesh doesn ' t select unit " )
bpy . types . Mesh . g3d_fullyOpaque = bpy . props . BoolProperty (
name = " fully opaque " ,
description = " sets opacity to 1.0, ignoring what ' s set in materials " )
2015-03-18 20:21:59 +01:00
bpy . types . Mesh . g3d_glow = bpy . props . BoolProperty (
name = " glow " ,
description = " let objects glow like particles " )
2014-01-17 23:14:40 +01:00
bpy . utils . register_module ( __name__ )
bpy . types . INFO_MT_file_import . append ( menu_func_import )
bpy . types . INFO_MT_file_export . append ( menu_func_export )
def unregister ( ) :
bpy . utils . unregister_module ( __name__ )
bpy . types . INFO_MT_file_import . remove ( menu_func_import )
bpy . types . INFO_MT_file_export . remove ( menu_func_export )
if __name__ == ' __main__ ' :
register ( )
# main()
#for obj in bpy.data.objects:
# if obj.type == 'MESH':
# obj.select = True
# bpy.ops.object.delete()
#G3DLoader("import.g3d", True, None)
#for obj in bpy.context.selected_objects:
# obj.select = False # deselect everything, so we get it all
#G3DSaver("test.g3d", bpy.context)