1 /*******************************************************************************
2 Copyright (c) 2012, Jonathan Hiller
3 
4 This file is part of the AMF Tools suite. http://amf.wikispaces.com/
5 AMF Tools is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
6 AMF Tools is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
7 See <http://www.opensource.org/licenses/lgpl-3.0.html> for license details.
8 *******************************************************************************/
9 
10 #include "X3D_File.h"
11 #include <string.h>
12 //#include "QOpenGL.h"
13 
14 #include "XmlStream.h"
15 
16 //#include <QImageReader>
17 
18 //#include <QMessageBox>
19 //#include <qfiledialog.h>
20 
21 //repeated...
22 #ifndef max
23 #define max(a,b)            (((a) > (b)) ? (a) : (b))
24 #endif
25 
26 #ifndef min
27 #define min(a,b)            (((a) < (b)) ? (a) : (b))
28 #endif
29 
30 
CX3D_File(void)31 CX3D_File::CX3D_File(void)
32 {
33 	Clear();
34 }
35 
~CX3D_File(void)36 CX3D_File::~CX3D_File(void)
37 {
38 }
39 
Load(std::string filename,std::string ImgPathIn)40 X3dLoadResult CX3D_File::Load(std::string filename, std::string ImgPathIn)
41 {
42 	Clear();
43 	CXmlStreamRead XML;
44 	if (!XML.LoadFile(filename)) return XLR_BADFILEPATH;
45 	filePath = filename;
46 	ImagePath = ImgPathIn;
47 	errors = "";
48 
49 	X3dLoadResult tmpRes;
50 
51 	if (XML.OpenElement("Scene")){
52 		int NumDown = 0;
53 		while (XML.OpenElement("Transform")) NumDown++; //ignore groups and transforms for now...
54 		while (XML.OpenElement("Group")) NumDown++; //ignore groups and transforms for now...
55 		while (XML.OpenElement("Transform")) NumDown++; //ignore groups and transforms for now...
56 		while (XML.OpenElement("Group")) NumDown++; //ignore groups and transforms for now...
57 		while (XML.OpenElement("Transform")) NumDown++; //ignore groups and transforms for now...
58 		while (XML.OpenElement("Group")) NumDown++; //ignore groups and transforms for now...
59 
60 
61 		//read as many shape tags as there are...
62 		xShapeNode tmpShape;
63 		while(XML.OpenElement("Shape", true)){ //<metadata>
64 			tmpRes = tmpShape.ReadXML(&XML, this, &errors);
65 			if (tmpRes != XLR_SUCCESS) return tmpRes;
66 			xShapes.push_back(tmpShape);
67 		}
68 //		if (xShapes.size() != 0) XML.UpLevel(); //</metadata>
69 
70 		for (int i=0; i<NumDown; i++) XML.CloseElement();
71 		XML.CloseElement();
72 	}
73 	IsLoaded = true;
74 	return XLR_SUCCESS;
75 }
76 
GetMinMax(double & minX,double & minY,double & minZ,double & maxX,double & maxY,double & maxZ)77 void CX3D_File::GetMinMax(double& minX, double& minY, double& minZ, double& maxX, double& maxY, double& maxZ)
78 {
79 	minX = minY = minZ = maxX = maxY = maxZ = 0;
80 	if (xShapes.size() == 0) return;
81 	if (xShapes[0].xIndexedFaceSet.Coordinate.size() < 3) return;
82 
83 	minX = maxX = xShapes[0].xIndexedFaceSet.Coordinate[0];
84 	minY = maxY = xShapes[0].xIndexedFaceSet.Coordinate[1];
85 	minZ = maxZ = xShapes[0].xIndexedFaceSet.Coordinate[2];
86 
87 	for (std::vector<xShapeNode>::iterator it = xShapes.begin(); it != xShapes.end(); it++) {
88 		if (it->xIndexedFaceSet.Coordinate.size()%3 != 0) continue;
89 
90 		for (std::vector<double>::iterator jt = it->xIndexedFaceSet.Coordinate.begin(); jt < it->xIndexedFaceSet.Coordinate.end(); jt += 3) {
91 			minX = (std::min)(minX, *jt);
92 			minY = (std::min)(minY, *(jt+1));
93 			minZ = (std::min)(minZ, *(jt+2));
94 			maxX = (std::max)(maxX, *jt);
95 			maxY = (std::max)(maxY, *(jt+1));
96 			maxZ = (std::max)(maxZ, *(jt+2));
97 		}
98 	}
99 }
100 
GetSize(double & sizeX,double & sizeY,double & sizeZ)101 void CX3D_File::GetSize(double& sizeX, double& sizeY, double& sizeZ)
102 {
103 	double xmin, ymin, zmin, xmax, ymax, zmax;
104 	GetMinMax(xmin, ymin, zmin, xmax, ymax, zmax);
105 	sizeX = xmax-xmin;
106 	sizeY = ymax-ymin;
107 	sizeZ = zmax-zmin;
108 
109 }
110 
Str2Data(std::string * pS,std::vector<int> * pD)111 void CX3D_File::Str2Data(std::string* pS, std::vector<int>* pD)
112 {
113 	int Size = pS->size();
114 	char *a=new char[Size+1];
115 	a[Size]=0;
116 	memcpy(a,pS->c_str(),Size);
117 
118 	char* pEnd = a;
119 	while (*pEnd != '\0'){
120 		pD->push_back(strtol(pEnd, &pEnd, 10));
121 		while (*pEnd == ' ' || *pEnd == '\t' || *pEnd == '\n') pEnd++;
122 	}
123 //
124 //	std::stringstream ss(*pS);
125 //	int i;
126 //	while (ss >> i) pD->push_back(i);
127 }
128 
Str2Data(std::string * pS,std::vector<double> * pD)129 void CX3D_File::Str2Data(std::string* pS, std::vector<double>* pD)
130 {
131 	int Size = pS->size();
132 	char *a=new char[Size+1];
133 	a[Size]=0;
134 	memcpy(a,pS->c_str(),Size);
135 
136 	char* pEnd = a;
137 	while (*pEnd != '\0'){
138 		pD->push_back(strtod(pEnd, &pEnd));
139 		while (*pEnd != 0 && *pEnd <= 33 ) pEnd++; //includes all whitespace, including space.
140 	}
141 
142 //	std::stringstream ss(*pS);
143 //	double i;
144 //	while (ss >> i) pD->push_back(i);
145 }
146 
147 
148 
ReadXML(CXmlStreamRead * pXML,CX3D_File * pX3dFile,std::string * pRetMessage)149 X3dLoadResult xAppearanceNode::ReadXML(CXmlStreamRead* pXML, CX3D_File* pX3dFile, std::string* pRetMessage)
150 {
151 	Clear();
152 
153 	if (pXML->OpenElement("ImageTexture")){
154 		std::string ImgPath;
155 		pXML->GetElAttS("url", &ImgPath);
156 
157 		if (!pXML->GetElAttB("repeatS", &repeatS)) repeatS = true; //defaults to true!
158 		if (!pXML->GetElAttB("repeatT", &repeatT)) repeatT = true; //defaults to true!
159 
160 //		QList<QByteArray> test = QImageReader::supportedImageFormats();
161 
162 		//if we've provided an image path, load that!
163 		if (pX3dFile->ImagePath != ""){
164 			if (!ImageTexture.LoadImage(pX3dFile->ImagePath)) return XLR_BADIMAGEPATH;
165 		}
166 		//otherwise try loading as is in the x3d file:
167 		else if (!ImageTexture.LoadImage(ImgPath)){ //if not at the absolute path...
168 
169 			//get image filename (without original path) and try in the folder the x3d file was in
170 			size_t start = ImgPath.find_last_of("/\\") +1;
171 			if (start == 0) start = ImgPath.find_first_of("\"")+1;//nothing found...
172 
173 			size_t end = start;
174 			while (ImgPath[end] != '\"') end++;
175 			std::string imgName = ImgPath.substr(start, end-start);
176 
177 			//get x3d file path
178 			std::string FilePath = pX3dFile->filePath.substr(0, pX3dFile->filePath.find_last_of("/\\")+1);
179 			//load the image here, or give an error if cannot find it...
180 			if (!ImageTexture.LoadImage(FilePath + imgName)){ //check the folder that the x3d file was located in
181 				pX3dFile->ImagePath = FilePath + imgName;
182 				*pRetMessage +=  "Could not find texture file " + imgName + ".\n";
183 				return XLR_BADIMAGEPATH;
184 			}
185 		}
186 //		pX3dFile->imagePath = ""; //flag for succesful image load...
187 
188 		pXML->CloseElement();
189 	}
190 	if (pXML->OpenElement("Material")){
191 		std::string Data;
192 		if (pXML->GetElAttS("diffuseColor", &Data)){
193 			std::vector<double> Color;
194 			CX3D_File::Str2Data(&Data, &Color);
195 			MatColorRed = Color[0];
196 			MatColorGreen = Color[1];
197 			MatColorBlue = Color[2];
198 		}
199 		pXML->CloseElement();
200 	}
201 	return XLR_SUCCESS;
202 }
203 
operator =(const xIndexedFaceSetNode & x)204 xIndexedFaceSetNode& xIndexedFaceSetNode::operator=(const xIndexedFaceSetNode& x)
205 {
206 	coordIndex=x.coordIndex;
207 	texCoordIndex=x.texCoordIndex;
208 	Coordinate=x.Coordinate;
209 	TextureCoordinate=x.TextureCoordinate;
210 	colorPerVertex=x.colorPerVertex;
211 	colorIndex=x.colorIndex;
212 	Color=x.Color;
213 	ColorRGBA=x.ColorRGBA;
214 	CoordDef=x.CoordDef;
215 	CoordUse=x.CoordUse;
216 	NumVertPerFacet=x.NumVertPerFacet;
217 	Colors=x.Colors;
218 	ColByIndex=x.ColByIndex;
219 	HasAlpha=x.HasAlpha;
220 	HasTexture=x.HasTexture;
221 	return *this;
222 }
223 
Clear()224 void xIndexedFaceSetNode::Clear()
225 {
226 	coordIndex.clear();
227 	texCoordIndex.clear();
228 	Coordinate.clear();
229 	TextureCoordinate.clear();
230 	colorPerVertex = true;
231 	colorIndex.clear();
232 	Color.clear();
233 	ColorRGBA.clear();
234 	CoordDef="";
235 	CoordUse="";
236 
237 	NumVertPerFacet = 0;
238 	Colors = false;
239 	ColByIndex = false;
240 	HasAlpha = false;
241 	HasTexture = false;
242 }
243 
244 
ReadXML(CXmlStreamRead * pXML,std::string * pRetMessage)245 X3dLoadResult xIndexedFaceSetNode::ReadXML(CXmlStreamRead* pXML, std::string* pRetMessage)
246 {
247 	Clear();
248 	std::string tmp;
249 	if (pXML->GetElAttS("coordIndex", &tmp)) CX3D_File::Str2Data(&tmp, &coordIndex);
250 	if (pXML->GetElAttS("colorIndex", &tmp)) CX3D_File::Str2Data(&tmp, &colorIndex);
251 	if (pXML->GetElAttS("texCoordIndex", &tmp)) CX3D_File::Str2Data(&tmp, &texCoordIndex);
252 
253 	colorPerVertex = true;
254 	if (pXML->GetElAttS("colorPerVertex", &tmp)){
255 		if (tmp == "false" || tmp == "False")	colorPerVertex = false; //default is true...
256 	}
257 
258 	if (pXML->OpenElement("Coordinate")){
259 		if (!pXML->GetElAttS("DEF", &CoordDef)) CoordDef="";
260 		if (!pXML->GetElAttS("USE", &CoordUse)) CoordUse="";
261 		if (pXML->GetElAttS("point", &tmp)) CX3D_File::Str2Data(&tmp, &Coordinate);
262 		pXML->CloseElement();
263 	}
264 
265 	if (pXML->OpenElement("TextureCoordinate")){
266 		if (pXML->GetElAttS("point", &tmp)) CX3D_File::Str2Data(&tmp, &TextureCoordinate);
267 		pXML->CloseElement();
268 	}
269 
270 	if (pXML->OpenElement("Color")){
271 		if (pXML->GetElAttS("color", &tmp)) CX3D_File::Str2Data(&tmp, &Color);
272 		pXML->CloseElement();
273 	}
274 	if (pXML->OpenElement("ColorRGBA")){
275 		if (pXML->GetElAttS("color", &tmp)) CX3D_File::Str2Data(&tmp, &ColorRGBA);
276 		pXML->CloseElement();
277 	}
278 	if (!FillDerivedInfo()) return XLR_NOSHAPE;
279 	return XLR_SUCCESS;
280 }
281 
FillDerivedInfo()282 bool xIndexedFaceSetNode::FillDerivedInfo() //fills in these calculated other parameters for easy access later.
283 {
284 	NumVertPerFacet = 0; //triangles or quads?
285 	if (coordIndex.size() > 3 && coordIndex[3] == -1) NumVertPerFacet = 3; //triangles!
286 	else if (coordIndex.size() > 4 && coordIndex[4] == -1) NumVertPerFacet = 4; //quads!
287 	else return false;
288 
289 
290 
291 	Colors = false;
292 	if (Color.size() != 0 || ColorRGBA.size() != 0) Colors = true;
293 
294 	ColByIndex = false;
295 	if (colorIndex.size() != 0) ColByIndex = true;
296 
297 	HasAlpha = false;
298 	if (ColorRGBA.size() != 0) HasAlpha = true;
299 
300 	HasTexture = false;
301 	if (texCoordIndex.size() != 0 && TextureCoordinate.size() != 0) HasTexture = true;
302 
303 	return true;
304 }
305 
GetNumTriangles()306 int xIndexedFaceSetNode::GetNumTriangles() //returns 2x number of quads if x3d has quads...
307 {
308 	if (NumVertPerFacet == 3) return coordIndex.size()/4;
309 	else if (NumVertPerFacet == 4) return coordIndex.size()*2/5;
310 	else return 0;
311 }
312 
GetCoordInd(int TriNum,int VertNum)313 int xIndexedFaceSetNode::GetCoordInd(int TriNum, int VertNum) //VertNum is 0, 1, or 2 (copy to GetTexCoordInd())
314 {
315 	if (NumVertPerFacet == 3) return coordIndex[TriNum*4+VertNum];
316 	else if (NumVertPerFacet == 4){
317 		if(TriNum%2 == 0) return coordIndex[(TriNum/2)*5+VertNum]; //first triangle of quad (012)
318 		else if (VertNum != 0) return coordIndex[(TriNum/2)*5+1+VertNum]; //second triangle of quad, last two verts ([0]23)
319 		else return coordIndex[(TriNum/2)*5]; //second triangle of quad, first vert (0[23])
320 	}
321 	else return -1;
322 }
323 
GetColorFace(int TriNum,ColChan Chan)324 double xIndexedFaceSetNode::GetColorFace(int TriNum, ColChan Chan) //triangle color (make sure Color && !colorPerVertex and HasAplpha if requesting alpha)
325 {
326 	if (NumVertPerFacet == 4) TriNum /=2;
327 
328 	if (ColByIndex){
329 		if (HasAlpha) return ColorRGBA[colorIndex[TriNum]*4+(int)Chan];
330 		else return Color[colorIndex[TriNum]*3+(int)Chan];
331 	}
332 	else {
333 		if (HasAlpha) return ColorRGBA[TriNum*4+(int)Chan];
334 		else return Color[TriNum*3+(int)Chan];
335 	}
336 }
337 
GetColorVert(int CoordNum,ColChan Chan)338 double xIndexedFaceSetNode::GetColorVert(int CoordNum, ColChan Chan) //vertex color (make sure Color && colorPerVertex and HasAlpha if requesting alpha)
339 {
340 	if (ColByIndex){
341 		//AMF does not support vertixes being mapped to different colors for different tirangles.
342 		if (HasAlpha) return ColorRGBA[0*4+(int)Chan];
343 		else return Color[0*3+(int)Chan];
344 	}
345 	else {
346 		if (HasAlpha) return ColorRGBA[CoordNum*4+(int)Chan];
347 		else return Color[CoordNum*3+(int)Chan];
348 	}
349 }
350 
GetColorVert(int TriNum,int VertNum,ColChan Chan)351 double xIndexedFaceSetNode::GetColorVert(int TriNum, int VertNum, ColChan Chan) //triangle color (make sure Color && colorPerVertex and HasAplpha if requesting alpha)
352 {
353 	if (ColByIndex){
354 		if (HasAlpha) return ColorRGBA[colorIndex[GetCoordInd(TriNum, VertNum)]*4+(int)Chan];
355 		else return Color[colorIndex[GetCoordInd(TriNum, VertNum)]*3+(int)Chan];
356 	}
357 	else {
358 		if (HasAlpha) return ColorRGBA[GetCoordInd(TriNum, VertNum)*4+(int)Chan];
359 		else return Color[GetCoordInd(TriNum, VertNum)*3+(int)Chan];
360 	}
361 }
362 
GetTexCoordInd(int TriNum,int VertNum)363 int xIndexedFaceSetNode::GetTexCoordInd(int TriNum, int VertNum) //copied fromG etCoordInd ()
364 {
365 	if (NumVertPerFacet == 3) return texCoordIndex[TriNum*4+VertNum];
366 	else if (NumVertPerFacet == 4){
367 		if(TriNum%2 == 0) return texCoordIndex[(TriNum/2)*5+VertNum]; //first triangle of quad (012)
368 		else if (VertNum != 0) return texCoordIndex[(TriNum/2)*5+1+VertNum]; //second triangle of quad, last two verts ([0]23)
369 		else return texCoordIndex[(TriNum/2)*5]; //second triangle of quad, first vert (0[23])
370 	}
371 	else return -1;
372 }
373 
ReadXML(CXmlStreamRead * pXML,CX3D_File * pX3dFile,std::string * pRetMessage)374 X3dLoadResult xShapeNode::ReadXML(CXmlStreamRead* pXML, CX3D_File* pX3dFile, std::string* pRetMessage)
375 {
376 	Clear();
377 	X3dLoadResult tmpRes;
378 	if (pXML->OpenElement("IndexedFaceSet")){
379 		tmpRes = xIndexedFaceSet.ReadXML(pXML, pRetMessage);
380 		if (tmpRes != XLR_SUCCESS) return tmpRes;
381 		pXML->CloseElement();
382 	}
383 	if (pXML->OpenElement("Appearance")){
384 		tmpRes = xAppearance.ReadXML(pXML, pX3dFile, pRetMessage);
385 		if (tmpRes != XLR_SUCCESS) return tmpRes;
386 		pXML->CloseElement();
387 	}
388 
389 	return XLR_SUCCESS;
390 }
391