Переглянути джерело

Rewriting the Mesh loader. The new format for meshes is now a binary format. Need to write documentation about the file format

Panagiotis Christopoulos Charitos 15 роки тому
батько
коміт
e2d27e87cb
11 змінених файлів з 682 додано та 504 видалено
  1. 1 1
      .cproject
  2. 1 1
      .project
  3. 2 2
      blenderscripts/main.py
  4. 62 7
      blenderscripts/mesh.py
  5. 395 394
      build/debug/Makefile
  6. 10 20
      src/Main.cpp
  7. 104 71
      src/Resources/Mesh.cpp
  8. 6 3
      src/Resources/Mesh.h
  9. 2 2
      src/Util/Tokenizer/Parser.h
  10. 82 0
      src/Util/Util.cpp
  11. 17 3
      src/Util/Util.h

+ 1 - 1
.cproject

@@ -20,7 +20,7 @@
 <option id="cdt.managedbuild.option.gnu.cross.prefix.124248385" name="Prefix" superClass="cdt.managedbuild.option.gnu.cross.prefix"/>
 <option id="cdt.managedbuild.option.gnu.cross.path.568633506" name="Path" superClass="cdt.managedbuild.option.gnu.cross.path"/>
 <targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.ELF" id="cdt.managedbuild.targetPlatform.gnu.cross.2099631" isAbstract="false" osList="all" superClass="cdt.managedbuild.targetPlatform.gnu.cross"/>
-<builder arguments="make -j 3" autoBuildTarget="all" buildPath="${workspace_loc:/anki/build/debug}" cleanBuildTarget="clean" command="nice" enableAutoBuild="false" enableCleanBuild="true" enabledIncrementalBuild="true" id="cdt.managedbuild.toolchain.gnu.cross.base.1593777304.618553305" incrementalBuildTarget="all" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" parallelizationNumber="1" superClass="org.eclipse.cdt.build.core.settings.default.builder">
+<builder arguments="make -j 4" autoBuildTarget="all" buildPath="${workspace_loc:/anki/build/debug}" cleanBuildTarget="clean" command="nice" enableAutoBuild="false" enableCleanBuild="true" enabledIncrementalBuild="true" id="cdt.managedbuild.toolchain.gnu.cross.base.1593777304.618553305" incrementalBuildTarget="all" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" parallelizationNumber="1" superClass="org.eclipse.cdt.build.core.settings.default.builder">
 <outputEntries>
 <entry flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="outputPath" name="build/debug"/>
 </outputEntries>

+ 1 - 1
.project

@@ -27,7 +27,7 @@
 				</dictionary>
 				<dictionary>
 					<key>org.eclipse.cdt.make.core.buildArguments</key>
-					<value>make -j 3</value>
+					<value>make -j 4</value>
 				</dictionary>
 				<dictionary>
 					<key>org.eclipse.cdt.make.core.buildCommand</key>

+ 2 - 2
blenderscripts/main.py

@@ -31,13 +31,13 @@ for obj in objs:
 	mi.flipYZ = 1
 	mesh.export(mi)
 	
-	si = skeleton.Initializer()
+	"""si = skeleton.Initializer()
 	si.skeleton = skeleton.getBlSkeletonFromBlObj(obj)
 	si.saveDir = "/home/godlike/src/anki/models/imp/"
 	si.flipYZ = 1
 	skeleton.export(si)
 	
-	"""ai = skelanim.Initializer()
+	ai = skelanim.Initializer()
 	ai.obj = obj
 	ai.saveDir = "/home/godlike/src/anki/models/imp/"
 	ai.flipYZ = 1

+ 62 - 7
blenderscripts/mesh.py

@@ -1,5 +1,6 @@
 import sys
 import os
+from struct import pack
 from copy import deepcopy
 from Blender import Mathutils
 from Blender.Mathutils import *
@@ -233,16 +234,17 @@ def	getAnkiMeshScript(mesh, skeleton, mtlName, flipYZ):
 	# write to ftxt
 	
 	# write mtl name
-	ftxt = "\"" + mtlName + "\"\n"
+	"""ftxt = "mesh\n{\n"
+	ftxt += "\tmaterial \"" + mtlName + "\"\n"
 	
 	# write verts
-	ftxt += str(len(ankiVerts)) + "\n"
+	ftxt += "\tvertsNum " + str(len(ankiVerts)) + "\n\tverts\n\t{\n"
 	for i in range(len(ankiVerts)):
 		ankiVert = ankiVerts[i]
 		if flipYZ == 0:
-			ftxt += str(ankiVert.x) + " " + str(ankiVert.y) + " " + str(ankiVert.z) + "\n"
+			ftxt += "\t\tvert {" + str(ankiVert.x) + " " + str(ankiVert.y) + " " + str(ankiVert.z) + "}\n"
 		else:
-			ftxt += str(ankiVert.x) + " " + str(ankiVert.z) + " " + str(-ankiVert.y) + "\n"
+			ftxt += "\t\tvert {" + str(ankiVert.x) + " " + str(ankiVert.z) + " " + str(-ankiVert.y) + "}\n"
 		
 	# write the tris
 	ftxt += str(len(ankiTris)) + "\n"
@@ -271,7 +273,60 @@ def	getAnkiMeshScript(mesh, skeleton, mtlName, flipYZ):
 	else:
 		ftxt += "0\n"
 
-	return ftxt
+	return ftxt"""
+	
+	# Magic
+	buff = pack("8s", "ANKIMESH")
+	
+	# Mesh name
+	str_ = "meshname"
+	buff += pack("I" + str(len(str_)) + "s", len(str_), str_)
+	
+	# Mtl name
+	str_ = mtlName
+	buff += pack("I" + str(len(str_)) + "s", len(str_), str_)
+	
+	# Verts num
+	buff += pack("I", len(ankiVerts))
+	
+	# Verts
+	for i in range(len(ankiVerts)):
+		ankiVert = ankiVerts[i]
+		if flipYZ == 0:
+			buff += pack("fff", ankiVert.x, ankiVert.y, ankiVert.z)
+		else:
+			buff += pack("fff", ankiVert.x, ankiVert.z, -ankiVert.y)
+	
+	# Tris num
+	buff += pack("I", len(ankiTris))
+	
+	# Tris
+	for ankiTri in ankiTris:
+		buff += pack("III", int(ankiTri.vertIds[0]), int(ankiTri.vertIds[1]), int(ankiTri.vertIds[2]))
+		
+	# Tex coords
+	if hasUvs:
+		buff += pack("I", len(ankiVerts))
+		for i in range(len(ankiVerts)):
+			buff += pack("ff", ankiVerts[i].s, ankiVerts[i].t)
+	else:
+		buff += pack("I", 0)
+		
+	# Bone weights
+	if skeleton != None:
+		updateAnkiVertsWithBoneWeights(mesh, skeleton, ankiVerts)
+		
+		buff += pack("I", len(ankiVerts))
+		
+		for i in range(len(ankiVerts)):
+			ankiVert = ankiVerts[i]
+			buff += pack("I", ankiVert.bonesNum)
+			for j in range(ankiVert.bonesNum):
+				buff += pack("If", ankiVert.boneIds[j], ankiVert.weights[j])
+	else:
+		buff += pack("I", 0)
+	
+	return buff
 			
 		
 #=======================================================================================================================
@@ -292,7 +347,7 @@ def export(meshInit):
 	
 	print("Trying to export mesh \"" + mesh.name + "\"")
 	filename = os.path.abspath(meshInit.saveDir + mesh.name + ".mesh")
-	file = open(filename, "w")
+	file = open(filename, "wb")
 	file.write(getAnkiMeshScript(mesh, skeleton, meshInit.mtlName, meshInit.flipYZ))
 	print("Mesh exported!! \"" + filename + "\"")	
-	
+	

Різницю між файлами не показано, бо вона завелика
+ 395 - 394
build/debug/Makefile


+ 10 - 20
src/Main.cpp

@@ -165,17 +165,18 @@ void init()
 	spot_lights[1]->init("maps/temple/light3.light");
 	spot_lights[1]->setLocalTransform(Transform(Vec3(-2.3, 6.3, 2.9), Mat3(Euler(toRad(-70), toRad(-20), 0.0)), 1.0));
 
+	const char* skybox_fnames [] = { "textures/env/hellsky4_forward.tga", "textures/env/hellsky4_back.tga", "textures/env/hellsky4_left.tga",
+																	 "textures/env/hellsky4_right.tga", "textures/env/hellsky4_up.tga", "textures/env/hellsky4_down.tga" };
+	app->getScene().skybox.load(skybox_fnames);
+
 	// horse
-	horse = new MeshNode();
+	/*horse = new MeshNode();
 	horse->init("meshes/horse/horse.mesh");
 	//horse->init("models/head/head.mesh");
-	horse->setLocalTransform(Transform(Vec3(-2, 0, 1), Mat3(Euler(-M::PI/2, 0.0, 0.0)), 0.5));
-	
-	RsrcPtr<Texture> ttex;
-	ttex.loadRsrc("gfx/stone.diff.png");
+	horse->setLocalTransform(Transform(Vec3(-2, 0, 1), Mat3(Euler(-M::PI/2, 0.0, 0.0)), 0.5));*/
 
 	// sarge
-	sarge = new MeshNode();
+	/*sarge = new MeshNode();
 	sarge->init("meshes/sphere/sphere16.mesh");
 	//sarge->setLocalTransform(Vec3(0, -2.8, 1.0), Mat3(Euler(-M::PI/2, 0.0, 0.0)), 1.1);
 	sarge->setLocalTransform(Transform(Vec3(0, 2.0, 2.0), Mat3::getIdentity(), 0.4));
@@ -183,7 +184,7 @@ void init()
 	// floor
 	floor__ = new MeshNode();
 	floor__->init("maps/temple/Cube.019.mesh");
-	floor__->setLocalTransform(Transform(Vec3(0.0, -0.19, 0.0), Mat3(Euler(-M::PI/2, 0.0, 0.0)), 0.8));
+	floor__->setLocalTransform(Transform(Vec3(0.0, -0.19, 0.0), Mat3(Euler(-M::PI/2, 0.0, 0.0)), 0.8));*/
 
 	// imp	
 	imp = new SkelModelNode();
@@ -193,6 +194,8 @@ void init()
 	ctrl->skelAnim.loadRsrc("models/imp/walk.imp.anim");
 	ctrl->step = 0.8;
 
+	return;
+
 	// cave map
 	/*for(int i=1; i<21; i++)
 	{
@@ -233,10 +236,6 @@ void init()
 	//floor_ = new floor_t;
 	//floor_->material = RsrcMngr::materials.load("materials/default.mtl");
 
-	const char* skybox_fnames [] = { "textures/env/hellsky4_forward.tga", "textures/env/hellsky4_back.tga", "textures/env/hellsky4_left.tga",
-																	 "textures/env/hellsky4_right.tga", "textures/env/hellsky4_up.tga", "textures/env/hellsky4_down.tga" };
-	app->getScene().skybox.load(skybox_fnames);
-
 
 	initPhysics();
 
@@ -371,15 +370,6 @@ void mainLoop()
 //======================================================================================================================
 int main(int argc, char* argv[])
 {
-	Scanner scanner;
-	stringstream ss;
-	ss << "{-10.1 0101}";
-	scanner.loadIstream(ss);
-	Vec2 v;
-	Parser::parseMathVector(scanner, v);
-	cout << v << endl;
-	return false;
-
 	new App(argc, argv);
 	init();
 

+ 104 - 71
src/Resources/Mesh.cpp

@@ -1,148 +1,181 @@
+#include <fstream>
 #include "Mesh.h"
-#include "Renderer.h"
-#include "Resource.h"
-#include "Scanner.h"
-#include "Parser.h"
 #include "Material.h"
 
 
+#define MESH_ERR(x) \
+	ERROR("File \"" << filename << "\": " << x)
+
+
 //======================================================================================================================
 // load                                                                                                                =
 //======================================================================================================================
 bool Mesh::load(const char* filename)
 {
-	Scanner scanner;
-	if(!scanner.loadFile(filename))
+	fstream file(filename, fstream::in | fstream::binary);
+
+	if(!file.is_open())
+	{
+		ERROR("Cannot open file \"" << filename << "\"");
 		return false;
+	}
+
+	// Magic word
+	char magic[8];
+	file.readsome(magic, 8);
+	if(file.fail() || memcmp(magic, "ANKIMESH", 8))
+	{
+		MESH_ERR("Incorrect file type");
+		return false;
+	}
 
-	const Scanner::Token* token;
+	// Mesh name
+	string meshName;
+	if(!Util::readStringFromBinaryStream(file, meshName))
+	{
+		MESH_ERR("Cannot read mesh name");
+		return false;
+	}
 
-	/*
-	 * Material
-	 */
-	token = &scanner.getNextToken();
-	if(token->getCode() != Scanner::TC_STRING)
+	// Material
+	string materialName;
+	if(!Util::readStringFromBinaryStream(file, materialName))
 	{
-		PARSE_ERR_EXPECTED("string");
+		MESH_ERR("Cannot read material name");
 		return false;
 	}
-	if(strlen(token->getValue().getString()) > 0)
-		material.loadRsrc(token->getValue().getString());
-
-	/*
-	 * Verts
-	 */
-	// verts num
-	token = &scanner.getNextToken();
-	if(token->getCode() != Scanner::TC_NUMBER || token->getDataType() != Scanner::DT_INT)
+	if(materialName.length() > 0)
 	{
-		PARSE_ERR_EXPECTED("integer");
+		material.loadRsrc(materialName.c_str());
+	}
+
+	// Verts num
+	uint vertsNum;
+	if(!Util::readUintFromBinaryStream(file, vertsNum))
+	{
+		MESH_ERR("Cannot read verts num");
 		return false;
 	}
-	vertCoords.resize(token->getValue().getInt());
+	vertCoords.resize(vertsNum);
 
-	// read the verts
+	// Verts
 	for(uint i=0; i<vertCoords.size(); i++)
 	{
-		if(!Parser::parseMathVector(scanner, vertCoords[i]))
+		for(uint j=0; j<3; j++)
 		{
-			return false;
+			if(!Util::readFloatFromBinaryStream(file, vertCoords[i][j]))
+			{
+				MESH_ERR("Cannot read vert coord");
+				return false;
+			}
 		}
 	}
 
-	/*
-	 * Faces
-	 */
-	// faces num
-	token = &scanner.getNextToken();
-	if(token->getCode() != Scanner::TC_NUMBER || token->getDataType() != Scanner::DT_INT)
+	// Faces num
+	uint facesNum;
+	if(!Util::readUintFromBinaryStream(file, facesNum))
 	{
-		PARSE_ERR_EXPECTED("integer");
+		MESH_ERR("Cannot read faces num");
 		return false;
 	}
-	tris.resize(token->getValue().getInt());
+	tris.resize(facesNum);
+
 	// read the faces
 	for(uint i=0; i<tris.size(); i++)
 	{
-		if(!Parser::parseArrOfNumbers<uint>(scanner, false, true, 3, tris[i].vertIds))
+		for(uint j=0; j<3; j++)
 		{
-			return false;
+			if(!Util::readUintFromBinaryStream(file, tris[i].vertIds[j]))
+			{
+				MESH_ERR("Cannot read vert index");
+				return false;
+			}
+
+			if(tris[i].vertIds[j] >= vertCoords.size())
+			{
+				MESH_ERR("Incorrect vert index " << tris[i].vertIds[j] << " (" << i << ", " << j << ")");
+				return false;
+			}
 		}
 	}
 
-	/*
-	 * Tex coords
-	 */
-	// UVs num
-	token = &scanner.getNextToken();
-	if(token->getCode() != Scanner::TC_NUMBER || token->getDataType() != Scanner::DT_INT)
+	// Tex coords num
+	uint texCoordsNum;
+	if(!Util::readUintFromBinaryStream(file, texCoordsNum))
 	{
-		PARSE_ERR_EXPECTED("integer");
+		MESH_ERR("Cannot read tex coords num");
 		return false;
 	}
-	texCoords.resize(token->getValue().getInt());
-	// read the texCoords
+	texCoords.resize(texCoordsNum);
+
+	// Tex coords
 	for(uint i=0; i<texCoords.size(); i++)
 	{
-		if(!Parser::parseArrOfNumbers(scanner, false, true, 2, &texCoords[i][0]))
-			return false;
+		for(uint j=0; j<2; j++)
+		{
+			if(!Util::readFloatFromBinaryStream(file, texCoords[i][j]))
+			{
+				MESH_ERR("Cannot read tex coord");
+				return false;
+			}
+		}
 	}
 
-	/*
-	 * Vert weights
-	 */
-	token = &scanner.getNextToken();
-	if(token->getCode() != Scanner::TC_NUMBER || token->getDataType() != Scanner::DT_INT)
+	// Vert weights num
+	uint vertWeightsNum;
+	if(!Util::readUintFromBinaryStream(file, vertWeightsNum))
 	{
-		PARSE_ERR_EXPECTED("integer");
+		MESH_ERR("Cannot read vert weights num");
 		return false;
 	}
-	vertWeights.resize(token->getValue().getInt());
+	vertWeights.resize(vertWeightsNum);
+
+	// Vert weights
 	for(uint i=0; i<vertWeights.size(); i++)
 	{
 		// get the bone connections num
-		token = &scanner.getNextToken();
-		if(token->getCode() != Scanner::TC_NUMBER || token->getDataType() != Scanner::DT_INT)
+		uint boneConnections;
+		if(!Util::readUintFromBinaryStream(file, boneConnections))
 		{
-			PARSE_ERR_EXPECTED("integer");
+			MESH_ERR("Cannot read vert weight bone connections num");
 			return false;
 		}
 
 		// we treat as error if one vert doesnt have a bone
-		if(token->getValue().getInt() < 1)
+		if(boneConnections < 1)
 		{
-			ERROR("Vert \"" << i << "\" doesnt have at least one bone");
+			MESH_ERR("Vert \"" << i << "\" sould have have at least one bone");
 			return false;
 		}
 
 		// and here is another possible error
-		if(token->getValue().getInt() > VertexWeight::MAX_BONES_PER_VERT)
+		if(boneConnections > VertexWeight::MAX_BONES_PER_VERT)
 		{
-			ERROR("Cannot have more than " << VertexWeight::MAX_BONES_PER_VERT << " bones per vertex");
+			MESH_ERR("Cannot have more than " << VertexWeight::MAX_BONES_PER_VERT << " bones per vertex");
 			return false;
 		}
-		vertWeights[i].bonesNum = token->getValue().getInt();
+		vertWeights[i].bonesNum = boneConnections;
 
 		// for all the weights of the current vertes
 		for(uint j=0; j<vertWeights[i].bonesNum; j++)
 		{
 			// read bone id
-			token = &scanner.getNextToken();
-			if(token->getCode() != Scanner::TC_NUMBER || token->getDataType() != Scanner::DT_INT)
+			uint boneId;
+			if(!Util::readUintFromBinaryStream(file, boneId))
 			{
-				PARSE_ERR_EXPECTED("integer");
+				MESH_ERR("Cannot read bone ID");
 				return false;
 			}
-			vertWeights[i].boneIds[j] = token->getValue().getInt();
+			vertWeights[i].boneIds[j] = boneId;
 
 			// read the weight of that bone
-			token = &scanner.getNextToken();
-			if(token->getCode() != Scanner::TC_NUMBER || token->getDataType() != Scanner::DT_FLOAT)
+			float weight;
+			if(!Util::readFloatFromBinaryStream(file, weight))
 			{
-				PARSE_ERR_EXPECTED("float");
+				MESH_ERR("Cannot read weight");
 				return false;
 			}
-			vertWeights[i].weights[j] = token->getValue().getFloat();
+			vertWeights[i].weights[j] = weight;
 		}
 	}
 

+ 6 - 3
src/Resources/Mesh.h

@@ -12,9 +12,12 @@
 class Material;
 
 
-/**
- * Mesh @ref Resource. If the material name is empty then the mesh wont be rendered and no VBOs will be created
- */
+/// Mesh Resource. If the material name is empty then the mesh wont be rendered and no VBOs will be created
+///
+/// File format:
+///
+/// <magic:ANKIMESH> <string:meshName> <string:material> <uint:vertsNum> <float:vert[0].x> <float:vert[0].y>
+/// <float:vert[0].y> ... <float:vert[n].x> <float:vert[n].y> <float:vert[n].z>
 class Mesh: public Resource
 {
 	public:

+ 2 - 2
src/Util/Tokenizer/Parser.h

@@ -1,5 +1,5 @@
-#ifndef _PARSER_H_
-#define _PARSER_H_
+#ifndef PARSER_H
+#define PARSER_H
 
 #include "Common.h"
 #include "Scanner.h"

+ 82 - 0
src/Util/Util.cpp

@@ -81,4 +81,86 @@ float randFloat(float max)
 }
 
 
+//======================================================================================================================
+// readStringFromBinaryStream                                                                                          =
+//======================================================================================================================
+bool readStringFromBinaryStream(istream& s, string& out, ByteOrder byteOrder)
+{
+	uchar c[4];
+	s.read((char*)c, sizeof(c));
+	if(s.fail())
+	{
+		ERROR("Failed to read string size");
+		return false;
+	}
+	uint size;
+	if(byteOrder == BO_LITTLE_ENDIAN)
+	{
+		size = (c[0] | (c[1]<<8) | (c[2]<<16) | (c[3]<<24));
+	}
+	else
+	{
+		size = ((c[0]<<24) | (c[1]<<16) | (c[2]<< 8) | c[3]);
+	}
+
+	const uint buffSize = 1024;
+	if((size + 1) > buffSize)
+	{
+		ERROR("String bugger than " << (buffSize + 1));
+		return false;
+	}
+	char buff[buffSize];
+	s.read(buff, size);
+	if(s.fail())
+	{
+		ERROR("Failed to read " << size << " bytes");
+		return false;
+	}
+	buff[size] = '\0';
+
+	out =  buff;
+	return true;
+}
+
+
+//======================================================================================================================
+// readUintFromBinaryStream                                                                                            =
+//======================================================================================================================
+bool readUintFromBinaryStream(istream& s, uint& out, ByteOrder byteOrder)
+{
+	uchar c[4];
+	s.read((char*)c, sizeof(c));
+	if(s.fail())
+	{
+		ERROR("Failed to read uint");
+		return false;
+	}
+
+	if(byteOrder == BO_LITTLE_ENDIAN)
+	{
+		out = (c[0] | (c[1]<<8) | (c[2]<<16) | (c[3]<<24));
+	}
+	else
+	{
+		out = ((c[0]<<24) | (c[1]<<16) | (c[2]<< 8) | c[3]);
+	}
+	return true;
+}
+
+
+//======================================================================================================================
+// readFloatFromBinaryStream                                                                                           =
+//======================================================================================================================
+bool readFloatFromBinaryStream(istream& s, float& out)
+{
+	s.read((char*)&out, sizeof(float));
+	if(s.fail())
+	{
+		ERROR("Failed to read float");
+		return false;
+	}
+	return true;
+}
+
+
 } // end namesapce

+ 17 - 3
src/Util/Util.h

@@ -4,9 +4,7 @@
 #include "Common.h"
 #include "Vec.h"
 
-/**
- * The namespace contains a few useful functions
- */
+/// The namespace contains a few useful functions
 namespace Util {
 
 extern int    randRange(int min, int max); ///< Pick a random number from min to max
@@ -18,6 +16,22 @@ extern float randFloat(float max);
 extern string      readFile(const char* filename); ///< Open a text file and return its contents into a string
 extern Vec<string> getFileLines(const char* filename); ///< Open a text file and return its lines into a string vector
 
+
+/// @name Binary file reading
+/// @{
+
+/// Byte order for reading binary files
+enum ByteOrder
+{
+	BO_LITTLE_ENDIAN, ///< The default
+	BO_BIG_ENDIAN
+};
+
+extern bool readStringFromBinaryStream(istream& s, string& out, ByteOrder byteOrder = BO_LITTLE_ENDIAN);
+extern bool readUintFromBinaryStream(istream& s, uint& out, ByteOrder byteOrder = BO_LITTLE_ENDIAN);
+extern bool readFloatFromBinaryStream(istream& s, float& out);
+/// @}
+
 }
 
 #endif

Деякі файли не було показано, через те що забагато файлів було змінено