/**********************************************************************
This file is part of Crack dot Com's free source code release of
Golgotha.
for
information about compiling & licensing issues visit this URL
If that doesn't help, contact Jonathan Clark at golgotha_source@usa.net (Subject should have "GOLG" in it) ***********************************************************************/ #include#include #include #include #include "r1_clip.hh" #include "software/r1_software.hh" #include "software/r1_software_texture.hh" #include "software/r1_software_globals.hh" #include "software/mappers.hh" #include "software/inline_fpu.hh" #include "time/profile.hh" static i4_profile_class pf_software_render_poly("software::render_poly"); #define R1_ABS(x) ((x)>0)?(x):(-(x)) r1_software_class::r1_software_class() { last_node=0; cur_s_width = 0; cur_t_height = 0; render_device_flags = R1_SOFTWARE; allow_backfaces = i4_T; use_spans = i4_T; do_perspective = i4_T; do_flat = i4_F; lock_cheat = i4_F; cur_color_tint = -1; holy_mode = i4_F; alphatexture_mode = i4_F; } r1_software_class::~r1_software_class() { } void r1_software_class::uninit() { delete tmanager; tmanager = 0; } void r1_software_class::set_write_mode(r1_write_mask_type mask) { write_mask=mask; //update_tri_function(); not needed on a write mask change } void r1_software_class::set_alpha_mode(r1_alpha_type type) { if (alpha_mode != type) { alpha_mode = type; update_tri_function(); } } void r1_software_class::set_shading_mode(r1_shading_type type) { if (shade_mode != type) { shade_mode = type; update_tri_function(); } } static i4_profile_class pf_software_select_texture("software select texture"); void r1_software_class::use_texture(r1_texture_handle material_ref, sw32 desired_width, w32 frame) { if (!tmanager->valid_handle(material_ref)) { disable_texture(); return; } sw32 w,h; pf_software_select_texture.start(); r1_miplevel_t *mip = tmanager->get_texture(material_ref, frame, desired_width, w,h); if (!mip) { disable_texture(); return ; } r1_software_texture_class::used_node *n = (r1_software_texture_class::used_node *)mip->vram_handle; r1_software_texture_class::free_node *f = (r1_software_texture_class::free_node *)n->node; r1_software_texture_ptr = (w16 *)f->start; r1_software_texture_width = f->mip->width; r1_software_texture_height = f->mip->height; switch (f->mip->width) { case 1: r1_software_twidth_log2 = 0; break; case 2: r1_software_twidth_log2 = 1; break; case 4: r1_software_twidth_log2 = 2; break; case 8: r1_software_twidth_log2 = 3; break; case 16: r1_software_twidth_log2 = 4; break; case 32: r1_software_twidth_log2 = 5; break; case 64: r1_software_twidth_log2 = 6; break; case 128: r1_software_twidth_log2 = 7; break; case 256: r1_software_twidth_log2 = 8; break; case 512: r1_software_twidth_log2 = 9; break; case 1024: r1_software_twidth_log2 = 10; break; } // don't select the texture again if it's the same one we used last time if (mip) { if (mip->entry->is_transparent()) { holy_mode = i4_T; alphatexture_mode = i4_F; } else if (mip->entry->is_alphatexture()) { alphatexture_mode = i4_T; holy_mode = i4_F; } else { holy_mode = i4_F; alphatexture_mode = i4_F; } if (mip != last_node) { ((r1_software_texture_class *)tmanager)->select_texture(mip->vram_handle, cur_s_width, cur_t_height); last_node = mip; } } texture_mode=i4_T; update_tri_function(); pf_software_select_texture.stop(); } // drawing will be done with the constant color if textures are disabled void r1_software_class::disable_texture() { texture_mode=i4_F; last_node=0; holy_mode=i4_F; alphatexture_mode=i4_F; update_tri_function(); } void r1_software_class::set_z_range(i4_float near_z, i4_float far_z) { r1_near_clip_z = near_z; r1_far_clip_z = far_z; } //initialized during init_ctable void r1_software_class::prepare_verts(s_vert *s_verts, r1_vert *r1_verts, sw32 num_verts) { sw32 i; s_vert *s_v = s_verts; r1_vert *r1_v = r1_verts; start_ceil(); //qftoi will use the ceil() function to convert floats to ints if (r1_software_render_expand_type!=R1_COPY_1x1_SCANLINE_SKIP) { for (i=0;i px = r1_v->px; s_v->py = r1_v->py; if (s_v->px < 0.0001) s_v->px = 0; else if (s_v->px > width_compare) s_v->px = width_compare; if (s_v->py < 0.0001) s_v->py = 0; else if (s_v->py > height_compare) s_v->py = height_compare; s_v->ix = qftoi(s_v->px); s_v->iy = qftoi(s_v->py); s_v->ooz = r1_v->w; s_v++; r1_v++; } } else { for (i=0;i px = r1_v->px; s_v->py = r1_v->py*0.5; if (s_v->px < 0.0001) s_v->px = 0; else if (s_v->px > width_compare) s_v->px = width_compare; if (s_v->py < 0.0001) s_v->py = 0; else if (s_v->py > (height_compare>>1)) s_v->py = (height_compare>>1); s_v->ix = qftoi(s_v->px); s_v->iy = qftoi(s_v->py); s_v->ooz = r1_v->w; s_v++; r1_v++; } } stop_ceil(); w32 r,g,b; start_trunc(); //qftoi will now truncate floats to ints switch (shade_mode) { case R1_CONSTANT_SHADING: //whenever set_constant_color is called, cur_color //is adjusted for the proper bit depth / display mode s_verts->color = cur_color; break; case R1_COLORED_SHADING: s_v = s_verts; r1_v = r1_verts; for (i=0;i r*red_clight_mul); g = qftoi(r1_v->g*green_clight_mul); b = qftoi(r1_v->b*blue_clight_mul); s_v->color = (r< color = s_v->color | (s_v->color << 16); s_v++; r1_v++; } //even in colored shade mode, we need to calculate l case R1_WHITE_SHADING: s_v = s_verts; r1_v = r1_verts; for (i=0;i l = qftoi(r1_v->r*(i4_float)NUM_LIGHT_SHADES); #ifdef DEBUG if (s_v->l > NUM_LIGHT_SHADES || s_v->l < 0) { if (s_v->l < 0) s_v->l = 0; else if (s_v->l > NUM_LIGHT_SHADES) s_v->l = NUM_LIGHT_SHADES; i4_warning("Bad lighting value"); } #endif s_v++; r1_v++; } break; } stop_trunc(); if (texture_mode) { s_vert *s_v = s_verts; r1_vert *r1_v = r1_verts; for (i=0;i s > 1.0001f || r1_v->s < -0.0001f) { // i4_warning("bad s texture coordinate"); } if (r1_v->t > 1.0001f || r1_v->t < -0.0001f) { // i4_warning("bad t texture coordinate"); } #endif //not perspective correct just yet, the poly might be affine mapped s_v->st_projected = i4_F; //software safety: computes texture coordinates as though valid texture ranges are from 0.5 to max-0.5 //decreases chance of stepping off texture. *could* be replaced to go from 0 to max but its probably risky s_v->s = (r1_v->s * (cur_s_width - 1.f) ) + 0.5f; s_v->t = (r1_v->t * (cur_t_height - 1.f) ) + 0.5f; //cap the values //this almost certainly cannot be removed since the maxtool often produces invalid texture coordinates, //although you might be able to cap it to the range [0,max] instead of [0.5, max-0.5] if (s_v->s < 0.5f) { s_v->s = 0.5f; } else if (s_v->s > cur_s_width - 0.5f) { s_v->s = cur_s_width - 0.5f; } if (s_v->t < 0.5f) { s_v->t = 0.5f; } else if (s_v->t > cur_t_height - 0.5f) { s_v->t = cur_t_height - 0.5f; } s_v++; r1_v++; } } } s_vert temp_s_verts[128]; void r1_software_class::render_poly(int t_verts, r1_vert *verts) { if (t_verts>128) return; if (!poly_setup_functions[small_poly_type]) return; if (!poly_setup_functions[big_poly_type]) return; pf_software_render_poly.start(); prepare_verts(temp_s_verts,verts,t_verts); s_vert *v0 = &temp_s_verts[0]; s_vert *v1 = &temp_s_verts[1]; s_vert *v2 = &temp_s_verts[2]; sw32 i; //make sure polys are valid //compute the area of the whole polygon tri_area_struct *t = triangle_info; total_poly_area = 0; for (i=1; i calc_area(v0->px, v1->px, v2->px, v0->py, v1->py, v2->py); total_poly_area += t->area; v1++; v2++; } //if its < 500 pixels, draw a "small" polygon (affine mapped) start_trunc(); //everything inside here pretty much uses the truncation qftoi() if (!do_perspective || ((R1_ABS(total_poly_area)) < 500) ) { //setup as a small poly (poly_setup_functions[small_poly_type])(temp_s_verts,t_verts); } else { //setup as a big poly (poly_setup_functions[big_poly_type])(temp_s_verts,t_verts); } stop_trunc(); pf_software_render_poly.stop(); } void r1_software_class::render_sprite(r1_vert *verts) { if (!poly_setup_functions[small_poly_type]) return; pf_software_render_poly.start(); prepare_verts(temp_s_verts,verts,4); tri_area_struct *t = triangle_info; total_poly_area = 0; s_vert *v0 = &temp_s_verts[0]; s_vert *v1 = &temp_s_verts[1]; s_vert *v2 = &temp_s_verts[2]; sw32 i; for (i=1; i<3; i++,t++) { t->calc_area(v0->px, v1->px, v2->px, v0->py, v1->py, v2->py); total_poly_area += t->area; v1++; v2++; } start_trunc(); //special case alpha-blended sprites if (small_poly_type==SPAN_TRI_AFFINE_UNLIT_ALPHA) { small_poly_type=SPAN_TRI_AFFINE_UNLIT_ALPHA_SPRITE; //sets it up as a sprite (doesnt do some of the unnecessary steps required on polygons) sprite_setup_affine_unlit_alpha(temp_s_verts,4); } else { //set it up as a polygon poly_setup_functions[small_poly_type](temp_s_verts,4); } stop_trunc(); pf_software_render_poly.stop(); } void r1_software_class::render_lines(int t_lines, r1_vert *verts) { if (num_buffered_lines>=max_soft_lines) return; sw32 i; w32 r,g,b; i4_float fr,fg,fb; w16 start_color,end_color; start_trunc(); for (i=0;i 1.f) fr = 1.f; fg = verts[i].g;// + verts[i+1].g; if (fg > 1.f) fg = 1.f; fb = verts[i].b;// + verts[i+1].b; if (fb > 1.f) fb = 1.f; r = qftoi(fr*red_clight_mul); g = qftoi(fg*green_clight_mul); b = qftoi(fb*blue_clight_mul); start_color = (r< 1.f) fr = 1.f; fg = verts[i+1].g;// + verts[i+1].g; if (fg > 1.f) fg = 1.f; fb = verts[i+1].b;// + verts[i+1].b; if (fb > 1.f) fb = 1.f; r = qftoi(fr*red_clight_mul); g = qftoi(fg*green_clight_mul); b = qftoi(fb*blue_clight_mul); end_color = (r< > 16; w32 g = (const_color & 0x0000FF00) >> 8; w32 b = (const_color & 0x000000FF); r = r >> (8-fmt.red_bits); g = g >> (8-fmt.green_bits); b = b >> (8-fmt.blue_bits); cur_color = (r< ) is //going to be drawn. this type will then be used as an index into the arrays //of function pointers (initialized in mappers.cc) to call functions that //handle that type of polygon / triangle if (texture_mode) { if (alpha_mode==R1_ALPHA_DISABLED) { if (shade_mode==R1_SHADE_DISABLED) { if (holy_mode) { small_poly_type = SPAN_TRI_AFFINE_UNLIT_HOLY; big_poly_type = SPAN_TRI_PERSPECTIVE_UNLIT_HOLY; } else if (alphatexture_mode) { small_poly_type = SPAN_TRI_AFFINE_UNLIT_ALPHA; big_poly_type = SPAN_TRI_PERSPECTIVE_UNLIT_ALPHA; } else { small_poly_type = SPAN_TRI_AFFINE_UNLIT; big_poly_type = SPAN_TRI_PERSPECTIVE_UNLIT; } } else if (shade_mode==R1_CONSTANT_SHADING) { small_poly_type = SPAN_TRI_SOLID_FILL; big_poly_type = SPAN_TRI_SOLID_FILL; } else { if (holy_mode) { small_poly_type = SPAN_TRI_AFFINE_UNLIT_HOLY; big_poly_type = SPAN_TRI_PERSPECTIVE_UNLIT_HOLY; } else if (alphatexture_mode) { small_poly_type = SPAN_TRI_AFFINE_UNLIT_ALPHA; big_poly_type = SPAN_TRI_PERSPECTIVE_UNLIT_ALPHA; } else { small_poly_type = SPAN_TRI_AFFINE_LIT; big_poly_type = SPAN_TRI_PERSPECTIVE_LIT; } } } else { //need a tmapper that does alpha if (holy_mode) { small_poly_type = SPAN_TRI_AFFINE_UNLIT_HOLY_BLEND; big_poly_type = SPAN_TRI_PERSPECTIVE_UNLIT_HOLY; } else if (alphatexture_mode) { small_poly_type = SPAN_TRI_AFFINE_UNLIT_ALPHA; big_poly_type = SPAN_TRI_PERSPECTIVE_UNLIT_ALPHA; } else { small_poly_type = SPAN_TRI_AFFINE_UNLIT; big_poly_type = SPAN_TRI_PERSPECTIVE_UNLIT; } } } else { if (alpha_mode==R1_ALPHA_DISABLED) { small_poly_type = SPAN_TRI_SOLID_FILL; big_poly_type = SPAN_TRI_SOLID_FILL; } else { switch(shade_mode) { case R1_SHADE_DISABLED: case R1_WHITE_SHADING: small_poly_type = SPAN_TRI_UNDEFINED; big_poly_type = SPAN_TRI_UNDEFINED; break; case R1_CONSTANT_SHADING: case R1_COLORED_SHADING: small_poly_type = SPAN_TRI_SOLID_BLEND; big_poly_type = SPAN_TRI_SOLID_BLEND; break; } } } } void r1_software_class::modify_features(w32 feature_bits, int on) { if (feature_bits & R1_SPANS) { if (on) use_spans = i4_T; else use_spans = i4_F; } if (feature_bits & R1_PERSPECTIVE_CORRECT) { if (on) do_perspective = i4_T; else do_perspective = i4_F; } if (feature_bits & R1_LOCK_CHEAT) { if (on) lock_cheat = i4_T; else lock_cheat = i4_F; } if (feature_bits & R1_Z_BIAS) { //maybe re-implement this? //if (on) // z_bias = 0.f;//0.01f; //else // z_bias = 0.f; } } i4_bool r1_software_class::is_feature_on(w32 feature_bits) { if (feature_bits & R1_SPANS) return use_spans; if (feature_bits & R1_PERSPECTIVE_CORRECT) return do_perspective; if (feature_bits & R1_LOCK_CHEAT) return lock_cheat; if (feature_bits & R1_Z_BIAS) { //if (z_bias!=0.f) // return i4_T; //else // return i4_F; } return i4_F; }