meshes can glow
This commit is contained in:
parent
14f04c5c35
commit
b3cf4bf1fd
|
@ -72,6 +72,7 @@ private:
|
||||||
bool twoSided;
|
bool twoSided;
|
||||||
bool customColor;
|
bool customColor;
|
||||||
bool noSelect;
|
bool noSelect;
|
||||||
|
bool glow;
|
||||||
|
|
||||||
uint32 textureFlags;
|
uint32 textureFlags;
|
||||||
|
|
||||||
|
@ -133,6 +134,7 @@ public:
|
||||||
bool getTwoSided() const {return twoSided;}
|
bool getTwoSided() const {return twoSided;}
|
||||||
bool getCustomTexture() const {return customColor;}
|
bool getCustomTexture() const {return customColor;}
|
||||||
bool getNoSelect() const {return noSelect;}
|
bool getNoSelect() const {return noSelect;}
|
||||||
|
bool getGlow() const {return glow;}
|
||||||
string getName() const {return name;}
|
string getName() const {return name;}
|
||||||
|
|
||||||
uint32 getTextureFlags() const { return textureFlags; }
|
uint32 getTextureFlags() const { return textureFlags; }
|
||||||
|
|
|
@ -43,7 +43,8 @@ enum ModelType{
|
||||||
enum MeshPropertyFlag{
|
enum MeshPropertyFlag{
|
||||||
mpfCustomColor= 1,
|
mpfCustomColor= 1,
|
||||||
mpfTwoSided= 2,
|
mpfTwoSided= 2,
|
||||||
mpfNoSelect= 4
|
mpfNoSelect= 4,
|
||||||
|
mpfGlow= 8
|
||||||
};
|
};
|
||||||
|
|
||||||
enum MeshTexture{
|
enum MeshTexture{
|
||||||
|
|
|
@ -175,6 +175,12 @@ void ModelRendererGl::renderMesh(Mesh *mesh,int renderMode) {
|
||||||
glEnable(GL_CULL_FACE);
|
glEnable(GL_CULL_FACE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(renderMode==rmNormal && mesh->getGlow()==true){
|
||||||
|
// glow on
|
||||||
|
glDisable(GL_LIGHTING);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
||||||
|
}
|
||||||
|
|
||||||
if(this->colorPickingMode == false) {
|
if(this->colorPickingMode == false) {
|
||||||
//set color
|
//set color
|
||||||
if(renderColors) {
|
if(renderColors) {
|
||||||
|
@ -324,6 +330,12 @@ void ModelRendererGl::renderMesh(Mesh *mesh,int renderMode) {
|
||||||
glDrawRangeElements(GL_TRIANGLES, 0, vertexCount-1, indexCount, GL_UNSIGNED_INT, mesh->getIndices());
|
glDrawRangeElements(GL_TRIANGLES, 0, vertexCount-1, indexCount, GL_UNSIGNED_INT, mesh->getIndices());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// glow
|
||||||
|
if(renderMode==rmNormal && mesh->getGlow()==true){
|
||||||
|
// glow off
|
||||||
|
glEnable(GL_LIGHTING);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
}
|
||||||
//assertions
|
//assertions
|
||||||
assertGl();
|
assertGl();
|
||||||
}
|
}
|
||||||
|
|
|
@ -225,6 +225,7 @@ Mesh::Mesh() {
|
||||||
twoSided= false;
|
twoSided= false;
|
||||||
customColor= false;
|
customColor= false;
|
||||||
noSelect= false;
|
noSelect= false;
|
||||||
|
glow= false;
|
||||||
|
|
||||||
textureFlags=0;
|
textureFlags=0;
|
||||||
|
|
||||||
|
@ -446,6 +447,7 @@ void Mesh::loadV2(int meshIndex, const string &dir, FILE *f, TextureManager *tex
|
||||||
twoSided= false;
|
twoSided= false;
|
||||||
customColor= false;
|
customColor= false;
|
||||||
noSelect= false;
|
noSelect= false;
|
||||||
|
glow= false;
|
||||||
|
|
||||||
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Load v2, this = %p Found meshHeader.hasTexture = %d, texName [%s] mtDiffuse = %d meshIndex = %d modelFile [%s]\n",this,meshHeader.hasTexture,toLower(reinterpret_cast<char*>(meshHeader.texName)).c_str(),mtDiffuse,meshIndex,modelFile.c_str());
|
if(SystemFlags::VERBOSE_MODE_ENABLED) printf("Load v2, this = %p Found meshHeader.hasTexture = %d, texName [%s] mtDiffuse = %d meshIndex = %d modelFile [%s]\n",this,meshHeader.hasTexture,toLower(reinterpret_cast<char*>(meshHeader.texName)).c_str(),mtDiffuse,meshIndex,modelFile.c_str());
|
||||||
|
|
||||||
|
@ -586,6 +588,7 @@ void Mesh::loadV3(int meshIndex, const string &dir, FILE *f,
|
||||||
twoSided= (meshHeader.properties & mp3TwoSided) != 0;
|
twoSided= (meshHeader.properties & mp3TwoSided) != 0;
|
||||||
customColor= (meshHeader.properties & mp3CustomColor) != 0;
|
customColor= (meshHeader.properties & mp3CustomColor) != 0;
|
||||||
noSelect = false;
|
noSelect = false;
|
||||||
|
glow = false;
|
||||||
|
|
||||||
textureFlags= 0;
|
textureFlags= 0;
|
||||||
if((meshHeader.properties & mp3NoTexture) != mp3NoTexture) {
|
if((meshHeader.properties & mp3NoTexture) != mp3NoTexture) {
|
||||||
|
@ -781,6 +784,7 @@ void Mesh::load(int meshIndex, const string &dir, FILE *f, TextureManager *textu
|
||||||
customColor= (meshHeader.properties & mpfCustomColor) != 0;
|
customColor= (meshHeader.properties & mpfCustomColor) != 0;
|
||||||
twoSided= (meshHeader.properties & mpfTwoSided) != 0;
|
twoSided= (meshHeader.properties & mpfTwoSided) != 0;
|
||||||
noSelect= (meshHeader.properties & mpfNoSelect) != 0;
|
noSelect= (meshHeader.properties & mpfNoSelect) != 0;
|
||||||
|
glow= (meshHeader.properties & mpfGlow) != 0;
|
||||||
|
|
||||||
//material
|
//material
|
||||||
diffuseColor= Vec3f(meshHeader.diffuseColor);
|
diffuseColor= Vec3f(meshHeader.diffuseColor);
|
||||||
|
@ -900,6 +904,9 @@ void Mesh::save(int meshIndex, const string &dir, FILE *f, TextureManager *textu
|
||||||
if(noSelect) {
|
if(noSelect) {
|
||||||
meshHeader.properties |= mpfNoSelect;
|
meshHeader.properties |= mpfNoSelect;
|
||||||
}
|
}
|
||||||
|
if(glow) {
|
||||||
|
meshHeader.properties |= mpfGlow;
|
||||||
|
}
|
||||||
|
|
||||||
meshHeader.textures = textureFlags;
|
meshHeader.textures = textureFlags;
|
||||||
fwrite(&meshHeader, sizeof(MeshHeader), 1, f);
|
fwrite(&meshHeader, sizeof(MeshHeader), 1, f);
|
||||||
|
@ -1532,6 +1539,7 @@ void Mesh::copyInto(Mesh *dest, bool ignoreInterpolationData,
|
||||||
dest->twoSided = this->twoSided;
|
dest->twoSided = this->twoSided;
|
||||||
dest->customColor = this->customColor;
|
dest->customColor = this->customColor;
|
||||||
dest->noSelect = this->noSelect;
|
dest->noSelect = this->noSelect;
|
||||||
|
dest->glow = this->glow;
|
||||||
|
|
||||||
dest->textureFlags = this->textureFlags;
|
dest->textureFlags = this->textureFlags;
|
||||||
|
|
||||||
|
@ -1605,6 +1613,7 @@ void Model::autoJoinMeshFrames() {
|
||||||
string("_") + intToStr(mesh.getCustomTexture()) +
|
string("_") + intToStr(mesh.getCustomTexture()) +
|
||||||
string("_") + intToStr(mesh.getNoSelect()) +
|
string("_") + intToStr(mesh.getNoSelect()) +
|
||||||
string("_") + floatToStr(mesh.getOpacity()) +
|
string("_") + floatToStr(mesh.getOpacity()) +
|
||||||
|
string("_") + floatToStr(mesh.getGlow()) +
|
||||||
string("_") + mesh.getDiffuseColor().getString() +
|
string("_") + mesh.getDiffuseColor().getString() +
|
||||||
string("_") + mesh.getSpecularColor().getString() +
|
string("_") + mesh.getSpecularColor().getString() +
|
||||||
string("_") + floatToStr(mesh.getSpecularPower());
|
string("_") + floatToStr(mesh.getSpecularPower());
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,729 +0,0 @@
|
||||||
###########################################################################
|
|
||||||
# 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)
|
|
||||||
#
|
|
||||||
# Updated Jan 2011 by Mark Vejvoda (SoftCoder) to properly import animations
|
|
||||||
# from G3D into Blender
|
|
||||||
#
|
|
||||||
# Started Date: 07 June 2005 Put Public 20 June 2005
|
|
||||||
# Ver: 0.01 Beta Exporter ripped off because work in Progress
|
|
||||||
# Distributed under the GNU PUBLIC LICENSE for www.megaglest.org and glest.org
|
|
||||||
###########################################################################
|
|
||||||
#NOTE:
|
|
||||||
# Copy this Script AND g3d_logo.png into .Blender\scripts
|
|
||||||
# directory then start inside blender Scripts window
|
|
||||||
# "Update Menus" after that this Script here is accesible
|
|
||||||
# as 'Wizards' G3d Fileformat Im/Exporter
|
|
||||||
#ToDo:
|
|
||||||
#Exporter Bughunt he will be rejoined next release
|
|
||||||
#Maybe Integrate UV Painter to Generate UVMaps from Blender Material and procedural Textures
|
|
||||||
#will be nice to paint wireframe too, so that one can easyly load into Paintprogram and see where to paint
|
|
||||||
#(Already possible through Blender functions so at the end of the list :-) )
|
|
||||||
###########################################################################
|
|
||||||
|
|
||||||
bl_info = {
|
|
||||||
"name": "MegaGlest G3D Fileformat Import/Exporter",
|
|
||||||
"description": "Imports and Exports the Glest fileformat V3/V4 (.g3d)",
|
|
||||||
"author": "Andreas Becker, Mark Vejvoda, William Zheng",
|
|
||||||
"version": (1,1),
|
|
||||||
"blender": (2, 5, 7),
|
|
||||||
"api": 35622,
|
|
||||||
'location': 'File > Import-Export',
|
|
||||||
"warning": '',
|
|
||||||
"wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/"\
|
|
||||||
"Scripts/Import-Export/MegaGlest_G3D",
|
|
||||||
"tracker_url": "http://projects.blender.org/tracker/index.php?"\
|
|
||||||
"func=detail&aid=<number>",
|
|
||||||
"category": "Import-Export"
|
|
||||||
}
|
|
||||||
|
|
||||||
"""
|
|
||||||
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{
|
|
||||||
mpfTwoSided= 1,
|
|
||||||
mpfCustomColor= 2,
|
|
||||||
};
|
|
||||||
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, only 0x1 is currently used, indicating that there is a diffuse texture in this mesh.
|
|
||||||
================================
|
|
||||||
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. In practice since Glest only uses 1 texture for each mesh the number of texture names should be 0 or 1.
|
|
||||||
================================
|
|
||||||
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
|
|
||||||
"""
|
|
||||||
###########################################################################
|
|
||||||
# Importing Structures needed (must later verify if i need them really all)
|
|
||||||
###########################################################################
|
|
||||||
import sys, struct, string, types
|
|
||||||
from types import *
|
|
||||||
import os
|
|
||||||
from os import path
|
|
||||||
from os.path import dirname
|
|
||||||
|
|
||||||
import bpy
|
|
||||||
from bpy.props import StringProperty
|
|
||||||
from io_utils import ImportHelper, ExportHelper
|
|
||||||
import mathutils
|
|
||||||
import math
|
|
||||||
|
|
||||||
import re
|
|
||||||
from string import *
|
|
||||||
from struct import *
|
|
||||||
|
|
||||||
|
|
||||||
###########################################################################
|
|
||||||
# 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
|
|
||||||
|
|
||||||
###########################################################################
|
|
||||||
# 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)
|
|
||||||
|
|
||||||
#print(type(data)); print(repr(data))
|
|
||||||
#self.id = bytes(data[0]).decode() + bytes(data[1]).decode() + bytes(data[2]).decode()
|
|
||||||
self.id = ''.join(item.decode() for item in data[0:3])
|
|
||||||
#self.id = data[0:3]
|
|
||||||
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.texturefilepath = None
|
|
||||||
else:
|
|
||||||
self.texturefilepath = "".join([x.decode("ascii", "ignore") for x in data[7:-1] if x.decode("ascii", "ignore") in string.printable])
|
|
||||||
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 __init__(self,fileID):
|
|
||||||
temp = fileID.read(struct.calcsize(self.binary_format))
|
|
||||||
data = struct.unpack(self.binary_format,temp)
|
|
||||||
self.meshname = "".join([x.decode("ascii", "ignore") for x in data[0:64] if x.decode("ascii", "ignore") in string.printable]) #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 flag
|
|
||||||
if self.properties & 1: #PropertyBit is Mesh TwoSided ?
|
|
||||||
self.istwosided = True
|
|
||||||
else:
|
|
||||||
self.istwosided = False
|
|
||||||
if self.properties & 2: #PropertyBit is Mesh Alpha Channel custom Color in Game ?
|
|
||||||
self.customalpha = True
|
|
||||||
else:
|
|
||||||
self.customalpha = False
|
|
||||||
if self.textures & 1: #PropertyBit is Mesh Textured ?
|
|
||||||
temp = fileID.read(struct.calcsize(self.texname_format))
|
|
||||||
data = struct.unpack(self.texname_format,temp)
|
|
||||||
self.texturefilepath = "".join([x.decode("ascii", "ignore") for x in data[0:-1] if x.decode("ascii", "ignore") in string.printable])
|
|
||||||
self.hastexture = True
|
|
||||||
else:
|
|
||||||
self.hastexture = False
|
|
||||||
self.texturefilepath = None
|
|
||||||
|
|
||||||
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 = "<%if" % int(header.framecount * header.vertexcount * 3)
|
|
||||||
#The same for Normals
|
|
||||||
normals_format = "<%if" % int(header.normalframecount * header.vertexcount * 3)
|
|
||||||
#Same here but Textures are 2D so only 2 Floats needed for Position inside Texture Bitmap
|
|
||||||
texturecoords_format = "<%if" % int(header.texturecoordframecount * header.vertexcount * 2)
|
|
||||||
#Colors in format RGBA
|
|
||||||
colors_format = "<%if" % int(header.colorframecount * 4)
|
|
||||||
#Indices
|
|
||||||
indices_format = "<%iI" % 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 = "<%if" % int(header.framecount * header.vertexcount * 3)
|
|
||||||
#The same for Normals
|
|
||||||
normals_format = "<%if" % int(header.framecount * header.vertexcount * 3)
|
|
||||||
#Same here but Textures are 2D so only 2 Floats needed for Position inside Texture Bitmap
|
|
||||||
texturecoords_format = "<%if" % int(header.vertexcount * 2)
|
|
||||||
#Indices
|
|
||||||
indices_format = "<%iI" % 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)))
|
|
||||||
|
|
||||||
def createMesh(filepath,header,data): #Create a Mesh inside Blender
|
|
||||||
mesh = bpy.data.meshes.new(header.meshname) #New Mesh
|
|
||||||
meshobj = bpy.data.objects.new(header.meshname,mesh) #New Object for the new Mesh
|
|
||||||
#meshobj.link(mesh) #Link the Mesh to the Object
|
|
||||||
sceneID.objects.link (meshobj) #Link the Object to the actual Scene
|
|
||||||
uvcoords = []
|
|
||||||
if header.hastexture: #Load Texture when assigned
|
|
||||||
texturefile = dirname(filepath)+os.sep+header.texturefilepath
|
|
||||||
texture = bpy.data.images.load(texturefile)
|
|
||||||
#mesh.hasFaceUV(1) #Because has Texture must have UV Coordinates
|
|
||||||
#mesh.hasVertexUV(1) #UV for each Vertex not only for Face
|
|
||||||
for x in range(0,len(data.texturecoords),2): #Prepare the UVs
|
|
||||||
uvcoords.append((data.texturecoords[x],data.texturecoords[x+1]))
|
|
||||||
else: #Has no Texture
|
|
||||||
texture = None
|
|
||||||
#mesh.hasFaceUV(0)
|
|
||||||
if header.isv4:
|
|
||||||
mesh.col(int(255*header.diffusecolor[0]),int(255*header.diffusecolor[1]),int(255*header.diffusecolor[2]),int(255*header.opacity))
|
|
||||||
if header.istwosided:
|
|
||||||
#mesh.setMode("TwoSided","AutoSmooth") #Added Autosmooth because i think it does no harm
|
|
||||||
mesh.show_double_sided = True
|
|
||||||
mesh.use_auto_smooth = True
|
|
||||||
else: #I wonder why TwoSided here takes no effect on Faces ??
|
|
||||||
mesh.use_auto_smooth = True #Same here
|
|
||||||
y=0
|
|
||||||
|
|
||||||
mesh.vertices.add(header.vertexcount)
|
|
||||||
vertexIndex=0;
|
|
||||||
|
|
||||||
for x in range(0,header.vertexcount*3,3): #Get the Vertices and Normals into empty Mesh
|
|
||||||
#and load UV coords when needed per Vertex
|
|
||||||
mesh.vertices[vertexIndex].co[0] = data.vertices[x]
|
|
||||||
mesh.vertices[vertexIndex].co[1] = data.vertices[x+1]
|
|
||||||
mesh.vertices[vertexIndex].co[2] = data.vertices[x+2]
|
|
||||||
mesh.vertices[vertexIndex].normal[0] = data.normals[x]
|
|
||||||
mesh.vertices[vertexIndex].normal[1] = data.normals[x+1]
|
|
||||||
mesh.vertices[vertexIndex].normal[2] = data.normals[x+2]
|
|
||||||
#if header.hastexture: ###Have to Check this per Vertex UV### may not be needed
|
|
||||||
# vertex.uvco[0] = data.texturecoords[y] ###but seems to do no harm :-)###
|
|
||||||
# vertex.uvco[1] = data.texturecoords[y+1]
|
|
||||||
#mesh.verts.append(vertex)
|
|
||||||
y= y+2
|
|
||||||
vertexIndex = vertexIndex + 1
|
|
||||||
|
|
||||||
mesh.faces.add(len(data.indices))
|
|
||||||
faceIndex=0;
|
|
||||||
|
|
||||||
for i in range(0,len(data.indices),3): #Build Faces into Mesh
|
|
||||||
# face = NMesh.Face()
|
|
||||||
mesh.faces[faceIndex].use_smooth = 1
|
|
||||||
|
|
||||||
#print(type(data.indices[i])); print(repr(data.indices[i]))
|
|
||||||
#print(type(mesh.vertices[data.indices[i]])); print(repr(mesh.vertices[data.indices[i]]))
|
|
||||||
#print(type(mesh.vertices[data.indices[i]].co[0])); print(repr(mesh.vertices[data.indices[i]].co[0]))
|
|
||||||
#print(type(mesh.faces[faceIndex].vertices[0])); print(repr(mesh.faces[faceIndex].vertices[0]))
|
|
||||||
|
|
||||||
mesh.faces[faceIndex].vertices[0] = mesh.vertices[data.indices[i]].index
|
|
||||||
mesh.faces[faceIndex].vertices[1] = mesh.vertices[data.indices[i+1]].index
|
|
||||||
mesh.faces[faceIndex].vertices[2] = mesh.vertices[data.indices[i+2]].index
|
|
||||||
if header.hastexture: #Put UV in Faces too and assign Texture when textured
|
|
||||||
mesh.uv_textures.new()
|
|
||||||
tface = mesh.uv_textures[0].data[faceIndex]
|
|
||||||
tface.uv1 = uvcoords[data.indices[i]]
|
|
||||||
tface.uv2 = uvcoords[data.indices[i+1]]
|
|
||||||
tface.uv3 = uvcoords[data.indices[i+2]]
|
|
||||||
tface.image = texture
|
|
||||||
tface.use_image = True
|
|
||||||
if header.istwosided: #Finaly found out how it works :-)
|
|
||||||
tface.use_twoside = True
|
|
||||||
|
|
||||||
# face.uv.append(uvcoords[data.indices[i]])
|
|
||||||
# face.uv.append(uvcoords[data.indices[i+1]])
|
|
||||||
# face.uv.append(uvcoords[data.indices[i+2]])
|
|
||||||
# face.image = texture
|
|
||||||
#if header.istwosided: #Finaly found out how it works :-)
|
|
||||||
# face.mode |= NMesh.FaceModes['TWOSIDE'] #Really Bad documented this works to set all modes
|
|
||||||
# mesh.faces.append(face)
|
|
||||||
faceIndex = faceIndex + 1
|
|
||||||
|
|
||||||
mesh.update() #Update changes to Mesh
|
|
||||||
#objdata = meshobj.data #Access Data of the Meshobject only the Obj has Key/IPOData
|
|
||||||
imported.append(meshobj) #Add to Imported Objects
|
|
||||||
for x in range(header.framecount): #Put in Vertex Positions for Keyanimation
|
|
||||||
for i in range(0,header.vertexcount*3,3):
|
|
||||||
vertexIndex = int(i/3)
|
|
||||||
dataIndex = x*header.vertexcount*3 + i
|
|
||||||
|
|
||||||
#print(type(vertexIndex)); print(repr(vertexIndex))
|
|
||||||
#print(type(dataIndex)); print(repr(dataIndex))
|
|
||||||
#print(type(mesh.vertices[vertexIndex].co[0])); print(repr(mesh.vertices[vertexIndex].co[0]))
|
|
||||||
#print(type(data.vertices[dataIndex])); print(repr(data.vertices[dataIndex]))
|
|
||||||
|
|
||||||
mesh.vertices[vertexIndex].co[0] = data.vertices[dataIndex]
|
|
||||||
mesh.vertices[vertexIndex].co[1] = data.vertices[dataIndex +1]
|
|
||||||
mesh.vertices[vertexIndex].co[2] = data.vertices[dataIndex +2]
|
|
||||||
#objdata.verts[i/3].co[0]= data.vertices[x*header.vertexcount*3 + i]
|
|
||||||
#objdata.verts[i/3].co[1]= data.vertices[x*header.vertexcount*3 + i +1]
|
|
||||||
#objdata.verts[i/3].co[2]= data.vertices[x*header.vertexcount*3 + i +2 ]
|
|
||||||
#objdata.update()
|
|
||||||
mesh.update()
|
|
||||||
#mesh.insertKey(x+1,"absolute")
|
|
||||||
shape_key = meshobj.shape_key_add()
|
|
||||||
|
|
||||||
#Blender.Set("curframe",x)
|
|
||||||
sceneID.frame_current = x
|
|
||||||
|
|
||||||
# Make the keys animate in the 3d view.
|
|
||||||
#key = mesh.key
|
|
||||||
#key.relative = False
|
|
||||||
bpy.data.shape_keys[0].use_relative = False
|
|
||||||
|
|
||||||
# Add an IPO to teh Key
|
|
||||||
#ipo = Blender.Ipo.New('Key', 'md2')
|
|
||||||
#key.ipo = ipo
|
|
||||||
# Add a curve to the IPO
|
|
||||||
#curve = ipo.addCurve('Basis')
|
|
||||||
#curve = bpy.data.curves.new('Key',CURVE)
|
|
||||||
|
|
||||||
# Add 2 points to cycle through the frames.
|
|
||||||
#curve.append((1, 0))
|
|
||||||
#curve.append((header.framecount, (header.framecount-1)/10.0))
|
|
||||||
#curve.interpolation = Blender.IpoCurve.InterpTypes.LINEAR
|
|
||||||
|
|
||||||
return
|
|
||||||
###########################################################################
|
|
||||||
# Import
|
|
||||||
###########################################################################
|
|
||||||
def G3DLoader(filepath): #Main Import Routine
|
|
||||||
global imported, sceneID
|
|
||||||
print ("\nNow Importing File : ", filepath)
|
|
||||||
fileID = open(filepath,"rb")
|
|
||||||
header = G3DHeader(fileID)
|
|
||||||
print("\nHeader ID : ", header.id)
|
|
||||||
print("Version : ", str(header.version))
|
|
||||||
if header.id != "G3D":
|
|
||||||
print("This is Not a G3D Model File")
|
|
||||||
fileID.close
|
|
||||||
return
|
|
||||||
if header.version not in (3, 4):
|
|
||||||
print("The Version of this G3D File is not Supported")
|
|
||||||
fileID.close
|
|
||||||
return
|
|
||||||
in_editmode = 0 #Must leave Editmode when active
|
|
||||||
if bpy.context.mode == 'EDIT': in_editmode = 1
|
|
||||||
if in_editmode: bpy.ops.object.editmode_toggle()
|
|
||||||
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=str(Blender.sys.makename(filepath,"",1)) #Generate the Base filepath without Path + extension
|
|
||||||
basename="g3d_mesh"
|
|
||||||
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("\nMesh 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.texturefilepath))
|
|
||||||
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)
|
|
||||||
fileID.close
|
|
||||||
print("Imported Objects: ", imported)
|
|
||||||
sceneID.frame_start = 1 #Yeah finally found this Options :-)
|
|
||||||
sceneID.frame_end = maxframe #Set it correctly to the last Animationstep :-))))
|
|
||||||
sceneID.frame_current = 1 #Why the Heck are the above Options not here accessible ????
|
|
||||||
anchor = bpy.data.objects.new(basename,None) #Build an "empty" to Parent all meshes to it for easy handling
|
|
||||||
sceneID.objects.link(anchor) #Link it to current Scene
|
|
||||||
#anchor.makeParent(imported) #Make it Parent for all imported Meshes
|
|
||||||
anchor.select = True #Select it
|
|
||||||
if in_editmode: bpy.ops.object.editmode_toggle() # Be nice and restore Editmode when was active
|
|
||||||
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 = False
|
|
||||||
print("\nMesh 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 : ", meshheader.diffusecolor)
|
|
||||||
print("specularcolor : ", meshheader.specularcolor)
|
|
||||||
print("specularpower : ", meshheader.specularpower)
|
|
||||||
print("opacity : ", meshheader.opacity)
|
|
||||||
print("properties : ", str(meshheader.properties))
|
|
||||||
print("textures : ", str(meshheader.textures))
|
|
||||||
print("texturename : ", str(meshheader.texturefilepath))
|
|
||||||
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)
|
|
||||||
fileID.close
|
|
||||||
sceneID.frame_start = 1 #Yeah finally found this Options :-)
|
|
||||||
sceneID.frame_end = maxframe #Set it correctly to the last Animationstep :-))))
|
|
||||||
sceneID.frame_current = 1 #Why the Heck are the above Options not here accessible ????
|
|
||||||
anchor = bpy.data.objects.new(basename,None) #Build an "empty" to Parent all meshes to it for easy handling
|
|
||||||
sceneID.objects.link(anchor) #Link it to current Scene
|
|
||||||
#anchor.parent = imported #Make it Parent for all imported Meshes
|
|
||||||
anchor.select = True #Select it
|
|
||||||
if in_editmode: bpy.ops.object.editmode_toggle() # Be nice and restore Editmode when was active
|
|
||||||
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):
|
|
||||||
print ("\nNow Exporting File: " + filepath)
|
|
||||||
fileID = open(filepath,"wb")
|
|
||||||
|
|
||||||
# G3DHeader v4
|
|
||||||
fileID.write(struct.pack("<3cB", b'G', b'3', b'D', 4))
|
|
||||||
#get real meshcount as len(bpy.data.meshes) holds also old meshes
|
|
||||||
meshCount = 0
|
|
||||||
for obj in bpy.data.objects:#context.selected_objects:
|
|
||||||
if obj.type == 'MESH':
|
|
||||||
meshCount += 1
|
|
||||||
# G3DModelHeaderv4
|
|
||||||
fileID.write(struct.pack("<HB", meshCount, 0))
|
|
||||||
# meshes
|
|
||||||
#for mesh in bpy.data.meshes:
|
|
||||||
for obj in bpy.data.objects:#context.selected_objects:
|
|
||||||
if obj.type != 'MESH':
|
|
||||||
continue
|
|
||||||
mesh = obj.data
|
|
||||||
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]
|
|
||||||
if material.active_texture.type == 'IMAGE':
|
|
||||||
diffuseColor = material.diffuse_color
|
|
||||||
specularColor = material.specular_color
|
|
||||||
opacity = material.alpha
|
|
||||||
textures = 1
|
|
||||||
texname = bpy.path.basename(material.active_texture.image.filepath)
|
|
||||||
else:
|
|
||||||
#FIXME: needs warning in gui
|
|
||||||
print("active texture in first material isn't of type IMAGE")
|
|
||||||
|
|
||||||
meshname = mesh.name
|
|
||||||
frameCount = context.scene.frame_end - context.scene.frame_start +1
|
|
||||||
#Real face count (only use triangle)
|
|
||||||
realFaceCount = 0
|
|
||||||
indices=[]
|
|
||||||
newverts=[]
|
|
||||||
if textures == 1:
|
|
||||||
uvtex = mesh.uv_textures[0]
|
|
||||||
uvlist = []
|
|
||||||
uvlist[:] = [[0]*2 for i in range(len(mesh.vertices))]
|
|
||||||
s = set()
|
|
||||||
for face in mesh.faces:
|
|
||||||
faceindices = [] # we create new faces when duplicating vertices
|
|
||||||
realFaceCount += 1
|
|
||||||
uvdata = uvtex.data[face.index]
|
|
||||||
for i in range(3):
|
|
||||||
vindex = face.vertices[i]
|
|
||||||
if vindex not in s:
|
|
||||||
s.add(vindex)
|
|
||||||
uvlist[vindex] = uvdata.uv[i]
|
|
||||||
elif uvlist[vindex] != uvdata.uv[i]:
|
|
||||||
# duplicate vertex because it takes part in different faces
|
|
||||||
# with different texcoords
|
|
||||||
newverts.append(vindex)
|
|
||||||
uvlist.append(uvdata.uv[i])
|
|
||||||
#FIXME: probably better with some counter
|
|
||||||
vindex = len(mesh.vertices) + len(newverts) -1
|
|
||||||
|
|
||||||
faceindices.append(vindex)
|
|
||||||
indices.extend(faceindices)
|
|
||||||
|
|
||||||
#FIXME: stupid copy&paste
|
|
||||||
if len(face.vertices) == 4:
|
|
||||||
faceindices = []
|
|
||||||
realFaceCount += 1
|
|
||||||
for i in [0,2,3]:
|
|
||||||
vindex = face.vertices[i]
|
|
||||||
if vindex not in s:
|
|
||||||
s.add(vindex)
|
|
||||||
uvlist[vindex] = uvdata.uv[i]
|
|
||||||
elif uvlist[vindex] != uvdata.uv[i]:
|
|
||||||
# duplicate vertex because it takes part in different faces
|
|
||||||
# with different texcoords
|
|
||||||
newverts.append(vindex)
|
|
||||||
uvlist.append(uvdata.uv[i])
|
|
||||||
#FIXME: probably better with some counter
|
|
||||||
vindex = len(mesh.vertices) + len(newverts) -1
|
|
||||||
|
|
||||||
faceindices.append(vindex)
|
|
||||||
indices.extend(faceindices)
|
|
||||||
else:
|
|
||||||
for face in mesh.faces:
|
|
||||||
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])
|
|
||||||
|
|
||||||
|
|
||||||
#FIXME: abort when no triangles as it crashs g3dviewer
|
|
||||||
if realFaceCount == 0:
|
|
||||||
print("no triangles found")
|
|
||||||
indexCount = realFaceCount * 3
|
|
||||||
vertexCount = len(mesh.vertices) + len(newverts)
|
|
||||||
specularPower = 9.999999 # unused, same as old exporter
|
|
||||||
properties = 0
|
|
||||||
if mesh.show_double_sided:
|
|
||||||
properties |= 1
|
|
||||||
if textures==1 and mesh.materials[0].use_face_texture_alpha:
|
|
||||||
properties |= 2
|
|
||||||
|
|
||||||
#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: this is hacky
|
|
||||||
if obj.find_armature() == None:
|
|
||||||
m = mesh.copy()
|
|
||||||
m.transform(obj.matrix_world)
|
|
||||||
else:
|
|
||||||
#FIXME: not sure what's better: PREVIEW or RENDER settings
|
|
||||||
m = obj.to_mesh(context.scene, True, 'RENDER')
|
|
||||||
for vertex in m.vertices:
|
|
||||||
vertices.extend(vertex.co)
|
|
||||||
normals.extend(vertex.normal)
|
|
||||||
for nv in newverts:
|
|
||||||
vertices.extend(m.vertices[nv].co)
|
|
||||||
normals.extend(m.vertices[nv].normal)
|
|
||||||
context.scene.frame_set(fcurrent)
|
|
||||||
|
|
||||||
# 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 == 1: # only when we have one
|
|
||||||
fileID.write(struct.pack("<64s", bytes(texname, "ascii")))
|
|
||||||
|
|
||||||
# see G3DMeshdataV4
|
|
||||||
vertex_format = "<%if" % int(frameCount * vertexCount * 3)
|
|
||||||
normals_format = "<%if" % int(frameCount * vertexCount * 3)
|
|
||||||
texturecoords_format = "<%if" % int(vertexCount * 2)
|
|
||||||
indices_format = "<%iI" % int(indexCount)
|
|
||||||
|
|
||||||
fileID.write(struct.pack(vertex_format, *vertices))
|
|
||||||
fileID.write(struct.pack(normals_format, *normals))
|
|
||||||
|
|
||||||
# texcoords
|
|
||||||
if textures == 1: # only when we have one
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
#---=== Register ===
|
|
||||||
class ImportG3D(bpy.types.Operator, ImportHelper):
|
|
||||||
'''Import from G3D file format (.g3d)'''
|
|
||||||
bl_idname = "import_mg.g3d"
|
|
||||||
bl_label = "Import G3D files"
|
|
||||||
|
|
||||||
filepath_ext = ".g3d"
|
|
||||||
filter_glob = StringProperty(default="*.g3d", options={'HIDDEN'})
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
try:
|
|
||||||
G3DLoader(**self.as_keywords(ignore=("filter_glob",)))
|
|
||||||
except:
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
return {'CANCELLED'}
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
class ExportG3D(bpy.types.Operator, ExportHelper):
|
|
||||||
'''Export to G3D file format (.g3d)'''
|
|
||||||
bl_idname = "export_mg.g3d"
|
|
||||||
bl_label = "Export G3D files"
|
|
||||||
|
|
||||||
filename_ext = ".g3d"
|
|
||||||
filter_glob = StringProperty(default="*.g3d", options={'HIDDEN'})
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
try:
|
|
||||||
G3DSaver(self.filepath, context)
|
|
||||||
except:
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
|
|
||||||
return {'CANCELLED'}
|
|
||||||
|
|
||||||
return {'FINISHED'}
|
|
||||||
|
|
||||||
def menu_func_import(self, context):
|
|
||||||
self.layout.operator(ImportG3D.bl_idname, text="MegaGlest 3D File (.g3d)")
|
|
||||||
|
|
||||||
def menu_func_export(self, context):
|
|
||||||
self.layout.operator(ExportG3D.bl_idname, text="MegaGlest 3D File (.g3d)")
|
|
||||||
|
|
||||||
def register():
|
|
||||||
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()
|
|
||||||
|
|
|
@ -1,700 +0,0 @@
|
||||||
# -*- 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)
|
|
||||||
#
|
|
||||||
# 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{
|
|
||||||
# mpfTwoSided= 1,
|
|
||||||
# mpfCustomColor= 2,
|
|
||||||
#};
|
|
||||||
#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, only 0x1 is currently used, indicating that there is a diffuse texture in this mesh.
|
|
||||||
#================================
|
|
||||||
#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. In practice since Glest only uses 1 texture for each mesh the number of texture names should be 0 or 1.
|
|
||||||
#================================
|
|
||||||
#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",
|
|
||||||
"author": "William Zheng (corrected by MrPostiga)",
|
|
||||||
"version": (0, 1, 1),
|
|
||||||
"blender": (2, 6, 0),
|
|
||||||
"api": 36079,
|
|
||||||
"location": "File > Import-Export",
|
|
||||||
"description": "Import/Export .g3d file",
|
|
||||||
"warning": "",
|
|
||||||
"wiki_url": "",
|
|
||||||
"tracker_url": "",
|
|
||||||
"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 sys, struct, string, types
|
|
||||||
from types import *
|
|
||||||
import os
|
|
||||||
from os import path
|
|
||||||
from os.path import dirname
|
|
||||||
###########################################################################
|
|
||||||
# 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.texturefilename = None
|
|
||||||
else:
|
|
||||||
self.texturefilename = "".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 __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 flag
|
|
||||||
if self.properties & 1: #PropertyBit is Mesh TwoSided ?
|
|
||||||
self.istwosided = True
|
|
||||||
else:
|
|
||||||
self.istwosided = False
|
|
||||||
if self.properties & 2: #PropertyBit is Mesh Alpha Channel custom Color in Game ?
|
|
||||||
self.customalpha = True
|
|
||||||
else:
|
|
||||||
self.customalpha = False
|
|
||||||
if self.textures & 1: #PropertyBit is Mesh Textured ?
|
|
||||||
temp = fileID.read(struct.calcsize(self.texname_format))
|
|
||||||
data = struct.unpack(self.texname_format,temp)
|
|
||||||
self.texturefilename = "".join([str(x, "ascii") for x in data[0:-1] if x[0] < 127 ])
|
|
||||||
self.hastexture = True
|
|
||||||
else:
|
|
||||||
self.hastexture = False
|
|
||||||
self.texturefilename = None
|
|
||||||
|
|
||||||
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 = "<%if" % int(header.framecount * header.vertexcount * 3)
|
|
||||||
#The same for Normals
|
|
||||||
normals_format = "<%if" % int(header.normalframecount * header.vertexcount * 3)
|
|
||||||
#Same here but Textures are 2D so only 2 Floats needed for Position inside Texture Bitmap
|
|
||||||
texturecoords_format = "<%if" % int(header.texturecoordframecount * header.vertexcount * 2)
|
|
||||||
#Colors in format RGBA
|
|
||||||
colors_format = "<%if" % int(header.colorframecount * 4)
|
|
||||||
#Indices
|
|
||||||
indices_format = "<%iI" % 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 = "<%if" % int(header.framecount * header.vertexcount * 3)
|
|
||||||
#The same for Normals
|
|
||||||
normals_format = "<%if" % int(header.framecount * header.vertexcount * 3)
|
|
||||||
#Same here but Textures are 2D so only 2 Floats needed for Position inside Texture Bitmap
|
|
||||||
texturecoords_format = "<%if" % int(header.vertexcount * 2)
|
|
||||||
#Indices
|
|
||||||
indices_format = "<%iI" % 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)))
|
|
||||||
|
|
||||||
def createMesh(filename,header,data): #Create a Mesh inside Blender
|
|
||||||
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 = []
|
|
||||||
image = None
|
|
||||||
if header.hastexture: #Load Texture when assigned
|
|
||||||
texturefile = dirname(filename)+os.sep+header.texturefilename
|
|
||||||
image = bpy.data.images.load(texturefile) #load_image(texturefile, dirname(filename))
|
|
||||||
for x in range(0,len(data.texturecoords),2): #Prepare the UV
|
|
||||||
uvcoords.append([data.texturecoords[x],data.texturecoords[x+1]])
|
|
||||||
|
|
||||||
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.append([0,0])
|
|
||||||
uv.append([0,0])
|
|
||||||
uv.append([0,0])
|
|
||||||
faceuv.append([uv,0,0,0])
|
|
||||||
mesh.faces.add(len(faces)//4)
|
|
||||||
mesh.faces.foreach_set("vertices_raw", faces)
|
|
||||||
mesh.faces.foreach_set("use_smooth", [True] * len(mesh.faces))
|
|
||||||
mesh.update_tag()
|
|
||||||
mesh.update() #Update changes to Mesh
|
|
||||||
#===================================================================================================
|
|
||||||
#Material Setup
|
|
||||||
#===================================================================================================
|
|
||||||
if header.hastexture:
|
|
||||||
materialname = "pskmat"
|
|
||||||
materials = []
|
|
||||||
texture = bpy.data.textures.new(name=header.texturefilename,type='IMAGE')
|
|
||||||
texture.image = image
|
|
||||||
matdata = bpy.data.materials.new(materialname + '1')
|
|
||||||
slot = matdata.texture_slots.add()
|
|
||||||
slot.texture = texture
|
|
||||||
slot.texture_coords = 'UV'
|
|
||||||
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.uv_textures.new(name=psktexname)
|
|
||||||
for countm in range(len(mesh.uv_textures)):
|
|
||||||
mesh.update()
|
|
||||||
uvtex = mesh.uv_textures[countm] #add one uv texture
|
|
||||||
mesh.update()
|
|
||||||
if (len(faceuv) > 0):
|
|
||||||
counttex = 0
|
|
||||||
countm = 0
|
|
||||||
for countm in range(len(mesh.uv_textures)):
|
|
||||||
mesh.update()
|
|
||||||
psktexname="psk" + str(countm)
|
|
||||||
uvtex = mesh.uv_textures[countm] #add one uv texture
|
|
||||||
mesh.update()
|
|
||||||
for i, face in enumerate(mesh.faces):
|
|
||||||
blender_tface = uvtex.data[i] #face
|
|
||||||
mfaceuv = faceuv[i]
|
|
||||||
#face.material_index = faceuv[i][1]
|
|
||||||
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 = image
|
|
||||||
#blender_tface.use_image = True
|
|
||||||
#blender_tface.blend_type = 'ALPHA'
|
|
||||||
#blender_tface.use_twoside = True
|
|
||||||
else:
|
|
||||||
blender_tface.uv1 = [0,0]
|
|
||||||
blender_tface.uv2 = [0,0]
|
|
||||||
blender_tface.uv3 = [0,0]
|
|
||||||
mesh.update()
|
|
||||||
imported.append(meshobj) #Add to Imported Objects
|
|
||||||
for x in range(header.framecount): #Put in Vertex Positions for Keyanimation
|
|
||||||
print("Frame"+str(x))
|
|
||||||
for i in range(0,header.vertexcount*3,3):
|
|
||||||
print(str(i//3)+':\t'+str(data.vertices[x*header.vertexcount*3 + i])+'\t'+
|
|
||||||
str(data.vertices[x*header.vertexcount*3 + i +1])+'\t'+
|
|
||||||
str(data.vertices[x*header.vertexcount*3 + i +2]))
|
|
||||||
mesh.vertices[i//3].co[0]= data.vertices[x*header.vertexcount*3 + i]
|
|
||||||
mesh.vertices[i//3].co[1]= data.vertices[x*header.vertexcount*3 + i +1]
|
|
||||||
mesh.vertices[i//3].co[2]= data.vertices[x*header.vertexcount*3 + i +2]
|
|
||||||
meshobj.keyframe_insert("location",-1, frame=(x+1))
|
|
||||||
mesh.update()
|
|
||||||
return
|
|
||||||
###########################################################################
|
|
||||||
# Import
|
|
||||||
###########################################################################
|
|
||||||
def G3DLoader(filepath): #Main Import Routine
|
|
||||||
global imported, sceneID
|
|
||||||
print ("\nNow Importing File: " + filepath)
|
|
||||||
fileID = open(filepath,"rb")
|
|
||||||
header = G3DHeader(fileID)
|
|
||||||
print ("\nHeader ID : " + header.id)
|
|
||||||
print ("Version : " + str(header.version))
|
|
||||||
if header.id != "G3D":
|
|
||||||
print ("This is Not a G3D Model File")
|
|
||||||
fileID.close
|
|
||||||
return
|
|
||||||
if header.version not in (3, 4):
|
|
||||||
print ("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 ("\nMesh 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.texturefilename))
|
|
||||||
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)
|
|
||||||
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 ("\nMesh 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.texturefilename))
|
|
||||||
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)
|
|
||||||
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):
|
|
||||||
print ("\nNow Exporting File: " + filepath)
|
|
||||||
fileID = open(filepath,"wb")
|
|
||||||
|
|
||||||
# G3DHeader v4
|
|
||||||
fileID.write(struct.pack("<3cB", b'G', b'3', b'D', 4))
|
|
||||||
#get real meshcount as len(bpy.data.meshes) holds also old meshes
|
|
||||||
meshCount = 0
|
|
||||||
for obj in bpy.data.objects:#context.selected_objects:
|
|
||||||
if obj.type == 'MESH':
|
|
||||||
meshCount += 1
|
|
||||||
# G3DModelHeaderv4
|
|
||||||
fileID.write(struct.pack("<HB", meshCount, 0))
|
|
||||||
# meshes
|
|
||||||
#for mesh in bpy.data.meshes:
|
|
||||||
for obj in bpy.data.objects:#context.selected_objects:
|
|
||||||
if obj.type != 'MESH':
|
|
||||||
continue
|
|
||||||
mesh = obj.data
|
|
||||||
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]
|
|
||||||
if material.active_texture.type == 'IMAGE':
|
|
||||||
diffuseColor = material.diffuse_color
|
|
||||||
specularColor = material.specular_color
|
|
||||||
opacity = material.alpha
|
|
||||||
textures = 1
|
|
||||||
texname = bpy.path.basename(material.active_texture.image.filepath)
|
|
||||||
else:
|
|
||||||
#FIXME: needs warning in gui
|
|
||||||
print("active texture in first material isn't of type IMAGE")
|
|
||||||
|
|
||||||
meshname = mesh.name
|
|
||||||
frameCount = context.scene.frame_end - context.scene.frame_start +1
|
|
||||||
#Real face count (only use triangle)
|
|
||||||
realFaceCount = 0
|
|
||||||
indices=[]
|
|
||||||
newverts=[]
|
|
||||||
if textures == 1:
|
|
||||||
uvtex = mesh.uv_textures[0]
|
|
||||||
uvlist = []
|
|
||||||
uvlist[:] = [[0]*2 for i in range(len(mesh.vertices))]
|
|
||||||
s = set()
|
|
||||||
for face in mesh.faces:
|
|
||||||
faceindices = [] # we create new faces when duplicating vertices
|
|
||||||
realFaceCount += 1
|
|
||||||
uvdata = uvtex.data[face.index]
|
|
||||||
for i in range(3):
|
|
||||||
vindex = face.vertices[i]
|
|
||||||
if vindex not in s:
|
|
||||||
s.add(vindex)
|
|
||||||
uvlist[vindex] = uvdata.uv[i]
|
|
||||||
elif uvlist[vindex] != uvdata.uv[i]:
|
|
||||||
# duplicate vertex because it takes part in different faces
|
|
||||||
# with different texcoords
|
|
||||||
newverts.append(vindex)
|
|
||||||
uvlist.append(uvdata.uv[i])
|
|
||||||
#FIXME: probably better with some counter
|
|
||||||
vindex = len(mesh.vertices) + len(newverts) -1
|
|
||||||
|
|
||||||
faceindices.append(vindex)
|
|
||||||
indices.extend(faceindices)
|
|
||||||
|
|
||||||
#FIXME: stupid copy&paste
|
|
||||||
if len(face.vertices) == 4:
|
|
||||||
faceindices = []
|
|
||||||
realFaceCount += 1
|
|
||||||
for i in [0,2,3]:
|
|
||||||
vindex = face.vertices[i]
|
|
||||||
if vindex not in s:
|
|
||||||
s.add(vindex)
|
|
||||||
uvlist[vindex] = uvdata.uv[i]
|
|
||||||
elif uvlist[vindex] != uvdata.uv[i]:
|
|
||||||
# duplicate vertex because it takes part in different faces
|
|
||||||
# with different texcoords
|
|
||||||
newverts.append(vindex)
|
|
||||||
uvlist.append(uvdata.uv[i])
|
|
||||||
#FIXME: probably better with some counter
|
|
||||||
vindex = len(mesh.vertices) + len(newverts) -1
|
|
||||||
|
|
||||||
faceindices.append(vindex)
|
|
||||||
indices.extend(faceindices)
|
|
||||||
else:
|
|
||||||
for face in mesh.faces:
|
|
||||||
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])
|
|
||||||
|
|
||||||
|
|
||||||
#FIXME: abort when no triangles as it crashs g3dviewer
|
|
||||||
if realFaceCount == 0:
|
|
||||||
print("no triangles found")
|
|
||||||
indexCount = realFaceCount * 3
|
|
||||||
vertexCount = len(mesh.vertices) + len(newverts)
|
|
||||||
specularPower = 9.999999 # unused, same as old exporter
|
|
||||||
properties = 0
|
|
||||||
if mesh.show_double_sided:
|
|
||||||
properties |= 1
|
|
||||||
if textures==1 and mesh.materials[0].use_face_texture_alpha:
|
|
||||||
properties |= 2
|
|
||||||
|
|
||||||
#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: this is hacky
|
|
||||||
if obj.find_armature() == None:
|
|
||||||
m = mesh.copy()
|
|
||||||
m.transform(obj.matrix_world)
|
|
||||||
else:
|
|
||||||
#FIXME: not sure what's better: PREVIEW or RENDER settings
|
|
||||||
m = obj.to_mesh(context.scene, True, 'RENDER')
|
|
||||||
for vertex in m.vertices:
|
|
||||||
vertices.extend(vertex.co)
|
|
||||||
normals.extend(vertex.normal)
|
|
||||||
for nv in newverts:
|
|
||||||
vertices.extend(m.vertices[nv].co)
|
|
||||||
normals.extend(m.vertices[nv].normal)
|
|
||||||
context.scene.frame_set(fcurrent)
|
|
||||||
|
|
||||||
# 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 == 1: # only when we have one
|
|
||||||
fileID.write(struct.pack("<64s", bytes(texname, "ascii")))
|
|
||||||
|
|
||||||
# see G3DMeshdataV4
|
|
||||||
vertex_format = "<%if" % int(frameCount * vertexCount * 3)
|
|
||||||
normals_format = "<%if" % int(frameCount * vertexCount * 3)
|
|
||||||
texturecoords_format = "<%if" % int(vertexCount * 2)
|
|
||||||
indices_format = "<%iI" % int(indexCount)
|
|
||||||
|
|
||||||
fileID.write(struct.pack(vertex_format, *vertices))
|
|
||||||
fileID.write(struct.pack(normals_format, *normals))
|
|
||||||
|
|
||||||
# texcoords
|
|
||||||
if textures == 1: # only when we have one
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
#---=== Register ===
|
|
||||||
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'})
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
try:
|
|
||||||
G3DLoader(**self.as_keywords(ignore=("filter_glob",)))
|
|
||||||
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'})
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
try:
|
|
||||||
G3DSaver(self.filepath, context)
|
|
||||||
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():
|
|
||||||
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()
|
|
||||||
|
|
|
@ -1,747 +0,0 @@
|
||||||
# -*- 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
|
|
||||||
#};
|
|
||||||
#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, only 0x1 is currently used, indicating that there is a diffuse texture in this mesh.
|
|
||||||
#================================
|
|
||||||
#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. In practice since Glest only uses 1 texture for each mesh the number of texture names should be 0 or 1.
|
|
||||||
#================================
|
|
||||||
#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",
|
|
||||||
"author": "various, see head of script",
|
|
||||||
"version": (0, 5, 2),
|
|
||||||
"blender": (2, 63, 0),
|
|
||||||
#"api": 36079,
|
|
||||||
"location": "File > Import-Export",
|
|
||||||
"description": "Import/Export .g3d file",
|
|
||||||
"warning": "",
|
|
||||||
"wiki_url": "",
|
|
||||||
"tracker_url": "",
|
|
||||||
"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 sys, struct, string, types
|
|
||||||
from types import *
|
|
||||||
import os
|
|
||||||
from os import path
|
|
||||||
from os.path import dirname
|
|
||||||
###########################################################################
|
|
||||||
# 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.texturefilename = None
|
|
||||||
else:
|
|
||||||
self.texturefilename = "".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 __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 flag
|
|
||||||
if self.properties & 1: #PropertyBit is Mesh Alpha Channel custom Color in Game ?
|
|
||||||
self.customalpha = True
|
|
||||||
else:
|
|
||||||
self.customalpha = False
|
|
||||||
if self.properties & 2: #PropertyBit is Mesh TwoSided ?
|
|
||||||
self.istwosided = True
|
|
||||||
else:
|
|
||||||
self.istwosided = False
|
|
||||||
if self.properties & 4:
|
|
||||||
self.noselect = True
|
|
||||||
else:
|
|
||||||
self.noselect = False
|
|
||||||
if self.textures & 1: #PropertyBit is Mesh Textured ?
|
|
||||||
temp = fileID.read(struct.calcsize(self.texname_format))
|
|
||||||
data = struct.unpack(self.texname_format,temp)
|
|
||||||
self.texturefilename = "".join([str(x, "ascii") for x in data[0:-1] if x[0] < 127 ])
|
|
||||||
self.hastexture = True
|
|
||||||
else:
|
|
||||||
self.hastexture = False
|
|
||||||
self.texturefilename = None
|
|
||||||
|
|
||||||
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 = "<%if" % int(header.framecount * header.vertexcount * 3)
|
|
||||||
#The same for Normals
|
|
||||||
normals_format = "<%if" % int(header.normalframecount * header.vertexcount * 3)
|
|
||||||
#Same here but Textures are 2D so only 2 Floats needed for Position inside Texture Bitmap
|
|
||||||
texturecoords_format = "<%if" % int(header.texturecoordframecount * header.vertexcount * 2)
|
|
||||||
#Colors in format RGBA
|
|
||||||
colors_format = "<%if" % int(header.colorframecount * 4)
|
|
||||||
#Indices
|
|
||||||
indices_format = "<%iI" % 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 = "<%if" % int(header.framecount * header.vertexcount * 3)
|
|
||||||
#The same for Normals
|
|
||||||
normals_format = "<%if" % int(header.framecount * header.vertexcount * 3)
|
|
||||||
#Same here but Textures are 2D so only 2 Floats needed for Position inside Texture Bitmap
|
|
||||||
texturecoords_format = "<%if" % int(header.vertexcount * 2)
|
|
||||||
#Indices
|
|
||||||
indices_format = "<%iI" % 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)))
|
|
||||||
|
|
||||||
def createMesh(filename,header,data): #Create a Mesh inside Blender
|
|
||||||
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 = []
|
|
||||||
image = None
|
|
||||||
if header.hastexture: #Load Texture when assigned
|
|
||||||
texturefile = dirname(filename)+os.sep+header.texturefilename
|
|
||||||
image = bpy.data.images.load(texturefile) #load_image(texturefile, dirname(filename))
|
|
||||||
for x in range(0,len(data.texturecoords),2): #Prepare the UV
|
|
||||||
uvcoords.append([data.texturecoords[x],data.texturecoords[x+1]])
|
|
||||||
|
|
||||||
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
|
|
||||||
mesh.g3d_noSelect = header.noselect
|
|
||||||
#===================================================================================================
|
|
||||||
#Material Setup
|
|
||||||
#===================================================================================================
|
|
||||||
if header.hastexture:
|
|
||||||
materialname = "pskmat"
|
|
||||||
materials = []
|
|
||||||
texture = bpy.data.textures.new(name=header.texturefilename,type='IMAGE')
|
|
||||||
texture.image = image
|
|
||||||
matdata = bpy.data.materials.new(materialname + '1')
|
|
||||||
slot = matdata.texture_slots.add()
|
|
||||||
slot.texture = texture
|
|
||||||
slot.texture_coords = 'UV'
|
|
||||||
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 = image
|
|
||||||
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()
|
|
||||||
#mesh.shape_keys.use_relative = False
|
|
||||||
#sk.interpolation = 'KEY_LINEAR'
|
|
||||||
for x in range(1,header.framecount): #Put in Vertex Positions for Keyanimation
|
|
||||||
#print("Frame"+str(x))
|
|
||||||
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]
|
|
||||||
#meshobj.keyframe_insert("location",-1, frame=(x+1))
|
|
||||||
#meshobj.keyframe_insert(data_path="data.vertices", frame=(x+1))
|
|
||||||
# update polygon structures from tessfaces
|
|
||||||
mesh.update()
|
|
||||||
mesh.update_tag()
|
|
||||||
return
|
|
||||||
###########################################################################
|
|
||||||
# Import
|
|
||||||
###########################################################################
|
|
||||||
def G3DLoader(filepath): #Main Import Routine
|
|
||||||
global imported, sceneID
|
|
||||||
print ("\nNow Importing File: " + filepath)
|
|
||||||
fileID = open(filepath,"rb")
|
|
||||||
header = G3DHeader(fileID)
|
|
||||||
print ("\nHeader ID : " + header.id)
|
|
||||||
print ("Version : " + str(header.version))
|
|
||||||
if header.id != "G3D":
|
|
||||||
print ("This is Not a G3D Model File")
|
|
||||||
fileID.close
|
|
||||||
return
|
|
||||||
if header.version not in (3, 4):
|
|
||||||
print ("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 ("\nMesh 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.texturefilename))
|
|
||||||
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)
|
|
||||||
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 ("\nMesh 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.texturefilename))
|
|
||||||
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)
|
|
||||||
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, operator):
|
|
||||||
print ("\nNow 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
|
|
||||||
|
|
||||||
if meshCount == 0:
|
|
||||||
print("ERROR: no meshes found")
|
|
||||||
operator.report({'ERROR'}, "no meshes found")
|
|
||||||
return
|
|
||||||
|
|
||||||
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]
|
|
||||||
if material.active_texture.type=='IMAGE' and len(mesh.uv_textures)>0:
|
|
||||||
diffuseColor = material.diffuse_color
|
|
||||||
specularColor = material.specular_color
|
|
||||||
if material.alpha == 0 : #ignore the opacity if it is 0 . in this case its set to 1.0 to make it visible
|
|
||||||
opacity = 1.0
|
|
||||||
else:
|
|
||||||
opacity = material.alpha
|
|
||||||
textures = 1
|
|
||||||
texname = bpy.path.basename(material.active_texture.image.filepath)
|
|
||||||
else:
|
|
||||||
print("WARNING: active texture in first material isn't of type IMAGE or it's not unwrapped, texture ignored")
|
|
||||||
operator.report({'WARNING'}, "active texture 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
|
|
||||||
#Real face count (triangles)
|
|
||||||
realFaceCount = 0
|
|
||||||
indices=[]
|
|
||||||
newverts=[]
|
|
||||||
mesh.calc_tessface() # tesselate n-polygons to triangles and quads
|
|
||||||
if textures == 1:
|
|
||||||
uvtex = mesh.tessface_uv_textures[0]
|
|
||||||
uvlist = []
|
|
||||||
uvlist[:] = [[0]*2 for i in range(len(mesh.vertices))]
|
|
||||||
s = set()
|
|
||||||
for face in mesh.tessfaces:
|
|
||||||
faceindices = [] # we create new faces when duplicating vertices
|
|
||||||
realFaceCount += 1
|
|
||||||
uvdata = uvtex.data[face.index]
|
|
||||||
for i in range(3):
|
|
||||||
vindex = face.vertices[i]
|
|
||||||
if vindex not in s:
|
|
||||||
s.add(vindex)
|
|
||||||
uvlist[vindex] = uvdata.uv[i]
|
|
||||||
elif uvlist[vindex] != uvdata.uv[i]:
|
|
||||||
# duplicate vertex because it takes part in different faces
|
|
||||||
# with different texcoords
|
|
||||||
newverts.append(vindex)
|
|
||||||
uvlist.append(uvdata.uv[i])
|
|
||||||
#FIXME: probably better with some counter
|
|
||||||
vindex = len(mesh.vertices) + len(newverts) -1
|
|
||||||
|
|
||||||
faceindices.append(vindex)
|
|
||||||
indices.extend(faceindices)
|
|
||||||
|
|
||||||
#FIXME: stupid copy&paste
|
|
||||||
if len(face.vertices) == 4:
|
|
||||||
faceindices = []
|
|
||||||
realFaceCount += 1
|
|
||||||
for i in [0,2,3]:
|
|
||||||
vindex = face.vertices[i]
|
|
||||||
if vindex not in s:
|
|
||||||
s.add(vindex)
|
|
||||||
uvlist[vindex] = uvdata.uv[i]
|
|
||||||
elif uvlist[vindex] != uvdata.uv[i]:
|
|
||||||
# duplicate vertex because it takes part in different faces
|
|
||||||
# with different texcoords
|
|
||||||
newverts.append(vindex)
|
|
||||||
uvlist.append(uvdata.uv[i])
|
|
||||||
#FIXME: probably better with some counter
|
|
||||||
vindex = len(mesh.vertices) + len(newverts) -1
|
|
||||||
|
|
||||||
faceindices.append(vindex)
|
|
||||||
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")
|
|
||||||
return
|
|
||||||
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
|
|
||||||
|
|
||||||
#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
|
|
||||||
for vertex in m.vertices:
|
|
||||||
vertices.extend(vertex.co)
|
|
||||||
normals.extend(vertex.normal)
|
|
||||||
|
|
||||||
for nv in newverts:
|
|
||||||
vertices.extend(m.vertices[nv].co)
|
|
||||||
normals.extend(m.vertices[nv].normal)
|
|
||||||
|
|
||||||
context.scene.frame_set(fcurrent)
|
|
||||||
|
|
||||||
# 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 == 1: # only when we have one
|
|
||||||
fileID.write(struct.pack("<64s", bytes(texname, "ascii")))
|
|
||||||
|
|
||||||
# see G3DMeshdataV4
|
|
||||||
vertex_format = "<%if" % int(frameCount * vertexCount * 3)
|
|
||||||
normals_format = "<%if" % int(frameCount * vertexCount * 3)
|
|
||||||
texturecoords_format = "<%if" % int(vertexCount * 2)
|
|
||||||
indices_format = "<%iI" % int(indexCount)
|
|
||||||
|
|
||||||
fileID.write(struct.pack(vertex_format, *vertices))
|
|
||||||
fileID.write(struct.pack(normals_format, *normals))
|
|
||||||
|
|
||||||
# texcoords
|
|
||||||
if textures == 1: # only when we have one
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
#---=== 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", text="team color")
|
|
||||||
self.layout.prop(context.object.data, "show_double_sided", text="double sided")
|
|
||||||
self.layout.prop(context.object.data, "g3d_noSelect", text="non-selectable")
|
|
||||||
|
|
||||||
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'})
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
try:
|
|
||||||
G3DLoader(**self.as_keywords(ignore=("filter_glob",)))
|
|
||||||
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'})
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
try:
|
|
||||||
G3DSaver(self.filepath, context, self)
|
|
||||||
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()
|
|
||||||
bpy.types.Mesh.g3d_noSelect = bpy.props.BoolProperty()
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
#G3DLoader("import.g3d")
|
|
||||||
#for obj in bpy.context.selected_objects:
|
|
||||||
# obj.select = False # deselect everything, so we get it all
|
|
||||||
#G3DSaver("test.g3d", bpy.context)
|
|
||||||
|
|
|
@ -226,6 +226,7 @@ class G3DMeshHeaderv4: #Read Meshheader
|
||||||
self.customalpha = bool(self.properties & 1)
|
self.customalpha = bool(self.properties & 1)
|
||||||
self.istwosided = bool(self.properties & 2)
|
self.istwosided = bool(self.properties & 2)
|
||||||
self.noselect = bool(self.properties & 4)
|
self.noselect = bool(self.properties & 4)
|
||||||
|
self.glow = bool(self.properties & 8)
|
||||||
|
|
||||||
self.hastexture = False
|
self.hastexture = False
|
||||||
self.diffusetexture = None
|
self.diffusetexture = None
|
||||||
|
@ -358,8 +359,10 @@ def createMesh(filename, header, data, toblender, operator):
|
||||||
mesh.show_double_sided = header.istwosided
|
mesh.show_double_sided = header.istwosided
|
||||||
if header.isv4:
|
if header.isv4:
|
||||||
mesh.g3d_noSelect = header.noselect
|
mesh.g3d_noSelect = header.noselect
|
||||||
|
mesh.g3d_glow = header.glow
|
||||||
else:
|
else:
|
||||||
mesh.g3d_noSelect = False
|
mesh.g3d_noSelect = False
|
||||||
|
mesh.glow = False
|
||||||
mesh.g3d_fullyOpaque = False
|
mesh.g3d_fullyOpaque = False
|
||||||
#===================================================================================================
|
#===================================================================================================
|
||||||
#Material Setup
|
#Material Setup
|
||||||
|
@ -705,6 +708,8 @@ def G3DSaver(filepath, context, toglest, operator):
|
||||||
properties |= 2
|
properties |= 2
|
||||||
if mesh.g3d_noSelect:
|
if mesh.g3d_noSelect:
|
||||||
properties |= 4
|
properties |= 4
|
||||||
|
if mesh.g3d_glow:
|
||||||
|
properties |= 8
|
||||||
|
|
||||||
#MeshData
|
#MeshData
|
||||||
vertices = []
|
vertices = []
|
||||||
|
@ -789,6 +794,7 @@ class G3DPanel(bpy.types.Panel):
|
||||||
self.layout.prop(context.object.data, "show_double_sided", text="double sided")
|
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_noSelect")
|
||||||
self.layout.prop(context.object.data, "g3d_fullyOpaque")
|
self.layout.prop(context.object.data, "g3d_fullyOpaque")
|
||||||
|
self.layout.prop(context.object.data, "g3d_glow")
|
||||||
|
|
||||||
class ImportG3D(bpy.types.Operator, ImportHelper):
|
class ImportG3D(bpy.types.Operator, ImportHelper):
|
||||||
'''Load a G3D file'''
|
'''Load a G3D file'''
|
||||||
|
@ -891,6 +897,9 @@ def register():
|
||||||
bpy.types.Mesh.g3d_fullyOpaque = bpy.props.BoolProperty(
|
bpy.types.Mesh.g3d_fullyOpaque = bpy.props.BoolProperty(
|
||||||
name="fully opaque",
|
name="fully opaque",
|
||||||
description="sets opacity to 1.0, ignoring what's set in materials")
|
description="sets opacity to 1.0, ignoring what's set in materials")
|
||||||
|
bpy.types.Mesh.g3d_glow = bpy.props.BoolProperty(
|
||||||
|
name="glow",
|
||||||
|
description="let objects glow like particles")
|
||||||
|
|
||||||
bpy.utils.register_module(__name__)
|
bpy.utils.register_module(__name__)
|
||||||
|
|
||||||
|
|
|
@ -1,535 +0,0 @@
|
||||||
#!BPY
|
|
||||||
# coding: utf-8
|
|
||||||
"""
|
|
||||||
Name: 'G3D XML Exporter Ver:1.1'
|
|
||||||
Blender: 2.43
|
|
||||||
Group: 'Export'
|
|
||||||
Tooltip: 'Exports directly to G3D(if xml2g is installed next to this script) or XML (which can be converted to .g3d using xml2g)'
|
|
||||||
"""
|
|
||||||
###########################################################################
|
|
||||||
# Glest Model / Texture / Animation Exporter
|
|
||||||
# for the game Glest that you can find at http://www.glest.org
|
|
||||||
# copyright 2005-2006 By Vincent Gadoury
|
|
||||||
# Started Date: December 18 2005 Put Public Decembre 20 2005
|
|
||||||
# Ver: 1.0 (Jan 30 2009)
|
|
||||||
# Distributed under the GNU PUBLIC LICENSE
|
|
||||||
# Based on g3d_support.py by Andreas Becker
|
|
||||||
# and on glexemel by Jonathan Merritt
|
|
||||||
###########################################################################
|
|
||||||
# (I'm sorry for the poor quality of this text,
|
|
||||||
# I'm not a native English speaker)
|
|
||||||
#
|
|
||||||
#INSTALLATION :
|
|
||||||
# Copy this Script into your .blender\scripts directory.
|
|
||||||
# if you want direct .g3d support unpack all needed glexemel files to this directory too:
|
|
||||||
# LINUX:
|
|
||||||
# In case of linux its only: xml2g and g3d.dtd ( included is a binary for linux32 bit).
|
|
||||||
# The sourcecode can be found here:
|
|
||||||
# http://www.glest.org/files/contrib/tools/glexemel-0.1a.zip
|
|
||||||
# WINDOWS:
|
|
||||||
# If you are using windows, all files from the "bin" directory from the
|
|
||||||
# following zip files are needed:
|
|
||||||
# http://www.glest.org/files/contrib/tools/glexemel_win32_0.1a.zip
|
|
||||||
#
|
|
||||||
# In Blender, open a Script Window and in the menu choose "Update Menus"
|
|
||||||
# Export with File->Export->G3D XML Exporter
|
|
||||||
#
|
|
||||||
#PREPARE THE MODEL :
|
|
||||||
# Each of your meshes' faces must be a triangle (3 vertex)
|
|
||||||
# In Edit mode, you can press Space, edit>faces>Convert to Triangles
|
|
||||||
# Create and place all your meshes before texturing or animating.
|
|
||||||
# The X axe is the floor and the Y axe is the elevation. The center
|
|
||||||
# of the Blender's 3D space will be the center of you object, at the
|
|
||||||
# ground level. Before exporting your model, apply the transformations
|
|
||||||
# to each of your mesh (In the 3D space window's menu :
|
|
||||||
# Object->Clear/Apply->Apply ... )
|
|
||||||
# This will place the center of your mesh (the yellow or pink dot)
|
|
||||||
# at the center of the Blender 3D space.
|
|
||||||
#
|
|
||||||
# !!!!!!!!!!!!!!
|
|
||||||
# !!!new since 0.1.4: !!!
|
|
||||||
# If you have a non animating model, you have to set the start frame
|
|
||||||
# and the end frame to '1' in blenders 'timeline'-window. Otherwise a non
|
|
||||||
# animating animation will be exported, containing a lot of frames and your
|
|
||||||
# model gets really big.
|
|
||||||
# !!!!!!!!!!!!!!
|
|
||||||
#
|
|
||||||
# Before doing any texturing or animation, try to export your model and
|
|
||||||
# check your python console (the terminal which opens Blender) to see
|
|
||||||
# if there is a warning about the number of used faces in the summary.
|
|
||||||
# If there is, try to delete "false" faces (which use only 2 vertices)
|
|
||||||
# and transform all your faces in triangle.
|
|
||||||
#
|
|
||||||
#EXPORTING :
|
|
||||||
# You MUST select the meshes you want to export.
|
|
||||||
# Only meshes are exported among the selected objects.
|
|
||||||
#
|
|
||||||
# The diffuse color of the mesh will be it's (first) material's color.
|
|
||||||
# The opacity of the mesh will be it's material's alpha.
|
|
||||||
# Double sided property of the mesh is exported (see F9).
|
|
||||||
# If you want to use custom color in the texture, your mesh must be
|
|
||||||
# double sided. If you don't want custom color to be activated for the
|
|
||||||
# mesh, add a new boolean property to it (F4) named customColor and with
|
|
||||||
# the false value. Custom Color set if the alpha of the texture is
|
|
||||||
# replaced for the color of the player.
|
|
||||||
#
|
|
||||||
#TEXTURING :
|
|
||||||
# To use a texture for the mesh, you must add a texture at the FIRST
|
|
||||||
# position to the mesh's material. The texture type must be 'image'
|
|
||||||
# and you should insert the real image. In fact, only the filename
|
|
||||||
# of the image is used. After having mapped the image on the
|
|
||||||
# mesh with UV Face selection, you don't need anymore to "stick"
|
|
||||||
# the coordinates to the vertices.
|
|
||||||
# Your texture's format must be TGA, without compression and with the origin
|
|
||||||
# at the bottom left position. The side of the image must be a multiple
|
|
||||||
# of 2. In practice, you must use only one image for all your meshes.
|
|
||||||
#
|
|
||||||
#ANIMATING :
|
|
||||||
# You can export all animations made with blenders armature system.
|
|
||||||
# You have to set the start frame and the end frame in the 'timeline'-window
|
|
||||||
# to define the animation which will be exported.
|
|
||||||
# If you animate without using the armatures it will not work!!
|
|
||||||
#
|
|
||||||
# LINKS :
|
|
||||||
# Animating with armatures
|
|
||||||
# http://www.blender.org/documentation/htmlI/x1829.html
|
|
||||||
# Map an image on your mesh with UV Face selection :
|
|
||||||
# http://en.wikibooks.org/wiki/Blender_3D:_Noob_to_Pro/UV_Map_Basics
|
|
||||||
# http://download.blender.org/documentation/htmlI/ch11s05.html
|
|
||||||
#
|
|
||||||
#CONVERT TO .G3D
|
|
||||||
# The following is no longer needed if you copied g3d.dtd and xml2g(.exe) next
|
|
||||||
# to this script:
|
|
||||||
#
|
|
||||||
# Use the xml2g program of the glexemel tool at
|
|
||||||
# http://www.glest.org/files/contrib/tools/
|
|
||||||
# Syntax : ./xml2g ExportedFile.xml DesiredFile.g3d
|
|
||||||
#
|
|
||||||
# WARNING : The current version of this program don't seems to work...
|
|
||||||
# You'll have to add the lines (if they aren't there) :
|
|
||||||
# #pragma pack(push, 1)
|
|
||||||
# near of the beginning of the file "g3dv4.h" of glexemel and
|
|
||||||
# #pragma pack(pop)
|
|
||||||
# before the #endif
|
|
||||||
#
|
|
||||||
#
|
|
||||||
#ChangeLog :
|
|
||||||
# 1.1: by Titus Tscharntke (titi)
|
|
||||||
# fixed bug in windows ( problems with file sparator char )
|
|
||||||
# 1.0: Big thanks go to Frank Tetzel (Yggdrasil) who finally made a
|
|
||||||
# direct export to .g3d possible. If you copy g3d.dtd and
|
|
||||||
# xml2g(.exe) next to this script it will export to .g3d.
|
|
||||||
# If not, it will export to .xml like before.
|
|
||||||
#
|
|
||||||
# 0.1.7 : by Titus Tscharntke (titi)
|
|
||||||
# removed non 7-bit ascii characters for newer versions of python
|
|
||||||
# 0.1.6 : by justWeedy(weedkiller)
|
|
||||||
# Basic colorsettings for maps are often unused but may cause
|
|
||||||
# models look "bright".
|
|
||||||
# There are Hardcoded defauld values to avoid this.
|
|
||||||
# Turn On/Off due the boolean <useHardCodedColor>
|
|
||||||
# 0.1.5 : by weedkiller
|
|
||||||
# You don't have to apply scale and rotation anymore
|
|
||||||
# 0.1.4 : by weedkiller and Titus Tscharntke ( www.titusgames.de )
|
|
||||||
# New implementation of the animation export ( including some usage changes!!! )
|
|
||||||
# Exporting animations is now possible with newer Blender versions ( tested with 2.43 )
|
|
||||||
#
|
|
||||||
# 0.1.3 : by Titus Tscharntke ( www.titusgames.de )
|
|
||||||
# Fixed a bug with getProperty() in blender 2.43
|
|
||||||
#
|
|
||||||
# 0.1.2 :
|
|
||||||
# Correcting an execution bug
|
|
||||||
# Adding the "frameCount" property for each object
|
|
||||||
# Using only the triangle faces for the index count
|
|
||||||
# (problems of linking between index and vertex may occur)
|
|
||||||
# Adding the summary display in the python console
|
|
||||||
# Adding a better documentation
|
|
||||||
# 0.1.1b :
|
|
||||||
# Adding a vertex duplication method to correct the vertex UV problem.
|
|
||||||
# Correcting NMVert.uvco misuse
|
|
||||||
#
|
|
||||||
#ToDo:
|
|
||||||
# More tests...
|
|
||||||
# More validations...
|
|
||||||
# Warn the user if there is something wrong, e.g. :
|
|
||||||
# - no mesh among the selected objects;
|
|
||||||
# - texture without uvco;
|
|
||||||
# - no material on a mesh.
|
|
||||||
# Test and correct the theoric linking problem if faces are ignored...
|
|
||||||
#
|
|
||||||
#Contributions :
|
|
||||||
# pseudonym404 : NMVert.uvco issue and solutions
|
|
||||||
#
|
|
||||||
###############################################################################
|
|
||||||
|
|
||||||
import Blender
|
|
||||||
from Blender import NMesh
|
|
||||||
from Blender import sys as bsys
|
|
||||||
|
|
||||||
import subprocess, os
|
|
||||||
|
|
||||||
|
|
||||||
# part Hardcoded Color >>
|
|
||||||
HardCodedDiffuse=0.588235
|
|
||||||
HardCodedSpecular=0.900000
|
|
||||||
useHardCodedColor='true' #'false' if not wanted
|
|
||||||
# part Hardcoded Color <<
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
seenindex=set()
|
|
||||||
uvlist=[]
|
|
||||||
|
|
||||||
# This list will contain the associative "real" vertex of each duplicated vertex
|
|
||||||
# in ascendant order.
|
|
||||||
# The fisrt "vertex" of the list (newvertices[0]) has the index len(mesh.verts)
|
|
||||||
newvertices=[]
|
|
||||||
currentnewvertex=0
|
|
||||||
|
|
||||||
#This list will contain all the "new" faces' indices
|
|
||||||
newindices=[]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def icmp(x,y):
|
|
||||||
return cmp(x,y)
|
|
||||||
|
|
||||||
def notseenindex(index):
|
|
||||||
seen = index in seenindex
|
|
||||||
if not seen: seenindex.add(index)
|
|
||||||
return not seen
|
|
||||||
|
|
||||||
# this will return the properties data if it exists, else the given defaultvalue
|
|
||||||
def getPropertyIfExists(blenderobject,propertyName,defaultvalue):
|
|
||||||
propertiesList = blenderobject.getAllProperties()
|
|
||||||
lpropertiesList = range( 0 , len(propertiesList) )
|
|
||||||
for iprop in lpropertiesList:
|
|
||||||
objectProperty = propertiesList[iprop]
|
|
||||||
currentPropertyName=objectProperty.getName()
|
|
||||||
if currentPropertyName == propertyName:
|
|
||||||
return objectProperty.getData()
|
|
||||||
return defaultvalue
|
|
||||||
|
|
||||||
|
|
||||||
def write_obj(filepath):
|
|
||||||
out = file(filepath, 'w')
|
|
||||||
|
|
||||||
print("-----------------------------------------------")
|
|
||||||
|
|
||||||
#Header
|
|
||||||
out.write( '<?xml version="1.0" encoding="ASCII" ?>\n' )
|
|
||||||
out.write( '<!DOCTYPE G3D SYSTEM "g3d.dtd">\n' )
|
|
||||||
out.write( '<!-- \n' )
|
|
||||||
out.write( ' This file is an XML encoding of a G3D binary\n' )
|
|
||||||
out.write( ' file. The XML format is by Jonathan Merritt\n' )
|
|
||||||
out.write( ' (not yet accepted as part of Glest!).\n' )
|
|
||||||
out.write( ' The file was exported from Blender with the\n' )
|
|
||||||
out.write( ' G3D-XML Exporter script by Vincent Gadoury.\n' )
|
|
||||||
out.write( '-->\n' )
|
|
||||||
out.write( '<G3D version="4">\n' )
|
|
||||||
|
|
||||||
objects = Blender.Object.GetSelected()
|
|
||||||
|
|
||||||
print("Exporting %i selected objects to XML-G3D format..." %(len(objects)))
|
|
||||||
|
|
||||||
#FOR EACH MESH
|
|
||||||
lobjects = range( 0 , len(objects) )
|
|
||||||
for iobj in lobjects:
|
|
||||||
object = objects[iobj]
|
|
||||||
|
|
||||||
mesh = object.getData()
|
|
||||||
# Skip the object if it's not a mesh
|
|
||||||
if type(mesh) != Blender.Types.NMeshType :
|
|
||||||
continue
|
|
||||||
|
|
||||||
#Clear the lists
|
|
||||||
seenindex.clear()
|
|
||||||
uvlist[:]=[]
|
|
||||||
newvertices[:]=[]
|
|
||||||
newindices[:]=[]
|
|
||||||
|
|
||||||
currentnewvertex=(len(mesh.verts))
|
|
||||||
|
|
||||||
#Find some properties of the mesh
|
|
||||||
|
|
||||||
image = None
|
|
||||||
textureName = ''
|
|
||||||
diffuseTexture = 'false'
|
|
||||||
opacity = 1
|
|
||||||
diffuseColor = [ 1.0, 1.0, 1.0 ]
|
|
||||||
|
|
||||||
#Find if the mesh has a material and a texture
|
|
||||||
# (opacity, diffuseColor, diffuseTexture, textureName)
|
|
||||||
if len(mesh.materials) > 0:
|
|
||||||
material = mesh.materials[0]
|
|
||||||
|
|
||||||
opacity = material.alpha
|
|
||||||
diffuseColor[0] = material.rgbCol[0]
|
|
||||||
diffuseColor[1] = material.rgbCol[1]
|
|
||||||
diffuseColor[2] = material.rgbCol[2]
|
|
||||||
|
|
||||||
# part Hardcoded Color >>
|
|
||||||
if useHardCodedColor :
|
|
||||||
diffuseColor[0] = HardCodedDiffuse
|
|
||||||
diffuseColor[1] = HardCodedDiffuse
|
|
||||||
diffuseColor[2] = HardCodedDiffuse
|
|
||||||
# part Hardcoded Color <<
|
|
||||||
|
|
||||||
if material.getTextures()[0]:
|
|
||||||
image = material.getTextures()[0].tex.getImage()
|
|
||||||
if image:
|
|
||||||
textureName = image.getFilename()
|
|
||||||
textureName = textureName.split(os.path.normcase('/'))[-1] #get only the filename
|
|
||||||
diffuseTexture = 'true'
|
|
||||||
#End material and texture
|
|
||||||
|
|
||||||
#TwoSided
|
|
||||||
if mesh.mode & NMesh.Modes['TWOSIDED']:
|
|
||||||
twoSided='true'
|
|
||||||
else:
|
|
||||||
twoSided='false'
|
|
||||||
|
|
||||||
#CustomColor
|
|
||||||
customColor = 1
|
|
||||||
customColor = getPropertyIfExists(object,'customColor',customColor)
|
|
||||||
if customColor:
|
|
||||||
customColor = 'true'
|
|
||||||
else:
|
|
||||||
customColor = 'false'
|
|
||||||
|
|
||||||
#Real face count (only use triangle)
|
|
||||||
realFaceCount = 0
|
|
||||||
for face in mesh.faces:
|
|
||||||
if (len(face.v) == 3):
|
|
||||||
realFaceCount += 1
|
|
||||||
|
|
||||||
#Frames number
|
|
||||||
frameCount = 1
|
|
||||||
#frameCount = getPropertyIfExists(object,'frameCount',frameCount)
|
|
||||||
startFrame = Blender.Get('staframe')
|
|
||||||
endFrame = Blender.Get('endframe')
|
|
||||||
frameCount = endFrame-startFrame+1
|
|
||||||
|
|
||||||
# TRANSFERING FACE TEXTURE COORD. TO VERTEX TEXTURE COORD.
|
|
||||||
# and duplicating vertex associated to different uvco
|
|
||||||
|
|
||||||
if textureName == '': #THERE IS NO TEXTURE
|
|
||||||
#create the index list, don't care of newvertices
|
|
||||||
for face in mesh.faces:
|
|
||||||
faceindices = []
|
|
||||||
if (len(face.v) == 3):
|
|
||||||
for vert in face.v:
|
|
||||||
faceindices.append(vert.index)
|
|
||||||
newindices.append(faceindices[0:3])
|
|
||||||
|
|
||||||
else: # THERE IS A TEXTURE
|
|
||||||
|
|
||||||
uvlist[:] = [[0]*3 for i in range( len(mesh.verts) )]
|
|
||||||
|
|
||||||
for face in mesh.faces:
|
|
||||||
faceindices = []
|
|
||||||
if (len(face.v) == 3):
|
|
||||||
for i in range(len(face.uv)):
|
|
||||||
vindex = face.v[i].index
|
|
||||||
|
|
||||||
if notseenindex(vindex):
|
|
||||||
uvlist[vindex] = [vindex, face.uv[i][0], face.uv[i][1]]
|
|
||||||
|
|
||||||
elif uvlist[vindex][1] != face.uv[i][0] or \
|
|
||||||
uvlist[vindex][2] != face.uv[i][1]:
|
|
||||||
#debug: print("dif: [%f,%f] et [%f,%f]" %( uvlist[vindex][1], face.uv[i][0], uvlist[vindex][2], face.uv[i][1] ))
|
|
||||||
#Create a new "entry" for an existing vertex
|
|
||||||
newvertices.append(vindex)
|
|
||||||
uvlist.append([currentnewvertex, face.uv[i][0], face.uv[i][1]])
|
|
||||||
vindex = currentnewvertex
|
|
||||||
currentnewvertex += 1
|
|
||||||
|
|
||||||
faceindices.append(vindex)
|
|
||||||
newindices.append(faceindices[0:3])
|
|
||||||
|
|
||||||
#End texture and vertex copy
|
|
||||||
|
|
||||||
|
|
||||||
# ---- BEGINNING OF THE WRITING OF THE FILE ----
|
|
||||||
|
|
||||||
#MESH HEADER
|
|
||||||
out.write( '\n<Mesh \n' )
|
|
||||||
out.write( ' name="%s" \n' % (mesh.name) )
|
|
||||||
out.write( ' frameCount="%i" \n' % ( frameCount ) )
|
|
||||||
out.write( ' vertexCount="%i" \n' % (len(mesh.verts) + len(newvertices)) )
|
|
||||||
out.write( ' indexCount="%i" \n' % ( realFaceCount * 3 ) )
|
|
||||||
out.write( ' specularPower="9.999999" \n' )
|
|
||||||
out.write( ' opacity="%f" \n' % (opacity) )
|
|
||||||
out.write( ' twoSided="%s" \n' % ( twoSided ) )
|
|
||||||
out.write( ' customColor="%s" \n' % ( customColor ) )
|
|
||||||
out.write( ' diffuseTexture="%s" > \n' % ( diffuseTexture ) )
|
|
||||||
|
|
||||||
#DIFFUSE
|
|
||||||
out.write( ' <Diffuse>\n <Color r="%f" g="%f" b="%f" />\n </Diffuse>\n' % ( diffuseColor[0], diffuseColor[1], diffuseColor[2] ) )
|
|
||||||
|
|
||||||
#SPECULAR # part Hardcoded Color: THIS WAS ALREADY HARDCODED ...
|
|
||||||
out.write( ' <Specular><Color r="%f" g="%f" b="%f" /></Specular>\n' % (HardCodedSpecular, HardCodedSpecular, HardCodedSpecular) )
|
|
||||||
|
|
||||||
#TEXTURE
|
|
||||||
if textureName == '':
|
|
||||||
out.write( ' <!-- NO TEXTURE -->\n' )
|
|
||||||
else:
|
|
||||||
out.write( ' <Texture name="%s" />\n' % (textureName) )
|
|
||||||
|
|
||||||
#For each FRAME
|
|
||||||
|
|
||||||
#VERTICES
|
|
||||||
framelist={}
|
|
||||||
l = range( startFrame , startFrame+frameCount )
|
|
||||||
for frame in l:
|
|
||||||
# set the right frame
|
|
||||||
Blender.Set('curframe', frame)
|
|
||||||
curObject=object
|
|
||||||
curMatrix= curObject.getMatrix('worldspace')
|
|
||||||
|
|
||||||
fmesh = curObject.getData()
|
|
||||||
|
|
||||||
# after creation of new Object, Selection is no longer reliable (new obj. are selected, too), already corrected in code (see: curObject= ...)
|
|
||||||
defmesh=Blender.Mesh.New(curObject.name+'F'+str(frame))
|
|
||||||
defmesh.getFromObject(curObject.name)
|
|
||||||
|
|
||||||
framelist[frame]=defmesh
|
|
||||||
|
|
||||||
# just to avoid work
|
|
||||||
fmesh=defmesh
|
|
||||||
|
|
||||||
out.write( ' <Vertices frame="%i">\n' % ( frame-startFrame ) )
|
|
||||||
# "real" vertex
|
|
||||||
for vert in fmesh.verts:
|
|
||||||
vert.co= vert.co * curMatrix
|
|
||||||
out.write( ' <Vertex x="%f" y="%f" z="%f" />\n' % (vert.co.x,vert.co.y,vert.co.z) )
|
|
||||||
# "linked" new vertex
|
|
||||||
for ind in newvertices:
|
|
||||||
out.write( ' <Vertex x="%f" y="%f" z="%f" />\n' % \
|
|
||||||
(fmesh.verts[ind].co.x, fmesh.verts[ind].co.y, fmesh.verts[ind].co.z) )
|
|
||||||
out.write( ' </Vertices>\n' )
|
|
||||||
|
|
||||||
#NORMALS
|
|
||||||
l = range( startFrame , startFrame+frameCount )
|
|
||||||
for frame in l:
|
|
||||||
Blender.Set('curframe', frame)
|
|
||||||
|
|
||||||
# getting the created meshes
|
|
||||||
fmesh = framelist[frame] #.getData()
|
|
||||||
|
|
||||||
out.write( ' <Normals frame="%i">\n' % ( frame-startFrame ) )
|
|
||||||
# "real" vertex
|
|
||||||
for vert in fmesh.verts:
|
|
||||||
out.write( ' <Normal x="%f" y="%f" z="%f" />\n' % (vert.no.x, vert.no.y, vert.no.z) )
|
|
||||||
# "linked" new vertex
|
|
||||||
for ind in newvertices:
|
|
||||||
out.write( ' <Normal x="%f" y="%f" z="%f" />\n' % \
|
|
||||||
(fmesh.verts[ind].no.x, fmesh.verts[ind].no.y, fmesh.verts[ind].no.z) )
|
|
||||||
out.write( ' </Normals>\n' )
|
|
||||||
|
|
||||||
#End FRAMES
|
|
||||||
|
|
||||||
|
|
||||||
#TEXTURES COORDS
|
|
||||||
if textureName == '':
|
|
||||||
out.write( ' <!-- NO TEXTURE COORDS -->\n' )
|
|
||||||
else:
|
|
||||||
out.write( ' <TexCoords>\n' )
|
|
||||||
#write list
|
|
||||||
for uv in uvlist:
|
|
||||||
out.write( ' <ST s="%f" t="%f" />\n' % (uv[1], uv[2]) )
|
|
||||||
out.write( ' </TexCoords>\n' )
|
|
||||||
|
|
||||||
#INDICES
|
|
||||||
out.write( ' <Indices>\n' )
|
|
||||||
for face in newindices:
|
|
||||||
for vert in face:
|
|
||||||
out.write( ' <Ix i="%i" />\n' % (vert) )
|
|
||||||
# out.write('\n')
|
|
||||||
out.write( ' </Indices>\n' )
|
|
||||||
|
|
||||||
#END MESH
|
|
||||||
out.write( '</Mesh>\n' )
|
|
||||||
|
|
||||||
#Printing a summary of the mesh to the output
|
|
||||||
print("\nObject : %s" %(object.getName() ) )
|
|
||||||
print("Mesh : %s" %(mesh.name) )
|
|
||||||
print("%i frames" % ( frameCount ) )
|
|
||||||
print("%i vertices" % (len(mesh.verts) + len(newvertices)) )
|
|
||||||
print("%i exported faces (%i real faces)" % (realFaceCount, len(mesh.faces) ) )
|
|
||||||
if realFaceCount != len(mesh.faces) :
|
|
||||||
print("WARNING : some faces have been ignored (not triangle)\n" +
|
|
||||||
" Errors may occur with faces and indices linking..." )
|
|
||||||
Blender.Draw.PupMenu("export warning:%t|WARNING : some faces have been ignored (not triangle)|" +
|
|
||||||
" Errors may occur with faces and indices linking...")
|
|
||||||
print("%i indices" % (realFaceCount * 3) )
|
|
||||||
print("opacity : %f" % (opacity) )
|
|
||||||
print("two sided : %s" % (twoSided) )
|
|
||||||
print("custom color : %s" % (customColor) )
|
|
||||||
print("Use a diffuse texture : %s" % (diffuseTexture) )
|
|
||||||
if textureName != '':
|
|
||||||
print( " texture name : %s" % (textureName) )
|
|
||||||
print("diffuse color : %f,%f,%f" % ( diffuseColor[0], diffuseColor[1], diffuseColor[2] ) )
|
|
||||||
print("Number of new vertices to fit uv mapping : %i\n" % ( len(newvertices) ) )
|
|
||||||
|
|
||||||
#FOOTER
|
|
||||||
out.write( '</G3D>\n' )
|
|
||||||
|
|
||||||
out.close()
|
|
||||||
#END write_obj
|
|
||||||
|
|
||||||
# file selector based on ac3d_export.py by Willian P. Germano
|
|
||||||
# File Selector callback:
|
|
||||||
def fs_callback(filename):
|
|
||||||
global cmd, extension, scriptsdir
|
|
||||||
if not filename.endswith(extension): filename = '%s%s' % (filename, extension)
|
|
||||||
if bsys.exists(filename):
|
|
||||||
if Blender.Draw.PupMenu('OVERWRITE?%t|File exists') != 1:
|
|
||||||
return
|
|
||||||
if cmd:
|
|
||||||
tmp = bsys.join(scriptsdir, "tmp.xml")
|
|
||||||
write_obj(tmp)
|
|
||||||
|
|
||||||
print "running glexemel"
|
|
||||||
cmd = [cmd, tmp, filename]
|
|
||||||
print cmd
|
|
||||||
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
||||||
out = p.stdout.read()
|
|
||||||
err = p.stderr.read()
|
|
||||||
res = p.wait()
|
|
||||||
if res != 0 or err:
|
|
||||||
s = ''
|
|
||||||
if out:
|
|
||||||
s += out.replace('\n', '|')
|
|
||||||
if err:
|
|
||||||
s += err.replace('\n', '|')
|
|
||||||
Blender.Draw.PupMenu('glexemel error: see console%t|' + s)
|
|
||||||
print out
|
|
||||||
print err
|
|
||||||
print res
|
|
||||||
else:
|
|
||||||
write_obj(filename)
|
|
||||||
print "glexemel not found"
|
|
||||||
|
|
||||||
|
|
||||||
scriptsdir = Blender.Get('scriptsdir')
|
|
||||||
files = os.listdir(scriptsdir)
|
|
||||||
cmd = ''
|
|
||||||
extension=".xml"
|
|
||||||
for fname in files:
|
|
||||||
if fname.startswith("xml2g"):
|
|
||||||
cmd = bsys.join(scriptsdir, fname)
|
|
||||||
extension = ".g3d"
|
|
||||||
break
|
|
||||||
|
|
||||||
OBJS = Blender.Object.GetSelected()
|
|
||||||
if not OBJS:
|
|
||||||
Blender.Draw.PupMenu('ERROR: no objects selected')
|
|
||||||
else:
|
|
||||||
fname = bsys.makename(ext=extension)
|
|
||||||
Blender.Window.FileSelector(fs_callback, "Export XML-G3D", fname)
|
|
||||||
#Blender.Window.FileSelector(write_obj, "Export")
|
|
Loading…
Reference in New Issue