void collision_response_slide(void* x, vec3* position, vec3* velocity, collision (*colfunc)(void* x, vec3* pos, vec3* vel) ) { collision col = colfunc(x, position, velocity); int count = 0; while (col.collided) { //renderer_add(x, render_object_line(*position, vec3_add(*position, col.norm), vec3_red(), count+1)); if (count++ == 100) { *velocity = vec3_zero(); break; } vec3 fwrd = vec3_mul(*velocity, col.time); float len = max(vec3_length(fwrd) - 0.001, 0.0) / vec3_length(fwrd); vec3 move = vec3_add(*position, vec3_mul(fwrd, len)); vec3 proj = vec3_project(vec3_mul(*velocity, (1.0-col.time)), col.norm); //renderer_add(x, render_object_line(*position, vec3_add(*position, vec3_normalize(proj)), vec3_green(), count+1)); *position = move; *velocity = proj; col = colfunc(x, position, velocity); } *position = vec3_add(*position, *velocity); }
void object3d_render(screen_buffer *screen, object3d *obj) { vec3 a, b, c; int i; for (i = 0; i < obj->vertex_cnt; i+=3) { a = obj->vertices[i][0]; b = obj->vertices[i+1][0]; c = obj->vertices[i+2][0]; vec3_rotate(&a, &obj->rotation); vec3_mul(&a, &obj->scale); vec3_add(&a, &obj->position); vec3_rotate(&b, &obj->rotation); vec3_mul(&b, &obj->scale); vec3_add(&b, &obj->position); vec3_rotate(&c, &obj->rotation); vec3_mul(&c, &obj->scale); vec3_add(&c, &obj->position); rasterize_vertex(screen, &a, &b, &c); //line(screen, a.x, a.y, b.x, b.y, '+'); //line(screen, b.x, b.y, c.x, c.y, '+'); //line(screen, c.x, c.y, a.x, a.y, '+'); } }
/* FIXME: Broken, entities "disappears" */ vec3 vec3_slerp(const vec3 *v, const vec3 *u, float t) { const double DOT_THRESHOLD = 0.9995; float dot; float theta; vec3 v0, u0, w; v0 = *v; u0 = *u; dot = vec3_dot(&v0, &u0); if (dot > DOT_THRESHOLD) { vec3_lerp(&v0, &u0, t); } theta = t * acosf(dot); w = vec3_mul(v, dot); w = vec3_sub(u, &w); //w = vector_normalize(&w); v0 = vec3_mul(&v0, cosf(theta)); u0 = vec3_mul(&u0, sinf(theta)); w = vec3_add(&v0, &u0); return w; }
static box_t get_box(const vec3_t *p0, const vec3_t *p1, const vec3_t *n, float r, const plane_t *plane) { mat4_t rot; box_t box; if (p1 == NULL) { box = bbox_from_extents(*p0, r, r, r); box = box_swap_axis(box, 2, 0, 1); return box; } if (r == 0) { box = bbox_grow(bbox_from_points(*p0, *p1), 0.5, 0.5, 0.5); // Apply the plane rotation. rot = plane->mat; rot.vecs[3] = vec4(0, 0, 0, 1); mat4_imul(&box.mat, rot); return box; } // Create a box for a line: int i; const vec3_t AXES[] = {vec3(1, 0, 0), vec3(0, 1, 0), vec3(0, 0, 1)}; box.mat = mat4_identity; box.p = vec3_mix(*p0, *p1, 0.5); box.d = vec3_sub(*p1, box.p); for (i = 0; i < 3; i++) { box.w = vec3_cross(box.d, AXES[i]); if (vec3_norm2(box.w) > 0) break; } if (i == 3) return box; box.w = vec3_mul(vec3_normalized(box.w), r); box.h = vec3_mul(vec3_normalized(vec3_cross(box.d, box.w)), r); return box; }
collision sphere_collide_face(sphere s, vec3 v, ctri ct) { //if (unlikely(sphere_intersects_face(s, ct.a, ct.b, ct.c, ct.norm))) { error("Collision Sphere Inside Mesh Face!"); } float angle = vec3_dot(ct.norm, v); float dist = vec3_dot(ct.norm, vec3_sub(s.center, ct.a)); float t0 = ( s.radius - dist) / angle; float t1 = (-s.radius - dist) / angle; float t = FLT_MAX; if (between_or(t0, 0, 1) && between_or(t1, 0, 1)) { t = min(t0, t1); } else if (between_or(t0, 0, 1)) { t = t0; } else if (between_or(t1, 0, 1)) { t = t1; } else { return collision_none(); } vec3 cpoint = vec3_add(s.center, vec3_mul(v, t)); vec3 spoint = vec3_add(cpoint, vec3_mul(ct.norm, -s.radius)); if (!point_inside_triangle(spoint, ct.a, ct.b, ct.c)) { return collision_none(); } else { return collision_new(t, spoint, ct.norm); } }
void matrix3_scale(struct matrix3 *dst, const struct matrix3 *m, const struct vec3 *v) { vec3_mul(&dst->x, &m->x, v); vec3_mul(&dst->y, &m->y, v); vec3_mul(&dst->z, &m->z, v); vec3_mul(&dst->t, &m->t, v); }
void scotland_update() { camera* cam = entity_get("camera"); light* sun = entity_get("sun"); static_object* skydome = entity_get("skydome"); landscape* world = entity_get("world"); sun_orbit += frame_time() * 0.01; sun->position.x = 512 + sin(sun_orbit) * 512; sun->position.y = cos(sun_orbit) * 512; sun->position.z = 512; sun->target = vec3_new(512, 0, 512); if (w_held || s_held) { vec3 cam_dir = vec3_normalize(vec3_sub(cam->target, cam->position)); float speed = 0.5; if (!freecam) speed = 0.05; if (w_held) { cam->position = vec3_add(cam->position, vec3_mul(cam_dir, speed)); } if (s_held) { cam->position = vec3_sub(cam->position, vec3_mul(cam_dir, speed)); } if (!freecam) { float height = terrain_height(asset_hndl_ptr(world->terrain), vec2_new(cam->position.x, cam->position.z)); cam->position.y = height + 1; } cam->target = vec3_add(cam->position, cam_dir); } Uint8 keystate = SDL_GetMouseState(NULL, NULL); if(keystate & SDL_BUTTON(1)){ float a1 = -(float)mouse_x * 0.005; float a2 = (float)mouse_y * 0.005; vec3 cam_dir = vec3_normalize(vec3_sub(cam->target, cam->position)); cam_dir.y += -a2; vec3 side_dir = vec3_normalize(vec3_cross(cam_dir, vec3_new(0,1,0))); cam_dir = vec3_add(cam_dir, vec3_mul(side_dir, -a1)); cam_dir = vec3_normalize(cam_dir); cam->target = vec3_add(cam->position, cam_dir); } mouse_x = 0; mouse_y = 0; ui_button* framerate = ui_elem_get("framerate"); ui_button_set_label(framerate, frame_rate_string()); }
static void vertex_shader(const vertex_t *in, vs_to_fs_t *out) { // decode vertex const vec3_t pc = { 0.5f, 0.5f, 0.5f }, nc = { 1.0f, 1.0f, 1.0f }; const float pi = 1.0f / 65535.0f, ni = 2.0f / 255.0f, uvi = 1.0f / 255.0f; vec3_t position = vec3_sub(vec3_mul(vec3(in->x, in->y, in->z), pi), pc); vec3_t normal = vec3_sub(vec3_mul(vec3(in->nx, in->ny, in->nz), ni), nc); vec2_t uv = vec2_mul(vec2(in->u, in->v), uvi); // transform vertex out->position = mat4_transform_position(mvp, position); out->normal = mat4_transform_vector(mv, normal); out->uv = uv; }
collision sphere_collide_edge(sphere s, vec3 v, vec3 e0, vec3 e1) { //Wif (unlikely(!line_outside_sphere(s, e0, e1))) { error("Collision Sphere Inside Mesh Edge!"); } vec3 x0 = vec3_sub(e0, s.center); vec3 x1 = vec3_sub(e1, s.center); vec3 d = vec3_sub(x1, x0); float dlen = vec3_length_sqrd(d); float vlen = vec3_length_sqrd(v); float xlen = vec3_length_sqrd(x0); float A = dlen * -vlen + vec3_dot(d, v) * vec3_dot(d, v); float B = dlen * 2 * vec3_dot(v, x0) - 2 * vec3_dot(d, v) * vec3_dot(d, x0); float C = dlen * (s.radius * s.radius - xlen) + vec3_dot(d, x0) * vec3_dot(d, x0); float t0, t1, t; if (!quadratic(A, B, C, &t0, &t1)) { return collision_none(); } if (between_or(t0, 0, 1) && between_or(t1, 0, 1)) { t = min(t0, t1); } else if (between_or(t0, 0, 1)) { t = t0; } else if (between_or(t1, 0, 1)) { t = t1; } else { return collision_none(); } float range = (vec3_dot(d, v) * t - vec3_dot(d, x0)) / dlen; if (!between_or(range, 0, 1)) { return collision_none(); } else { vec3 spoint = vec3_add(e0, vec3_mul(d, range)); return collision_new(t, spoint, vec3_normalize(vec3_sub(s.center, spoint))); } }
void camera_control_orbit(camera* c, SDL_Event e) { float a1 = 0; float a2 = 0; vec3 axis; vec3 translation = c->target; c->position = vec3_sub(c->position, translation); c->target = vec3_sub(c->target, translation); switch(e.type) { case SDL_MOUSEMOTION: if (e.motion.state & SDL_BUTTON(1)) { a1 = e.motion.xrel * -0.005; a2 = e.motion.yrel * 0.005; c->position = mat3_mul_vec3(mat3_rotation_y( a1 ), c->position ); axis = vec3_normalize(vec3_cross( vec3_sub(c->position, c->target) , vec3_new(0,1,0) )); c->position = mat3_mul_vec3(mat3_rotation_angle_axis(a2, axis), c->position ); } break; case SDL_MOUSEWHEEL: c->position = vec3_add(c->position, vec3_mul(vec3_normalize(c->position), -e.wheel.y)); break; } c->position = vec3_add(c->position, translation); c->target = vec3_add(c->target, translation); }
collision point_collide_edge(vec3 p, vec3 v, vec3 e0, vec3 e1) { vec3 x0 = vec3_sub(e0, p); vec3 x1 = vec3_sub(e1, p); vec3 d = vec3_sub(x1, x0); float dlen = vec3_length_sqrd(d); float vlen = vec3_length_sqrd(v); float xlen = vec3_length_sqrd(x0); float A = dlen * -vlen + vec3_dot(d, v) * vec3_dot(d, v); float B = dlen * 2 * vec3_dot(v, x0) - 2 * vec3_dot(d, v) * vec3_dot(d, x0); float C = dlen * - xlen + vec3_dot(d, x0) * vec3_dot(d, x0); float t0, t1, t; if (!quadratic(A, B, C, &t0, &t1)) { return collision_none(); } if (between_or(t0, 0, 1) && between_or(t1, 0, 1)) { t = min(t0, t1); } else if (between_or(t0, 0, 1)) { t = t0; } else if (between_or(t1, 0, 1)) { t = t1; } else { return collision_none(); } float range = (vec3_dot(d, v) * t - vec3_dot(d, x0)) / dlen; if (!between_or(range, 0, 1)) { return collision_none(); } else { vec3 spoint = vec3_add(e0, vec3_mul(d, range)); return collision_new(t, spoint, vec3_normalize(vec3_sub(p, spoint))); } }
//----------------------------------------------------------------------------------------------------------------------------------------- // calcul la position dans le monde des coordonnées de l'objet void ObjBones::Transform(ObjetU3D* obj) { CoordU3D* ctmp = obj->Ctab; OneObjBones* tmpB; BonesCoordListe* cbtmp; Mat3x4 mat = obj->GetObjToWorld(); /* for( U32 a=0; a<obj->nbcoords; a++,ctmp++ ) vec3_set( ctmp->trans, 0.0f, 0.0f, 0.0f ); for( U32 a=0; a<obj->nbcoords; a++,ctmp++ ) vec3_set( ctmp->origine, 0.0f, 0.0f, 0.0f );*/ //------------- ini toutes les coords touchées par au moins 1 bone cbtmp = ListeCoord; while (cbtmp) { vec3_mul(cbtmp->coord->origine, cbtmp->origine, cbtmp->Influence); cbtmp = cbtmp->suiv; } tmpB = AllBones; while (tmpB) { tmpB->Transform(); tmpB = tmpB->suiv; } //------------- Transform ensuite toute les coordonnées ki on été modifiées cbtmp = ListeCoord; while (cbtmp) { cbtmp->coord->trans[0] = mat.GetXTransfo(cbtmp->coord->origine); cbtmp->coord->trans[1] = mat.GetYTransfo(cbtmp->coord->origine); cbtmp->coord->trans[2] = mat.GetZTransfo(cbtmp->coord->origine); cbtmp = cbtmp->suiv; } }
float vec3_dot (const vec3 v1, const vec3 v2) { vec3 mul_result = vec3_mul(v1, v2); float result = mul_result.x + mul_result.y + mul_result.z; return result; }
//---------------------------------------------------------------------------------------------------------------------------------------- void OneNebuRayon::GetVal(Ufloat debut[3], Ufloat fin[3], Ufloat& intensity) { Ufloat pos = timeToGo / totalTime; vec3_mul(debut, vec, dist*pos); debut[0] += BIGPART_X; debut[1] += BIGPART_Y; debut[2] += BIGPART_Z; vec3_mul(fin, vec, (dist+taille)*pos); fin[0] += BIGPART_X; fin[1] += BIGPART_Y; fin[2] += BIGPART_Z; intensity = .7f - pos*pos*.7f; }
void camera_control_freecam(camera* c, float timestep) { const Uint8* kbstate = SDL_GetKeyboardState(NULL); if (kbstate[SDL_SCANCODE_W] || kbstate[SDL_SCANCODE_S] || kbstate[SDL_SCANCODE_A] || kbstate[SDL_SCANCODE_D]) { vec3 cam_dir = vec3_normalize(vec3_sub(c->target, c->position)); vec3 side_dir = vec3_normalize(vec3_cross(cam_dir, vec3_new(0,1,0))); const float speed = 100 * timestep; if (kbstate[SDL_SCANCODE_W]) { c->position = vec3_add(c->position, vec3_mul(cam_dir, speed)); c->target = vec3_add(c->target, vec3_mul(cam_dir, speed)); } if (kbstate[SDL_SCANCODE_S]) { c->position = vec3_sub(c->position, vec3_mul(cam_dir, speed)); c->target = vec3_sub(c->target, vec3_mul(cam_dir, speed)); } if (kbstate[SDL_SCANCODE_D]) { c->position = vec3_add(c->position, vec3_mul(side_dir, speed)); c->target = vec3_add(c->target, vec3_mul(side_dir, speed)); } if (kbstate[SDL_SCANCODE_A]) { c->position = vec3_sub(c->position, vec3_mul(side_dir, speed)); c->target = vec3_sub(c->target, vec3_mul(side_dir, speed)); } } int mouse_x, mouse_y; Uint8 mstate = SDL_GetRelativeMouseState(&mouse_x, &mouse_y); if (mstate & SDL_BUTTON(1)) { float a1 = -(float)mouse_x * 0.005; float a2 = (float)mouse_y * 0.005; vec3 cam_dir = vec3_normalize(vec3_sub(c->target, c->position)); cam_dir.y += -a2; vec3 side_dir = vec3_normalize(vec3_cross(cam_dir, vec3_new(0,1,0))); cam_dir = vec3_add(cam_dir, vec3_mul(side_dir, -a1)); cam_dir = vec3_normalize(cam_dir); c->target = vec3_add(c->position, cam_dir); } }
t_real collision_test_sphere_b(t_ray *ray, t_vec3 *sphere_pos) { t_vec3 osp; t_vec3 dosp; osp = vec3_sub(ray->origin, *sphere_pos); dosp = vec3_mul(ray->direction, osp); return (2.0 * (dosp.x + dosp.y + dosp.z)); }
//---------------------------------------------------------------------------------------------------------- // calcul new position des coord du bone void OneObjBones::Transform() { BonesCoord* tmp = AllCoords; Ufloat vtmp[3]; for (U32 a=0; a<nbcoord; a++,tmp++) { WorldMatrix.GetTransfo(vtmp, tmp->origine); vec3_mul(vtmp, vtmp, tmp->Influence); // vec3_add( tmp->coord->trans, vtmp, tmp->coord->trans ); vec3_add(tmp->coord->origine, vtmp, tmp->coord->origine); } }
vec3 vec3_lerp(const vec3 *v, const vec3 *u, float t) { vec3 w; memset(&w, 0, sizeof(w)); w = vec3_sub(u, v); w = vec3_mul(&w, t); w = vec3_add(v, &w); return w; }
static int _llfunc_vec3_mul(lua_State *L) { vec3 *v = (vec3*)userdata_get_or_die(L, 1); if (lua_isuserdata(L, 2)) { mat4 *m = (mat4*)lua_touserdata(L, 2); vec3 *r = (vec3*)userdata_get_or_new(L, 3, sizeof(vec3)); vec3_mul_mat4(v, m, r); } else { float k = (float)luaL_checknumber(L, 2); vec3 *r = (vec3*)userdata_get_or_new(L, 3, sizeof(vec3)); vec3_mul(v, k, r); } return 1; }
collision sphere_collide_sphere(sphere s, vec3 v, sphere s0) { //if (unlikely(!sphere_outside_sphere(s, s0))) { error("Collision Sphere Inside Sphere!"); } vec3 o = vec3_sub(s.center, s0.center); float A = vec3_dot(v, v); float B = 2 * vec3_dot(v, o); float C = vec3_dot(o, o) - ((s.radius + s0.radius) * (s.radius + s0.radius)); float t0, t1, t; if (!quadratic(A, B, C, &t0, &t1)) { return collision_none(); } if (between_or(t0, 0, 1) && between_or(t1, 0, 1)) { t = min(t0, t1); } else if (between_or(t0, 0, 1)) { t = t0; } else if (between_or(t1, 0, 1)) { t = t1; } else { return collision_none(); } vec3 proj = vec3_add(s.center, vec3_mul(v, t)); vec3 twrd = vec3_normalize(vec3_sub(s0.center, proj)); vec3 p = vec3_add(proj, vec3_mul(twrd, s.radius)); return collision_new(t, p, vec3_normalize(vec3_sub(s.center, p))); }
//---------------------------------------------------------------------------------------------------------------------------------------- void CoupleShadowP3D::IncAnim(Ufloat laptime) { Mat3x4 mat; Ufloat tmp[3]= {0.0f,1.0f,0.0f}; Ufloat alpha = petal->GetAlpha(); vec4_set(p1->RVBA, 1.0f, 1.0f, 1.0f, alpha); petal->GetQuat().matrix(mat); vec3_mul(tmp, petal->GetPos(), FACT_SCENE_CM); mat.SetPos(tmp[0], tmp[1], tmp[2]); mat *= petal->GetMass() + tmp[1]*.001f; for (U32 a=0; a<4; a++) { mat.GetTransfo(p1->Vtab[a]->c->trans, p1->Vtab[a]->c->origine); p1->Vtab[a]->c->trans[1] = 0.0f; } }
/* * Create the vertex array with vertexes in mip level order. */ static void lsc_setupPatchVtxArr(lsc l, int m, int x0, int x1, int y0, int y1, float *vtxArr) { int i, j, k; vec3 v; unsigned char *hm = l->hm; int hmSize = l->hmSize; int patchSize = l->patchSize; float texScale = (float)l->texTile / (float)l->hmSize; float baseTexScale = (float)l->baseTexTile / (float)l->hmSize; int S = x0 % (l->hmSize / l->texTile); int T = y0 % (l->hmSize / l->texTile); unsigned short *patchIdx = l->patchIdx; if (hm) hm = &hm[x0 + y0*(hmSize+1)]; for (j = 0; j <= patchSize; j++) { for (i = 0; i <= patchSize; i++) { k = patchIdx[j*(patchSize + 1) + i]; v[0] = i + x0; v[1] = j + y0; /* Texture 0 */ vtxArr[8*k + 4] = (S + i) * texScale; vtxArr[8*k + 5] = (T + j) * texScale; /* Texture 1 */ vtxArr[8*k + 6] = v[0] * baseTexScale; vtxArr[8*k + 7] = v[1] * baseTexScale; /* Vertex */ if (hm) v[2] = hm[j*(hmSize + 1) + i]; else v[2] = 0.0f; vec3_mul(v, l->scale, v); vec3_cpy(v, (&vtxArr[8*k])); } } }
//---------------------------------------------------------------------------------------------------------------------------------------- void CoupleOnePetals3D::IncAnim(Ufloat laptime) { Mat3x4 mat; Ufloat tmp[3]; CalcPos(laptime); Ufloat alpha = GetAlpha(); vec4_set(p1->RVBA, 1.0f, 1.0f, 1.0f, alpha); vec4_set(p2->RVBA, 1.0f, 1.0f, 1.0f, alpha); rot *= incrot * laptime; rot.matrix(mat); vec3_mul(tmp, GetPos(), FACT_SCENE_CM); mat.SetPos(tmp[0], tmp[1], tmp[2]); for (U32 a=0; a<6; a++) mat.GetTransfo(coord[a]->trans, coord[a]->origine); }
pixel_data_t world_render(world_t *world, camera_t *camera) { pixel_t **pixels = calloc(camera->h, sizeof(pixel_t *)); for (uint32_t i = 0; i < camera->h; i++) { pixels[i] = malloc(sizeof(pixel_t) * camera->w); } pixel_data_t pixel_data = { camera->w, camera->h, pixels }; for (uint32_t x = 0; x < camera->w; x++) { for (uint32_t y = 0; y < camera->h; y++) { ray_t *ray = ray_new(camera->pos, dir_for_pixel(camera, x, y)); sphere_t *sphere = ptr_array_index(world->spheres, 0); vec3_t normal; double distance; if (!object_get_collision(OBJECT(sphere), ray, &distance, &normal)) continue; vec3_t p = vec3_add(ray->pos, vec3_mul(ray->dir, distance)); pixel_t pixel = { 1, 0, 0, 0 }; for (uint32_t i = 0; i < world->lights->len; i++) { light_t *light = ptr_array_index(world->lights, i); vec3_t l = vec3_normalize(vec3_sub(p, light->pos)); double lambert = MAX(0, vec3_dot(l, normal) * LAMBERT_COEF); pixel.a = 1; pixel.r += 1 * lambert; pixel.g += 1 * lambert; pixel.b += 1 * lambert; } pixels[x][y] = pixel; } } return pixel_data; }
t_vec3 raytracing_color(t_env *e, t_ray *ray, t_cam *cam, t_obj *obj) { t_lgt *light; t_vec3 color; t_vec3 diffuse; t_vec3 specular; color = (t_vec3) {0, 0, 0}; light = e->lgt; while (light != NULL) { set_light(ray->hit, obj, light); set_color(obj, ray); color = vec3_add(color, vec3_fmul(light->color, obj->mat.ambient)); diffuse = set_diffuse(obj, light); specular = set_specular(obj, cam, light); color = vec3_add(color, vec3_add(diffuse, specular)); color = vec3_mul(color, obj->mat.color); obj->mat.receive_shadow ? set_shadow(e, &color, *light, obj) : 0; light = light->next; } return (color); }
collision point_collide_face(vec3 p, vec3 v, ctri ct) { float angle = vec3_dot(ct.norm, v); float dist = vec3_dot(ct.norm, vec3_sub(p, ct.a)); float t0 = -dist / angle; float t1 = -dist / angle; float t = FLT_MAX; if (between_or(t0, 0, 1) && between_or(t1, 0, 1)) { t = min(t0, t1); } else if (between_or(t0, 0, 1)) { t = t0; } else if (between_or(t1, 0, 1)) { t = t1; } else { return collision_none(); } vec3 cpoint = vec3_add(p, vec3_mul(v, t)); if (!point_inside_triangle(cpoint, ct.a, ct.b, ct.c)) { return collision_none(); } else { return collision_new(t, cpoint, ct.norm); } }
void bounds_scale(struct bounds *dst, const struct bounds *b, const struct vec3 *v) { vec3_mul(&dst->min, &b->min, v); vec3_mul(&dst->max, &b->max, v); }
X42_EXPORT affine_t* X42_CALL x42_GetAnimBoneMatrices( void *out_buffer, const x42data_t *x42, const x42animLerp_t *lerp, x42opts_t *opts ) { uint i; affine_t * RESTRICT ret; const vec3_t * RESTRICT pv; const vec3_t * RESTRICT sv; const quat_t * RESTRICT rv; REF_PARAM( opts ); #ifndef LIBX42_NO_PARAM_VALIDATION demand_rn( out_buffer != NULL, X42_ERR_BADPTR, "out_buffer is NULL" ); demand_rn( lerp != NULL, X42_ERR_BADPTR, "lerp is NULL" ); demand_rn( x42 != NULL, X42_ERR_BADPTR, "x42 is NULL" ); demand_rn( x42_ValidateHeader( &x42->header ), X42_ERR_BADDATA, "invalid x42 header data" ); #endif pv = lerp->p; sv = lerp->s; rv = lerp->r; ret = (affine_t*)out_buffer; for( i = 0; i < x42->header.numBones; i++ ) { affine_t * RESTRICT o = ret + i; const x42bone_t * RESTRICT b = x42->bones + i; if( b->parentIdx != X42_MODEL_BONE && (b->flags & X42_BF_USE_INV_PARENT_SCALE) ) { vec3_t ips; affine_t rs; quat_to_affine( &rs, rv[i] ); vec3_scale( rs.c[0], rs.c[0], sv[i][0] ); vec3_scale( rs.c[1], rs.c[1], sv[i][1] ); vec3_scale( rs.c[2], rs.c[2], sv[i][2] ); ips[0] = 1.0F / sv[b->parentIdx][0]; ips[1] = 1.0F / sv[b->parentIdx][1]; ips[2] = 1.0F / sv[b->parentIdx][2]; vec3_mul( o->c[0], rs.c[0], ips ); vec3_mul( o->c[1], rs.c[1], ips ); vec3_mul( o->c[2], rs.c[2], ips ); } else { quat_to_affine( o, rv[i] ); vec3_scale( o->c[0], o->c[0], sv[i][0] ); vec3_scale( o->c[1], o->c[1], sv[i][1] ); vec3_scale( o->c[2], o->c[2], sv[i][2] ); } vec3_cpy( o->c[3], pv[i] ); } for( i = 0; i < x42->header.numBones; i++ ) { const x42bone_t * RESTRICT b = x42->bones + i; if( b->parentIdx != X42_MODEL_BONE ) affine_mul( ret + i, ret + b->parentIdx, ret + i ); else if( x42->header.runFlags & X42_RF_ROOT_MATRIX ) affine_mul( ret + i, &x42->rootMatrix, ret + i ); } return ret; }
/* * This function is called (see bottom of this file for more details) * whenever the OBS filter interface changes. So when the user is messing * with a slider this function is called to update the internal settings * in OBS, and hence the settings being passed to the CPU/GPU. */ static void color_correction_filter_update(void *data, obs_data_t *settings) { struct color_correction_filter_data *filter = data; /* Build our Gamma numbers. */ double gamma = obs_data_get_double(settings, SETTING_GAMMA); gamma = (gamma < 0.0) ? (-gamma + 1.0) : (1.0 / (gamma + 1.0)); vec3_set(&filter->gamma, (float)gamma, (float)gamma, (float)gamma); /* Build our contrast number. */ filter->contrast = (float)obs_data_get_double(settings, SETTING_CONTRAST) + 1.0f; float one_minus_con = (1.0f - filter->contrast) / 2.0f; /* Now let's build our Contrast matrix. */ filter->con_matrix = (struct matrix4) { filter->contrast, 0.0f, 0.0f, 0.0f, 0.0f, filter->contrast, 0.0f, 0.0f, 0.0f, 0.0f, filter->contrast, 0.0f, one_minus_con, one_minus_con, one_minus_con, 1.0f }; /* Build our brightness number. */ filter->brightness = (float)obs_data_get_double(settings, SETTING_BRIGHTNESS); /* * Now let's build our Brightness matrix. * Earlier (in the function color_correction_filter_create) we set * this matrix to the identity matrix, so now we only need * to set the 3 variables that have changed. */ filter->bright_matrix.t.x = filter->brightness; filter->bright_matrix.t.y = filter->brightness; filter->bright_matrix.t.z = filter->brightness; /* Build our Saturation number. */ filter->saturation = (float)obs_data_get_double(settings, SETTING_SATURATION) + 1.0f; /* Factor in the selected color weights. */ float one_minus_sat = (1.0f - filter->saturation) / 3.0f; float sat_val = one_minus_sat + filter->saturation; /* Now we build our Saturation matrix. */ filter->sat_matrix = (struct matrix4) { sat_val, one_minus_sat, one_minus_sat, 0.0f, one_minus_sat, sat_val, one_minus_sat, 0.0f, one_minus_sat, one_minus_sat, sat_val, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f }; /* Build our Hue number. */ filter->hue_shift = (float)obs_data_get_double(settings, SETTING_HUESHIFT); /* Build our Transparency number. */ filter->opacity = (float)obs_data_get_int(settings, SETTING_OPACITY) * 0.01f; /* Hue is the radian of 0 to 360 degrees. */ float half_angle = 0.5f * (float)(filter->hue_shift / (180.0f / M_PI)); /* Pseudo-Quaternion To Matrix. */ float rot_quad1 = root3 * (float)sin(half_angle); vec3_set(&filter->rot_quaternion, rot_quad1, rot_quad1, rot_quad1); filter->rot_quaternion_w = (float)cos(half_angle); vec3_mul(&filter->cross, &filter->rot_quaternion, &filter->rot_quaternion); vec3_mul(&filter->square, &filter->rot_quaternion, &filter->rot_quaternion); vec3_mulf(&filter->wimag, &filter->rot_quaternion, filter->rot_quaternion_w); vec3_mulf(&filter->square, &filter->square, 2.0f); vec3_sub(&filter->diag, &filter->half_unit, &filter->square); vec3_add(&filter->a_line, &filter->cross, &filter->wimag); vec3_sub(&filter->b_line, &filter->cross, &filter->wimag); /* Now we build our Hue and Opacity matrix. */ filter->hue_op_matrix = (struct matrix4) { filter->diag.x * 2.0f, filter->b_line.z * 2.0f, filter->a_line.y * 2.0f, 0.0f, filter->a_line.z * 2.0f, filter->diag.y * 2.0f, filter->b_line.x * 2.0f, 0.0f, filter->b_line.y * 2.0f, filter->a_line.x * 2.0f, filter->diag.z * 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, filter->opacity }; /* Now get the overlay color data. */ uint32_t color = (uint32_t)obs_data_get_int(settings, SETTING_COLOR); vec4_from_rgba(&filter->color, color); /* * Now let's build our Color 'overlay' matrix. * Earlier (in the function color_correction_filter_create) we set * this matrix to the identity matrix, so now we only need * to set the 6 variables that have changed. */ filter->color_matrix.x.x = filter->color.x; filter->color_matrix.y.y = filter->color.y; filter->color_matrix.z.z = filter->color.z; filter->color_matrix.t.x = filter->color.w * filter->color.x; filter->color_matrix.t.y = filter->color.w * filter->color.y; filter->color_matrix.t.z = filter->color.w * filter->color.z; /* First we apply the Contrast & Brightness matrix. */ matrix4_mul(&filter->final_matrix, &filter->bright_matrix, &filter->con_matrix); /* Now we apply the Saturation matrix. */ matrix4_mul(&filter->final_matrix, &filter->final_matrix, &filter->sat_matrix); /* Next we apply the Hue+Opacity matrix. */ matrix4_mul(&filter->final_matrix, &filter->final_matrix, &filter->hue_op_matrix); /* Lastly we apply the Color Wash matrix. */ matrix4_mul(&filter->final_matrix, &filter->final_matrix, &filter->color_matrix); } /* * Since this is C we have to be careful when destroying/removing items from * OBS. Jim has added several useful functions to help keep memory leaks to * a minimum, and handle the destruction and construction of these filters. */ static void color_correction_filter_destroy(void *data) { struct color_correction_filter_data *filter = data; if (filter->effect) { obs_enter_graphics(); gs_effect_destroy(filter->effect); obs_leave_graphics(); } bfree(data); } /* * When you apply a filter OBS creates it, and adds it to the source. OBS also * starts rendering it immediately. This function doesn't just 'create' the * filter, it also calls the render function (farther below) that contains the * actual rendering code. */ static void *color_correction_filter_create(obs_data_t *settings, obs_source_t *context) { /* * Because of limitations of pre-c99 compilers, you can't create an * array that doesn't have a known size at compile time. The below * function calculates the size needed and allocates memory to * handle the source. */ struct color_correction_filter_data *filter = bzalloc(sizeof(struct color_correction_filter_data)); /* * By default the effect file is stored in the ./data directory that * your filter resides in. */ char *effect_path = obs_module_file("color_correction_filter.effect"); filter->context = context; /* Set/clear/assign for all necessary vectors. */ vec3_set(&filter->half_unit, 0.5f, 0.5f, 0.5f); matrix4_identity(&filter->bright_matrix); matrix4_identity(&filter->color_matrix); /* Here we enter the GPU drawing/shader portion of our code. */ obs_enter_graphics(); /* Load the shader on the GPU. */ filter->effect = gs_effect_create_from_file(effect_path, NULL); /* If the filter is active pass the parameters to the filter. */ if (filter->effect) { filter->gamma_param = gs_effect_get_param_by_name( filter->effect, SETTING_GAMMA); filter->final_matrix_param = gs_effect_get_param_by_name( filter->effect, "color_matrix"); } obs_leave_graphics(); bfree(effect_path); /* * If the filter has been removed/deactivated, destroy the filter * and exit out so we don't crash OBS by telling it to update * values that don't exist anymore. */ if (!filter->effect) { color_correction_filter_destroy(filter); return NULL; } /* * It's important to call the update function here. If we don't * we could end up with the user controlled sliders and values * updating, but the visuals not updating to match. */ color_correction_filter_update(filter, settings); return filter; } /* This is where the actual rendering of the filter takes place. */ static void color_correction_filter_render(void *data, gs_effect_t *effect) { struct color_correction_filter_data *filter = data; if (!obs_source_process_filter_begin(filter->context, GS_RGBA, OBS_ALLOW_DIRECT_RENDERING)) return; /* Now pass the interface variables to the .effect file. */ gs_effect_set_vec3(filter->gamma_param, &filter->gamma); gs_effect_set_matrix4(filter->final_matrix_param, &filter->final_matrix); obs_source_process_filter_end(filter->context, filter->effect, 0, 0); UNUSED_PARAMETER(effect); } /* * This function sets the interface. the types (add_*_Slider), the type of * data collected (int), the internal name, user-facing name, minimum, * maximum and step values. While a custom interface can be built, for a * simple filter like this it's better to use the supplied functions. */ static obs_properties_t *color_correction_filter_properties(void *data) { obs_properties_t *props = obs_properties_create(); obs_properties_add_float_slider(props, SETTING_GAMMA, TEXT_GAMMA, -3.0f, 3.0f, 0.01f); obs_properties_add_float_slider(props, SETTING_CONTRAST, TEXT_CONTRAST, -2.0f, 2.0f, 0.01f); obs_properties_add_float_slider(props, SETTING_BRIGHTNESS, TEXT_BRIGHTNESS, -1.0f, 1.0f, 0.01f); obs_properties_add_float_slider(props, SETTING_SATURATION, TEXT_SATURATION, -1.0f, 5.0f, 0.01f); obs_properties_add_float_slider(props, SETTING_HUESHIFT, TEXT_HUESHIFT, -180.0f, 180.0f, 0.01f); obs_properties_add_int_slider(props, SETTING_OPACITY, TEXT_OPACITY, 0, 100, 1); obs_properties_add_color(props, SETTING_COLOR, TEXT_COLOR); UNUSED_PARAMETER(data); return props; } /* * As the functions' namesake, this provides the default settings for any * options you wish to provide a default for. Try to select defaults that * make sense to the end user, or that don't effect the data. * *NOTE* this function is completely optional, as is providing a default * for any particular setting. */ static void color_correction_filter_defaults(obs_data_t *settings) { obs_data_set_default_double(settings, SETTING_GAMMA, 0.0); obs_data_set_default_double(settings, SETTING_CONTRAST, 0.0); obs_data_set_default_double(settings, SETTING_BRIGHTNESS, 0.0); obs_data_set_default_double(settings, SETTING_SATURATION, 0.0); obs_data_set_default_double(settings, SETTING_HUESHIFT, 0.0); obs_data_set_default_double(settings, SETTING_OPACITY, 100.0); obs_data_set_default_int(settings, SETTING_COLOR, 0xFFFFFF); } /* * So how does OBS keep track of all these plug-ins/filters? How does OBS know * which function to call when it needs to update a setting? Or a source? Or * what type of source this is? * * OBS does it through the obs_source_info_struct. Notice how variables are * assigned the name of a function? Notice how the function name has the * variable name in it? While not mandatory, it helps a ton for you (and those * reading your code) to follow this convention. */ struct obs_source_info color_filter = { .id = "color_filter", .type = OBS_SOURCE_TYPE_FILTER, .output_flags = OBS_SOURCE_VIDEO, .get_name = color_correction_filter_name, .create = color_correction_filter_create, .destroy = color_correction_filter_destroy, .video_render = color_correction_filter_render, .update = color_correction_filter_update, .get_properties = color_correction_filter_properties, .get_defaults = color_correction_filter_defaults };
union vec3* vec3_mul_self(union vec3 *vi, float scalar) { return vec3_mul(vi, vi, scalar); }