示例#1
0
/**
 * \brief Calculates the face normal of the stick when one vertex is overridden.
 * \param self Terrain stick.
 * \param vertex_x Index of the overridden vertex.
 * \param vertex_y Index of the overridden vertex.
 * \param vertex_offset Overridden vertex offset.
 * \param result Return location for the vector.
 */
void liext_terrain_stick_get_normal_override (
	const LIExtTerrainStick* self,
	int                      vertex_x,
	int                      vertex_y,
	float                    vertex_offset,
	LIMatVector*             result)
{
	float offsets[2][2];
	LIMatVector v1;
	LIMatVector v2;
	LIMatVector v3;
	LIMatVector v4;
	LIMatVector n1;
	LIMatVector n2;

	offsets[0][0] = self->vertices[0][0].offset;
	offsets[1][0] = self->vertices[1][0].offset;
	offsets[0][1] = self->vertices[0][1].offset;
	offsets[1][1] = self->vertices[1][1].offset;
	offsets[vertex_x][vertex_y] = vertex_offset;
	v1 = limat_vector_init (1.0f, offsets[1][0] - offsets[0][0], 0.0f);
	v2 = limat_vector_init (0.0f, offsets[0][1] - offsets[0][0], 1.0f);
	v3 = limat_vector_init (-1.0f, offsets[1][0] - offsets[1][1], 0.0f);
	v4 = limat_vector_init (0.0f, offsets[0][1] - offsets[1][1], -1.0f);
	n1 = limat_vector_normalize (limat_vector_cross (v2, v1));
	n2 = limat_vector_normalize (limat_vector_cross (v4, v3));
	*result = limat_vector_multiply (limat_vector_add (n1, n2), 0.5f);
}
示例#2
0
static void Model_get_center_offset (LIScrArgs* args)
{
	LIMatVector ctr;
	LIMdlModel* self;

	self = args->self;
	ctr = limat_vector_add (self->bounds.min, self->bounds.max);
	ctr = limat_vector_multiply (ctr, 0.5f);
	liscr_args_seti_vector (args, &ctr);
}
示例#3
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);
}
示例#4
0
/**
 * \brief Calculates the face normal of the stick.
 * \param self Terrain stick.
 * \param result Return location for the vector.
 */
void liext_terrain_stick_get_normal (
	const LIExtTerrainStick* self,
	LIMatVector*             result)
{
	LIMatVector v1;
	LIMatVector v2;
	LIMatVector v3;
	LIMatVector v4;
	LIMatVector n1;
	LIMatVector n2;

	v1 = limat_vector_init (1.0f, self->vertices[1][0].offset - self->vertices[0][0].offset, 0.0f);
	v2 = limat_vector_init (0.0f, self->vertices[0][1].offset - self->vertices[0][0].offset, 1.0f);
	v3 = limat_vector_init (-1.0f, self->vertices[0][1].offset - self->vertices[1][1].offset, 0.0f);
	v4 = limat_vector_init (0.0f, self->vertices[1][0].offset - self->vertices[1][1].offset, -1.0f);
	n1 = limat_vector_normalize (limat_vector_cross (v2, v1));
	n2 = limat_vector_normalize (limat_vector_cross (v4, v3));
	*result = limat_vector_normalize (limat_vector_add (n1, n2));
}
示例#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;
}