/** * \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); }
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); }
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); }
/** * \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)); }
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); }
/** * \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; }