Subversion Repositories gelsvn

Rev

Rev 594 | Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
396 jab 1
#include "CGLA/Vec4f.h"
2
#include "CGLA/Vec2f.h"
3
#include "CGLA/Vec3f.h"
4
#include "IDBufferWireFrameRenderer.h"
5
#include "glsl_shader.h"
6
#include <HMesh/Manifold.h>
7
#include <HMesh/FaceCirculator.h>
8
#include <GLGraphics/draw.h>
9
 
10
using namespace std;
11
using namespace CGLA;
12
using namespace GLGraphics;
13
using namespace HMesh;
14
 
15
namespace
16
{
17
string line_atten_frag = 
18
	"uniform int ATTEN_MODE;\n"
19
	"uniform float THICKNESS;\n"
20
	"uniform sampler2DRect IDMAP;\n"
21
	"uniform float TRANSITION;\n"
22
	"\n"
23
	"varying vec3 _id;\n"
24
	"varying vec2 p_2d_0;\n"
25
	"varying vec2 p_2d_1;\n"
26
	"\n"
27
	"const int LINEAR_ATTENUATION = 1;\n"
28
	"const int HERMITE_ATTENUATION = 2;\n"
29
	"\n"
30
	"const float HERMITE_COL_ATTEN_BIAS = 0.25;\n"
31
	"const float LINEAR_COL_ATTEN_BIAS = 0.4;\n"
32
	"const float HERMITE_ATTEN_BEGIN = 0.75;\n"
33
	"const float HERMITE_ATTEN_END = 1.0;\n"
34
	"\n"
35
	"void main(void)\n"
36
	"{\n"
37
	"	vec2 dir = (p_2d_1-p_2d_0);\n"
38
	"	float sq_len01 = dot(dir,dir);\n"
39
	"	vec2 v0 = (gl_FragCoord.xy)-p_2d_0;\n"
40
	"	vec2 v1 = (gl_FragCoord.xy)-p_2d_1;\n"
41
	"	float t = dot(dir, v0);\n"
42
	"	float d;\n"
43
	"	\n"
44
	"	if(t<=0.0)\n"
45
	"			d = length(v0);\n"
46
	"	else if(t>= sq_len01)\n"
47
	"			d = length(v1);\n"
48
	"	else\n"
49
	"			d = length(dir * t / sq_len01 - v0);\n"
50
	"	\n"
51
	"	vec3 iddiff = texture2DRect(IDMAP,gl_FragCoord.xy).xyz - _id;\n"
52
	" 	if(dot(iddiff, iddiff)<1e-6)\n"
53
	"		gl_FragDepth = 0.0;\n"
54
	"	else	\n"
55
	"		gl_FragDepth = gl_FragCoord.z;\n"
56
	"	\n"
57
	"	\n"
58
	"	float t1 = THICKNESS;\n"
59
	"	if(ATTEN_MODE == HERMITE_ATTENUATION)\n"
60
	"		{\n"
61
	"			float width_atten = smoothstep(HERMITE_ATTEN_END,HERMITE_ATTEN_BEGIN,\n"
62
	"																		 gl_FragCoord.z);\n"
63
	"			t1 *= width_atten;\n"
64
	"		}\n"
65
	"	else if(ATTEN_MODE == LINEAR_ATTENUATION)\n"
66
	"		{\n"
67
	"			float width_atten = 1.0-gl_FragCoord.z;\n"
68
	"			t1 *= width_atten;\n"
69
	"		}			\n"
70
	"	float t2 = t1+TRANSITION;\n"
71
	"\n"
72
	"	float I;\n"
73
	"	if(d<t1) \n"
74
	"			I = 1.0;\n"
75
	"	else\n"
76
	"	{\n"
77
	"			float x = (d-t1);\n"
78
	"			I = exp2(-x*x*2);\n"
79
	"	}\n"
80
	" 	gl_FragColor.rgb = gl_Color.rgb;\n"
81
	"	gl_FragColor.a = I;\n"
82
	"}\n";
83
 
84
	string line_frag = 
85
	"uniform sampler2DRect IDMAP;\n"
86
	"uniform float TRANSITION;\n"
87
	"	\n"
88
	"varying vec3 _id;\n"
89
	"varying vec2 p_2d_0;\n"
90
	"varying vec2 p_2d_1;\n"
91
	"\n"
92
	"void main(void)\n"
93
	"{\n"
94
	"	vec2 dir = (p_2d_1-p_2d_0);\n"
95
	"	float sq_len01 = dot(dir,dir);\n"
96
	"	vec2 v0 = gl_FragCoord.xy-p_2d_0;\n"
97
	"	vec2 v1 = gl_FragCoord.xy-p_2d_1;\n"
98
	"	float t = dot(dir, v0);\n"
99
	"	float sq_d;\n"
100
	"	\n"
101
	"	if(t<=0.0)\n"
102
	"		sq_d = dot(v0,v0);\n"
103
	"	else if(t>= sq_len01)\n"
104
	"		sq_d = dot(v1,v1);\n"
105
	"	else\n"
106
	"		{\n"
107
	"			float area = (v0.x*v1.y - v0.y * v1.x);\n"
108
	"			sq_d = area*area/sq_len01;\n"
109
	"		}\n"
110
	"\n"
111
	"	vec3 iddiff = texture2DRect(IDMAP,gl_FragCoord.xy).xyz -_id;\n"
112
	"	if(dot(iddiff, iddiff)<1e-6)\n"
113
	"		gl_FragDepth = 0.0;\n"
114
	" 	else	\n"
115
	" 		gl_FragDepth = gl_FragCoord.z;\n"
116
	"	\n"
117
	"	gl_FragColor.a = exp2(-sq_d*2.0);\n"
118
	"	gl_FragColor.rgb = gl_Color.rgb;\n"
119
	"}\n";
120
 
121
	string line_vert = 
122
	"uniform float THICKNESS;\n"
123
	"uniform vec2 WIN_SCALE;\n"
124
	"uniform vec2 INV_WIN_SCALE;\n"
125
	"uniform float TRANSITION;\n"
126
	"\n"
127
	"attribute vec4 id;\n"
128
	"attribute vec4 opp_vertex;\n"
129
	"attribute vec2 displace;\n"
130
	"\n"
131
	"varying vec3 _id;\n"
132
	"varying vec2 p_2d_0;\n"
133
	"varying vec2 p_2d_1;\n"
134
	"\n"
135
	"\n"
136
	"void main(void)\n"
137
	"{\n"
138
	"	vec4 p0 = gl_ModelViewProjectionMatrix * opp_vertex;\n"
139
	"	float w0 = p0.w; \n"
140
	"\n"
141
	"	vec4 p1 = gl_ModelViewProjectionMatrix * gl_Vertex;\n"
142
	"	float w1 = p1.w;\n"
143
	"		\n"
144
	"	p_2d_0 = (p0.xy/w0+vec2(1,1)) * WIN_SCALE;\n"
145
	"	p_2d_1 = (p1.xy/w1+vec2(1,1)) * WIN_SCALE;\n"
146
	"	\n"
147
	"	vec2 a = normalize(p_2d_1 - p_2d_0);\n"
148
	"	vec2 a_h = vec2(-a.y, a.x);\n"
149
	"	vec2 scale = (TRANSITION+THICKNESS)*INV_WIN_SCALE;\n"
150
	"\n"
151
	"	gl_Position = (displace.x>0.0) ? p1 : p0;\n"
152
	"	vec2 offset = a * displace.x + a_h * displace.y;\n"
153
	"	gl_Position.xy += scale * gl_Position.w * offset;\n"
154
	"\n"
155
	"	_id = id.rgb;\n"
156
	"	gl_FrontColor = gl_Color;\n"
157
	"	gl_FrontSecondaryColor = gl_SecondaryColor;\n"
158
	"}\n";
159
 
160
}
161
 
162
namespace GLGraphics
163
{
164
	IDBufferWireframeRenderer::~IDBufferWireframeRenderer()
165
	{
166
		glDeleteShader(vs);	
167
		glDeleteShader(fs);
168
		glDeleteProgram(line_prog);
169
		glDeleteTextures(1, &idmap);
170
		glDeleteBuffers(1, &vertex_buffername);
171
		glDeleteBuffers(1, &colors_buffername);
172
 
173
		glDeleteBuffers(1, &line_id_attrib);
174
		glDeleteBuffers(1, &line_vertex_pos);
175
		glDeleteBuffers(1, &line_disp_attrib);			
176
		glDeleteBuffers(1, &line_opp_attrib);
177
	}
178
 
179
	IDBufferWireframeRenderer::IDBufferWireframeRenderer(int _XSZ, int _YSZ,
180
										 HMesh::Manifold& _mesh, 
181
										 float _thickness, 
182
										 float _transition, 
183
										 int atten_mode): 
184
	mesh(_mesh), XSZ(_XSZ), YSZ(_YSZ), thickness(_thickness), transition(_transition)
185
	{
186
 
187
		if(atten_mode == 0 && thickness == 0.0)
188
		{
189
			vs = create_glsl_shader(GL_VERTEX_SHADER, line_vert);
190
			fs = create_glsl_shader(GL_FRAGMENT_SHADER, line_frag);
191
		}
192
		else
193
		{
194
			vs = create_glsl_shader(GL_VERTEX_SHADER, line_vert);
195
			fs = create_glsl_shader(GL_FRAGMENT_SHADER, line_atten_frag);
196
		}
197
 
198
		line_prog = glCreateProgram();
199
		glAttachShader(line_prog, vs);
200
		glAttachShader(line_prog, fs);
201
		glLinkProgram(line_prog);
202
		glUseProgram(line_prog);
203
 
204
		glUniform1f(glGetUniformLocation(line_prog,"THICKNESS"), 
205
					thickness);
206
		glUniform1f(glGetUniformLocation(line_prog,"TRANSITION"), 
207
					transition);
208
		glUniform1i(glGetUniformLocation(line_prog,"ATTEN_MODE"),
209
					atten_mode);
210
		glUniform2f(glGetUniformLocation(line_prog,"WIN_SCALE"), 
211
					XSZ/2.0, YSZ/2.0);
212
		glUniform2f(glGetUniformLocation(line_prog,"INV_WIN_SCALE"), 
213
					2.0/XSZ, 2.0/YSZ);
214
		glUniform1i(glGetUniformLocation(line_prog,"IDMAP"), 0);
215
 
216
		id_attrib = glGetAttribLocation(line_prog, "id");
217
		popp_attrib = glGetAttribLocation(line_prog, "opp_vertex");
218
		disp_attrib = glGetAttribLocation(line_prog, "displace");
219
		glUseProgram(0);
220
 
221
		glGenTextures(1, &idmap);
222
		glBindTexture(GL_TEXTURE_RECTANGLE_ARB, idmap);
223
		glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
224
		glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
225
		glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_WRAP_S,GL_CLAMP);
226
		glTexParameteri(GL_TEXTURE_RECTANGLE_ARB,GL_TEXTURE_WRAP_T,GL_CLAMP);
227
 
228
		//create the texture
229
		glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_RGBA8, XSZ, YSZ,
230
					 0, GL_RGB, GL_FLOAT, 0);
231
 
232
 
233
		glGenBuffers(1, &vertex_buffername);
234
		glGenBuffers(1, &colors_buffername);
235
 
236
		glGenBuffers(1, &line_id_attrib);
237
		glGenBuffers(1, &line_vertex_pos);
238
		glGenBuffers(1, &line_disp_attrib);
239
		glGenBuffers(1, &line_opp_attrib);
240
 
241
		triangles = mesh.no_faces();
242
		vector<Vec3f> verts;
243
		vector<Vec3f> cols;
244
		int i=0;
245
		for(FaceIter f = mesh.faces_begin(); f != mesh.faces_end(); ++f, ++i)
246
		{
247
			Vec3uc idv(id_get(i));
248
			Vec3f idvec(idv[0]/255.0, idv[1]/255.0, idv[2]/255.0);
249
			FaceCirculator fc(f);
250
			while(!fc.end())
251
			{
252
				cols.push_back(idvec);
253
				verts.push_back(fc.get_vertex()->pos);
254
				++fc;
255
			}
256
        }
257
		glBindBuffer(GL_ARRAY_BUFFER, vertex_buffername);
258
		glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*verts.size(),
259
					 (float*)&verts[0],GL_STATIC_DRAW);
260
 
261
 
262
 
263
		glBindBuffer(GL_ARRAY_BUFFER, colors_buffername);
264
		glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*cols.size(),
265
					 (float*)&cols[0],GL_STATIC_DRAW);
266
 
267
 
268
		vector<Vec3f> line_ids;
269
		vector<Vec3f> vertex_positions;
270
		vector<Vec2f> displacements;
271
		vector<Vec3f> opposite_positions;
272
 
273
 
274
		quads = 0;
275
		mesh.enumerate_faces();
276
		for(FaceIter f=mesh.faces_begin(); f!=mesh.faces_end(); ++f)
277
		{
278
			for(FaceCirculator fc(f); !fc.end(); ++fc)
279
			{
280
				++quads;
281
				HalfEdgeIter h = fc.get_halfedge();
282
				Vec3uc idv(id_get(h->face->touched));
283
				Vec3f v0 = h->vert->pos;
284
				Vec3f v1 = h->opp->vert->pos;
285
				Vec3f idvec(idv[0]/255.0, idv[1]/255.0, idv[2]/255.0);
286
 
287
				line_ids.push_back(idvec);
288
				opposite_positions.push_back(v0);
289
				displacements.push_back(Vec2f(1,-1));
290
				vertex_positions.push_back(v1);
291
 
292
 
293
				line_ids.push_back(idvec);
294
				opposite_positions.push_back(v0);
295
				displacements.push_back(Vec2f(1, 1));
296
				vertex_positions.push_back(v1);
297
 
298
 
299
				line_ids.push_back(idvec);
300
				opposite_positions.push_back(v0);
301
				displacements.push_back(Vec2f(-1,1));
302
				vertex_positions.push_back(v1);
303
 
304
 
305
				line_ids.push_back(idvec);
306
				opposite_positions.push_back(v0);
307
				displacements.push_back(Vec2f(-1,-1));
308
				vertex_positions.push_back(v1);
309
			}
310
		}
311
 
312
		glBindBuffer(GL_ARRAY_BUFFER, line_id_attrib);
313
 
314
		glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*line_ids.size(),
315
					 (float*)&line_ids[0],GL_STATIC_DRAW);
316
 
317
		glBindBuffer(GL_ARRAY_BUFFER, line_opp_attrib);
318
		glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*opposite_positions.size(),
319
					 (float*)&opposite_positions[0],GL_STATIC_DRAW);
320
 
321
		glBindBuffer(GL_ARRAY_BUFFER, line_disp_attrib);
322
		glBufferData(GL_ARRAY_BUFFER, sizeof(float)*2*displacements.size(),
323
					 (float*)&displacements[0],GL_STATIC_DRAW);
324
 
325
		glBindBuffer(GL_ARRAY_BUFFER, line_vertex_pos);
326
		glBufferData(GL_ARRAY_BUFFER, sizeof(float)*3*vertex_positions.size(),
327
					 (float*)&vertex_positions[0],GL_STATIC_DRAW);
328
 
329
 
330
 
331
	}
332
 
333
 
334
	void IDBufferWireframeRenderer::draw(const Vec3f& color, const Vec3f& clear_color)
335
	{
336
		// push those attributes we change.
337
		glPushAttrib(GL_COLOR_BUFFER_BIT|
338
					 GL_CURRENT_BIT|
339
					 GL_TRANSFORM_BIT|
340
					 GL_DEPTH_BUFFER_BIT);
341
 
342
		// Store information about whether we use lighting
343
		GLboolean lights_on;
344
		glGetBooleanv(GL_LIGHTING, &lights_on);
345
 
346
		// Store color information
347
		Vec4f current_color;
348
		glGetFloatv(GL_CURRENT_COLOR, &current_color[0]);
349
 
350
		// Store the current draw buffer 
351
		GLint _currentDrawbuf;
352
		glGetIntegerv(GL_DRAW_BUFFER, &_currentDrawbuf); 
353
 
354
		// Enable depth testing
355
		glEnable(GL_DEPTH_TEST);
356
 
357
		// ------------------------------
358
		// Pass 1: Draw the ID map
359
		// Each polygon has a unique ID which is coded as a colour and drawn
360
		// into the ID buffer
361
		glDisable(GL_LIGHTING);
362
		glClearColor(0,0,0,0);
363
		glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
364
 
365
		glBindBuffer(GL_ARRAY_BUFFER, vertex_buffername);
366
		glVertexPointer(3,GL_FLOAT,0,static_cast<char*>(0));
367
		glEnableClientState(GL_VERTEX_ARRAY);
368
 
369
		glBindBuffer(GL_ARRAY_BUFFER, colors_buffername);
370
		glColorPointer(3,GL_FLOAT,0,static_cast<char*>(0));
371
		glEnableClientState(GL_COLOR_ARRAY);
372
 
373
		int vertex_idx = 0;
374
		for(FaceIter f=mesh.faces_begin(); f != mesh.faces_end(); ++f)
375
		{
376
			FaceCirculator fc(f);
377
			glBegin(GL_POLYGON);
378
			while(!fc.end())
379
			{
380
				glArrayElement(vertex_idx++);
381
				++fc;
382
			}
383
			glEnd();
384
		}
385
		glFinish();
386
		glDisableClientState(GL_COLOR_ARRAY);
387
		glDisableClientState(GL_VERTEX_ARRAY);
388
 
389
		glBindTexture(GL_TEXTURE_RECTANGLE_ARB, idmap);
390
		glCopyTexSubImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, 0,0,0,0,XSZ, YSZ);
391
 
392
		// Clear color buffer but retain depth buffer.
393
		glClear(GL_COLOR_BUFFER_BIT);
394
 
395
		// Enable blending for all subsequent passes
396
		glEnable(GL_BLEND);
397
 
398
		// ------------------------------
399
		// PASS 3: Draw lines using ID map texture.
400
		// For each polygon, all edges are drawn as prefiltered
401
		// lines. A given fragment belonging to a line will be written
402
		// to the framebuffer if either of the following three conditions are met
403
		// - its ID matches the contents of the ID  buffer for the corresponding 
404
		// pixel (in the ID buffer)
405
		// - The ID of the corresponding pixel is 0 - meaning we are outside.
406
		// - The depth test is passed.
407
		// The final condition ensures that line pixels which are on an interior
408
		// contour will be drawn.
409
		//
410
		// If the line fragment is written into the framebuffer - two values are
411
		// actually written: The colour of the line and an alpha value which
412
		// corresponds to the value of the filter function.
413
		//
414
		// During this pass, blending is enabled and the blending equation is set
415
		// to max. Since the alpha values are the values of the filter (large if 
416
		// close to line) this means that we replace a pixel value with an 
417
		// incoming fragment if the incoming fragment is closer to a line
418
		// than the pixel value.
419
		//
420
		// The depth values are not changed during this pass.
421
		glEnable(GL_TEXTURE_RECTANGLE_ARB);
422
		glBindTexture(GL_TEXTURE_RECTANGLE_ARB, idmap);
423
		glDepthMask(GL_FALSE);
424
		glBlendEquation(GL_MAX);
425
		glUseProgram(line_prog);
426
		glColor3fv(color.get());
427
		glSecondaryColor3fv(clear_color.get());
428
 
429
		float lw;
430
		glGetFloatv(GL_LINE_WIDTH, &lw);
431
		glLineWidth(ceil(2.0*(thickness+transition)));
432
 
433
 
434
		glBindBuffer(GL_ARRAY_BUFFER, line_id_attrib);
435
		glVertexAttribPointer(id_attrib, 3,GL_FLOAT,GL_FALSE,0,static_cast<char*>(0));
436
		glEnableVertexAttribArray(id_attrib);
437
 
438
 
439
		glBindBuffer(GL_ARRAY_BUFFER, line_disp_attrib);
440
		glVertexAttribPointer(disp_attrib, 2,GL_FLOAT,GL_FALSE,0,static_cast<char*>(0));
441
		glEnableVertexAttribArray(disp_attrib);
442
 
443
 
444
		glBindBuffer(GL_ARRAY_BUFFER, line_opp_attrib);
445
		glVertexAttribPointer(popp_attrib, 3,GL_FLOAT,GL_FALSE,0,static_cast<char*>(0));
446
		glEnableVertexAttribArray(popp_attrib);
447
 
448
 
449
		glBindBuffer(GL_ARRAY_BUFFER, line_vertex_pos);
450
		glVertexPointer(3,GL_FLOAT,0,static_cast<char*>(0));
451
		glEnableClientState(GL_VERTEX_ARRAY);
452
 
453
		glDrawArrays(GL_QUADS, 0, quads*4);
454
 
455
		glDisableVertexAttribArray(id_attrib);
456
		glDisableVertexAttribArray(disp_attrib);
457
		glDisableVertexAttribArray(popp_attrib);
458
		glDisableClientState(GL_VERTEX_ARRAY);
459
 
460
 
461
		glLineWidth(lw);
462
 
463
		glUseProgram(0);
464
		glDisable(GL_TEXTURE_RECTANGLE_ARB);
465
		glDepthMask(GL_TRUE);
466
 
467
		// ------------------------------
468
		// Pass 4: Draw with shading
469
		// In this pass we draw the shaded model. At this point, the framebuffer
470
		// contains alpha values and line colours and also depth values.
471
		//
472
		// The depth test is set to `equal' and the shaded fragments from the 
473
		// filled polygons are combined with the line colours using the alpha 
474
		// values already stored in the frame buffer.
475
		//
476
		// The framebuffer lines along the contour also need to be blended. 
477
		// Hence, a screen filling quad is drawn. 
478
		glDepthFunc(GL_LEQUAL);
479
		glBlendEquation(GL_FUNC_ADD);
480
		glBlendFuncSeparate(GL_ONE_MINUS_DST_ALPHA, GL_DST_ALPHA, 
481
							GL_ZERO, GL_ONE);
482
		if(lights_on)
483
			glEnable(GL_LIGHTING);
484
		else
485
			glColor4fv(current_color.get());
486
 
487
		glBindBuffer(GL_ARRAY_BUFFER, vertex_buffername);
488
		glVertexPointer(3,GL_FLOAT,0,static_cast<char*>(0));
489
		glEnableClientState(GL_VERTEX_ARRAY);
490
 
491
 
492
		vertex_idx = 0;
493
		for(FaceIter f=mesh.faces_begin(); f != mesh.faces_end(); ++f)
494
		{
495
			FaceCirculator fc(f);
496
			glBegin(GL_POLYGON);
497
			glNormal3fv(normal(f).get());
498
			while(!fc.end())
499
			{
500
				glArrayElement(vertex_idx++);
501
				++fc;
502
			}
503
			glEnd();
504
		}
505
 
506
		glDisableClientState(GL_VERTEX_ARRAY);
507
 
508
 
509
		glDisable(GL_LIGHTING);
510
 
511
 
512
		double mvmat[16];
513
		glGetDoublev(GL_MODELVIEW_MATRIX, mvmat);
514
		double prjmat[16];
515
		glGetDoublev(GL_PROJECTION_MATRIX, prjmat);
516
		glMatrixMode(GL_PROJECTION);
517
		glLoadIdentity();
518
		glMatrixMode(GL_MODELVIEW);
519
		glLoadIdentity();
520
		glColor3fv(clear_color.get());
521
		glBegin(GL_QUADS);
522
		glVertex3f(-1,-1,1);
523
		glVertex3f( 1,-1,1);
524
		glVertex3f( 1, 1,1);
525
		glVertex3f(-1, 1,1);
526
		glEnd();
527
 
528
 
529
 
530
		glMatrixMode(GL_PROJECTION);
531
		glLoadMatrixd(prjmat);
532
		glMatrixMode(GL_MODELVIEW);
533
		glLoadMatrixd(mvmat);
534
 
535
		glPopAttrib();
536
		if(lights_on)
537
			glEnable(GL_LIGHTING);
538
 
539
		//			cout << gluErrorString(glGetError()) << endl;
540
	}
541
}