/* * 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 "io/key.h" #include "physics/physics.h" // For Physics_viewer_bank for g3_draw_rotated_bitmap #include "render/3dinternal.h" //vertex buffers for polygon drawing and clipping static vertex **Vbuf0 = NULL; static vertex **Vbuf1 = NULL; static int Num_vbufs_allocated = 0; static void g3_deallocate_vbufs() { if (Vbuf0 != NULL) { vm_free(Vbuf0); Vbuf0 = NULL; } if (Vbuf1 != NULL) { vm_free(Vbuf1); Vbuf1 = NULL; } } static void g3_allocate_vbufs(int nv) { static ubyte dealloc = 0; Assert( nv >= 0 ); Assert( nv || Num_vbufs_allocated ); if (!dealloc) { atexit(g3_deallocate_vbufs); dealloc = 1; } if (nv > Num_vbufs_allocated) { g3_deallocate_vbufs(); Vbuf0 = (vertex**) vm_malloc( nv * sizeof(vertex) ); Vbuf1 = (vertex**) vm_malloc( nv * sizeof(vertex) ); Num_vbufs_allocated = nv; } // make sure everything is valid Verify( Vbuf0 != NULL ); Verify( Vbuf1 != NULL ); } /** * 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); } int do_facing_check(const vec3d *norm, vertex **vertlist, const vec3d *p) { Assert( G3_count == 1 ); if (norm) { //have normal Assert(norm->xyz.x || norm->xyz.y || norm->xyz.z); return g3_check_normal_facing(p,norm); } else { //normal not specified, so must compute vec3d tempv; //get three points (rotated) and compute normal vm_vec_perp(&tempv,&vertlist[0]->world,&vertlist[1]->world,&vertlist[2]->world); return (vm_vec_dot(&tempv,&vertlist[1]->world ) < 0.0); } } //like g3_draw_poly(), but checks to see if facing. If surface normal is //NULL, this routine must compute it, which will be slow. It is better to //pre-compute the normal, and pass it to this function. When the normal //is passed, this function works like g3_check_normal_facing() plus //g3_draw_poly(). //returns -1 if not facing, 1 if off screen, 0 if drew int g3_draw_poly_if_facing(int nv, vertex **pointlist, uint tmap_flags, const vec3d *norm, const vec3d *pnt) { Assert( G3_count == 1 ); if (do_facing_check(norm,pointlist,pnt)) return g3_draw_poly(nv,pointlist,tmap_flags); else return 255; } //draw a polygon. //Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture. //returns 1 if off screen, 0 if drew int g3_draw_poly(int nv, vertex **pointlist, uint tmap_flags) { int i; vertex **bufptr; ccodes cc; Assert( G3_count == 1 ); if(tmap_flags & TMAP_HTL_3D_UNLIT) { gr_tmapper( nv, pointlist, tmap_flags ); return 0; } //don't clip in HT&L mode, the card does it for us if(tmap_flags & TMAP_FLAG_TRISTRIP) { gr_tmapper( nv, pointlist, tmap_flags ); return 0; } g3_allocate_vbufs(nv); cc.cc_or = 0; cc.cc_and = 0xff; bufptr = Vbuf0; for (i=0;icodes; cc.cc_or |= p->codes; } if (cc.cc_and) return 1; //all points off screen if (cc.cc_or) { Assert( G3_count == 1 ); bufptr = clip_polygon(Vbuf0,Vbuf1,&nv,&cc,tmap_flags); if (nv && !(cc.cc_or & CC_BEHIND) && !cc.cc_and) { for (i=0;iflags&PF_PROJECTED)) g3_project_vertex(p); if (p->flags&PF_OVERFLOW) { goto free_points; } } gr_tmapper( nv, bufptr, tmap_flags ); } free_points: ; for (i=0;iflags & PF_TEMP_POINT) free_temp_point(bufptr[i]); } else { //now make list of 2d coords (& check for overflow) for (i=0;iflags&PF_PROJECTED)) g3_project_vertex(p); if (p->flags&PF_OVERFLOW) { return 255; } } gr_tmapper( nv, bufptr, tmap_flags ); } return 0; //say it drew } int g3_draw_polygon(const vec3d *pos, const matrix *ori, float width, float height, int tmap_flags) { //idiot-proof if(width == 0 || height == 0) return 0; 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; gr_render(NUM_VERTICES, v, tmap_flags); return 0; } int g3_draw_polygon(const vec3d *pos, const vec3d *norm, float width, float height, int tmap_flags) { matrix m; vm_vector_2_matrix(&m, norm, NULL, NULL); return g3_draw_polygon(pos, &m, width, height, tmap_flags); } // Draw a polygon. Same as g3_draw_poly, but it bashes sw to a constant value // for all vertexes. Needs to be done after clipping to get them all. //Set TMAP_FLAG_TEXTURED in the tmap_flags to texture map it with current texture. //returns 1 if off screen, 0 if drew int g3_draw_poly_constant_sw(int nv, vertex **pointlist, uint tmap_flags, float constant_sw) { int i; vertex **bufptr; ccodes cc; Assert( G3_count == 1 ); if(tmap_flags & TMAP_HTL_3D_UNLIT) { gr_tmapper( nv, pointlist, tmap_flags ); return 0; } if(tmap_flags & TMAP_FLAG_TRISTRIP || tmap_flags & TMAP_FLAG_TRILIST){ for (i=0;iflags&PF_PROJECTED))g3_project_vertex(pointlist[i]); } gr_tmapper( nv, pointlist, tmap_flags ); return 0; } g3_allocate_vbufs(nv); cc.cc_or = 0; cc.cc_and = 0xff; bufptr = Vbuf0; for (i=0;icodes; cc.cc_or |= p->codes; } if (cc.cc_and) return 1; //all points off screen if (cc.cc_or) { Assert( G3_count == 1 ); bufptr = clip_polygon(Vbuf0, Vbuf1, &nv, &cc, tmap_flags); if (nv && !(cc.cc_or & CC_BEHIND) && !cc.cc_and) { for (i=0;iflags&PF_PROJECTED)) g3_project_vertex(p); if (p->flags&PF_OVERFLOW) { goto free_points; } p->screen.xyw.w = constant_sw; } gr_tmapper( nv, bufptr, tmap_flags ); } free_points: ; for (i=0;iflags & PF_TEMP_POINT){ free_temp_point(bufptr[i]); } } } else { //now make list of 2d coords (& check for overflow) for (i=0;iflags&PF_PROJECTED)) g3_project_vertex(p); if (p->flags&PF_OVERFLOW) { return 255; } p->screen.xyw.w = constant_sw; } gr_tmapper( nv, bufptr, tmap_flags ); } return 0; //say it drew } //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; } //alternate method int g3_draw_bitmap_3d(vertex *pnt, int orient, float rad, uint tmap_flags, float depth) { rad *= 1.41421356f;//1/0.707, becase these are the points of a square or width and hieght rad 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); vertex *ptlist[4] = { &P[3], &P[2], &P[1], &P[0] }; 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; } int cull = gr_set_cull(0); g3_draw_poly(4,ptlist,tmap_flags); gr_set_cull(cull); return 0; } int g3_draw_bitmap_3d_v(vertex *pnt, int orient, float rad, uint tmap_flags, float depth, float c) { vec3d PNT(pnt->world); vec3d p[4]; vertex P[4]; int i; vertex *ptlist[4] = { &P[3], &P[2], &P[1], &P[0] }; float aspect = gr_screen.aspect*(float)gr_screen.clip_width/(float)gr_screen.clip_height;//seems that we have to corect for the aspect ratio p[0].xyz.x = rad * aspect; p[0].xyz.y = rad; p[0].xyz.z = -depth; p[1].xyz.x = -rad * aspect; p[1].xyz.y = rad; p[1].xyz.z = -depth; p[2].xyz.x = -rad * aspect; p[2].xyz.y = -rad; p[2].xyz.z = -depth; p[3].xyz.x = rad * aspect; p[3].xyz.y = -rad; p[3].xyz.z = -depth; for(i = 0; i<4; i++){ vec3d t = p[i]; vm_vec_unrotate(&p[i],&t,&View_matrix);//point it at the eye vm_vec_add2(&p[i],&PNT);//move it } //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]); for( i = 0; i<4; i++){ P[i].r = (ubyte)(255.0f * c); P[i].g = (ubyte)(255.0f * c); P[i].b = (ubyte)(255.0f * c); } //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; int cull = gr_set_cull(0); g3_draw_poly(4,ptlist,tmap_flags | TMAP_FLAG_RGB | TMAP_FLAG_GOURAUD); gr_set_cull(cull); return 0; } int g3_draw_bitmap_3d_volume(vertex *pnt, int orient, float rad, uint tmap_flags, float depth, int resolution) { float s = 1.0f; float res = float(resolution); for(int i = 0; i bh ) { height = rad*2.0f; width = height*i2fl(bw)/i2fl(bh); } else { width = height = rad*2.0f; } } 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 = t*Matrix_scale.xyz.x; t = (height*Canv_h2)/pnt->world.xyz.z; h = t*Matrix_scale.xyz.y; float z,sw; z = 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; } gr_scaler(&va, &vb, bw_bitmap); 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; } //alternate method int g3_draw_rotated_bitmap_3d(vertex *pnt,float angle, float rad,uint tmap_flags, float depth) { rad *= 1.41421356f;//1/0.707, becase these are the points of a square or width and hieght rad 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); vertex *ptlist[4] = { &P[3], &P[2], &P[1], &P[0] }; 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 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; int cull = gr_set_cull(0); g3_draw_poly(4,ptlist,tmap_flags); gr_set_cull(cull); return 0; } //draws a bitmap with the specified 3d width & height //returns 1 if off screen, 0 if drew int g3_draw_rotated_bitmap(vertex *pnt,float angle, float rad,uint tmap_flags, float depth) { if(tmap_flags & TMAP_HTL_3D_UNLIT) { return g3_draw_rotated_bitmap_3d(pnt, angle, rad, tmap_flags, depth); } vertex v[4]; vertex *vertlist[4] = { &v[3], &v[2], &v[1], &v[0] }; float sa, ca; int i; memset(v,0,sizeof(vertex)*4); Assert( G3_count == 1 ); angle+=Physics_viewer_bank; if ( angle < 0.0f ) angle += PI2; else if ( angle > PI2 ) angle -= PI2; sa = sinf(angle); ca = cosf(angle); float width, height; if ( tmap_flags & TMAP_FLAG_TEXTURED ) { int bw, bh; bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL ); if ( bw < bh ) { width = rad; height = width*i2fl(bh)/i2fl(bw); } else if ( bw > bh ) { height = rad; width = height*i2fl(bw)/i2fl(bh); } else { width = height = rad; } } else { width = height = rad; } v[0].world.xyz.x = (-width*ca + height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x; v[0].world.xyz.y = (-width*sa - height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y; v[0].world.xyz.z = pnt->world.xyz.z; v[0].screen.xyw.w = 0.0f; v[0].texture_position.u = 0.0f; v[0].texture_position.v = 1.0f; v[1].world.xyz.x = (width*ca + height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x; v[1].world.xyz.y = (width*sa - height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y; v[1].world.xyz.z = pnt->world.xyz.z; v[1].screen.xyw.w = 0.0f; v[1].texture_position.u = 1.0f; v[1].texture_position.v = 1.0f; v[2].world.xyz.x = (width*ca - height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x; v[2].world.xyz.y = (width*sa + height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y; v[2].world.xyz.z = pnt->world.xyz.z; v[2].screen.xyw.w = 0.0f; v[2].texture_position.u = 1.0f; v[2].texture_position.v = 0.0f; v[3].world.xyz.x = (-width*ca - height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x; v[3].world.xyz.y = (-width*sa + height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y; v[3].world.xyz.z = pnt->world.xyz.z; v[3].screen.xyw.w = 0.0f; v[3].texture_position.u = 0.0f; v[3].texture_position.v = 0.0f; ubyte codes_and=0xff; float sw,z; z = pnt->world.xyz.z - rad / 4.0f; if ( z < 0.0f ) z = 0.0f; sw = 1.0f / z; for (i=0; i<4; i++ ) { //now code the four points codes_and &= g3_code_vertex(&v[i]); v[i].flags = 0; // mark as not yet projected } if (codes_and) return 1; //1 means off screen // clip and draw it g3_draw_poly_constant_sw(4, vertlist, tmap_flags, sw ); return 0; } /** As a define only for readability. */ #define TRIANGLE_AREA(_p, _q, _r) do {\ vec3d a, b, cross;\ \ a.xyz.x = _q->world.xyz.x - _p->world.xyz.x;\ a.xyz.y = _q->world.xyz.y - _p->world.xyz.y;\ a.xyz.z = 0.0f;\ \ b.xyz.x = _r->world.xyz.x - _p->world.xyz.x;\ b.xyz.y = _r->world.xyz.y - _p->world.xyz.y;\ b.xyz.z = 0.0f;\ \ vm_vec_cross(&cross, &a, &b);\ total_area += vm_vec_mag(&cross) * 0.5f;\ } while(0); float g3_get_poly_area(int nv, vertex **pointlist) { int idx; float total_area = 0.0f; // each triangle for(idx=1; idxcodes; cc.cc_or |= p->codes; } if (cc.cc_and){ return 0.0f; //all points off screen } if (cc.cc_or) { Assert( G3_count == 1 ); bufptr = clip_polygon(Vbuf0, Vbuf1, &nv, &cc, tmap_flags); if (nv && !(cc.cc_or & CC_BEHIND) && !cc.cc_and) { for (i=0;iflags&PF_PROJECTED)) g3_project_vertex(p); if (p->flags&PF_OVERFLOW) { goto free_points; } p->screen.xyw.w = constant_sw; } // check area p_area = g3_get_poly_area(nv, bufptr); if(p_area > area){ return 0.0f; } gr_tmapper( nv, bufptr, tmap_flags ); } free_points: ; for (i=0;iflags & PF_TEMP_POINT){ free_temp_point(bufptr[i]); } } } else { //now make list of 2d coords (& check for overflow) for (i=0;iflags&PF_PROJECTED)) g3_project_vertex(p); if (p->flags&PF_OVERFLOW) { return 0.0f; } p->screen.xyw.w = constant_sw; } // check area p_area = g3_get_poly_area(nv, bufptr); if(p_area > area){ return 0.0f; } gr_tmapper( nv, bufptr, tmap_flags ); } // how much area we drew return p_area; } //draws a bitmap with the specified 3d width & height //returns 1 if off screen, 0 if drew float g3_draw_rotated_bitmap_area(vertex *pnt,float angle, float rad,uint tmap_flags, float area) { vertex v[4]; vertex *vertlist[4] = { &v[3], &v[2], &v[1], &v[0] }; float sa, ca; int i; memset(v,0,sizeof(vertex)*4); Assert( G3_count == 1 ); angle+=Physics_viewer_bank; if ( angle < 0.0f ){ angle += PI2; } else if ( angle > PI2 ) { angle -= PI2; } sa = sinf(angle); ca = cosf(angle); float width, height; if ( tmap_flags & TMAP_FLAG_TEXTURED ) { int bw, bh; bm_get_info( gr_screen.current_bitmap, &bw, &bh, NULL ); if ( bw < bh ) { width = rad; height = width*i2fl(bh)/i2fl(bw); } else if ( bw > bh ) { height = rad; width = height*i2fl(bw)/i2fl(bh); } else { width = height = rad; } } else { width = height = rad; } v[0].world.xyz.x = (-width*ca + height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x; v[0].world.xyz.y = (-width*sa - height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y; v[0].world.xyz.z = pnt->world.xyz.z; v[0].screen.xyw.w = 0.0f; v[0].texture_position.u = 0.0f; v[0].texture_position.v = 1.0f; v[1].world.xyz.x = (width*ca + height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x; v[1].world.xyz.y = (width*sa - height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y; v[1].world.xyz.z = pnt->world.xyz.z; v[1].screen.xyw.w = 0.0f; v[1].texture_position.u = 1.0f; v[1].texture_position.v = 1.0f; v[2].world.xyz.x = (width*ca - height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x; v[2].world.xyz.y = (width*sa + height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y; v[2].world.xyz.z = pnt->world.xyz.z; v[2].screen.xyw.w = 0.0f; v[2].texture_position.u = 1.0f; v[2].texture_position.v = 0.0f; v[3].world.xyz.x = (-width*ca - height*sa)*Matrix_scale.xyz.x + pnt->world.xyz.x; v[3].world.xyz.y = (-width*sa + height*ca)*Matrix_scale.xyz.y + pnt->world.xyz.y; v[3].world.xyz.z = pnt->world.xyz.z; v[3].screen.xyw.w = 0.0f; v[3].texture_position.u = 0.0f; v[3].texture_position.v = 0.0f; ubyte codes_and=0xff; float sw,z; z = pnt->world.xyz.z - rad / 4.0f; if ( z < 0.0f ) z = 0.0f; sw = 1.0f / z; for (i=0; i<4; i++ ) { //now code the four points codes_and &= g3_code_vertex(&v[i]); v[i].flags = 0; // mark as not yet projected } if (codes_and){ return 0.0f; } // clip and draw it return g3_draw_poly_constant_sw_area(4, vertlist, tmap_flags, sw, area ); } #include "graphics/2d.h" typedef struct horz_pt { float x, y; int edge; } horz_pt; //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 ); } // Draws a polygon always facing the viewer. // compute the corners of a rod. fills in vertbuf. // Verts has any needs uv's or l's or can be NULL if none needed. int g3_draw_rod(const vec3d *p0, float width1, const vec3d *p1, float width2, vertex *verts, uint tmap_flags) { vec3d uvec, fvec, rvec, center; vm_vec_sub( &fvec, p0, p1 ); vm_vec_normalize_safe( &fvec ); vm_vec_avg( ¢er, p0, p1 ); vm_vec_sub( &rvec, &Eye_position, ¢er ); vm_vec_normalize( &rvec ); vm_vec_cross(&uvec,&fvec,&rvec); //normalize new perpendicular vector vm_vec_normalize(&uvec); //now recompute right vector, in case it wasn't entirely perpendiclar vm_vec_cross(&rvec,&uvec,&fvec); // Now have uvec, which is up vector and rvec which is the normal // of the face. int i; vec3d vecs[4]; vertex pts[4]; vertex *ptlist[4] = { &pts[3], &pts[2], &pts[1], &pts[0] }; vm_vec_scale_add( &vecs[0], p0, &uvec, width1/2.0f ); vm_vec_scale_add( &vecs[1], p1, &uvec, width2/2.0f ); vm_vec_scale_add( &vecs[2], p1, &uvec, -width2/2.0f ); vm_vec_scale_add( &vecs[3], p0, &uvec, -width1/2.0f ); for (i=0; i<4; i++ ) { if ( verts ) { pts[i] = verts[i]; } g3_transfer_vertex( &pts[i], &vecs[i] ); } ptlist[0]->texture_position.u = 0.0f; ptlist[0]->texture_position.v = 0.0f; ptlist[0]->r = gr_screen.current_color.red; ptlist[0]->g = gr_screen.current_color.green; ptlist[0]->b = gr_screen.current_color.blue; ptlist[0]->a = gr_screen.current_color.alpha; ptlist[1]->texture_position.u = 1.0f; ptlist[1]->texture_position.v = 0.0f; ptlist[1]->r = gr_screen.current_color.red; ptlist[1]->g = gr_screen.current_color.green; ptlist[1]->b = gr_screen.current_color.blue; ptlist[1]->a = gr_screen.current_color.alpha; ptlist[2]->texture_position.u = 1.0f; ptlist[2]->texture_position.v = 1.0f; ptlist[2]->r = gr_screen.current_color.red; ptlist[2]->g = gr_screen.current_color.green; ptlist[2]->b = gr_screen.current_color.blue; ptlist[2]->a = gr_screen.current_color.alpha; ptlist[3]->texture_position.u = 0.0f; ptlist[3]->texture_position.v = 1.0f; ptlist[3]->r = gr_screen.current_color.red; ptlist[3]->g = gr_screen.current_color.green; ptlist[3]->b = gr_screen.current_color.blue; ptlist[3]->a = gr_screen.current_color.alpha; return g3_draw_poly(4,ptlist,tmap_flags); } #define MAX_ROD_VECS 100 int g3_draw_rod(int num_points, const vec3d *pvecs, float width, uint tmap_flags) { vec3d uvec, fvec, rvec; vec3d vecs[2]; vertex pts[MAX_ROD_VECS]; vertex *ptlist[MAX_ROD_VECS]; int i, nv = 0; Assert( num_points >= 2 ); Assert( (num_points * 2) <= MAX_ROD_VECS ); 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_VECS-2) { Warning(LOCATION, "Hit high-water mark (%i) in g3_draw_rod()!!\n", MAX_ROD_VECS); break; } ptlist[nv] = &pts[nv]; ptlist[nv+1] = &pts[nv+1]; g3_transfer_vertex( &pts[nv], &vecs[0] ); g3_transfer_vertex( &pts[nv+1], &vecs[1] ); ptlist[nv]->texture_position.u = 1.0f; ptlist[nv]->texture_position.v = i2fl(i); ptlist[nv]->r = gr_screen.current_color.red; ptlist[nv]->g = gr_screen.current_color.green; ptlist[nv]->b = gr_screen.current_color.blue; ptlist[nv]->a = gr_screen.current_color.alpha; ptlist[nv+1]->texture_position.u = 0.0f; ptlist[nv+1]->texture_position.v = i2fl(i); ptlist[nv+1]->r = gr_screen.current_color.red; ptlist[nv+1]->g = gr_screen.current_color.green; ptlist[nv+1]->b = gr_screen.current_color.blue; ptlist[nv+1]->a = gr_screen.current_color.alpha; nv += 2; } // we should always have at least 4 verts, and there should always be an even number Assert( (nv >= 4) && !(nv % 2) ); int rc = g3_draw_poly(nv, ptlist, tmap_flags); return rc; } // 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 } } } }; #define MAX_PERSPECTIVE_DIVISIONS 5 // should never even come close to this limit 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); } // draw a perspective bitmap based on angles and radius float p_phi = 10.0f; float p_theta = 10.0f; int g3_draw_perspective_bitmap(const angles *a, float scale_x, float scale_y, int div_x, int div_y, uint tmap_flags) { vec3d s_points[MAX_PERSPECTIVE_DIVISIONS+1][MAX_PERSPECTIVE_DIVISIONS+1]; vec3d t_points[MAX_PERSPECTIVE_DIVISIONS+1][MAX_PERSPECTIVE_DIVISIONS+1]; vertex v[4]; vertex *verts[4]; matrix m, m_bank; int idx, s_idx; int saved_zbuffer_mode; float ui, vi; angles bank_first; // cap division values // div_x = div_x > MAX_PERSPECTIVE_DIVISIONS ? MAX_PERSPECTIVE_DIVISIONS : div_x; div_x = 1; div_y = div_y > MAX_PERSPECTIVE_DIVISIONS ? MAX_PERSPECTIVE_DIVISIONS : div_y; // texture increment values ui = 1.0f / (float)div_x; vi = 1.0f / (float)div_y; // adjust for aspect ratio scale_x *= ((float)gr_screen.max_w / (float)gr_screen.max_h) + 0.55f; // fudge factor float s_phi = 0.5f + (((p_phi * scale_x) / 360.0f) / 2.0f); float s_theta = (((p_theta * scale_y) / 360.0f) / 2.0f); float d_phi = -(((p_phi * scale_x) / 360.0f) / (float)(div_x)); float d_theta = -(((p_theta * scale_y) / 360.0f) / (float)(div_y)); // bank matrix bank_first.p = 0.0f; bank_first.b = a->b; bank_first.h = 0.0f; vm_angles_2_matrix(&m_bank, &bank_first); // convert angles to matrix angles a_temp = *a; a_temp.b = 0.0f; vm_angles_2_matrix(&m, &a_temp); // generate the bitmap points for(idx=0; idx<=div_x; idx++){ for(s_idx=0; s_idx<=div_y; s_idx++){ // get world spherical coords stars_project_2d_onto_sphere(&s_points[idx][s_idx], 1000.0f, s_phi + ((float)idx*d_phi), s_theta + ((float)s_idx*d_theta)); // bank the bitmap first vm_vec_rotate(&t_points[idx][s_idx], &s_points[idx][s_idx], &m_bank); // rotate on the sphere vm_vec_rotate(&s_points[idx][s_idx], &t_points[idx][s_idx], &m); } } // turn off zbuffering saved_zbuffer_mode = gr_zbuffer_get(); gr_zbuffer_set(GR_ZBUFF_NONE); // turn off culling int cull = gr_set_cull(0); // render all polys for(idx=0; idxbitmap_2d_poly_list_size){ if(bitmap_2d_poly_list)delete[]bitmap_2d_poly_list; if(bitmap_2d_poly_vertlist)delete[]bitmap_2d_poly_vertlist; bitmap_2d_poly_list = new vertex[6* n_bm]; bitmap_2d_poly_vertlist = new vertex*[6*n_bm]; for(int i = 0; i<6*n_bm; i++)bitmap_2d_poly_vertlist[i] = &bitmap_2d_poly_list[i]; memset(bitmap_2d_poly_list,0,sizeof(vertex)*6*n_bm); } g3_start_frame(1); // turn off zbuffering saved_zbuffer_mode = gr_zbuffer_get(); gr_zbuffer_set(GR_ZBUFF_NONE); for(int i = 0; iscreen.xyw.x = (float)b_list[i].x; V->screen.xyw.y = (float)b_list[i].y; V->screen.xyw.w = 0.0f; V->texture_position.u = 0.0f; V->texture_position.v = 0.0f; V->flags = PF_PROJECTED; V->codes = 0; V++; V->screen.xyw.x = (float)(b_list[i].x + b_list[i].w); V->screen.xyw.y = (float)b_list[i].y; V->screen.xyw.w = 0.0f; V->texture_position.u = 1.0f; V->texture_position.v = 0.0f; V->flags = PF_PROJECTED; V->codes = 0; V++; V->screen.xyw.x = (float)(b_list[i].x + b_list[i].w); V->screen.xyw.y = (float)(b_list[i].y + b_list[i].h); V->screen.xyw.w = 0.0f; V->texture_position.u = 1.0f; V->texture_position.v = 1.0f; V->flags = PF_PROJECTED; V->codes = 0; //tri two V++; V->screen.xyw.x = (float)b_list[i].x; V->screen.xyw.y = (float)b_list[i].y; V->screen.xyw.w = 0.0f; V->texture_position.u = 0.0f; V->texture_position.v = 0.0f; V->flags = PF_PROJECTED; V->codes = 0; V++; V->screen.xyw.x = (float)(b_list[i].x + b_list[i].w); V->screen.xyw.y = (float)(b_list[i].y + b_list[i].h); V->screen.xyw.w = 0.0f; V->texture_position.u = 1.0f; V->texture_position.v = 1.0f; V->flags = PF_PROJECTED; V->codes = 0; V++; V->screen.xyw.x = (float)b_list[i].x; V->screen.xyw.y = (float)(b_list[i].y + b_list[i].h); V->screen.xyw.w = 0.0f; V->texture_position.u = 0.0f; V->texture_position.v = 1.0f; V->flags = PF_PROJECTED; V->codes = 0; } // set debrief ret = g3_draw_poly_constant_sw(6*n_bm, bitmap_2d_poly_vertlist, TMAP_FLAG_TEXTURED | TMAP_FLAG_TRILIST | additional_tmap_flags, 0.1f); g3_end_frame(); gr_zbuffer_set(saved_zbuffer_mode); return ret; } //draws an array of bitmap_rect_list objects //see tmapper.h for explaination of structure bitmap_rect_list int g3_draw_2d_poly_bitmap_rect_list(bitmap_rect_list* b_list, int n_bm, uint additional_tmap_flags) { int ret; int saved_zbuffer_mode; if(n_bm>bitmap_2d_poly_list_size){ if(bitmap_2d_poly_list)delete[]bitmap_2d_poly_list; if(bitmap_2d_poly_vertlist)delete[]bitmap_2d_poly_vertlist; bitmap_2d_poly_list = new vertex[6* n_bm]; bitmap_2d_poly_vertlist = new vertex*[6*n_bm]; for(int i = 0; i<6*n_bm; i++)bitmap_2d_poly_vertlist[i] = &bitmap_2d_poly_list[i]; } g3_start_frame(1); // turn off zbuffering saved_zbuffer_mode = gr_zbuffer_get(); gr_zbuffer_set(GR_ZBUFF_NONE); for(int i = 0; iscreen.xyw.x = (float)b->x; V->screen.xyw.y = (float)b->y; V->screen.xyw.w = 0.0f; V->texture_position.u = (float)t->u0; V->texture_position.v = (float)t->v0; V->flags = PF_PROJECTED; V->codes = 0; V++; V->screen.xyw.x = (float)(b->x + b->w); V->screen.xyw.y = (float)b->y; V->screen.xyw.w = 0.0f; V->texture_position.u = (float)t->u1; V->texture_position.v = (float)t->v0; V->flags = PF_PROJECTED; V->codes = 0; V++; V->screen.xyw.x = (float)(b->x + b->w); V->screen.xyw.y = (float)(b->y + b->h); V->screen.xyw.w = 0.0f; V->texture_position.u = (float)t->u1; V->texture_position.v = (float)t->v1; V->flags = PF_PROJECTED; V->codes = 0; //tri two V++; V->screen.xyw.x = (float)b->x; V->screen.xyw.y = (float)b->y; V->screen.xyw.w = 0.0f; V->texture_position.u = (float)t->u0; V->texture_position.v = (float)t->v0; V->flags = PF_PROJECTED; V->codes = 0; V++; V->screen.xyw.x = (float)(b->x + b->w); V->screen.xyw.y = (float)(b->y + b->h); V->screen.xyw.w = 0.0f; V->texture_position.u = (float)t->u1; V->texture_position.v = (float)t->v1; V->flags = PF_PROJECTED; V->codes = 0; V++; V->screen.xyw.x = (float)b->x; V->screen.xyw.y = (float)(b->y + b->h); V->screen.xyw.w = 0.0f; V->texture_position.u = (float)t->u0; V->texture_position.v = (float)t->v1; V->flags = PF_PROJECTED; V->codes = 0; } // set debrief ret = g3_draw_poly_constant_sw(6*n_bm, bitmap_2d_poly_vertlist, TMAP_FLAG_TEXTURED | TMAP_FLAG_TRILIST | additional_tmap_flags, 0.1f); g3_end_frame(); gr_zbuffer_set(saved_zbuffer_mode); return ret; } void g3_draw_htl_line(const vec3d *start, const vec3d *end) { gr_line_htl(start, end); } void g3_draw_htl_sphere(const vec3d* position, float radius) { g3_start_instance_matrix(position, &vmd_identity_matrix, true); gr_sphere_htl(radius); g3_done_instance(true); } //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++// //flash ball stuff void flash_ball::initialize(int 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; } int 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, outer, inner); vm_vec_random_cone(&end, dir, outer, inner); 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) { int n; int nverts = w(off+bsp_data+8); int offset = w(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, outer, inner); 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(float rad, float intinsity, float life){ flash_ball::batcher.allocate(n_rays); for(int i = 0; i