Advertisement

04.19.2008 at 09:18PM PDT, ID: 23337366
[x]
Attachment Details
[x]
The Solution Rating System

With so many solutions, how can you tell which solutions are most likely to help you and which ones are not? To provide you with a tool to use, we rate our solutions based on various elements that most accurately determine if a solution is a quality solution. To explain what factors affect the solution rating, here are the elements we take into consideration when formulating our solution rating.

  • The Grade of the Solution
  • The Zone Rank of the Expert Providing the Solution
  • The Number of Author and Expert Comments
  • The Number of Experts Contributing
  • The Feedback of the Community

Your Input Matters
Because of the way the system is set up, the most important variable in this equation is you. As a member of Experts Exchange, you are able to cast your vote on the quality of the solutions in regard to how complete, accurate, helpful and easy to understand each solution is. When you provide your feedback, each rating is adjusted accordingly. So, if you see a solution that has a poor rating that you think is a good solution, let us know by rating it. As you do, the rating will be adjusted and will become more accurate for other members of our site.

If you have any suggestions that you would like to make for our rating system, please ask a question in the Suggestions Zone of Community Support.

Thank you!

Ray tracing algorithm as a stack?

Tags: C
Hi guys!

I have an interesting question. It's related to Computer Graphics (ray tracing), but you need not be an expert in the field.

The core of the ray tracing algorithm is in the snippet below. It's intuitive and well commented. One ray is spawned per pixel location, returning a color to paint on the screen. It needs to be implemented it in CUDA. For those who aren't familiar, think of CUDA as an extension to C, which allows you to write and run general-purpose code on a modern GPU.

The problem is that CUDA doesn't support recursion, which is at the core of the algorithm. However, some people have suggested turning the recursion mechanism into a stack mechanism in memory instead. Unfortunately, this is the point where I feel completely dumb-founded! :(

Do you guys have any thoughts on how to do this?
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
color spawnRay(ray, depth) {
       
       // Code to determine if ray hit an object
       // ...
 
       // If ray didn't intersect anything, just return background color        
       if (!intersection) {
           return background_color;
   
       // There has been an intersection      
       } else {
           
           // MAX_DEPTH sets a limit to the recursion depth (10 is fine)
           if (depth < MAX_DEPTH) {
               
               //Spawn reflection ray if intersected object hit is reflective
               if (kr > 0) {
                   retcolor += spawnRay(reflect_ray, depth+1);
               }
               
               //Spawn transmission ray if intersected object is transparent                
               if (kt > 0) {
                   retcolor += spawnRay(trans_ray, depth+1);
               }
           }
       }
       return retcolor;
   }
Start your free trial to view this solution
Question Stats
Zone: Programming
Question Asked By: Koraxen
Solution Provided By: ozo
Participating Experts: 4
Solution Grade: A
Views: 0
Translate:
Loading Advertisement...
04.19.2008 at 10:38PM PDT, ID: 21395127

Rank: Sage

All comments and solutions are available to Premium Service Members only.

Start your 7-day free trial and see for yourself why Experts Exchange is the easiest and most proven technology resource in the world. Get Started

Already a member? Login to view this solution.

 
04.19.2008 at 10:53PM PDT, ID: 21395147

Rank: Wizard

All comments and solutions are available to Premium Service Members only.

Start your 7-day free trial and see for yourself why Experts Exchange is the easiest and most proven technology resource in the world. Get Started

Already a member? Login to view this solution.

 
04.20.2008 at 01:59AM PDT, ID: 21395397

All comments and solutions are available to Premium Service Members only.

Start your 7-day free trial and see for yourself why Experts Exchange is the easiest and most proven technology resource in the world. Get Started

Already a member? Login to view this solution.

 
04.20.2008 at 02:08AM PDT, ID: 21395417

All comments and solutions are available to Premium Service Members only.

Start your 7-day free trial and see for yourself why Experts Exchange is the easiest and most proven technology resource in the world. Get Started

Already a member? Login to view this solution.

 
04.20.2008 at 06:59AM PDT, ID: 21396015

Rank: Guru

All comments and solutions are available to Premium Service Members only.

Start your 7-day free trial and see for yourself why Experts Exchange is the easiest and most proven technology resource in the world. Get Started

Already a member? Login to view this solution.

 
04.20.2008 at 07:40AM PDT, ID: 21396142

Rank: Guru

All comments and solutions are available to Premium Service Members only.

Start your 7-day free trial and see for yourself why Experts Exchange is the easiest and most proven technology resource in the world. Get Started

Already a member? Login to view this solution.

 
04.20.2008 at 07:51AM PDT, ID: 21396207

Rank: Guru

All comments and solutions are available to Premium Service Members only.

Start your 7-day free trial and see for yourself why Experts Exchange is the easiest and most proven technology resource in the world. Get Started

Already a member? Login to view this solution.

 
04.21.2008 at 10:48AM PDT, ID: 21404256

All comments and solutions are available to Premium Service Members only.

Start your 7-day free trial and see for yourself why Experts Exchange is the easiest and most proven technology resource in the world. Get Started

Already a member? Login to view this solution.

 
04.21.2008 at 11:31AM PDT, ID: 21404632

Rank: Guru

All comments and solutions are available to Premium Service Members only.

Start your 7-day free trial and see for yourself why Experts Exchange is the easiest and most proven technology resource in the world. Get Started

Already a member? Login to view this solution.

 
04.23.2008 at 08:29AM PDT, ID: 21421773

All comments and solutions are available to Premium Service Members only.

Start your 7-day free trial and see for yourself why Experts Exchange is the easiest and most proven technology resource in the world. Get Started

Already a member? Login to view this solution.

 
 
Loading Advertisement...
Microsoft
  • Internet Protocols
  • Applications
  • Development
  • OS
  • Hardware
  • Windows Security
Apple
  • Operating Systems
  • Hardware
  • Programming
  • Networking
  • Software
Internet
  • Search Engines
  • File Sharing
  • WebTrends / Stats
  • Spy / Ad Blockers
  • Web Browsers
  • New Net Users
  • Web Development
  • Chat / IM
  • Anti Spam
  • Web Servers
  • Anti-Virus
  • Email Clients
Gamers
  • Tips
  • Online / MMORPG
  • Puzzle
  • Emulators
  • Action / Adventure
  • Role Playing
  • Consoles
  • Game Programming
  • Strategy
  • Sports
  • Misc
  • Computer Games
Digital Living
  • Hardware
  • Automotive
  • New Net Users
  • New Users
  • Software
  • Digital Music
  • Gaming World
  • Home Security
  • Apple
  • Networking Hardware
Virus & Spyware
  • Vulnerabilities
  • IDS
  • Encryption
  • Anti-Virus
  • Operating Systems Security
  • Software Firewalls
  • WebApplications
  • Cell Phones
  • Operating Systems
  • Internet
  • Hardware Firewalls
Hardware
  • Displays / Monitors
  • Handhelds / PDAs
  • Components
  • Peripherals
  • Laptops/Notebooks
  • Servers
  • Misc
  • Apple
  • Embedded Hardware
  • Networking Hardware
  • Storage
  • Desktops
  • New Users
Software
  • System Utilities
  • Industry Specific
  • Network Management
  • Photos / Graphics
  • Page Layout
  • VMware
  • Misc
  • Web Development
  • OS
  • CYGWIN
  • Voice Recognition
  • Virtualization
  • Message Queue
  • Quality Assurance
  • Security
  • Firewalls
  • MultiMedia Applications
  • Development
  • Database
  • Office / Productivity
  • Business Management
  • OS/2 Apps
  • Server Software
  • Internet / Email
ITPro
  • OS
  • Storage
  • Encryption
  • Operating Systems Security
  • Apple Hardware
  • Laptops & Notebooks
  • Servers
  • Networking Hardware
  • Peripherals
  • Devices
  • Displays / Monitors
  • WebTrends / Stats
  • Search Engines
  • Firewalls
  • Web Computing
  • WebApplications
  • IDS
  • Vulnerabilities
  • Email Clients
  • File Sharing
  • Spy / Ad Blockers
  • Web Browsers
  • Web Servers
  • Networking
  • Anti-Virus
  • Consulting
  • Chat / IM
  • Anti Spam
Developer
  • Web Servers
  • Web Browsers
  • Game Programming
  • Dev Tools
  • Industry Specific
  • Office / Productivity
  • Database
  • CYGWIN
  • Web Development
  • Search Engines
  • File Sharing
  • WebTrends / Stats
  • Programming
  • Content Management
  • Application Servers
  • Protocols
Storage
  • Removable Backup Media
  • Storage Technology
  • Servers
  • Grid
  • Remote Access
  • Backup / Restore
  • Misc
  • Hard Drives
OS
  • Miscellaneous
  • Security
  • Development
  • Linux
  • VMware
  • MainFrame OS
  • Unix
  • Apple
  • OS / 2
  • AS / 400
  • BeOS
  • Microsoft
  • VMS / OpenVMS
Database
  • Oracle
  • Miscellaneous
  • MySQL
  • Software
  • Sybase
  • Contact Management
  • PostgreSQL
  • Data Manipulation
  • Clarion
  • InterSystems Cache
  • Siebel
  • MUMPS
  • OLAP
  • SQLBase
  • SAS
  • GIS & GPS
  • 4GL
  • Berkeley DB
  • DB2
  • Informix
  • Interbase / Firebird
  • FoxPro
  • Reporting
  • LDAP
  • Filemaker Pro
  • MS SQL Server
  • dBase
  • MS Access
Security
  • Misc
  • Web Browsers
  • Software Firewalls
  • Operating Systems Security
  • File Sharing
  • Spy / Ad Blockers
  • Vulnerabilities
  • WebApplications
  • IDS
  • Anti-Virus
  • Encryption
  • Anti Spam
  • Email Clients
  • VPN
  • Chat / IM
Programming
  • Editors IDEs
  • Installation
  • Handhelds / PDAs
  • Multimedia Programming
  • System / Kernel
  • Automation
  • Algorithms
  • Game
  • Signal Processing
  • Project Management
  • Open Source
  • Database
  • Misc
  • Languages
  • Processor Platforms
  • Theory
Web Development
  • Scripting
  • Blogs
  • Web Servers
  • Software
  • Search Engines
  • Web Graphics
  • Web Services
  • Images
  • Internet Marketing
  • Images and Photos
  • Components
  • Document Imaging
  • Web Languages/Standards
  • Illustration
  • WebApplications
  • Fonts
  • WebTrends / Stats
  • Authoring
  • Digital Camera Software
  • Miscellaneous
Networking
  • Protocols
  • Apple Networking
  • Network Management
  • Message Queue
  • Application Servers
  • Content Management
  • File Servers
  • Email Servers
  • Misc
  • Java Editors & IDEs
  • Wireless
  • Networking Hardware
  • Backup / Restore
  • System Utilities
  • ISPs & Hosting
  • Web Servers
  • Storage Technology
  • Removable Backup Media
  • Servers
  • Web Computing
  • Broadband
  • Grid
  • OS / 2
  • Novell Netware
  • Unix Networking
  • Windows Networking
  • Security
  • Telecommunications
  • Operating Systems
  • Linux Networking
Other
  • Lounge
  • Business Travel
  • Community Support
  • New Net Users
  • Philosophy / Religion
  • Math / Science
  • Miscellaneous
  • URLs
  • Expert Lounge
  • Politics
  • Puzzles / Riddles
  • Automotive
Community Support
  • Suggestions
  • New to EE
  • New Topics
  • CleanUp
  • Announcements
  • General
  • Feedback
  • Input
  • EE Bugs
 
04.19.2008 at 10:38PM PDT, ID: 21395127

Rank: Sage

>However, some people have suggested turning the recursion mechanism into a
>stack mechanism in memory instead.

What this means is that you do the recursion management yourself which is going to be cumbersome and should be your last option. Usually a better approach is to use an iterative version of the algorithm if possible.

In any case if you expect deep recursion to happen you would run out of memory (and your kernel will crash) very soon. The reason CUDA does not permit recursion is because of hardware limitations. It depends on registers for speed-up and has limited local memory.

If you insist on doing stack management yourself, then you can possibly use something like
http://www.c6software.com/articles/UsingAStack.aspx
http://haacked.com/archive/2007/03/04/Replacing_Recursion_With_a_Stack.aspx

But again I don't recollect CUDA supporting C++ STL, so it might be quite a bit of work!! Much better easier option would be to implement an iterative version.

Good Luck!!
 
04.19.2008 at 10:53PM PDT, ID: 21395147

Rank: Wizard

color spawnRay(ray) {
  depth=1;
  stack[depth]=ray;
  while( depth ){
    ray=stack[depth];
    depth = depth-1;
  // Code to determine if ray hit an object
  // ...

  // If ray didn't intersect anything, just return background color
  if (!intersection) {
    retcolor += background_color;
    // There has been an intersection
  } else {
    // MAX_DEPTH sets a limit to the recursion depth (10 is fine)
    if (depth < MAX_DEPTH) {

      //Spawn transmission ray if intersected object is transparent
      if (kt > 0) {
        depth=depth+1;
        raystack[depth]=ray;
      }

      //Spawn reflection ray if intersected object hit is reflective
      if (kr > 0) {
        depth=depth+1;
        raystack[depth]=ray;
      }

    }
  }

  return retcolor;
}
Accepted Solution
 
04.20.2008 at 01:59AM PDT, ID: 21395397
When you say "doesn't support recursion" how does it stop it?

Recursion is a technique, not (erm, thinking of the right word) a method or something like that which can be unsupported.

If you can write a function which can return early (bailout), then you can write a recursive function.

The only way I can think of it not working is if somehow the runtime engine doesn't have a stack. But this would be VERY VERY odd.

Simple recursion...

function factorial(value) {
 if (1 == value) {
  return 1;
 } else {
  return value * factorial(value - 1);
 }
}

This is recursion. It is just normal function calling.

 
04.20.2008 at 02:08AM PDT, ID: 21395417
And I've just read http://forums.nvidia.com/index.php?showtopic=65244.

So, no stack.

Building your own stack is technically possible, but the overhead is not insignificant.

If you have limits on memory, then running out of stack space is going to happen pretty quickly.
 
04.20.2008 at 06:59AM PDT, ID: 21396015

Rank: Guru

I havent compiled or tested this so forgive typos etc. but this is the essential functionality you need I think.

Paul
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
/*
	Unrolled recursive function.
	
	At each level in the tree we must evaluate the reflection ray and the transmission ray.
	
	When the intersection is calculated, both tRay and rRay are calculated along with kr and kt.
	
	To ensure that intersection is called minimally we must therefore have at least 4 stacks. 
	The tRay, the rRay, kt and kr.
	
	We also need a state that tells us which ray we have processed (ray, t or r). This must also be stacked so
	that makes 5 stacks. 
*/
 
// Two stages at each level.
Ray tStack[MAX];
Ray rStack[MAX];
int ktStack[MAX];
int krStack[MAX];
// Next ray to process at this level (tRay or rRay).
#define STATE_T_RAY (0)
#define STATE_R_RAY (1)
#define STATE_COMPLETE (2)
int trStack[MAX];
int d; // Depth.
 
color traceRay(ray) {
	// Start at depth 0.
	d = 0;
	// Keep walking the tree until we get back to depth 0.
	do {
		// Trace the ray.
		if ( intersection(ray) ) {
			// Keep the results of the intersection call at the current level.
			tStack[d] = trans_ray;
			ktStack[d] = kt;
			rStack[d] = reflect_ray;
			krStack[d] = kr;
			// Next state at this level is to check the t ray.
			trStack[d] = STATE_T_RAY;
		} else {
			// No intersection, add background colour
			retcolor += background_color;
			//  and were done at this level.
			d -= 1;
		}
		
		// Which ray should we work on for the next iteration?
 
		// Initially nothing.
		ray = null;
		// Walk the stack till we're done or we know which ray to do next.
		while ( ray == null && d >= 0 ) {
			// Check the state.
			switch ( trStack[d] ) {
				case STATE_T_RAY:
					// Was there one?
					if ( d < MAX && ktStack[d] > 0 ) {
						// Do the transmission ray.
						ray = tStack[d];
						// Step my state.
						trStack[d] += 1;
						// Step up.
						trStack[++d] = STATE_T_RAY;
					}
					break;
					
				case STATE_R_RAY:
					// Was there one?
					if ( d < MAX && krStack[d] > 0 ) {
						// Do the reflection ray.
						ray = rStack[d];
						// Step my state.
						trStack[d] += 1;
						// Step up.
						trStack[++d] = STATE_T_RAY;
					}
					break;
					
				default:
					// Complete at this level, step out.
					d -= 1;
					break;
			}
		}
	} while( d >= 0 );
	
	return retcolor;
}	
Open in New Window
 
04.20.2008 at 07:40AM PDT, ID: 21396142

Rank: Guru

Hmm ... I dont seem to have handled the MAX limit properly. It will infinite loop at MAX.

This should fix that.

Paul

1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
/*
	Unrolled recursive function.
	
	At each level in the tree we must evaluate the reflection ray and the transmission ray.
	
	When the intersection is calculated, both tRay and rRay are calculated along with kr and kt.
	
	To ensure that intersection is called minimally we must therefore have at least 4 stacks. 
	The tRay, the rRay, kt and kr.
	
	We also need a state that tells us which ray we have processed (ray, t or r). This must also be stacked so
	that makes 5 stacks. 
*/
 
// Two stages at each level.
Ray tStack[MAX];
Ray rStack[MAX];
int ktStack[MAX];
int krStack[MAX];
// Next ray to process at this level (tRay or rRay).
#define STATE_T_RAY (0)
#define STATE_R_RAY (1)
#define STATE_COMPLETE (2)
int trStack[MAX];
int d; // Depth.
 
color traceRay(ray) {
	// Start at depth 0.
	d = 0;
	// Keep walking the tree until we get back to depth 0.
	do {
		// Trace the ray.
		if ( intersection(ray) ) {
			// Keep the results of the intersection call at the current level.
			tStack[d] = trans_ray;
			ktStack[d] = kt;
			rStack[d] = reflect_ray;
			krStack[d] = kr;
			// Next state at this level is to check the t ray.
			trStack[d] = STATE_T_RAY;
		} else {
			// No intersection, add background colour
			retcolor += background_color;
			//  and were done at this level.
			d -= 1;
		}
		
		// Which ray should we work on for the next iteration?
 
		// Initially nothing.
		ray = null;
		// Walk the stack till we're done or we know which ray to do next.
		while ( ray == null && d >= 0 ) {
			// Check the state.
			switch ( trStack[d] ) {
				case STATE_T_RAY:
					// Step my state.
					trStack[d] += 1;
					// Was there one?
					if ( ktStack[d] > 0 && d < MAX ) {
						// Do the transmission ray.
						ray = tStack[d];
						// Step up.
						trStack[++d] = STATE_T_RAY;
					}
					break;
					
				case STATE_R_RAY:
					// Step my state.
					trStack[d] += 1;
					// Was there one?
					if ( krStack[d] > 0 && d < MAX  ) {
						// Do the reflection ray.
						ray = rStack[d];
						// Step up.
						trStack[++d] = STATE_T_RAY;
					}
					break;
					
				default:
					// Complete at this level, step out.
					d -= 1;
					break;
			}
		}
	} while( d >= 0 );
	
	return retcolor;
}	
Open in New Window
 
04.20.2008 at 07:51AM PDT, ID: 21396207

Rank: Guru

And a slight improvement:
...
                  // Check and step the state.
                  switch ( trStack[d]++ ) {
...
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
/*
	Unrolled recursive function.
	
	At each level in the tree we must evaluate the reflection ray and the transmission ray.
	
	When the intersection is calculated, both tRay and rRay are calculated along with kr and kt.
	
	To ensure that intersection is called minimally we must therefore have at least 4 stacks. 
	The tRay, the rRay, kt and kr.
	
	We also need a state that tells us which ray we have processed (ray, t or r). This must also be stacked so
	that makes 5 stacks. 
*/
 
// Two stages at each level.
Ray tStack[MAX];
Ray rStack[MAX];
int ktStack[MAX];
int krStack[MAX];
// Next ray to process at this level (tRay or rRay).
#define STATE_T_RAY (0)
#define STATE_R_RAY (1)
#define STATE_COMPLETE (2)
int trStack[MAX];
int d; // Depth.
 
color traceRay(ray) {
	// Start at depth 0.
	d = 0;
	// Keep walking the tree until we get back to depth 0.
	do {
		// Trace the ray.
		if ( intersection(ray) ) {
			// Keep the results of the intersection call at the current level.
			tStack[d] = trans_ray;
			ktStack[d] = kt;
			rStack[d] = reflect_ray;
			krStack[d] = kr;
			// Next state at this level is to check the t ray.
			trStack[d] = STATE_T_RAY;
		} else {
			// No intersection, add background colour
			retcolor += background_color;
			//  and were done at this level.
			d -= 1;
		}
		
		// Which ray should we work on for the next iteration?
 
		// Initially nothing.
		ray = null;
		// Walk the stack till we're done or we know which ray to do next.
		while ( ray == null && d >= 0 ) {
			// Check and step the state.
			switch ( trStack[d]++ ) {
				case STATE_T_RAY:
					// Was there one?
					if ( ktStack[d] > 0 && d < MAX ) {
						// Do the transmission ray.
						ray = tStack[d];
						// Step up.
						trStack[++d] = STATE_T_RAY;
					}
					break;
					
				case STATE_R_RAY:
					// Was there one?
					if ( krStack[d] > 0 && d < MAX  ) {
						// Do the reflection ray.
						ray = rStack[d];
						// Step up.
						trStack[++d] = STATE_T_RAY;
					}
					break;
					
				default:
					// Complete at this level, step out.
					d -= 1;
					break;
			}
		}
	} while( d >= 0 );
	
	return retcolor;
}	
Open in New Window
Assisted Solution
 
04.21.2008 at 10:48AM PDT, ID: 21404256
RQuadling:
Yes it seems odd that recursion isn't supported in CUDA, but unfortunately that's the way it is. The reason why ray tracing isn't done in mainstream GPUs is because of the high memory accesses. However at this point I just want to get it working, and then do any optimizations possible.

Ozo and Paul:
Thank you very for the contribution. I wish I had a framework to test out the code, but I think this will give me a general idea of how it works. From what I understand, recursion can be replaced by your iterative codde, which theoretically behaves in a "tree-like" way. Am I correct? I wish I had a good grasp of the tree mechanism. My experience with recursion is limited, since the only applications I've done with it are FIbonacci and Ray Tracing.
 
04.21.2008 at 11:31AM PDT, ID: 21404632

Rank: Guru

>>From what I understand, recursion can be replaced by your iterative codde, which theoretically behaves in a "tree-like" way. Am I correct?

Correct! :)

Paul
 
04.23.2008 at 08:29AM PDT, ID: 21421773
Ok time to assign points!

The project is still in the air though, so its likely I'll post another (to be continued...) thread here in the future.
 
Thanks again!
 
 
20080236-EE-VQP-29 / EE_QW_2_20070628