static float private_calculate_collapse_cost_geom (
	LIMdlBuilder*    self,
	LIMdlBuilderLod* lod,
	LIMdlEdge*       edge)
{
	LIMdlVertex* v1 = self->model->vertices.array + edge->i1;
	LIMdlVertex* v2 = self->model->vertices.array + edge->i2;
	LIMatVector d_vtx = limat_vector_subtract (v1->coord, v2->coord);

	return limat_vector_dot (d_vtx, d_vtx);
}
示例#2
0
/**
 * \brief Creates a new polygon with three vertices.
 * \param ops Vertex access operations.
 * \param v0 Vertex.
 * \param v1 Vertex.
 * \param v2 Vertex.
 * \return New polygon or NULL.
 */
LIMatPolygon* limat_polygon_new_from_triangle (
	const LIMatVtxops* ops,
	const void*        v0,
	const void*        v1,
	const void*        v2)
{
	LIMatPolygon* self;
	LIMatVector coord0;
	LIMatVector coord1;
	LIMatVector coord2;

	/* Allocate self. */
	self = (LIMatPolygon*) lisys_calloc (1, sizeof (LIMatPolygon));
	if (self == NULL)
		return NULL;
	self->ops = ops;
	ops->getcoord (v0, &coord0);
	ops->getcoord (v1, &coord1);
	ops->getcoord (v2, &coord2);
	self->normal = limat_vector_normalize (
		limat_vector_cross (
		limat_vector_subtract (coord2, coord1),
		limat_vector_subtract (coord1, coord0)));

	/* Allocate vertices. */
	self->vertices.capacity = 3;
	self->vertices.count = 3;
	self->vertices.vertices = lisys_malloc (3 * ops->size);
	if (self->vertices.vertices == NULL)
	{
		lisys_free (self);
		return NULL;
	}
	memcpy ((char*) self->vertices.vertices + 0 * ops->size, v0, ops->size);
	memcpy ((char*) self->vertices.vertices + 1 * ops->size, v1, ops->size);
	memcpy ((char*) self->vertices.vertices + 2 * ops->size, v2, ops->size);

	return self;
}
static float private_calculate_collapse_cost_attr (
	LIMdlBuilder*    self,
	LIMdlBuilderLod* lod,
	LIMdlEdge*       edge)
{
	LIMdlVertex* v1 = self->model->vertices.array + edge->i1;
	LIMdlVertex* v2 = self->model->vertices.array + edge->i2;
	LIMatVector d_nml = limat_vector_subtract (v1->normal, v2->normal);
	float d_tex[2] = { v1->texcoord[0] - v2->texcoord[0], v1->texcoord[1] - v2->texcoord[1] };
	float d_col[4] = { v1->color[0] - v2->color[0], v1->color[1] - v2->color[1],
	                   v1->color[2] - v2->color[2], v1->color[3] - v2->color[3] };

	return limat_vector_dot (d_nml, d_nml) +
		(d_tex[0] * d_tex[0] + d_tex[1] * d_tex[1]) +
		(d_col[0] * d_col[0] + d_col[1] * d_col[1] + d_col[2] * d_col[2] + d_col[3] * d_col[3]);
}
示例#4
0
static void Camera_picking_ray (LIScrArgs* args)
{
	float fardist = 50.0f;
	float neardist = 0.0f;
	LIMatVector cursor;
	LIMatVector dir;
	LIMatVector ray0;
	LIMatVector ray1;
	LIExtCamera* self = args->self;

	/* Handle arguments. */
	liscr_args_gets_float (args, "far", &fardist);
	liscr_args_gets_float (args, "near", &neardist);
	if (!liscr_args_gets_vector (args, "cursor", &cursor))
	{
		cursor.x = self->view.viewport[0] + self->view.viewport[2] / 2.0f;
		cursor.y = self->view.viewport[1] + self->view.viewport[3] / 2.0f;
	}
	else
		cursor.y = self->view.viewport[3] - cursor.y - 1;

	/* Calculate ray vector. */
	cursor.z = 0.0f;
	if (!liext_camera_unproject (self, &cursor, &ray0))
		return;
	cursor.z = 1.0f;
	if (!liext_camera_unproject (self, &cursor, &ray1))
		return;
	dir = limat_vector_subtract (ray1, ray0);
	dir = limat_vector_normalize (dir);

	/* Apply near and far distances specified by the user. */
	ray1 = limat_vector_add (ray0, limat_vector_multiply (dir, fardist));
	ray0 = limat_vector_add (ray0, limat_vector_multiply (dir, neardist));
	liscr_args_seti_vector (args, &ray0);
	liscr_args_seti_vector (args, &ray1);
}
示例#5
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);
		}
示例#6
0
/**
 * \brief Casts a sphere against the stick and returns the hit fraction.
 *
 * FIXME: Doesn't work yet.
 *
 * \param self Terrain stick.
 * \param bot00 Bottom surface Y offset.
 * \param bot10 Bottom surface Y offset.
 * \param bot01 Bottom surface Y offset.
 * \param bot11 Bottom surface Y offset.
 * \param top00 Top surface Y offset.
 * \param top10 Top surface Y offset.
 * \param top01 Top surface Y offset.
 * \param top11 Top surface Y offset.
 * \param sphere_rel_cast_start Cast start position of the sphere, in grid units relative to the column origin.
 * \param sphere_rel_cast_end Cast end position of the sphere, in grid units relative to the column origin.
 * \param sphere_radius Sphere radius, in grid units.
 * \param result Return location for the hit fraction.
 * \return Nonzero if hit. Zero otherwise.
 */
int liext_terrain_stick_cast_sphere (
	const LIExtTerrainStick* self,
	float                    bot00,
	float                    bot10,
	float                    bot01,
	float                    bot11,
	float                    top00,
	float                    top10,
	float                    top01,
	float                    top11,
	const LIMatVector*       sphere_rel_cast_start,
	const LIMatVector*       sphere_rel_cast_end,
	float                    sphere_radius,
	LIExtTerrainCollision*   result)
{
	float min;
	float max;
	LIExtTerrainCollision best;
	LIExtTerrainCollision frac;
	LIMatVector v;
	LIMatVector vtx[3];
	LIMatVector point;
	LIMatPlane plane;

	frac.x = 0;
	frac.z = 0;
	best.fraction = LIMAT_INFINITE;
	v = limat_vector_subtract (*sphere_rel_cast_end, *sphere_rel_cast_start);

	/* Left. */
	limat_plane_init (&plane, -1.0f, 0.0f, 0.0f, 0.0f);
	frac.fraction = limat_plane_cast_sphere (&plane, sphere_rel_cast_start, sphere_rel_cast_end, sphere_radius);
	if (frac.fraction >= 0.0f && best.fraction > frac.fraction)
	{
		point = limat_vector_add (*sphere_rel_cast_start, limat_vector_multiply (v, frac.fraction));
		min = limat_mix (bot00, bot01, point.z);
		max = limat_mix (top00, top01, point.z);
		if (point.z >= 0 && point.z <= 1.0f && min <= point.y && point.y <= max)
		{
			/* Direct face hit. */
			best = frac;
			best.normal = limat_vector_init (-1.0f, 0.0f, 0.0f);
			best.point = limat_vector_init (0.0f, point.y, point.z);
		}
		else
		{
			/* Potential edge hit. */
			point.z = LIMAT_CLAMP (point.z, 0.0f, 1.0f);
			min = limat_mix (bot00, bot01, point.z);
			max = limat_mix (top00, top01, point.z);
			point.y = LIMAT_CLAMP (point.y, min, max);
			if (limat_intersect_point_cast_sphere (&point, sphere_rel_cast_start, sphere_rel_cast_end, sphere_radius, &frac.fraction))
			{
				if (frac.fraction >= 0.0f && best.fraction > frac.fraction)
				{
					best = frac;
					best.normal = limat_vector_init (-1.0f, 0.0f, 0.0f);
					best.point = limat_vector_init (0.0f, point.y, point.z);
				}
			}
		}
	}

	/* Right. */
	limat_plane_init (&plane, 1.0f, 0.0f, 0.0f, 1.0f);
	frac.fraction = limat_plane_cast_sphere (&plane, sphere_rel_cast_start, sphere_rel_cast_end, sphere_radius);
	if (frac.fraction >= 0.0f && best.fraction > frac.fraction)
	{
		point = limat_vector_add (*sphere_rel_cast_start, limat_vector_multiply (v, frac.fraction));
		min = limat_mix (bot10, bot11, point.z);
		max = limat_mix (top10, top11, point.z);
		if (point.z >= 0 && point.z <= 1.0f && min <= point.y && point.y <= max)
		{
			/* Direct face hit. */
			best = frac;
			best.normal = limat_vector_init (1.0f, 0.0f, 0.0f);
			best.point = limat_vector_init (1.0f, point.y, point.z);
		}
		else
		{
			/* Potential edge hit. */
			point.z = LIMAT_CLAMP (point.z, 0.0f, 1.0f);
			min = limat_mix (bot10, bot11, point.z);
			max = limat_mix (top10, top11, point.z);
			point.y = LIMAT_CLAMP (point.y, min, max);
			if (limat_intersect_point_cast_sphere (&point, sphere_rel_cast_start, sphere_rel_cast_end, sphere_radius, &frac.fraction))
			{
				if (frac.fraction >= 0.0f && best.fraction > frac.fraction)
				{
					best = frac;
					best.normal = limat_vector_init (1.0f, 0.0f, 0.0f);
					best.point = limat_vector_init (1.0f, point.y, point.z);
				}
			}
		}
	}

	/* Front. */
	limat_plane_init (&plane, 0.0f, 0.0f, -1.0f, 0.0f);
	frac.fraction = limat_plane_cast_sphere (&plane, sphere_rel_cast_start, sphere_rel_cast_end, sphere_radius);
	if (frac.fraction >= 0.0f && best.fraction > frac.fraction)
	{
		point = limat_vector_add (*sphere_rel_cast_start, limat_vector_multiply (v, frac.fraction));
		min = limat_mix (bot00, bot10, point.z);
		max = limat_mix (top00, top10, point.z);
		if (point.x >= 0 && point.x <= 1.0f && min <= point.y && point.y <= max)
		{
			/* Direct face hit. */
			best = frac;
			best.normal = limat_vector_init (0.0f, 0.0f, -1.0f);
			best.point = limat_vector_init (point.x, point.y, 0.0f);
		}
		else
		{
			/* Potential edge hit. */
			point.x = LIMAT_CLAMP (point.x, 0.0f, 1.0f);
			min = limat_mix (bot00, bot10, point.z);
			max = limat_mix (top00, top10, point.z);
			point.y = LIMAT_CLAMP (point.y, min, max);
			if (limat_intersect_point_cast_sphere (&point, sphere_rel_cast_start, sphere_rel_cast_end, sphere_radius, &frac.fraction))
			{
				if (frac.fraction >= 0.0f && best.fraction > frac.fraction)
				{
					best = frac;
					best.normal = limat_vector_init (0.0f, 0.0f, -1.0f);
					best.point = limat_vector_init (point.x, point.y, 0.0f);
				}
			}
		}
	}

	/* Back. */
	limat_plane_init (&plane, 0.0f, 0.0f, 1.0f, 1.0f);
	frac.fraction = limat_plane_cast_sphere (&plane, sphere_rel_cast_start, sphere_rel_cast_end, sphere_radius);
	if (frac.fraction >= 0.0f && best.fraction > frac.fraction)
	{
		point = limat_vector_add (*sphere_rel_cast_start, limat_vector_multiply (v, frac.fraction));
		min = limat_mix (bot01, bot11, point.z);
		max = limat_mix (top01, top11, point.z);
		if (point.x >= 0 && point.x <= 1.0f && min <= point.y && point.y <= max)
		{
			/* Direct face hit. */
			best = frac;
			best.normal = limat_vector_init (0.0f, 0.0f, 1.0f);
			best.point = limat_vector_init (point.x, point.y, 1.0f);
		}
		else
		{
			/* Potential edge hit. */
			point.x = LIMAT_CLAMP (point.x, 0.0f, 1.0f);
			min = limat_mix (bot01, bot11, point.z);
			max = limat_mix (top01, top11, point.z);
			point.y = LIMAT_CLAMP (point.y, min, max);
			if (limat_intersect_point_cast_sphere (&point, sphere_rel_cast_start, sphere_rel_cast_end, sphere_radius, &frac.fraction))
			{
				if (frac.fraction >= 0.0f && best.fraction > frac.fraction)
				{
					best = frac;
					best.normal = limat_vector_init (0.0f, 0.0f, 1.0f);
					best.point = limat_vector_init (point.x, point.y, 1.0f);
				}
			}
		}
	}

	/* Bottom. */
	vtx[2] = limat_vector_init (0.0f, bot00, 0.0f);
	vtx[1] = limat_vector_init (0.0f, bot01, 1.0f);
	vtx[0] = limat_vector_init (1.0f, bot11, 1.0f);
	limat_plane_init_from_points (&plane, vtx + 0, vtx + 1, vtx + 2);
	lisys_assert (plane.y < 0.0f);
	frac.fraction = limat_plane_cast_sphere (&plane, sphere_rel_cast_start, sphere_rel_cast_end, sphere_radius);
	if (frac.fraction >= 0.0f && best.fraction > frac.fraction)
	{
		point = limat_vector_add (*sphere_rel_cast_start, limat_vector_multiply (v, frac.fraction));
		if (point.x >= 0.0f && point.z >= 0.0f && point.z <= 1.0f && point.z >= point.x)
		{
			/* Direct face hit. */
			best = frac;
			limat_plane_get_normal (&plane, &best.normal);
			best.point = limat_vector_add (point, limat_vector_multiply (best.normal, -sphere_radius));
		}
		else
		{
			/* TODO: Potential edge hit. */
		}
	}
	vtx[2] = limat_vector_init (0.0f, bot00, 0.0f);
	vtx[1] = limat_vector_init (1.0f, bot11, 1.0f);
	vtx[0] = limat_vector_init (1.0f, bot10, 0.0f);
	limat_plane_init_from_points (&plane, vtx + 0, vtx + 1, vtx + 2);
	lisys_assert (plane.y < 0.0f);
	frac.fraction = limat_plane_cast_sphere (&plane, sphere_rel_cast_start, sphere_rel_cast_end, sphere_radius);
	if (frac.fraction >= 0.0f && best.fraction > frac.fraction)
	{
		point = limat_vector_add (*sphere_rel_cast_start, limat_vector_multiply (v, frac.fraction));
		if (point.x >= 0.0f && point.z >= 0.0f && point.x <= 1.0f && point.x >= point.z)
		{
			/* Direct face hit. */
			best = frac;
			limat_plane_get_normal (&plane, &best.normal);
			best.point = limat_vector_add (point, limat_vector_multiply (best.normal, -sphere_radius));
		}
		else
		{
			/* TODO: Potential edge hit. */
		}
	}

	/* Top. */
	vtx[2] = limat_vector_init (0.0f, top00, 0.0f);
	vtx[1] = limat_vector_init (1.0f, top10, 0.0f);
	vtx[0] = limat_vector_init (1.0f, top11, 1.0f);
	limat_plane_init_from_points (&plane, vtx + 0, vtx + 1, vtx + 2);
	frac.fraction = limat_plane_cast_sphere (&plane, sphere_rel_cast_start, sphere_rel_cast_end, sphere_radius);
	lisys_assert (plane.y > 0.0f);
	if (frac.fraction >= 0.0f && best.fraction > frac.fraction)
	{
		point = limat_vector_add (*sphere_rel_cast_start, limat_vector_multiply (v, frac.fraction));
		if (point.x >= 0.0f && point.z >= 0.0f && point.x <= 1.0f && point.x >= point.z)
		{
			/* Direct face hit. */
			best = frac;
			limat_plane_get_normal (&plane, &best.normal);
			best.point = limat_vector_add (point, limat_vector_multiply (best.normal, -sphere_radius));
		}
		else
		{
			/* TODO: Potential edge hit. */
		}
	}
	vtx[2] = limat_vector_init (0.0f, top00, 0.0f);
	vtx[1] = limat_vector_init (1.0f, top11, 1.0f);
	vtx[0] = limat_vector_init (0.0f, top10, 1.0f);
	limat_plane_init_from_points (&plane, vtx + 0, vtx + 1, vtx + 2);
	lisys_assert (plane.y > 0.0f);
	frac.fraction = limat_plane_cast_sphere (&plane, sphere_rel_cast_start, sphere_rel_cast_end, sphere_radius);
	if (frac.fraction >= 0.0f && best.fraction > frac.fraction)
	{
		point = limat_vector_add (*sphere_rel_cast_start, limat_vector_multiply (v, frac.fraction));
		if (point.x >= 0.0f && point.z >= 0.0f && point.z <= 1.0f && point.z >= point.x)
		{
			/* Direct face hit. */
			best = frac;
			limat_plane_get_normal (&plane, &best.normal);
			best.point = limat_vector_add (point, limat_vector_multiply (best.normal, -sphere_radius));
		}
		else
		{
			/* TODO: Potential edge hit. */
		}
	}

	/* Check whether a collision occurred. */
	if (best.fraction > 1.0f || best.fraction == LIMAT_INFINITE)
		return 0;
	*result = best;
	return 1;
}
示例#7
0
/**
 * \brief Splits the polygon in two parts by the plane.
 *
 * If the algorithm runs out of memory, zero is returned and at least one
 * of the resulting polygons misses one or more vertices.
 *
 * Uses the Sutherland-Hodgman algorithm.
 *
 * \param self Polygon.
 * \param plane Plane.
 * \param front Return location for the polygon on the front side.
 * \param back Return location for the polygon on the back side.
 * \return Nonzero on success.
 */
int limat_polygon_split (
	const LIMatPolygon* self,
	const LIMatPlane*   plane,
	LIMatPolygon*       front,
	LIMatPolygon*       back)
{
	int i;
	int ret = 1;
	int curr_in;
	int prev_in;
	float frac;
	void* curr_vtx;
	void* prev_vtx;
	void* tmp_vtx;
	LIMatVector curr_coord;
	LIMatVector prev_coord;
	LIMatVector tmp_coord = { 0.0f, 0.0f, 0.0f };
	const LIMatVtxops* ops = self->ops;

	/* Initialize. */
	front->data = self->data;
	front->normal = self->normal;
	back->data = self->data;
	back->normal = self->normal;
	front->vertices.count = 0;
	back->vertices.count = 0;
	if (!self->vertices.count)
		return 1;
	prev_vtx = (char*) self->vertices.vertices + (self->vertices.count - 1) * ops->size;
	ops->getcoord (prev_vtx, &prev_coord);
	prev_in = limat_plane_signed_distance_to_point (plane, &prev_coord) >= 0.0f;
	tmp_vtx = lisys_malloc (ops->size);
	if (tmp_vtx == NULL)
		return 0;

	/* Calculate the vertices of the new polygons. */
	for (i = 0 ; i < self->vertices.count ; i++)
	{
		curr_vtx = (char*) self->vertices.vertices + i * ops->size;
		ops->getcoord (curr_vtx, &curr_coord);
		curr_in = limat_plane_signed_distance_to_point (plane, &curr_coord) >= 0.0f;
		if (curr_in && !prev_in)
		{
			/* Back to front. */
			limat_plane_intersects_segment (plane, &prev_coord, &curr_coord, &tmp_coord);
			frac = limat_vector_get_length (limat_vector_subtract (prev_coord, tmp_coord)) /
			       limat_vector_get_length (limat_vector_subtract (prev_coord, curr_coord));
			ops->setcoord (tmp_vtx, &tmp_coord);
			ops->interpolate (curr_vtx, prev_vtx, frac, tmp_vtx);
			ret &= limat_polygon_add_vertices (back, tmp_vtx, 1);
			ret &= limat_polygon_add_vertices (front, tmp_vtx, 1);
			ret &= limat_polygon_add_vertices (front, curr_vtx, 1);
		}
		else if (!curr_in && prev_in)
		{
			/* Front to back. */
			limat_plane_intersects_segment (plane, &prev_coord, &curr_coord, &tmp_coord);
			frac = limat_vector_get_length (limat_vector_subtract (prev_coord, tmp_coord)) /
			       limat_vector_get_length (limat_vector_subtract (prev_coord, curr_coord));
			ops->setcoord (tmp_vtx, &tmp_coord);
			ops->interpolate (curr_vtx, prev_vtx, frac, tmp_vtx);
			ret &= limat_polygon_add_vertices (front, tmp_vtx, 1);
			ret &= limat_polygon_add_vertices (back, tmp_vtx, 1);
			ret &= limat_polygon_add_vertices (back, curr_vtx, 1);
		}
		else if (curr_in)
		{
			/* Stay in front. */
			ret &= limat_polygon_add_vertices (front, curr_vtx, 1);
		}
		else
		{
			/* Stay behind. */
			ret &= limat_polygon_add_vertices (back, curr_vtx, 1);
		}
		prev_coord = curr_coord;
		prev_vtx = curr_vtx;
		prev_in = curr_in;
	}

	lisys_free (tmp_vtx);

	return ret;
}