static int private_add_shape ( LIPhyShape* self, btConvexShape* shape, const LIMatTransform* transform) { btVector3 p(0.0f, 0.0f, 0.0f); btQuaternion r(0.0f, 0.0f, 0.0f, 1.0f); btTransform center_of_mass(r, btVector3 ( self->center_of_mass.x, self->center_of_mass.y, self->center_of_mass.z)); if (transform != NULL) { p = btVector3 (transform->position.x, transform->position.y, transform->position.z); r = btQuaternion(transform->rotation.x, transform->rotation.y, transform->rotation.z, transform->rotation.w); } try { self->shape->addChildShape (center_of_mass.inverse() * btTransform (r, p), shape); } catch (...) { return 0; } /* Update the bounding box. */ btVector3 min; btVector3 max; self->shape->getAabb(btTransform::getIdentity (), min, max); self->bounds.min = limat_vector_init (min[0], min[1], min[2]); self->bounds.max = limat_vector_init (max[0], max[1], max[2]); return 1; }
/** * \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); }
/** * \brief Resets the vertices to the flat orientation. * \param self Terrain stick. */ void liext_terrain_stick_reset_vertices ( LIExtTerrainStick* self) { memset (self->vertices, 0, sizeof (self->vertices)); self->vertices[0][0].normal = limat_vector_init (0.0f, 1.0f, 0.0f); self->vertices[1][0].normal = limat_vector_init (0.0f, 1.0f, 0.0f); self->vertices[0][1].normal = limat_vector_init (0.0f, 1.0f, 0.0f); self->vertices[1][1].normal = limat_vector_init (0.0f, 1.0f, 0.0f); }
static void Heightmap_new (LIScrArgs* args) { int i; int size; float spacing; float scaling; LIExtHeightmap* heightmap; LIExtHeightmapModule* module; LIImgImage* image; LIMatVector position; LIScrData* data; /* Get arguments. */ module = liscr_script_get_userdata (args->script, LIEXT_SCRIPT_HEIGHTMAP); if (!liscr_args_geti_vector (args, 0, &position)) position = limat_vector_init (0.0f, 0.0f, 0.0f); if (!liscr_args_geti_int (args, 1, &size)) size = 33; else if (size < 0) size = 0; if (!liscr_args_geti_float (args, 2, &spacing)) spacing = 1.0f; else if (spacing <= 0.0f) spacing = 1.0f; if (!liscr_args_geti_float (args, 3, &scaling)) scaling = 1.0f; else if (scaling <= 0.0f) scaling = 1.0f; if (liscr_args_geti_data (args, 4, LIEXT_SCRIPT_IMAGE, &data)) image = liscr_data_get_data (data); else image = NULL; /* Ensure that the size is valid. */ for (i = 32 ; i < 65536 ; i *= 2) { if (size == i + 1) break; } if (size != i + 1) { lisys_error_set (EINVAL, "invalid heightmap size"); lisys_error_report (); return; } /* Allocate the heightmap. */ heightmap = liext_heightmap_new (module, image, &position, size, spacing, scaling); if (heightmap == NULL) return; /* Allocate the userdata. */ data = liscr_data_new (args->script, args->lua, heightmap, LIEXT_SCRIPT_HEIGHTMAP, liext_heightmap_free); if (data == NULL) { liext_heightmap_free (heightmap); return; } liscr_args_seti_stack (args); }
static void Widgets_find_widget (LIScrArgs* args) { int x; int y; LIExtModule* module; LIWdgWidget* widget; LIMatVector vector; LIScrData* data; if (!liscr_args_gets_vector (args, "point", &vector) && !liscr_args_geti_vector (args, 0, &vector)) { SDL_GetMouseState (&x, &y); vector = limat_vector_init (x, y, 0.0f); } module = liscr_script_get_userdata (args->script, LIEXT_SCRIPT_WIDGETS); widget = liwdg_manager_find_widget_by_point (module->widgets, (int) vector.x, (int) vector.y); if (widget == NULL) return; data = liwdg_widget_get_script (widget); if (data == NULL) return; liscr_args_seti_data (args, data); }
static void Widget_get_offset (LIScrArgs* args) { int x; int y; LIMatVector v; liwdg_widget_get_offset (args->self, &x, &y); v = limat_vector_init (x, y, 0.0f); liscr_args_seti_vector (args, &v); }
/** * \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)); }
/** * \brief Creates a new terrain stick. * \return Terrain stick, or NULL. */ LIExtTerrainStick* liext_terrain_stick_new ( int material, float height) { LIExtTerrainStick* self; /* Allocate self. */ self = lisys_calloc (1, sizeof (LIExtTerrainStick)); if (self == NULL) return NULL; self->material = material; self->height = height; self->vertices[0][0].normal = limat_vector_init (0.0f, 1.0f, 0.0f); self->vertices[1][0].normal = limat_vector_init (0.0f, 1.0f, 0.0f); self->vertices[0][1].normal = limat_vector_init (0.0f, 1.0f, 0.0f); self->vertices[1][1].normal = limat_vector_init (0.0f, 1.0f, 0.0f); return self; }
static void Terrain_build_chunk_model (LIScrArgs* args) { int grid_x; int grid_z; LIExtTerrain* self; LIExtTerrainModule* module; LIMatVector offset; LIMdlModel* model; LIScrData* data; /* Get the arguments. */ self = args->self; module = liscr_script_get_userdata (args->script, LIEXT_SCRIPT_TERRAIN); 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_vector (args, 2, &offset)) offset = limat_vector_init (grid_x * self->grid_size, 0.0f, grid_z * self->grid_size); /* Build the model. */ model = liext_terrain_build_chunk_model (self, grid_x, grid_z, &offset); if (model == NULL) return; /* Copy the model. */ model = limdl_model_new_copy (model, 0); if (model == NULL) return; /* Allocate the unique ID. */ if (!limdl_manager_add_model (module->program->models, model)) { limdl_model_free (model); return; } /* Allocate the userdata. */ data = liscr_data_new (args->script, args->lua, model, LISCR_SCRIPT_MODEL, limdl_manager_free_model); if (data == NULL) { limdl_model_free (model); return; } liscr_args_seti_stack (args); }
void liwdg_widget_set_allocation ( LIWdgWidget* self, int x, int y, int w, int h) { LIMatVector pos; if (self->allocation.x != x || self->allocation.y != y || self->allocation.width != w || self->allocation.height != h) { self->allocation.x = x; self->allocation.y = y; self->allocation.width = w; self->allocation.height = h; pos = limat_vector_init (x, y, 0.0f); liren_render_overlay_set_position (self->manager->render, self->overlay, &pos); private_rebuild (self, PRIVATE_REBUILD_REQUEST | PRIVATE_REBUILD_HORZ | PRIVATE_REBUILD_VERT | PRIVATE_REBUILD_CHILDREN); } }
static void Voxel_copy_region (LIScrArgs* args) { int i; int length; int sector; int offset[3]; LIArcPacket* packet; LIScrData* data; LIExtModule* module; LIMatVector point; LIMatVector size; LIVoxVoxel* result; /* Get region offset and size. */ module = liscr_script_get_userdata (args->script, LIEXT_SCRIPT_VOXEL); if (liscr_args_gets_int (args, "sector", §or)) { lialg_sectors_index_to_offset (module->program->sectors, sector, offset + 0, offset + 1, offset + 2); point = limat_vector_init (offset[0], offset[1], offset[2]); point = limat_vector_multiply (point, module->voxels->tiles_per_line); size.x = size.y = size.z = module->voxels->tiles_per_line; length = module->voxels->tiles_per_sector; } else if (liscr_args_gets_vector (args, "point", &point) && liscr_args_gets_vector (args, "size", &size)) { if (point.x < 0.0f || point.y < 0.0f || point.z < 0.0f || size.x < 1.0f || size.y < 1.0f || size.z < 1.0f) return; length = (int) size.x * (int) size.y * (int) size.z; } else return; /* Read voxel data. */ result = lisys_calloc (length, sizeof (LIVoxVoxel)); if (result == NULL) return; livox_manager_copy_voxels (module->voxels, (int) point.x, (int) point.y, (int) point.z, (int) size.x, (int) size.y, (int) size.z, result); /* Create a packet writer. */ packet = liarc_packet_new_writable (0); if (packet == NULL) { lisys_free (result); return; } /* Write the dimensions. */ if (!liarc_writer_append_uint32 (packet->writer, (int) size.x) || !liarc_writer_append_uint32 (packet->writer, (int) size.y) || !liarc_writer_append_uint32 (packet->writer, (int) size.z)) { lisys_free (result); return; } /* Write voxel data. */ for (i = 0 ; i < length ; i++) { if (!livox_voxel_write (result + i, packet->writer)) { lisys_free (result); return; } } lisys_free (result); /* Return data. */ data = liscr_data_new (args->script, args->lua, packet, LISCR_SCRIPT_PACKET, liarc_packet_free); if (data == NULL) { liarc_packet_free (packet); return; } liscr_args_seti_stack (args); }
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; }