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