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