Пример #1
0
/**
 * \brief Clamps the vertices of the stick so that they fit above the previous stick's vertices.
 * \param self Terrain stick.
 * \param prev Terrain stick.
 */
void liext_terrain_stick_clamp_vertices_bottom (
	LIExtTerrainStick* self,
	LIExtTerrainStick* prev)
{
	int x;
	int z;

	for (z = 0 ; z < 2 ; z++)
	{
		for (x = 0 ; x < 2 ; x++)
		{
			if (prev != NULL)
			{
				self->vertices[x][z].offset = LIMAT_MAX (
					self->vertices[x][z].offset,
					prev->vertices[x][z].offset - self->height);
			}
			else
			{
				self->vertices[x][z].offset = LIMAT_MAX (
					self->vertices[x][z].offset,
					-self->height);
			}
		}
	}
}
Пример #2
0
/**
 * \brief Sets the vertex offsets of the stick.
 *
 * The values chosen are the maximum of the current ones and those given in
 * the slope argument.
 *
 * \param self Stick.
 * \param slope List of four floats.
 * \param offset Extra offset to add to the slope values.
 */
void liext_terrain_stick_set_vertices_max (
	LIExtTerrainStick* self,
	const float*       slope,
	float              offset)
{
	if (self == NULL)
		return;
	self->vertices[0][0].offset = LIMAT_MAX (self->vertices[0][0].offset, slope[0] + offset);
	self->vertices[1][0].offset = LIMAT_MAX (self->vertices[1][0].offset, slope[1] + offset);
	self->vertices[0][1].offset = LIMAT_MAX (self->vertices[0][1].offset, slope[2] + offset);
	self->vertices[1][1].offset = LIMAT_MAX (self->vertices[1][1].offset, slope[3] + offset);
}
Пример #3
0
/**
 * \brief Gets the full size request of the widget.
 *
 * Returns the larger of the user and hard size requests, combined with the
 * style paddings of the widget.
 *
 * \param self Widget.
 * \param request Return location for the size.
 */
void liwdg_widget_get_request (
	LIWdgWidget* self,
	LIWdgSize*   request)
{
	request->width = self->request[0].width;
	request->height = self->request[0].height;
	if (self->request[1].width != -1)
		request->width = LIMAT_MAX (request->width, self->request[1].width);
	if (self->request[1].height != -1)
		request->height = LIMAT_MAX (request->height, self->request[1].height);
	if (self->request[2].width != -1)
		request->width = LIMAT_MAX (request->width, self->request[2].width);
	if (self->request[2].height != -1)
		request->height = LIMAT_MAX (request->height, self->request[2].height);
}
Пример #4
0
static void Terrain_add_box (LIScrArgs* args)
{
	int grid_x1;
	int grid_z1;
	int grid_x2;
	int grid_z2;
	float world_y;
	float world_h;
	int material;

	/* Get the arguments. */
	if (!liscr_args_geti_int (args, 0, &grid_x1) || grid_x1 < 0)
		return;
	if (!liscr_args_geti_int (args, 1, &grid_z1) || grid_z1 < 0)
		return;
	if (!liscr_args_geti_int (args, 2, &grid_x2) || grid_x2 < grid_x1)
		return;
	if (!liscr_args_geti_int (args, 3, &grid_z2) || grid_z2 < grid_z1)
		return;
	if (!liscr_args_geti_float (args, 4, &world_y) || world_y < 0.0f)
		return;
	if (!liscr_args_geti_float (args, 5, &world_h))
		return;
	world_h = LIMAT_MAX (world_h, LIEXT_STICK_EPSILON);
	if (!liscr_args_geti_int (args, 6, &material) || material < 0)
		return;

	liscr_args_seti_int (args, liext_terrain_add_box (args->self,
		grid_x1, grid_z1, grid_x2, grid_z2, world_y, world_h,
		material, NULL, NULL));
}
Пример #5
0
static void Terrain_add_stick_filter_mask (LIScrArgs* args)
{
	int grid_x;
	int grid_z;
	float world_y;
	float world_h;
	int material;
	int mask;

	/* Get the arguments. */
	if (!liscr_args_geti_int (args, 0, &grid_x) || grid_x < 0)
		return;
	if (!liscr_args_geti_int (args, 1, &grid_z) || grid_z < 0)
		return;
	if (!liscr_args_geti_float (args, 2, &world_y) || world_y < 0.0f)
		return;
	if (!liscr_args_geti_float (args, 3, &world_h))
		return;
	world_h = LIMAT_MAX (world_h, LIEXT_STICK_EPSILON);
	if (!liscr_args_geti_int (args, 4, &material) || material < 0)
		return;
	if (!liscr_args_geti_int (args, 5, &mask) || mask < 0)
		return;

	liscr_args_seti_bool (args, liext_terrain_add_stick (args->self, grid_x, grid_z, world_y, world_h, material,
		liext_terrain_stick_filter_mask, &mask));
}
Пример #6
0
static void Sound_set_music_fading (LIScrArgs* args)
{
#ifndef LI_DISABLE_SOUND
	float value;
	LIExtModule* module;

	if (liscr_args_geti_float (args, 0, &value))
	{
		value = LIMAT_MAX (0.0f, value);
		module = liscr_script_get_userdata (args->script, LIEXT_SCRIPT_SOUND);
		liext_sound_set_music_fading (module, value);
	}
#endif
}
Пример #7
0
static void Model_calculate_lod (LIScrArgs* args)
{
	int levels = 5;
	float factor = 0.3f;
	LIEngModel* model;
	LIMdlBuilder* builder;

	model = args->self;
	if (liscr_args_geti_int (args, 0, &levels))
		levels = LIMAT_MAX (1, levels);
	if (liscr_args_geti_float (args, 1, &factor))
		factor = LIMAT_CLAMP (factor, 0.0f, 1.0f);

	builder = limdl_builder_new (model->model);
	if (builder == NULL)
		return;
	limdl_builder_calculate_lod (builder, levels, factor);
	limdl_builder_finish (builder);
	limdl_builder_free (builder);
	liscr_args_seti_bool (args, 1);
}
Пример #8
0
static LIMdlEdge* private_extract_edges (
	LIMdlBuilder*    self,
	LIMdlBuilderLod* lod,
	int              index_count,
	int*             edge_count)
{
	int i;
	int j;
	int k;
	uint32_t i1;
	uint32_t i2;
	uint32_t index;
	uint32_t face[4];
	LIAlgU32dic* dict;
	LIMdlEdge* edge;
	LIMdlEdge* edges;
	LIMdlBuilderFaces* group;

	/* Allocate the edge list. */
	edges = lisys_calloc (index_count, sizeof (LIMdlEdge));
	if (edges == NULL)
		return NULL;
	*edge_count = 0;

	/* Allocate a temporary lookup table. */
	dict = lialg_u32dic_new ();
	if (dict == NULL)
	{
		lisys_free (edges);
		return NULL;
	}

	/* Extract the edges using the index lists. */
	for (i = 0 ; i < lod->face_groups.count ; i++)
	{
		group = lod->face_groups.array + i;
		for (j = 0 ; j < group->indices.count ; j += 3)
		{
			face[0] = group->indices.array[j];
			face[1] = group->indices.array[j + 1];
			face[2] = group->indices.array[j + 2];
			face[3] = face[0];
			for (k = 0 ; k < 3 ; k++)
			{
				i1 = LIMAT_MIN (face[k], face[k + 1]);
				i2 = LIMAT_MAX (face[k], face[k + 1]);
				index = i1 + i2 * index_count;
				edge = lialg_u32dic_find (dict, index);
				if (edge == NULL)
				{
					edge = edges + *edge_count;
					edge->i1 = i1;
					edge->i2 = i2;
					edge->users = 1;
					lialg_u32dic_insert (dict, index, edge);
					*edge_count += 1;
				}
				else
					edge->users++;
			}
		}
	}

	/* Free the lookup table. */
	lialg_u32dic_free (dict);

	return edges;
}
Пример #9
0
static void Terrain_count_column_materials (LIScrArgs* args)
{
	int grid_x;
	int grid_z;
	float y;
	float min;
	float max;
	float length;
	float start = 0.0f;
	float height = -1.0f;
	LIExtTerrainColumn* column;
	LIExtTerrainStick* stick;

	/* Get the arguments. */
	if (!liscr_args_geti_int (args, 0, &grid_x) || grid_x < 0)
		return;
	if (!liscr_args_geti_int (args, 1, &grid_z) || grid_z < 0)
		return;
	if (liscr_args_geti_float (args, 2, &start))
		start = LIMAT_MAX (0.0f, start);
	if (liscr_args_geti_float (args, 3, &height))
		height = LIMAT_MAX (0.0f, height);
	if (!liscr_args_geti_table (args, 4))
		return;

	/* Get the column. */
	column = liext_terrain_get_column (args->self, grid_x, grid_z);
	if (column == NULL)
		return;

	/* Count the materials and add the counts to the table. */
	for (y = 0.0f, stick = column->sticks ; stick != NULL ; y += stick->height, stick = stick->next)
	{
		if (height >= 0.0f)
		{
			min = LIMAT_MAX (y, start);
			max = LIMAT_MIN (y + stick->height, start + height);
		}
		else
		{
			min = LIMAT_MAX (y, start);
			max = y + stick->height;
		}
		length = max - min;
		if (length > 0.0f)
		{
			lua_pushnumber (args->lua, stick->material);
			lua_pushnumber (args->lua, stick->material);
			lua_gettable (args->lua, -3);
			if (lua_type (args->lua, -1) == LUA_TNUMBER)
				length += lua_tonumber (args->lua, -1);
			lua_pop (args->lua, 1);
			lua_pushnumber (args->lua, length);
			lua_settable (args->lua, -3);
		}
	}

	/* Add the remainder if the range was given. */
	if (height >= 0.0f)
	{
		length = start + height - y;
		if (length > 0.0f)
		{
			lua_pushnumber (args->lua, 0);
			lua_pushnumber (args->lua, 0);
			lua_gettable (args->lua, -3);
			if (lua_type (args->lua, -1) == LUA_TNUMBER)
				length += lua_tonumber (args->lua, -1);
			lua_pop (args->lua, 1);
			lua_pushnumber (args->lua, length);
			lua_settable (args->lua, -3);
		}
	}
}
Пример #10
0
static void Voxel_find_blocks (LIScrArgs* args)
{
	int sx;
	int sy;
	int sz;
	int index;
	int line;
	int stamp;
	float radius;
	LIAlgRange sectors;
	LIAlgRange blocks;
	LIAlgRange range;
	LIAlgRangeIter iter0;
	LIAlgRangeIter iter1;
	LIExtModule* module;
	LIMatVector min;
	LIMatVector max;
	LIMatVector point;
	LIMatVector size;
	LIVoxBlock* block;
	LIVoxSector* sector;

	/* Initialize arguments. */
	if (!liscr_args_gets_vector (args, "point", &point))
		return;
	liscr_args_gets_float (args, "radius", &radius);
	liscr_args_set_output (args, LISCR_ARGS_OUTPUT_TABLE_FORCE);
	module = liscr_script_get_userdata (args->script, LIEXT_SCRIPT_VOXEL);
	line = module->voxels->blocks_per_line * module->voxels->sectors->count;

	/* Calculate sight volume. */
	size = limat_vector_init (radius, radius, radius);
	min = limat_vector_subtract (point, size);
	max = limat_vector_add (point, size);
	sectors = lialg_range_new_from_aabb (&min, &max, module->voxels->sectors->width);
	sectors = lialg_range_clamp (sectors, 0, module->voxels->sectors->count - 1);
	blocks = lialg_range_new_from_aabb (&min, &max, module->voxels->sectors->width / module->voxels->blocks_per_line);
	blocks = lialg_range_clamp (blocks, 0, module->voxels->blocks_per_line * module->voxels->sectors->count - 1);

	/* Loop through visible sectors. */
	LIALG_RANGE_FOREACH (iter0, sectors)
	{
		/* Get voxel sector. */
		sector = lialg_sectors_data_index (module->voxels->sectors, LIALG_SECTORS_CONTENT_VOXEL, iter0.index, 0);
		if (sector == NULL)
			continue;

		/* Calculate visible block range. */
		livox_sector_get_offset (sector, &sx, &sy, &sz);
		sx *= module->voxels->blocks_per_line;
		sy *= module->voxels->blocks_per_line;
		sz *= module->voxels->blocks_per_line;
		range.min = 0;
		range.max = module->voxels->blocks_per_line;
		range.minx = LIMAT_MAX (blocks.minx - sx, 0);
		range.miny = LIMAT_MAX (blocks.miny - sy, 0);
		range.minz = LIMAT_MAX (blocks.minz - sz, 0);
		range.maxx = LIMAT_MIN (blocks.maxx - sx, module->voxels->blocks_per_line - 1);
		range.maxy = LIMAT_MIN (blocks.maxy - sy, module->voxels->blocks_per_line - 1);
		range.maxz = LIMAT_MIN (blocks.maxz - sz, module->voxels->blocks_per_line - 1);

		/* Loop through visible blocks. */
		LIALG_RANGE_FOREACH (iter1, range)
		{
			block = livox_sector_get_block (sector, iter1.x, iter1.y, iter1.z);
			stamp = livox_block_get_stamp (block);
			index = (sx + iter1.x) + (sy + iter1.y) * line + (sz + iter1.z) * line * line;
			liscr_args_setf_float (args, index, stamp);
		}
Пример #11
0
static void private_rebuild (
	LIWdgWidget* self,
	int          flags)
{
	int x;
	int y;
	int wmax;
	int hmax;
	int wpad;
	int hpad;
	int wreq;
	int hreq;
	int start;
	int expand;
	LIWdgWidget* child;
	LIWdgRect rect;
	LIWdgSize size;

	if (self->rebuilding)
		return;
	self->rebuilding = 1;

	if (self->homogeneous)
	{
		if (1)
		{
			/* Calculate the width request. */
			wmax = 0;
			wpad = self->margin_right + self->margin_left;
			for (x = 0 ; x < self->width ; x++)
			{
				if (self->cols[x].request > 0)
				{
					if (x != self->width - 1)
						wpad += self->col_spacing;
					if (wmax < self->cols[x].request)
						wmax = self->cols[x].request;
				}
			}
			wreq = wpad + wmax * self->width;

			/* Calculate the height request. */
			hmax = 0;
			hpad = self->margin_top + self->margin_bottom;
			for (y = 0 ; y < self->height ; y++)
			{
				if (self->rows[y].request > 0)
				{
					if (y != self->height - 1)
						hpad += self->row_spacing;
					if (hmax < self->rows[y].request)
						hmax = self->rows[y].request;
				}
			}
			hreq = hpad + hmax * self->height;

			/* Set the size request. */
			liwdg_widget_set_request (self, 0, wreq, hreq);
			liwdg_widget_get_allocation (self, &rect);
			liwdg_widget_get_request (self, &size);
			rect.width = LIMAT_MAX (size.width, rect.width);
			rect.height = LIMAT_MAX (size.height, rect.height);
			liwdg_widget_set_allocation (self, rect.x, rect.y, rect.width, rect.height);
		}

		liwdg_widget_get_allocation (self, &rect);

		if (flags & PRIVATE_REBUILD_HORZ)
		{
			/* Get horizontal expansion. */
			if (self->col_expand > 0)
			{
				expand = rect.width - wreq;
				lisys_assert (expand >= 0);
				expand /= self->width;
			}
			else
				expand = 0;

			/* Set horizontal allocations. */
			start = self->margin_left;
			for (x = 0 ; x < self->width ; x++)
			{
				self->cols[x].start = start;
				self->cols[x].allocation = wmax;
				self->cols[x].allocation += expand;
				start += self->cols[x].allocation;
				start += self->col_spacing;
			}
		}

		if (flags & PRIVATE_REBUILD_VERT)
		{
			/* Get vertical expansion. */
			if (self->row_expand > 0)
			{
				expand = rect.height - hreq;
				lisys_assert (expand >= 0);
				expand /= self->height;
			}
			else
				expand = 0;

			/* Set vertical allocations. */
			start = self->margin_top;
			for (y = 0 ; y < self->height ; y++)
			{
				self->rows[y].start = start;
				self->rows[y].allocation = hmax;
				self->rows[y].allocation += expand;
				start += self->rows[y].allocation;
				start += self->row_spacing;
			}
		}
	}
	else
	{
		if (flags & PRIVATE_REBUILD_REQUEST)
		{
			/* Calculate the width request. */
			wreq = self->margin_right + self->margin_left;
			for (x = 0 ; x < self->width ; x++)
			{
				if (self->cols[x].request > 0)
				{
					if (x != self->width - 1)
						wreq += self->col_spacing;
					wreq += self->cols[x].request;
				}
			}

			/* Calculate the height request. */
			hreq = self->margin_top + self->margin_bottom;
			for (y = 0 ; y < self->height ; y++)
			{
				if (self->rows[y].request > 0)
				{
					if (y != self->height - 1)
						hreq += self->row_spacing;
					hreq += self->rows[y].request;
				}
			}

			/* Set the size request. */
			liwdg_widget_set_request (self, 0, wreq, hreq);
			liwdg_widget_get_allocation (self, &rect);
			liwdg_widget_get_request (self, &size);
			rect.width = LIMAT_MAX (size.width, rect.width);
			rect.height = LIMAT_MAX (size.height, rect.height);
			liwdg_widget_set_allocation (self, rect.x, rect.y, rect.width, rect.height);
		}

		liwdg_widget_get_allocation (self, &rect);

		if (flags & PRIVATE_REBUILD_HORZ)
		{
			/* Get horizontal expansion. */
			if (self->col_expand > 0)
			{
				expand = rect.width - self->margin_left - self->margin_right;
				lisys_assert (expand >= 0);
				for (x = 0 ; x < self->width ; x++)
				{
					if (self->cols[x].request)
					{
						expand -= self->cols[x].request;
						if (x < self->width - 1)
							expand -= self->col_spacing;
					}
				}
				lisys_assert (expand >= 0);
				expand /= self->col_expand;
			}
			else
				expand = 0;

			/* Set horizontal allocations. */
			start = self->margin_left;
			for (x = 0 ; x < self->width ; x++)
			{
				self->cols[x].start = start;
				self->cols[x].allocation = self->cols[x].request;
				if (self->cols[x].expand)
					self->cols[x].allocation += expand;
				start += self->cols[x].allocation;
				if (self->cols[x].request)
					start += self->col_spacing;
			}
		}

		if (flags & PRIVATE_REBUILD_VERT)
		{
			/* Get vertical expansion. */
			if (self->row_expand > 0)
			{
				expand = rect.height - self->margin_top - self->margin_bottom;
				lisys_assert (expand >= 0);
				for (y = 0 ; y < self->height ; y++)
				{
					if (self->rows[y].request)
					{
						expand -= self->rows[y].request;
						if (y < self->height - 1)
							expand -= self->row_spacing;
					}
				}
				lisys_assert (expand >= 0);
				expand /= self->row_expand;
			}
			else
				expand = 0;

			/* Set vertical allocations. */
			start = self->margin_top;
			for (y = 0 ; y < self->height ; y++)
			{
				self->rows[y].start = start;
				self->rows[y].allocation = self->rows[y].request;
				if (self->rows[y].expand)
					self->rows[y].allocation += expand;
				start += self->rows[y].allocation;
				if (self->rows[y].request)
					start += self->row_spacing;
			}
		}
	}

	if (flags & PRIVATE_REBUILD_CHILDREN)
	{
		liwdg_widget_get_allocation (self, &rect);

		/* Set positions of widgets. */
		for (x = 0 ; x < self->width ; x++)
		{
			for (y = 0 ; y < self->height ; y++)
			{
				child = self->cells[x + y * self->width].child;
				if (child != NULL)
				{
					liwdg_widget_set_allocation (child,
						rect.x + self->cols[x].start,
						rect.y + self->rows[y].start,
						self->cols[x].allocation,
						self->rows[y].allocation);
				}
			}
		}
		for (child = self->children ; child != NULL ; child = child->below)
			liwdg_widget_child_request (self, child);
	}

	self->rebuilding = 0;
}
Пример #12
0
/**
 * \brief Subtracts a stick from this one.
 * \param self Terrain stick.
 * \param y Vertical offset of the subtracted stick, relative to the stick bottom.
 * \param h Height of the subtracted stick.
 * \param bot00 Bottom surface vertex offsets, relative to the bottom of the subtracted stick.
 * \param bot10 Bottom surface vertex offsets, relative to the bottom of the subtracted stick.
 * \param bot01 Bottom surface vertex offsets, relative to the bottom of the subtracted stick.
 * \param bot11 Bottom surface vertex offsets, relative to the bottom of the subtracted stick.
 * \param top00 Top surface vertex offsets, relative to the top of the subtracted stick.
 * \param top10 Top surface vertex offsets, relative to the top of the subtracted stick.
 * \param top01 Top surface vertex offsets, relative to the top of the subtracted stick.
 * \param top11 Top surface vertex offsets, relative to the top of the subtracted stick.
 * \return Number between 1-6, or 0 if ran out of memory.
 */
int liext_terrain_stick_subtract (
	LIExtTerrainStick* self,
	float              y,
	float              h,
	float              bot00,
	float              bot10,
	float              bot01,
	float              bot11,
	float              top00,
	float              top10,
	float              top01,
	float              top11)
{
	LIExtTerrainStick* stick;

	/* A) Is the stick completely below us?
	 *
	 * .....SSSSSSSSSS.....
	 * XXX??...............
	 */
	if (y + h <= 0.0f)
	{
		/* In this case, nothing is done. The vertices of the stick may still
		   overlap with us, but that should be handled by calling this
		   function for the previous stick. */
		return 1;
	}

	/* B) Is the stick completely above us?
	 *
	 * .....SSSSSSSSSS.....
	 * ...............??XXX
	 */
	if (y >= self->height)
	{
		/* In this case, only the vertices of the stick may change, but the
		   height and position remain the same. The vertices are clipped so
		   that they stay below the bottom of the subtracted stick, in the
		   manner similar to case E. */
		self->vertices[0][0].offset = LIMAT_MIN (self->vertices[0][0].offset, y + bot00 - self->height);
		self->vertices[1][0].offset = LIMAT_MIN (self->vertices[1][0].offset, y + bot10 - self->height);
		self->vertices[0][1].offset = LIMAT_MIN (self->vertices[0][1].offset, y + bot01 - self->height);
		self->vertices[1][1].offset = LIMAT_MIN (self->vertices[1][1].offset, y + bot11 - self->height);
		return 2;
	}

	/* C) Does the stick replace us completely?
	 *
	 * .....SSSSSSSSSS.....
	 * ..???XXXXXXXXXX???..
	 */
	if (y <= 0.0f && y + h >= self->height)
	{
		/* In this case, the stick would become degenerate since its height
		   becomes zero. We just indicate that with the return value. The
		   caller should decide what to do about degenerate sticks. */
		return 3;
	}

	/* D) Does the stick replace part of the bottom?
	 *
	 * .....SSSSSSSSSS.....
	 * ..???XXXXX..........
	 */
	if (y <= 0.0f)
	{
		/* In this case, the stick is shortened by the length of the
		   intersection. As we assume that this function has been called
		   for the previous stick, we also adjust the top surface so that
		   the vertices are above the vertices of the previous stick. */
		/* Note the the Y offset of this stick is considered to increase
		   the same amount as its height decreases, meaning that the vertices
		   technically remain at the same absolute positions. This means that
		   the original original absolute Y coordinates must be used when
		   fixing the offsets of the surface vertices. */
		self->vertices[0][0].offset = LIMAT_MAX (self->vertices[0][0].offset, y + h + top00 - self->height);
		self->vertices[1][0].offset = LIMAT_MAX (self->vertices[1][0].offset, y + h + top10 - self->height);
		self->vertices[0][1].offset = LIMAT_MAX (self->vertices[0][1].offset, y + h + top01 - self->height);
		self->vertices[1][1].offset = LIMAT_MAX (self->vertices[1][1].offset, y + h + top11 - self->height);
		self->height -= y + h;
		private_clamp_vertices (self, self->next);
		return 4;
	}

	/* E) Does the stick replace part of the top?
	 *
	 * .....SSSSSSSSSS.....
	 * ..........XXXXX???..
	 */
	if (y + h >= self->height)
	{
		/* In this case, the stick is shortened by the length of the
		   intersection. The vertices are first raised by the length of
		   the intersection so that they will be at the original absolute
		   Y offsets. Then, they are clamped to be below the bottom vertices
		   of the subtracted stick, similar to case B. */
		self->vertices[0][0].offset += self->height - y;
		self->vertices[1][0].offset += self->height - y;
		self->vertices[0][1].offset += self->height - y;
		self->vertices[1][1].offset += self->height - y;
		self->height = y;
		self->vertices[0][0].offset = LIMAT_MIN (self->vertices[0][0].offset, y + bot00 - self->height);
		self->vertices[1][0].offset = LIMAT_MIN (self->vertices[1][0].offset, y + bot10 - self->height);
		self->vertices[0][1].offset = LIMAT_MIN (self->vertices[0][1].offset, y + bot01 - self->height);
		self->vertices[1][1].offset = LIMAT_MIN (self->vertices[1][1].offset, y + bot11 - self->height);
		return 5;
	}

	/* F) Does the stick replace part of the middle?
	 *
	 * .....SSSSSSSSSS.....
	 * ........XXXX........
	 */
	if (y > 0.0f && y + h < self->height)
	{
		/* In this case, the stick is split in two parts so that the split
		   point lies at the start of the subtracted stick. The second part
		   of the stick is allocated and inserted after this stick. After
		   that, the vertices of this stick are modified similar to case E.
		   The vertices of the next stick are clamped so that they will be
		   above the top vertices of the subtracted stick. */
		stick = liext_terrain_stick_new (self->material, self->height - y - h);
		if (stick == NULL)
			return 0;
		stick->vertices[0][0].offset = LIMAT_MAX (self->vertices[0][0].offset, y + h + top00 - self->height);
		stick->vertices[1][0].offset = LIMAT_MAX (self->vertices[1][0].offset, y + h + top10 - self->height);
		stick->vertices[0][1].offset = LIMAT_MAX (self->vertices[0][1].offset, y + h + top01 - self->height);
		stick->vertices[1][1].offset = LIMAT_MAX (self->vertices[1][1].offset, y + h + top11 - self->height);
		private_clamp_vertices (stick, self->next);
		stick->next = self->next;
		self->next = stick;
		self->vertices[0][0].offset += self->height - y;
		self->vertices[1][0].offset += self->height - y;
		self->vertices[0][1].offset += self->height - y;
		self->vertices[1][1].offset += self->height - y;
		self->height = y;
		self->vertices[0][0].offset = LIMAT_MIN (self->vertices[0][0].offset, y + bot00 - self->height);
		self->vertices[1][0].offset = LIMAT_MIN (self->vertices[1][0].offset, y + bot10 - self->height);
		self->vertices[0][1].offset = LIMAT_MIN (self->vertices[0][1].offset, y + bot01 - self->height);
		self->vertices[1][1].offset = LIMAT_MIN (self->vertices[1][1].offset, y + bot11 - self->height);
		return 6;
	}

	/* Only the above six cases are possible. */
	lisys_assert (0);

	return 0;
}