Пример #1
0
// Returns NONE of it does not have valid clip info
short RenderVisTreeClass::calculate_endpoint_clipping_information(
  short endpoint_index,
  uint16 clip_flags)
{
  // If this endpoint was not transformed, then don't do anything with it,
  // and indicate that it's not a valid endpoint
  if (!TEST_RENDER_FLAG(endpoint_index, _endpoint_has_been_transformed)) {
    return NONE;
  }

  // LP addition: extend the endpoint-clip list
  endpoint_clip_data Dummy;
  Dummy.flags = 0;                              // Fake initialization to shut up CW
  EndpointClips.push_back(Dummy);
  size_t Length = EndpointClips.size();
  assert(Length <= 32767);
  assert(Length >= 1);
  size_t LastIndex = Length-1;

  endpoint_data *endpoint= get_endpoint_data(endpoint_index);
  endpoint_clip_data *data= &EndpointClips[LastIndex];
  int32 x;

  assert((clip_flags&(_clip_left|_clip_right)));       /* must have a clip flag */
  assert((clip_flags&(_clip_left|_clip_right))!=(_clip_left|_clip_right));       /* but canÕt have both */
  assert(!TEST_RENDER_FLAG(endpoint_index, _endpoint_has_clip_data));

  // LP change: compose a true transformed point to replace endpoint->transformed,
  // and use it in the upcoming code
  long_vector2d transformed_endpoint;
  overflow_short_to_long_2d(endpoint->transformed,endpoint->flags,
                            transformed_endpoint);

  data->flags= clip_flags&(_clip_left|_clip_right);
  switch (data->flags)
  {
  case _clip_left:
    data->vector.i= transformed_endpoint.i;
    data->vector.j= transformed_endpoint.j;
    break;
  case _clip_right:               /* negatives so we clip to the correct side */
    data->vector.i= -transformed_endpoint.i;
    data->vector.j= -transformed_endpoint.j;
    break;
  }
  // warn(data->vector.i);

  // assert(TEST_RENDER_FLAG(endpoint_index, _endpoint_has_been_transformed));
  x= endpoint_x_coordinates[endpoint_index];

  data->x= (short)PIN(x, 0, view->screen_width);

  return (short)LastIndex;
}
Пример #2
0
// Add a polygon to the polygon queue
void RenderVisTreeClass::PUSH_POLYGON_INDEX(short polygon_index)
{
	if (TEST_RENDER_FLAG(polygon_index, _polygon_is_visible))
		return;
	
	// Grow the list only if necessary
	if (polygon_queue_size < PolygonQueue.size())
		PolygonQueue[polygon_queue_size]= polygon_index;
	else
		PolygonQueue.push_back(polygon_index);
	polygon_queue_size++;
	
	SET_RENDER_FLAG(polygon_index, _polygon_is_visible);
}
/* be sure to examine all of a nodeÕs parents for clipping information (gak!) */
clipping_window_data *RenderSortPolyClass::build_clipping_windows(
	node_data *ChainBegin)
{
	// LP change: growable lists
	AccumulatedLineClips.clear();
	AccumulatedEndpointClips.clear();
	clipping_window_data *first_clipping_window = NULL;
	clipping_window_data *last_clipping_window = NULL;
	endpoint_clip_data *endpoint;
	line_clip_data *line;
	short x0, x1; /* ignoring what clipping parameters weÕve gotten, this is the left and right borders of this node on the screen */
	short i, j;

	// LP: references to simplify the code
	vector<endpoint_clip_data>& EndpointClips = RVPtr->EndpointClips;
	vector<line_clip_data>& LineClips = RVPtr->LineClips;
	vector<clipping_window_data>& ClippingWindows = RVPtr->ClippingWindows;
	vector<short>& endpoint_x_coordinates = RVPtr->endpoint_x_coordinates;
	
	/* calculate x0,x1 (real left and right borders of this node) in case the left and right borders
		of the window are sloppy */
	{
		// LP change: look at beginning of chain
		polygon_data *polygon= get_polygon_data(ChainBegin->polygon_index); /* all these nodes should be the same */
		
		x0= SHRT_MAX, x1= SHRT_MIN;
		for (i= 0;i<polygon->vertex_count;++i)
		{
			short endpoint_index= polygon->endpoint_indexes[i];
			
			if (TEST_RENDER_FLAG(endpoint_index, _endpoint_has_been_transformed))
			{
				short x= endpoint_x_coordinates[endpoint_index];
				
				if (x<x0) x0= x;
				if (x>x1) x1= x;
			}
			else
			{
				x0= SHRT_MIN, x1= SHRT_MAX;
				break;
			}
		}
	}
	
	/* add left, top and bottom of screen */
	endpoint_clip_data *EndpointClipPtr = &EndpointClips[indexLEFT_SIDE_OF_SCREEN];
	AccumulatedEndpointClips.push_back(EndpointClipPtr);
	line_clip_data *LineClipPtr = &LineClips[indexTOP_AND_BOTTOM_OF_SCREEN];
	AccumulatedLineClips.push_back(LineClipPtr);

	/* accumulate clipping information, left to right, into local arrays */
	// Move along chain
	for (node_data *ChainNode = ChainBegin; ChainNode; ChainNode = ChainNode->PS_Shared)
	{
		node_data *node;
		
		// LP change: use chain node as starting point
		for (node= ChainNode;node;node= node->parent) /* examine this node and all parents! */
		{
			/* sort in endpoint clips (left to right) */
			for (i= 0;i<node->clipping_endpoint_count;++i)
			{
				endpoint= &EndpointClips[node->clipping_endpoints[i]];
				
				for (j= 0;j<short(AccumulatedEndpointClips.size());++j)
				{
					if (AccumulatedEndpointClips[j]==endpoint) { j= NONE; break; } /* found duplicate */
					if ((AccumulatedEndpointClips[j]->x==endpoint->x&&endpoint->flags==_clip_left) ||
						AccumulatedEndpointClips[j]->x>endpoint->x)
					{
						break; /* found sorting position if x is greater or x is equal and this is a left clip */
					}
				}
				
				if (j!=NONE) /* if the endpoint was not a duplicate */
				{
					/* expand the array, if necessary, and add the new endpoint */
					int Length = AccumulatedEndpointClips.size();
					AccumulatedEndpointClips.push_back(NULL);
					assert(AccumulatedEndpointClips.size() <= 32767);		// Originally a short value
					if (j!=Length) memmove(&AccumulatedEndpointClips[j+1], &AccumulatedEndpointClips[j],
						(Length-j)*sizeof(endpoint_clip_data *));
					AccumulatedEndpointClips[j]= endpoint;
				}
			}

			/* sort in line clips, avoiding redundancies;  calculate_vertical_line_clip_data(),
				the function which deals with these, does not depend on them being sorted */
			for (i= 0;i<node->clipping_line_count;++i)
			{
				line= &LineClips[node->clipping_lines[i]];
				
				for (j= 0;j<short(AccumulatedLineClips.size());++j) if (AccumulatedLineClips[j]==line) break; /* found duplicate */
				if (j==short(AccumulatedLineClips.size())) /* if the line was not a duplicate */
				{
					AccumulatedLineClips.push_back(line);
					assert(AccumulatedLineClips.size() <= 32767);		// Originally a short value
				}
			}
		}
	}
	
//	dprintf("#%d accumulated points @ %p", accumulated_endpoint_clip_count, accumulated_endpoint_clips);
//	dprintf("#%d accumulated lines @ %p", accumulated_line_clip_count, accumulated_line_clips);

	/* add right side of screen */
	EndpointClipPtr = &EndpointClips[indexRIGHT_SIDE_OF_SCREEN];
	AccumulatedEndpointClips.push_back(EndpointClipPtr);

	/* build the clipping windows */
	{
		short state= _looking_for_left_clip;
		endpoint_clip_data *left_clip = NULL, *right_clip = NULL;

		for (i= 0;i<short(AccumulatedEndpointClips.size());++i)
		{
			endpoint= AccumulatedEndpointClips[i];
	
			switch (endpoint->flags)
			{
				case _clip_left:
					switch (state)
					{
						case _looking_for_left_clip:
							left_clip= endpoint;
							state= _looking_for_right_clip;
							break;
						case _looking_for_right_clip:
							left_clip= endpoint; /* found more strict clipping point, use it instead */
							break;
					}
					break;
				
				case _clip_right:
					switch (state)
					{
						case _looking_for_right_clip:
							right_clip= endpoint;
							state= _building_clip_window;
							break;
						
						/* ignore _left_clips */
					}
					break;
				
				default:
					vassert(false,csprintf(temporary,"RenderSortPoly.cpp: build_clipping_windows(): bad state: %d",state));
					break;
			}

			if (state==_building_clip_window)
			{
				if (left_clip->x<view->screen_width && right_clip->x>0 && left_clip->x<right_clip->x)
				{
					// LP change: clipping windows are in growable list
					size_t Length = ClippingWindows.size();
					POINTER_DATA OldCWPointer = POINTER_CAST(&ClippingWindows.front());
					
					// Add a dummy object and check if the pointer got changed
					clipping_window_data Dummy;
					Dummy.next_window = NULL;			// Fake initialization to shut up CW
					ClippingWindows.push_back(Dummy);
					POINTER_DATA NewCWPointer = POINTER_CAST(&ClippingWindows.front());
				
					if (NewCWPointer != OldCWPointer)
					{
						// Get the clipping windows and sorted nodes into sync; no render objects yet
						for (size_t k=0; k<Length; k++)
						{
							clipping_window_data &ClippingWindow = ClippingWindows[k];
							if (ClippingWindow.next_window != NULL)
								ClippingWindow.next_window = (clipping_window_data *)(NewCWPointer + (POINTER_CAST(ClippingWindow.next_window) - OldCWPointer));
						}
						for (size_t k=0; k<SortedNodes.size(); k++)
						{
							sorted_node_data &SortedNode = SortedNodes[k];
							if (SortedNode.clipping_windows != NULL)
								SortedNode.clipping_windows = (clipping_window_data *)(NewCWPointer + (POINTER_CAST(SortedNode.clipping_windows) - OldCWPointer));
						}
					}
					clipping_window_data *window= &ClippingWindows[Length];
					
					/* handle maintaining the linked list of clipping windows */
					if (!first_clipping_window)
					{
						first_clipping_window= last_clipping_window= window;
					}
					else
					{
						last_clipping_window->next_window= window;
						last_clipping_window= window;
					}
					
					window->x0= left_clip->x, window->x1= right_clip->x;
					window->left= left_clip->vector;
					window->right= right_clip->vector;
					calculate_vertical_clip_data(&AccumulatedLineClips.front(), AccumulatedLineClips.size(), window,
						MAX(x0, window->x0), MIN(x1, window->x1));
					window->next_window= NULL;
				}
				
				state= _looking_for_left_clip;
			}
		}
	}

	return first_clipping_window;
}
Пример #4
0
// LP change: make it better able to do long-distance views
// Using parent index instead of pointer to avoid stale-pointer bug
void RenderVisTreeClass::cast_render_ray(long_vector2d *_vector, int16 endpoint_index,
	node_data* parent, int16 bias) /* _clockwise or _counterclockwise for walking endpoints */
{
	auto polygon_index = parent->polygon_index;

	do
	{
		auto clipping_endpoint_index = endpoint_index;
		int16 clipping_line_index;
		auto clip_flags = next_polygon_along_line(&polygon_index,
		(world_point2d *) &view->origin, _vector, &clipping_endpoint_index, &clipping_line_index, bias);
		
		if (polygon_index == NONE)
		{
			if (clip_flags & _split_render_ray)
			{
				cast_render_ray( _vector, endpoint_index, parent, _clockwise_bias);
				cast_render_ray( _vector, endpoint_index, parent, _counterclockwise_bias);
			}
			continue;
		}
		
		node_data **node_reference, *node;
		
		/* find the old node referencing this polygon transition or build one */
		for( node_reference = &parent->children; *node_reference && (*node_reference)->polygon_index != polygon_index;
				node_reference = &(*node_reference)->siblings)
			;
		node = *node_reference;
		if (!node)
		{
			// LP change: using growable list
			// Contents get swapped when the length starts to exceed the capacity.
			// When they are not NULL,
			// "parent", "siblings" and "children" are pointers to members,
			// "reference" is a pointer to a member with an offset.
			// Cast the pointers to whatever size of integer the system uses.
			const size_t Length = Nodes.size();
			
			node_data Dummy;
			Dummy.flags = 0;				// Fake initialization to shut up CW
			Nodes.push_back(Dummy);
			node = &Nodes[Length];		// The length here is the "old" length
			
			*node_reference = node;
			INITIALIZE_NODE(node, polygon_index, 0, parent, node_reference);
			
			// Place new node in tree if it has gotten rooted
			if (Length > 0)
			{
				node_data *CurrNode = &Nodes.front();
			while(true)
			{
				const int32 PolyDiff = int32(polygon_index) - int32(CurrNode->polygon_index);
				if (PolyDiff > 0)
				{
					node_data *NextNode = CurrNode->PS_Greater;
					if (NextNode)
						// Advance
						CurrNode = NextNode;
					else
					{
						// Attach to end
						CurrNode->PS_Greater = node;
						break;
					}
				}
				else if (PolyDiff < 0)
				{
					node_data *NextNode = CurrNode->PS_Less;
					if (NextNode)
						// Advance
						CurrNode = NextNode;
					else
					{
						// Attach to end
						CurrNode->PS_Less = node;
						break;
					}
				}
				else // Equal
				{
					node_data *NextNode = CurrNode->PS_Shared;
					if (NextNode)
						// Splice node into shared-polygon chain
						node->PS_Shared = NextNode;
					CurrNode->PS_Shared = node;
					break;
				}
			}
			}
		}

		/* update the line clipping information, if necessary, for this node (don't add
			duplicates */
		if (clipping_line_index != NONE)
		{
			
			if (!TEST_RENDER_FLAG(clipping_line_index, _line_has_clip_data))
				calculate_line_clipping_information(clipping_line_index, clip_flags);
			clipping_line_index	= line_clip_indexes[clipping_line_index];
			
			ix i;
			const auto nodeClippingLineCount = node->clipping_line_count;
			
			for( i = 0; i < nodeClippingLineCount && node->clipping_lines[i] != clipping_line_index; ++i)
				;
			if (i == nodeClippingLineCount)
			{
				assert(node->clipping_line_count < MAXIMUM_CLIPPING_LINES_PER_NODE);
				node->clipping_lines[ node->clipping_line_count++ ] = clipping_line_index;
			}
		}
		
		/* update endpoint clipping information for this node if we have a valid endpoint with clip */
		if( !isNONE(clipping_endpoint_index) && clip_flags & ( _clip_left | _clip_right ) )
		{
			clipping_endpoint_index = calculate_endpoint_clipping_information(
				clipping_endpoint_index, clip_flags);
			
			// Be sure it's valid
			if (!isNONE(clipping_endpoint_index)  && node->clipping_endpoint_count < MAXIMUM_CLIPPING_ENDPOINTS_PER_NODE)
				node->clipping_endpoints[ node->clipping_endpoint_count++ ] = clipping_endpoint_index;
		}
		
		parent = node;
	
	}
	while (polygon_index != NONE);
}
Пример #5
0
// Main routine
void RenderVisTreeClass::build_render_tree()
{
	assert(view);	// Idiot-proofing

	/* initialize the queue where we remember polygons we need to fire at */
	initialize_polygon_queue();

	/* initialize our node list to contain the root, etc. */
	initialize_render_tree();
	
	/* reset clipping buffers */
	initialize_clip_data();
	
	// LP change:
	// Adjusted for long-vector handling
	// Using start index of list of nodes: 0
	long_vector2d view_edge;
	
	short_to_long_2d( view->left_edge, view_edge );
	cast_render_ray( &view_edge, NONE, &Nodes.front(), _counterclockwise_bias );
	
	short_to_long_2d( view->right_edge, view_edge );
	cast_render_ray( &view_edge, NONE, &Nodes.front(), _clockwise_bias );
	
	/* 
		pull polygons off the queue, fire at all their new endpoints, building the tree as we go 
	*/
	while (polygon_queue_size)
	{
		auto polygon_index 	= PolygonQueue[ --polygon_queue_size ];
		polygon_data *polygon 	= get_polygon_data(polygon_index);
		
		assert( !POLYGON_IS_DETACHED(polygon) );
		
		const ix vertex_count = polygon->vertex_count;
		for( ix vertex_index = 0; vertex_index < vertex_count; ++vertex_index )
		{
			const auto endpoint_index	= polygon->endpoint_indexes[vertex_index];
			endpoint_data *endpoint		= get_endpoint_data(endpoint_index);
			
			if (TEST_RENDER_FLAG(endpoint_index, _endpoint_has_been_visited))
				continue;
			// LP change: move toward correct handling of long distances
			long_vector2d _vector;
			
			/* transform all visited endpoints */
			endpoint->transformed = endpoint->vertex;
			transform_overflow_point2d( &endpoint->transformed, 
						(world_point2d *) &view->origin, 
						view->yaw, 
						&endpoint->flags );
						
			/* calculate an outbound vector to this endpoint */
			// LP: changed to do long distance correctly.	
			_vector.i 	= int32( endpoint->vertex.x ) - int32( view->origin.x );
			_vector.j	= int32( endpoint->vertex.y ) - int32( view->origin.y );
			
			// LP change: compose a true transformed point to replace endpoint->transformed,
			// and use it in the upcoming code
			long_vector2d transformed_endpoint;
			overflow_short_to_long_2d( 	endpoint->transformed, 
							endpoint->flags, 
							transformed_endpoint );
			if (transformed_endpoint.i > 0)
			{
				const int32 x = view->half_screen_width + 
				( transformed_endpoint.j * view->world_to_screen_x ) / transformed_endpoint.i;
				
				endpoint_x_coordinates[ endpoint_index ] = 
					static_cast< int16 >( PIN(x, INT16_MIN, INT16_MAX) );
					
				SET_RENDER_FLAG(endpoint_index, _endpoint_has_been_transformed);
			}
			
			/* 
				do two cross products to determine whether this endpoint is in our view cone or not
				(we don't have to cast at points outside the cone) 
			*/
			const auto ri = view->right_edge.i;
			const auto rj = view->right_edge.j;
			const int32 crossprod_right = ( ri * _vector.j ) - ( rj * _vector.i );
			
			const auto li = view->left_edge.i;
			const auto lj = view->left_edge.j;
			const int32 crossprod_left = ( li * _vector.j ) - ( lj * _vector.i );
			
			if( crossprod_right <= 0 && crossprod_left >= 0 )
			{
				//it's in our view, cast at it
				int16 endpoint_;
				
				if( ENDPOINT_IS_TRANSPARENT(endpoint) )
					endpoint_ = NONE;
				else
					endpoint_ = endpoint_index;
					
				cast_render_ray(&_vector, endpoint_, &Nodes.front(), _no_bias);
			}
			SET_RENDER_FLAG(endpoint_index, _endpoint_has_been_visited);
		
		}
	}
}
void RenderVisTreeClass::calculate_line_clipping_information(
	short line_index,
	uint16 clip_flags)
{
	// LP addition: extend the line-clip list
	line_clip_data Dummy;
	Dummy.flags = 0;			// Fake initialization to shut up CW
	LineClips.push_back(Dummy);
	size_t Length = LineClips.size();
	assert(Length <= 32767);
	assert(Length >= 1);
	size_t LastIndex = Length-1;
	
	line_data *line= get_line_data(line_index);
	// LP change: relabeling p0 and p1 so as not to conflict with later use
	world_point2d p0_orig= get_endpoint_data(line->endpoint_indexes[0])->vertex;
	world_point2d p1_orig= get_endpoint_data(line->endpoint_indexes[1])->vertex;
	// LP addition: place for new line data
	line_clip_data *data= &LineClips[LastIndex];

	/* itÕs possible (in fact, likely) that this lineÕs endpoints have not been transformed yet,
		so we have to do it ourselves */
	// LP change: making the operation long-distance friendly
	uint16 p0_flags = 0, p1_flags = 0;
	transform_overflow_point2d(&p0_orig, (world_point2d *) &view->origin, view->yaw, &p0_flags);
	transform_overflow_point2d(&p1_orig, (world_point2d *) &view->origin, view->yaw, &p1_flags);
	
	// Defining long versions here and copying over
	long_point2d p0, p1;
	long_vector2d *pv0ptr = (long_vector2d*)(&p0), *pv1ptr = (long_vector2d*)(&p1);
	overflow_short_to_long_2d(p0_orig,p0_flags,*pv0ptr);
	overflow_short_to_long_2d(p1_orig,p1_flags,*pv1ptr);
	
	clip_flags&= _clip_up|_clip_down;	
	assert(clip_flags&(_clip_up|_clip_down));
	assert(!TEST_RENDER_FLAG(line_index, _line_has_clip_data));

	SET_RENDER_FLAG(line_index, _line_has_clip_data);
	line_clip_indexes[line_index]= static_cast<vector<size_t>::value_type>(LastIndex);
	
	data->flags= 0;

	if (p0.x>0 && p1.x>0)
	{
		// LP change:
		long_point2d *p;
		world_distance z;
		long transformed_z;
		long y, y0, y1;
		long x0= view->half_screen_width + (p0.y*view->world_to_screen_x)/p0.x;
		long x1= view->half_screen_width + (p1.y*view->world_to_screen_x)/p1.x;
	
		data->x0= (short)PIN(x0, 0, view->screen_width);
		data->x1= (short)PIN(x1, 0, view->screen_width);
		if (data->x1<data->x0) SWAP(data->x0, data->x1);
		if (data->x1>data->x0)
		{
			if (clip_flags&_clip_up)
			{
				/* precalculate z and transformed_z */
				z= line->lowest_adjacent_ceiling-view->origin.z;
				transformed_z= z*view->world_to_screen_y;
				
				/* calculate and clip y0 and y1 (screen y-coordinates of each side of the line) */
				y0= (p0.x>0) ? (view->half_screen_height - transformed_z/p0.x + view->dtanpitch) : 0;
				y1= (p1.x>0) ? (view->half_screen_height - transformed_z/p1.x + view->dtanpitch) : 0;
		
				/* pick the highest (closest to zero) and pin it to the screen */
				if (y0<y1) y= y0, p= &p0; else y= y1, p= &p1;
				y= PIN(y, 0, view->screen_height);
				
				/* if weÕre not useless (clipping up off the top of the screen) set up top-clip information) */
				if (y<=0)
				{
					clip_flags&= ~_clip_up;
				}
				else
				{
					data->top_vector.i= - p->x, data->top_vector.j= - z;
					data->top_y= y;
				}
			}
			
			if (clip_flags&_clip_down)
			{
				z= line->highest_adjacent_floor - view->origin.z;
				transformed_z= z*view->world_to_screen_y;
				
				/* calculate and clip y0 and y1 (screen y-coordinates of each side of the line) */
				y0= (p0.x>0) ? (view->half_screen_height - transformed_z/p0.x + view->dtanpitch) : view->screen_height;
				y1= (p1.x>0) ? (view->half_screen_height - transformed_z/p1.x + view->dtanpitch) : view->screen_height;
				
				/* pick the highest (closest to zero screen_height) and pin it to the screen */
				if (y0>y1) y= y0, p= &p0; else y= y1, p= &p1;
				y= PIN(y, 0, view->screen_height);
				
				/* if weÕre not useless (clipping up off the bottom of the screen) set up top-clip information) */
				if (y>=view->screen_height)
				{
					clip_flags&= ~_clip_down;
				}
				else
				{
					data->bottom_vector.i= p->x,  data->bottom_vector.j= z;
					data->bottom_y= y;
				}
			}
	
			data->flags= clip_flags;
//			dprintf("line #%d clips %x @ %p", line_index, clip_flags, data);
		}
	}
}
// LP change: make it better able to do long-distance views
// Using parent index instead of pointer to avoid stale-pointer bug
void RenderVisTreeClass::cast_render_ray(
	long_vector2d *_vector, // world_vector2d *vector,
	short endpoint_index,
	int ParentIndex, /* 0==root */
	short bias) /* _clockwise or _counterclockwise for walking endpoints */
{
	// LP: keep the parent-node pointer and index in sync with each other:
	node_data *parent =  &Nodes.front() + ParentIndex;
	
	short polygon_index= parent->polygon_index;

//	dprintf("shooting at e#%d of p#%d", endpoint_index, polygon_index);
	
	do
	{
		short clipping_endpoint_index= endpoint_index;
		short clipping_line_index;
		uint16 clip_flags= next_polygon_along_line(&polygon_index, (world_point2d *) &view->origin, _vector, &clipping_endpoint_index, &clipping_line_index, bias);
		
		if (polygon_index==NONE)
		{
			if (clip_flags&_split_render_ray)
			{
				cast_render_ray(_vector, endpoint_index, ParentIndex, _clockwise_bias);
				cast_render_ray(_vector, endpoint_index, ParentIndex, _counterclockwise_bias);
				// LP: could have reallocated, so keep in sync!
				parent =  &Nodes.front() + ParentIndex;
			}
		}
		else
		{
			node_data **node_reference, *node;
			
			/* find the old node referencing this polygon transition or build one */
			for (node_reference= &parent->children;
					*node_reference && (*node_reference)->polygon_index!=polygon_index;
					node_reference= &(*node_reference)->siblings)
				;
			node= *node_reference;
			if (!node)
			{
				// LP change: using growable list
				// Contents get swapped when the length starts to exceed the capacity.
				// When they are not NULL,
				// "parent", "siblings" and "children" are pointers to members,
				// "reference" is a pointer to a member with an offset.
				// Cast the pointers to whatever size of integer the system uses.
				size_t Length = Nodes.size();
				POINTER_DATA OldNodePointer = POINTER_CAST(&Nodes.front());
				
				// Add a dummy object and check if the pointer got changed
				node_data Dummy;
				Dummy.flags = 0;				// Fake initialization to shut up CW
				Nodes.push_back(Dummy);
				POINTER_DATA NewNodePointer = POINTER_CAST(&Nodes.front());

				if (NewNodePointer != OldNodePointer)				
				{
					for (size_t k=0; k<Length; k++)
					{
						node_data &Node = Nodes[k];
						// If NULL, then these pointers were already copied.
						if (Node.parent)
							Node.parent = (node_data *)(NewNodePointer + (POINTER_CAST(Node.parent) - OldNodePointer));
						if (Node.reference)
							Node.reference = (node_data **)(NewNodePointer + (POINTER_CAST(Node.reference) - OldNodePointer));
						if (Node.siblings)
							Node.siblings = (node_data *)(NewNodePointer + (POINTER_CAST(Node.siblings) - OldNodePointer));
						if (Node.children)
							Node.children = (node_data *)(NewNodePointer + (POINTER_CAST(Node.children) - OldNodePointer));
						if (Node.PS_Greater)
							Node.PS_Greater = (node_data *)(NewNodePointer + (POINTER_CAST(Node.PS_Greater) - OldNodePointer));
						if (Node.PS_Less)
							Node.PS_Less = (node_data *)(NewNodePointer + (POINTER_CAST(Node.PS_Less) - OldNodePointer));
						if (Node.PS_Shared)
							Node.PS_Shared = (node_data *)(NewNodePointer + (POINTER_CAST(Node.PS_Shared) - OldNodePointer));
					}
					
					// Edit parent-node pointer also
					parent =  &Nodes.front() + ParentIndex;

					// CB: Find the node reference again, the old one may point to stale memory
					for (node_reference= &parent->children;
							*node_reference && (*node_reference)->polygon_index!=polygon_index;
							node_reference= &(*node_reference)->siblings)
						;
				}
				node = &Nodes[Length];		// The length here is the "old" length
				
				*node_reference= node;
				INITIALIZE_NODE(node, polygon_index, 0, parent, node_reference);
				
				// Place new node in tree if it has gotten rooted
				if (Length > 0)
				{
				node_data *CurrNode = &Nodes.front();
				while(true)
				{
					long PolyDiff = long(polygon_index) - long(CurrNode->polygon_index);
					if (PolyDiff > 0)
					{
						node_data *NextNode = CurrNode->PS_Greater;
						if (NextNode)
							// Advance
							CurrNode = NextNode;
						else
						{
							// Attach to end
							CurrNode->PS_Greater = node;
							break;
						}
					}
					else if (PolyDiff < 0)
					{
						node_data *NextNode = CurrNode->PS_Less;
						if (NextNode)
							// Advance
							CurrNode = NextNode;
						else
						{
							// Attach to end
							CurrNode->PS_Less = node;
							break;
						}
					}
					else // Equal
					{
						node_data *NextNode = CurrNode->PS_Shared;
						if (NextNode)
							// Splice node into shared-polygon chain
							node->PS_Shared = NextNode;
						CurrNode->PS_Shared = node;
						break;
					}
				}
				}
			}

			/* update the line clipping information, if necessary, for this node (donÕt add
				duplicates */
			if (clipping_line_index!=NONE)
			{
				short i;
				
				if (!TEST_RENDER_FLAG(clipping_line_index, _line_has_clip_data))
					calculate_line_clipping_information(clipping_line_index, clip_flags);
				clipping_line_index= line_clip_indexes[clipping_line_index];
				
				for (i=0;
						i<node->clipping_line_count&&node->clipping_lines[i]!=clipping_line_index;
						++i)
					;
				if (i==node->clipping_line_count)
				{
					assert(node->clipping_line_count<MAXIMUM_CLIPPING_LINES_PER_NODE);
					node->clipping_lines[node->clipping_line_count++]= clipping_line_index;
				}
			}
			
			/* update endpoint clipping information for this node if we have a valid endpoint with clip */
			if (clipping_endpoint_index!=NONE && (clip_flags&(_clip_left|_clip_right)))
			{
				clipping_endpoint_index= calculate_endpoint_clipping_information(clipping_endpoint_index, clip_flags);
				
				// Be sure it's valid
				if (clipping_endpoint_index != NONE)
				{
					if (node->clipping_endpoint_count<MAXIMUM_CLIPPING_ENDPOINTS_PER_NODE)
						node->clipping_endpoints[node->clipping_endpoint_count++]= clipping_endpoint_index;
				}
			}
			
			parent= node;
			// LP: keep in sync!
			ParentIndex = static_cast<int>(parent - &Nodes.front());
		}
	}
	while (polygon_index!=NONE);
}