/* * Copyright (C) Volition, Inc. 1999. All rights reserved. * * All source code herein is the property of Volition, Inc. You may not sell * or otherwise commercially exploit the source or things you created based on the * source. * */ #include "bmpman/bmpman.h" #include "cmdline/cmdline.h" #include "globalincs/alphacolors.h" #include "graphics/grbatch.h" #include "graphics/tmapper.h" #include "graphics/matrix.h" #include "io/key.h" #include "physics/physics.h" // For Physics_viewer_bank for g3_draw_rotated_bitmap #include "render/3dinternal.h" #include "render/batching.h" /** * Deal with a clipped line */ int must_clip_line(vertex *p0, vertex *p1, ubyte codes_or, uint flags) { int ret = 0; clip_line(&p0, &p1, codes_or, flags); if (p0->codes & p1->codes) goto free_points; codes_or = (unsigned char)(p0->codes | p1->codes); if (codes_or & CC_BEHIND) goto free_points; if (!(p0->flags&PF_PROJECTED)) g3_project_vertex(p0); if (p0->flags&PF_OVERFLOW) goto free_points; if (!(p1->flags&PF_PROJECTED)) g3_project_vertex(p1); if (p1->flags&PF_OVERFLOW) goto free_points; gr_aaline( p0, p1 ); ret = 1; //frees temp points free_points: if (p0->flags & PF_TEMP_POINT) free_temp_point(p0); if (p1->flags & PF_TEMP_POINT) free_temp_point(p1); return ret; } /** * Draws a line. takes two points. returns true if drew */ int g3_draw_line(vertex *p0, vertex *p1) { #ifdef FRED_OGL_COMMENT_OUT_FOR_NOW if(Fred_running) { gr_aaline( p0, p1 ); return 0; } #endif ubyte codes_or; Assert( G3_count == 1 ); if (p0->codes & p1->codes) return 0; codes_or = (unsigned char)(p0->codes | p1->codes); if (codes_or & CC_BEHIND) return must_clip_line(p0,p1,codes_or,0); if (!(p0->flags&PF_PROJECTED)) g3_project_vertex(p0); if (p0->flags&PF_OVERFLOW) return must_clip_line(p0,p1,codes_or,0); if (!(p1->flags&PF_PROJECTED)) g3_project_vertex(p1); if (p1->flags&PF_OVERFLOW) return must_clip_line(p0,p1,codes_or,0); gr_aaline( p0, p1 ); return 0; } //returns true if a plane is facing the viewer. takes the unrotated surface //normal of the plane, and a point on it. The normal need not be normalized int g3_check_normal_facing(const vec3d *v, const vec3d *norm) { vec3d tempv; Assert( G3_count == 1 ); vm_vec_sub(&tempv,&View_position,v); return (vm_vec_dot(&tempv,norm) > 0.0f); } //draw a sortof sphere - i.e., the 2d radius is proportional to the 3d //radius, but not to the distance from the eye int g3_draw_sphere(vertex *pnt, float rad) { Assert( G3_count == 1 ); if (! (pnt->codes & CC_BEHIND)) { if (! (pnt->flags & PF_PROJECTED)) g3_project_vertex(pnt); if (! (pnt->codes & PF_OVERFLOW)) { float r2,t; r2 = rad*Matrix_scale.xyz.x; t=r2*Canv_w2/pnt->world.xyz.z; gr_circle(fl2i(pnt->screen.xyw.x),fl2i(pnt->screen.xyw.y),fl2i(t*2.0f),GR_RESIZE_NONE); } } return 0; } int g3_draw_sphere_ez(const vec3d *pnt, float rad) { vertex pt; ubyte flags; Assert( G3_count == 1 ); flags = g3_rotate_vertex(&pt, pnt); if (flags == 0) { g3_project_vertex(&pt); if (!(pt.flags & PF_OVERFLOW)) { g3_draw_sphere( &pt, rad ); } } return 0; } // get bitmap dims onscreen as if g3_draw_bitmap() had been called int g3_get_bitmap_dims(int bitmap, vertex *pnt, float rad, int *x, int *y, int *w, int *h, int *size) { float t; float width, height; int bw, bh; bm_get_info( bitmap, &bw, &bh, NULL ); if ( bw < bh ) { width = rad*2.0f; height = width*i2fl(bh)/i2fl(bw); } else if ( bw > bh ) { height = rad*2.0f; width = height*i2fl(bw)/i2fl(bh); } else { width = height = rad*2.0f; } Assert( G3_count == 1 ); if ( pnt->codes & (CC_BEHIND|CC_OFF_USER) ) { return 1; } if (!(pnt->flags&PF_PROJECTED)){ g3_project_vertex(pnt); } if (pnt->flags & PF_OVERFLOW){ return 1; } t = (width*Canv_w2)/pnt->world.xyz.z; *w = (int)(t*Matrix_scale.xyz.x); t = (height*Canv_h2)/pnt->world.xyz.z; *h = (int)(t*Matrix_scale.xyz.y); *x = (int)(pnt->screen.xyw.x - *w/2.0f); *y = (int)(pnt->screen.xyw.y - *h/2.0f); *size = MAX(bw, bh); return 0; } #include "graphics/2d.h" //draws a horizon. takes eax=sky_color, edx=ground_color void g3_draw_horizon_line() { int s1, s2; int cpnt; horz_pt horz_pts[4]; // 0 = left, 1 = right vec3d horizon_vec; float up_right, down_right,down_left,up_left; Assert( G3_count == 1 ); //compute horizon_vector horizon_vec.xyz.x = Unscaled_matrix.vec.rvec.xyz.y*Matrix_scale.xyz.y*Matrix_scale.xyz.z; horizon_vec.xyz.y = Unscaled_matrix.vec.uvec.xyz.y*Matrix_scale.xyz.x*Matrix_scale.xyz.z; horizon_vec.xyz.z = Unscaled_matrix.vec.fvec.xyz.y*Matrix_scale.xyz.x*Matrix_scale.xyz.y; // now compute values & flag for 4 corners. up_right = horizon_vec.xyz.x + horizon_vec.xyz.y + horizon_vec.xyz.z; down_right = horizon_vec.xyz.x - horizon_vec.xyz.y + horizon_vec.xyz.z; down_left = -horizon_vec.xyz.x - horizon_vec.xyz.y + horizon_vec.xyz.z; up_left = -horizon_vec.xyz.x + horizon_vec.xyz.y + horizon_vec.xyz.z; //check flags for all sky or all ground. if ( (up_right<0.0f)&&(down_right<0.0f)&&(down_left<0.0f)&&(up_left<0.0f) ) { return; } if ( (up_right>0.0f)&&(down_right>0.0f)&&(down_left>0.0f)&&(up_left>0.0f) ) { return; } // check for intesection with each of four edges & compute horizon line cpnt = 0; // check intersection with left edge s1 = up_left > 0.0f; s2 = down_left > 0.0f; if ( s1 != s2 ) { horz_pts[cpnt].x = 0.0f; horz_pts[cpnt].y = fl_abs(up_left * Canv_h2 / horizon_vec.xyz.y); horz_pts[cpnt].edge = 0; cpnt++; } // check intersection with top edge s1 = up_left > 0.0f; s2 = up_right > 0.0f; if ( s1 != s2 ) { horz_pts[cpnt].x = fl_abs(up_left * Canv_w2 / horizon_vec.xyz.x); horz_pts[cpnt].y = 0.0f; horz_pts[cpnt].edge = 1; cpnt++; } // check intersection with right edge s1 = up_right > 0.0f; s2 = down_right > 0.0f; if ( s1 != s2 ) { horz_pts[cpnt].x = i2fl(Canvas_width)-1; horz_pts[cpnt].y = fl_abs(up_right * Canv_h2 / horizon_vec.xyz.y); horz_pts[cpnt].edge = 2; cpnt++; } //check intersection with bottom edge s1 = down_right > 0.0f; s2 = down_left > 0.0f; if ( s1 != s2 ) { horz_pts[cpnt].x = fl_abs(down_left * Canv_w2 / horizon_vec.xyz.x); horz_pts[cpnt].y = i2fl(Canvas_height)-1; horz_pts[cpnt].edge = 3; cpnt++; } if ( cpnt != 2 ) { mprintf(( "HORZ: Wrong number of points (%d)\n", cpnt )); return; } //make sure first edge is left if ( horz_pts[0].x > horz_pts[1].x ) { horz_pt tmp; tmp = horz_pts[0]; horz_pts[0] = horz_pts[1]; horz_pts[1] = tmp; } // draw from left to right. gr_line( fl2i(horz_pts[0].x),fl2i(horz_pts[0].y),fl2i(horz_pts[1].x),fl2i(horz_pts[1].y), GR_RESIZE_NONE ); } // draw a perspective bitmap based on angles and radius vec3d g3_square[4] = { { { { -1.0f, -1.0f, 20.0f } } }, { { { -1.0f, 1.0f, 20.0f } } }, { { { 1.0f, 1.0f, 20.0f } } }, { { { 1.0f, -1.0f, 20.0f } } } }; void stars_project_2d_onto_sphere( vec3d *pnt, float rho, float phi, float theta ) { float a = PI * phi; float b = PI2 * theta; float sin_a = sinf(a); // coords pnt->xyz.z = rho * sin_a * cosf(b); pnt->xyz.y = rho * sin_a * sinf(b); pnt->xyz.x = rho * cosf(a); } void g3_draw_htl_line(const vec3d *start, const vec3d *end) { //gr_line_htl(start, end); g3_render_line_3d(true, start, end); } void g3_draw_htl_sphere(color* clr, const vec3d* position, float radius, gr_alpha_blend alpha_blend_mode, gr_zbuffer_type zbuffer_mode) { g3_start_instance_matrix(position, &vmd_identity_matrix, true); material material_def; material_def.set_blend_mode(alpha_blend_mode); material_def.set_depth_mode(zbuffer_mode); material_def.set_color(*clr); gr_sphere(&material_def, radius); g3_done_instance(true); } void g3_draw_htl_sphere(color *clr, const vec3d* position, float radius) { g3_draw_htl_sphere(clr, position, radius, ALPHA_BLEND_NONE, ZBUFFER_TYPE_FULL); } void g3_draw_htl_sphere(const vec3d* position, float radius) { g3_draw_htl_sphere(&gr_screen.current_color, position, radius); } void g3_render_primitives(material* mat, vertex* verts, int n_verts, primitive_type prim_type, bool orthographic) { vertex_layout layout; if ( orthographic ) { layout.add_vertex_component(vertex_format_data::POSITION2, sizeof(vertex), (int)offsetof(vertex, screen)); } else { layout.add_vertex_component(vertex_format_data::POSITION3, sizeof(vertex), (int)offsetof(vertex, world)); } if ( orthographic ) { gr_render_primitives_2d_immediate(mat, prim_type, &layout, n_verts, verts, n_verts * sizeof(vertex)); } else { gr_render_primitives_immediate(mat, prim_type, &layout, n_verts, verts, n_verts * sizeof(vertex)); } } void g3_render_primitives_textured(material* mat, vertex* verts, int n_verts, primitive_type prim_type, bool orthographic) { vertex_layout layout; if ( orthographic ) { layout.add_vertex_component(vertex_format_data::POSITION2, sizeof(vertex), (int)offsetof(vertex, screen)); } else { layout.add_vertex_component(vertex_format_data::POSITION3, sizeof(vertex), (int)offsetof(vertex, world)); } layout.add_vertex_component(vertex_format_data::TEX_COORD2, sizeof(vertex), (int)offsetof(vertex, texture_position)); if ( orthographic ) { gr_render_primitives_2d_immediate(mat, prim_type, &layout, n_verts, verts, n_verts * sizeof(vertex)); } else { gr_render_primitives_immediate(mat, prim_type, &layout, n_verts, verts, n_verts * sizeof(vertex)); } } void g3_render_primitives_colored(material* mat, vertex* verts, int n_verts, primitive_type prim_type, bool orthographic) { vertex_layout layout; if ( orthographic ) { layout.add_vertex_component(vertex_format_data::POSITION2, sizeof(vertex), (int)offsetof(vertex, screen)); } else { layout.add_vertex_component(vertex_format_data::POSITION3, sizeof(vertex), (int)offsetof(vertex, world)); } layout.add_vertex_component(vertex_format_data::COLOR4, sizeof(vertex), (int)offsetof(vertex, r)); if ( orthographic ) { gr_render_primitives_2d_immediate(mat, prim_type, &layout, n_verts, verts, n_verts * sizeof(vertex)); } else { gr_render_primitives_immediate(mat, prim_type, &layout, n_verts, verts, n_verts * sizeof(vertex)); } } void g3_render_primitives_colored_textured(material* mat, vertex* verts, int n_verts, primitive_type prim_type, bool orthographic) { vertex_layout layout; if ( orthographic ) { layout.add_vertex_component(vertex_format_data::POSITION2, sizeof(vertex), (int)offsetof(vertex, screen)); } else { layout.add_vertex_component(vertex_format_data::POSITION3, sizeof(vertex), (int)offsetof(vertex, world)); } layout.add_vertex_component(vertex_format_data::TEX_COORD2, sizeof(vertex), (int)offsetof(vertex, texture_position)); layout.add_vertex_component(vertex_format_data::COLOR4, sizeof(vertex), (int)offsetof(vertex, r)); if ( orthographic ) { gr_render_primitives_2d_immediate(mat, prim_type, &layout, n_verts, verts, n_verts * sizeof(vertex)); } else { gr_render_primitives_immediate(mat, prim_type, &layout, n_verts, verts, n_verts * sizeof(vertex)); } } // adapted from g3_draw_polygon() void g3_render_rect_oriented_internal(material* material_params, vec3d *pos, matrix *ori, float width, float height) { //idiot-proof if ( width == 0 || height == 0 ) return; Assert(pos != NULL); Assert(ori != NULL); //Let's begin. const int NUM_VERTICES = 4; vec3d p[NUM_VERTICES] = { ZERO_VECTOR }; vertex v[NUM_VERTICES]; memset(v, 0, sizeof(v)); p[0].xyz.x = width; p[0].xyz.y = height; p[1].xyz.x = -width; p[1].xyz.y = height; p[2].xyz.x = -width; p[2].xyz.y = -height; p[3].xyz.x = width; p[3].xyz.y = -height; for ( int i = 0; i < NUM_VERTICES; i++ ) { vec3d tmp = vmd_zero_vector; //Rotate correctly vm_vec_unrotate(&tmp, &p[i], ori); //Move to point in space vm_vec_add2(&tmp, pos); //Convert to vertex g3_transfer_vertex(&v[i], &tmp); } v[0].texture_position.u = 1.0f; v[0].texture_position.v = 0.0f; v[1].texture_position.u = 0.0f; v[1].texture_position.v = 0.0f; v[2].texture_position.u = 0.0f; v[2].texture_position.v = 1.0f; v[3].texture_position.u = 1.0f; v[3].texture_position.v = 1.0f; g3_render_primitives_textured(material_params, v, 4, PRIM_TYPE_TRIFAN, false); } void g3_render_rect_oriented(material* mat_info, vec3d *pos, matrix *ori, float width, float height) { g3_render_rect_oriented_internal(mat_info, pos, ori, width, height); } void g3_render_rect_oriented(material* mat_info, vec3d *pos, vec3d *norm, float width, float height) { Assertion(norm != nullptr && vm_vec_is_normalized(norm), "input vector must be normalized!"); matrix m; vm_vector_2_matrix_norm(&m, norm, nullptr, nullptr); g3_render_rect_oriented_internal(mat_info, pos, &m, width, height); } // adapted g3_draw_rotated_bitmap_3d() //void render_rotated_bitmap(int texture, float alpha, vertex *pnt, float angle, float rad) void g3_render_rect_screen_aligned_rotated(material *mat_params, vertex *pnt, float angle, float rad) { // holdover mistake from retail causes these bitmaps to be rendered 41% bigger than rad // this turns radius into the diagonal distance, but the methods below presume manhattan distance (unadjusted radius) rad *= 1.41421356f; angle -= Physics_viewer_bank; if ( angle < 0.0f ) { angle += PI2; } else if ( angle > PI2 ) { angle -= PI2; } vec3d PNT(pnt->world); vec3d p[4]; vertex P[4]; vec3d fvec, rvec, uvec; vm_vec_sub(&fvec, &View_position, &PNT); vm_vec_normalize_safe(&fvec); vm_rot_point_around_line(&uvec, &View_matrix.vec.uvec, angle, &vmd_zero_vector, &View_matrix.vec.fvec); vm_vec_normalize(&uvec); vm_vec_cross(&rvec, &View_matrix.vec.fvec, &uvec); vm_vec_normalize(&rvec); vm_vec_scale_add(&p[0], &PNT, &rvec, rad); vm_vec_scale_add(&p[2], &PNT, &rvec, -rad); vm_vec_scale_add(&p[1], &p[2], &uvec, rad); vm_vec_scale_add(&p[3], &p[0], &uvec, -rad); vm_vec_scale_add(&p[0], &p[0], &uvec, rad); vm_vec_scale_add(&p[2], &p[2], &uvec, -rad); //move all the data from the vecs into the verts g3_transfer_vertex(&P[0], &p[3]); g3_transfer_vertex(&P[1], &p[2]); g3_transfer_vertex(&P[2], &p[1]); g3_transfer_vertex(&P[3], &p[0]); //set up the UV coords P[0].texture_position.u = 0.0f; P[0].texture_position.v = 0.0f; P[1].texture_position.u = 1.0f; P[1].texture_position.v = 0.0f; P[2].texture_position.u = 1.0f; P[2].texture_position.v = 1.0f; P[3].texture_position.u = 0.0f; P[3].texture_position.v = 1.0f; g3_render_primitives_textured(mat_params, P, 4, PRIM_TYPE_TRIFAN, false); } // adapted from gr_opengl_scaler() void g3_render_rect_scaler(material *mat_params, vertex *va, vertex *vb) { float x0, y0, x1, y1; float u0, v0, u1, v1; float clipped_x0, clipped_y0, clipped_x1, clipped_y1; float clipped_u0, clipped_v0, clipped_u1, clipped_v1; float xmin, xmax, ymin, ymax; int dx0, dy0, dx1, dy1; x0 = va->screen.xyw.x; y0 = va->screen.xyw.y; x1 = vb->screen.xyw.x; y1 = vb->screen.xyw.y; xmin = i2fl(gr_screen.clip_left); ymin = i2fl(gr_screen.clip_top); xmax = i2fl(gr_screen.clip_right); ymax = i2fl(gr_screen.clip_bottom); u0 = va->texture_position.u; v0 = va->texture_position.v; u1 = vb->texture_position.u; v1 = vb->texture_position.v; // Check for obviously offscreen bitmaps... if ( (y1 <= y0) || (x1 <= x0) ) { return; } if ( (x1 < xmin) || (x0 > xmax) ) { return; } if ( (y1 < ymin) || (y0 > ymax) ) { return; } clipped_u0 = u0; clipped_v0 = v0; clipped_u1 = u1; clipped_v1 = v1; clipped_x0 = x0; clipped_y0 = y0; clipped_x1 = x1; clipped_y1 = y1; // Clip the left, moving u0 right as necessary if ( x0 < xmin ) { clipped_u0 = FIND_SCALED_NUM(xmin, x0, x1, u0, u1); clipped_x0 = xmin; } // Clip the right, moving u1 left as necessary if ( x1 > xmax ) { clipped_u1 = FIND_SCALED_NUM(xmax, x0, x1, u0, u1); clipped_x1 = xmax; } // Clip the top, moving v0 down as necessary if ( y0 < ymin ) { clipped_v0 = FIND_SCALED_NUM(ymin, y0, y1, v0, v1); clipped_y0 = ymin; } // Clip the bottom, moving v1 up as necessary if ( y1 > ymax ) { clipped_v1 = FIND_SCALED_NUM(ymax, y0, y1, v0, v1); clipped_y1 = ymax; } dx0 = fl2i(clipped_x0); dx1 = fl2i(clipped_x1); dy0 = fl2i(clipped_y0); dy1 = fl2i(clipped_y1); if ( (dx1 <= dx0) || (dy1 <= dy0) ) { return; } vertex v[4]; v[0].screen.xyw.x = clipped_x0; v[0].screen.xyw.y = clipped_y0; v[0].screen.xyw.w = va->screen.xyw.w; v[0].world.xyz.z = va->world.xyz.z; v[0].texture_position.u = clipped_u0; v[0].texture_position.v = clipped_v0; v[1].screen.xyw.x = clipped_x1; v[1].screen.xyw.y = clipped_y0; v[1].screen.xyw.w = va->screen.xyw.w; v[1].world.xyz.z = va->world.xyz.z; v[1].texture_position.u = clipped_u1; v[1].texture_position.v = clipped_v0; v[2].screen.xyw.x = clipped_x1; v[2].screen.xyw.y = clipped_y1; v[2].screen.xyw.w = va->screen.xyw.w; v[2].world.xyz.z = va->world.xyz.z; v[2].texture_position.u = clipped_u1; v[2].texture_position.v = clipped_v1; v[3].screen.xyw.x = clipped_x0; v[3].screen.xyw.y = clipped_y1; v[3].screen.xyw.w = va->screen.xyw.w; v[3].world.xyz.z = va->world.xyz.z; v[3].texture_position.u = clipped_u0; v[3].texture_position.v = clipped_v1; g3_render_primitives_textured(mat_params, v, 4, PRIM_TYPE_TRIFAN, true); } // adapted from g3_draw_bitmap() //void render_oriented_bitmap_2d(int texture, float alpha, bool blending, vertex *pnt, int orient, float rad) void g3_render_rect_screen_aligned_2d(material *mat_params, vertex *pnt, int orient, float rad, bool isFaraway) { vertex va, vb; float t, w, h; float width, height; int bw, bh; bm_get_info(mat_params->get_texture_map(TM_BASE_TYPE), &bw, &bh, NULL); if ( bw < bh ) { width = rad*2.0f; height = width*i2fl(bh) / i2fl(bw); } else if ( bw > bh ) { height = rad*2.0f; width = height*i2fl(bw) / i2fl(bh); } else { width = height = rad*2.0f; } Assert(G3_count == 1); if ( pnt->codes & (CC_BEHIND | CC_OFF_USER) ) return; if ( !(pnt->flags&PF_PROJECTED) ) g3_project_vertex(pnt); if ( pnt->flags & PF_OVERFLOW ) return; t = width * gr_screen.clip_width * 0.5f; if (!isFaraway) t /= pnt->world.xyz.z; w = t*Matrix_scale.xyz.x; t = height * gr_screen.clip_height * 0.5f; if (!isFaraway) t /= pnt->world.xyz.z; h = t*Matrix_scale.xyz.y; float z, sw; z = isFaraway ? 100000 : pnt->world.xyz.z - rad / 2.0f; if ( z <= 0.0f ) { z = 0.0f; sw = 0.0f; } else { sw = 1.0f / z; } va.screen.xyw.x = pnt->screen.xyw.x - w / 2.0f; va.screen.xyw.y = pnt->screen.xyw.y - h / 2.0f; va.screen.xyw.w = sw; va.world.xyz.z = z; vb.screen.xyw.x = va.screen.xyw.x + w; vb.screen.xyw.y = va.screen.xyw.y + h; vb.screen.xyw.w = sw; vb.world.xyz.z = z; if ( orient & 1 ) { va.texture_position.u = 1.0f; vb.texture_position.u = 0.0f; } else { va.texture_position.u = 0.0f; vb.texture_position.u = 1.0f; } if ( orient & 2 ) { va.texture_position.v = 1.0f; vb.texture_position.v = 0.0f; } else { va.texture_position.v = 0.0f; vb.texture_position.v = 1.0f; } g3_render_rect_scaler(mat_params, &va, &vb); } // adapted from g3_draw_bitmap_3d void g3_render_rect_screen_aligned(material *mat_params, vertex *pnt, int orient, float rad, float depth) { // holdover mistake from retail causes these bitmaps to be rendered 41% bigger than rad // this turns radius into the diagonal distance, but the methods below presume manhattan distance (unadjusted radius) rad *= 1.41421356f; vec3d PNT(pnt->world); vec3d p[4]; vertex P[4]; vec3d fvec, rvec, uvec; vm_vec_sub(&fvec, &View_position, &PNT); vm_vec_normalize(&fvec); uvec = View_matrix.vec.uvec; vm_vec_normalize(&uvec); rvec = View_matrix.vec.rvec; vm_vec_normalize(&rvec); vm_vec_scale_add(&PNT, &PNT, &fvec, depth); vm_vec_scale_add(&p[0], &PNT, &rvec, rad); vm_vec_scale_add(&p[2], &PNT, &rvec, -rad); vm_vec_scale_add(&p[1], &p[2], &uvec, rad); vm_vec_scale_add(&p[3], &p[0], &uvec, -rad); vm_vec_scale_add(&p[0], &p[0], &uvec, rad); vm_vec_scale_add(&p[2], &p[2], &uvec, -rad); //move all the data from the vecs into the verts g3_transfer_vertex(&P[0], &p[3]); g3_transfer_vertex(&P[1], &p[2]); g3_transfer_vertex(&P[2], &p[1]); g3_transfer_vertex(&P[3], &p[0]); // set up the UV coords if ( orient & 1 ) { P[0].texture_position.u = 1.0f; P[1].texture_position.u = 0.0f; P[2].texture_position.u = 0.0f; P[3].texture_position.u = 1.0f; } else { P[0].texture_position.u = 0.0f; P[1].texture_position.u = 1.0f; P[2].texture_position.u = 1.0f; P[3].texture_position.u = 0.0f; } if ( orient & 2 ) { P[0].texture_position.v = 1.0f; P[1].texture_position.v = 1.0f; P[2].texture_position.v = 0.0f; P[3].texture_position.v = 0.0f; } else { P[0].texture_position.v = 0.0f; P[1].texture_position.v = 0.0f; P[2].texture_position.v = 1.0f; P[3].texture_position.v = 1.0f; } g3_render_primitives_textured(mat_params, P, 4, PRIM_TYPE_TRIFAN, false); } // adapted from g3_draw_laser() //void render_laser_2d(int texture, color* clr, float alpha, vec3d *headp, float head_width, vec3d *tailp, float tail_width, float max_len) void g3_render_laser_2d(material *mat_params, vec3d *headp, float head_width, vec3d *tailp, float tail_width, float max_len) { float headx, heady, headr, tailx, taily, tailr; vertex pt1, pt2; float depth; Assert(G3_count == 1); g3_rotate_vertex(&pt1, headp); g3_project_vertex(&pt1); if ( pt1.flags & PF_OVERFLOW ) return; g3_rotate_vertex(&pt2, tailp); g3_project_vertex(&pt2); if ( pt2.flags & PF_OVERFLOW ) return; if ( (pt1.codes & pt2.codes) != 0 ) { // Both off the same side return; } headx = pt1.screen.xyw.x; heady = pt1.screen.xyw.y; headr = (head_width*Matrix_scale.xyz.x*Canv_w2*pt1.screen.xyw.w); tailx = pt2.screen.xyw.x; taily = pt2.screen.xyw.y; tailr = (tail_width*Matrix_scale.xyz.x*Canv_w2*pt2.screen.xyw.w); float len_2d = fl_sqrt((tailx - headx)*(tailx - headx) + (taily - heady)*(taily - heady)); // Cap the length if needed. if ( (max_len > 1.0f) && (len_2d > max_len) ) { float ratio = max_len / len_2d; tailx = headx + (tailx - headx) * ratio; taily = heady + (taily - heady) * ratio; tailr = headr + (tailr - headr) * ratio; len_2d = fl_sqrt((tailx - headx)*(tailx - headx) + (taily - heady)*(taily - heady)); } depth = (pt1.world.xyz.z + pt2.world.xyz.z)*0.5f; float max_r = headr; float a; if ( tailr > max_r ) max_r = tailr; if ( max_r < 1.0f ) max_r = 1.0f; float mx, my, w, h1, h2; if ( len_2d < max_r ) { h1 = headr + (max_r - len_2d); if ( h1 > max_r ) h1 = max_r; h2 = tailr + (max_r - len_2d); if ( h2 > max_r ) h2 = max_r; len_2d = max_r; if ( fl_abs(tailx - headx) > 0.01f ) { a = (float)atan2(taily - heady, tailx - headx); } else { a = 0.0f; } w = len_2d; } else { a = atan2_safe(taily - heady, tailx - headx); w = len_2d; h1 = headr; h2 = tailr; } mx = (tailx + headx) / 2.0f; my = (taily + heady) / 2.0f; // Draw box with width 'w' and height 'h' at angle 'a' from horizontal // centered around mx, my if ( h1 < 1.0f ) h1 = 1.0f; if ( h2 < 1.0f ) h2 = 1.0f; float sa, ca; sa = (float)sin(a); ca = (float)cos(a); vertex v[4]; memset(v, 0, sizeof(vertex) * 4); if ( depth < 0.0f ) depth = 0.0f; v[0].screen.xyw.x = (-w / 2.0f)*ca + (-h1 / 2.0f)*sa + mx; v[0].screen.xyw.y = (-w / 2.0f)*sa - (-h1 / 2.0f)*ca + my; v[0].world.xyz.z = pt1.world.xyz.z; v[0].screen.xyw.w = pt1.screen.xyw.w; v[0].texture_position.u = 0.0f; v[0].texture_position.v = 0.0f; v[0].b = 191; v[1].screen.xyw.x = (w / 2.0f)*ca + (-h2 / 2.0f)*sa + mx; v[1].screen.xyw.y = (w / 2.0f)*sa - (-h2 / 2.0f)*ca + my; v[1].world.xyz.z = pt2.world.xyz.z; v[1].screen.xyw.w = pt2.screen.xyw.w; v[1].texture_position.u = 1.0f; v[1].texture_position.v = 0.0f; v[1].b = 191; v[2].screen.xyw.x = (w / 2.0f)*ca + (h2 / 2.0f)*sa + mx; v[2].screen.xyw.y = (w / 2.0f)*sa - (h2 / 2.0f)*ca + my; v[2].world.xyz.z = pt2.world.xyz.z; v[2].screen.xyw.w = pt2.screen.xyw.w; v[2].texture_position.u = 1.0f; v[2].texture_position.v = 1.0f; v[2].b = 191; v[3].screen.xyw.x = (-w / 2.0f)*ca + (h1 / 2.0f)*sa + mx; v[3].screen.xyw.y = (-w / 2.0f)*sa - (h1 / 2.0f)*ca + my; v[3].world.xyz.z = pt1.world.xyz.z; v[3].screen.xyw.w = pt1.screen.xyw.w; v[3].texture_position.u = 0.0f; v[3].texture_position.v = 1.0f; v[3].b = 191; g3_render_primitives_textured(mat_params, v, 4, PRIM_TYPE_TRIFAN, true); } // adapted from g3_draw_rod() void g3_render_rod(const color *clr, int num_points, const vec3d *pvecs, float width) { const int MAX_ROD_VERTS = 100; vec3d uvec, fvec, rvec; vec3d vecs[2]; vertex pts[MAX_ROD_VERTS]; int i, nv = 0; Assert(num_points >= 2); Assert((num_points * 2) <= MAX_ROD_VERTS); for ( i = 0; i < num_points; i++ ) { vm_vec_sub(&fvec, &View_position, &pvecs[i]); vm_vec_normalize_safe(&fvec); int first = i + 1; int second = i - 1; if ( i == 0 ) { first = 1; second = 0; } else if ( i == num_points - 1 ) { first = i; } vm_vec_sub(&rvec, &pvecs[first], &pvecs[second]); vm_vec_normalize_safe(&rvec); vm_vec_cross(&uvec, &rvec, &fvec); vm_vec_scale_add(&vecs[0], &pvecs[i], &uvec, width * 0.5f); vm_vec_scale_add(&vecs[1], &pvecs[i], &uvec, -width * 0.5f); if ( nv > MAX_ROD_VERTS - 2 ) { Warning(LOCATION, "Hit high-water mark (%i) in g3_draw_rod()!!\n", MAX_ROD_VERTS); break; } g3_transfer_vertex(&pts[nv], &vecs[0]); g3_transfer_vertex(&pts[nv + 1], &vecs[1]); pts[nv].texture_position.u = 1.0f; pts[nv].texture_position.v = i2fl(i); pts[nv].r = clr->red; pts[nv].g = clr->green; pts[nv].b = clr->blue; pts[nv].a = clr->alpha; pts[nv + 1].texture_position.u = 0.0f; pts[nv + 1].texture_position.v = i2fl(i); pts[nv + 1].r = clr->red; pts[nv + 1].g = clr->green; pts[nv + 1].b = clr->blue; pts[nv + 1].a = clr->alpha; nv += 2; } // we should always have at least 4 verts, and there should always be an even number Assert((nv >= 4) && !(nv % 2)); material material_params; material_params.set_depth_mode(ZBUFFER_TYPE_READ); material_params.set_blend_mode(ALPHA_BLEND_ALPHA_BLEND_ALPHA); g3_render_primitives_colored(&material_params, pts, nv, PRIM_TYPE_TRISTRIP, false); } // adapted from g3_draw_2d_shield_icon() void g3_render_shield_icon(color *clr, coord2d coords[6], int resize_mode) { GR_DEBUG_SCOPE("Render shield icon"); vertex v[6]; memset(v, 0, sizeof(vertex) * 6); if ( resize_mode != GR_RESIZE_NONE ) { gr_resize_screen_pos(&coords[0].x, &coords[0].y, NULL, NULL, resize_mode); gr_resize_screen_pos(&coords[1].x, &coords[1].y, NULL, NULL, resize_mode); gr_resize_screen_pos(&coords[2].x, &coords[2].y, NULL, NULL, resize_mode); gr_resize_screen_pos(&coords[3].x, &coords[3].y, NULL, NULL, resize_mode); gr_resize_screen_pos(&coords[4].x, &coords[4].y, NULL, NULL, resize_mode); gr_resize_screen_pos(&coords[5].x, &coords[5].y, NULL, NULL, resize_mode); } float sw = 0.1f; // stuff coords v[0].screen.xyw.x = i2fl(coords[0].x); v[0].screen.xyw.y = i2fl(coords[0].y); v[0].screen.xyw.w = sw; v[0].texture_position.u = 0.0f; v[0].texture_position.v = 0.0f; v[0].flags = PF_PROJECTED; v[0].codes = 0; v[0].r = (ubyte)clr->red; v[0].g = (ubyte)clr->green; v[0].b = (ubyte)clr->blue; v[0].a = 0; v[1].screen.xyw.x = i2fl(coords[1].x); v[1].screen.xyw.y = i2fl(coords[1].y); v[1].screen.xyw.w = sw; v[1].texture_position.u = 0.0f; v[1].texture_position.v = 0.0f; v[1].flags = PF_PROJECTED; v[1].codes = 0; v[1].r = (ubyte)clr->red; v[1].g = (ubyte)clr->green; v[1].b = (ubyte)clr->blue; v[1].a = (ubyte)clr->alpha; v[2].screen.xyw.x = i2fl(coords[2].x); v[2].screen.xyw.y = i2fl(coords[2].y); v[2].screen.xyw.w = sw; v[2].texture_position.u = 0.0f; v[2].texture_position.v = 0.0f; v[2].flags = PF_PROJECTED; v[2].codes = 0; v[2].r = (ubyte)clr->red; v[2].g = (ubyte)clr->green; v[2].b = (ubyte)clr->blue; v[2].a = 0; v[3].screen.xyw.x = i2fl(coords[3].x); v[3].screen.xyw.y = i2fl(coords[3].y); v[3].screen.xyw.w = sw; v[3].texture_position.u = 0.0f; v[3].texture_position.v = 0.0f; v[3].flags = PF_PROJECTED; v[3].codes = 0; v[3].r = (ubyte)clr->red; v[3].g = (ubyte)clr->green; v[3].b = (ubyte)clr->blue; v[3].a = (ubyte)clr->alpha; v[4].screen.xyw.x = i2fl(coords[4].x); v[4].screen.xyw.y = i2fl(coords[4].y); v[4].screen.xyw.w = sw; v[4].texture_position.u = 0.0f; v[4].texture_position.v = 0.0f; v[4].flags = PF_PROJECTED; v[4].codes = 0; v[4].r = (ubyte)clr->red; v[4].g = (ubyte)clr->green; v[4].b = (ubyte)clr->blue; v[4].a = 0; v[5].screen.xyw.x = i2fl(coords[5].x); v[5].screen.xyw.y = i2fl(coords[5].y); v[5].screen.xyw.w = sw; v[5].texture_position.u = 0.0f; v[5].texture_position.v = 0.0f; v[5].flags = PF_PROJECTED; v[5].codes = 0; v[5].r = (ubyte)clr->red; v[5].g = (ubyte)clr->green; v[5].b = (ubyte)clr->blue; v[5].a = 0; material material_instance; material_instance.set_depth_mode(ZBUFFER_TYPE_NONE); material_instance.set_blend_mode(ALPHA_BLEND_ALPHA_BLEND_ALPHA); material_instance.set_cull_mode(false); material_instance.set_color(1.0f, 1.0f, 1.0f, 1.0f); // draw the polys g3_render_primitives_colored(&material_instance, v, 6, PRIM_TYPE_TRISTRIP, true); } void g3_render_shield_icon(coord2d coords[6], int resize_mode) { g3_render_shield_icon(&gr_screen.current_color, coords, resize_mode); } // adapted from gr_opengl_line_htl() void g3_render_line_3d(color *clr, bool depth_testing, const vec3d *start, const vec3d *end) { material mat; mat.set_depth_mode((depth_testing) ? ZBUFFER_TYPE_READ : ZBUFFER_TYPE_NONE); mat.set_color(*clr); mat.set_cull_mode(false); if ( clr->is_alphacolor ) { mat.set_blend_mode(ALPHA_BLEND_ALPHA_BLEND_ALPHA); } else { mat.set_blend_mode(ALPHA_BLEND_NONE); } float line[6] = { start->xyz.x, start->xyz.y, start->xyz.z, end->xyz.x, end->xyz.y, end->xyz.z }; vertex_layout vert_def; vert_def.add_vertex_component(vertex_format_data::POSITION3, sizeof(float) * 3, 0); gr_render_primitives_immediate(&mat, PRIM_TYPE_LINES, &vert_def, 2, line, sizeof(float) * 6); } void g3_render_line_3d(bool depth_testing, const vec3d *start, const vec3d *end) { g3_render_line_3d(&gr_screen.current_color, depth_testing, start, end); } void g3_render_sphere(color *clr, vec3d* position, float radius) { g3_start_instance_matrix(position, &vmd_identity_matrix, true); vec3d scale = {{{ radius, radius, radius }}}; gr_push_scale_matrix(&scale); material material_def; material_def.set_blend_mode(ALPHA_BLEND_NONE); material_def.set_depth_mode(ZBUFFER_TYPE_FULL); material_def.set_color(*clr); material_def.set_cull_mode(false); gr_sphere(&material_def, radius); gr_pop_scale_matrix(); g3_done_instance(true); } void g3_render_sphere(vec3d* position, float radius) { g3_render_sphere(&gr_screen.current_color, position, radius); } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// //flash ball stuff void flash_ball::initialize(uint number, float min_ray_width, float max_ray_width, const vec3d* dir, const vec3d* pcenter, float outer, float inner, ubyte max_r, ubyte max_g, ubyte max_b, ubyte min_r, ubyte min_g, ubyte min_b) { if(number < 1) return; center = *pcenter; if(n_rays < number){ if(ray)vm_free(ray); ray = (flash_beam*)vm_malloc(sizeof(flash_beam)*number); n_rays = number; } uint i; for(i = 0; i= PI2){ //random sphere vec3d start, end; vm_vec_rand_vec_quick(&start); vm_vec_rand_vec_quick(&end); ray[i].start.world = start; ray[i].end.world = end; }else{ //random cones vec3d start, end; vm_vec_random_cone(&start, dir, inner, outer); vm_vec_random_cone(&end, dir, inner, outer); ray[i].start.world = start; ray[i].end.world = end; } if(max_ray_width == 0.0f)ray[i].width=min_ray_width; else ray[i].width = frand_range(min_ray_width, max_ray_width); } } #define uw(p) (*((uint *) (p))) #define w(p) (*((int *) (p))) #define wp(p) ((int *) (p)) #define vp(p) ((vec3d *) (p)) #define fl(p) (*((float *) (p))) void flash_ball::defpoint(int off, ubyte *bsp_data) { uint n; uint nverts = uw(off+bsp_data+8); uint offset = uw(off+bsp_data+16); ubyte * normcount = off+bsp_data+20; vec3d *src = vp(off+bsp_data+offset); if(n_rays < nverts){ if(ray)vm_free(ray); ray = (flash_beam*)vm_malloc(sizeof(flash_beam)*nverts); n_rays = nverts; } { vec3d temp; for (n=0; n= PI2){ //random sphere vec3d end; vm_vec_rand_vec_quick(&end); ray[i].end.world = end; }else{ //random cones vec3d end; vm_vec_random_cone(&end, dir, inner, outer); ray[i].end.world = end; } if(max_ray_width == 0.0f)ray[i].width=min_ray_width; else ray[i].width = frand_range(min_ray_width, max_ray_width); } } //rad how wide the ball should be //intinsity how visable it should be //life how far along from start to end should it be void flash_ball::render(int texture, float rad, float intinsity, float life){ for(uint i = 0; i < n_rays; i++){ vec3d end; vm_vec_interp_constant(&end, &ray[i].start.world, &ray[i].end.world, life); vm_vec_scale(&end, rad); vm_vec_add2(&end, ¢er); batching_add_beam(texture, ¢er, &end, ray[i].width*rad, intinsity); } }