1 //////////////////////////////////////////////////////////////////////
2 //
3 //                             Pixie
4 //
5 // Copyright � 1999 - 2003, Okan Arikan
6 //
7 // Contact: okan@cs.utexas.edu
8 //
9 //	This library is free software; you can redistribute it and/or
10 //	modify it under the terms of the GNU Lesser General Public
11 //	License as published by the Free Software Foundation; either
12 //	version 2.1 of the License, or (at your option) any later version.
13 //
14 //	This library is distributed in the hope that it will be useful,
15 //	but WITHOUT ANY WARRANTY; without even the implied warranty of
16 //	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 //	Lesser General Public License for more details.
18 //
19 //	You should have received a copy of the GNU Lesser General Public
20 //	License along with this library; if not, write to the Free Software
21 //	Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 //
23 ///////////////////////////////////////////////////////////////////////
24 ///////////////////////////////////////////////////////////////////////
25 //
26 //  File				:	raytracer.cpp
27 //  Classes				:	CRaytracer
28 //  Description			:
29 //
30 ////////////////////////////////////////////////////////////////////////
31 #include <math.h>
32 
33 #include "raytracer.h"
34 #include "shading.h"
35 #include "random.h"
36 #include "memory.h"
37 #include "error.h"
38 #include "renderer.h"
39 
40 ///////////////////////////////////////////////////////////////////////
41 // Class				:	CPrimaryBundle
42 // Method				:	CPrimaryBundle
43 // Description			:	Ctor
44 // Return Value			:	-
45 // Comments				:
CPrimaryBundle(int mr,int numSamples,int nExtraChans,int * sampOrder,int numExtraSamp,float * sampDefaults)46 CPrimaryBundle::CPrimaryBundle(int mr,int numSamples,int nExtraChans,int *sampOrder,int numExtraSamp,float *sampDefaults) {
47 	maxPrimaryRays		=	mr;
48 	numExtraChannels	=	0;		// These will be filled in after constuction if needed
49 	sampleOrder			=	NULL;
50 	rayBase				=	new CPrimaryRay[maxPrimaryRays];
51 	rays				=	new CRay*[maxPrimaryRays];
52 	last				=	0;
53 	depth				=	0;
54 	numRays				=	0;
55 	allSamples			=	new float[numSamples*maxPrimaryRays];
56 
57 	float	*src		=	allSamples;
58 	for (int i=0;i<maxPrimaryRays;i++,src+=numSamples) {
59 		rayBase[i].samples	=	src;
60 	}
61 
62 	numExtraChannels	=	nExtraChans;
63 	sampleOrder			=	sampOrder;
64 	numExtraSamples		=	numExtraSamp;
65 	sampleDefaults		=	sampDefaults;
66 
67 }
68 
69 ///////////////////////////////////////////////////////////////////////
70 // Class				:	CPrimaryBundle
71 // Method				:	~CPrimaryBundle
72 // Description			:	Dtor
73 // Return Value			:	-
74 // Comments				:
~CPrimaryBundle()75 CPrimaryBundle::~CPrimaryBundle() {
76 	delete [] rayBase;
77 	delete [] rays;
78 	delete [] allSamples;
79 }
80 
81 ///////////////////////////////////////////////////////////////////////
82 // Class				:	CRaytracer
83 // Method				:	postTraceAction
84 // Description			:	Post trace action, force shading
85 // Return Value			:	-
86 // Comments				:
postTraceAction()87 int		CPrimaryBundle::postTraceAction() {
88 	return TRUE;
89 }
90 
91 ///////////////////////////////////////////////////////////////////////
92 // Class				:	CRaytracer
93 // Method				:	postShade
94 // Description			:	Record the raytracing results
95 // Return Value			:	-
96 // Comments				:
postShade(int nr,CRay ** r,float ** varying)97 void	CPrimaryBundle::postShade(int nr,CRay **r,float **varying)	{
98 	float		*Ci		=	varying[VARIABLE_CI];
99 	float		*Oi		=	varying[VARIABLE_OI];
100 	const int	*cOrder	=	sampleOrder;
101 
102 	// FIXME: make this deal with comp and non comp channels properly
103 	if (depth == 0) {
104 
105 		// First hit
106 		for (int i=0;i<nr;i++,Ci+=3,Oi+=3) {
107 			CPrimaryRay	*cRay	=	(CPrimaryRay *) r[i];
108 
109 			// Is this a matte surface
110 			if (cRay->object->attributes->flags & ATTRIBUTES_FLAGS_MATTE) {
111 				initv(cRay->color,0);
112 				initv(cRay->opacity,0);
113 				cRay->ropacity[0]	=	1-Oi[0];
114 				cRay->ropacity[1]	=	1-Oi[1];
115 				cRay->ropacity[2]	=	1-Oi[2];
116 			} else {
117 				movvv(cRay->color,Ci);
118 				movvv(cRay->opacity,Oi);
119 				cRay->ropacity[0]	=	1-Oi[0];
120 				cRay->ropacity[1]	=	1-Oi[1];
121 				cRay->ropacity[2]	=	1-Oi[2];
122 			}
123 
124 			 if ((Oi[0] < CRenderer::opacityThreshold[0]) ||
125 			 	 (Oi[1] < CRenderer::opacityThreshold[1]) ||
126 			 	 (Oi[2] < CRenderer::opacityThreshold[2])) {
127 				rays[last++]	=	cRay;
128 			} else {
129 				movvv(cRay->samples,cRay->color);
130 			}
131 
132 			cRay->samples[3]	=	(float) ((cRay->opacity[0] + cRay->opacity[1] + cRay->opacity[2])*0.333333333);
133 			cRay->samples[4]	=	cRay->t;
134 		}
135 
136 		// Copy the extra samples
137 		int	k = 5;
138 		for (int i=0;i<numExtraChannels;i++) {
139 			const int 	outType			= *cOrder++;
140 			const int 	channelSamples	= *cOrder++;
141 			const float	*s				= varying[outType];
142 			float		*d;
143 
144 			switch(channelSamples) {
145 			case 0:
146 				break;
147 			case 1:
148 				for (int j=0;j<nr;j++) {
149 					d		=	((CPrimaryRay *) r[j])->samples + k;
150 					*d++	=	*s++;
151 				}
152 				k++;
153 				break;
154 			case 2:
155 				for (int j=0;j<nr;j++) {
156 					d		=	((CPrimaryRay *) r[j])->samples + k;
157 					*d++	=	*s++;
158 					*d++	=	*s++;
159 				}
160 				k	+=	2;
161 				break;
162 			case 3:
163 				for (int j=0;j<nr;j++) {
164 					d		=	((CPrimaryRay *) r[j])->samples + k;
165 					*d++	=	*s++;
166 					*d++	=	*s++;
167 					*d++	=	*s++;
168 				}
169 				k	+=	3;
170 				break;
171 			case 4:
172 				for (int j=0;j<nr;j++) {
173 					d		=	((CPrimaryRay *) r[j])->samples + k;
174 					*d++	=	*s++;
175 					*d++	=	*s++;
176 					*d++	=	*s++;
177 					*d++	=	*s++;
178 				}
179 				k	+=	4;
180 				break;
181 			default:
182 				for (int j=0;j<nr;j++) {
183 					d		=	((CPrimaryRay *) r[j])->samples + k;
184 					for (int l=channelSamples;l>0;l--) {
185 						*d++	=	*s++;
186 					}
187 				}
188 				k	+=	channelSamples;
189 			}
190 		}
191 	} else {
192 		// Transparency hit
193 		for (int i=0;i<nr;i++,Ci+=3,Oi+=3) {
194 			CPrimaryRay	*cRay		=	(CPrimaryRay *) r[i];
195 
196 			const	int	transparent	= ( (Oi[0] < CRenderer::opacityThreshold[0]) ||
197 								 		(Oi[1] < CRenderer::opacityThreshold[1]) ||
198 								 		(Oi[2] < CRenderer::opacityThreshold[2]));
199 
200 			if (cRay->object->attributes->flags & ATTRIBUTES_FLAGS_MATTE) {
201 				cRay->ropacity[0]	*=	1-Oi[0];
202 				cRay->ropacity[1]	*=	1-Oi[1];
203 				cRay->ropacity[2]	*=	1-Oi[2];
204 			} else {
205 				vector	t;
206 				movvv(t,Oi);
207 				mulvv(Ci,cRay->ropacity);
208 				mulvv(Oi,cRay->ropacity);
209 
210 				addvv(cRay->color,Ci);
211 				addvv(cRay->opacity,Oi);
212 				cRay->ropacity[0]	*=	1-t[0];
213 				cRay->ropacity[1]	*=	1-t[1];
214 				cRay->ropacity[2]	*=	1-t[2];
215 			}
216 
217 			if (transparent) {
218 				rays[last++]		=	cRay;
219 			} else {
220 				movvv(cRay->samples,cRay->color);
221 			}
222 
223 			cRay->samples[3]	=	(float) ((cRay->opacity[0] + cRay->opacity[1] + cRay->opacity[2])*0.333333333);//1;
224 
225 		}
226 
227 		// The extra samples are copied from the closest intersection
228 	}
229 }
230 
231 ///////////////////////////////////////////////////////////////////////
232 // Class				:	CRaytracer
233 // Method				:	CRaytracer
234 // Description			:	Ctor
235 // Return Value			:	-
236 // Comments				:
postShade(int nr,CRay ** r)237 void	CPrimaryBundle::postShade(int nr,CRay **r) {
238 	if (depth == 0) {
239 		for (int i=0;i<nr;i++) {
240 			CPrimaryRay	*cRay	=	(CPrimaryRay *) r[i];
241 
242 			cRay->samples[0]	=	0;
243 			cRay->samples[1]	=	0;
244 			cRay->samples[2]	=	0;
245 			cRay->samples[3]	=	0;
246 			cRay->samples[4]	=	C_INFINITY;
247 		}
248 
249 		// zero the extra samples
250 		if (numExtraSamples > 0) {
251 			for (int j=0;j<nr;j++) {
252 				float		*d		=	((CPrimaryRay *) r[j])->samples + 5;
253 				const float *src	=	sampleDefaults;
254 				for (int i=0;i<numExtraSamples;i++)
255 					*d++ = *src++;
256 			}
257 		}
258 	} else {
259 		for (int i=0;i<nr;i++) {
260 			CPrimaryRay	*cRay	=	(CPrimaryRay *) r[i];
261 
262 			movvv(cRay->samples,cRay->color);
263 		}
264 	}
265 }
266 
267 ///////////////////////////////////////////////////////////////////////
268 // Class				:	CRaytracer
269 // Method				:	CRaytracer
270 // Description			:	Ctor
271 // Return Value			:	-
272 // Comments				:
post()273 void	CPrimaryBundle::post() {
274 	numRays	=	last;
275 	last	=	0;
276 	depth++;
277 }
278 
279 
280 ///////////////////////////////////////////////////////////////////////
281 // Class				:	CRaytracer
282 // Method				:	CRaytracer
283 // Description			:	Ctor
284 // Return Value			:	-
285 // Comments				:
CRaytracer(int thread)286 CRaytracer::CRaytracer(int thread) : CShadingContext(thread), primaryBundle(CRenderer::shootStep,CRenderer::numSamples,CRenderer::numExtraChannels,CRenderer::sampleOrder,CRenderer::numExtraSamples,CRenderer::sampleDefaults)  {
287 	CRenderer::raytracingFlags	|=	ATTRIBUTES_FLAGS_PRIMARY_VISIBLE;
288 
289 	const int		xoffset		=	(int) ceil((CRenderer::pixelFilterWidth		- 1)*0.5f);
290 	const int		yoffset		=	(int) ceil((CRenderer::pixelFilterHeight	- 1)*0.5f);
291 	const int		xpixels		=	CRenderer::bucketWidth + 2*xoffset;
292 	const int		ypixels		=	CRenderer::bucketHeight + 2*yoffset;
293 
294 	fbContribution				=	new float[xpixels*ypixels];
295 	fbPixels					=	new float[xpixels*ypixels*CRenderer::numSamples];
296 }
297 
298 ///////////////////////////////////////////////////////////////////////
299 // Class				:	CRaytracer
300 // Method				:	~CRaytracer
301 // Description			:	Dtor
302 // Return Value			:	-
303 // Comments				:
~CRaytracer()304 CRaytracer::~CRaytracer() {
305 	delete [] fbContribution;
306 	delete [] fbPixels;
307 }
308 
309 
310 ///////////////////////////////////////////////////////////////////////
311 // Class				:	CRaytracer
312 // Method				:	replace
313 // Description			:	Replace the occurance of a pointer with another
314 // Return Value			:	-
315 // Comments				:
renderingLoop()316 void	CRaytracer::renderingLoop() {
317 	int				left;
318 	int				top;
319 	int				width;
320 	int				height;
321 	CRenderer::CJob	job;
322 
323 	memBegin(threadMemory);
324 
325 	// While not done
326 	while(TRUE) {
327 
328 		// Get the job from the renderer
329 		CRenderer::dispatchJob(thread,job);
330 
331 		// Process the job
332 		if (job.type == CRenderer::CJob::TERMINATE) {
333 
334 			// End the context
335 			break;
336 		} else if (job.type == CRenderer::CJob::BUCKET) {
337 			const int	x	=	job.xBucket;
338 			const int	y	=	job.yBucket;
339 
340 			assert(x < CRenderer::xBuckets);
341 			assert(y < CRenderer::yBuckets);
342 
343 			currentXBucket	=	x;
344 			currentYBucket	=	y;
345 
346 			left			=	x*CRenderer::bucketWidth;
347 			top				=	y*CRenderer::bucketHeight;
348 			width			=	min(CRenderer::bucketWidth,CRenderer::xPixels-left);
349 			height			=	min(CRenderer::bucketHeight,CRenderer::yPixels-top);
350 
351 			// Sample the framebuffer
352 			sample(left,top,width,height);
353 
354 			// Flush the data to the out devices
355 			CRenderer::commit(left,top,width,height,fbPixels);
356 
357 			// Send bucket data if we're a netrender
358 			if (CRenderer::netClient != INVALID_SOCKET) {
359 				CRenderer::sendBucketDataChannels(currentXBucket,currentYBucket);
360 			}
361 
362 			currentXBucket++;
363 			if (currentXBucket == CRenderer::xBuckets) {
364 				currentXBucket	=	0;
365 				currentYBucket++;
366 			}
367 
368 		} else {
369 			error(CODE_BUG,"Invalid job for the hider\n");
370 		}
371 	}
372 
373 	memEnd(threadMemory);
374 }
375 
376 
377 
378 
379 ///////////////////////////////////////////////////////////////////////
380 // Class				:	CRaytracer
381 // Method				:	sampleFi
382 // Description			:	Samples a rectangular array of pixels
383 // Return Value			:	-
384 // Comments				:
sample(int left,int top,int xpixels,int ypixels)385 void	CRaytracer::sample(int left,int top,int xpixels,int ypixels) {
386 	int				maxShading			=	primaryBundle.maxPrimaryRays;
387 	int				i,j;
388 	const int		xsamples			=	xpixels*CRenderer::pixelXsamples + 2*CRenderer::xSampleOffset;
389 	const int		ysamples			=	ypixels*CRenderer::pixelYsamples + 2*CRenderer::ySampleOffset;
390 	CPrimaryRay		*rays				=	primaryBundle.rayBase;
391 	CRay			**rayPointers		=	primaryBundle.rays;
392 	CPrimaryRay		*cRay;
393 	const float		invXsamples			=	1 / (float) CRenderer::pixelXsamples;
394 	const float		invYsamples			=	1 / (float) CRenderer::pixelYsamples;
395 
396 	// Clear the framebuffer
397 	for (i=0;i<(xpixels*ypixels);i++) {
398 		fbContribution[i]	=	0;
399 		fbPixels[i]			=	0;
400 	}
401 	for (;i<(xpixels*ypixels*CRenderer::numSamples);i++) {
402 		fbPixels[i]			=	0;
403 	}
404 
405 	// Generate the image
406 	{
407 		int		numShading	=	0;
408 		int		x,y;
409 		cRay				=	rays;
410 
411 		for (j=0;j<ysamples;j+=8) {
412 			for (i=0;i<xsamples;i+=8) {
413 				const int	my	=	min(ysamples-j,8);
414 				const int	mx	=	min(xsamples-i,8);
415 
416 				for (y=0;y<my;y++) {
417 					for (x=0;x<mx;x++) {
418 						cRay->x						=	(float) left + (float) (i+x-CRenderer::xSampleOffset+CRenderer::jitter*(urand()-(float) 0.5) + (float) 0.5)*invXsamples;	// Center the sample location in the pixel
419 						cRay->y						=	(float) top  + (float) (j+y-CRenderer::ySampleOffset+CRenderer::jitter*(urand()-(float) 0.5) + (float) 0.5)*invYsamples;
420 
421 						rayPointers[numShading++]	=	cRay;
422 						cRay++;
423 
424 						if (numShading >= maxShading) {
425 							computeSamples(rays,numShading);
426 							splatSamples(rays,numShading,left,top,xpixels,ypixels);
427 							cRay					=	rays;
428 							numShading				=	0;
429 						}
430 					}
431 				}
432 			}
433 		}
434 
435 		// Shade the leftover samples
436 		if (numShading > 0) {
437 			computeSamples(rays,numShading);
438 			splatSamples(rays,numShading,left,top,xpixels,ypixels);
439 		}
440 	}
441 
442 	// At this point, fb should contain the framebuffer, normalize the framebuffer
443 	for (i=0;i<xpixels*ypixels;i++) {
444 		const float	invContribution	=	1 / fbContribution[i];
445 
446 		for (int k=0;k<CRenderer::numSamples;k++) {
447 			fbPixels[i*CRenderer::numSamples+k]	*=	invContribution;
448 		}
449 	}
450 }
451 
452 
453 
454 ///////////////////////////////////////////////////////////////////////
455 // Class				:	CRaytracer
456 // Method				:	computeSamples
457 // Description			:	Raytrace the sample locations
458 // Return Value			:	-
459 // Comments				:
computeSamples(CPrimaryRay * rays,int numShading)460 void	CRaytracer::computeSamples(CPrimaryRay *rays,int numShading) {
461 	int			i;
462 	float		x,y;
463 	CPrimaryRay	*cRay	=	rays;
464 
465 	if (CRenderer::aperture == 0) {
466 		// No Depth of field effect
467 
468 		for (i=numShading;i>0;i--,cRay++) {
469 			// First two numbers in the samples list are the x/y coordinates in pixels of the sample location
470 			x					=	cRay->x;
471 			y					=	cRay->y;
472 
473 			vector		from,to;
474 			pixels2camera(from,x,y,0);
475 			pixels2camera(to,x,y,CRenderer::imagePlane);
476 
477 			movvv(cRay->from,from);
478 			subvv(cRay->dir,to,from);
479 			normalizev(cRay->dir);
480 
481 			cRay->time			=	((CRenderer::flags & OPTIONS_FLAGS_SAMPLEMOTION) ? urand() : 0);
482 			cRay->t				=	C_INFINITY;
483 			cRay->flags			=	ATTRIBUTES_FLAGS_PRIMARY_VISIBLE;
484 			cRay->tmin			=	0;
485 		}
486 	} else {
487 		for (i=numShading;i>0;i--,cRay++) {
488 			// First two numbers in the samples list are the x/y coordinates in pixels of the sample location
489 			x					=	cRay->x;
490 			y					=	cRay->y;
491 
492 			vector		from,to;
493 			pixels2camera(from,x,y,0);
494 			pixels2camera(to,x,y,CRenderer::focaldistance);
495 
496 			const float	theta	=	(float) (urand()*2*C_PI);
497 			const float	r		=	urand()*CRenderer::aperture;
498 			from[COMP_X]		+=	cosf(theta) * r;
499 			from[COMP_Y]		+=	sinf(theta) * r;
500 
501 			movvv(cRay->from,from);
502 			subvv(cRay->dir,to,from);
503 			normalizev(cRay->dir);
504 
505 			cRay->time			=	((CRenderer::flags & OPTIONS_FLAGS_SAMPLEMOTION) ? urand() : 0);
506 			cRay->t				=	C_INFINITY;
507 			cRay->flags			=	ATTRIBUTES_FLAGS_PRIMARY_VISIBLE;
508 			cRay->tmin			=	0;
509 		}
510 	}
511 
512 	// Setup the ray differentials
513 	if (CRenderer::projection == OPTIONS_PROJECTION_PERSPECTIVE) {
514 		const float	a	=	CRenderer::dxdPixel/CRenderer::imagePlane;
515 
516 		cRay	=	rays;
517 		for (i=numShading;i>0;i--,cRay++) {
518 			cRay->db			=	0;
519 			cRay->da			=	a;
520 		}
521 	} else{
522 		const float	b	=	CRenderer::dxdPixel;
523 
524 		cRay	=	rays;
525 		for (i=numShading;i>0;i--,cRay++) {
526 			cRay->da			=	0;
527 			cRay->db			=	b;
528 		}
529 	}
530 
531 	// Actually raytrace
532 	primaryBundle.numRays	=	numShading;
533 	primaryBundle.last		=	0;
534 	primaryBundle.depth		=	0;
535 	trace(&primaryBundle);
536 
537 	numRaytraceRays	+=	numShading;
538 }
539 
540 ///////////////////////////////////////////////////////////////////////
541 // Class				:	CRaytracer
542 // Method				:	splatSamples
543 // Description			:	Splat the samples on the image
544 // Return Value			:	-
545 // Comments				:
splatSamples(CPrimaryRay * samples,int numShading,int left,int top,int xpixels,int ypixels)546 void	CRaytracer::splatSamples(CPrimaryRay *samples,int numShading,int left,int top,int xpixels,int ypixels) {
547 	const int		pw				=	(int) ceil((CRenderer::pixelFilterWidth-1)*0.5f);
548 	const int		ph				=	(int) ceil((CRenderer::pixelFilterHeight-1)*0.5f);
549 	const int		filterWidth		=	CRenderer::pixelXsamples + 2*CRenderer::xSampleOffset;
550 	const int		filterHeight	=	CRenderer::pixelYsamples + 2*CRenderer::ySampleOffset;
551 
552 	// Process each sample
553 	for (int i=0;i<numShading;i++,samples++) {
554 		const float	x			=	samples->x;
555 		const float	y			=	samples->y;
556 		float		*fbs		=	samples->samples;
557 		int			pixelX,pixelY;
558 		int			ix			=	(int) floor(x);
559 		int			iy			=	(int) floor(y);
560 		int			pl			=	ix - pw;
561 		int			pr			=	ix + pw;
562 		int			pt			=	iy - ph;
563 		int			pb			=	iy + ph;
564 
565 		pl				=	max(pl,left);
566 		pt				=	max(pt,top);
567 		pr				=	min(pr,left + xpixels - 1);
568 		pb				=	min(pb,top + ypixels - 1);
569 
570 		/*
571 		for (pixelY=pt;pixelY<=pb;pixelY++) {
572 			for (pixelX=pl;pixelX<=pr;pixelX++) {
573 				const int	px				=	(int) floor((x - (pixelX + 0.5f))*filterWidth) + filterWidth>>1;
574 				const int	py				=	(int) floor((y - (pixelY + 0.5f))*filterWidth) + filterHeight>>1;
575 				const float	contribution	=	CRenderer::pixelFilterKernel[py*filterWidth+px];
576 				float		*dest			=	&fbPixels[((pixelY-top)*xpixels+pixelX-left)*CRenderer::numSamples];
577 				const float	*src			=	fbs;
578 
579 				assert((top+ypixels) > pixelY);
580 				assert((left+xpixels) > pixelX);
581 
582 				for (int j=CRenderer::numSamples;j>0;j--) {
583 					*dest++	+=	(*src++)*contribution;
584 				}
585 			}
586 		}
587 		*/
588 
589 		float	cx,cy;
590 		for (cy=pt + 0.5f - y,pixelY=pt;pixelY<=pb;pixelY++,cy++) {
591 			for (cx=pl + 0.5f - x,pixelX=pl;pixelX<=pr;pixelX++,cx++) {
592 				const float	contribution	=	CRenderer::pixelFilter(cx,cy,CRenderer::pixelFilterWidth,CRenderer::pixelFilterHeight);
593 				float		*dest			=	&fbPixels[((pixelY-top)*xpixels+pixelX-left)*CRenderer::numSamples];
594 				const float	*src			=	fbs;
595 
596 				assert((top+ypixels) > pixelY);
597 				assert((left+xpixels) > pixelX);
598 
599 				// Save the contribution for later normalization
600 				fbContribution[((pixelY-top)*xpixels+pixelX-left)]	+=	contribution;
601 
602 				// Accumulate the pixel filter results
603 				for (int j=CRenderer::numSamples;j>0;j--) {
604 					*dest++	+=	(*src++)*contribution;
605 				}
606 			}
607 		}
608 	}
609 }
610 
611