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