From 1fba10104e76e937eeac60bc207a74012ab936dc Mon Sep 17 00:00:00 2001 From: Tim Angus Date: Fri, 15 Feb 2013 23:46:37 +0000 Subject: renderer -> renderergl1, rend2 -> renderergl2 --- Makefile | 340 +-- src/client/client.h | 2 +- src/null/null_glimp.c | 2 +- src/rend2/glsl/bokeh_fp.glsl | 70 - src/rend2/glsl/bokeh_vp.glsl | 13 - src/rend2/glsl/calclevels4x_fp.glsl | 55 - src/rend2/glsl/calclevels4x_vp.glsl | 13 - src/rend2/glsl/depthblur_fp.glsl | 58 - src/rend2/glsl/depthblur_vp.glsl | 12 - src/rend2/glsl/dlight_fp.glsl | 12 - src/rend2/glsl/dlight_vp.glsl | 92 - src/rend2/glsl/down4x_fp.glsl | 34 - src/rend2/glsl/down4x_vp.glsl | 13 - src/rend2/glsl/fogpass_fp.glsl | 9 - src/rend2/glsl/fogpass_vp.glsl | 117 - src/rend2/glsl/generic_fp.glsl | 43 - src/rend2/glsl/generic_vp.glsl | 251 -- src/rend2/glsl/lightall_fp.glsl | 383 --- src/rend2/glsl/lightall_vp.glsl | 219 -- src/rend2/glsl/pshadow_fp.glsl | 98 - src/rend2/glsl/pshadow_vp.glsl | 17 - src/rend2/glsl/shadowfill_fp.glsl | 41 - src/rend2/glsl/shadowfill_vp.glsl | 89 - src/rend2/glsl/shadowmask_fp.glsl | 127 - src/rend2/glsl/shadowmask_vp.glsl | 18 - src/rend2/glsl/ssao_fp.glsl | 86 - src/rend2/glsl/ssao_vp.glsl | 12 - src/rend2/glsl/texturecolor_fp.glsl | 12 - src/rend2/glsl/texturecolor_vp.glsl | 15 - src/rend2/glsl/tonemap_fp.glsl | 48 - src/rend2/glsl/tonemap_vp.glsl | 13 - src/rend2/qgl.h | 755 ------ src/rend2/tr_animation.c | 658 ----- src/rend2/tr_backend.c | 1843 -------------- src/rend2/tr_bsp.c | 3370 -------------------------- src/rend2/tr_cmds.c | 668 ----- src/rend2/tr_curve.c | 806 ------- src/rend2/tr_extensions.c | 667 ----- src/rend2/tr_extramath.c | 233 -- src/rend2/tr_extramath.h | 101 - src/rend2/tr_extratypes.h | 43 - src/rend2/tr_fbo.c | 861 ------- src/rend2/tr_fbo.h | 64 - src/rend2/tr_flares.c | 532 ---- src/rend2/tr_font.c | 554 ----- src/rend2/tr_glsl.c | 1932 --------------- src/rend2/tr_image.c | 3462 -------------------------- src/rend2/tr_image_bmp.c | 243 -- src/rend2/tr_image_jpg.c | 441 ---- src/rend2/tr_image_pcx.c | 179 -- src/rend2/tr_image_png.c | 2490 ------------------- src/rend2/tr_image_tga.c | 324 --- src/rend2/tr_init.c | 1480 ------------ src/rend2/tr_light.c | 447 ---- src/rend2/tr_local.h | 2856 ---------------------- src/rend2/tr_main.c | 2882 ---------------------- src/rend2/tr_marks.c | 466 ---- src/rend2/tr_mesh.c | 405 ---- src/rend2/tr_model.c | 1583 ------------ src/rend2/tr_model_iqm.c | 1058 -------- src/rend2/tr_noise.c | 93 - src/rend2/tr_postprocess.c | 488 ---- src/rend2/tr_postprocess.h | 33 - src/rend2/tr_scene.c | 520 ---- src/rend2/tr_shade.c | 1866 -------------- src/rend2/tr_shade_calc.c | 1339 ----------- src/rend2/tr_shader.c | 3741 ----------------------------- src/rend2/tr_shadows.c | 343 --- src/rend2/tr_sky.c | 916 ------- src/rend2/tr_subs.c | 48 - src/rend2/tr_surface.c | 1698 ------------- src/rend2/tr_vbo.c | 928 ------- src/rend2/tr_world.c | 850 ------- src/renderer/iqm.h | 129 - src/renderer/qgl.h | 381 --- src/renderer/tr_animation.c | 659 ----- src/renderer/tr_backend.c | 1162 --------- src/renderer/tr_bsp.c | 1870 -------------- src/renderer/tr_cmds.c | 591 ----- src/renderer/tr_curve.c | 626 ----- src/renderer/tr_flares.c | 529 ---- src/renderer/tr_font.c | 555 ----- src/renderer/tr_image.c | 1608 ------------- src/renderer/tr_image_bmp.c | 240 -- src/renderer/tr_image_jpg.c | 438 ---- src/renderer/tr_image_pcx.c | 176 -- src/renderer/tr_image_png.c | 2486 ------------------- src/renderer/tr_image_tga.c | 321 --- src/renderer/tr_init.c | 1375 ----------- src/renderer/tr_light.c | 395 --- src/renderer/tr_local.h | 1728 ------------- src/renderer/tr_main.c | 1399 ----------- src/renderer/tr_marks.c | 459 ---- src/renderer/tr_mesh.c | 417 ---- src/renderer/tr_model.c | 1318 ---------- src/renderer/tr_model_iqm.c | 1058 -------- src/renderer/tr_noise.c | 92 - src/renderer/tr_public.h | 196 -- src/renderer/tr_scene.c | 412 ---- src/renderer/tr_shade.c | 1528 ------------ src/renderer/tr_shade_calc.c | 1217 ---------- src/renderer/tr_shader.c | 3068 ----------------------- src/renderer/tr_shadows.c | 344 --- src/renderer/tr_sky.c | 846 ------- src/renderer/tr_subs.c | 48 - src/renderer/tr_surface.c | 1245 ---------- src/renderer/tr_types.h | 221 -- src/renderer/tr_world.c | 669 ------ src/renderercommon/iqm.h | 129 + src/renderercommon/tr_public.h | 196 ++ src/renderercommon/tr_types.h | 221 ++ src/renderergl1/qgl.h | 381 +++ src/renderergl1/tr_animation.c | 659 +++++ src/renderergl1/tr_backend.c | 1162 +++++++++ src/renderergl1/tr_bsp.c | 1870 ++++++++++++++ src/renderergl1/tr_cmds.c | 591 +++++ src/renderergl1/tr_curve.c | 626 +++++ src/renderergl1/tr_flares.c | 529 ++++ src/renderergl1/tr_font.c | 555 +++++ src/renderergl1/tr_image.c | 1608 +++++++++++++ src/renderergl1/tr_image_bmp.c | 240 ++ src/renderergl1/tr_image_jpg.c | 438 ++++ src/renderergl1/tr_image_pcx.c | 176 ++ src/renderergl1/tr_image_png.c | 2486 +++++++++++++++++++ src/renderergl1/tr_image_tga.c | 321 +++ src/renderergl1/tr_init.c | 1375 +++++++++++ src/renderergl1/tr_light.c | 395 +++ src/renderergl1/tr_local.h | 1728 +++++++++++++ src/renderergl1/tr_main.c | 1399 +++++++++++ src/renderergl1/tr_marks.c | 459 ++++ src/renderergl1/tr_mesh.c | 417 ++++ src/renderergl1/tr_model.c | 1318 ++++++++++ src/renderergl1/tr_model_iqm.c | 1058 ++++++++ src/renderergl1/tr_noise.c | 92 + src/renderergl1/tr_scene.c | 412 ++++ src/renderergl1/tr_shade.c | 1528 ++++++++++++ src/renderergl1/tr_shade_calc.c | 1217 ++++++++++ src/renderergl1/tr_shader.c | 3068 +++++++++++++++++++++++ src/renderergl1/tr_shadows.c | 343 +++ src/renderergl1/tr_sky.c | 846 +++++++ src/renderergl1/tr_subs.c | 48 + src/renderergl1/tr_surface.c | 1245 ++++++++++ src/renderergl1/tr_world.c | 669 ++++++ src/renderergl2/glsl/bokeh_fp.glsl | 70 + src/renderergl2/glsl/bokeh_vp.glsl | 13 + src/renderergl2/glsl/calclevels4x_fp.glsl | 55 + src/renderergl2/glsl/calclevels4x_vp.glsl | 13 + src/renderergl2/glsl/depthblur_fp.glsl | 58 + src/renderergl2/glsl/depthblur_vp.glsl | 12 + src/renderergl2/glsl/dlight_fp.glsl | 12 + src/renderergl2/glsl/dlight_vp.glsl | 92 + src/renderergl2/glsl/down4x_fp.glsl | 34 + src/renderergl2/glsl/down4x_vp.glsl | 13 + src/renderergl2/glsl/fogpass_fp.glsl | 9 + src/renderergl2/glsl/fogpass_vp.glsl | 117 + src/renderergl2/glsl/generic_fp.glsl | 43 + src/renderergl2/glsl/generic_vp.glsl | 251 ++ src/renderergl2/glsl/lightall_fp.glsl | 383 +++ src/renderergl2/glsl/lightall_vp.glsl | 219 ++ src/renderergl2/glsl/pshadow_fp.glsl | 98 + src/renderergl2/glsl/pshadow_vp.glsl | 17 + src/renderergl2/glsl/shadowfill_fp.glsl | 41 + src/renderergl2/glsl/shadowfill_vp.glsl | 89 + src/renderergl2/glsl/shadowmask_fp.glsl | 127 + src/renderergl2/glsl/shadowmask_vp.glsl | 18 + src/renderergl2/glsl/ssao_fp.glsl | 86 + src/renderergl2/glsl/ssao_vp.glsl | 12 + src/renderergl2/glsl/texturecolor_fp.glsl | 12 + src/renderergl2/glsl/texturecolor_vp.glsl | 15 + src/renderergl2/glsl/tonemap_fp.glsl | 48 + src/renderergl2/glsl/tonemap_vp.glsl | 13 + src/renderergl2/qgl.h | 755 ++++++ src/renderergl2/tr_animation.c | 658 +++++ src/renderergl2/tr_backend.c | 1843 ++++++++++++++ src/renderergl2/tr_bsp.c | 3370 ++++++++++++++++++++++++++ src/renderergl2/tr_cmds.c | 668 +++++ src/renderergl2/tr_curve.c | 806 +++++++ src/renderergl2/tr_extensions.c | 667 +++++ src/renderergl2/tr_extramath.c | 233 ++ src/renderergl2/tr_extramath.h | 101 + src/renderergl2/tr_extratypes.h | 43 + src/renderergl2/tr_fbo.c | 861 +++++++ src/renderergl2/tr_fbo.h | 64 + src/renderergl2/tr_flares.c | 532 ++++ src/renderergl2/tr_font.c | 554 +++++ src/renderergl2/tr_glsl.c | 1932 +++++++++++++++ src/renderergl2/tr_image.c | 3462 ++++++++++++++++++++++++++ src/renderergl2/tr_image_bmp.c | 243 ++ src/renderergl2/tr_image_jpg.c | 441 ++++ src/renderergl2/tr_image_pcx.c | 179 ++ src/renderergl2/tr_image_png.c | 2490 +++++++++++++++++++ src/renderergl2/tr_image_tga.c | 324 +++ src/renderergl2/tr_init.c | 1480 ++++++++++++ src/renderergl2/tr_light.c | 447 ++++ src/renderergl2/tr_local.h | 2856 ++++++++++++++++++++++ src/renderergl2/tr_main.c | 2882 ++++++++++++++++++++++ src/renderergl2/tr_marks.c | 466 ++++ src/renderergl2/tr_mesh.c | 405 ++++ src/renderergl2/tr_model.c | 1583 ++++++++++++ src/renderergl2/tr_model_iqm.c | 1058 ++++++++ src/renderergl2/tr_noise.c | 93 + src/renderergl2/tr_postprocess.c | 488 ++++ src/renderergl2/tr_postprocess.h | 33 + src/renderergl2/tr_scene.c | 520 ++++ src/renderergl2/tr_shade.c | 1866 ++++++++++++++ src/renderergl2/tr_shade_calc.c | 1339 +++++++++++ src/renderergl2/tr_shader.c | 3741 +++++++++++++++++++++++++++++ src/renderergl2/tr_shadows.c | 344 +++ src/renderergl2/tr_sky.c | 916 +++++++ src/renderergl2/tr_subs.c | 48 + src/renderergl2/tr_surface.c | 1698 +++++++++++++ src/renderergl2/tr_vbo.c | 928 +++++++ src/renderergl2/tr_world.c | 850 +++++++ src/sdl/sdl_gamma.c | 2 +- src/sdl/sdl_glimp.c | 3 +- 215 files changed, 76217 insertions(+), 76216 deletions(-) delete mode 100644 src/rend2/glsl/bokeh_fp.glsl delete mode 100644 src/rend2/glsl/bokeh_vp.glsl delete mode 100644 src/rend2/glsl/calclevels4x_fp.glsl delete mode 100644 src/rend2/glsl/calclevels4x_vp.glsl delete mode 100644 src/rend2/glsl/depthblur_fp.glsl delete mode 100644 src/rend2/glsl/depthblur_vp.glsl delete mode 100644 src/rend2/glsl/dlight_fp.glsl delete mode 100644 src/rend2/glsl/dlight_vp.glsl delete mode 100644 src/rend2/glsl/down4x_fp.glsl delete mode 100644 src/rend2/glsl/down4x_vp.glsl delete mode 100644 src/rend2/glsl/fogpass_fp.glsl delete mode 100644 src/rend2/glsl/fogpass_vp.glsl delete mode 100644 src/rend2/glsl/generic_fp.glsl delete mode 100644 src/rend2/glsl/generic_vp.glsl delete mode 100644 src/rend2/glsl/lightall_fp.glsl delete mode 100644 src/rend2/glsl/lightall_vp.glsl delete mode 100644 src/rend2/glsl/pshadow_fp.glsl delete mode 100644 src/rend2/glsl/pshadow_vp.glsl delete mode 100644 src/rend2/glsl/shadowfill_fp.glsl delete mode 100644 src/rend2/glsl/shadowfill_vp.glsl delete mode 100644 src/rend2/glsl/shadowmask_fp.glsl delete mode 100644 src/rend2/glsl/shadowmask_vp.glsl delete mode 100644 src/rend2/glsl/ssao_fp.glsl delete mode 100644 src/rend2/glsl/ssao_vp.glsl delete mode 100644 src/rend2/glsl/texturecolor_fp.glsl delete mode 100644 src/rend2/glsl/texturecolor_vp.glsl delete mode 100644 src/rend2/glsl/tonemap_fp.glsl delete mode 100644 src/rend2/glsl/tonemap_vp.glsl delete mode 100644 src/rend2/qgl.h delete mode 100644 src/rend2/tr_animation.c delete mode 100644 src/rend2/tr_backend.c delete mode 100644 src/rend2/tr_bsp.c delete mode 100644 src/rend2/tr_cmds.c delete mode 100644 src/rend2/tr_curve.c delete mode 100644 src/rend2/tr_extensions.c delete mode 100644 src/rend2/tr_extramath.c delete mode 100644 src/rend2/tr_extramath.h delete mode 100644 src/rend2/tr_extratypes.h delete mode 100644 src/rend2/tr_fbo.c delete mode 100644 src/rend2/tr_fbo.h delete mode 100644 src/rend2/tr_flares.c delete mode 100644 src/rend2/tr_font.c delete mode 100644 src/rend2/tr_glsl.c delete mode 100644 src/rend2/tr_image.c delete mode 100644 src/rend2/tr_image_bmp.c delete mode 100644 src/rend2/tr_image_jpg.c delete mode 100644 src/rend2/tr_image_pcx.c delete mode 100644 src/rend2/tr_image_png.c delete mode 100644 src/rend2/tr_image_tga.c delete mode 100644 src/rend2/tr_init.c delete mode 100644 src/rend2/tr_light.c delete mode 100644 src/rend2/tr_local.h delete mode 100644 src/rend2/tr_main.c delete mode 100644 src/rend2/tr_marks.c delete mode 100644 src/rend2/tr_mesh.c delete mode 100644 src/rend2/tr_model.c delete mode 100644 src/rend2/tr_model_iqm.c delete mode 100644 src/rend2/tr_noise.c delete mode 100644 src/rend2/tr_postprocess.c delete mode 100644 src/rend2/tr_postprocess.h delete mode 100644 src/rend2/tr_scene.c delete mode 100644 src/rend2/tr_shade.c delete mode 100644 src/rend2/tr_shade_calc.c delete mode 100644 src/rend2/tr_shader.c delete mode 100644 src/rend2/tr_shadows.c delete mode 100644 src/rend2/tr_sky.c delete mode 100644 src/rend2/tr_subs.c delete mode 100644 src/rend2/tr_surface.c delete mode 100644 src/rend2/tr_vbo.c delete mode 100644 src/rend2/tr_world.c delete mode 100644 src/renderer/iqm.h delete mode 100644 src/renderer/qgl.h delete mode 100644 src/renderer/tr_animation.c delete mode 100644 src/renderer/tr_backend.c delete mode 100644 src/renderer/tr_bsp.c delete mode 100644 src/renderer/tr_cmds.c delete mode 100644 src/renderer/tr_curve.c delete mode 100644 src/renderer/tr_flares.c delete mode 100644 src/renderer/tr_font.c delete mode 100644 src/renderer/tr_image.c delete mode 100644 src/renderer/tr_image_bmp.c delete mode 100644 src/renderer/tr_image_jpg.c delete mode 100644 src/renderer/tr_image_pcx.c delete mode 100644 src/renderer/tr_image_png.c delete mode 100644 src/renderer/tr_image_tga.c delete mode 100644 src/renderer/tr_init.c delete mode 100644 src/renderer/tr_light.c delete mode 100644 src/renderer/tr_local.h delete mode 100644 src/renderer/tr_main.c delete mode 100644 src/renderer/tr_marks.c delete mode 100644 src/renderer/tr_mesh.c delete mode 100644 src/renderer/tr_model.c delete mode 100644 src/renderer/tr_model_iqm.c delete mode 100644 src/renderer/tr_noise.c delete mode 100644 src/renderer/tr_public.h delete mode 100644 src/renderer/tr_scene.c delete mode 100644 src/renderer/tr_shade.c delete mode 100644 src/renderer/tr_shade_calc.c delete mode 100644 src/renderer/tr_shader.c delete mode 100644 src/renderer/tr_shadows.c delete mode 100644 src/renderer/tr_sky.c delete mode 100644 src/renderer/tr_subs.c delete mode 100644 src/renderer/tr_surface.c delete mode 100644 src/renderer/tr_types.h delete mode 100644 src/renderer/tr_world.c create mode 100644 src/renderercommon/iqm.h create mode 100644 src/renderercommon/tr_public.h create mode 100644 src/renderercommon/tr_types.h create mode 100644 src/renderergl1/qgl.h create mode 100644 src/renderergl1/tr_animation.c create mode 100644 src/renderergl1/tr_backend.c create mode 100644 src/renderergl1/tr_bsp.c create mode 100644 src/renderergl1/tr_cmds.c create mode 100644 src/renderergl1/tr_curve.c create mode 100644 src/renderergl1/tr_flares.c create mode 100644 src/renderergl1/tr_font.c create mode 100644 src/renderergl1/tr_image.c create mode 100644 src/renderergl1/tr_image_bmp.c create mode 100644 src/renderergl1/tr_image_jpg.c create mode 100644 src/renderergl1/tr_image_pcx.c create mode 100644 src/renderergl1/tr_image_png.c create mode 100644 src/renderergl1/tr_image_tga.c create mode 100644 src/renderergl1/tr_init.c create mode 100644 src/renderergl1/tr_light.c create mode 100644 src/renderergl1/tr_local.h create mode 100644 src/renderergl1/tr_main.c create mode 100644 src/renderergl1/tr_marks.c create mode 100644 src/renderergl1/tr_mesh.c create mode 100644 src/renderergl1/tr_model.c create mode 100644 src/renderergl1/tr_model_iqm.c create mode 100644 src/renderergl1/tr_noise.c create mode 100644 src/renderergl1/tr_scene.c create mode 100644 src/renderergl1/tr_shade.c create mode 100644 src/renderergl1/tr_shade_calc.c create mode 100644 src/renderergl1/tr_shader.c create mode 100644 src/renderergl1/tr_shadows.c create mode 100644 src/renderergl1/tr_sky.c create mode 100644 src/renderergl1/tr_subs.c create mode 100644 src/renderergl1/tr_surface.c create mode 100644 src/renderergl1/tr_world.c create mode 100644 src/renderergl2/glsl/bokeh_fp.glsl create mode 100644 src/renderergl2/glsl/bokeh_vp.glsl create mode 100644 src/renderergl2/glsl/calclevels4x_fp.glsl create mode 100644 src/renderergl2/glsl/calclevels4x_vp.glsl create mode 100644 src/renderergl2/glsl/depthblur_fp.glsl create mode 100644 src/renderergl2/glsl/depthblur_vp.glsl create mode 100644 src/renderergl2/glsl/dlight_fp.glsl create mode 100644 src/renderergl2/glsl/dlight_vp.glsl create mode 100644 src/renderergl2/glsl/down4x_fp.glsl create mode 100644 src/renderergl2/glsl/down4x_vp.glsl create mode 100644 src/renderergl2/glsl/fogpass_fp.glsl create mode 100644 src/renderergl2/glsl/fogpass_vp.glsl create mode 100644 src/renderergl2/glsl/generic_fp.glsl create mode 100644 src/renderergl2/glsl/generic_vp.glsl create mode 100644 src/renderergl2/glsl/lightall_fp.glsl create mode 100644 src/renderergl2/glsl/lightall_vp.glsl create mode 100644 src/renderergl2/glsl/pshadow_fp.glsl create mode 100644 src/renderergl2/glsl/pshadow_vp.glsl create mode 100644 src/renderergl2/glsl/shadowfill_fp.glsl create mode 100644 src/renderergl2/glsl/shadowfill_vp.glsl create mode 100644 src/renderergl2/glsl/shadowmask_fp.glsl create mode 100644 src/renderergl2/glsl/shadowmask_vp.glsl create mode 100644 src/renderergl2/glsl/ssao_fp.glsl create mode 100644 src/renderergl2/glsl/ssao_vp.glsl create mode 100644 src/renderergl2/glsl/texturecolor_fp.glsl create mode 100644 src/renderergl2/glsl/texturecolor_vp.glsl create mode 100644 src/renderergl2/glsl/tonemap_fp.glsl create mode 100644 src/renderergl2/glsl/tonemap_vp.glsl create mode 100644 src/renderergl2/qgl.h create mode 100644 src/renderergl2/tr_animation.c create mode 100644 src/renderergl2/tr_backend.c create mode 100644 src/renderergl2/tr_bsp.c create mode 100644 src/renderergl2/tr_cmds.c create mode 100644 src/renderergl2/tr_curve.c create mode 100644 src/renderergl2/tr_extensions.c create mode 100644 src/renderergl2/tr_extramath.c create mode 100644 src/renderergl2/tr_extramath.h create mode 100644 src/renderergl2/tr_extratypes.h create mode 100644 src/renderergl2/tr_fbo.c create mode 100644 src/renderergl2/tr_fbo.h create mode 100644 src/renderergl2/tr_flares.c create mode 100644 src/renderergl2/tr_font.c create mode 100644 src/renderergl2/tr_glsl.c create mode 100644 src/renderergl2/tr_image.c create mode 100644 src/renderergl2/tr_image_bmp.c create mode 100644 src/renderergl2/tr_image_jpg.c create mode 100644 src/renderergl2/tr_image_pcx.c create mode 100644 src/renderergl2/tr_image_png.c create mode 100644 src/renderergl2/tr_image_tga.c create mode 100644 src/renderergl2/tr_init.c create mode 100644 src/renderergl2/tr_light.c create mode 100644 src/renderergl2/tr_local.h create mode 100644 src/renderergl2/tr_main.c create mode 100644 src/renderergl2/tr_marks.c create mode 100644 src/renderergl2/tr_mesh.c create mode 100644 src/renderergl2/tr_model.c create mode 100644 src/renderergl2/tr_model_iqm.c create mode 100644 src/renderergl2/tr_noise.c create mode 100644 src/renderergl2/tr_postprocess.c create mode 100644 src/renderergl2/tr_postprocess.h create mode 100644 src/renderergl2/tr_scene.c create mode 100644 src/renderergl2/tr_shade.c create mode 100644 src/renderergl2/tr_shade_calc.c create mode 100644 src/renderergl2/tr_shader.c create mode 100644 src/renderergl2/tr_shadows.c create mode 100644 src/renderergl2/tr_sky.c create mode 100644 src/renderergl2/tr_subs.c create mode 100644 src/renderergl2/tr_surface.c create mode 100644 src/renderergl2/tr_vbo.c create mode 100644 src/renderergl2/tr_world.c diff --git a/Makefile b/Makefile index 8354df18..13b1f018 100644 --- a/Makefile +++ b/Makefile @@ -38,8 +38,8 @@ endif ifndef BUILD_GAME_QVM BUILD_GAME_QVM = endif -ifndef BUILD_RENDERER_REND2 - BUILD_RENDERER_REND2= +ifndef BUILD_RENDERER_OPENGL2 + BUILD_RENDERER_OPENGL2= endif ############################################################################# @@ -198,8 +198,8 @@ BD=$(BUILD_DIR)/debug-$(PLATFORM)-$(ARCH) BR=$(BUILD_DIR)/release-$(PLATFORM)-$(ARCH) CDIR=$(MOUNT_DIR)/client SDIR=$(MOUNT_DIR)/server -RDIR=$(MOUNT_DIR)/renderer -R2DIR=$(MOUNT_DIR)/rend2 +RGL1DIR=$(MOUNT_DIR)/renderergl1 +RGL2DIR=$(MOUNT_DIR)/renderergl2 CMDIR=$(MOUNT_DIR)/qcommon SDLDIR=$(MOUNT_DIR)/sdl ASMDIR=$(MOUNT_DIR)/asm @@ -834,13 +834,13 @@ endif ifneq ($(BUILD_CLIENT),0) ifneq ($(USE_RENDERER_DLOPEN),0) TARGETS += $(B)/$(CLIENTBIN)$(FULLBINEXT) $(B)/renderer_opengl1_$(SHLIBNAME) - ifneq ($(BUILD_RENDERER_REND2), 0) - TARGETS += $(B)/renderer_rend2_$(SHLIBNAME) + ifneq ($(BUILD_RENDERER_OPENGL2), 0) + TARGETS += $(B)/renderer_opengl2_$(SHLIBNAME) endif else TARGETS += $(B)/$(CLIENTBIN)$(FULLBINEXT) - ifneq ($(BUILD_RENDERER_REND2), 0) - TARGETS += $(B)/$(CLIENTBIN)_rend2$(FULLBINEXT) + ifneq ($(BUILD_RENDERER_OPENGL2), 0) + TARGETS += $(B)/$(CLIENTBIN)_opengl2$(FULLBINEXT) endif endif endif @@ -1108,9 +1108,9 @@ makedirs: @if [ ! -d $(BUILD_DIR) ];then $(MKDIR) $(BUILD_DIR);fi @if [ ! -d $(B) ];then $(MKDIR) $(B);fi @if [ ! -d $(B)/client ];then $(MKDIR) $(B)/client;fi - @if [ ! -d $(B)/renderer ];then $(MKDIR) $(B)/renderer;fi - @if [ ! -d $(B)/rend2 ];then $(MKDIR) $(B)/rend2;fi - @if [ ! -d $(B)/rend2/glsl ];then $(MKDIR) $(B)/rend2/glsl;fi + @if [ ! -d $(B)/renderergl1 ];then $(MKDIR) $(B)/renderergl1;fi + @if [ ! -d $(B)/renderergl2 ];then $(MKDIR) $(B)/renderergl2;fi + @if [ ! -d $(B)/renderergl2/glsl ];then $(MKDIR) $(B)/renderergl2/glsl;fi @if [ ! -d $(B)/ded ];then $(MKDIR) $(B)/ded;fi @if [ ! -d $(B)/$(BASEGAME) ];then $(MKDIR) $(B)/$(BASEGAME);fi @if [ ! -d $(B)/$(BASEGAME)/cgame ];then $(MKDIR) $(B)/$(BASEGAME)/cgame;fi @@ -1362,171 +1362,171 @@ else endif Q3R2OBJ = \ - $(B)/rend2/tr_animation.o \ - $(B)/rend2/tr_backend.o \ - $(B)/rend2/tr_bsp.o \ - $(B)/rend2/tr_cmds.o \ - $(B)/rend2/tr_curve.o \ - $(B)/rend2/tr_extramath.o \ - $(B)/rend2/tr_extensions.o \ - $(B)/rend2/tr_fbo.o \ - $(B)/rend2/tr_flares.o \ - $(B)/rend2/tr_font.o \ - $(B)/rend2/tr_glsl.o \ - $(B)/rend2/tr_image.o \ - $(B)/rend2/tr_image_png.o \ - $(B)/rend2/tr_image_jpg.o \ - $(B)/rend2/tr_image_bmp.o \ - $(B)/rend2/tr_image_tga.o \ - $(B)/rend2/tr_image_pcx.o \ - $(B)/rend2/tr_init.o \ - $(B)/rend2/tr_light.o \ - $(B)/rend2/tr_main.o \ - $(B)/rend2/tr_marks.o \ - $(B)/rend2/tr_mesh.o \ - $(B)/rend2/tr_model.o \ - $(B)/rend2/tr_model_iqm.o \ - $(B)/rend2/tr_noise.o \ - $(B)/rend2/tr_postprocess.o \ - $(B)/rend2/tr_scene.o \ - $(B)/rend2/tr_shade.o \ - $(B)/rend2/tr_shade_calc.o \ - $(B)/rend2/tr_shader.o \ - $(B)/rend2/tr_shadows.o \ - $(B)/rend2/tr_sky.o \ - $(B)/rend2/tr_surface.o \ - $(B)/rend2/tr_vbo.o \ - $(B)/rend2/tr_world.o \ + $(B)/renderergl2/tr_animation.o \ + $(B)/renderergl2/tr_backend.o \ + $(B)/renderergl2/tr_bsp.o \ + $(B)/renderergl2/tr_cmds.o \ + $(B)/renderergl2/tr_curve.o \ + $(B)/renderergl2/tr_extramath.o \ + $(B)/renderergl2/tr_extensions.o \ + $(B)/renderergl2/tr_fbo.o \ + $(B)/renderergl2/tr_flares.o \ + $(B)/renderergl2/tr_font.o \ + $(B)/renderergl2/tr_glsl.o \ + $(B)/renderergl2/tr_image.o \ + $(B)/renderergl2/tr_image_png.o \ + $(B)/renderergl2/tr_image_jpg.o \ + $(B)/renderergl2/tr_image_bmp.o \ + $(B)/renderergl2/tr_image_tga.o \ + $(B)/renderergl2/tr_image_pcx.o \ + $(B)/renderergl2/tr_init.o \ + $(B)/renderergl2/tr_light.o \ + $(B)/renderergl2/tr_main.o \ + $(B)/renderergl2/tr_marks.o \ + $(B)/renderergl2/tr_mesh.o \ + $(B)/renderergl2/tr_model.o \ + $(B)/renderergl2/tr_model_iqm.o \ + $(B)/renderergl2/tr_noise.o \ + $(B)/renderergl2/tr_postprocess.o \ + $(B)/renderergl2/tr_scene.o \ + $(B)/renderergl2/tr_shade.o \ + $(B)/renderergl2/tr_shade_calc.o \ + $(B)/renderergl2/tr_shader.o \ + $(B)/renderergl2/tr_shadows.o \ + $(B)/renderergl2/tr_sky.o \ + $(B)/renderergl2/tr_surface.o \ + $(B)/renderergl2/tr_vbo.o \ + $(B)/renderergl2/tr_world.o \ \ - $(B)/renderer/sdl_gamma.o \ - $(B)/renderer/sdl_glimp.o + $(B)/renderergl1/sdl_gamma.o \ + $(B)/renderergl1/sdl_glimp.o Q3R2STRINGOBJ = \ - $(B)/rend2/glsl/bokeh_fp.o \ - $(B)/rend2/glsl/bokeh_vp.o \ - $(B)/rend2/glsl/calclevels4x_fp.o \ - $(B)/rend2/glsl/calclevels4x_vp.o \ - $(B)/rend2/glsl/depthblur_fp.o \ - $(B)/rend2/glsl/depthblur_vp.o \ - $(B)/rend2/glsl/dlight_fp.o \ - $(B)/rend2/glsl/dlight_vp.o \ - $(B)/rend2/glsl/down4x_fp.o \ - $(B)/rend2/glsl/down4x_vp.o \ - $(B)/rend2/glsl/fogpass_fp.o \ - $(B)/rend2/glsl/fogpass_vp.o \ - $(B)/rend2/glsl/generic_fp.o \ - $(B)/rend2/glsl/generic_vp.o \ - $(B)/rend2/glsl/lightall_fp.o \ - $(B)/rend2/glsl/lightall_vp.o \ - $(B)/rend2/glsl/pshadow_fp.o \ - $(B)/rend2/glsl/pshadow_vp.o \ - $(B)/rend2/glsl/shadowfill_fp.o \ - $(B)/rend2/glsl/shadowfill_vp.o \ - $(B)/rend2/glsl/shadowmask_fp.o \ - $(B)/rend2/glsl/shadowmask_vp.o \ - $(B)/rend2/glsl/ssao_fp.o \ - $(B)/rend2/glsl/ssao_vp.o \ - $(B)/rend2/glsl/texturecolor_fp.o \ - $(B)/rend2/glsl/texturecolor_vp.o \ - $(B)/rend2/glsl/tonemap_fp.o \ - $(B)/rend2/glsl/tonemap_vp.o + $(B)/renderergl2/glsl/bokeh_fp.o \ + $(B)/renderergl2/glsl/bokeh_vp.o \ + $(B)/renderergl2/glsl/calclevels4x_fp.o \ + $(B)/renderergl2/glsl/calclevels4x_vp.o \ + $(B)/renderergl2/glsl/depthblur_fp.o \ + $(B)/renderergl2/glsl/depthblur_vp.o \ + $(B)/renderergl2/glsl/dlight_fp.o \ + $(B)/renderergl2/glsl/dlight_vp.o \ + $(B)/renderergl2/glsl/down4x_fp.o \ + $(B)/renderergl2/glsl/down4x_vp.o \ + $(B)/renderergl2/glsl/fogpass_fp.o \ + $(B)/renderergl2/glsl/fogpass_vp.o \ + $(B)/renderergl2/glsl/generic_fp.o \ + $(B)/renderergl2/glsl/generic_vp.o \ + $(B)/renderergl2/glsl/lightall_fp.o \ + $(B)/renderergl2/glsl/lightall_vp.o \ + $(B)/renderergl2/glsl/pshadow_fp.o \ + $(B)/renderergl2/glsl/pshadow_vp.o \ + $(B)/renderergl2/glsl/shadowfill_fp.o \ + $(B)/renderergl2/glsl/shadowfill_vp.o \ + $(B)/renderergl2/glsl/shadowmask_fp.o \ + $(B)/renderergl2/glsl/shadowmask_vp.o \ + $(B)/renderergl2/glsl/ssao_fp.o \ + $(B)/renderergl2/glsl/ssao_vp.o \ + $(B)/renderergl2/glsl/texturecolor_fp.o \ + $(B)/renderergl2/glsl/texturecolor_vp.o \ + $(B)/renderergl2/glsl/tonemap_fp.o \ + $(B)/renderergl2/glsl/tonemap_vp.o Q3ROBJ = \ - $(B)/renderer/tr_animation.o \ - $(B)/renderer/tr_backend.o \ - $(B)/renderer/tr_bsp.o \ - $(B)/renderer/tr_cmds.o \ - $(B)/renderer/tr_curve.o \ - $(B)/renderer/tr_flares.o \ - $(B)/renderer/tr_font.o \ - $(B)/renderer/tr_image.o \ - $(B)/renderer/tr_image_png.o \ - $(B)/renderer/tr_image_jpg.o \ - $(B)/renderer/tr_image_bmp.o \ - $(B)/renderer/tr_image_tga.o \ - $(B)/renderer/tr_image_pcx.o \ - $(B)/renderer/tr_init.o \ - $(B)/renderer/tr_light.o \ - $(B)/renderer/tr_main.o \ - $(B)/renderer/tr_marks.o \ - $(B)/renderer/tr_mesh.o \ - $(B)/renderer/tr_model.o \ - $(B)/renderer/tr_model_iqm.o \ - $(B)/renderer/tr_noise.o \ - $(B)/renderer/tr_scene.o \ - $(B)/renderer/tr_shade.o \ - $(B)/renderer/tr_shade_calc.o \ - $(B)/renderer/tr_shader.o \ - $(B)/renderer/tr_shadows.o \ - $(B)/renderer/tr_sky.o \ - $(B)/renderer/tr_surface.o \ - $(B)/renderer/tr_world.o \ + $(B)/renderergl1/tr_animation.o \ + $(B)/renderergl1/tr_backend.o \ + $(B)/renderergl1/tr_bsp.o \ + $(B)/renderergl1/tr_cmds.o \ + $(B)/renderergl1/tr_curve.o \ + $(B)/renderergl1/tr_flares.o \ + $(B)/renderergl1/tr_font.o \ + $(B)/renderergl1/tr_image.o \ + $(B)/renderergl1/tr_image_png.o \ + $(B)/renderergl1/tr_image_jpg.o \ + $(B)/renderergl1/tr_image_bmp.o \ + $(B)/renderergl1/tr_image_tga.o \ + $(B)/renderergl1/tr_image_pcx.o \ + $(B)/renderergl1/tr_init.o \ + $(B)/renderergl1/tr_light.o \ + $(B)/renderergl1/tr_main.o \ + $(B)/renderergl1/tr_marks.o \ + $(B)/renderergl1/tr_mesh.o \ + $(B)/renderergl1/tr_model.o \ + $(B)/renderergl1/tr_model_iqm.o \ + $(B)/renderergl1/tr_noise.o \ + $(B)/renderergl1/tr_scene.o \ + $(B)/renderergl1/tr_shade.o \ + $(B)/renderergl1/tr_shade_calc.o \ + $(B)/renderergl1/tr_shader.o \ + $(B)/renderergl1/tr_shadows.o \ + $(B)/renderergl1/tr_sky.o \ + $(B)/renderergl1/tr_surface.o \ + $(B)/renderergl1/tr_world.o \ \ - $(B)/renderer/sdl_gamma.o \ - $(B)/renderer/sdl_glimp.o + $(B)/renderergl1/sdl_gamma.o \ + $(B)/renderergl1/sdl_glimp.o ifneq ($(USE_RENDERER_DLOPEN), 0) Q3ROBJ += \ - $(B)/renderer/q_shared.o \ - $(B)/renderer/puff.o \ - $(B)/renderer/q_math.o \ - $(B)/renderer/tr_subs.o + $(B)/renderergl1/q_shared.o \ + $(B)/renderergl1/puff.o \ + $(B)/renderergl1/q_math.o \ + $(B)/renderergl1/tr_subs.o Q3R2OBJ += \ - $(B)/renderer/q_shared.o \ - $(B)/renderer/puff.o \ - $(B)/renderer/q_math.o \ - $(B)/renderer/tr_subs.o + $(B)/renderergl1/q_shared.o \ + $(B)/renderergl1/puff.o \ + $(B)/renderergl1/q_math.o \ + $(B)/renderergl1/tr_subs.o endif ifneq ($(USE_INTERNAL_JPEG),0) JPGOBJ = \ - $(B)/renderer/jaricom.o \ - $(B)/renderer/jcapimin.o \ - $(B)/renderer/jcapistd.o \ - $(B)/renderer/jcarith.o \ - $(B)/renderer/jccoefct.o \ - $(B)/renderer/jccolor.o \ - $(B)/renderer/jcdctmgr.o \ - $(B)/renderer/jchuff.o \ - $(B)/renderer/jcinit.o \ - $(B)/renderer/jcmainct.o \ - $(B)/renderer/jcmarker.o \ - $(B)/renderer/jcmaster.o \ - $(B)/renderer/jcomapi.o \ - $(B)/renderer/jcparam.o \ - $(B)/renderer/jcprepct.o \ - $(B)/renderer/jcsample.o \ - $(B)/renderer/jctrans.o \ - $(B)/renderer/jdapimin.o \ - $(B)/renderer/jdapistd.o \ - $(B)/renderer/jdarith.o \ - $(B)/renderer/jdatadst.o \ - $(B)/renderer/jdatasrc.o \ - $(B)/renderer/jdcoefct.o \ - $(B)/renderer/jdcolor.o \ - $(B)/renderer/jddctmgr.o \ - $(B)/renderer/jdhuff.o \ - $(B)/renderer/jdinput.o \ - $(B)/renderer/jdmainct.o \ - $(B)/renderer/jdmarker.o \ - $(B)/renderer/jdmaster.o \ - $(B)/renderer/jdmerge.o \ - $(B)/renderer/jdpostct.o \ - $(B)/renderer/jdsample.o \ - $(B)/renderer/jdtrans.o \ - $(B)/renderer/jerror.o \ - $(B)/renderer/jfdctflt.o \ - $(B)/renderer/jfdctfst.o \ - $(B)/renderer/jfdctint.o \ - $(B)/renderer/jidctflt.o \ - $(B)/renderer/jidctfst.o \ - $(B)/renderer/jidctint.o \ - $(B)/renderer/jmemmgr.o \ - $(B)/renderer/jmemnobs.o \ - $(B)/renderer/jquant1.o \ - $(B)/renderer/jquant2.o \ - $(B)/renderer/jutils.o + $(B)/renderergl1/jaricom.o \ + $(B)/renderergl1/jcapimin.o \ + $(B)/renderergl1/jcapistd.o \ + $(B)/renderergl1/jcarith.o \ + $(B)/renderergl1/jccoefct.o \ + $(B)/renderergl1/jccolor.o \ + $(B)/renderergl1/jcdctmgr.o \ + $(B)/renderergl1/jchuff.o \ + $(B)/renderergl1/jcinit.o \ + $(B)/renderergl1/jcmainct.o \ + $(B)/renderergl1/jcmarker.o \ + $(B)/renderergl1/jcmaster.o \ + $(B)/renderergl1/jcomapi.o \ + $(B)/renderergl1/jcparam.o \ + $(B)/renderergl1/jcprepct.o \ + $(B)/renderergl1/jcsample.o \ + $(B)/renderergl1/jctrans.o \ + $(B)/renderergl1/jdapimin.o \ + $(B)/renderergl1/jdapistd.o \ + $(B)/renderergl1/jdarith.o \ + $(B)/renderergl1/jdatadst.o \ + $(B)/renderergl1/jdatasrc.o \ + $(B)/renderergl1/jdcoefct.o \ + $(B)/renderergl1/jdcolor.o \ + $(B)/renderergl1/jddctmgr.o \ + $(B)/renderergl1/jdhuff.o \ + $(B)/renderergl1/jdinput.o \ + $(B)/renderergl1/jdmainct.o \ + $(B)/renderergl1/jdmarker.o \ + $(B)/renderergl1/jdmaster.o \ + $(B)/renderergl1/jdmerge.o \ + $(B)/renderergl1/jdpostct.o \ + $(B)/renderergl1/jdsample.o \ + $(B)/renderergl1/jdtrans.o \ + $(B)/renderergl1/jerror.o \ + $(B)/renderergl1/jfdctflt.o \ + $(B)/renderergl1/jfdctfst.o \ + $(B)/renderergl1/jfdctint.o \ + $(B)/renderergl1/jidctflt.o \ + $(B)/renderergl1/jidctfst.o \ + $(B)/renderergl1/jidctint.o \ + $(B)/renderergl1/jmemmgr.o \ + $(B)/renderergl1/jmemnobs.o \ + $(B)/renderergl1/jquant1.o \ + $(B)/renderergl1/jquant2.o \ + $(B)/renderergl1/jutils.o endif ifeq ($(ARCH),x86) @@ -1685,7 +1685,7 @@ $(B)/renderer_opengl1_$(SHLIBNAME): $(Q3ROBJ) $(JPGOBJ) $(Q)$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(Q3ROBJ) $(JPGOBJ) \ $(THREAD_LIBS) $(LIBSDLMAIN) $(RENDERER_LIBS) $(LIBS) -$(B)/renderer_rend2_$(SHLIBNAME): $(Q3R2OBJ) $(Q3R2STRINGOBJ) $(JPGOBJ) +$(B)/renderer_opengl2_$(SHLIBNAME): $(Q3R2OBJ) $(Q3R2STRINGOBJ) $(JPGOBJ) $(echo_cmd) "LD $@" $(Q)$(CC) $(CFLAGS) $(SHLIBLDFLAGS) -o $@ $(Q3R2OBJ) $(Q3R2STRINGOBJ) $(JPGOBJ) \ $(THREAD_LIBS) $(LIBSDLMAIN) $(RENDERER_LIBS) $(LIBS) @@ -1696,7 +1696,7 @@ $(B)/$(CLIENTBIN)$(FULLBINEXT): $(Q3OBJ) $(Q3ROBJ) $(JPGOBJ) $(LIBSDLMAIN) -o $@ $(Q3OBJ) $(Q3ROBJ) $(JPGOBJ) \ $(LIBSDLMAIN) $(CLIENT_LIBS) $(RENDERER_LIBS) $(LIBS) -$(B)/$(CLIENTBIN)_rend2$(FULLBINEXT): $(Q3OBJ) $(Q3R2OBJ) $(Q3R2STRINGOBJ) $(JPGOBJ) $(LIBSDLMAIN) +$(B)/$(CLIENTBIN)_opengl2$(FULLBINEXT): $(Q3OBJ) $(Q3R2OBJ) $(Q3R2STRINGOBJ) $(JPGOBJ) $(LIBSDLMAIN) $(echo_cmd) "LD $@" $(Q)$(CC) $(CLIENT_CFLAGS) $(CFLAGS) $(CLIENT_LDFLAGS) $(LDFLAGS) \ -o $@ $(Q3OBJ) $(Q3R2OBJ) $(Q3R2STRINGOBJ) $(JPGOBJ) \ @@ -2025,25 +2025,25 @@ $(B)/client/%.o: $(SYSDIR)/%.rc $(DO_WINDRES) -$(B)/renderer/%.o: $(CMDIR)/%.c +$(B)/renderergl1/%.o: $(CMDIR)/%.c $(DO_REF_CC) -$(B)/renderer/%.o: $(SDLDIR)/%.c +$(B)/renderergl1/%.o: $(SDLDIR)/%.c $(DO_REF_CC) -$(B)/renderer/%.o: $(JPDIR)/%.c +$(B)/renderergl1/%.o: $(JPDIR)/%.c $(DO_REF_CC) -$(B)/renderer/%.o: $(RDIR)/%.c +$(B)/renderergl1/%.o: $(RGL1DIR)/%.c $(DO_REF_CC) -$(B)/rend2/glsl/%.c: $(R2DIR)/glsl/%.glsl +$(B)/renderergl2/glsl/%.c: $(RGL2DIR)/glsl/%.glsl $(DO_REF_STR) -$(B)/rend2/glsl/%.o: $(B)/rend2/glsl/%.c +$(B)/renderergl2/glsl/%.o: $(B)/renderergl2/glsl/%.c $(DO_REF_CC) -$(B)/rend2/%.o: $(R2DIR)/%.c +$(B)/renderergl2/%.o: $(RGL2DIR)/%.c $(DO_REF_CC) diff --git a/src/client/client.h b/src/client/client.h index f3d52a24..de029364 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -24,7 +24,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include "../qcommon/q_shared.h" #include "../qcommon/qcommon.h" -#include "../renderer/tr_public.h" +#include "../renderercommon/tr_public.h" #include "../ui/ui_public.h" #include "keys.h" #include "snd_public.h" diff --git a/src/null/null_glimp.c b/src/null/null_glimp.c index aae34eb0..f33508d9 100644 --- a/src/null/null_glimp.c +++ b/src/null/null_glimp.c @@ -20,7 +20,7 @@ along with Tremulous; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ -#include "../renderer/tr_local.h" +#include "../renderergl1/tr_local.h" qboolean ( * qwglSwapIntervalEXT)( int interval ); diff --git a/src/rend2/glsl/bokeh_fp.glsl b/src/rend2/glsl/bokeh_fp.glsl deleted file mode 100644 index d08816ae..00000000 --- a/src/rend2/glsl/bokeh_fp.glsl +++ /dev/null @@ -1,70 +0,0 @@ -uniform sampler2D u_TextureMap; - -uniform vec4 u_Color; - -uniform vec2 u_InvTexRes; -varying vec2 var_TexCoords; - -void main() -{ - vec4 color; - vec2 tc; - -#if 0 - float c[7] = float[7](1.0, 0.9659258263, 0.8660254038, 0.7071067812, 0.5, 0.2588190451, 0.0); - - tc = var_TexCoords + u_InvTexRes * vec2( c[0], c[6]); color = texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( c[1], c[5]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( c[2], c[4]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( c[3], c[3]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( c[4], c[2]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( c[5], c[1]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( c[6], c[0]); color += texture2D(u_TextureMap, tc); - - tc = var_TexCoords + u_InvTexRes * vec2( c[1], -c[5]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( c[2], -c[4]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( c[3], -c[3]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( c[4], -c[2]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( c[5], -c[1]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( c[6], -c[0]); color += texture2D(u_TextureMap, tc); - - tc = var_TexCoords + u_InvTexRes * vec2( -c[0], c[6]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( -c[1], c[5]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( -c[2], c[4]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( -c[3], c[3]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( -c[4], c[2]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( -c[5], c[1]); color += texture2D(u_TextureMap, tc); - - tc = var_TexCoords + u_InvTexRes * vec2( -c[1], -c[5]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( -c[2], -c[4]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( -c[3], -c[3]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( -c[4], -c[2]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( -c[5], -c[1]); color += texture2D(u_TextureMap, tc); - - gl_FragColor = color * 0.04166667 * u_Color; -#endif - - float c[5] = float[5](1.0, 0.9238795325, 0.7071067812, 0.3826834324, 0.0); - - tc = var_TexCoords + u_InvTexRes * vec2( c[0], c[4]); color = texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( c[1], c[3]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( c[2], c[2]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( c[3], c[1]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( c[4], c[0]); color += texture2D(u_TextureMap, tc); - - tc = var_TexCoords + u_InvTexRes * vec2( c[1], -c[3]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( c[2], -c[2]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( c[3], -c[1]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( c[4], -c[0]); color += texture2D(u_TextureMap, tc); - - tc = var_TexCoords + u_InvTexRes * vec2( -c[0], c[4]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( -c[1], c[3]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( -c[2], c[2]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( -c[3], c[1]); color += texture2D(u_TextureMap, tc); - - tc = var_TexCoords + u_InvTexRes * vec2( -c[1], -c[3]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( -c[2], -c[2]); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( -c[3], -c[1]); color += texture2D(u_TextureMap, tc); - - gl_FragColor = color * 0.0625 * u_Color; -} diff --git a/src/rend2/glsl/bokeh_vp.glsl b/src/rend2/glsl/bokeh_vp.glsl deleted file mode 100644 index 5ca41600..00000000 --- a/src/rend2/glsl/bokeh_vp.glsl +++ /dev/null @@ -1,13 +0,0 @@ -attribute vec4 attr_Position; -attribute vec4 attr_TexCoord0; - -uniform mat4 u_ModelViewProjectionMatrix; - -varying vec2 var_TexCoords; - - -void main() -{ - gl_Position = u_ModelViewProjectionMatrix * attr_Position; - var_TexCoords = attr_TexCoord0.st; -} diff --git a/src/rend2/glsl/calclevels4x_fp.glsl b/src/rend2/glsl/calclevels4x_fp.glsl deleted file mode 100644 index c8cf06c7..00000000 --- a/src/rend2/glsl/calclevels4x_fp.glsl +++ /dev/null @@ -1,55 +0,0 @@ -uniform sampler2D u_TextureMap; - -uniform vec4 u_Color; - -uniform vec2 u_InvTexRes; -varying vec2 var_TexCoords; - -const vec3 LUMINANCE_VECTOR = vec3(0.2125, 0.7154, 0.0721); //vec3(0.299, 0.587, 0.114); - -vec3 GetValues(vec2 offset, vec3 current) -{ - vec3 minAvgMax; - vec2 tc = var_TexCoords + u_InvTexRes * offset; minAvgMax = texture2D(u_TextureMap, tc).rgb; - -#ifdef FIRST_PASS - float lumi = max(dot(LUMINANCE_VECTOR, minAvgMax), 0.000001); - float loglumi = clamp(log2(lumi), -10.0, 10.0); - minAvgMax = vec3(loglumi * 0.05 + 0.5); -#endif - - return vec3(min(current.x, minAvgMax.x), current.y + minAvgMax.y, max(current.z, minAvgMax.z)); -} - -void main() -{ - vec3 current = vec3(1.0, 0.0, 0.0); - -#ifdef FIRST_PASS - current = GetValues(vec2( 0.0, 0.0), current); -#else - current = GetValues(vec2(-1.5, -1.5), current); - current = GetValues(vec2(-0.5, -1.5), current); - current = GetValues(vec2( 0.5, -1.5), current); - current = GetValues(vec2( 1.5, -1.5), current); - - current = GetValues(vec2(-1.5, -0.5), current); - current = GetValues(vec2(-0.5, -0.5), current); - current = GetValues(vec2( 0.5, -0.5), current); - current = GetValues(vec2( 1.5, -0.5), current); - - current = GetValues(vec2(-1.5, 0.5), current); - current = GetValues(vec2(-0.5, 0.5), current); - current = GetValues(vec2( 0.5, 0.5), current); - current = GetValues(vec2( 1.5, 0.5), current); - - current = GetValues(vec2(-1.5, 1.5), current); - current = GetValues(vec2(-0.5, 1.5), current); - current = GetValues(vec2( 0.5, 1.5), current); - current = GetValues(vec2( 1.5, 1.5), current); - - current.y *= 0.0625; -#endif - - gl_FragColor = vec4(current, 1.0f); -} diff --git a/src/rend2/glsl/calclevels4x_vp.glsl b/src/rend2/glsl/calclevels4x_vp.glsl deleted file mode 100644 index 5ca41600..00000000 --- a/src/rend2/glsl/calclevels4x_vp.glsl +++ /dev/null @@ -1,13 +0,0 @@ -attribute vec4 attr_Position; -attribute vec4 attr_TexCoord0; - -uniform mat4 u_ModelViewProjectionMatrix; - -varying vec2 var_TexCoords; - - -void main() -{ - gl_Position = u_ModelViewProjectionMatrix * attr_Position; - var_TexCoords = attr_TexCoord0.st; -} diff --git a/src/rend2/glsl/depthblur_fp.glsl b/src/rend2/glsl/depthblur_fp.glsl deleted file mode 100644 index 93895b4e..00000000 --- a/src/rend2/glsl/depthblur_fp.glsl +++ /dev/null @@ -1,58 +0,0 @@ -uniform sampler2D u_ScreenImageMap; -uniform sampler2D u_ScreenDepthMap; - -uniform vec4 u_ViewInfo; // zfar / znear, zfar -varying vec2 var_ScreenTex; - -//float gauss[5] = float[5](0.30, 0.23, 0.097, 0.024, 0.0033); -float gauss[4] = float[4](0.40, 0.24, 0.054, 0.0044); -//float gauss[3] = float[3](0.60, 0.19, 0.0066); -#define GAUSS_SIZE 4 - -float getLinearDepth(sampler2D depthMap, const vec2 tex, const float zFarDivZNear) -{ - float sampleZDivW = texture2D(depthMap, tex).r; - return 1.0 / mix(zFarDivZNear, 1.0, sampleZDivW); -} - -vec4 depthGaussian1D(sampler2D imageMap, sampler2D depthMap, vec2 tex, float zFarDivZNear, float zFar) -{ - float scale = 1.0 / 256.0; - -#if defined(USE_HORIZONTAL_BLUR) - vec2 direction = vec2(1.0, 0.0) * scale; -#else // if defined(USE_VERTICAL_BLUR) - vec2 direction = vec2(0.0, 1.0) * scale; -#endif - - float depthCenter = zFar * getLinearDepth(depthMap, tex, zFarDivZNear); - vec2 centerSlope = vec2(dFdx(depthCenter), dFdy(depthCenter)) / vec2(dFdx(tex.x), dFdy(tex.y)); - - vec4 result = texture2D(imageMap, tex) * gauss[0]; - float total = gauss[0]; - - int i, j; - for (i = 0; i < 2; i++) - { - for (j = 1; j < GAUSS_SIZE; j++) - { - vec2 offset = direction * j; - float depthSample = zFar * getLinearDepth(depthMap, tex + offset, zFarDivZNear); - float depthExpected = depthCenter + dot(centerSlope, offset); - if(abs(depthSample - depthExpected) < 5.0) - { - result += texture2D(imageMap, tex + offset) * gauss[j]; - total += gauss[j]; - } - } - - direction = -direction; - } - - return result / total; -} - -void main() -{ - gl_FragColor = depthGaussian1D(u_ScreenImageMap, u_ScreenDepthMap, var_ScreenTex, u_ViewInfo.x, u_ViewInfo.y); -} diff --git a/src/rend2/glsl/depthblur_vp.glsl b/src/rend2/glsl/depthblur_vp.glsl deleted file mode 100644 index 9c46a79f..00000000 --- a/src/rend2/glsl/depthblur_vp.glsl +++ /dev/null @@ -1,12 +0,0 @@ -attribute vec4 attr_Position; -attribute vec4 attr_TexCoord0; - -varying vec2 var_ScreenTex; - -void main() -{ - gl_Position = attr_Position; - var_ScreenTex = attr_TexCoord0.xy; - //vec2 screenCoords = gl_Position.xy / gl_Position.w; - //var_ScreenTex = screenCoords * 0.5 + 0.5; -} diff --git a/src/rend2/glsl/dlight_fp.glsl b/src/rend2/glsl/dlight_fp.glsl deleted file mode 100644 index 8ffca5b9..00000000 --- a/src/rend2/glsl/dlight_fp.glsl +++ /dev/null @@ -1,12 +0,0 @@ -uniform sampler2D u_DiffuseMap; - -varying vec2 var_Tex1; -varying vec4 var_Color; - - -void main() -{ - vec4 color = texture2D(u_DiffuseMap, var_Tex1); - - gl_FragColor = color * var_Color; -} diff --git a/src/rend2/glsl/dlight_vp.glsl b/src/rend2/glsl/dlight_vp.glsl deleted file mode 100644 index d9fd71d0..00000000 --- a/src/rend2/glsl/dlight_vp.glsl +++ /dev/null @@ -1,92 +0,0 @@ -attribute vec4 attr_Position; -attribute vec4 attr_TexCoord0; -attribute vec3 attr_Normal; - -uniform vec4 u_DlightInfo; - -#if defined(USE_DEFORM_VERTEXES) -uniform int u_DeformGen; -uniform float u_DeformParams[5]; -uniform float u_Time; -#endif - -uniform vec4 u_Color; -uniform mat4 u_ModelViewProjectionMatrix; - -varying vec2 var_Tex1; -varying vec4 var_Color; - -#if defined(USE_DEFORM_VERTEXES) -vec3 DeformPosition(const vec3 pos, const vec3 normal, const vec2 st) -{ - if (u_DeformGen == 0) - { - return pos; - } - - float base = u_DeformParams[0]; - float amplitude = u_DeformParams[1]; - float phase = u_DeformParams[2]; - float frequency = u_DeformParams[3]; - float spread = u_DeformParams[4]; - - if (u_DeformGen == DGEN_BULGE) - { - phase *= M_PI * 0.25 * st.x; - } - else // if (u_DeformGen <= DGEN_WAVE_INVERSE_SAWTOOTH) - { - phase += dot(pos.xyz, vec3(spread)); - } - - float value = phase + (u_Time * frequency); - float func; - - if (u_DeformGen == DGEN_WAVE_SIN) - { - func = sin(value * 2.0 * M_PI); - } - else if (u_DeformGen == DGEN_WAVE_SQUARE) - { - func = sign(sin(value * 2.0 * M_PI)); - } - else if (u_DeformGen == DGEN_WAVE_TRIANGLE) - { - func = abs(fract(value + 0.75) - 0.5) * 4.0 - 1.0; - } - else if (u_DeformGen == DGEN_WAVE_SAWTOOTH) - { - func = fract(value); - } - else if (u_DeformGen == DGEN_WAVE_INVERSE_SAWTOOTH) - { - func = (1.0 - fract(value)); - } - else if (u_DeformGen == DGEN_BULGE) - { - func = sin(value); - } - - return pos + normal * (base + func * amplitude); -} -#endif - -void main() -{ - vec4 position = attr_Position; - vec3 normal = attr_Normal; - -#if defined(USE_DEFORM_VERTEXES) - position.xyz = DeformPosition(position.xyz, normal, attr_TexCoord0.st); -#endif - - gl_Position = u_ModelViewProjectionMatrix * position; - - vec3 dist = u_DlightInfo.xyz - position.xyz; - - var_Tex1 = dist.xy * u_DlightInfo.a + vec2(0.5); - float dlightmod = step(0.0, dot(dist, normal)); - dlightmod *= clamp(2.0 * (1.0 - abs(dist.z) * u_DlightInfo.a), 0.0, 1.0); - - var_Color = u_Color * dlightmod; -} diff --git a/src/rend2/glsl/down4x_fp.glsl b/src/rend2/glsl/down4x_fp.glsl deleted file mode 100644 index 0f88fb2e..00000000 --- a/src/rend2/glsl/down4x_fp.glsl +++ /dev/null @@ -1,34 +0,0 @@ -uniform sampler2D u_TextureMap; - -uniform vec2 u_InvTexRes; -varying vec2 var_TexCoords; - -void main() -{ - vec4 color; - vec2 tc; - - tc = var_TexCoords + u_InvTexRes * vec2(-1.5, -1.5); color = texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2(-0.5, -1.5); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( 0.5, -1.5); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( 1.5, -1.5); color += texture2D(u_TextureMap, tc); - - tc = var_TexCoords + u_InvTexRes * vec2(-1.5, -0.5); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2(-0.5, -0.5); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( 0.5, -0.5); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( 1.5, -0.5); color += texture2D(u_TextureMap, tc); - - tc = var_TexCoords + u_InvTexRes * vec2(-1.5, 0.5); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2(-0.5, 0.5); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( 0.5, 0.5); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( 1.5, 0.5); color += texture2D(u_TextureMap, tc); - - tc = var_TexCoords + u_InvTexRes * vec2(-1.5, 1.5); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2(-0.5, 1.5); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( 0.5, 1.5); color += texture2D(u_TextureMap, tc); - tc = var_TexCoords + u_InvTexRes * vec2( 1.5, 1.5); color += texture2D(u_TextureMap, tc); - - color *= 0.0625; - - gl_FragColor = color; -} diff --git a/src/rend2/glsl/down4x_vp.glsl b/src/rend2/glsl/down4x_vp.glsl deleted file mode 100644 index 5ca41600..00000000 --- a/src/rend2/glsl/down4x_vp.glsl +++ /dev/null @@ -1,13 +0,0 @@ -attribute vec4 attr_Position; -attribute vec4 attr_TexCoord0; - -uniform mat4 u_ModelViewProjectionMatrix; - -varying vec2 var_TexCoords; - - -void main() -{ - gl_Position = u_ModelViewProjectionMatrix * attr_Position; - var_TexCoords = attr_TexCoord0.st; -} diff --git a/src/rend2/glsl/fogpass_fp.glsl b/src/rend2/glsl/fogpass_fp.glsl deleted file mode 100644 index 91884304..00000000 --- a/src/rend2/glsl/fogpass_fp.glsl +++ /dev/null @@ -1,9 +0,0 @@ -uniform vec4 u_Color; - -varying float var_Scale; - -void main() -{ - gl_FragColor = u_Color; - gl_FragColor.a *= sqrt(clamp(var_Scale, 0.0, 1.0)); -} diff --git a/src/rend2/glsl/fogpass_vp.glsl b/src/rend2/glsl/fogpass_vp.glsl deleted file mode 100644 index f18bc707..00000000 --- a/src/rend2/glsl/fogpass_vp.glsl +++ /dev/null @@ -1,117 +0,0 @@ -attribute vec4 attr_Position; -attribute vec3 attr_Normal; -attribute vec4 attr_TexCoord0; - -//#if defined(USE_VERTEX_ANIMATION) -attribute vec4 attr_Position2; -attribute vec3 attr_Normal2; -//#endif - -uniform vec4 u_FogDistance; -uniform vec4 u_FogDepth; -uniform float u_FogEyeT; - -//#if defined(USE_DEFORM_VERTEXES) -uniform int u_DeformGen; -uniform float u_DeformParams[5]; -//#endif - -uniform float u_Time; -uniform mat4 u_ModelViewProjectionMatrix; - -//#if defined(USE_VERTEX_ANIMATION) -uniform float u_VertexLerp; -//#endif - -varying float var_Scale; - -#if defined(USE_DEFORM_VERTEXES) -vec3 DeformPosition(const vec3 pos, const vec3 normal, const vec2 st) -{ - if (u_DeformGen == 0) - { - return pos; - } - - float base = u_DeformParams[0]; - float amplitude = u_DeformParams[1]; - float phase = u_DeformParams[2]; - float frequency = u_DeformParams[3]; - float spread = u_DeformParams[4]; - - if (u_DeformGen == DGEN_BULGE) - { - phase *= M_PI * 0.25 * st.x; - } - else // if (u_DeformGen <= DGEN_WAVE_INVERSE_SAWTOOTH) - { - phase += dot(pos.xyz, vec3(spread)); - } - - float value = phase + (u_Time * frequency); - float func; - - if (u_DeformGen == DGEN_WAVE_SIN) - { - func = sin(value * 2.0 * M_PI); - } - else if (u_DeformGen == DGEN_WAVE_SQUARE) - { - func = sign(sin(value * 2.0 * M_PI)); - } - else if (u_DeformGen == DGEN_WAVE_TRIANGLE) - { - func = abs(fract(value + 0.75) - 0.5) * 4.0 - 1.0; - } - else if (u_DeformGen == DGEN_WAVE_SAWTOOTH) - { - func = fract(value); - } - else if (u_DeformGen == DGEN_WAVE_INVERSE_SAWTOOTH) - { - func = (1.0 - fract(value)); - } - else if (u_DeformGen == DGEN_BULGE) - { - func = sin(value); - } - - return pos + normal * (base + func * amplitude); -} -#endif - -float CalcFog(vec4 position) -{ - float s = dot(position, u_FogDistance) * 8.0; - float t = dot(position, u_FogDepth); - - if (t < 1.0) - { - t = step(step(0.0, -u_FogEyeT), t); - } - else - { - t /= t - min(u_FogEyeT, 0.0); - } - - return s * t; -} - -void main() -{ -#if defined(USE_VERTEX_ANIMATION) - vec4 position = mix(attr_Position, attr_Position2, u_VertexLerp); - vec3 normal = normalize(mix(attr_Normal, attr_Normal2, u_VertexLerp)); -#else - vec4 position = attr_Position; - vec3 normal = attr_Normal; -#endif - -#if defined(USE_DEFORM_VERTEXES) - position.xyz = DeformPosition(position.xyz, normal, attr_TexCoord0.st); -#endif - - gl_Position = u_ModelViewProjectionMatrix * position; - - var_Scale = CalcFog(position); -} diff --git a/src/rend2/glsl/generic_fp.glsl b/src/rend2/glsl/generic_fp.glsl deleted file mode 100644 index dea52e06..00000000 --- a/src/rend2/glsl/generic_fp.glsl +++ /dev/null @@ -1,43 +0,0 @@ -uniform sampler2D u_DiffuseMap; - -#if defined(USE_LIGHTMAP) -uniform sampler2D u_LightMap; - -uniform int u_Texture1Env; -#endif - -varying vec2 var_DiffuseTex; - -#if defined(USE_LIGHTMAP) -varying vec2 var_LightTex; -#endif - -varying vec4 var_Color; - - -void main() -{ - vec4 color = texture2D(u_DiffuseMap, var_DiffuseTex); -#if defined(USE_LIGHTMAP) - vec4 color2 = texture2D(u_LightMap, var_LightTex); - #if defined(RGBE_LIGHTMAP) - color2.rgb *= exp2(color2.a * 255.0 - 128.0); - color2.a = 1.0; - #endif - - if (u_Texture1Env == TEXENV_MODULATE) - { - color *= color2; - } - else if (u_Texture1Env == TEXENV_ADD) - { - color += color2; - } - else if (u_Texture1Env == TEXENV_REPLACE) - { - color = color2; - } -#endif - - gl_FragColor = color * var_Color; -} diff --git a/src/rend2/glsl/generic_vp.glsl b/src/rend2/glsl/generic_vp.glsl deleted file mode 100644 index 67360b1b..00000000 --- a/src/rend2/glsl/generic_vp.glsl +++ /dev/null @@ -1,251 +0,0 @@ -attribute vec4 attr_Position; -attribute vec3 attr_Normal; - -#if defined(USE_VERTEX_ANIMATION) -attribute vec4 attr_Position2; -attribute vec3 attr_Normal2; -#endif - -attribute vec4 attr_Color; -attribute vec4 attr_TexCoord0; - -#if defined(USE_LIGHTMAP) || defined(USE_TCGEN) -attribute vec4 attr_TexCoord1; -#endif - -uniform vec4 u_DiffuseTexMatrix; -uniform vec4 u_DiffuseTexOffTurb; - -#if defined(USE_TCGEN) || defined(USE_RGBAGEN) -uniform vec3 u_ViewOrigin; -#endif - -#if defined(USE_TCGEN) -uniform int u_TCGen0; -uniform vec3 u_TCGen0Vector0; -uniform vec3 u_TCGen0Vector1; -#endif - -#if defined(USE_FOG) -uniform vec4 u_FogDistance; -uniform vec4 u_FogDepth; -uniform float u_FogEyeT; -uniform vec4 u_FogColorMask; -#endif - -#if defined(USE_DEFORM_VERTEXES) -uniform int u_DeformGen; -uniform float u_DeformParams[5]; -uniform float u_Time; -#endif - -uniform mat4 u_ModelViewProjectionMatrix; -uniform vec4 u_BaseColor; -uniform vec4 u_VertColor; - -#if defined(USE_RGBAGEN) -uniform int u_ColorGen; -uniform int u_AlphaGen; -uniform vec3 u_AmbientLight; -uniform vec3 u_DirectedLight; -uniform vec4 u_LightOrigin; -uniform float u_PortalRange; -#endif - -#if defined(USE_VERTEX_ANIMATION) -uniform float u_VertexLerp; -#endif - -varying vec2 var_DiffuseTex; -#if defined(USE_LIGHTMAP) -varying vec2 var_LightTex; -#endif -varying vec4 var_Color; - -#if defined(USE_DEFORM_VERTEXES) -vec3 DeformPosition(const vec3 pos, const vec3 normal, const vec2 st) -{ - float base = u_DeformParams[0]; - float amplitude = u_DeformParams[1]; - float phase = u_DeformParams[2]; - float frequency = u_DeformParams[3]; - float spread = u_DeformParams[4]; - - if (u_DeformGen == DGEN_BULGE) - { - phase *= M_PI * 0.25 * st.x; - } - else // if (u_DeformGen <= DGEN_WAVE_INVERSE_SAWTOOTH) - { - phase += dot(pos.xyz, vec3(spread)); - } - - float value = phase + (u_Time * frequency); - float func; - - if (u_DeformGen == DGEN_WAVE_SIN) - { - func = sin(value * 2.0 * M_PI); - } - else if (u_DeformGen == DGEN_WAVE_SQUARE) - { - func = sign(sin(value * 2.0 * M_PI)); - } - else if (u_DeformGen == DGEN_WAVE_TRIANGLE) - { - func = abs(fract(value + 0.75) - 0.5) * 4.0 - 1.0; - } - else if (u_DeformGen == DGEN_WAVE_SAWTOOTH) - { - func = fract(value); - } - else if (u_DeformGen == DGEN_WAVE_INVERSE_SAWTOOTH) - { - func = (1.0 - fract(value)); - } - else if (u_DeformGen == DGEN_BULGE) - { - func = sin(value); - } - - return pos + normal * (base + func * amplitude); -} -#endif - -#if defined(USE_TCGEN) -vec2 GenTexCoords(int TCGen, vec3 position, vec3 normal, vec3 TCGenVector0, vec3 TCGenVector1) -{ - vec2 tex = attr_TexCoord0.st; - - if (TCGen == TCGEN_LIGHTMAP) - { - tex = attr_TexCoord1.st; - } - else if (TCGen == TCGEN_ENVIRONMENT_MAPPED) - { - vec3 viewer = normalize(u_ViewOrigin - position); - tex = -reflect(viewer, normal).yz * vec2(0.5, -0.5) + 0.5; - } - else if (TCGen == TCGEN_VECTOR) - { - tex = vec2(dot(position, TCGenVector0), dot(position, TCGenVector1)); - } - - return tex; -} -#endif - -#if defined(USE_TCMOD) -vec2 ModTexCoords(vec2 st, vec3 position, vec4 texMatrix, vec4 offTurb) -{ - float amplitude = offTurb.z; - float phase = offTurb.w; - vec2 st2 = vec2(dot(st, texMatrix.xz), dot(st, texMatrix.yw)) + offTurb.xy; - - vec3 offsetPos = position / 1024.0; - offsetPos.x += offsetPos.z; - - vec2 texOffset = sin((offsetPos.xy + vec2(phase)) * 2.0 * M_PI); - - return st2 + texOffset * amplitude; -} -#endif - -#if defined(USE_RGBAGEN) -vec4 CalcColor(vec3 position, vec3 normal) -{ - vec4 color = u_VertColor * attr_Color + u_BaseColor; - - if (u_ColorGen == CGEN_LIGHTING_DIFFUSE) - { - float incoming = clamp(dot(normal, u_LightOrigin.xyz), 0.0, 1.0); - - color.rgb = clamp(u_DirectedLight * incoming + u_AmbientLight, 0.0, 1.0); - } - - vec3 toView = u_ViewOrigin - position; - vec3 viewer = normalize(u_ViewOrigin - position); - - if (u_AlphaGen == AGEN_LIGHTING_SPECULAR) - { - vec3 lightDir = normalize(vec3(-960.0, -1980.0, 96.0) - position.xyz); - vec3 halfangle = normalize(lightDir + viewer); - - color.a = pow(max(dot(normal, halfangle), 0.0), 8.0); - } - else if (u_AlphaGen == AGEN_PORTAL) - { - float alpha = length(toView) / u_PortalRange; - - color.a = clamp(alpha, 0.0, 1.0); - } - else if (u_AlphaGen == AGEN_FRESNEL) - { - color.a = 0.10 + 0.90 * pow(1.0 - dot(normal, viewer), 5); - } - - return color; -} -#endif - -#if defined(USE_FOG) -float CalcFog(vec4 position) -{ - float s = dot(position, u_FogDistance) * 8.0; - float t = dot(position, u_FogDepth); - - if (t < 1.0) - { - t = step(step(0.0, -u_FogEyeT), t); - } - else - { - t /= t - min(u_FogEyeT, 0.0); - } - - return s * t; -} -#endif - -void main() -{ -#if defined(USE_VERTEX_ANIMATION) - vec4 position = mix(attr_Position, attr_Position2, u_VertexLerp); - vec3 normal = normalize(mix(attr_Normal, attr_Normal2, u_VertexLerp)); -#else - vec4 position = attr_Position; - vec3 normal = attr_Normal; -#endif - -#if defined(USE_DEFORM_VERTEXES) - position.xyz = DeformPosition(position.xyz, normal, attr_TexCoord0.st); -#endif - - gl_Position = u_ModelViewProjectionMatrix * position; - -#if defined(USE_TCGEN) - vec2 tex = GenTexCoords(u_TCGen0, position.xyz, normal, u_TCGen0Vector0, u_TCGen0Vector1); -#else - vec2 tex = attr_TexCoord0.st; -#endif - -#if defined(USE_TCMOD) - var_DiffuseTex = ModTexCoords(tex, position.xyz, u_DiffuseTexMatrix, u_DiffuseTexOffTurb); -#else - var_DiffuseTex = tex; -#endif - -#if defined(USE_LIGHTMAP) - var_LightTex = attr_TexCoord1.st; -#endif - -#if defined(USE_RGBAGEN) - var_Color = CalcColor(position.xyz, normal); -#else - var_Color = u_VertColor * attr_Color + u_BaseColor; -#endif - -#if defined(USE_FOG) - var_Color *= vec4(1.0) - u_FogColorMask * sqrt(clamp(CalcFog(position), 0.0, 1.0)); -#endif -} diff --git a/src/rend2/glsl/lightall_fp.glsl b/src/rend2/glsl/lightall_fp.glsl deleted file mode 100644 index ec9bb2dc..00000000 --- a/src/rend2/glsl/lightall_fp.glsl +++ /dev/null @@ -1,383 +0,0 @@ -uniform sampler2D u_DiffuseMap; - -#if defined(USE_LIGHTMAP) -uniform sampler2D u_LightMap; -#endif - -#if defined(USE_NORMALMAP) -uniform sampler2D u_NormalMap; -#endif - -#if defined(USE_DELUXEMAP) -uniform sampler2D u_DeluxeMap; -#endif - -#if defined(USE_SPECULARMAP) -uniform sampler2D u_SpecularMap; -#endif - -#if defined(USE_SHADOWMAP) -uniform sampler2D u_ShadowMap; -#endif - -uniform vec3 u_ViewOrigin; - -#if defined(USE_TCGEN) -uniform int u_TCGen0; -#endif - -#if defined(USE_LIGHT_VECTOR) -uniform vec3 u_DirectedLight; -uniform vec3 u_AmbientLight; -uniform float u_LightRadius; -#endif - -#if defined(USE_LIGHT) -uniform vec2 u_MaterialInfo; -#endif - -varying vec2 var_DiffuseTex; -#if defined(USE_LIGHTMAP) -varying vec2 var_LightTex; -#endif -varying vec4 var_Color; - -#if defined(USE_NORMALMAP) && !defined(USE_VERT_TANGENT_SPACE) -varying vec3 var_Position; -#endif - -#if defined(USE_TCGEN) || defined(USE_NORMALMAP) || (defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)) -varying vec3 var_SampleToView; -#endif - -#if !defined(USE_FAST_LIGHT) -varying vec3 var_Normal; -#endif - -#if defined(USE_VERT_TANGENT_SPACE) -varying vec3 var_Tangent; -varying vec3 var_Bitangent; -#endif - -varying vec3 var_VertLight; - -#if defined(USE_LIGHT) && !defined(USE_DELUXEMAP) -varying vec3 var_WorldLight; -#endif - -#define EPSILON 0.00000001 - -#if defined(USE_PARALLAXMAP) -float SampleHeight(sampler2D normalMap, vec2 t) -{ - #if defined(SWIZZLE_NORMALMAP) - return texture2D(normalMap, t).r; - #else - return texture2D(normalMap, t).a; - #endif -} - -float RayIntersectDisplaceMap(vec2 dp, vec2 ds, sampler2D normalMap) -{ - const int linearSearchSteps = 16; - const int binarySearchSteps = 6; - - float depthStep = 1.0 / float(linearSearchSteps); - - // current size of search window - float size = depthStep; - - // current depth position - float depth = 0.0; - - // best match found (starts with last position 1.0) - float bestDepth = 1.0; - - // search front to back for first point inside object - for(int i = 0; i < linearSearchSteps - 1; ++i) - { - depth += size; - - float t = 1.0 - SampleHeight(normalMap, dp + ds * depth); - - if(bestDepth > 0.996) // if no depth found yet - if(depth >= t) - bestDepth = depth; // store best depth - } - - depth = bestDepth; - - // recurse around first point (depth) for closest match - for(int i = 0; i < binarySearchSteps; ++i) - { - size *= 0.5; - - float t = 1.0 - SampleHeight(normalMap, dp + ds * depth); - - if(depth >= t) - { - bestDepth = depth; - depth -= 2.0 * size; - } - - depth += size; - } - - return bestDepth; -} -#endif - -float CalcDiffuse(vec3 N, vec3 L, vec3 E, float NE, float NL, float fzero, float shininess) -{ - #if defined(USE_OREN_NAYAR) || defined(USE_TRIACE_OREN_NAYAR) - float gamma = dot(E, L) - NE * NL; - float B = 2.22222 + 0.1 * shininess; - - #if defined(USE_OREN_NAYAR) - float A = 1.0 - 1.0 / (2.0 + 0.33 * shininess); - gamma = clamp(gamma, 0.0, 1.0); - #endif - - #if defined(USE_TRIACE_OREN_NAYAR) - float A = 1.0 - 1.0 / (2.0 + 0.65 * shininess); - - if (gamma >= 0.0) - #endif - { - B *= max(max(NL, NE), EPSILON); - } - - return (A + gamma / B) * (1.0 - fzero); - #else - return 1.0 - fzero; - #endif -} - -#if defined(USE_SPECULARMAP) -float CalcSpecular(float NH, float NL, float NE, float EH, float fzero, float shininess) -{ - #if defined(USE_BLINN) || defined(USE_TRIACE) || defined(USE_TORRANCE_SPARROW) - float blinn = pow(NH, shininess); - #endif - - #if defined(USE_BLINN) - return blinn; - #endif - - #if defined(USE_COOK_TORRANCE) || defined (USE_TRIACE) || defined (USE_TORRANCE_SPARROW) - float fresnel = fzero + (1.0 - fzero) * pow(1.0 - EH, 5); - #endif - - #if defined(USE_COOK_TORRANCE) || defined(USE_TORRANCE_SPARROW) - float geo = 2.0 * NH * min(NE, NL); - geo /= max(EH, geo); - #endif - - #if defined(USE_COOK_TORRANCE) - float m_sq = 2.0 / max(shininess, EPSILON); - float NH_sq = NH * NH; - float m_NH_sq = m_sq * NH_sq; - float beckmann = exp((NH_sq - 1.0) / max(m_NH_sq, EPSILON)) / max(4.0 * m_NH_sq * NH_sq, EPSILON); - - return fresnel * geo * beckmann / max(NE, EPSILON); - #endif - - #if defined(USE_TRIACE) - float scale = 0.1248582 * shininess + 0.2691817; - - return fresnel * scale * blinn / max(max(NL, NE), EPSILON); - #endif - - #if defined(USE_TORRANCE_SPARROW) - float scale = 0.125 * shininess + 1.0; - - return fresnel * geo * scale * blinn / max(NE, EPSILON); - #endif -} -#endif - -void main() -{ -#if !defined(USE_FAST_LIGHT) && (defined(USE_LIGHT) || defined(USE_NORMALMAP)) - vec3 surfNormal = normalize(var_Normal); -#endif - -#if defined(USE_DELUXEMAP) - vec3 worldLight = 2.0 * texture2D(u_DeluxeMap, var_LightTex).xyz - vec3(1.0); - //worldLight += var_WorldLight * 0.0001; -#elif defined(USE_LIGHT) - vec3 worldLight = var_WorldLight; -#endif - -#if defined(USE_LIGHTMAP) - vec4 lightSample = texture2D(u_LightMap, var_LightTex).rgba; - #if defined(RGBE_LIGHTMAP) - lightSample.rgb *= exp2(lightSample.a * 255.0 - 128.0); - #endif - vec3 directedLight = lightSample.rgb; -#elif defined(USE_LIGHT_VECTOR) && !defined(USE_FAST_LIGHT) - #if defined(USE_INVSQRLIGHT) - float intensity = 1.0 / dot(worldLight, worldLight); - #else - float intensity = clamp((1.0 - dot(worldLight, worldLight) / (u_LightRadius * u_LightRadius)) * 1.07, 0.0, 1.0); - #endif - - vec3 directedLight = u_DirectedLight * intensity; - vec3 ambientLight = u_AmbientLight; - - #if defined(USE_SHADOWMAP) - vec2 shadowTex = gl_FragCoord.xy * r_FBufScale; - directedLight *= texture2D(u_ShadowMap, shadowTex).r; - #endif -#elif defined(USE_LIGHT_VERTEX) && !defined(USE_FAST_LIGHT) - vec3 directedLight = var_VertLight; -#endif - -#if defined(USE_TCGEN) || defined(USE_NORMALMAP) || (defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)) - vec3 SampleToView = normalize(var_SampleToView); -#endif - vec2 tex = var_DiffuseTex; - - float ambientDiff = 1.0; - -#if defined(USE_NORMALMAP) - #if defined(USE_VERT_TANGENT_SPACE) - vec3 tangent = var_Tangent; - vec3 bitangent = var_Bitangent; - #else - vec3 q0 = dFdx(var_Position); - vec3 q1 = dFdy(var_Position); - vec2 st0 = dFdx(tex); - vec2 st1 = dFdy(tex); - float dir = sign(st1.t * st0.s - st0.t * st1.s); - - vec3 tangent = normalize( q0 * st1.t - q1 * st0.t) * dir; - vec3 bitangent = -normalize( q0 * st1.s - q1 * st0.s) * dir; - #endif - - mat3 tangentToWorld = mat3(tangent, bitangent, var_Normal); - - #if defined(USE_PARALLAXMAP) - vec3 offsetDir = normalize(SampleToView * tangentToWorld); - #if 0 - float height = SampleHeight(u_NormalMap, tex); - float pdist = 0.05 * height - (0.05 / 2.0); - #else - offsetDir.xy *= -0.05 / offsetDir.z; - float pdist = RayIntersectDisplaceMap(tex, offsetDir.xy, u_NormalMap); - #endif - tex += offsetDir.xy * pdist; - #endif - #if defined(SWIZZLE_NORMALMAP) - vec3 normal = 2.0 * texture2D(u_NormalMap, tex).agb - 1.0; - #else - vec3 normal = 2.0 * texture2D(u_NormalMap, tex).rgb - 1.0; - #endif - normal.z = sqrt(clamp(1.0 - dot(normal.xy, normal.xy), 0.0, 1.0)); - vec3 worldNormal = tangentToWorld * normal; - #if defined(r_normalAmbient) - ambientDiff = 0.781341 * normal.z + 0.218659; - #endif -#elif defined(USE_LIGHT) && !defined(USE_FAST_LIGHT) - vec3 worldNormal = surfNormal; -#endif - -#if (defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)) || (defined(USE_TCGEN) && defined(USE_NORMALMAP)) - worldNormal = normalize(worldNormal); -#endif - -#if defined(USE_TCGEN) && defined(USE_NORMALMAP) - if (u_TCGen0 == TCGEN_ENVIRONMENT_MAPPED) - { - tex = -reflect(normalize(SampleToView), worldNormal).yz * vec2(0.5, -0.5) + 0.5; - } -#endif - - vec4 diffuse = texture2D(u_DiffuseMap, tex); - -#if defined(USE_LIGHT) && defined(USE_FAST_LIGHT) - #if defined(USE_LIGHTMAP) - diffuse.rgb *= directedLight; - #endif -#elif defined(USE_LIGHT) - worldLight = normalize(worldLight); - - float surfNL = clamp(dot(surfNormal, worldLight), 0.0, 1.0); - - #if defined(USE_LIGHTMAP) || defined(USE_LIGHT_VERTEX) - #if defined(USE_STANDARD_DELUXEMAP) - // Standard deluxe mapping treats the light sample as fully directed - // and doesn't compensate for light angle attenuation. - vec3 ambientLight = vec3(0.0); - #else - // Separate the light sample into directed and ambient parts. - // - // ambientMax - if the cosine of the angle between the surface - // normal and the light is below this value, the light - // is fully ambient. - // directedMax - if the cosine of the angle between the surface - // normal and the light is above this value, the light - // is fully directed. - const float ambientMax = 0.25; - const float directedMax = 0.5; - - float directedScale = clamp((surfNL - ambientMax) / (directedMax - ambientMax), 0.0, 1.0); - - // Scale the directed portion to compensate for the baked-in - // light angle attenuation. - directedScale /= max(surfNL, ambientMax); - - #if defined(r_normalAmbient) - directedScale *= 1.0 - r_normalAmbient; - #endif - - // Recover any unused light as ambient - vec3 ambientLight = directedLight; - directedLight *= directedScale; - ambientLight -= directedLight * surfNL; - #endif - #endif - - float NL = clamp(dot(worldNormal, worldLight), 0.0, 1.0); - float NE = clamp(dot(worldNormal, SampleToView), 0.0, 1.0); - - float fzero = u_MaterialInfo.x; - float shininess = u_MaterialInfo.y; - - #if defined(USE_SPECULARMAP) - vec4 specular = texture2D(u_SpecularMap, tex); - //specular.rgb = clamp(specular.rgb - diffuse.rgb, 0.0, 1.0); - shininess *= specular.a; - #endif - - float directedDiff = NL * CalcDiffuse(worldNormal, worldLight, SampleToView, NE, NL, fzero, shininess); - diffuse.rgb *= directedLight * directedDiff + ambientDiff * ambientLight; - - #if defined(USE_SPECULARMAP) - vec3 halfAngle = normalize(worldLight + SampleToView); - - float EH = clamp(dot(SampleToView, halfAngle), 0.0, 1.0); - float NH = clamp(dot(worldNormal, halfAngle), 0.0, 1.0); - - float directedSpec = NL * CalcSpecular(NH, NL, NE, EH, fzero, shininess); - - #if defined(r_normalAmbient) - vec3 ambientHalf = normalize(surfNormal + SampleToView); - float ambientSpec = max(dot(ambientHalf, worldNormal) + 0.5, 0.0); - ambientSpec *= ambientSpec * 0.44; - ambientSpec = pow(ambientSpec, shininess) * fzero; - specular.rgb *= directedSpec * directedLight + ambientSpec * ambientLight; - #else - specular.rgb *= directedSpec * directedLight; - #endif - #endif -#endif - - gl_FragColor = diffuse; - -#if defined(USE_SPECULARMAP) && defined(USE_LIGHT) && !defined(USE_FAST_LIGHT) - gl_FragColor.rgb += specular.rgb; -#endif - - gl_FragColor *= var_Color; -} diff --git a/src/rend2/glsl/lightall_vp.glsl b/src/rend2/glsl/lightall_vp.glsl deleted file mode 100644 index 05a41f4d..00000000 --- a/src/rend2/glsl/lightall_vp.glsl +++ /dev/null @@ -1,219 +0,0 @@ -attribute vec4 attr_TexCoord0; -#if defined(USE_LIGHTMAP) -attribute vec4 attr_TexCoord1; -#endif -attribute vec4 attr_Color; - -attribute vec4 attr_Position; -attribute vec3 attr_Normal; - -#if defined(USE_VERT_TANGENT_SPACE) -attribute vec3 attr_Tangent; -attribute vec3 attr_Bitangent; -#endif - -#if defined(USE_VERTEX_ANIMATION) -attribute vec4 attr_Position2; -attribute vec3 attr_Normal2; - #if defined(USE_VERT_TANGENT_SPACE) -attribute vec3 attr_Tangent2; -attribute vec3 attr_Bitangent2; - #endif -#endif - -#if defined(USE_LIGHT) && !defined(USE_LIGHT_VECTOR) -attribute vec3 attr_LightDirection; -#endif - -#if defined(USE_TCGEN) || defined(USE_NORMALMAP) || defined(USE_LIGHT) && !defined(USE_FAST_LIGHT) -uniform vec3 u_ViewOrigin; -#endif - -#if defined(USE_TCGEN) -uniform int u_TCGen0; -#endif - -#if defined(USE_TCMOD) -uniform vec4 u_DiffuseTexMatrix; -uniform vec4 u_DiffuseTexOffTurb; -#endif - -uniform mat4 u_ModelViewProjectionMatrix; -uniform vec4 u_BaseColor; -uniform vec4 u_VertColor; - -#if defined(USE_MODELMATRIX) -uniform mat4 u_ModelMatrix; -#endif - -#if defined(USE_VERTEX_ANIMATION) -uniform float u_VertexLerp; -#endif - -#if defined(USE_LIGHT_VECTOR) -uniform vec4 u_LightOrigin; - #if defined(USE_FAST_LIGHT) -uniform vec3 u_DirectedLight; -uniform vec3 u_AmbientLight; -uniform float u_LightRadius; - #endif -#endif - -varying vec2 var_DiffuseTex; - -#if defined(USE_LIGHTMAP) -varying vec2 var_LightTex; -#endif - -#if defined(USE_TCGEN) || defined(USE_NORMALMAP) || (defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)) -varying vec3 var_SampleToView; -#endif - -varying vec4 var_Color; - -#if defined(USE_NORMALMAP) && !defined(USE_VERT_TANGENT_SPACE) -varying vec3 var_Position; -#endif - - -#if !defined(USE_FAST_LIGHT) -varying vec3 var_Normal; - #if defined(USE_VERT_TANGENT_SPACE) -varying vec3 var_Tangent; -varying vec3 var_Bitangent; - #endif -#endif - -#if defined(USE_LIGHT_VERTEX) && !defined(USE_FAST_LIGHT) -varying vec3 var_VertLight; -#endif - -#if defined(USE_LIGHT) && !defined(USE_DELUXEMAP) && !defined(USE_FAST_LIGHT) -varying vec3 var_WorldLight; -#endif - -#if defined(USE_TCMOD) -vec2 ModTexCoords(vec2 st, vec3 position, vec4 texMatrix, vec4 offTurb) -{ - float amplitude = offTurb.z; - float phase = offTurb.w; - vec2 st2 = vec2(dot(st, texMatrix.xz), dot(st, texMatrix.yw)) + offTurb.xy; - - vec3 offsetPos = position / 1024.0; - offsetPos.x += offsetPos.z; - - vec2 texOffset = sin((offsetPos.xy + vec2(phase)) * 2.0 * M_PI); - - return st2 + texOffset * amplitude; -} -#endif - - -void main() -{ -#if defined(USE_VERTEX_ANIMATION) - vec4 position = mix(attr_Position, attr_Position2, u_VertexLerp); - vec3 normal = normalize(mix(attr_Normal, attr_Normal2, u_VertexLerp)); - #if defined(USE_VERT_TANGENT_SPACE) - vec3 tangent = normalize(mix(attr_Tangent, attr_Tangent2, u_VertexLerp)); - vec3 bitangent = normalize(mix(attr_Bitangent, attr_Bitangent2, u_VertexLerp)); - #endif -#else - vec4 position = attr_Position; - vec3 normal = attr_Normal; - #if defined(USE_VERT_TANGENT_SPACE) - vec3 tangent = attr_Tangent; - vec3 bitangent = attr_Bitangent; - #endif -#endif - - gl_Position = u_ModelViewProjectionMatrix * position; - -#if (defined(USE_LIGHTMAP) || defined(USE_LIGHT_VERTEX)) && !defined(USE_DELUXEMAP) && !defined(USE_FAST_LIGHT) - vec3 worldLight = attr_LightDirection; -#endif - -#if defined(USE_MODELMATRIX) - position = u_ModelMatrix * position; - normal = (u_ModelMatrix * vec4(normal, 0.0)).xyz; - #if defined(USE_VERT_TANGENT_SPACE) - tangent = (u_ModelMatrix * vec4(tangent, 0.0)).xyz; - bitangent = (u_ModelMatrix * vec4(bitangent, 0.0)).xyz; - #endif - - #if defined(USE_LIGHTMAP) && !defined(USE_DELUXEMAP) && !defined(USE_FAST_LIGHT) - worldLight = (u_ModelMatrix * vec4(worldLight, 0.0)).xyz; - #endif -#endif - -#if defined(USE_NORMALMAP) && !defined(USE_VERT_TANGENT_SPACE) - var_Position = position.xyz; -#endif - -#if defined(USE_TCGEN) || defined(USE_NORMALMAP) || defined(USE_LIGHT) && !defined(USE_FAST_LIGHT) - vec3 SampleToView = u_ViewOrigin - position.xyz; -#endif - -#if defined(USE_TCGEN) || defined(USE_NORMALMAP) || (defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)) - var_SampleToView = SampleToView; -#endif - - vec2 tex; - -#if defined(USE_TCGEN) - if (u_TCGen0 == TCGEN_ENVIRONMENT_MAPPED) - { - tex = -reflect(normalize(SampleToView), normal).yz * vec2(0.5, -0.5) + 0.5; - } - else -#endif - { - tex = attr_TexCoord0.st; - } - -#if defined(USE_TCMOD) - var_DiffuseTex = ModTexCoords(tex, position.xyz, u_DiffuseTexMatrix, u_DiffuseTexOffTurb); -#else - var_DiffuseTex = tex; -#endif - -#if defined(USE_LIGHTMAP) - var_LightTex = attr_TexCoord1.st; -#endif - -#if !defined(USE_FAST_LIGHT) - var_Normal = normal; - #if defined(USE_VERT_TANGENT_SPACE) - var_Tangent = tangent; - var_Bitangent = bitangent; - #endif -#endif - -#if defined(USE_LIGHT) && !defined(USE_DELUXEMAP) - #if defined(USE_LIGHT_VECTOR) - vec3 worldLight = u_LightOrigin.xyz - (position.xyz * u_LightOrigin.w); - #endif - #if !defined(USE_FAST_LIGHT) - var_WorldLight = worldLight; - #endif -#endif - -#if defined(USE_LIGHT_VERTEX) && !defined(USE_FAST_LIGHT) - var_VertLight = u_VertColor.rgb * attr_Color.rgb; - var_Color.rgb = vec3(1.0); - var_Color.a = u_VertColor.a * attr_Color.a + u_BaseColor.a; -#else - var_Color = u_VertColor * attr_Color + u_BaseColor; -#endif - -#if defined(USE_LIGHT_VECTOR) && defined(USE_FAST_LIGHT) - #if defined(USE_INVSQRLIGHT) - float intensity = 1.0 / dot(worldLight, worldLight); - #else - float intensity = clamp((1.0 - dot(worldLight, worldLight) / (u_LightRadius * u_LightRadius)) * 1.07, 0.0, 1.0); - #endif - float NL = clamp(dot(normal, normalize(worldLight)), 0.0, 1.0); - - var_Color.rgb *= u_DirectedLight * intensity * NL + u_AmbientLight; -#endif -} diff --git a/src/rend2/glsl/pshadow_fp.glsl b/src/rend2/glsl/pshadow_fp.glsl deleted file mode 100644 index b152971a..00000000 --- a/src/rend2/glsl/pshadow_fp.glsl +++ /dev/null @@ -1,98 +0,0 @@ -uniform sampler2D u_ShadowMap; - -uniform vec3 u_LightForward; -uniform vec3 u_LightUp; -uniform vec3 u_LightRight; -uniform vec4 u_LightOrigin; -uniform float u_LightRadius; -varying vec3 var_Position; -varying vec3 var_Normal; - -float sampleDistMap(sampler2D texMap, vec2 uv, float scale) -{ - vec3 distv = texture2D(texMap, uv).xyz; - return dot(distv, vec3(1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0)) * scale; -} - -void main() -{ - vec3 lightToPos = var_Position - u_LightOrigin.xyz; - vec2 st = vec2(-dot(u_LightRight, lightToPos), dot(u_LightUp, lightToPos)); - - float fade = length(st); - -#if defined(USE_DISCARD) - if (fade >= 1.0) - { - discard; - } -#endif - - fade = clamp(8.0 - fade * 8.0, 0.0, 1.0); - - st = st * 0.5 + vec2(0.5); - -#if defined(USE_SOLID_PSHADOWS) - float intensity = max(sign(u_LightRadius - length(lightToPos)), 0.0); -#else - float intensity = clamp((1.0 - dot(lightToPos, lightToPos) / (u_LightRadius * u_LightRadius)) * 2.0, 0.0, 1.0); -#endif - - float lightDist = length(lightToPos); - float dist; - -#if defined(USE_DISCARD) - if (dot(u_LightForward, lightToPos) <= 0.0) - { - discard; - } - - if (dot(var_Normal, lightToPos) > 0.0) - { - discard; - } -#else - intensity *= max(sign(dot(u_LightForward, lightToPos)), 0.0); - intensity *= max(sign(-dot(var_Normal, lightToPos)), 0.0); -#endif - - intensity *= fade; -#if defined(USE_PCF) - float part; - - dist = sampleDistMap(u_ShadowMap, st + vec2(-1.0/512.0, -1.0/512.0), u_LightRadius); - part = max(sign(lightDist - dist), 0.0); - - dist = sampleDistMap(u_ShadowMap, st + vec2( 1.0/512.0, -1.0/512.0), u_LightRadius); - part += max(sign(lightDist - dist), 0.0); - - dist = sampleDistMap(u_ShadowMap, st + vec2(-1.0/512.0, 1.0/512.0), u_LightRadius); - part += max(sign(lightDist - dist), 0.0); - - dist = sampleDistMap(u_ShadowMap, st + vec2( 1.0/512.0, 1.0/512.0), u_LightRadius); - part += max(sign(lightDist - dist), 0.0); - - #if defined(USE_DISCARD) - if (part <= 0.0) - { - discard; - } - #endif - - intensity *= part * 0.25; -#else - dist = sampleDistMap(u_ShadowMap, st, u_LightRadius); - - #if defined(USE_DISCARD) - if (lightDist - dist <= 0.0) - { - discard; - } - #endif - - intensity *= max(sign(lightDist - dist), 0.0); -#endif - - gl_FragColor.rgb = vec3(0); - gl_FragColor.a = clamp(intensity, 0.0, 0.75); -} diff --git a/src/rend2/glsl/pshadow_vp.glsl b/src/rend2/glsl/pshadow_vp.glsl deleted file mode 100644 index 0e0e3b3d..00000000 --- a/src/rend2/glsl/pshadow_vp.glsl +++ /dev/null @@ -1,17 +0,0 @@ -attribute vec4 attr_Position; -attribute vec3 attr_Normal; - -uniform mat4 u_ModelViewProjectionMatrix; -varying vec3 var_Position; -varying vec3 var_Normal; - - -void main() -{ - vec4 position = attr_Position; - - gl_Position = u_ModelViewProjectionMatrix * position; - - var_Position = position.xyz; - var_Normal = attr_Normal; -} diff --git a/src/rend2/glsl/shadowfill_fp.glsl b/src/rend2/glsl/shadowfill_fp.glsl deleted file mode 100644 index 150f3d12..00000000 --- a/src/rend2/glsl/shadowfill_fp.glsl +++ /dev/null @@ -1,41 +0,0 @@ -uniform vec4 u_LightOrigin; -uniform float u_LightRadius; - -varying vec3 var_Position; - -void main() -{ -#if defined(USE_DEPTH) - float depth = length(u_LightOrigin.xyz - var_Position) / u_LightRadius; - #if 0 - // 32 bit precision - const vec4 bitSh = vec4( 256 * 256 * 256, 256 * 256, 256, 1); - const vec4 bitMsk = vec4( 0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0); - - vec4 comp; - comp = depth * bitSh; - comp.xyz = fract(comp.xyz); - comp -= comp.xxyz * bitMsk; - gl_FragColor = comp; - #endif - - #if 1 - // 24 bit precision - const vec3 bitSh = vec3( 256 * 256, 256, 1); - const vec3 bitMsk = vec3( 0, 1.0 / 256.0, 1.0 / 256.0); - - vec3 comp; - comp = depth * bitSh; - comp.xy = fract(comp.xy); - comp -= comp.xxy * bitMsk; - gl_FragColor = vec4(comp, 1.0); - #endif - - #if 0 - // 8 bit precision - gl_FragColor = vec4(depth, depth, depth, 1); - #endif -#else - gl_FragColor = vec4(0, 0, 0, 1); -#endif -} diff --git a/src/rend2/glsl/shadowfill_vp.glsl b/src/rend2/glsl/shadowfill_vp.glsl deleted file mode 100644 index 10802eca..00000000 --- a/src/rend2/glsl/shadowfill_vp.glsl +++ /dev/null @@ -1,89 +0,0 @@ -attribute vec4 attr_Position; -attribute vec3 attr_Normal; -attribute vec4 attr_TexCoord0; - -//#if defined(USE_VERTEX_ANIMATION) -attribute vec4 attr_Position2; -attribute vec3 attr_Normal2; -//#endif - -//#if defined(USE_DEFORM_VERTEXES) -uniform int u_DeformGen; -uniform float u_DeformParams[5]; -//#endif - -uniform float u_Time; -uniform mat4 u_ModelViewProjectionMatrix; - -uniform mat4 u_ModelMatrix; - -//#if defined(USE_VERTEX_ANIMATION) -uniform float u_VertexLerp; -//#endif - -varying vec3 var_Position; - -vec3 DeformPosition(const vec3 pos, const vec3 normal, const vec2 st) -{ - if (u_DeformGen == 0) - { - return pos; - } - - float base = u_DeformParams[0]; - float amplitude = u_DeformParams[1]; - float phase = u_DeformParams[2]; - float frequency = u_DeformParams[3]; - float spread = u_DeformParams[4]; - - if (u_DeformGen == DGEN_BULGE) - { - phase *= M_PI * 0.25 * st.x; - } - else // if (u_DeformGen <= DGEN_WAVE_INVERSE_SAWTOOTH) - { - phase += dot(pos.xyz, vec3(spread)); - } - - float value = phase + (u_Time * frequency); - float func; - - if (u_DeformGen == DGEN_WAVE_SIN) - { - func = sin(value * 2.0 * M_PI); - } - else if (u_DeformGen == DGEN_WAVE_SQUARE) - { - func = sign(sin(value * 2.0 * M_PI)); - } - else if (u_DeformGen == DGEN_WAVE_TRIANGLE) - { - func = abs(fract(value + 0.75) - 0.5) * 4.0 - 1.0; - } - else if (u_DeformGen == DGEN_WAVE_SAWTOOTH) - { - func = fract(value); - } - else if (u_DeformGen == DGEN_WAVE_INVERSE_SAWTOOTH) - { - func = (1.0 - fract(value)); - } - else if (u_DeformGen == DGEN_BULGE) - { - func = sin(value); - } - - return pos + normal * (base + func * amplitude); -} - -void main() -{ - vec4 position = mix(attr_Position, attr_Position2, u_VertexLerp); - vec3 normal = normalize(mix(attr_Normal, attr_Normal2, u_VertexLerp)); - - position.xyz = DeformPosition(position.xyz, normal, attr_TexCoord0.st); - - gl_Position = u_ModelViewProjectionMatrix * position; - - var_Position = (u_ModelMatrix * position).xyz; -} diff --git a/src/rend2/glsl/shadowmask_fp.glsl b/src/rend2/glsl/shadowmask_fp.glsl deleted file mode 100644 index b3a698c8..00000000 --- a/src/rend2/glsl/shadowmask_fp.glsl +++ /dev/null @@ -1,127 +0,0 @@ -uniform sampler2D u_ScreenDepthMap; - -uniform sampler2D u_ShadowMap; -#if defined(USE_SHADOW_CASCADE) -uniform sampler2D u_ShadowMap2; -uniform sampler2D u_ShadowMap3; -#endif - -uniform mat4 u_ShadowMvp; -#if defined(USE_SHADOW_CASCADE) -uniform mat4 u_ShadowMvp2; -uniform mat4 u_ShadowMvp3; -#endif - -uniform vec3 u_ViewOrigin; -uniform vec4 u_ViewInfo; // zfar / znear, zfar - -varying vec2 var_DepthTex; -varying vec3 var_ViewDir; - -// Input: It uses texture coords as the random number seed. -// Output: Random number: [0,1), that is between 0.0 and 0.999999... inclusive. -// Author: Michael Pohoreski -// Copyright: Copyleft 2012 :-) -// Source: http://stackoverflow.com/questions/5149544/can-i-generate-a-random-number-inside-a-pixel-shader - -float random( const vec2 p ) -{ - // We need irrationals for pseudo randomness. - // Most (all?) known transcendental numbers will (generally) work. - const vec2 r = vec2( - 23.1406926327792690, // e^pi (Gelfond's constant) - 2.6651441426902251); // 2^sqrt(2) (Gelfond-Schneider constant) - //return fract( cos( mod( 123456789., 1e-7 + 256. * dot(p,r) ) ) ); - return mod( 123456789., 1e-7 + 256. * dot(p,r) ); -} - -float PCF(const sampler2D shadowmap, const vec2 st, const float dist) -{ - float mult; - float scale = 2.0 / r_shadowMapSize; - -#if defined(USE_SHADOW_FILTER) - float r = random(var_DepthTex.xy); - float sinr = sin(r) * scale; - float cosr = cos(r) * scale; - mat2 rmat = mat2(cosr, sinr, -sinr, cosr); - - mult = step(dist, texture2D(shadowmap, st + rmat * vec2(-0.7055767, 0.196515)).r); - mult += step(dist, texture2D(shadowmap, st + rmat * vec2(0.3524343, -0.7791386)).r); - mult += step(dist, texture2D(shadowmap, st + rmat * vec2(0.2391056, 0.9189604)).r); - #if defined(USE_SHADOW_FILTER2) - mult += step(dist, texture2D(shadowmap, st + rmat * vec2(-0.07580382, -0.09224417)).r); - mult += step(dist, texture2D(shadowmap, st + rmat * vec2(0.5784913, -0.002528916)).r); - mult += step(dist, texture2D(shadowmap, st + rmat * vec2(0.192888, 0.4064181)).r); - mult += step(dist, texture2D(shadowmap, st + rmat * vec2(-0.6335801, -0.5247476)).r); - mult += step(dist, texture2D(shadowmap, st + rmat * vec2(-0.5579782, 0.7491854)).r); - mult += step(dist, texture2D(shadowmap, st + rmat * vec2(0.7320465, 0.6317794)).r); - - mult *= 0.11111; - #else - mult *= 0.33333; - #endif -#else - mult = step(dist, texture2D(shadowmap, st).r); -#endif - - return mult; -} - -float getLinearDepth(sampler2D depthMap, vec2 tex, float zFarDivZNear) -{ - float sampleZDivW = texture2D(depthMap, tex).r; - return 1.0 / mix(zFarDivZNear, 1.0, sampleZDivW); -} - -void main() -{ - float result; - - float depth = getLinearDepth(u_ScreenDepthMap, var_DepthTex, u_ViewInfo.x); - float sampleZ = u_ViewInfo.y * depth; - - vec4 biasPos = vec4(u_ViewOrigin + var_ViewDir * depth * 0.99, 1.0); - - vec4 shadowpos = u_ShadowMvp * biasPos; - -#if defined(USE_SHADOW_CASCADE) - const float fadeTo = 0.5; - result = fadeTo; -#else - result = 0.0; -#endif - - if (all(lessThanEqual(abs(shadowpos.xyz), vec3(abs(shadowpos.w))))) - { - shadowpos.xyz = shadowpos.xyz / shadowpos.w * 0.5 + 0.5; - result = PCF(u_ShadowMap, shadowpos.xy, shadowpos.z); - } -#if defined(USE_SHADOW_CASCADE) - else - { - shadowpos = u_ShadowMvp2 * biasPos; - - if (all(lessThanEqual(abs(shadowpos.xyz), vec3(abs(shadowpos.w))))) - { - shadowpos.xyz = shadowpos.xyz / shadowpos.w * 0.5 + 0.5; - result = PCF(u_ShadowMap2, shadowpos.xy, shadowpos.z); - } - else - { - shadowpos = u_ShadowMvp3 * biasPos; - - if (all(lessThanEqual(abs(shadowpos.xyz), vec3(abs(shadowpos.w))))) - { - shadowpos.xyz = shadowpos.xyz / shadowpos.w * 0.5 + 0.5; - result = PCF(u_ShadowMap3, shadowpos.xy, shadowpos.z); - - float fade = clamp(sampleZ / r_shadowCascadeZFar * 10.0 - 9.0, 0.0, 1.0); - result = mix(result, fadeTo, fade); - } - } - } -#endif - - gl_FragColor = vec4(vec3(result), 1.0); -} diff --git a/src/rend2/glsl/shadowmask_vp.glsl b/src/rend2/glsl/shadowmask_vp.glsl deleted file mode 100644 index 13166a24..00000000 --- a/src/rend2/glsl/shadowmask_vp.glsl +++ /dev/null @@ -1,18 +0,0 @@ -attribute vec4 attr_Position; -attribute vec4 attr_TexCoord0; - -uniform vec3 u_ViewForward; -uniform vec3 u_ViewLeft; -uniform vec3 u_ViewUp; -uniform vec4 u_ViewInfo; // zfar / znear - -varying vec2 var_DepthTex; -varying vec3 var_ViewDir; - -void main() -{ - gl_Position = attr_Position; - vec2 screenCoords = gl_Position.xy / gl_Position.w; - var_DepthTex = attr_TexCoord0.xy; - var_ViewDir = u_ViewForward + u_ViewLeft * -screenCoords.x + u_ViewUp * screenCoords.y; -} diff --git a/src/rend2/glsl/ssao_fp.glsl b/src/rend2/glsl/ssao_fp.glsl deleted file mode 100644 index 6263284c..00000000 --- a/src/rend2/glsl/ssao_fp.glsl +++ /dev/null @@ -1,86 +0,0 @@ -uniform sampler2D u_ScreenDepthMap; - -uniform vec4 u_ViewInfo; // zfar / znear, zfar - -varying vec2 var_ScreenTex; - -vec2 poissonDisc[9] = vec2[9]( -vec2(-0.7055767, 0.196515), vec2(0.3524343, -0.7791386), -vec2(0.2391056, 0.9189604), vec2(-0.07580382, -0.09224417), -vec2(0.5784913, -0.002528916), vec2(0.192888, 0.4064181), -vec2(-0.6335801, -0.5247476), vec2(-0.5579782, 0.7491854), -vec2(0.7320465, 0.6317794) -); - -// Input: It uses texture coords as the random number seed. -// Output: Random number: [0,1), that is between 0.0 and 0.999999... inclusive. -// Author: Michael Pohoreski -// Copyright: Copyleft 2012 :-) -// Source: http://stackoverflow.com/questions/5149544/can-i-generate-a-random-number-inside-a-pixel-shader - -float random( const vec2 p ) -{ - // We need irrationals for pseudo randomness. - // Most (all?) known transcendental numbers will (generally) work. - const vec2 r = vec2( - 23.1406926327792690, // e^pi (Gelfond's constant) - 2.6651441426902251); // 2^sqrt(2) (Gelfond-Schneider constant) - //return fract( cos( mod( 123456789., 1e-7 + 256. * dot(p,r) ) ) ); - return mod( 123456789., 1e-7 + 256. * dot(p,r) ); -} - -mat2 randomRotation( const vec2 p ) -{ - float r = random(p); - float sinr = sin(r); - float cosr = cos(r); - return mat2(cosr, sinr, -sinr, cosr); -} - -float getLinearDepth(sampler2D depthMap, const vec2 tex, const float zFarDivZNear) -{ - float sampleZDivW = texture2D(depthMap, tex).r; - return 1.0 / mix(zFarDivZNear, 1.0, sampleZDivW); -} - -float ambientOcclusion(sampler2D depthMap, const vec2 tex, const float zFarDivZNear, const float zFar) -{ - float result = 0; - - float sampleZ = zFar * getLinearDepth(depthMap, tex, zFarDivZNear); - - vec2 expectedSlope = vec2(dFdx(sampleZ), dFdy(sampleZ)) / vec2(dFdx(tex.x), dFdy(tex.y)); - - if (length(expectedSlope) > 5000.0) - return 1.0; - - vec2 offsetScale = vec2(3.0 / sampleZ); - - mat2 rmat = randomRotation(tex); - - int i; - for (i = 0; i < 3; i++) - { - vec2 offset = rmat * poissonDisc[i] * offsetScale; - float sampleZ2 = zFar * getLinearDepth(depthMap, tex + offset, zFarDivZNear); - - if (abs(sampleZ - sampleZ2) > 20.0) - result += 1.0; - else - { - float expectedZ = sampleZ + dot(expectedSlope, offset); - result += step(expectedZ - 1.0, sampleZ2); - } - } - - result *= 0.33333; - - return result; -} - -void main() -{ - float result = ambientOcclusion(u_ScreenDepthMap, var_ScreenTex, u_ViewInfo.x, u_ViewInfo.y); - - gl_FragColor = vec4(vec3(result), 1.0); -} diff --git a/src/rend2/glsl/ssao_vp.glsl b/src/rend2/glsl/ssao_vp.glsl deleted file mode 100644 index 9c46a79f..00000000 --- a/src/rend2/glsl/ssao_vp.glsl +++ /dev/null @@ -1,12 +0,0 @@ -attribute vec4 attr_Position; -attribute vec4 attr_TexCoord0; - -varying vec2 var_ScreenTex; - -void main() -{ - gl_Position = attr_Position; - var_ScreenTex = attr_TexCoord0.xy; - //vec2 screenCoords = gl_Position.xy / gl_Position.w; - //var_ScreenTex = screenCoords * 0.5 + 0.5; -} diff --git a/src/rend2/glsl/texturecolor_fp.glsl b/src/rend2/glsl/texturecolor_fp.glsl deleted file mode 100644 index 5646b511..00000000 --- a/src/rend2/glsl/texturecolor_fp.glsl +++ /dev/null @@ -1,12 +0,0 @@ -#version 120 - -uniform sampler2D u_DiffuseMap; -uniform vec4 u_Color; - -varying vec2 var_Tex1; - - -void main() -{ - gl_FragColor = texture2D(u_DiffuseMap, var_Tex1) * u_Color; -} diff --git a/src/rend2/glsl/texturecolor_vp.glsl b/src/rend2/glsl/texturecolor_vp.glsl deleted file mode 100644 index ae26a18e..00000000 --- a/src/rend2/glsl/texturecolor_vp.glsl +++ /dev/null @@ -1,15 +0,0 @@ -#version 120 - -attribute vec4 attr_Position; -attribute vec4 attr_TexCoord0; - -uniform mat4 u_ModelViewProjectionMatrix; - -varying vec2 var_Tex1; - - -void main() -{ - gl_Position = u_ModelViewProjectionMatrix * attr_Position; - var_Tex1 = attr_TexCoord0.st; -} diff --git a/src/rend2/glsl/tonemap_fp.glsl b/src/rend2/glsl/tonemap_fp.glsl deleted file mode 100644 index 9b18de8a..00000000 --- a/src/rend2/glsl/tonemap_fp.glsl +++ /dev/null @@ -1,48 +0,0 @@ -uniform sampler2D u_TextureMap; -uniform sampler2D u_LevelsMap; - -uniform vec4 u_Color; - -uniform vec2 u_AutoExposureMinMax; -uniform vec3 u_ToneMinAvgMaxLinear; - -varying vec2 var_TexCoords; - -const vec3 LUMINANCE_VECTOR = vec3(0.2125, 0.7154, 0.0721); //vec3(0.299, 0.587, 0.114); - -vec3 FilmicTonemap(vec3 x) -{ - const float SS = 0.22; // Shoulder Strength - const float LS = 0.30; // Linear Strength - const float LA = 0.10; // Linear Angle - const float TS = 0.20; // Toe Strength - const float TAN = 0.01; // Toe Angle Numerator - const float TAD = 0.30; // Toe Angle Denominator - - vec3 SSxx = SS * x * x; - vec3 LSx = LS * x; - vec3 LALSx = LSx * LA; - - return ((SSxx + LALSx + TS * TAN) / (SSxx + LSx + TS * TAD)) - TAN / TAD; - - //return ((x*(SS*x+LA*LS)+TS*TAN)/(x*(SS*x+LS)+TS*TAD)) - TAN/TAD; - -} - -void main() -{ - vec4 color = texture2D(u_TextureMap, var_TexCoords) * u_Color; - vec3 minAvgMax = texture2D(u_LevelsMap, var_TexCoords).rgb; - vec3 logMinAvgMaxLum = clamp(minAvgMax * 20.0 - 10.0, -u_AutoExposureMinMax.y, -u_AutoExposureMinMax.x); - - float avgLum = exp2(logMinAvgMaxLum.y); - //float maxLum = exp2(logMinAvgMaxLum.z); - - color.rgb *= u_ToneMinAvgMaxLinear.y / avgLum; - color.rgb = max(vec3(0.0), color.rgb - vec3(u_ToneMinAvgMaxLinear.x)); - - vec3 fWhite = 1.0 / FilmicTonemap(vec3(u_ToneMinAvgMaxLinear.z - u_ToneMinAvgMaxLinear.x)); - color.rgb = FilmicTonemap(color.rgb) * fWhite; - - gl_FragColor = clamp(color, 0.0, 1.0); -} diff --git a/src/rend2/glsl/tonemap_vp.glsl b/src/rend2/glsl/tonemap_vp.glsl deleted file mode 100644 index 5ca41600..00000000 --- a/src/rend2/glsl/tonemap_vp.glsl +++ /dev/null @@ -1,13 +0,0 @@ -attribute vec4 attr_Position; -attribute vec4 attr_TexCoord0; - -uniform mat4 u_ModelViewProjectionMatrix; - -varying vec2 var_TexCoords; - - -void main() -{ - gl_Position = u_ModelViewProjectionMatrix * attr_Position; - var_TexCoords = attr_TexCoord0.st; -} diff --git a/src/rend2/qgl.h b/src/rend2/qgl.h deleted file mode 100644 index 6013a87c..00000000 --- a/src/rend2/qgl.h +++ /dev/null @@ -1,755 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -/* -** QGL.H -*/ - -#ifndef __QGL_H__ -#define __QGL_H__ - -#ifdef USE_LOCAL_HEADERS -# include "SDL_opengl.h" -#else -# include -#endif - -extern void (APIENTRYP qglActiveTextureARB) (GLenum texture); -extern void (APIENTRYP qglClientActiveTextureARB) (GLenum texture); -extern void (APIENTRYP qglMultiTexCoord2fARB) (GLenum target, GLfloat s, GLfloat t); - -extern void (APIENTRYP qglLockArraysEXT) (GLint first, GLsizei count); -extern void (APIENTRYP qglUnlockArraysEXT) (void); - -// GL_EXT_draw_range_elements -extern void (APIENTRY * qglDrawRangeElementsEXT) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); - -// GL_EXT_multi_draw_arrays -extern void (APIENTRY * qglMultiDrawArraysEXT) (GLenum, GLint *, GLsizei *, GLsizei); -extern void (APIENTRY * qglMultiDrawElementsEXT) (GLenum, const GLsizei *, GLenum, const GLvoid **, GLsizei); - -// GL_ARB_shading_language_100 -#ifndef GL_ARB_shading_language_100 -#define GL_ARB_shading_language_100 -#define GL_SHADING_LANGUAGE_VERSION_ARB 0x8B8C -#endif - -// GL_ARB_vertex_program -extern void (APIENTRY * qglVertexAttrib4fARB) (GLuint, GLfloat, GLfloat, GLfloat, GLfloat); -extern void (APIENTRY * qglVertexAttrib4fvARB) (GLuint, const GLfloat *); -extern void (APIENTRY * qglVertexAttribPointerARB) (GLuint index, GLint size, GLenum type, GLboolean normalized, - GLsizei stride, const GLvoid * pointer); -extern void (APIENTRY * qglEnableVertexAttribArrayARB) (GLuint index); -extern void (APIENTRY * qglDisableVertexAttribArrayARB) (GLuint index); - -// GL_ARB_vertex_buffer_object -extern void (APIENTRY * qglBindBufferARB) (GLenum target, GLuint buffer); -extern void (APIENTRY * qglDeleteBuffersARB) (GLsizei n, const GLuint * buffers); -extern void (APIENTRY * qglGenBuffersARB) (GLsizei n, GLuint * buffers); -extern GLboolean(APIENTRY * qglIsBufferARB) (GLuint buffer); -extern void (APIENTRY * qglBufferDataARB) (GLenum target, GLsizeiptrARB size, const GLvoid * data, GLenum usage); -extern void (APIENTRY * qglBufferSubDataARB) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid * data); -extern void (APIENTRY * qglGetBufferSubDataARB) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid * data); -extern void (APIENTRY * qglGetBufferParameterivARB) (GLenum target, GLenum pname, GLint * params); -extern void (APIENTRY * qglGetBufferPointervARB) (GLenum target, GLenum pname, GLvoid * *params); - -// GL_ARB_shader_objects -extern void (APIENTRY * qglDeleteObjectARB) (GLhandleARB obj); -extern GLhandleARB(APIENTRY * qglGetHandleARB) (GLenum pname); -extern void (APIENTRY * qglDetachObjectARB) (GLhandleARB containerObj, GLhandleARB attachedObj); -extern GLhandleARB(APIENTRY * qglCreateShaderObjectARB) (GLenum shaderType); -extern void (APIENTRY * qglShaderSourceARB) (GLhandleARB shaderObj, GLsizei count, const GLcharARB * *string, - const GLint * length); -extern void (APIENTRY * qglCompileShaderARB) (GLhandleARB shaderObj); -extern GLhandleARB(APIENTRY * qglCreateProgramObjectARB) (void); -extern void (APIENTRY * qglAttachObjectARB) (GLhandleARB containerObj, GLhandleARB obj); -extern void (APIENTRY * qglLinkProgramARB) (GLhandleARB programObj); -extern void (APIENTRY * qglUseProgramObjectARB) (GLhandleARB programObj); -extern void (APIENTRY * qglValidateProgramARB) (GLhandleARB programObj); -extern void (APIENTRY * qglUniform1fARB) (GLint location, GLfloat v0); -extern void (APIENTRY * qglUniform2fARB) (GLint location, GLfloat v0, GLfloat v1); -extern void (APIENTRY * qglUniform3fARB) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); -extern void (APIENTRY * qglUniform4fARB) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); -extern void (APIENTRY * qglUniform1iARB) (GLint location, GLint v0); -extern void (APIENTRY * qglUniform2iARB) (GLint location, GLint v0, GLint v1); -extern void (APIENTRY * qglUniform3iARB) (GLint location, GLint v0, GLint v1, GLint v2); -extern void (APIENTRY * qglUniform4iARB) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); -extern void (APIENTRY * qglUniform1fvARB) (GLint location, GLsizei count, const GLfloat * value); -extern void (APIENTRY * qglUniform2fvARB) (GLint location, GLsizei count, const GLfloat * value); -extern void (APIENTRY * qglUniform3fvARB) (GLint location, GLsizei count, const GLfloat * value); -extern void (APIENTRY * qglUniform4fvARB) (GLint location, GLsizei count, const GLfloat * value); -extern void (APIENTRY * qglUniform2ivARB) (GLint location, GLsizei count, const GLint * value); -extern void (APIENTRY * qglUniform3ivARB) (GLint location, GLsizei count, const GLint * value); -extern void (APIENTRY * qglUniform4ivARB) (GLint location, GLsizei count, const GLint * value); -extern void (APIENTRY * qglUniformMatrix2fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); -extern void (APIENTRY * qglUniformMatrix3fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); -extern void (APIENTRY * qglUniformMatrix4fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); -extern void (APIENTRY * qglGetObjectParameterfvARB) (GLhandleARB obj, GLenum pname, GLfloat * params); -extern void (APIENTRY * qglGetObjectParameterivARB) (GLhandleARB obj, GLenum pname, GLint * params); -extern void (APIENTRY * qglGetInfoLogARB) (GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog); -extern void (APIENTRY * qglGetAttachedObjectsARB) (GLhandleARB containerObj, GLsizei maxCount, GLsizei * count, - GLhandleARB * obj); -extern GLint(APIENTRY * qglGetUniformLocationARB) (GLhandleARB programObj, const GLcharARB * name); -extern void (APIENTRY * qglGetActiveUniformARB) (GLhandleARB programObj, GLuint index, GLsizei maxIndex, GLsizei * length, - GLint * size, GLenum * type, GLcharARB * name); -extern void (APIENTRY * qglGetUniformfvARB) (GLhandleARB programObj, GLint location, GLfloat * params); -extern void (APIENTRY * qglGetUniformivARB) (GLhandleARB programObj, GLint location, GLint * params); -extern void (APIENTRY * qglGetShaderSourceARB) (GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * source); - -// GL_ARB_vertex_shader -extern void (APIENTRY * qglBindAttribLocationARB) (GLhandleARB programObj, GLuint index, const GLcharARB * name); -extern void (APIENTRY * qglGetActiveAttribARB) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei * length, - GLint * size, GLenum * type, GLcharARB * name); -extern GLint(APIENTRY * qglGetAttribLocationARB) (GLhandleARB programObj, const GLcharARB * name); - -// GL_ARB_texture_compression -extern void (APIENTRY * qglCompressedTexImage3DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, - GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); -extern void (APIENTRY * qglCompressedTexImage2DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, - GLint border, GLsizei imageSize, const GLvoid *data); -extern void (APIENTRY * qglCompressedTexImage1DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, - GLsizei imageSize, const GLvoid *data); -extern void (APIENTRY * qglCompressedTexSubImage3DARB)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, - GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data); -extern void (APIENTRY * qglCompressedTexSubImage2DARB)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, - GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); -extern void (APIENTRY * qglCompressedTexSubImage1DARB)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, - GLsizei imageSize, const GLvoid *data); -extern void (APIENTRY * qglGetCompressedTexImageARB)(GLenum target, GLint lod, - GLvoid *img); - -// GL_NVX_gpu_memory_info -#ifndef GL_NVX_gpu_memory_info -#define GL_NVX_gpu_memory_info -#define GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX 0x9047 -#define GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX 0x9048 -#define GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX 0x9049 -#define GL_GPU_MEMORY_INFO_EVICTION_COUNT_NVX 0x904A -#define GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX 0x904B -#endif - -// GL_ATI_meminfo -#ifndef GL_ATI_meminfo -#define GL_ATI_meminfo -#define GL_VBO_FREE_MEMORY_ATI 0x87FB -#define GL_TEXTURE_FREE_MEMORY_ATI 0x87FC -#define GL_RENDERBUFFER_FREE_MEMORY_ATI 0x87FD -#endif - -// GL_ARB_texture_float -#ifndef GL_ARB_texture_float -#define GL_ARB_texture_float -#define GL_TEXTURE_RED_TYPE_ARB 0x8C10 -#define GL_TEXTURE_GREEN_TYPE_ARB 0x8C11 -#define GL_TEXTURE_BLUE_TYPE_ARB 0x8C12 -#define GL_TEXTURE_ALPHA_TYPE_ARB 0x8C13 -#define GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14 -#define GL_TEXTURE_INTENSITY_TYPE_ARB 0x8C15 -#define GL_TEXTURE_DEPTH_TYPE_ARB 0x8C16 -#define GL_UNSIGNED_NORMALIZED_ARB 0x8C17 -#define GL_RGBA32F_ARB 0x8814 -#define GL_RGB32F_ARB 0x8815 -#define GL_ALPHA32F_ARB 0x8816 -#define GL_INTENSITY32F_ARB 0x8817 -#define GL_LUMINANCE32F_ARB 0x8818 -#define GL_LUMINANCE_ALPHA32F_ARB 0x8819 -#define GL_RGBA16F_ARB 0x881A -#define GL_RGB16F_ARB 0x881B -#define GL_ALPHA16F_ARB 0x881C -#define GL_INTENSITY16F_ARB 0x881D -#define GL_LUMINANCE16F_ARB 0x881E -#define GL_LUMINANCE_ALPHA16F_ARB 0x881F -#endif - -#ifndef GL_ARB_half_float_pixel -#define GL_ARB_half_float_pixel -#define GL_HALF_FLOAT_ARB 0x140B -#endif - -// GL_EXT_framebuffer_object -extern GLboolean (APIENTRY * qglIsRenderbufferEXT)(GLuint renderbuffer); -extern void (APIENTRY * qglBindRenderbufferEXT)(GLenum target, GLuint renderbuffer); -extern void (APIENTRY * qglDeleteRenderbuffersEXT)(GLsizei n, const GLuint *renderbuffers); -extern void (APIENTRY * qglGenRenderbuffersEXT)(GLsizei n, GLuint *renderbuffers); -extern void (APIENTRY * qglRenderbufferStorageEXT)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); -extern void (APIENTRY * qglGetRenderbufferParameterivEXT)(GLenum target, GLenum pname, GLint *params); -extern GLboolean (APIENTRY * qglIsFramebufferEXT)(GLuint framebuffer); -extern void (APIENTRY * qglBindFramebufferEXT)(GLenum target, GLuint framebuffer); -extern void (APIENTRY * qglDeleteFramebuffersEXT)(GLsizei n, const GLuint *framebuffers); -extern void (APIENTRY * qglGenFramebuffersEXT)(GLsizei n, GLuint *framebuffers); -extern GLenum (APIENTRY * qglCheckFramebufferStatusEXT)(GLenum target); -extern void (APIENTRY * qglFramebufferTexture1DEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, - GLint level); -extern void (APIENTRY * qglFramebufferTexture2DEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, - GLint level); -extern void (APIENTRY * qglFramebufferTexture3DEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, - GLint level, GLint zoffset); -extern void (APIENTRY * qglFramebufferRenderbufferEXT)(GLenum target, GLenum attachment, GLenum renderbuffertarget, - GLuint renderbuffer); -extern void (APIENTRY * qglGetFramebufferAttachmentParameterivEXT)(GLenum target, GLenum attachment, GLenum pname, GLint *params); -extern void (APIENTRY * qglGenerateMipmapEXT)(GLenum target); - -#ifndef GL_EXT_framebuffer_object -#define GL_EXT_framebuffer_object -#define GL_FRAMEBUFFER_EXT 0x8D40 -#define GL_RENDERBUFFER_EXT 0x8D41 -#define GL_STENCIL_INDEX1_EXT 0x8D46 -#define GL_STENCIL_INDEX4_EXT 0x8D47 -#define GL_STENCIL_INDEX8_EXT 0x8D48 -#define GL_STENCIL_INDEX16_EXT 0x8D49 -#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42 -#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43 -#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44 -#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50 -#define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51 -#define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52 -#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53 -#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54 -#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55 -#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0 -#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1 -#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2 -#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3 -#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4 -#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 -#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 -#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 -#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 -#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 -#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 -#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 -#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 -#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 -#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9 -#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA -#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB -#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC -#define GL_COLOR_ATTACHMENT13_EXT 0x8CED -#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE -#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF -#define GL_DEPTH_ATTACHMENT_EXT 0x8D00 -#define GL_STENCIL_ATTACHMENT_EXT 0x8D20 -#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 -#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 -#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 -#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 -#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA -#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB -#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC -#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD -#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6 -#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7 -#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF -#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8 -#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506 -#endif - -// GL_EXT_packed_depth_stencil -#ifndef GL_EXT_packed_depth_stencil -#define GL_EXT_packed_depth_stencil -#define GL_DEPTH_STENCIL_EXT 0x84F9 -#define GL_UNSIGNED_INT_24_8_EXT 0x84FA -#define GL_DEPTH24_STENCIL8_EXT 0x88F0 -#define GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1 -#endif - -// GL_ARB_occlusion_query -extern void (APIENTRY * qglGenQueriesARB)(GLsizei n, GLuint *ids); -extern void (APIENTRY * qglDeleteQueriesARB)(GLsizei n, const GLuint *ids); -extern GLboolean (APIENTRY * qglIsQueryARB)(GLuint id); -extern void (APIENTRY * qglBeginQueryARB)(GLenum target, GLuint id); -extern void (APIENTRY * qglEndQueryARB)(GLenum target); -extern void (APIENTRY * qglGetQueryivARB)(GLenum target, GLenum pname, GLint *params); -extern void (APIENTRY * qglGetQueryObjectivARB)(GLuint id, GLenum pname, GLint *params); -extern void (APIENTRY * qglGetQueryObjectuivARB)(GLuint id, GLenum pname, GLuint *params); - -#ifndef GL_ARB_occlusion_query -#define GL_ARB_occlusion_query -#define GL_SAMPLES_PASSED_ARB 0x8914 -#define GL_QUERY_COUNTER_BITS_ARB 0x8864 -#define GL_CURRENT_QUERY_ARB 0x8865 -#define GL_QUERY_RESULT_ARB 0x8866 -#define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867 -#endif - -// GL_EXT_framebuffer_blit -extern void (APIENTRY * qglBlitFramebufferEXT)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, - GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, - GLbitfield mask, GLenum filter); - -#ifndef GL_EXT_framebuffer_blit -#define GL_EXT_framebuffer_blit -#define GL_READ_FRAMEBUFFER_EXT 0x8CA8 -#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 -#define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6 -#define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA -#endif - -// GL_EXT_framebuffer_multisample -extern void (APIENTRY * qglRenderbufferStorageMultisampleEXT)(GLenum target, GLsizei samples, - GLenum internalformat, GLsizei width, GLsizei height); - -#ifndef GL_EXT_framebuffer_multisample -#define GL_EXT_framebuffer_multisample -#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB -#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56 -#define GL_MAX_SAMPLES_EXT 0x8D57 -#endif - -#ifndef GL_EXT_texture_sRGB -#define GL_EXT_texture_sRGB -#define GL_SRGB_EXT 0x8C40 -#define GL_SRGB8_EXT 0x8C41 -#define GL_SRGB_ALPHA_EXT 0x8C42 -#define GL_SRGB8_ALPHA8_EXT 0x8C43 -#define GL_SLUMINANCE_ALPHA_EXT 0x8C44 -#define GL_SLUMINANCE8_ALPHA8_EXT 0x8C45 -#define GL_SLUMINANCE_EXT 0x8C46 -#define GL_SLUMINANCE8_EXT 0x8C47 -#define GL_COMPRESSED_SRGB_EXT 0x8C48 -#define GL_COMPRESSED_SRGB_ALPHA_EXT 0x8C49 -#define GL_COMPRESSED_SLUMINANCE_EXT 0x8C4A -#define GL_COMPRESSED_SLUMINANCE_ALPHA_EXT 0x8C4B -#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C -#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D -#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E -#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F -#endif - -#ifndef GL_EXT_framebuffer_sRGB -#define GL_EXT_framebuffer_sRGB -#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 -#endif - -#ifndef GL_EXT_texture_compression_latc -#define GL_EXT_texture_compression_latc -#define GL_COMPRESSED_LUMINANCE_LATC1_EXT 0x8C70 -#define GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT 0x8C71 -#define GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72 -#define GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT 0x8C73 -#endif - -#ifndef GL_ARB_texture_compression_bptc -#define GL_ARB_texture_compression_bptc -#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C -#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D -#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E -#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F -#endif - -// GL_ARB_draw_buffers -extern void (APIENTRY * qglDrawBuffersARB)(GLsizei n, const GLenum *bufs); -#ifndef GL_ARB_draw_buffers -#define GL_ARB_draw_buffers -#define GL_MAX_DRAW_BUFFERS_ARB 0x8824 -#define GL_DRAW_BUFFER0_ARB 0x8825 -#define GL_DRAW_BUFFER1_ARB 0x8826 -#define GL_DRAW_BUFFER2_ARB 0x8827 -#define GL_DRAW_BUFFER3_ARB 0x8828 -#define GL_DRAW_BUFFER4_ARB 0x8829 -#define GL_DRAW_BUFFER5_ARB 0x882A -#define GL_DRAW_BUFFER6_ARB 0x882B -#define GL_DRAW_BUFFER7_ARB 0x882C -#define GL_DRAW_BUFFER8_ARB 0x882D -#define GL_DRAW_BUFFER9_ARB 0x882E -#define GL_DRAW_BUFFER10_ARB 0x882F -#define GL_DRAW_BUFFER11_ARB 0x8830 -#define GL_DRAW_BUFFER12_ARB 0x8831 -#define GL_DRAW_BUFFER13_ARB 0x8832 -#define GL_DRAW_BUFFER14_ARB 0x8833 -#define GL_DRAW_BUFFER15_ARB 0x8834 -#endif - -#ifndef GL_ARB_depth_clamp -#define GL_ARB_depth_clamp -#define GL_DEPTH_CLAMP 0x864F -#endif - -#if defined(WIN32) -// WGL_ARB_create_context -#ifndef WGL_ARB_create_context -#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 -#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 -#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 -#define WGL_CONTEXT_FLAGS_ARB 0x2094 -#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 -#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001 -#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 -#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 -#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 -#define ERROR_INVALID_VERSION_ARB 0x2095 -#define ERROR_INVALID_PROFILE_ARB 0x2096 -#endif - -extern HGLRC(APIENTRY * qwglCreateContextAttribsARB) (HDC hdC, HGLRC hShareContext, const int *attribList); -#endif - -#if 0 //defined(__linux__) -// GLX_ARB_create_context -#ifndef GLX_ARB_create_context -#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001 -#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 -#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 -#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 -#define GLX_CONTEXT_FLAGS_ARB 0x2094 -#endif - -extern GLXContext (APIENTRY * qglXCreateContextAttribsARB) (Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list); -#endif - -//=========================================================================== - -#define qglAccum glAccum -#define qglAlphaFunc glAlphaFunc -#define qglAreTexturesResident glAreTexturesResident -#define qglArrayElement glArrayElement -#define qglBegin glBegin -#define qglBindTexture glBindTexture -#define qglBitmap glBitmap -#define qglBlendFunc glBlendFunc -#define qglCallList glCallList -#define qglCallLists glCallLists -#define qglClear glClear -#define qglClearAccum glClearAccum -#define qglClearColor glClearColor -#define qglClearDepth glClearDepth -#define qglClearIndex glClearIndex -#define qglClearStencil glClearStencil -#define qglClipPlane glClipPlane -#define qglColor3b glColor3b -#define qglColor3bv glColor3bv -#define qglColor3d glColor3d -#define qglColor3dv glColor3dv -#define qglColor3f glColor3f -#define qglColor3fv glColor3fv -#define qglColor3i glColor3i -#define qglColor3iv glColor3iv -#define qglColor3s glColor3s -#define qglColor3sv glColor3sv -#define qglColor3ub glColor3ub -#define qglColor3ubv glColor3ubv -#define qglColor3ui glColor3ui -#define qglColor3uiv glColor3uiv -#define qglColor3us glColor3us -#define qglColor3usv glColor3usv -#define qglColor4b glColor4b -#define qglColor4bv glColor4bv -#define qglColor4d glColor4d -#define qglColor4dv glColor4dv -#define qglColor4f glColor4f -#define qglColor4fv glColor4fv -#define qglColor4i glColor4i -#define qglColor4iv glColor4iv -#define qglColor4s glColor4s -#define qglColor4sv glColor4sv -#define qglColor4ub glColor4ub -#define qglColor4ubv glColor4ubv -#define qglColor4ui glColor4ui -#define qglColor4uiv glColor4uiv -#define qglColor4us glColor4us -#define qglColor4usv glColor4usv -#define qglColorMask glColorMask -#define qglColorMaterial glColorMaterial -#define qglColorPointer glColorPointer -#define qglCopyPixels glCopyPixels -#define qglCopyTexImage1D glCopyTexImage1D -#define qglCopyTexImage2D glCopyTexImage2D -#define qglCopyTexSubImage1D glCopyTexSubImage1D -#define qglCopyTexSubImage2D glCopyTexSubImage2D -#define qglCullFace glCullFace -#define qglDeleteLists glDeleteLists -#define qglDeleteTextures glDeleteTextures -#define qglDepthFunc glDepthFunc -#define qglDepthMask glDepthMask -#define qglDepthRange glDepthRange -#define qglDisable glDisable -#define qglDisableClientState glDisableClientState -#define qglDrawArrays glDrawArrays -#define qglDrawBuffer glDrawBuffer -#define qglDrawElements glDrawElements -#define qglDrawPixels glDrawPixels -#define qglEdgeFlag glEdgeFlag -#define qglEdgeFlagPointer glEdgeFlagPointer -#define qglEdgeFlagv glEdgeFlagv -#define qglEnable glEnable -#define qglEnableClientState glEnableClientState -#define qglEnd glEnd -#define qglEndList glEndList -#define qglEvalCoord1d glEvalCoord1d -#define qglEvalCoord1dv glEvalCoord1dv -#define qglEvalCoord1f glEvalCoord1f -#define qglEvalCoord1fv glEvalCoord1fv -#define qglEvalCoord2d glEvalCoord2d -#define qglEvalCoord2dv glEvalCoord2dv -#define qglEvalCoord2f glEvalCoord2f -#define qglEvalCoord2fv glEvalCoord2fv -#define qglEvalMesh1 glEvalMesh1 -#define qglEvalMesh2 glEvalMesh2 -#define qglEvalPoint1 glEvalPoint1 -#define qglEvalPoint2 glEvalPoint2 -#define qglFeedbackBuffer glFeedbackBuffer -#define qglFinish glFinish -#define qglFlush glFlush -#define qglFogf glFogf -#define qglFogfv glFogfv -#define qglFogi glFogi -#define qglFogiv glFogiv -#define qglFrontFace glFrontFace -#define qglFrustum glFrustum -#define qglGenLists glGenLists -#define qglGenTextures glGenTextures -#define qglGetBooleanv glGetBooleanv -#define qglGetClipPlane glGetClipPlane -#define qglGetDoublev glGetDoublev -#define qglGetError glGetError -#define qglGetFloatv glGetFloatv -#define qglGetIntegerv glGetIntegerv -#define qglGetLightfv glGetLightfv -#define qglGetLightiv glGetLightiv -#define qglGetMapdv glGetMapdv -#define qglGetMapfv glGetMapfv -#define qglGetMapiv glGetMapiv -#define qglGetMaterialfv glGetMaterialfv -#define qglGetMaterialiv glGetMaterialiv -#define qglGetPixelMapfv glGetPixelMapfv -#define qglGetPixelMapuiv glGetPixelMapuiv -#define qglGetPixelMapusv glGetPixelMapusv -#define qglGetPointerv glGetPointerv -#define qglGetPolygonStipple glGetPolygonStipple -#define qglGetString glGetString -#define qglGetTexGendv glGetTexGendv -#define qglGetTexGenfv glGetTexGenfv -#define qglGetTexGeniv glGetTexGeniv -#define qglGetTexImage glGetTexImage -#define qglGetTexLevelParameterfv glGetTexLevelParameterfv -#define qglGetTexLevelParameteriv glGetTexLevelParameteriv -#define qglGetTexParameterfv glGetTexParameterfv -#define qglGetTexParameteriv glGetTexParameteriv -#define qglHint glHint -#define qglIndexMask glIndexMask -#define qglIndexPointer glIndexPointer -#define qglIndexd glIndexd -#define qglIndexdv glIndexdv -#define qglIndexf glIndexf -#define qglIndexfv glIndexfv -#define qglIndexi glIndexi -#define qglIndexiv glIndexiv -#define qglIndexs glIndexs -#define qglIndexsv glIndexsv -#define qglIndexub glIndexub -#define qglIndexubv glIndexubv -#define qglInitNames glInitNames -#define qglInterleavedArrays glInterleavedArrays -#define qglIsEnabled glIsEnabled -#define qglIsList glIsList -#define qglIsTexture glIsTexture -#define qglLightModelf glLightModelf -#define qglLightModelfv glLightModelfv -#define qglLightModeli glLightModeli -#define qglLightModeliv glLightModeliv -#define qglLightf glLightf -#define qglLightfv glLightfv -#define qglLighti glLighti -#define qglLightiv glLightiv -#define qglLineStipple glLineStipple -#define qglLineWidth glLineWidth -#define qglListBase glListBase -#define qglLoadIdentity glLoadIdentity -#define qglLoadMatrixd glLoadMatrixd -#define qglLoadMatrixf glLoadMatrixf -#define qglLoadName glLoadName -#define qglLogicOp glLogicOp -#define qglMap1d glMap1d -#define qglMap1f glMap1f -#define qglMap2d glMap2d -#define qglMap2f glMap2f -#define qglMapGrid1d glMapGrid1d -#define qglMapGrid1f glMapGrid1f -#define qglMapGrid2d glMapGrid2d -#define qglMapGrid2f glMapGrid2f -#define qglMaterialf glMaterialf -#define qglMaterialfv glMaterialfv -#define qglMateriali glMateriali -#define qglMaterialiv glMaterialiv -#define qglMatrixMode glMatrixMode -#define qglMultMatrixd glMultMatrixd -#define qglMultMatrixf glMultMatrixf -#define qglNewList glNewList -#define qglNormal3b glNormal3b -#define qglNormal3bv glNormal3bv -#define qglNormal3d glNormal3d -#define qglNormal3dv glNormal3dv -#define qglNormal3f glNormal3f -#define qglNormal3fv glNormal3fv -#define qglNormal3i glNormal3i -#define qglNormal3iv glNormal3iv -#define qglNormal3s glNormal3s -#define qglNormal3sv glNormal3sv -#define qglNormalPointer glNormalPointer -#define qglOrtho glOrtho -#define qglPassThrough glPassThrough -#define qglPixelMapfv glPixelMapfv -#define qglPixelMapuiv glPixelMapuiv -#define qglPixelMapusv glPixelMapusv -#define qglPixelStoref glPixelStoref -#define qglPixelStorei glPixelStorei -#define qglPixelTransferf glPixelTransferf -#define qglPixelTransferi glPixelTransferi -#define qglPixelZoom glPixelZoom -#define qglPointSize glPointSize -#define qglPolygonMode glPolygonMode -#define qglPolygonOffset glPolygonOffset -#define qglPolygonStipple glPolygonStipple -#define qglPopAttrib glPopAttrib -#define qglPopClientAttrib glPopClientAttrib -#define qglPopMatrix glPopMatrix -#define qglPopName glPopName -#define qglPrioritizeTextures glPrioritizeTextures -#define qglPushAttrib glPushAttrib -#define qglPushClientAttrib glPushClientAttrib -#define qglPushMatrix glPushMatrix -#define qglPushName glPushName -#define qglRasterPos2d glRasterPos2d -#define qglRasterPos2dv glRasterPos2dv -#define qglRasterPos2f glRasterPos2f -#define qglRasterPos2fv glRasterPos2fv -#define qglRasterPos2i glRasterPos2i -#define qglRasterPos2iv glRasterPos2iv -#define qglRasterPos2s glRasterPos2s -#define qglRasterPos2sv glRasterPos2sv -#define qglRasterPos3d glRasterPos3d -#define qglRasterPos3dv glRasterPos3dv -#define qglRasterPos3f glRasterPos3f -#define qglRasterPos3fv glRasterPos3fv -#define qglRasterPos3i glRasterPos3i -#define qglRasterPos3iv glRasterPos3iv -#define qglRasterPos3s glRasterPos3s -#define qglRasterPos3sv glRasterPos3sv -#define qglRasterPos4d glRasterPos4d -#define qglRasterPos4dv glRasterPos4dv -#define qglRasterPos4f glRasterPos4f -#define qglRasterPos4fv glRasterPos4fv -#define qglRasterPos4i glRasterPos4i -#define qglRasterPos4iv glRasterPos4iv -#define qglRasterPos4s glRasterPos4s -#define qglRasterPos4sv glRasterPos4sv -#define qglReadBuffer glReadBuffer -#define qglReadPixels glReadPixels -#define qglRectd glRectd -#define qglRectdv glRectdv -#define qglRectf glRectf -#define qglRectfv glRectfv -#define qglRecti glRecti -#define qglRectiv glRectiv -#define qglRects glRects -#define qglRectsv glRectsv -#define qglRenderMode glRenderMode -#define qglRotated glRotated -#define qglRotatef glRotatef -#define qglScaled glScaled -#define qglScalef glScalef -#define qglScissor glScissor -#define qglSelectBuffer glSelectBuffer -#define qglShadeModel glShadeModel -#define qglStencilFunc glStencilFunc -#define qglStencilMask glStencilMask -#define qglStencilOp glStencilOp -#define qglTexCoord1d glTexCoord1d -#define qglTexCoord1dv glTexCoord1dv -#define qglTexCoord1f glTexCoord1f -#define qglTexCoord1fv glTexCoord1fv -#define qglTexCoord1i glTexCoord1i -#define qglTexCoord1iv glTexCoord1iv -#define qglTexCoord1s glTexCoord1s -#define qglTexCoord1sv glTexCoord1sv -#define qglTexCoord2d glTexCoord2d -#define qglTexCoord2dv glTexCoord2dv -#define qglTexCoord2f glTexCoord2f -#define qglTexCoord2fv glTexCoord2fv -#define qglTexCoord2i glTexCoord2i -#define qglTexCoord2iv glTexCoord2iv -#define qglTexCoord2s glTexCoord2s -#define qglTexCoord2sv glTexCoord2sv -#define qglTexCoord3d glTexCoord3d -#define qglTexCoord3dv glTexCoord3dv -#define qglTexCoord3f glTexCoord3f -#define qglTexCoord3fv glTexCoord3fv -#define qglTexCoord3i glTexCoord3i -#define qglTexCoord3iv glTexCoord3iv -#define qglTexCoord3s glTexCoord3s -#define qglTexCoord3sv glTexCoord3sv -#define qglTexCoord4d glTexCoord4d -#define qglTexCoord4dv glTexCoord4dv -#define qglTexCoord4f glTexCoord4f -#define qglTexCoord4fv glTexCoord4fv -#define qglTexCoord4i glTexCoord4i -#define qglTexCoord4iv glTexCoord4iv -#define qglTexCoord4s glTexCoord4s -#define qglTexCoord4sv glTexCoord4sv -#define qglTexCoordPointer glTexCoordPointer -#define qglTexEnvf glTexEnvf -#define qglTexEnvfv glTexEnvfv -#define qglTexEnvi glTexEnvi -#define qglTexEnviv glTexEnviv -#define qglTexGend glTexGend -#define qglTexGendv glTexGendv -#define qglTexGenf glTexGenf -#define qglTexGenfv glTexGenfv -#define qglTexGeni glTexGeni -#define qglTexGeniv glTexGeniv -#define qglTexImage1D glTexImage1D -#define qglTexImage2D glTexImage2D -#define qglTexParameterf glTexParameterf -#define qglTexParameterfv glTexParameterfv -#define qglTexParameteri glTexParameteri -#define qglTexParameteriv glTexParameteriv -#define qglTexSubImage1D glTexSubImage1D -#define qglTexSubImage2D glTexSubImage2D -#define qglTranslated glTranslated -#define qglTranslatef glTranslatef -#define qglVertex2d glVertex2d -#define qglVertex2dv glVertex2dv -#define qglVertex2f glVertex2f -#define qglVertex2fv glVertex2fv -#define qglVertex2i glVertex2i -#define qglVertex2iv glVertex2iv -#define qglVertex2s glVertex2s -#define qglVertex2sv glVertex2sv -#define qglVertex3d glVertex3d -#define qglVertex3dv glVertex3dv -#define qglVertex3f glVertex3f -#define qglVertex3fv glVertex3fv -#define qglVertex3i glVertex3i -#define qglVertex3iv glVertex3iv -#define qglVertex3s glVertex3s -#define qglVertex3sv glVertex3sv -#define qglVertex4d glVertex4d -#define qglVertex4dv glVertex4dv -#define qglVertex4f glVertex4f -#define qglVertex4fv glVertex4fv -#define qglVertex4i glVertex4i -#define qglVertex4iv glVertex4iv -#define qglVertex4s glVertex4s -#define qglVertex4sv glVertex4sv -#define qglVertexPointer glVertexPointer -#define qglViewport glViewport - -#endif diff --git a/src/rend2/tr_animation.c b/src/rend2/tr_animation.c deleted file mode 100644 index 794111c6..00000000 --- a/src/rend2/tr_animation.c +++ /dev/null @@ -1,658 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "tr_local.h" - -/* - -All bones should be an identity orientation to display the mesh exactly -as it is specified. - -For all other frames, the bones represent the transformation from the -orientation of the bone in the base frame to the orientation in this -frame. - -*/ - -/* -============== -R_AddAnimSurfaces -============== -*/ -void R_AddAnimSurfaces( trRefEntity_t *ent ) { - md4Header_t *header; - md4Surface_t *surface; - md4LOD_t *lod; - shader_t *shader; - int i; - - header = (md4Header_t *) tr.currentModel->modelData; - lod = (md4LOD_t *)( (byte *)header + header->ofsLODs ); - - surface = (md4Surface_t *)( (byte *)lod + lod->ofsSurfaces ); - for ( i = 0 ; i < lod->numSurfaces ; i++ ) { - shader = R_GetShaderByHandle( surface->shaderIndex ); - R_AddDrawSurf( (void *)surface, shader, 0 /*fogNum*/, qfalse, qfalse ); - surface = (md4Surface_t *)( (byte *)surface + surface->ofsEnd ); - } -} - -/* -============== -RB_SurfaceAnim -============== -*/ -void RB_SurfaceAnim( md4Surface_t *surface ) { - int i, j, k; - float frontlerp, backlerp; - int *triangles; - int indexes; - int baseIndex, baseVertex; - int numVerts; - md4Vertex_t *v; - md4Bone_t bones[MD4_MAX_BONES]; - md4Bone_t *bonePtr, *bone; - md4Header_t *header; - md4Frame_t *frame; - md4Frame_t *oldFrame; - int frameSize; - - - if ( backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame ) { - backlerp = 0; - frontlerp = 1; - } else { - backlerp = backEnd.currentEntity->e.backlerp; - frontlerp = 1.0f - backlerp; - } - header = (md4Header_t *)((byte *)surface + surface->ofsHeader); - - frameSize = (size_t)( &((md4Frame_t *)0)->bones[ header->numBones ] ); - - frame = (md4Frame_t *)((byte *)header + header->ofsFrames + - backEnd.currentEntity->e.frame * frameSize ); - oldFrame = (md4Frame_t *)((byte *)header + header->ofsFrames + - backEnd.currentEntity->e.oldframe * frameSize ); - - RB_CheckOverflow( surface->numVerts, surface->numTriangles * 3 ); - - triangles = (int *) ((byte *)surface + surface->ofsTriangles); - indexes = surface->numTriangles * 3; - baseIndex = tess.numIndexes; - baseVertex = tess.numVertexes; - for (j = 0 ; j < indexes ; j++) { - tess.indexes[baseIndex + j] = baseIndex + triangles[j]; - } - tess.numIndexes += indexes; - - // - // lerp all the needed bones - // - if ( !backlerp ) { - // no lerping needed - bonePtr = frame->bones; - } else { - bonePtr = bones; - for ( i = 0 ; i < header->numBones*12 ; i++ ) { - ((float *)bonePtr)[i] = frontlerp * ((float *)frame->bones)[i] - + backlerp * ((float *)oldFrame->bones)[i]; - } - } - - // - // deform the vertexes by the lerped bones - // - numVerts = surface->numVerts; - // FIXME - // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left - // in for reference. - //v = (md4Vertex_t *) ((byte *)surface + surface->ofsVerts + 12); - v = (md4Vertex_t *) ((byte *)surface + surface->ofsVerts); - for ( j = 0; j < numVerts; j++ ) { - vec3_t tempVert, tempNormal; - md4Weight_t *w; - - VectorClear( tempVert ); - VectorClear( tempNormal ); - w = v->weights; - for ( k = 0 ; k < v->numWeights ; k++, w++ ) { - bone = bonePtr + w->boneIndex; - - tempVert[0] += w->boneWeight * ( DotProduct( bone->matrix[0], w->offset ) + bone->matrix[0][3] ); - tempVert[1] += w->boneWeight * ( DotProduct( bone->matrix[1], w->offset ) + bone->matrix[1][3] ); - tempVert[2] += w->boneWeight * ( DotProduct( bone->matrix[2], w->offset ) + bone->matrix[2][3] ); - - tempNormal[0] += w->boneWeight * DotProduct( bone->matrix[0], v->normal ); - tempNormal[1] += w->boneWeight * DotProduct( bone->matrix[1], v->normal ); - tempNormal[2] += w->boneWeight * DotProduct( bone->matrix[2], v->normal ); - } - - tess.xyz[baseVertex + j][0] = tempVert[0]; - tess.xyz[baseVertex + j][1] = tempVert[1]; - tess.xyz[baseVertex + j][2] = tempVert[2]; - - tess.normal[baseVertex + j][0] = tempNormal[0]; - tess.normal[baseVertex + j][1] = tempNormal[1]; - tess.normal[baseVertex + j][2] = tempNormal[2]; - - tess.texCoords[baseVertex + j][0][0] = v->texCoords[0]; - tess.texCoords[baseVertex + j][0][1] = v->texCoords[1]; - - // FIXME - // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left - // in for reference. - //v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights] + 12 ); - v = (md4Vertex_t *)&v->weights[v->numWeights]; - } - - tess.numVertexes += surface->numVerts; -} - - -#ifdef RAVENMD4 - -// copied and adapted from tr_mesh.c - -/* -============= -R_MDRCullModel -============= -*/ - -static int R_MDRCullModel( mdrHeader_t *header, trRefEntity_t *ent ) { - vec3_t bounds[2]; - mdrFrame_t *oldFrame, *newFrame; - int i, frameSize; - - frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] ); - - // compute frame pointers - newFrame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.frame); - oldFrame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.oldframe); - - // cull bounding sphere ONLY if this is not an upscaled entity - if ( !ent->e.nonNormalizedAxes ) - { - if ( ent->e.frame == ent->e.oldframe ) - { - switch ( R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ) ) - { - // Ummm... yeah yeah I know we don't really have an md3 here.. but we pretend - // we do. After all, the purpose of md4s are not that different, are they? - - case CULL_OUT: - tr.pc.c_sphere_cull_md3_out++; - return CULL_OUT; - - case CULL_IN: - tr.pc.c_sphere_cull_md3_in++; - return CULL_IN; - - case CULL_CLIP: - tr.pc.c_sphere_cull_md3_clip++; - break; - } - } - else - { - int sphereCull, sphereCullB; - - sphereCull = R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ); - if ( newFrame == oldFrame ) { - sphereCullB = sphereCull; - } else { - sphereCullB = R_CullLocalPointAndRadius( oldFrame->localOrigin, oldFrame->radius ); - } - - if ( sphereCull == sphereCullB ) - { - if ( sphereCull == CULL_OUT ) - { - tr.pc.c_sphere_cull_md3_out++; - return CULL_OUT; - } - else if ( sphereCull == CULL_IN ) - { - tr.pc.c_sphere_cull_md3_in++; - return CULL_IN; - } - else - { - tr.pc.c_sphere_cull_md3_clip++; - } - } - } - } - - // calculate a bounding box in the current coordinate system - for (i = 0 ; i < 3 ; i++) { - bounds[0][i] = oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i]; - bounds[1][i] = oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i]; - } - - switch ( R_CullLocalBox( bounds ) ) - { - case CULL_IN: - tr.pc.c_box_cull_md3_in++; - return CULL_IN; - case CULL_CLIP: - tr.pc.c_box_cull_md3_clip++; - return CULL_CLIP; - case CULL_OUT: - default: - tr.pc.c_box_cull_md3_out++; - return CULL_OUT; - } -} - -/* -================= -R_MDRComputeFogNum - -================= -*/ - -int R_MDRComputeFogNum( mdrHeader_t *header, trRefEntity_t *ent ) { - int i, j; - fog_t *fog; - mdrFrame_t *mdrFrame; - vec3_t localOrigin; - int frameSize; - - if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { - return 0; - } - - frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] ); - - // FIXME: non-normalized axis issues - mdrFrame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.frame); - VectorAdd( ent->e.origin, mdrFrame->localOrigin, localOrigin ); - for ( i = 1 ; i < tr.world->numfogs ; i++ ) { - fog = &tr.world->fogs[i]; - for ( j = 0 ; j < 3 ; j++ ) { - if ( localOrigin[j] - mdrFrame->radius >= fog->bounds[1][j] ) { - break; - } - if ( localOrigin[j] + mdrFrame->radius <= fog->bounds[0][j] ) { - break; - } - } - if ( j == 3 ) { - return i; - } - } - - return 0; -} - - -/* -============== -R_MDRAddAnimSurfaces -============== -*/ - -// much stuff in there is just copied from R_AddMd3Surfaces in tr_mesh.c - -void R_MDRAddAnimSurfaces( trRefEntity_t *ent ) { - mdrHeader_t *header; - mdrSurface_t *surface; - mdrLOD_t *lod; - shader_t *shader; - skin_t *skin; - int i, j; - int lodnum = 0; - int fogNum = 0; - int cull; - qboolean personalModel; - - header = (mdrHeader_t *) tr.currentModel->modelData; - - personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal; - - if ( ent->e.renderfx & RF_WRAP_FRAMES ) - { - ent->e.frame %= header->numFrames; - ent->e.oldframe %= header->numFrames; - } - - // - // Validate the frames so there is no chance of a crash. - // This will write directly into the entity structure, so - // when the surfaces are rendered, they don't need to be - // range checked again. - // - if ((ent->e.frame >= header->numFrames) - || (ent->e.frame < 0) - || (ent->e.oldframe >= header->numFrames) - || (ent->e.oldframe < 0) ) - { - ri.Printf( PRINT_DEVELOPER, "R_MDRAddAnimSurfaces: no such frame %d to %d for '%s'\n", - ent->e.oldframe, ent->e.frame, tr.currentModel->name ); - ent->e.frame = 0; - ent->e.oldframe = 0; - } - - // - // cull the entire model if merged bounding box of both frames - // is outside the view frustum. - // - cull = R_MDRCullModel (header, ent); - if ( cull == CULL_OUT ) { - return; - } - - // figure out the current LOD of the model we're rendering, and set the lod pointer respectively. - lodnum = R_ComputeLOD(ent); - // check whether this model has as that many LODs at all. If not, try the closest thing we got. - if(header->numLODs <= 0) - return; - if(header->numLODs <= lodnum) - lodnum = header->numLODs - 1; - - lod = (mdrLOD_t *)( (byte *)header + header->ofsLODs); - for(i = 0; i < lodnum; i++) - { - lod = (mdrLOD_t *) ((byte *) lod + lod->ofsEnd); - } - - // set up lighting - if ( !personalModel || r_shadows->integer > 1 ) - { - R_SetupEntityLighting( &tr.refdef, ent ); - } - - // fogNum? - fogNum = R_MDRComputeFogNum( header, ent ); - - surface = (mdrSurface_t *)( (byte *)lod + lod->ofsSurfaces ); - - for ( i = 0 ; i < lod->numSurfaces ; i++ ) - { - - if(ent->e.customShader) - shader = R_GetShaderByHandle(ent->e.customShader); - else if(ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins) - { - skin = R_GetSkinByHandle(ent->e.customSkin); - shader = tr.defaultShader; - - for(j = 0; j < skin->numSurfaces; j++) - { - if (!strcmp(skin->surfaces[j]->name, surface->name)) - { - shader = skin->surfaces[j]->shader; - break; - } - } - } - else if(surface->shaderIndex > 0) - shader = R_GetShaderByHandle( surface->shaderIndex ); - else - shader = tr.defaultShader; - - // we will add shadows even if the main object isn't visible in the view - - // stencil shadows can't do personal models unless I polyhedron clip - if ( !personalModel - && r_shadows->integer == 2 - && fogNum == 0 - && !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) - && shader->sort == SS_OPAQUE ) - { - R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, qfalse, qfalse ); - } - - // projection shadows work fine with personal models - if ( r_shadows->integer == 3 - && fogNum == 0 - && (ent->e.renderfx & RF_SHADOW_PLANE ) - && shader->sort == SS_OPAQUE ) - { - R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, qfalse, qfalse ); - } - - if (!personalModel) - R_AddDrawSurf( (void *)surface, shader, fogNum, qfalse, qfalse ); - - surface = (mdrSurface_t *)( (byte *)surface + surface->ofsEnd ); - } -} - -/* -============== -RB_MDRSurfaceAnim -============== -*/ -void RB_MDRSurfaceAnim( md4Surface_t *surface ) -{ - int i, j, k; - float frontlerp, backlerp; - int *triangles; - int indexes; - int baseIndex, baseVertex; - int numVerts; - mdrVertex_t *v; - mdrHeader_t *header; - mdrFrame_t *frame; - mdrFrame_t *oldFrame; - mdrBone_t bones[MD4_MAX_BONES], *bonePtr, *bone; - - int frameSize; - - // don't lerp if lerping off, or this is the only frame, or the last frame... - // - if (backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame) - { - backlerp = 0; // if backlerp is 0, lerping is off and frontlerp is never used - frontlerp = 1; - } - else - { - backlerp = backEnd.currentEntity->e.backlerp; - frontlerp = 1.0f - backlerp; - } - - header = (mdrHeader_t *)((byte *)surface + surface->ofsHeader); - - frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] ); - - frame = (mdrFrame_t *)((byte *)header + header->ofsFrames + - backEnd.currentEntity->e.frame * frameSize ); - oldFrame = (mdrFrame_t *)((byte *)header + header->ofsFrames + - backEnd.currentEntity->e.oldframe * frameSize ); - - RB_CheckOverflow( surface->numVerts, surface->numTriangles ); - - triangles = (int *) ((byte *)surface + surface->ofsTriangles); - indexes = surface->numTriangles * 3; - baseIndex = tess.numIndexes; - baseVertex = tess.numVertexes; - - // Set up all triangles. - for (j = 0 ; j < indexes ; j++) - { - tess.indexes[baseIndex + j] = baseVertex + triangles[j]; - } - tess.numIndexes += indexes; - - // - // lerp all the needed bones - // - if ( !backlerp ) - { - // no lerping needed - bonePtr = frame->bones; - } - else - { - bonePtr = bones; - - for ( i = 0 ; i < header->numBones*12 ; i++ ) - { - ((float *)bonePtr)[i] = frontlerp * ((float *)frame->bones)[i] + backlerp * ((float *)oldFrame->bones)[i]; - } - } - - // - // deform the vertexes by the lerped bones - // - numVerts = surface->numVerts; - v = (mdrVertex_t *) ((byte *)surface + surface->ofsVerts); - for ( j = 0; j < numVerts; j++ ) - { - vec3_t tempVert, tempNormal; - mdrWeight_t *w; - - VectorClear( tempVert ); - VectorClear( tempNormal ); - w = v->weights; - for ( k = 0 ; k < v->numWeights ; k++, w++ ) - { - bone = bonePtr + w->boneIndex; - - tempVert[0] += w->boneWeight * ( DotProduct( bone->matrix[0], w->offset ) + bone->matrix[0][3] ); - tempVert[1] += w->boneWeight * ( DotProduct( bone->matrix[1], w->offset ) + bone->matrix[1][3] ); - tempVert[2] += w->boneWeight * ( DotProduct( bone->matrix[2], w->offset ) + bone->matrix[2][3] ); - - tempNormal[0] += w->boneWeight * DotProduct( bone->matrix[0], v->normal ); - tempNormal[1] += w->boneWeight * DotProduct( bone->matrix[1], v->normal ); - tempNormal[2] += w->boneWeight * DotProduct( bone->matrix[2], v->normal ); - } - - tess.xyz[baseVertex + j][0] = tempVert[0]; - tess.xyz[baseVertex + j][1] = tempVert[1]; - tess.xyz[baseVertex + j][2] = tempVert[2]; - - tess.normal[baseVertex + j][0] = tempNormal[0]; - tess.normal[baseVertex + j][1] = tempNormal[1]; - tess.normal[baseVertex + j][2] = tempNormal[2]; - - tess.texCoords[baseVertex + j][0][0] = v->texCoords[0]; - tess.texCoords[baseVertex + j][0][1] = v->texCoords[1]; - - v = (mdrVertex_t *)&v->weights[v->numWeights]; - } - - tess.numVertexes += surface->numVerts; -} - - -#define MC_MASK_X ((1<<(MC_BITS_X))-1) -#define MC_MASK_Y ((1<<(MC_BITS_Y))-1) -#define MC_MASK_Z ((1<<(MC_BITS_Z))-1) -#define MC_MASK_VECT ((1<<(MC_BITS_VECT))-1) - -#define MC_SCALE_VECT (1.0f/(float)((1<<(MC_BITS_VECT-1))-2)) - -#define MC_POS_X (0) -#define MC_SHIFT_X (0) - -#define MC_POS_Y ((((MC_BITS_X))/8)) -#define MC_SHIFT_Y ((((MC_BITS_X)%8))) - -#define MC_POS_Z ((((MC_BITS_X+MC_BITS_Y))/8)) -#define MC_SHIFT_Z ((((MC_BITS_X+MC_BITS_Y)%8))) - -#define MC_POS_V11 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z))/8)) -#define MC_SHIFT_V11 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z)%8))) - -#define MC_POS_V12 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT))/8)) -#define MC_SHIFT_V12 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT)%8))) - -#define MC_POS_V13 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*2))/8)) -#define MC_SHIFT_V13 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*2)%8))) - -#define MC_POS_V21 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*3))/8)) -#define MC_SHIFT_V21 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*3)%8))) - -#define MC_POS_V22 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*4))/8)) -#define MC_SHIFT_V22 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*4)%8))) - -#define MC_POS_V23 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*5))/8)) -#define MC_SHIFT_V23 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*5)%8))) - -#define MC_POS_V31 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*6))/8)) -#define MC_SHIFT_V31 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*6)%8))) - -#define MC_POS_V32 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*7))/8)) -#define MC_SHIFT_V32 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*7)%8))) - -#define MC_POS_V33 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*8))/8)) -#define MC_SHIFT_V33 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*8)%8))) - -void MC_UnCompress(float mat[3][4],const unsigned char * comp) -{ - int val; - - val=(int)((unsigned short *)(comp))[0]; - val-=1<<(MC_BITS_X-1); - mat[0][3]=((float)(val))*MC_SCALE_X; - - val=(int)((unsigned short *)(comp))[1]; - val-=1<<(MC_BITS_Y-1); - mat[1][3]=((float)(val))*MC_SCALE_Y; - - val=(int)((unsigned short *)(comp))[2]; - val-=1<<(MC_BITS_Z-1); - mat[2][3]=((float)(val))*MC_SCALE_Z; - - val=(int)((unsigned short *)(comp))[3]; - val-=1<<(MC_BITS_VECT-1); - mat[0][0]=((float)(val))*MC_SCALE_VECT; - - val=(int)((unsigned short *)(comp))[4]; - val-=1<<(MC_BITS_VECT-1); - mat[0][1]=((float)(val))*MC_SCALE_VECT; - - val=(int)((unsigned short *)(comp))[5]; - val-=1<<(MC_BITS_VECT-1); - mat[0][2]=((float)(val))*MC_SCALE_VECT; - - - val=(int)((unsigned short *)(comp))[6]; - val-=1<<(MC_BITS_VECT-1); - mat[1][0]=((float)(val))*MC_SCALE_VECT; - - val=(int)((unsigned short *)(comp))[7]; - val-=1<<(MC_BITS_VECT-1); - mat[1][1]=((float)(val))*MC_SCALE_VECT; - - val=(int)((unsigned short *)(comp))[8]; - val-=1<<(MC_BITS_VECT-1); - mat[1][2]=((float)(val))*MC_SCALE_VECT; - - - val=(int)((unsigned short *)(comp))[9]; - val-=1<<(MC_BITS_VECT-1); - mat[2][0]=((float)(val))*MC_SCALE_VECT; - - val=(int)((unsigned short *)(comp))[10]; - val-=1<<(MC_BITS_VECT-1); - mat[2][1]=((float)(val))*MC_SCALE_VECT; - - val=(int)((unsigned short *)(comp))[11]; - val-=1<<(MC_BITS_VECT-1); - mat[2][2]=((float)(val))*MC_SCALE_VECT; -} -#endif diff --git a/src/rend2/tr_backend.c b/src/rend2/tr_backend.c deleted file mode 100644 index fd61ee76..00000000 --- a/src/rend2/tr_backend.c +++ /dev/null @@ -1,1843 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#include "tr_local.h" - -backEndData_t *backEndData; -backEndState_t backEnd; - - -static float s_flipMatrix[16] = { - // convert from our coordinate system (looking down X) - // to OpenGL's coordinate system (looking down -Z) - 0, 0, -1, 0, - -1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 0, 1 -}; - - -/* -** GL_Bind2 -*/ -void GL_Bind2( image_t *image, GLenum type ) { - int texnum; - - if ( !image ) { - ri.Printf( PRINT_WARNING, "GL_Bind2: NULL image\n" ); - texnum = tr.defaultImage->texnum; - } else { - texnum = image->texnum; - } - - if ( r_nobind->integer && tr.dlightImage ) { // performance evaluation option - texnum = tr.dlightImage->texnum; - } - - if ( glState.currenttextures[glState.currenttmu] != texnum ) { - image->frameUsed = tr.frameCount; - glState.currenttextures[glState.currenttmu] = texnum; - qglBindTexture (type, texnum); - } -} - -/* -** GL_Bind2 -*/ -void GL_Bind( image_t *image ) -{ - GL_Bind2( image, GL_TEXTURE_2D ); -} - -/* -** GL_BindCubemap -*/ -void GL_BindCubemap( image_t *image ) -{ - GL_Bind2( image, GL_TEXTURE_CUBE_MAP ); -} - -/* -** GL_SelectTexture -*/ -void GL_SelectTexture( int unit ) -{ - if ( glState.currenttmu == unit ) - { - return; - } - - if (!(unit >= 0 && unit <= 31)) - ri.Error( ERR_DROP, "GL_SelectTexture: unit = %i", unit ); - - qglActiveTextureARB( GL_TEXTURE0_ARB + unit ); - - glState.currenttmu = unit; -} - - -/* -** GL_BindMultitexture -*/ -void GL_BindMultitexture( image_t *image0, GLuint env0, image_t *image1, GLuint env1 ) { - int texnum0, texnum1; - - texnum0 = image0->texnum; - texnum1 = image1->texnum; - - if ( r_nobind->integer && tr.dlightImage ) { // performance evaluation option - texnum0 = texnum1 = tr.dlightImage->texnum; - } - - if ( glState.currenttextures[1] != texnum1 ) { - GL_SelectTexture( 1 ); - image1->frameUsed = tr.frameCount; - glState.currenttextures[1] = texnum1; - qglBindTexture( GL_TEXTURE_2D, texnum1 ); - } - if ( glState.currenttextures[0] != texnum0 ) { - GL_SelectTexture( 0 ); - image0->frameUsed = tr.frameCount; - glState.currenttextures[0] = texnum0; - qglBindTexture( GL_TEXTURE_2D, texnum0 ); - } -} - -/* -** GL_BindToTMU -*/ -void GL_BindToTMU( image_t *image, int tmu ) -{ - int texnum; - int oldtmu = glState.currenttmu; - - if (!image) - texnum = 0; - else - texnum = image->texnum; - - if ( glState.currenttextures[tmu] != texnum ) { - GL_SelectTexture( tmu ); - if (image) - image->frameUsed = tr.frameCount; - glState.currenttextures[tmu] = texnum; - qglBindTexture( GL_TEXTURE_2D, texnum ); - GL_SelectTexture( oldtmu ); - } -} - - -/* -** GL_Cull -*/ -void GL_Cull( int cullType ) { - if ( glState.faceCulling == cullType ) { - return; - } - - glState.faceCulling = cullType; - - if ( cullType == CT_TWO_SIDED ) - { - qglDisable( GL_CULL_FACE ); - } - else - { - qboolean cullFront; - qglEnable( GL_CULL_FACE ); - - cullFront = (cullType == CT_FRONT_SIDED); - if ( backEnd.viewParms.isMirror ) - { - cullFront = !cullFront; - } - - if ( backEnd.currentEntity && backEnd.currentEntity->mirrored ) - { - cullFront = !cullFront; - } - - qglCullFace( cullFront ? GL_FRONT : GL_BACK ); - } -} - -/* -** GL_TexEnv -*/ -void GL_TexEnv( int env ) -{ - if ( env == glState.texEnv[glState.currenttmu] ) - { - return; - } - - glState.texEnv[glState.currenttmu] = env; - - - switch ( env ) - { - case GL_MODULATE: - qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); - break; - case GL_REPLACE: - qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); - break; - case GL_DECAL: - qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL ); - break; - case GL_ADD: - qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD ); - break; - default: - ri.Error( ERR_DROP, "GL_TexEnv: invalid env '%d' passed", env ); - break; - } -} - -/* -** GL_State -** -** This routine is responsible for setting the most commonly changed state -** in Q3. -*/ -void GL_State( unsigned long stateBits ) -{ - unsigned long diff = stateBits ^ glState.glStateBits; - - if ( !diff ) - { - return; - } - - // - // check depthFunc bits - // - if ( diff & GLS_DEPTHFUNC_BITS ) - { - if ( stateBits & GLS_DEPTHFUNC_EQUAL ) - { - qglDepthFunc( GL_EQUAL ); - } - else if ( stateBits & GLS_DEPTHFUNC_GREATER) - { - qglDepthFunc( GL_GREATER ); - } - else - { - qglDepthFunc( GL_LEQUAL ); - } - } - - // - // check blend bits - // - if ( diff & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) - { - GLenum srcFactor, dstFactor; - - if ( stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) - { - switch ( stateBits & GLS_SRCBLEND_BITS ) - { - case GLS_SRCBLEND_ZERO: - srcFactor = GL_ZERO; - break; - case GLS_SRCBLEND_ONE: - srcFactor = GL_ONE; - break; - case GLS_SRCBLEND_DST_COLOR: - srcFactor = GL_DST_COLOR; - break; - case GLS_SRCBLEND_ONE_MINUS_DST_COLOR: - srcFactor = GL_ONE_MINUS_DST_COLOR; - break; - case GLS_SRCBLEND_SRC_ALPHA: - srcFactor = GL_SRC_ALPHA; - break; - case GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA: - srcFactor = GL_ONE_MINUS_SRC_ALPHA; - break; - case GLS_SRCBLEND_DST_ALPHA: - srcFactor = GL_DST_ALPHA; - break; - case GLS_SRCBLEND_ONE_MINUS_DST_ALPHA: - srcFactor = GL_ONE_MINUS_DST_ALPHA; - break; - case GLS_SRCBLEND_ALPHA_SATURATE: - srcFactor = GL_SRC_ALPHA_SATURATE; - break; - default: - srcFactor = GL_ONE; // to get warning to shut up - ri.Error( ERR_DROP, "GL_State: invalid src blend state bits" ); - break; - } - - switch ( stateBits & GLS_DSTBLEND_BITS ) - { - case GLS_DSTBLEND_ZERO: - dstFactor = GL_ZERO; - break; - case GLS_DSTBLEND_ONE: - dstFactor = GL_ONE; - break; - case GLS_DSTBLEND_SRC_COLOR: - dstFactor = GL_SRC_COLOR; - break; - case GLS_DSTBLEND_ONE_MINUS_SRC_COLOR: - dstFactor = GL_ONE_MINUS_SRC_COLOR; - break; - case GLS_DSTBLEND_SRC_ALPHA: - dstFactor = GL_SRC_ALPHA; - break; - case GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA: - dstFactor = GL_ONE_MINUS_SRC_ALPHA; - break; - case GLS_DSTBLEND_DST_ALPHA: - dstFactor = GL_DST_ALPHA; - break; - case GLS_DSTBLEND_ONE_MINUS_DST_ALPHA: - dstFactor = GL_ONE_MINUS_DST_ALPHA; - break; - default: - dstFactor = GL_ONE; // to get warning to shut up - ri.Error( ERR_DROP, "GL_State: invalid dst blend state bits" ); - break; - } - - qglEnable( GL_BLEND ); - qglBlendFunc( srcFactor, dstFactor ); - } - else - { - qglDisable( GL_BLEND ); - } - } - - // - // check depthmask - // - if ( diff & GLS_DEPTHMASK_TRUE ) - { - if ( stateBits & GLS_DEPTHMASK_TRUE ) - { - qglDepthMask( GL_TRUE ); - } - else - { - qglDepthMask( GL_FALSE ); - } - } - - // - // fill/line mode - // - if ( diff & GLS_POLYMODE_LINE ) - { - if ( stateBits & GLS_POLYMODE_LINE ) - { - qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); - } - else - { - qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); - } - } - - // - // depthtest - // - if ( diff & GLS_DEPTHTEST_DISABLE ) - { - if ( stateBits & GLS_DEPTHTEST_DISABLE ) - { - qglDisable( GL_DEPTH_TEST ); - } - else - { - qglEnable( GL_DEPTH_TEST ); - } - } - - // - // alpha test - // - if ( diff & GLS_ATEST_BITS ) - { - switch ( stateBits & GLS_ATEST_BITS ) - { - case 0: - qglDisable( GL_ALPHA_TEST ); - break; - case GLS_ATEST_GT_0: - qglEnable( GL_ALPHA_TEST ); - qglAlphaFunc( GL_GREATER, 0.0f ); - break; - case GLS_ATEST_LT_80: - qglEnable( GL_ALPHA_TEST ); - qglAlphaFunc( GL_LESS, 0.5f ); - break; - case GLS_ATEST_GE_80: - qglEnable( GL_ALPHA_TEST ); - qglAlphaFunc( GL_GEQUAL, 0.5f ); - break; - default: - assert( 0 ); - break; - } - } - - glState.glStateBits = stateBits; -} - - -void GL_SetProjectionMatrix(matrix_t matrix) -{ - Matrix16Copy(matrix, glState.projection); - Matrix16Multiply(glState.projection, glState.modelview, glState.modelviewProjection); -} - - -void GL_SetModelviewMatrix(matrix_t matrix) -{ - Matrix16Copy(matrix, glState.modelview); - Matrix16Multiply(glState.projection, glState.modelview, glState.modelviewProjection); -} - - -/* -================ -RB_Hyperspace - -A player has predicted a teleport, but hasn't arrived yet -================ -*/ -static void RB_Hyperspace( void ) { - float c; - - if ( !backEnd.isHyperspace ) { - // do initialization shit - } - - c = ( backEnd.refdef.time & 255 ) / 255.0f; - qglClearColor( c, c, c, 1 ); - qglClear( GL_COLOR_BUFFER_BIT ); - - backEnd.isHyperspace = qtrue; -} - - -static void SetViewportAndScissor( void ) { - GL_SetProjectionMatrix( backEnd.viewParms.projectionMatrix ); - - // set the window clipping - qglViewport( backEnd.viewParms.viewportX, backEnd.viewParms.viewportY, - backEnd.viewParms.viewportWidth, backEnd.viewParms.viewportHeight ); - qglScissor( backEnd.viewParms.viewportX, backEnd.viewParms.viewportY, - backEnd.viewParms.viewportWidth, backEnd.viewParms.viewportHeight ); -} - -/* -================= -RB_BeginDrawingView - -Any mirrored or portaled views have already been drawn, so prepare -to actually render the visible surfaces for this view -================= -*/ -void RB_BeginDrawingView (void) { - int clearBits = 0; - - // sync with gl if needed - if ( r_finish->integer == 1 && !glState.finishCalled ) { - qglFinish (); - glState.finishCalled = qtrue; - } - if ( r_finish->integer == 0 ) { - glState.finishCalled = qtrue; - } - - // we will need to change the projection matrix before drawing - // 2D images again - backEnd.projection2D = qfalse; - - if (glRefConfig.framebufferObject) - { - // FIXME: HUGE HACK: render to the screen fbo if we've already postprocessed the frame and aren't drawing more world - // drawing more world check is in case of double renders, such as skyportals - if (backEnd.viewParms.targetFbo == NULL) - { - if (!tr.renderFbo || (backEnd.framePostProcessed && (backEnd.refdef.rdflags & RDF_NOWORLDMODEL))) - { - FBO_Bind(tr.screenScratchFbo); - } - else - { - FBO_Bind(tr.renderFbo); - } - } - else - { - FBO_Bind(backEnd.viewParms.targetFbo); - } - } - - // - // set the modelview matrix for the viewer - // - SetViewportAndScissor(); - - // ensures that depth writes are enabled for the depth clear - GL_State( GLS_DEFAULT ); - // clear relevant buffers - clearBits = GL_DEPTH_BUFFER_BIT; - - if ( r_measureOverdraw->integer || r_shadows->integer == 2 ) - { - clearBits |= GL_STENCIL_BUFFER_BIT; - } - if ( r_fastsky->integer && !( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) ) - { - clearBits |= GL_COLOR_BUFFER_BIT; // FIXME: only if sky shaders have been used -#ifdef _DEBUG - qglClearColor( 0.8f, 0.7f, 0.4f, 1.0f ); // FIXME: get color of sky -#else - qglClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); // FIXME: get color of sky -#endif - } - - // clear to white for shadow maps - if (backEnd.viewParms.flags & VPF_SHADOWMAP) - { - clearBits |= GL_COLOR_BUFFER_BIT; - qglClearColor( 1.0f, 1.0f, 1.0f, 1.0f ); - } - - qglClear( clearBits ); - - if ( ( backEnd.refdef.rdflags & RDF_HYPERSPACE ) ) - { - RB_Hyperspace(); - return; - } - else - { - backEnd.isHyperspace = qfalse; - } - - glState.faceCulling = -1; // force face culling to set next time - - // we will only draw a sun if there was sky rendered in this view - backEnd.skyRenderedThisView = qfalse; - - // clip to the plane of the portal - if ( backEnd.viewParms.isPortal ) { -#if 0 - float plane[4]; - double plane2[4]; - - plane[0] = backEnd.viewParms.portalPlane.normal[0]; - plane[1] = backEnd.viewParms.portalPlane.normal[1]; - plane[2] = backEnd.viewParms.portalPlane.normal[2]; - plane[3] = backEnd.viewParms.portalPlane.dist; - - plane2[0] = DotProduct (backEnd.viewParms.or.axis[0], plane); - plane2[1] = DotProduct (backEnd.viewParms.or.axis[1], plane); - plane2[2] = DotProduct (backEnd.viewParms.or.axis[2], plane); - plane2[3] = DotProduct (plane, backEnd.viewParms.or.origin) - plane[3]; -#endif - GL_SetModelviewMatrix( s_flipMatrix ); - } -} - - -#define MAC_EVENT_PUMP_MSEC 5 - -/* -================== -RB_RenderDrawSurfList -================== -*/ -void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { - shader_t *shader, *oldShader; - int fogNum, oldFogNum; - int entityNum, oldEntityNum; - int dlighted, oldDlighted; - int pshadowed, oldPshadowed; - qboolean depthRange, oldDepthRange, isCrosshair, wasCrosshair; - int i; - drawSurf_t *drawSurf; - int oldSort; - float originalTime; - FBO_t* fbo = NULL; - qboolean inQuery = qfalse; - - float depth[2]; - - - // save original time for entity shader offsets - originalTime = backEnd.refdef.floatTime; - - fbo = glState.currentFBO; - - // draw everything - oldEntityNum = -1; - backEnd.currentEntity = &tr.worldEntity; - oldShader = NULL; - oldFogNum = -1; - oldDepthRange = qfalse; - wasCrosshair = qfalse; - oldDlighted = qfalse; - oldPshadowed = qfalse; - oldSort = -1; - depthRange = qfalse; - - depth[0] = 0.f; - depth[1] = 1.f; - - backEnd.pc.c_surfaces += numDrawSurfs; - - for (i = 0, drawSurf = drawSurfs ; i < numDrawSurfs ; i++, drawSurf++) { - if ( drawSurf->sort == oldSort ) { - if (backEnd.depthFill && shader && shader->sort != SS_OPAQUE) - continue; - - // fast path, same as previous sort - rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface ); - continue; - } - oldSort = drawSurf->sort; - R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted, &pshadowed ); - - // - // change the tess parameters if needed - // a "entityMergable" shader is a shader that can have surfaces from seperate - // entities merged into a single batch, like smoke and blood puff sprites - if (shader != oldShader || fogNum != oldFogNum || dlighted != oldDlighted || pshadowed != oldPshadowed - || ( entityNum != oldEntityNum && !shader->entityMergable ) ) { - if (oldShader != NULL) { - RB_EndSurface(); - } - RB_BeginSurface( shader, fogNum ); - backEnd.pc.c_surfBatches++; - oldShader = shader; - oldFogNum = fogNum; - oldDlighted = dlighted; - oldPshadowed = pshadowed; - } - - if (backEnd.depthFill && shader && shader->sort != SS_OPAQUE) - continue; - - // - // change the modelview matrix if needed - // - if ( entityNum != oldEntityNum ) { - qboolean sunflare = qfalse; - depthRange = isCrosshair = qfalse; - - if ( entityNum != REFENTITYNUM_WORLD ) { - backEnd.currentEntity = &backEnd.refdef.entities[entityNum]; - backEnd.refdef.floatTime = originalTime - backEnd.currentEntity->e.shaderTime; - // we have to reset the shaderTime as well otherwise image animations start - // from the wrong frame - tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; - - // set up the transformation matrix - R_RotateForEntity( backEnd.currentEntity, &backEnd.viewParms, &backEnd.or ); - - // set up the dynamic lighting if needed - if ( backEnd.currentEntity->needDlights ) { - R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.or ); - } - - if(backEnd.currentEntity->e.renderfx & RF_DEPTHHACK) - { - // hack the depth range to prevent view model from poking into walls - depthRange = qtrue; - - if(backEnd.currentEntity->e.renderfx & RF_CROSSHAIR) - isCrosshair = qtrue; - } - } else { - backEnd.currentEntity = &tr.worldEntity; - backEnd.refdef.floatTime = originalTime; - backEnd.or = backEnd.viewParms.world; - // we have to reset the shaderTime as well otherwise image animations on - // the world (like water) continue with the wrong frame - tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; - R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.or ); - } - - GL_SetModelviewMatrix( backEnd.or.modelMatrix ); - - // - // change depthrange. Also change projection matrix so first person weapon does not look like coming - // out of the screen. - // - if (oldDepthRange != depthRange || wasCrosshair != isCrosshair) - { - if (depthRange) - { - if(backEnd.viewParms.stereoFrame != STEREO_CENTER) - { - if(isCrosshair) - { - if(oldDepthRange) - { - // was not a crosshair but now is, change back proj matrix - GL_SetProjectionMatrix( backEnd.viewParms.projectionMatrix ); - } - } - else - { - viewParms_t temp = backEnd.viewParms; - - R_SetupProjection(&temp, r_znear->value, 0, qfalse); - - GL_SetProjectionMatrix( temp.projectionMatrix ); - } - } - - if(!oldDepthRange) - { - depth[0] = 0; - depth[1] = 0.3f; - qglDepthRange (depth[0], depth[1]); - } - } - else - { - if(!wasCrosshair && backEnd.viewParms.stereoFrame != STEREO_CENTER) - { - GL_SetProjectionMatrix( backEnd.viewParms.projectionMatrix ); - } - - if (!sunflare) - qglDepthRange (0, 1); - - depth[0] = 0; - depth[1] = 1; - } - - oldDepthRange = depthRange; - wasCrosshair = isCrosshair; - } - - oldEntityNum = entityNum; - } - - // add the triangles for this surface - rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface ); - } - - backEnd.refdef.floatTime = originalTime; - - // draw the contents of the last shader batch - if (oldShader != NULL) { - RB_EndSurface(); - } - - if (inQuery) { - inQuery = qfalse; - qglEndQueryARB(GL_SAMPLES_PASSED_ARB); - } - - if (glRefConfig.framebufferObject) - FBO_Bind(fbo); - - // go back to the world modelview matrix - - GL_SetModelviewMatrix( backEnd.viewParms.world.modelMatrix ); - - qglDepthRange (0, 1); -} - - -/* -============================================================================ - -RENDER BACK END FUNCTIONS - -============================================================================ -*/ - -/* -================ -RB_SetGL2D - -================ -*/ -void RB_SetGL2D (void) { - matrix_t matrix; - int width, height; - - if (backEnd.projection2D && backEnd.last2DFBO == glState.currentFBO) - return; - - backEnd.projection2D = qtrue; - backEnd.last2DFBO = glState.currentFBO; - - if (glState.currentFBO) - { - width = glState.currentFBO->width; - height = glState.currentFBO->height; - } - else - { - width = glConfig.vidWidth; - height = glConfig.vidHeight; - } - - // set 2D virtual screen size - qglViewport( 0, 0, width, height ); - qglScissor( 0, 0, width, height ); - - Matrix16Ortho(0, width, height, 0, 0, 1, matrix); - GL_SetProjectionMatrix(matrix); - Matrix16Identity(matrix); - GL_SetModelviewMatrix(matrix); - - GL_State( GLS_DEPTHTEST_DISABLE | - GLS_SRCBLEND_SRC_ALPHA | - GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); - - qglDisable( GL_CULL_FACE ); - qglDisable( GL_CLIP_PLANE0 ); - - // set time for 2D shaders - backEnd.refdef.time = ri.Milliseconds(); - backEnd.refdef.floatTime = backEnd.refdef.time * 0.001f; - - // reset color scaling - backEnd.refdef.colorScale = 1.0f; -} - - -/* -============= -RE_StretchRaw - -FIXME: not exactly backend -Stretches a raw 32 bit power of 2 bitmap image over the given screen rectangle. -Used for cinematics. -============= -*/ -void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty) { - int i, j; - int start, end; - shaderProgram_t *sp = &tr.textureColorShader; - vec4_t color; - - if ( !tr.registered ) { - return; - } - R_IssuePendingRenderCommands(); - - // we definately want to sync every frame for the cinematics - qglFinish(); - - start = 0; - if ( r_speeds->integer ) { - start = ri.Milliseconds(); - } - - // make sure rows and cols are powers of 2 - for ( i = 0 ; ( 1 << i ) < cols ; i++ ) { - } - for ( j = 0 ; ( 1 << j ) < rows ; j++ ) { - } - if ( ( 1 << i ) != cols || ( 1 << j ) != rows) { - ri.Error (ERR_DROP, "Draw_StretchRaw: size not a power of 2: %i by %i", cols, rows); - } - - GL_Bind( tr.scratchImage[client] ); - - // if the scratchImage isn't in the format we want, specify it as a new texture - if ( cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height ) { - tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols; - tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows; - qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - } else { - if (dirty) { - // otherwise, just subimage upload it so that drivers can tell we are going to be changing - // it and don't try and do a texture compression - qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data ); - } - } - - if ( r_speeds->integer ) { - end = ri.Milliseconds(); - ri.Printf( PRINT_ALL, "qglTexSubImage2D %i, %i: %i msec\n", cols, rows, end - start ); - } - - // FIXME: HUGE hack - if (glRefConfig.framebufferObject) - { - if (!tr.renderFbo || backEnd.framePostProcessed) - { - FBO_Bind(tr.screenScratchFbo); - } - else - { - FBO_Bind(tr.renderFbo); - } - } - - RB_SetGL2D(); - - tess.numIndexes = 0; - tess.numVertexes = 0; - tess.firstIndex = 0; - tess.minIndex = 0; - tess.maxIndex = 0; - - tess.xyz[tess.numVertexes][0] = x; - tess.xyz[tess.numVertexes][1] = y; - tess.xyz[tess.numVertexes][2] = 0; - tess.xyz[tess.numVertexes][3] = 1; - tess.texCoords[tess.numVertexes][0][0] = 0.5f / cols; - tess.texCoords[tess.numVertexes][0][1] = 0.5f / rows; - tess.texCoords[tess.numVertexes][1][0] = 0; - tess.texCoords[tess.numVertexes][1][1] = 1; - tess.numVertexes++; - - tess.xyz[tess.numVertexes][0] = x + w; - tess.xyz[tess.numVertexes][1] = y; - tess.xyz[tess.numVertexes][2] = 0; - tess.xyz[tess.numVertexes][3] = 1; - tess.texCoords[tess.numVertexes][0][0] = (cols - 0.5f) / cols; - tess.texCoords[tess.numVertexes][0][1] = 0.5f / rows; - tess.texCoords[tess.numVertexes][1][0] = 0; - tess.texCoords[tess.numVertexes][1][1] = 1; - tess.numVertexes++; - - tess.xyz[tess.numVertexes][0] = x + w; - tess.xyz[tess.numVertexes][1] = y + h; - tess.xyz[tess.numVertexes][2] = 0; - tess.xyz[tess.numVertexes][3] = 1; - tess.texCoords[tess.numVertexes][0][0] = (cols - 0.5f) / cols; - tess.texCoords[tess.numVertexes][0][1] = (rows - 0.5f) / rows; - tess.texCoords[tess.numVertexes][1][0] = 0; - tess.texCoords[tess.numVertexes][1][1] = 1; - tess.numVertexes++; - - tess.xyz[tess.numVertexes][0] = x; - tess.xyz[tess.numVertexes][1] = y + h; - tess.xyz[tess.numVertexes][2] = 0; - tess.xyz[tess.numVertexes][3] = 1; - tess.texCoords[tess.numVertexes][0][0] = 0.5f / cols; - tess.texCoords[tess.numVertexes][0][1] = (rows - 0.5f) / rows; - tess.texCoords[tess.numVertexes][1][0] = 0; - tess.texCoords[tess.numVertexes][1][1] = 1; - tess.numVertexes++; - - tess.indexes[tess.numIndexes++] = 0; - tess.indexes[tess.numIndexes++] = 1; - tess.indexes[tess.numIndexes++] = 2; - tess.indexes[tess.numIndexes++] = 0; - tess.indexes[tess.numIndexes++] = 2; - tess.indexes[tess.numIndexes++] = 3; - tess.minIndex = 0; - tess.maxIndex = 3; - - // FIXME: A lot of this can probably be removed for speed, and refactored into a more convenient function - RB_UpdateVBOs(ATTR_POSITION | ATTR_TEXCOORD); - - sp = &tr.textureColorShader; - - GLSL_VertexAttribsState(ATTR_POSITION | ATTR_TEXCOORD); - - GLSL_BindProgram(sp); - - GLSL_SetUniformMatrix16(sp, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); - VectorSet4(color, 1, 1, 1, 1); - GLSL_SetUniformVec4(sp, TEXTURECOLOR_UNIFORM_COLOR, color); - - R_DrawElementsVBO(tess.numIndexes, tess.firstIndex, tess.minIndex, tess.maxIndex); - - //R_BindNullVBO(); - //R_BindNullIBO(); - - tess.numIndexes = 0; - tess.numVertexes = 0; - tess.firstIndex = 0; - tess.minIndex = 0; - tess.maxIndex = 0; -} - -void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty) { - - GL_Bind( tr.scratchImage[client] ); - - // if the scratchImage isn't in the format we want, specify it as a new texture - if ( cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height ) { - tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols; - tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows; - qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - } else { - if (dirty) { - // otherwise, just subimage upload it so that drivers can tell we are going to be changing - // it and don't try and do a texture compression - qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data ); - } - } -} - - -/* -============= -RB_SetColor - -============= -*/ -const void *RB_SetColor( const void *data ) { - const setColorCommand_t *cmd; - - cmd = (const setColorCommand_t *)data; - - backEnd.color2D[0] = cmd->color[0] * 255; - backEnd.color2D[1] = cmd->color[1] * 255; - backEnd.color2D[2] = cmd->color[2] * 255; - backEnd.color2D[3] = cmd->color[3] * 255; - - return (const void *)(cmd + 1); -} - -/* -============= -RB_StretchPic -============= -*/ -const void *RB_StretchPic ( const void *data ) { - const stretchPicCommand_t *cmd; - shader_t *shader; - int numVerts, numIndexes; - - cmd = (const stretchPicCommand_t *)data; - - // FIXME: HUGE hack - if (glRefConfig.framebufferObject) - { - if (!tr.renderFbo || backEnd.framePostProcessed) - { - FBO_Bind(tr.screenScratchFbo); - } - else - { - FBO_Bind(tr.renderFbo); - } - } - - RB_SetGL2D(); - - shader = cmd->shader; - if ( shader != tess.shader ) { - if ( tess.numIndexes ) { - RB_EndSurface(); - } - backEnd.currentEntity = &backEnd.entity2D; - RB_BeginSurface( shader, 0 ); - } - - RB_CHECKOVERFLOW( 4, 6 ); - numVerts = tess.numVertexes; - numIndexes = tess.numIndexes; - - tess.numVertexes += 4; - tess.numIndexes += 6; - - tess.indexes[ numIndexes ] = numVerts + 3; - tess.indexes[ numIndexes + 1 ] = numVerts + 0; - tess.indexes[ numIndexes + 2 ] = numVerts + 2; - tess.indexes[ numIndexes + 3 ] = numVerts + 2; - tess.indexes[ numIndexes + 4 ] = numVerts + 0; - tess.indexes[ numIndexes + 5 ] = numVerts + 1; - - { - vec4_t color; - - VectorScale4(backEnd.color2D, 1.0f / 255.0f, color); - - VectorCopy4(color, tess.vertexColors[ numVerts ]); - VectorCopy4(color, tess.vertexColors[ numVerts + 1]); - VectorCopy4(color, tess.vertexColors[ numVerts + 2]); - VectorCopy4(color, tess.vertexColors[ numVerts + 3 ]); - } - - tess.xyz[ numVerts ][0] = cmd->x; - tess.xyz[ numVerts ][1] = cmd->y; - tess.xyz[ numVerts ][2] = 0; - - tess.texCoords[ numVerts ][0][0] = cmd->s1; - tess.texCoords[ numVerts ][0][1] = cmd->t1; - - tess.xyz[ numVerts + 1 ][0] = cmd->x + cmd->w; - tess.xyz[ numVerts + 1 ][1] = cmd->y; - tess.xyz[ numVerts + 1 ][2] = 0; - - tess.texCoords[ numVerts + 1 ][0][0] = cmd->s2; - tess.texCoords[ numVerts + 1 ][0][1] = cmd->t1; - - tess.xyz[ numVerts + 2 ][0] = cmd->x + cmd->w; - tess.xyz[ numVerts + 2 ][1] = cmd->y + cmd->h; - tess.xyz[ numVerts + 2 ][2] = 0; - - tess.texCoords[ numVerts + 2 ][0][0] = cmd->s2; - tess.texCoords[ numVerts + 2 ][0][1] = cmd->t2; - - tess.xyz[ numVerts + 3 ][0] = cmd->x; - tess.xyz[ numVerts + 3 ][1] = cmd->y + cmd->h; - tess.xyz[ numVerts + 3 ][2] = 0; - - tess.texCoords[ numVerts + 3 ][0][0] = cmd->s1; - tess.texCoords[ numVerts + 3 ][0][1] = cmd->t2; - - return (const void *)(cmd + 1); -} - - -/* -============= -RB_DrawSurfs - -============= -*/ -const void *RB_DrawSurfs( const void *data ) { - const drawSurfsCommand_t *cmd; - - // finish any 2D drawing if needed - if ( tess.numIndexes ) { - RB_EndSurface(); - } - - cmd = (const drawSurfsCommand_t *)data; - - backEnd.refdef = cmd->refdef; - backEnd.viewParms = cmd->viewParms; - - // clear the z buffer, set the modelview, etc - RB_BeginDrawingView (); - - if (glRefConfig.framebufferObject && (backEnd.viewParms.flags & VPF_DEPTHCLAMP) && glRefConfig.depthClamp) - { - qglEnable(GL_DEPTH_CLAMP); - } - - if (glRefConfig.framebufferObject && !(backEnd.refdef.rdflags & RDF_NOWORLDMODEL) && (r_depthPrepass->integer || (backEnd.viewParms.flags & VPF_DEPTHSHADOW))) - { - FBO_t *oldFbo = glState.currentFBO; - - backEnd.depthFill = qtrue; - qglColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - RB_RenderDrawSurfList( cmd->drawSurfs, cmd->numDrawSurfs ); - qglColorMask(!backEnd.colorMask[0], !backEnd.colorMask[1], !backEnd.colorMask[2], !backEnd.colorMask[3]); - backEnd.depthFill = qfalse; - - if (tr.msaaResolveFbo) - { - // If we're using multisampling, resolve the depth first - FBO_FastBlit(tr.renderFbo, NULL, tr.msaaResolveFbo, NULL, GL_DEPTH_BUFFER_BIT, GL_NEAREST); - } - else if (tr.renderFbo == NULL) - { - // If we're rendering directly to the screen, copy the depth to a texture - GL_BindToTMU(tr.renderDepthImage, 0); - qglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, 0, 0, glConfig.vidWidth, glConfig.vidHeight, 0); - } - - if (r_ssao->integer) - { - // need the depth in a texture we can do GL_LINEAR sampling on, so copy it to an HDR image - FBO_BlitFromTexture(tr.renderDepthImage, NULL, NULL, tr.hdrDepthFbo, NULL, NULL, NULL, 0); - } - - if (backEnd.viewParms.flags & VPF_USESUNLIGHT) - { - vec4_t quadVerts[4]; - vec2_t texCoords[4]; - vec4_t box; - - FBO_Bind(tr.screenShadowFbo); - - box[0] = (backEnd.refdef.x ) * tr.screenShadowFbo->width / (float)glConfig.vidWidth; - box[1] = (backEnd.refdef.y ) * tr.screenShadowFbo->height / (float)glConfig.vidHeight; - box[2] = (backEnd.refdef.width ) * tr.screenShadowFbo->width / (float)glConfig.vidWidth; - box[3] = (backEnd.refdef.height) * tr.screenShadowFbo->height / (float)glConfig.vidHeight; - - qglViewport(box[0], box[1], box[2], box[3]); - qglScissor(box[0], box[1], box[2], box[3]); - - box[0] = (backEnd.refdef.x ) / (float)glConfig.vidWidth; - box[1] = (backEnd.refdef.y ) / (float)glConfig.vidHeight; - box[2] = (backEnd.refdef.x + backEnd.refdef.width ) / (float)glConfig.vidWidth; - box[3] = (backEnd.refdef.y + backEnd.refdef.height) / (float)glConfig.vidHeight; - - texCoords[0][0] = box[0]; texCoords[0][1] = box[3]; - texCoords[1][0] = box[2]; texCoords[1][1] = box[3]; - texCoords[2][0] = box[2]; texCoords[2][1] = box[1]; - texCoords[3][0] = box[0]; texCoords[3][1] = box[1]; - - box[0] = -1.0f; - box[1] = -1.0f; - box[2] = 1.0f; - box[3] = 1.0f; - - VectorSet4(quadVerts[0], box[0], box[3], 0, 1); - VectorSet4(quadVerts[1], box[2], box[3], 0, 1); - VectorSet4(quadVerts[2], box[2], box[1], 0, 1); - VectorSet4(quadVerts[3], box[0], box[1], 0, 1); - - GL_State( GLS_DEPTHTEST_DISABLE ); - - GLSL_BindProgram(&tr.shadowmaskShader); - - GL_BindToTMU(tr.renderDepthImage, TB_COLORMAP); - GL_BindToTMU(tr.sunShadowDepthImage[0], TB_SHADOWMAP); - GL_BindToTMU(tr.sunShadowDepthImage[1], TB_SHADOWMAP2); - GL_BindToTMU(tr.sunShadowDepthImage[2], TB_SHADOWMAP3); - - GLSL_SetUniformMatrix16(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMVP, backEnd.refdef.sunShadowMvp[0]); - GLSL_SetUniformMatrix16(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMVP2, backEnd.refdef.sunShadowMvp[1]); - GLSL_SetUniformMatrix16(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMVP3, backEnd.refdef.sunShadowMvp[2]); - - GLSL_SetUniformVec3(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWORIGIN, backEnd.refdef.vieworg); - { - vec4_t viewInfo; - vec3_t viewVector; - - float zmax = backEnd.viewParms.zFar; - float ymax = zmax * tan(backEnd.viewParms.fovY * M_PI / 360.0f); - float xmax = zmax * tan(backEnd.viewParms.fovX * M_PI / 360.0f); - - float zmin = r_znear->value; - - VectorScale(backEnd.refdef.viewaxis[0], zmax, viewVector); - GLSL_SetUniformVec3(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWFORWARD, viewVector); - VectorScale(backEnd.refdef.viewaxis[1], xmax, viewVector); - GLSL_SetUniformVec3(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWLEFT, viewVector); - VectorScale(backEnd.refdef.viewaxis[2], ymax, viewVector); - GLSL_SetUniformVec3(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWUP, viewVector); - - VectorSet4(viewInfo, zmax / zmin, zmax, 0.0, 0.0); - - GLSL_SetUniformVec4(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWINFO, viewInfo); - } - - - RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes); - } - - if (r_ssao->integer) - { - vec4_t quadVerts[4]; - vec2_t texCoords[4]; - - FBO_Bind(tr.quarterFbo[0]); - - qglViewport(0, 0, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height); - qglScissor(0, 0, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height); - - VectorSet4(quadVerts[0], -1, 1, 0, 1); - VectorSet4(quadVerts[1], 1, 1, 0, 1); - VectorSet4(quadVerts[2], 1, -1, 0, 1); - VectorSet4(quadVerts[3], -1, -1, 0, 1); - - texCoords[0][0] = 0; texCoords[0][1] = 1; - texCoords[1][0] = 1; texCoords[1][1] = 1; - texCoords[2][0] = 1; texCoords[2][1] = 0; - texCoords[3][0] = 0; texCoords[3][1] = 0; - - GL_State( GLS_DEPTHTEST_DISABLE ); - - GLSL_BindProgram(&tr.ssaoShader); - - GL_BindToTMU(tr.hdrDepthImage, TB_COLORMAP); - - { - vec4_t viewInfo; - - float zmax = backEnd.viewParms.zFar; - float zmin = r_znear->value; - - VectorSet4(viewInfo, zmax / zmin, zmax, 0.0, 0.0); - - GLSL_SetUniformVec4(&tr.ssaoShader, SSAO_UNIFORM_VIEWINFO, viewInfo); - } - - RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes); - - - FBO_Bind(tr.quarterFbo[1]); - - qglViewport(0, 0, tr.quarterFbo[1]->width, tr.quarterFbo[1]->height); - qglScissor(0, 0, tr.quarterFbo[1]->width, tr.quarterFbo[1]->height); - - GLSL_BindProgram(&tr.depthBlurShader[0]); - - GL_BindToTMU(tr.quarterImage[0], TB_COLORMAP); - GL_BindToTMU(tr.hdrDepthImage, TB_LIGHTMAP); - - { - vec4_t viewInfo; - - float zmax = backEnd.viewParms.zFar; - float zmin = r_znear->value; - - VectorSet4(viewInfo, zmax / zmin, zmax, 0.0, 0.0); - - GLSL_SetUniformVec4(&tr.depthBlurShader[0], DEPTHBLUR_UNIFORM_VIEWINFO, viewInfo); - } - - RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes); - - - FBO_Bind(tr.screenSsaoFbo); - - qglViewport(0, 0, tr.screenSsaoFbo->width, tr.screenSsaoFbo->height); - qglScissor(0, 0, tr.screenSsaoFbo->width, tr.screenSsaoFbo->height); - - GLSL_BindProgram(&tr.depthBlurShader[1]); - - GL_BindToTMU(tr.quarterImage[1], TB_COLORMAP); - GL_BindToTMU(tr.hdrDepthImage, TB_LIGHTMAP); - - { - vec4_t viewInfo; - - float zmax = backEnd.viewParms.zFar; - float zmin = r_znear->value; - - VectorSet4(viewInfo, zmax / zmin, zmax, 0.0, 0.0); - - GLSL_SetUniformVec4(&tr.depthBlurShader[1], DEPTHBLUR_UNIFORM_VIEWINFO, viewInfo); - } - - - RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes); - } - - // reset viewport and scissor - FBO_Bind(oldFbo); - SetViewportAndScissor(); - } - - if (glRefConfig.framebufferObject && (backEnd.viewParms.flags & VPF_DEPTHCLAMP) && glRefConfig.depthClamp) - { - qglDisable(GL_DEPTH_CLAMP); - } - - if (!(backEnd.viewParms.flags & VPF_DEPTHSHADOW)) - { - RB_RenderDrawSurfList( cmd->drawSurfs, cmd->numDrawSurfs ); - - if (r_drawSun->integer) - { - RB_DrawSun(0.1, tr.sunShader); - } - - if (r_drawSunRays->integer) - { - FBO_t *oldFbo = glState.currentFBO; - FBO_Bind(tr.sunRaysFbo); - - qglClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); - qglClear( GL_COLOR_BUFFER_BIT ); - - if (glRefConfig.occlusionQuery) - { - tr.sunFlareQueryActive[tr.sunFlareQueryIndex] = qtrue; - qglBeginQueryARB(GL_SAMPLES_PASSED_ARB, tr.sunFlareQuery[tr.sunFlareQueryIndex]); - } - - RB_DrawSun(0.3, tr.sunFlareShader); - - if (glRefConfig.occlusionQuery) - { - qglEndQueryARB(GL_SAMPLES_PASSED_ARB); - } - - FBO_Bind(oldFbo); - } - - // darken down any stencil shadows - RB_ShadowFinish(); - - // add light flares on lights that aren't obscured - RB_RenderFlares(); - } - - //if (glRefConfig.framebufferObject) - //FBO_Bind(NULL); - - return (const void *)(cmd + 1); -} - - -/* -============= -RB_DrawBuffer - -============= -*/ -const void *RB_DrawBuffer( const void *data ) { - const drawBufferCommand_t *cmd; - - cmd = (const drawBufferCommand_t *)data; - - // finish any 2D drawing if needed - if(tess.numIndexes) - RB_EndSurface(); - - if (glRefConfig.framebufferObject) - FBO_Bind(NULL); - - qglDrawBuffer( cmd->buffer ); - - // clear screen for debugging - if ( r_clear->integer ) { - qglClearColor( 1, 0, 0.5, 1 ); - qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - } - - return (const void *)(cmd + 1); -} - -/* -=============== -RB_ShowImages - -Draw all the images to the screen, on top of whatever -was there. This is used to test for texture thrashing. - -Also called by RE_EndRegistration -=============== -*/ -void RB_ShowImages( void ) { - int i; - image_t *image; - float x, y, w, h; - int start, end; - - RB_SetGL2D(); - - qglClear( GL_COLOR_BUFFER_BIT ); - - qglFinish(); - - start = ri.Milliseconds(); - - for ( i=0 ; iinteger == 2 ) { - w *= image->uploadWidth / 512.0f; - h *= image->uploadHeight / 512.0f; - } - - { - vec4_t quadVerts[4]; - - GL_Bind(image); - - VectorSet4(quadVerts[0], x, y, 0, 1); - VectorSet4(quadVerts[1], x + w, y, 0, 1); - VectorSet4(quadVerts[2], x + w, y + h, 0, 1); - VectorSet4(quadVerts[3], x, y + h, 0, 1); - - RB_InstantQuad(quadVerts); - } - } - - qglFinish(); - - end = ri.Milliseconds(); - ri.Printf( PRINT_ALL, "%i msec to draw all images\n", end - start ); - -} - -/* -============= -RB_ColorMask - -============= -*/ -const void *RB_ColorMask(const void *data) -{ - const colorMaskCommand_t *cmd = data; - - // finish any 2D drawing if needed - if(tess.numIndexes) - RB_EndSurface(); - - if (glRefConfig.framebufferObject) - { - // reverse color mask, so 0 0 0 0 is the default - backEnd.colorMask[0] = !cmd->rgba[0]; - backEnd.colorMask[1] = !cmd->rgba[1]; - backEnd.colorMask[2] = !cmd->rgba[2]; - backEnd.colorMask[3] = !cmd->rgba[3]; - } - - qglColorMask(cmd->rgba[0], cmd->rgba[1], cmd->rgba[2], cmd->rgba[3]); - - return (const void *)(cmd + 1); -} - -/* -============= -RB_ClearDepth - -============= -*/ -const void *RB_ClearDepth(const void *data) -{ - const clearDepthCommand_t *cmd = data; - - // finish any 2D drawing if needed - if(tess.numIndexes) - RB_EndSurface(); - - // texture swapping test - if (r_showImages->integer) - RB_ShowImages(); - - if (glRefConfig.framebufferObject) - { - if (!tr.renderFbo || backEnd.framePostProcessed) - { - FBO_Bind(tr.screenScratchFbo); - } - else - { - FBO_Bind(tr.renderFbo); - } - } - - qglClear(GL_DEPTH_BUFFER_BIT); - - // if we're doing MSAA, clear the depth texture for the resolve buffer - if (tr.msaaResolveFbo) - { - FBO_Bind(tr.msaaResolveFbo); - qglClear(GL_DEPTH_BUFFER_BIT); - } - - - return (const void *)(cmd + 1); -} - -/* -============= -RB_SwapBuffers - -============= -*/ -const void *RB_SwapBuffers( const void *data ) { - const swapBuffersCommand_t *cmd; - - // finish any 2D drawing if needed - if ( tess.numIndexes ) { - RB_EndSurface(); - } - - // texture swapping test - if ( r_showImages->integer ) { - RB_ShowImages(); - } - - cmd = (const swapBuffersCommand_t *)data; - - // we measure overdraw by reading back the stencil buffer and - // counting up the number of increments that have happened - if ( r_measureOverdraw->integer ) { - int i; - long sum = 0; - unsigned char *stencilReadback; - - stencilReadback = ri.Hunk_AllocateTempMemory( glConfig.vidWidth * glConfig.vidHeight ); - qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencilReadback ); - - for ( i = 0; i < glConfig.vidWidth * glConfig.vidHeight; i++ ) { - sum += stencilReadback[i]; - } - - backEnd.pc.c_overDraw += sum; - ri.Hunk_FreeTempMemory( stencilReadback ); - } - - if (glRefConfig.framebufferObject) - { - if (!backEnd.framePostProcessed) - { - if (tr.msaaResolveFbo && r_hdr->integer) - { - // Resolving an RGB16F MSAA FBO to the screen messes with the brightness, so resolve to an RGB16F FBO first - FBO_FastBlit(tr.renderFbo, NULL, tr.msaaResolveFbo, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST); - FBO_FastBlit(tr.msaaResolveFbo, NULL, tr.screenScratchFbo, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST); - } - else if (tr.renderFbo) - { - FBO_FastBlit(tr.renderFbo, NULL, tr.screenScratchFbo, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST); - } - } - - if (tr.screenScratchFbo) - { - vec4_t color; - - color[0] = - color[1] = - color[2] = pow(2, tr.overbrightBits); //exp2(tr.overbrightBits); - color[3] = 1.0f; - - // turn off colormask when copying final image - if (backEnd.colorMask[0] || backEnd.colorMask[1] || backEnd.colorMask[2] || backEnd.colorMask[3]) - qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - FBO_Blit(tr.screenScratchFbo, NULL, NULL, NULL, NULL, NULL, color, 0); - - if (backEnd.colorMask[0] || backEnd.colorMask[1] || backEnd.colorMask[2] || backEnd.colorMask[3]) - qglColorMask(!backEnd.colorMask[0], !backEnd.colorMask[1], !backEnd.colorMask[2], !backEnd.colorMask[3]); - } - } - - if ( !glState.finishCalled ) { - qglFinish(); - } - - GLimp_LogComment( "***************** RB_SwapBuffers *****************\n\n\n" ); - - GLimp_EndFrame(); - - backEnd.framePostProcessed = qfalse; - backEnd.projection2D = qfalse; - - return (const void *)(cmd + 1); -} - -/* -============= -RB_CapShadowMap - -============= -*/ -const void *RB_CapShadowMap(const void *data) -{ - const capShadowmapCommand_t *cmd = data; - - // finish any 2D drawing if needed - if(tess.numIndexes) - RB_EndSurface(); - - if (cmd->map != -1) - { - GL_SelectTexture(0); - if (cmd->cubeSide != -1) - { - GL_BindCubemap(tr.shadowCubemaps[cmd->map]); - qglCopyTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cmd->cubeSide, 0, GL_RGBA8, backEnd.refdef.x, glConfig.vidHeight - ( backEnd.refdef.y + PSHADOW_MAP_SIZE ), PSHADOW_MAP_SIZE, PSHADOW_MAP_SIZE, 0); - } - else - { - GL_Bind(tr.pshadowMaps[cmd->map]); - qglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, backEnd.refdef.x, glConfig.vidHeight - ( backEnd.refdef.y + PSHADOW_MAP_SIZE ), PSHADOW_MAP_SIZE, PSHADOW_MAP_SIZE, 0); - } - } - - return (const void *)(cmd + 1); -} - - - -/* -============= -RB_PostProcess - -============= -*/ -const void *RB_PostProcess(const void *data) -{ - const postProcessCommand_t *cmd = data; - FBO_t *srcFbo; - qboolean autoExposure; - - // finish any 2D drawing if needed - if(tess.numIndexes) - RB_EndSurface(); - - if (!glRefConfig.framebufferObject || !r_postProcess->integer) - { - // do nothing - return (const void *)(cmd + 1); - } - - srcFbo = tr.renderFbo; - if (tr.msaaResolveFbo) - { - // Resolve the MSAA before anything else - FBO_FastBlit(tr.renderFbo, NULL, tr.msaaResolveFbo, NULL, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST); - srcFbo = tr.msaaResolveFbo; - } - - if (r_ssao->integer) - { - FBO_BlitFromTexture(tr.screenSsaoImage, NULL, NULL, srcFbo, NULL, NULL, NULL, GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO); - } - - if (srcFbo) - { - if (r_hdr->integer && (r_toneMap->integer || r_forceToneMap->integer)) - { - autoExposure = r_autoExposure->integer || r_forceAutoExposure->integer; - RB_ToneMap(srcFbo, autoExposure); - } - else if (r_cameraExposure->value == 0.0f) - { - FBO_FastBlit(srcFbo, NULL, tr.screenScratchFbo, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST); - } - else - { - vec4_t color; - - color[0] = - color[1] = - color[2] = pow(2, r_cameraExposure->value); //exp2(r_cameraExposure->value); - color[3] = 1.0f; - - FBO_Blit(srcFbo, NULL, NULL, tr.screenScratchFbo, NULL, NULL, color, 0); - } - } - - if (r_drawSunRays->integer) - RB_SunRays(); - - if (1) - RB_BokehBlur(backEnd.refdef.blurFactor); - else - RB_GaussianBlur(backEnd.refdef.blurFactor); - - if (0) - { - vec4i_t dstBox; - VectorSet4(dstBox, 0, 0, 128, 128); - FBO_BlitFromTexture(tr.sunShadowDepthImage[0], NULL, NULL, tr.screenScratchFbo, dstBox, NULL, NULL, 0); - VectorSet4(dstBox, 128, 0, 128, 128); - FBO_BlitFromTexture(tr.sunShadowDepthImage[1], NULL, NULL, tr.screenScratchFbo, dstBox, NULL, NULL, 0); - VectorSet4(dstBox, 256, 0, 128, 128); - FBO_BlitFromTexture(tr.sunShadowDepthImage[2], NULL, NULL, tr.screenScratchFbo, dstBox, NULL, NULL, 0); - } - - if (0) - { - vec4i_t dstBox; - VectorSet4(dstBox, 256, glConfig.vidHeight - 256, 256, 256); - FBO_BlitFromTexture(tr.renderDepthImage, NULL, NULL, tr.screenScratchFbo, dstBox, NULL, NULL, 0); - VectorSet4(dstBox, 512, glConfig.vidHeight - 256, 256, 256); - FBO_BlitFromTexture(tr.screenShadowImage, NULL, NULL, tr.screenScratchFbo, dstBox, NULL, NULL, 0); - } - - if (0) - { - vec4i_t dstBox; - VectorSet4(dstBox, 256, glConfig.vidHeight - 256, 256, 256); - FBO_BlitFromTexture(tr.sunRaysImage, NULL, NULL, tr.screenScratchFbo, dstBox, NULL, NULL, 0); - } - - backEnd.framePostProcessed = qtrue; - - return (const void *)(cmd + 1); -} - -/* -==================== -RB_ExecuteRenderCommands -==================== -*/ -void RB_ExecuteRenderCommands( const void *data ) { - int t1, t2; - - t1 = ri.Milliseconds (); - - while ( 1 ) { - data = PADP(data, sizeof(void *)); - - switch ( *(const int *)data ) { - case RC_SET_COLOR: - data = RB_SetColor( data ); - break; - case RC_STRETCH_PIC: - data = RB_StretchPic( data ); - break; - case RC_DRAW_SURFS: - data = RB_DrawSurfs( data ); - break; - case RC_DRAW_BUFFER: - data = RB_DrawBuffer( data ); - break; - case RC_SWAP_BUFFERS: - data = RB_SwapBuffers( data ); - break; - case RC_SCREENSHOT: - data = RB_TakeScreenshotCmd( data ); - break; - case RC_VIDEOFRAME: - data = RB_TakeVideoFrameCmd( data ); - break; - case RC_COLORMASK: - data = RB_ColorMask(data); - break; - case RC_CLEARDEPTH: - data = RB_ClearDepth(data); - break; - case RC_CAPSHADOWMAP: - data = RB_CapShadowMap(data); - break; - case RC_POSTPROCESS: - data = RB_PostProcess(data); - break; - case RC_END_OF_LIST: - default: - // finish any 2D drawing if needed - if(tess.numIndexes) - RB_EndSurface(); - - // stop rendering - t2 = ri.Milliseconds (); - backEnd.pc.msec = t2 - t1; - return; - } - } - -} diff --git a/src/rend2/tr_bsp.c b/src/rend2/tr_bsp.c deleted file mode 100644 index c14245b8..00000000 --- a/src/rend2/tr_bsp.c +++ /dev/null @@ -1,3370 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_map.c - -#include "tr_local.h" - -/* - -Loads and prepares a map file for scene rendering. - -A single entry point: - -void RE_LoadWorldMap( const char *name ); - -*/ - -static world_t s_worldData; -static byte *fileBase; - -int c_subdivisions; -int c_gridVerts; - -//=============================================================================== - -static void HSVtoRGB( float h, float s, float v, float rgb[3] ) -{ - int i; - float f; - float p, q, t; - - h *= 5; - - i = floor( h ); - f = h - i; - - p = v * ( 1 - s ); - q = v * ( 1 - s * f ); - t = v * ( 1 - s * ( 1 - f ) ); - - switch ( i ) - { - case 0: - rgb[0] = v; - rgb[1] = t; - rgb[2] = p; - break; - case 1: - rgb[0] = q; - rgb[1] = v; - rgb[2] = p; - break; - case 2: - rgb[0] = p; - rgb[1] = v; - rgb[2] = t; - break; - case 3: - rgb[0] = p; - rgb[1] = q; - rgb[2] = v; - break; - case 4: - rgb[0] = t; - rgb[1] = p; - rgb[2] = v; - break; - case 5: - rgb[0] = v; - rgb[1] = p; - rgb[2] = q; - break; - } -} - -/* -=============== -R_ColorShiftLightingBytes - -=============== -*/ -static void R_ColorShiftLightingBytes( byte in[4], byte out[4] ) { - int shift, r, g, b; - - // shift the color data based on overbright range - shift = r_mapOverBrightBits->integer - tr.overbrightBits; - - // shift the data based on overbright range - r = in[0] << shift; - g = in[1] << shift; - b = in[2] << shift; - - // normalize by color instead of saturating to white - if ( ( r | g | b ) > 255 ) { - int max; - - max = r > g ? r : g; - max = max > b ? max : b; - r = r * 255 / max; - g = g * 255 / max; - b = b * 255 / max; - } - - out[0] = r; - out[1] = g; - out[2] = b; - out[3] = in[3]; -} - - -/* -=============== -R_ColorShiftLightingBytes - -=============== -*/ -static void R_ColorShiftLightingFloats(float in[4], float out[4], float scale ) -{ - scale *= pow(2.0f, r_mapOverBrightBits->integer - tr.overbrightBits); - - out[0] = in[0] * scale; - out[1] = in[1] * scale; - out[2] = in[2] * scale; - out[3] = in[3]; -} - - -void ColorToRGBE(const vec3_t color, unsigned char rgbe[4]) -{ - vec3_t sample; - float maxComponent; - int e; - - VectorCopy(color, sample); - - maxComponent = sample[0]; - if(sample[1] > maxComponent) - maxComponent = sample[1]; - if(sample[2] > maxComponent) - maxComponent = sample[2]; - - if(maxComponent < 1e-32) - { - rgbe[0] = 0; - rgbe[1] = 0; - rgbe[2] = 0; - rgbe[3] = 0; - } - else - { -#if 0 - maxComponent = frexp(maxComponent, &e) * 255.0 / maxComponent; - rgbe[0] = (unsigned char) (sample[0] * maxComponent); - rgbe[1] = (unsigned char) (sample[1] * maxComponent); - rgbe[2] = (unsigned char) (sample[2] * maxComponent); - rgbe[3] = (unsigned char) (e + 128); -#else - e = ceil(log(maxComponent) / log(2.0f));//ceil(log2(maxComponent)); - VectorScale(sample, 1.0 / pow(2.0f, e)/*exp2(e)*/, sample); - - rgbe[0] = (unsigned char) (sample[0] * 255); - rgbe[1] = (unsigned char) (sample[1] * 255); - rgbe[2] = (unsigned char) (sample[2] * 255); - rgbe[3] = (unsigned char) (e + 128); -#endif - } -} - - -void ColorToRGBA16F(const vec3_t color, unsigned short rgba16f[4]) -{ - rgba16f[0] = FloatToHalf(color[0]); - rgba16f[1] = FloatToHalf(color[1]); - rgba16f[2] = FloatToHalf(color[2]); - rgba16f[3] = FloatToHalf(1.0f); -} - - -/* -=============== -R_LoadLightmaps - -=============== -*/ -#define DEFAULT_LIGHTMAP_SIZE 128 -#define MAX_LIGHTMAP_PAGES 2 -static void R_LoadLightmaps( lump_t *l, lump_t *surfs ) { - byte *buf, *buf_p; - dsurface_t *surf; - int len; - byte *image; - int i, j, numLightmaps, textureInternalFormat = 0; - float maxIntensity = 0; - double sumIntensity = 0; - - len = l->filelen; - if ( !len ) { - return; - } - buf = fileBase + l->fileofs; - - // we are about to upload textures - R_IssuePendingRenderCommands(); - - tr.lightmapSize = DEFAULT_LIGHTMAP_SIZE; - numLightmaps = len / (tr.lightmapSize * tr.lightmapSize * 3); - - // check for deluxe mapping - if (numLightmaps <= 1) - { - tr.worldDeluxeMapping = qfalse; - } - else - { - tr.worldDeluxeMapping = qtrue; - for( i = 0, surf = (dsurface_t *)(fileBase + surfs->fileofs); - i < surfs->filelen / sizeof(dsurface_t); i++, surf++ ) { - int lightmapNum = LittleLong( surf->lightmapNum ); - - if ( lightmapNum >= 0 && (lightmapNum & 1) != 0 ) { - tr.worldDeluxeMapping = qfalse; - break; - } - } - } - - image = ri.Malloc(tr.lightmapSize * tr.lightmapSize * 4 * 2); - - if (tr.worldDeluxeMapping) - numLightmaps >>= 1; - - if(numLightmaps == 1) - { - //FIXME: HACK: maps with only one lightmap turn up fullbright for some reason. - //this avoids this, but isn't the correct solution. - numLightmaps++; - } - else if (r_mergeLightmaps->integer && numLightmaps >= 1024 ) - { - // FIXME: fat light maps don't support more than 1024 light maps - ri.Printf(PRINT_WARNING, "WARNING: number of lightmaps > 1024\n"); - numLightmaps = 1024; - } - - // use fat lightmaps of an appropriate size - if (r_mergeLightmaps->integer) - { - tr.fatLightmapSize = 512; - tr.fatLightmapStep = tr.fatLightmapSize / tr.lightmapSize; - - // at most MAX_LIGHTMAP_PAGES - while (tr.fatLightmapStep * tr.fatLightmapStep * MAX_LIGHTMAP_PAGES < numLightmaps && tr.fatLightmapSize != glConfig.maxTextureSize ) - { - tr.fatLightmapSize <<= 1; - tr.fatLightmapStep = tr.fatLightmapSize / tr.lightmapSize; - } - - tr.numLightmaps = numLightmaps / (tr.fatLightmapStep * tr.fatLightmapStep); - - if (numLightmaps % (tr.fatLightmapStep * tr.fatLightmapStep) != 0) - tr.numLightmaps++; - } - else - { - tr.numLightmaps = numLightmaps; - } - - tr.lightmaps = ri.Hunk_Alloc( tr.numLightmaps * sizeof(image_t *), h_low ); - - if (tr.worldDeluxeMapping) - { - tr.deluxemaps = ri.Hunk_Alloc( tr.numLightmaps * sizeof(image_t *), h_low ); - } - - if (r_hdr->integer && glRefConfig.textureFloat && glRefConfig.halfFloatPixel) - textureInternalFormat = GL_RGBA16F_ARB; - - if (r_mergeLightmaps->integer) - { - for (i = 0; i < tr.numLightmaps; i++) - { - tr.lightmaps[i] = R_CreateImage(va("_fatlightmap%d", i), NULL, tr.fatLightmapSize, tr.fatLightmapSize, IMGTYPE_COLORALPHA, IMGFLAG_NOLIGHTSCALE | IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, textureInternalFormat ); - - if (tr.worldDeluxeMapping) - { - tr.deluxemaps[i] = R_CreateImage(va("_fatdeluxemap%d", i), NULL, tr.fatLightmapSize, tr.fatLightmapSize, IMGTYPE_DELUXE, IMGFLAG_NOLIGHTSCALE | IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, 0 ); - } - } - } - - for(i = 0; i < numLightmaps; i++) - { - int xoff = 0, yoff = 0; - int lightmapnum = i; - // expand the 24 bit on-disk to 32 bit - - if (r_mergeLightmaps->integer) - { - int lightmaponpage = i % (tr.fatLightmapStep * tr.fatLightmapStep); - xoff = (lightmaponpage % tr.fatLightmapStep) * tr.lightmapSize; - yoff = (lightmaponpage / tr.fatLightmapStep) * tr.lightmapSize; - - lightmapnum /= (tr.fatLightmapStep * tr.fatLightmapStep); - } - - // if (tr.worldLightmapping) - { - char filename[MAX_QPATH]; - byte *hdrLightmap = NULL; - float lightScale = 1.0f; - int size = 0; - - // look for hdr lightmaps - if (r_hdr->integer) - { - Com_sprintf( filename, sizeof( filename ), "maps/%s/lm_%04d.hdr", s_worldData.baseName, i * (tr.worldDeluxeMapping ? 2 : 1) ); - //ri.Printf(PRINT_ALL, "looking for %s\n", filename); - - size = ri.FS_ReadFile(filename, (void **)&hdrLightmap); - } - - if (hdrLightmap) - { - byte *p = hdrLightmap; - //ri.Printf(PRINT_ALL, "found!\n"); - - /* FIXME: don't just skip over this header and actually parse it */ - while (size && !(*p == '\n' && *(p+1) == '\n')) - { - size--; - p++; - } - - if (!size) - ri.Error(ERR_DROP, "Bad header for %s!\n", filename); - - size -= 2; - p += 2; - - while (size && !(*p == '\n')) - { - size--; - p++; - } - - size--; - p++; - - buf_p = (byte *)p; - -#if 0 // HDRFILE_RGBE - if (size != tr.lightmapSize * tr.lightmapSize * 4) - ri.Error(ERR_DROP, "Bad size for %s (%i)!\n", filename, size); -#else // HDRFILE_FLOAT - if (size != tr.lightmapSize * tr.lightmapSize * 12) - ri.Error(ERR_DROP, "Bad size for %s (%i)!\n", filename, size); -#endif - } - else - { - if (tr.worldDeluxeMapping) - buf_p = buf + (i * 2) * tr.lightmapSize * tr.lightmapSize * 3; - else - buf_p = buf + i * tr.lightmapSize * tr.lightmapSize * 3; - } - - lightScale = pow(2, r_mapOverBrightBits->integer - tr.overbrightBits - 8); //exp2(r_mapOverBrightBits->integer - tr.overbrightBits - 8); - - for ( j = 0 ; j < tr.lightmapSize * tr.lightmapSize; j++ ) - { - if (r_hdr->integer) - { - float color[3]; - - if (hdrLightmap) - { -#if 0 // HDRFILE_RGBE - float exponent = exp2(buf_p[j*4+3] - 128); - - color[0] = buf_p[j*4+0] * exponent; - color[1] = buf_p[j*4+1] * exponent; - color[2] = buf_p[j*4+2] * exponent; -#else // HDRFILE_FLOAT - memcpy(color, &buf_p[j*12], 12); - - color[0] = LittleFloat(color[0]); - color[1] = LittleFloat(color[1]); - color[2] = LittleFloat(color[2]); -#endif - } - else - { - //hack: convert LDR lightmap to HDR one - color[0] = (buf_p[j*3+0] + 1.0f); - color[1] = (buf_p[j*3+1] + 1.0f); - color[2] = (buf_p[j*3+2] + 1.0f); - - // if under an arbitrary value (say 12) grey it out - // this prevents weird splotches in dimly lit areas - if (color[0] + color[1] + color[2] < 12.0f) - { - float avg = (color[0] + color[1] + color[2]) * 0.3333f; - color[0] = avg; - color[1] = avg; - color[2] = avg; - } - } - - VectorScale(color, lightScale, color); - - if (glRefConfig.textureFloat && glRefConfig.halfFloatPixel) - ColorToRGBA16F(color, (unsigned short *)(&image[j*8])); - else - ColorToRGBE(color, &image[j*4]); - } - else - { - if ( r_lightmap->integer == 2 ) - { // color code by intensity as development tool (FIXME: check range) - float r = buf_p[j*3+0]; - float g = buf_p[j*3+1]; - float b = buf_p[j*3+2]; - float intensity; - float out[3] = {0.0, 0.0, 0.0}; - - intensity = 0.33f * r + 0.685f * g + 0.063f * b; - - if ( intensity > 255 ) - intensity = 1.0f; - else - intensity /= 255.0f; - - if ( intensity > maxIntensity ) - maxIntensity = intensity; - - HSVtoRGB( intensity, 1.00, 0.50, out ); - - image[j*4+0] = out[0] * 255; - image[j*4+1] = out[1] * 255; - image[j*4+2] = out[2] * 255; - image[j*4+3] = 255; - - sumIntensity += intensity; - } - else - { - R_ColorShiftLightingBytes( &buf_p[j*3], &image[j*4] ); - image[j*4+3] = 255; - } - } - } - - if (r_mergeLightmaps->integer) - R_UpdateSubImage(tr.lightmaps[lightmapnum], image, xoff, yoff, tr.lightmapSize, tr.lightmapSize); - else - tr.lightmaps[i] = R_CreateImage(va("*lightmap%d", i), image, tr.lightmapSize, tr.lightmapSize, IMGTYPE_COLORALPHA, IMGFLAG_NOLIGHTSCALE | IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, textureInternalFormat ); - - if (hdrLightmap) - ri.FS_FreeFile(hdrLightmap); - } - - if (tr.worldDeluxeMapping) - { - buf_p = buf + (i * 2 + 1) * tr.lightmapSize * tr.lightmapSize * 3; - - for ( j = 0 ; j < tr.lightmapSize * tr.lightmapSize; j++ ) { - image[j*4+0] = buf_p[j*3+0]; - image[j*4+1] = buf_p[j*3+1]; - image[j*4+2] = buf_p[j*3+2]; - - // make 0,0,0 into 127,127,127 - if ((image[j*4+0] == 0) && (image[j*4+0] == 0) && (image[j*4+2] == 0)) - { - image[j*4+0] = - image[j*4+1] = - image[j*4+2] = 127; - } - - image[j*4+3] = 255; - } - - if (r_mergeLightmaps->integer) - { - R_UpdateSubImage(tr.deluxemaps[lightmapnum], image, xoff, yoff, tr.lightmapSize, tr.lightmapSize ); - } - else - { - tr.deluxemaps[i] = R_CreateImage(va("*deluxemap%d", i), image, tr.lightmapSize, tr.lightmapSize, IMGTYPE_DELUXE, IMGFLAG_NOLIGHTSCALE | IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, 0 ); - } - } - } - - if ( r_lightmap->integer == 2 ) { - ri.Printf( PRINT_ALL, "Brightest lightmap value: %d\n", ( int ) ( maxIntensity * 255 ) ); - } - - ri.Free(image); -} - - -static float FatPackU(float input, int lightmapnum) -{ - if (lightmapnum < 0) - return input; - - if (tr.worldDeluxeMapping) - lightmapnum >>= 1; - - lightmapnum %= (tr.fatLightmapStep * tr.fatLightmapStep); - - if(tr.fatLightmapSize > 0) - { - int x = lightmapnum % tr.fatLightmapStep; - - return (input / ((float)tr.fatLightmapStep)) + ((1.0 / ((float)tr.fatLightmapStep)) * (float)x); - } - - return input; -} - -static float FatPackV(float input, int lightmapnum) -{ - if (lightmapnum < 0) - return input; - - if (tr.worldDeluxeMapping) - lightmapnum >>= 1; - - lightmapnum %= (tr.fatLightmapStep * tr.fatLightmapStep); - - if(tr.fatLightmapSize > 0) - { - int y = lightmapnum / tr.fatLightmapStep; - - return (input / ((float)tr.fatLightmapStep)) + ((1.0 / ((float)tr.fatLightmapStep)) * (float)y); - } - - return input; -} - - -static int FatLightmap(int lightmapnum) -{ - if (lightmapnum < 0) - return lightmapnum; - - if (tr.worldDeluxeMapping) - lightmapnum >>= 1; - - if (tr.fatLightmapSize > 0) - { - return lightmapnum / (tr.fatLightmapStep * tr.fatLightmapStep); - } - - return lightmapnum; -} - -/* -================= -RE_SetWorldVisData - -This is called by the clipmodel subsystem so we can share the 1.8 megs of -space in big maps... -================= -*/ -void RE_SetWorldVisData( const byte *vis ) { - tr.externalVisData = vis; -} - - -/* -================= -R_LoadVisibility -================= -*/ -static void R_LoadVisibility( lump_t *l ) { - int len; - byte *buf; - - len = ( s_worldData.numClusters + 63 ) & ~63; - s_worldData.novis = ri.Hunk_Alloc( len, h_low ); - Com_Memset( s_worldData.novis, 0xff, len ); - - len = l->filelen; - if ( !len ) { - return; - } - buf = fileBase + l->fileofs; - - s_worldData.numClusters = LittleLong( ((int *)buf)[0] ); - s_worldData.clusterBytes = LittleLong( ((int *)buf)[1] ); - - // CM_Load should have given us the vis data to share, so - // we don't need to allocate another copy - if ( tr.externalVisData ) { - s_worldData.vis = tr.externalVisData; - } else { - byte *dest; - - dest = ri.Hunk_Alloc( len - 8, h_low ); - Com_Memcpy( dest, buf + 8, len - 8 ); - s_worldData.vis = dest; - } -} - -//=============================================================================== - - -/* -=============== -ShaderForShaderNum -=============== -*/ -static shader_t *ShaderForShaderNum( int shaderNum, int lightmapNum ) { - shader_t *shader; - dshader_t *dsh; - - int _shaderNum = LittleLong( shaderNum ); - if ( _shaderNum < 0 || _shaderNum >= s_worldData.numShaders ) { - ri.Error( ERR_DROP, "ShaderForShaderNum: bad num %i", _shaderNum ); - } - dsh = &s_worldData.shaders[ _shaderNum ]; - - if ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) { - lightmapNum = LIGHTMAP_BY_VERTEX; - } - - if ( r_fullbright->integer ) { - lightmapNum = LIGHTMAP_WHITEIMAGE; - } - - shader = R_FindShader( dsh->shader, lightmapNum, qtrue ); - - // if the shader had errors, just use default shader - if ( shader->defaultShader ) { - return tr.defaultShader; - } - - return shader; -} - -/* -=============== -ParseFace -=============== -*/ -static void ParseFace( dsurface_t *ds, drawVert_t *verts, float *hdrVertColors, msurface_t *surf, int *indexes ) { - int i, j; - srfSurfaceFace_t *cv; - srfTriangle_t *tri; - int numVerts, numTriangles, badTriangles; - int realLightmapNum; - - realLightmapNum = LittleLong( ds->lightmapNum ); - - // get fog volume - surf->fogIndex = LittleLong( ds->fogNum ) + 1; - - // get shader value - surf->shader = ShaderForShaderNum( ds->shaderNum, FatLightmap(realLightmapNum) ); - if ( r_singleShader->integer && !surf->shader->isSky ) { - surf->shader = tr.defaultShader; - } - - numVerts = LittleLong(ds->numVerts); - if (numVerts > MAX_FACE_POINTS) { - ri.Printf( PRINT_WARNING, "WARNING: MAX_FACE_POINTS exceeded: %i\n", numVerts); - numVerts = MAX_FACE_POINTS; - surf->shader = tr.defaultShader; - } - - numTriangles = LittleLong(ds->numIndexes) / 3; - - //cv = ri.Hunk_Alloc(sizeof(*cv), h_low); - cv = (void *)surf->data; - cv->surfaceType = SF_FACE; - - cv->numTriangles = numTriangles; - cv->triangles = ri.Hunk_Alloc(numTriangles * sizeof(cv->triangles[0]), h_low); - - cv->numVerts = numVerts; - cv->verts = ri.Hunk_Alloc(numVerts * sizeof(cv->verts[0]), h_low); - - // copy vertexes - surf->cullinfo.type = CULLINFO_PLANE | CULLINFO_BOX; - ClearBounds(surf->cullinfo.bounds[0], surf->cullinfo.bounds[1]); - verts += LittleLong(ds->firstVert); - for(i = 0; i < numVerts; i++) - { - vec4_t color; - - for(j = 0; j < 3; j++) - { - cv->verts[i].xyz[j] = LittleFloat(verts[i].xyz[j]); - cv->verts[i].normal[j] = LittleFloat(verts[i].normal[j]); - } - AddPointToBounds(cv->verts[i].xyz, surf->cullinfo.bounds[0], surf->cullinfo.bounds[1]); - for(j = 0; j < 2; j++) - { - cv->verts[i].st[j] = LittleFloat(verts[i].st[j]); - //cv->verts[i].lightmap[j] = LittleFloat(verts[i].lightmap[j]); - } - cv->verts[i].lightmap[0] = FatPackU(LittleFloat(verts[i].lightmap[0]), realLightmapNum); - cv->verts[i].lightmap[1] = FatPackV(LittleFloat(verts[i].lightmap[1]), realLightmapNum); - - if (hdrVertColors) - { - color[0] = hdrVertColors[(ds->firstVert + i) * 3 ]; - color[1] = hdrVertColors[(ds->firstVert + i) * 3 + 1]; - color[2] = hdrVertColors[(ds->firstVert + i) * 3 + 2]; - } - else - { - //hack: convert LDR vertex colors to HDR - if (r_hdr->integer) - { - color[0] = verts[i].color[0] + 1.0f; - color[1] = verts[i].color[1] + 1.0f; - color[2] = verts[i].color[2] + 1.0f; - } - else - { - color[0] = verts[i].color[0]; - color[1] = verts[i].color[1]; - color[2] = verts[i].color[2]; - } - - } - color[3] = verts[i].color[3] / 255.0f; - - R_ColorShiftLightingFloats( color, cv->verts[i].vertexColors, 1.0f / 255.0f ); - } - - // copy triangles - badTriangles = 0; - indexes += LittleLong(ds->firstIndex); - for(i = 0, tri = cv->triangles; i < numTriangles; i++, tri++) - { - for(j = 0; j < 3; j++) - { - tri->indexes[j] = LittleLong(indexes[i * 3 + j]); - - if(tri->indexes[j] < 0 || tri->indexes[j] >= numVerts) - { - ri.Error(ERR_DROP, "Bad index in face surface"); - } - } - - if ((tri->indexes[0] == tri->indexes[1]) || (tri->indexes[1] == tri->indexes[2]) || (tri->indexes[0] == tri->indexes[2])) - { - tri--; - badTriangles++; - } - } - - if (badTriangles) - { - ri.Printf(PRINT_WARNING, "Face has bad triangles, originally shader %s %d tris %d verts, now %d tris\n", surf->shader->name, numTriangles, numVerts, numTriangles - badTriangles); - cv->numTriangles -= badTriangles; - } - - // take the plane information from the lightmap vector - for ( i = 0 ; i < 3 ; i++ ) { - cv->plane.normal[i] = LittleFloat( ds->lightmapVecs[2][i] ); - } - cv->plane.dist = DotProduct( cv->verts[0].xyz, cv->plane.normal ); - SetPlaneSignbits( &cv->plane ); - cv->plane.type = PlaneTypeForNormal( cv->plane.normal ); - surf->cullinfo.plane = cv->plane; - - surf->data = (surfaceType_t *)cv; - -#ifdef USE_VERT_TANGENT_SPACE - // Tr3B - calc tangent spaces - { - srfVert_t *dv[3]; - - for(i = 0, tri = cv->triangles; i < numTriangles; i++, tri++) - { - dv[0] = &cv->verts[tri->indexes[0]]; - dv[1] = &cv->verts[tri->indexes[1]]; - dv[2] = &cv->verts[tri->indexes[2]]; - - R_CalcTangentVectors(dv); - } - } -#endif -} - - -/* -=============== -ParseMesh -=============== -*/ -static void ParseMesh ( dsurface_t *ds, drawVert_t *verts, float *hdrVertColors, msurface_t *surf ) { - srfGridMesh_t *grid; - int i, j; - int width, height, numPoints; - srfVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE]; - vec3_t bounds[2]; - vec3_t tmpVec; - static surfaceType_t skipData = SF_SKIP; - int realLightmapNum; - - realLightmapNum = LittleLong( ds->lightmapNum ); - - // get fog volume - surf->fogIndex = LittleLong( ds->fogNum ) + 1; - - // get shader value - surf->shader = ShaderForShaderNum( ds->shaderNum, FatLightmap(realLightmapNum) ); - if ( r_singleShader->integer && !surf->shader->isSky ) { - surf->shader = tr.defaultShader; - } - - // we may have a nodraw surface, because they might still need to - // be around for movement clipping - if ( s_worldData.shaders[ LittleLong( ds->shaderNum ) ].surfaceFlags & SURF_NODRAW ) { - surf->data = &skipData; - return; - } - - width = LittleLong( ds->patchWidth ); - height = LittleLong( ds->patchHeight ); - - if(width < 0 || width > MAX_PATCH_SIZE || height < 0 || height > MAX_PATCH_SIZE) - ri.Error(ERR_DROP, "ParseMesh: bad size"); - - verts += LittleLong( ds->firstVert ); - numPoints = width * height; - for(i = 0; i < numPoints; i++) - { - vec4_t color; - - for(j = 0; j < 3; j++) - { - points[i].xyz[j] = LittleFloat(verts[i].xyz[j]); - points[i].normal[j] = LittleFloat(verts[i].normal[j]); - } - - for(j = 0; j < 2; j++) - { - points[i].st[j] = LittleFloat(verts[i].st[j]); - //points[i].lightmap[j] = LittleFloat(verts[i].lightmap[j]); - } - points[i].lightmap[0] = FatPackU(LittleFloat(verts[i].lightmap[0]), realLightmapNum); - points[i].lightmap[1] = FatPackV(LittleFloat(verts[i].lightmap[1]), realLightmapNum); - - if (hdrVertColors) - { - color[0] = hdrVertColors[(ds->firstVert + i) * 3 ]; - color[1] = hdrVertColors[(ds->firstVert + i) * 3 + 1]; - color[2] = hdrVertColors[(ds->firstVert + i) * 3 + 2]; - } - else - { - //hack: convert LDR vertex colors to HDR - if (r_hdr->integer) - { - color[0] = verts[i].color[0] + 1.0f; - color[1] = verts[i].color[1] + 1.0f; - color[2] = verts[i].color[2] + 1.0f; - } - else - { - color[0] = verts[i].color[0]; - color[1] = verts[i].color[1]; - color[2] = verts[i].color[2]; - } - } - color[3] = verts[i].color[3] / 255.0f; - - R_ColorShiftLightingFloats( color, points[i].vertexColors, 1.0f / 255.0f ); - } - - // pre-tesseleate - grid = R_SubdividePatchToGrid( width, height, points ); - surf->data = (surfaceType_t *)grid; - - // copy the level of detail origin, which is the center - // of the group of all curves that must subdivide the same - // to avoid cracking - for ( i = 0 ; i < 3 ; i++ ) { - bounds[0][i] = LittleFloat( ds->lightmapVecs[0][i] ); - bounds[1][i] = LittleFloat( ds->lightmapVecs[1][i] ); - } - VectorAdd( bounds[0], bounds[1], bounds[1] ); - VectorScale( bounds[1], 0.5f, grid->lodOrigin ); - VectorSubtract( bounds[0], grid->lodOrigin, tmpVec ); - grid->lodRadius = VectorLength( tmpVec ); -} - -/* -=============== -ParseTriSurf -=============== -*/ -static void ParseTriSurf( dsurface_t *ds, drawVert_t *verts, float *hdrVertColors, msurface_t *surf, int *indexes ) { - srfTriangles_t *cv; - srfTriangle_t *tri; - int i, j; - int numVerts, numTriangles, badTriangles; - - // get fog volume - surf->fogIndex = LittleLong( ds->fogNum ) + 1; - - // get shader - surf->shader = ShaderForShaderNum( ds->shaderNum, LIGHTMAP_BY_VERTEX ); - if ( r_singleShader->integer && !surf->shader->isSky ) { - surf->shader = tr.defaultShader; - } - - numVerts = LittleLong(ds->numVerts); - numTriangles = LittleLong(ds->numIndexes) / 3; - - //cv = ri.Hunk_Alloc(sizeof(*cv), h_low); - cv = (void *)surf->data; - cv->surfaceType = SF_TRIANGLES; - - cv->numTriangles = numTriangles; - cv->triangles = ri.Hunk_Alloc(numTriangles * sizeof(cv->triangles[0]), h_low); - - cv->numVerts = numVerts; - cv->verts = ri.Hunk_Alloc(numVerts * sizeof(cv->verts[0]), h_low); - - surf->data = (surfaceType_t *) cv; - - // copy vertexes - surf->cullinfo.type = CULLINFO_BOX; - ClearBounds(surf->cullinfo.bounds[0], surf->cullinfo.bounds[1]); - verts += LittleLong(ds->firstVert); - for(i = 0; i < numVerts; i++) - { - vec4_t color; - - for(j = 0; j < 3; j++) - { - cv->verts[i].xyz[j] = LittleFloat(verts[i].xyz[j]); - cv->verts[i].normal[j] = LittleFloat(verts[i].normal[j]); - } - - AddPointToBounds( cv->verts[i].xyz, surf->cullinfo.bounds[0], surf->cullinfo.bounds[1] ); - - for(j = 0; j < 2; j++) - { - cv->verts[i].st[j] = LittleFloat(verts[i].st[j]); - cv->verts[i].lightmap[j] = LittleFloat(verts[i].lightmap[j]); - } - - if (hdrVertColors) - { - color[0] = hdrVertColors[(ds->firstVert + i) * 3 ]; - color[1] = hdrVertColors[(ds->firstVert + i) * 3 + 1]; - color[2] = hdrVertColors[(ds->firstVert + i) * 3 + 2]; - } - else - { - //hack: convert LDR vertex colors to HDR - if (r_hdr->integer) - { - color[0] = verts[i].color[0] + 1.0f; - color[1] = verts[i].color[1] + 1.0f; - color[2] = verts[i].color[2] + 1.0f; - } - else - { - color[0] = verts[i].color[0]; - color[1] = verts[i].color[1]; - color[2] = verts[i].color[2]; - } - } - color[3] = verts[i].color[3] / 255.0f; - - R_ColorShiftLightingFloats( color, cv->verts[i].vertexColors, 1.0f / 255.0f ); - } - - // copy triangles - badTriangles = 0; - indexes += LittleLong(ds->firstIndex); - for(i = 0, tri = cv->triangles; i < numTriangles; i++, tri++) - { - for(j = 0; j < 3; j++) - { - tri->indexes[j] = LittleLong(indexes[i * 3 + j]); - - if(tri->indexes[j] < 0 || tri->indexes[j] >= numVerts) - { - ri.Error(ERR_DROP, "Bad index in face surface"); - } - } - - if ((tri->indexes[0] == tri->indexes[1]) || (tri->indexes[1] == tri->indexes[2]) || (tri->indexes[0] == tri->indexes[2])) - { - tri--; - badTriangles++; - } - } - - if (badTriangles) - { - ri.Printf(PRINT_WARNING, "Trisurf has bad triangles, originally shader %s %d tris %d verts, now %d tris\n", surf->shader->name, numTriangles, numVerts, numTriangles - badTriangles); - cv->numTriangles -= badTriangles; - } - -#ifdef USE_VERT_TANGENT_SPACE - // Tr3B - calc tangent spaces - { - srfVert_t *dv[3]; - - for(i = 0, tri = cv->triangles; i < numTriangles; i++, tri++) - { - dv[0] = &cv->verts[tri->indexes[0]]; - dv[1] = &cv->verts[tri->indexes[1]]; - dv[2] = &cv->verts[tri->indexes[2]]; - - R_CalcTangentVectors(dv); - } - } -#endif -} - -/* -=============== -ParseFlare -=============== -*/ -static void ParseFlare( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int *indexes ) { - srfFlare_t *flare; - int i; - - // get fog volume - surf->fogIndex = LittleLong( ds->fogNum ) + 1; - - // get shader - surf->shader = ShaderForShaderNum( ds->shaderNum, LIGHTMAP_BY_VERTEX ); - if ( r_singleShader->integer && !surf->shader->isSky ) { - surf->shader = tr.defaultShader; - } - - //flare = ri.Hunk_Alloc( sizeof( *flare ), h_low ); - flare = (void *)surf->data; - flare->surfaceType = SF_FLARE; - - surf->data = (surfaceType_t *)flare; - - for ( i = 0 ; i < 3 ; i++ ) { - flare->origin[i] = LittleFloat( ds->lightmapOrigin[i] ); - flare->color[i] = LittleFloat( ds->lightmapVecs[0][i] ); - flare->normal[i] = LittleFloat( ds->lightmapVecs[2][i] ); - } -} - - -/* -================= -R_MergedWidthPoints - -returns true if there are grid points merged on a width edge -================= -*/ -int R_MergedWidthPoints(srfGridMesh_t *grid, int offset) { - int i, j; - - for (i = 1; i < grid->width-1; i++) { - for (j = i + 1; j < grid->width-1; j++) { - if ( fabs(grid->verts[i + offset].xyz[0] - grid->verts[j + offset].xyz[0]) > .1) continue; - if ( fabs(grid->verts[i + offset].xyz[1] - grid->verts[j + offset].xyz[1]) > .1) continue; - if ( fabs(grid->verts[i + offset].xyz[2] - grid->verts[j + offset].xyz[2]) > .1) continue; - return qtrue; - } - } - return qfalse; -} - -/* -================= -R_MergedHeightPoints - -returns true if there are grid points merged on a height edge -================= -*/ -int R_MergedHeightPoints(srfGridMesh_t *grid, int offset) { - int i, j; - - for (i = 1; i < grid->height-1; i++) { - for (j = i + 1; j < grid->height-1; j++) { - if ( fabs(grid->verts[grid->width * i + offset].xyz[0] - grid->verts[grid->width * j + offset].xyz[0]) > .1) continue; - if ( fabs(grid->verts[grid->width * i + offset].xyz[1] - grid->verts[grid->width * j + offset].xyz[1]) > .1) continue; - if ( fabs(grid->verts[grid->width * i + offset].xyz[2] - grid->verts[grid->width * j + offset].xyz[2]) > .1) continue; - return qtrue; - } - } - return qfalse; -} - -/* -================= -R_FixSharedVertexLodError_r - -NOTE: never sync LoD through grid edges with merged points! - -FIXME: write generalized version that also avoids cracks between a patch and one that meets half way? -================= -*/ -void R_FixSharedVertexLodError_r( int start, srfGridMesh_t *grid1 ) { - int j, k, l, m, n, offset1, offset2, touch; - srfGridMesh_t *grid2; - - for ( j = start; j < s_worldData.numsurfaces; j++ ) { - // - grid2 = (srfGridMesh_t *) s_worldData.surfaces[j].data; - // if this surface is not a grid - if ( grid2->surfaceType != SF_GRID ) continue; - // if the LOD errors are already fixed for this patch - if ( grid2->lodFixed == 2 ) continue; - // grids in the same LOD group should have the exact same lod radius - if ( grid1->lodRadius != grid2->lodRadius ) continue; - // grids in the same LOD group should have the exact same lod origin - if ( grid1->lodOrigin[0] != grid2->lodOrigin[0] ) continue; - if ( grid1->lodOrigin[1] != grid2->lodOrigin[1] ) continue; - if ( grid1->lodOrigin[2] != grid2->lodOrigin[2] ) continue; - // - touch = qfalse; - for (n = 0; n < 2; n++) { - // - if (n) offset1 = (grid1->height-1) * grid1->width; - else offset1 = 0; - if (R_MergedWidthPoints(grid1, offset1)) continue; - for (k = 1; k < grid1->width-1; k++) { - for (m = 0; m < 2; m++) { - - if (m) offset2 = (grid2->height-1) * grid2->width; - else offset2 = 0; - if (R_MergedWidthPoints(grid2, offset2)) continue; - for ( l = 1; l < grid2->width-1; l++) { - // - if ( fabs(grid1->verts[k + offset1].xyz[0] - grid2->verts[l + offset2].xyz[0]) > .1) continue; - if ( fabs(grid1->verts[k + offset1].xyz[1] - grid2->verts[l + offset2].xyz[1]) > .1) continue; - if ( fabs(grid1->verts[k + offset1].xyz[2] - grid2->verts[l + offset2].xyz[2]) > .1) continue; - // ok the points are equal and should have the same lod error - grid2->widthLodError[l] = grid1->widthLodError[k]; - touch = qtrue; - } - } - for (m = 0; m < 2; m++) { - - if (m) offset2 = grid2->width-1; - else offset2 = 0; - if (R_MergedHeightPoints(grid2, offset2)) continue; - for ( l = 1; l < grid2->height-1; l++) { - // - if ( fabs(grid1->verts[k + offset1].xyz[0] - grid2->verts[grid2->width * l + offset2].xyz[0]) > .1) continue; - if ( fabs(grid1->verts[k + offset1].xyz[1] - grid2->verts[grid2->width * l + offset2].xyz[1]) > .1) continue; - if ( fabs(grid1->verts[k + offset1].xyz[2] - grid2->verts[grid2->width * l + offset2].xyz[2]) > .1) continue; - // ok the points are equal and should have the same lod error - grid2->heightLodError[l] = grid1->widthLodError[k]; - touch = qtrue; - } - } - } - } - for (n = 0; n < 2; n++) { - // - if (n) offset1 = grid1->width-1; - else offset1 = 0; - if (R_MergedHeightPoints(grid1, offset1)) continue; - for (k = 1; k < grid1->height-1; k++) { - for (m = 0; m < 2; m++) { - - if (m) offset2 = (grid2->height-1) * grid2->width; - else offset2 = 0; - if (R_MergedWidthPoints(grid2, offset2)) continue; - for ( l = 1; l < grid2->width-1; l++) { - // - if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[0] - grid2->verts[l + offset2].xyz[0]) > .1) continue; - if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[1] - grid2->verts[l + offset2].xyz[1]) > .1) continue; - if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[2] - grid2->verts[l + offset2].xyz[2]) > .1) continue; - // ok the points are equal and should have the same lod error - grid2->widthLodError[l] = grid1->heightLodError[k]; - touch = qtrue; - } - } - for (m = 0; m < 2; m++) { - - if (m) offset2 = grid2->width-1; - else offset2 = 0; - if (R_MergedHeightPoints(grid2, offset2)) continue; - for ( l = 1; l < grid2->height-1; l++) { - // - if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[0] - grid2->verts[grid2->width * l + offset2].xyz[0]) > .1) continue; - if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[1] - grid2->verts[grid2->width * l + offset2].xyz[1]) > .1) continue; - if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[2] - grid2->verts[grid2->width * l + offset2].xyz[2]) > .1) continue; - // ok the points are equal and should have the same lod error - grid2->heightLodError[l] = grid1->heightLodError[k]; - touch = qtrue; - } - } - } - } - if (touch) { - grid2->lodFixed = 2; - R_FixSharedVertexLodError_r ( start, grid2 ); - //NOTE: this would be correct but makes things really slow - //grid2->lodFixed = 1; - } - } -} - -/* -================= -R_FixSharedVertexLodError - -This function assumes that all patches in one group are nicely stitched together for the highest LoD. -If this is not the case this function will still do its job but won't fix the highest LoD cracks. -================= -*/ -void R_FixSharedVertexLodError( void ) { - int i; - srfGridMesh_t *grid1; - - for ( i = 0; i < s_worldData.numsurfaces; i++ ) { - // - grid1 = (srfGridMesh_t *) s_worldData.surfaces[i].data; - // if this surface is not a grid - if ( grid1->surfaceType != SF_GRID ) - continue; - // - if ( grid1->lodFixed ) - continue; - // - grid1->lodFixed = 2; - // recursively fix other patches in the same LOD group - R_FixSharedVertexLodError_r( i + 1, grid1); - } -} - - -/* -=============== -R_StitchPatches -=============== -*/ -int R_StitchPatches( int grid1num, int grid2num ) { - float *v1, *v2; - srfGridMesh_t *grid1, *grid2; - int k, l, m, n, offset1, offset2, row, column; - - grid1 = (srfGridMesh_t *) s_worldData.surfaces[grid1num].data; - grid2 = (srfGridMesh_t *) s_worldData.surfaces[grid2num].data; - for (n = 0; n < 2; n++) { - // - if (n) offset1 = (grid1->height-1) * grid1->width; - else offset1 = 0; - if (R_MergedWidthPoints(grid1, offset1)) - continue; - for (k = 0; k < grid1->width-2; k += 2) { - - for (m = 0; m < 2; m++) { - - if ( grid2->width >= MAX_GRID_SIZE ) - break; - if (m) offset2 = (grid2->height-1) * grid2->width; - else offset2 = 0; - for ( l = 0; l < grid2->width-1; l++) { - // - v1 = grid1->verts[k + offset1].xyz; - v2 = grid2->verts[l + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - - v1 = grid1->verts[k + 2 + offset1].xyz; - v2 = grid2->verts[l + 1 + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - // - v1 = grid2->verts[l + offset2].xyz; - v2 = grid2->verts[l + 1 + offset2].xyz; - if ( fabs(v1[0] - v2[0]) < .01 && - fabs(v1[1] - v2[1]) < .01 && - fabs(v1[2] - v2[2]) < .01) - continue; - // - //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); - // insert column into grid2 right after after column l - if (m) row = grid2->height-1; - else row = 0; - grid2 = R_GridInsertColumn( grid2, l+1, row, - grid1->verts[k + 1 + offset1].xyz, grid1->widthLodError[k+1]); - grid2->lodStitched = qfalse; - s_worldData.surfaces[grid2num].data = (void *) grid2; - return qtrue; - } - } - for (m = 0; m < 2; m++) { - - if (grid2->height >= MAX_GRID_SIZE) - break; - if (m) offset2 = grid2->width-1; - else offset2 = 0; - for ( l = 0; l < grid2->height-1; l++) { - // - v1 = grid1->verts[k + offset1].xyz; - v2 = grid2->verts[grid2->width * l + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - - v1 = grid1->verts[k + 2 + offset1].xyz; - v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - // - v1 = grid2->verts[grid2->width * l + offset2].xyz; - v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) < .01 && - fabs(v1[1] - v2[1]) < .01 && - fabs(v1[2] - v2[2]) < .01) - continue; - // - //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); - // insert row into grid2 right after after row l - if (m) column = grid2->width-1; - else column = 0; - grid2 = R_GridInsertRow( grid2, l+1, column, - grid1->verts[k + 1 + offset1].xyz, grid1->widthLodError[k+1]); - grid2->lodStitched = qfalse; - s_worldData.surfaces[grid2num].data = (void *) grid2; - return qtrue; - } - } - } - } - for (n = 0; n < 2; n++) { - // - if (n) offset1 = grid1->width-1; - else offset1 = 0; - if (R_MergedHeightPoints(grid1, offset1)) - continue; - for (k = 0; k < grid1->height-2; k += 2) { - for (m = 0; m < 2; m++) { - - if ( grid2->width >= MAX_GRID_SIZE ) - break; - if (m) offset2 = (grid2->height-1) * grid2->width; - else offset2 = 0; - for ( l = 0; l < grid2->width-1; l++) { - // - v1 = grid1->verts[grid1->width * k + offset1].xyz; - v2 = grid2->verts[l + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - - v1 = grid1->verts[grid1->width * (k + 2) + offset1].xyz; - v2 = grid2->verts[l + 1 + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - // - v1 = grid2->verts[l + offset2].xyz; - v2 = grid2->verts[(l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) < .01 && - fabs(v1[1] - v2[1]) < .01 && - fabs(v1[2] - v2[2]) < .01) - continue; - // - //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); - // insert column into grid2 right after after column l - if (m) row = grid2->height-1; - else row = 0; - grid2 = R_GridInsertColumn( grid2, l+1, row, - grid1->verts[grid1->width * (k + 1) + offset1].xyz, grid1->heightLodError[k+1]); - grid2->lodStitched = qfalse; - s_worldData.surfaces[grid2num].data = (void *) grid2; - return qtrue; - } - } - for (m = 0; m < 2; m++) { - - if (grid2->height >= MAX_GRID_SIZE) - break; - if (m) offset2 = grid2->width-1; - else offset2 = 0; - for ( l = 0; l < grid2->height-1; l++) { - // - v1 = grid1->verts[grid1->width * k + offset1].xyz; - v2 = grid2->verts[grid2->width * l + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - - v1 = grid1->verts[grid1->width * (k + 2) + offset1].xyz; - v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - // - v1 = grid2->verts[grid2->width * l + offset2].xyz; - v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) < .01 && - fabs(v1[1] - v2[1]) < .01 && - fabs(v1[2] - v2[2]) < .01) - continue; - // - //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); - // insert row into grid2 right after after row l - if (m) column = grid2->width-1; - else column = 0; - grid2 = R_GridInsertRow( grid2, l+1, column, - grid1->verts[grid1->width * (k + 1) + offset1].xyz, grid1->heightLodError[k+1]); - grid2->lodStitched = qfalse; - s_worldData.surfaces[grid2num].data = (void *) grid2; - return qtrue; - } - } - } - } - for (n = 0; n < 2; n++) { - // - if (n) offset1 = (grid1->height-1) * grid1->width; - else offset1 = 0; - if (R_MergedWidthPoints(grid1, offset1)) - continue; - for (k = grid1->width-1; k > 1; k -= 2) { - - for (m = 0; m < 2; m++) { - - if ( grid2->width >= MAX_GRID_SIZE ) - break; - if (m) offset2 = (grid2->height-1) * grid2->width; - else offset2 = 0; - for ( l = 0; l < grid2->width-1; l++) { - // - v1 = grid1->verts[k + offset1].xyz; - v2 = grid2->verts[l + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - - v1 = grid1->verts[k - 2 + offset1].xyz; - v2 = grid2->verts[l + 1 + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - // - v1 = grid2->verts[l + offset2].xyz; - v2 = grid2->verts[(l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) < .01 && - fabs(v1[1] - v2[1]) < .01 && - fabs(v1[2] - v2[2]) < .01) - continue; - // - //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); - // insert column into grid2 right after after column l - if (m) row = grid2->height-1; - else row = 0; - grid2 = R_GridInsertColumn( grid2, l+1, row, - grid1->verts[k - 1 + offset1].xyz, grid1->widthLodError[k+1]); - grid2->lodStitched = qfalse; - s_worldData.surfaces[grid2num].data = (void *) grid2; - return qtrue; - } - } - for (m = 0; m < 2; m++) { - - if (grid2->height >= MAX_GRID_SIZE) - break; - if (m) offset2 = grid2->width-1; - else offset2 = 0; - for ( l = 0; l < grid2->height-1; l++) { - // - v1 = grid1->verts[k + offset1].xyz; - v2 = grid2->verts[grid2->width * l + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - - v1 = grid1->verts[k - 2 + offset1].xyz; - v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - // - v1 = grid2->verts[grid2->width * l + offset2].xyz; - v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) < .01 && - fabs(v1[1] - v2[1]) < .01 && - fabs(v1[2] - v2[2]) < .01) - continue; - // - //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); - // insert row into grid2 right after after row l - if (m) column = grid2->width-1; - else column = 0; - grid2 = R_GridInsertRow( grid2, l+1, column, - grid1->verts[k - 1 + offset1].xyz, grid1->widthLodError[k+1]); - if (!grid2) - break; - grid2->lodStitched = qfalse; - s_worldData.surfaces[grid2num].data = (void *) grid2; - return qtrue; - } - } - } - } - for (n = 0; n < 2; n++) { - // - if (n) offset1 = grid1->width-1; - else offset1 = 0; - if (R_MergedHeightPoints(grid1, offset1)) - continue; - for (k = grid1->height-1; k > 1; k -= 2) { - for (m = 0; m < 2; m++) { - - if ( grid2->width >= MAX_GRID_SIZE ) - break; - if (m) offset2 = (grid2->height-1) * grid2->width; - else offset2 = 0; - for ( l = 0; l < grid2->width-1; l++) { - // - v1 = grid1->verts[grid1->width * k + offset1].xyz; - v2 = grid2->verts[l + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - - v1 = grid1->verts[grid1->width * (k - 2) + offset1].xyz; - v2 = grid2->verts[l + 1 + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - // - v1 = grid2->verts[l + offset2].xyz; - v2 = grid2->verts[(l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) < .01 && - fabs(v1[1] - v2[1]) < .01 && - fabs(v1[2] - v2[2]) < .01) - continue; - // - //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); - // insert column into grid2 right after after column l - if (m) row = grid2->height-1; - else row = 0; - grid2 = R_GridInsertColumn( grid2, l+1, row, - grid1->verts[grid1->width * (k - 1) + offset1].xyz, grid1->heightLodError[k+1]); - grid2->lodStitched = qfalse; - s_worldData.surfaces[grid2num].data = (void *) grid2; - return qtrue; - } - } - for (m = 0; m < 2; m++) { - - if (grid2->height >= MAX_GRID_SIZE) - break; - if (m) offset2 = grid2->width-1; - else offset2 = 0; - for ( l = 0; l < grid2->height-1; l++) { - // - v1 = grid1->verts[grid1->width * k + offset1].xyz; - v2 = grid2->verts[grid2->width * l + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - - v1 = grid1->verts[grid1->width * (k - 2) + offset1].xyz; - v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - // - v1 = grid2->verts[grid2->width * l + offset2].xyz; - v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) < .01 && - fabs(v1[1] - v2[1]) < .01 && - fabs(v1[2] - v2[2]) < .01) - continue; - // - //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); - // insert row into grid2 right after after row l - if (m) column = grid2->width-1; - else column = 0; - grid2 = R_GridInsertRow( grid2, l+1, column, - grid1->verts[grid1->width * (k - 1) + offset1].xyz, grid1->heightLodError[k+1]); - grid2->lodStitched = qfalse; - s_worldData.surfaces[grid2num].data = (void *) grid2; - return qtrue; - } - } - } - } - return qfalse; -} - -/* -=============== -R_TryStitchPatch - -This function will try to stitch patches in the same LoD group together for the highest LoD. - -Only single missing vertice cracks will be fixed. - -Vertices will be joined at the patch side a crack is first found, at the other side -of the patch (on the same row or column) the vertices will not be joined and cracks -might still appear at that side. -=============== -*/ -int R_TryStitchingPatch( int grid1num ) { - int j, numstitches; - srfGridMesh_t *grid1, *grid2; - - numstitches = 0; - grid1 = (srfGridMesh_t *) s_worldData.surfaces[grid1num].data; - for ( j = 0; j < s_worldData.numsurfaces; j++ ) { - // - grid2 = (srfGridMesh_t *) s_worldData.surfaces[j].data; - // if this surface is not a grid - if ( grid2->surfaceType != SF_GRID ) continue; - // grids in the same LOD group should have the exact same lod radius - if ( grid1->lodRadius != grid2->lodRadius ) continue; - // grids in the same LOD group should have the exact same lod origin - if ( grid1->lodOrigin[0] != grid2->lodOrigin[0] ) continue; - if ( grid1->lodOrigin[1] != grid2->lodOrigin[1] ) continue; - if ( grid1->lodOrigin[2] != grid2->lodOrigin[2] ) continue; - // - while (R_StitchPatches(grid1num, j)) - { - numstitches++; - } - } - return numstitches; -} - -/* -=============== -R_StitchAllPatches -=============== -*/ -void R_StitchAllPatches( void ) { - int i, stitched, numstitches; - srfGridMesh_t *grid1; - - numstitches = 0; - do - { - stitched = qfalse; - for ( i = 0; i < s_worldData.numsurfaces; i++ ) { - // - grid1 = (srfGridMesh_t *) s_worldData.surfaces[i].data; - // if this surface is not a grid - if ( grid1->surfaceType != SF_GRID ) - continue; - // - if ( grid1->lodStitched ) - continue; - // - grid1->lodStitched = qtrue; - stitched = qtrue; - // - numstitches += R_TryStitchingPatch( i ); - } - } - while (stitched); - ri.Printf( PRINT_ALL, "stitched %d LoD cracks\n", numstitches ); -} - -/* -=============== -R_MovePatchSurfacesToHunk -=============== -*/ -void R_MovePatchSurfacesToHunk(void) { - int i, size; - srfGridMesh_t *grid, *hunkgrid; - - for ( i = 0; i < s_worldData.numsurfaces; i++ ) { - // - grid = (srfGridMesh_t *) s_worldData.surfaces[i].data; - // if this surface is not a grid - if ( grid->surfaceType != SF_GRID ) - continue; - // - size = sizeof(*grid); - hunkgrid = ri.Hunk_Alloc(size, h_low); - Com_Memcpy(hunkgrid, grid, size); - - hunkgrid->widthLodError = ri.Hunk_Alloc( grid->width * 4, h_low ); - Com_Memcpy( hunkgrid->widthLodError, grid->widthLodError, grid->width * 4 ); - - hunkgrid->heightLodError = ri.Hunk_Alloc( grid->height * 4, h_low ); - Com_Memcpy( hunkgrid->heightLodError, grid->heightLodError, grid->height * 4 ); - - hunkgrid->numTriangles = grid->numTriangles; - hunkgrid->triangles = ri.Hunk_Alloc(grid->numTriangles * sizeof(srfTriangle_t), h_low); - Com_Memcpy(hunkgrid->triangles, grid->triangles, grid->numTriangles * sizeof(srfTriangle_t)); - - hunkgrid->numVerts = grid->numVerts; - hunkgrid->verts = ri.Hunk_Alloc(grid->numVerts * sizeof(srfVert_t), h_low); - Com_Memcpy(hunkgrid->verts, grid->verts, grid->numVerts * sizeof(srfVert_t)); - - R_FreeSurfaceGridMesh( grid ); - - s_worldData.surfaces[i].data = (void *) hunkgrid; - } -} - - -/* -================= -BSPSurfaceCompare -compare function for qsort() -================= -*/ -static int BSPSurfaceCompare(const void *a, const void *b) -{ - msurface_t *aa, *bb; - - aa = *(msurface_t **) a; - bb = *(msurface_t **) b; - - // shader first - if(aa->shader->sortedIndex < bb->shader->sortedIndex) - return -1; - - else if(aa->shader->sortedIndex > bb->shader->sortedIndex) - return 1; - - // by fogIndex - if(aa->fogIndex < bb->fogIndex) - return -1; - - else if(aa->fogIndex > bb->fogIndex) - return 1; - - return 0; -} - - -static void CopyVert(const srfVert_t * in, srfVert_t * out) -{ - int j; - - for(j = 0; j < 3; j++) - { - out->xyz[j] = in->xyz[j]; -#ifdef USE_VERT_TANGENT_SPACE - out->tangent[j] = in->tangent[j]; - out->bitangent[j] = in->bitangent[j]; -#endif - out->normal[j] = in->normal[j]; - out->lightdir[j] = in->lightdir[j]; - } - - for(j = 0; j < 2; j++) - { - out->st[j] = in->st[j]; - out->lightmap[j] = in->lightmap[j]; - } - - for(j = 0; j < 4; j++) - { - out->vertexColors[j] = in->vertexColors[j]; - } -} - - -/* -=============== -R_CreateWorldVBO -=============== -*/ -static void R_CreateWorldVBO(void) -{ - int i, j, k; - - int numVerts; - srfVert_t *verts; - - int numTriangles; - srfTriangle_t *triangles; - - int numSurfaces; - msurface_t *surface; - msurface_t **surfacesSorted; - - int startTime, endTime; - - startTime = ri.Milliseconds(); - - numVerts = 0; - numTriangles = 0; - numSurfaces = 0; - for(k = 0, surface = &s_worldData.surfaces[0]; k < s_worldData.numsurfaces /* s_worldData.numWorldSurfaces */; k++, surface++) - { - if(*surface->data == SF_FACE) - { - srfSurfaceFace_t *face = (srfSurfaceFace_t *) surface->data; - - if(face->numVerts) - numVerts += face->numVerts; - - if(face->numTriangles) - numTriangles += face->numTriangles; - - numSurfaces++; - } - else if(*surface->data == SF_GRID) - { - srfGridMesh_t *grid = (srfGridMesh_t *) surface->data; - - if(grid->numVerts) - numVerts += grid->numVerts; - - if(grid->numTriangles) - numTriangles += grid->numTriangles; - - numSurfaces++; - } - else if(*surface->data == SF_TRIANGLES) - { - srfTriangles_t *tri = (srfTriangles_t *) surface->data; - - if(tri->numVerts) - numVerts += tri->numVerts; - - if(tri->numTriangles) - numTriangles += tri->numTriangles; - - numSurfaces++; - } - } - - if(!numVerts || !numTriangles) - return; - - ri.Printf(PRINT_ALL, "...calculating world VBO ( %i verts %i tris )\n", numVerts, numTriangles); - - // create arrays - - verts = ri.Hunk_AllocateTempMemory(numVerts * sizeof(srfVert_t)); - - triangles = ri.Hunk_AllocateTempMemory(numTriangles * sizeof(srfTriangle_t)); - - // presort surfaces - surfacesSorted = ri.Malloc(numSurfaces * sizeof(*surfacesSorted)); - - j = 0; - for(k = 0, surface = &s_worldData.surfaces[0]; k < s_worldData.numsurfaces; k++, surface++) - { - if(*surface->data == SF_FACE || *surface->data == SF_GRID || *surface->data == SF_TRIANGLES) - { - surfacesSorted[j++] = surface; - } - } - - qsort(surfacesSorted, numSurfaces, sizeof(*surfacesSorted), BSPSurfaceCompare); - - // set up triangle indices - numVerts = 0; - numTriangles = 0; - for(k = 0, surface = surfacesSorted[k]; k < numSurfaces; k++, surface = surfacesSorted[k]) - { - if(*surface->data == SF_FACE) - { - srfSurfaceFace_t *srf = (srfSurfaceFace_t *) surface->data; - - srf->firstIndex = numTriangles * 3; - - if(srf->numTriangles) - { - srfTriangle_t *tri; - - srf->minIndex = numVerts + srf->triangles->indexes[0]; - srf->maxIndex = numVerts + srf->triangles->indexes[0]; - - for(i = 0, tri = srf->triangles; i < srf->numTriangles; i++, tri++) - { - for(j = 0; j < 3; j++) - { - triangles[numTriangles + i].indexes[j] = numVerts + tri->indexes[j]; - srf->minIndex = MIN(srf->minIndex, numVerts + tri->indexes[j]); - srf->maxIndex = MAX(srf->maxIndex, numVerts + tri->indexes[j]); - } - } - - numTriangles += srf->numTriangles; - } - - if(srf->numVerts) - numVerts += srf->numVerts; - } - else if(*surface->data == SF_GRID) - { - srfGridMesh_t *srf = (srfGridMesh_t *) surface->data; - - srf->firstIndex = numTriangles * 3; - - if(srf->numTriangles) - { - srfTriangle_t *tri; - - srf->minIndex = numVerts + srf->triangles->indexes[0]; - srf->maxIndex = numVerts + srf->triangles->indexes[0]; - - for(i = 0, tri = srf->triangles; i < srf->numTriangles; i++, tri++) - { - for(j = 0; j < 3; j++) - { - triangles[numTriangles + i].indexes[j] = numVerts + tri->indexes[j]; - srf->minIndex = MIN(srf->minIndex, numVerts + tri->indexes[j]); - srf->maxIndex = MAX(srf->maxIndex, numVerts + tri->indexes[j]); - } - } - - numTriangles += srf->numTriangles; - } - - if(srf->numVerts) - numVerts += srf->numVerts; - } - else if(*surface->data == SF_TRIANGLES) - { - srfTriangles_t *srf = (srfTriangles_t *) surface->data; - - srf->firstIndex = numTriangles * 3; - - if(srf->numTriangles) - { - srfTriangle_t *tri; - - srf->minIndex = numVerts + srf->triangles->indexes[0]; - srf->maxIndex = numVerts + srf->triangles->indexes[0]; - - for(i = 0, tri = srf->triangles; i < srf->numTriangles; i++, tri++) - { - for(j = 0; j < 3; j++) - { - triangles[numTriangles + i].indexes[j] = numVerts + tri->indexes[j]; - srf->minIndex = MIN(srf->minIndex, numVerts + tri->indexes[j]); - srf->maxIndex = MAX(srf->maxIndex, numVerts + tri->indexes[j]); - } - } - - numTriangles += srf->numTriangles; - } - - if(srf->numVerts) - numVerts += srf->numVerts; - } - } - - // build vertices - numVerts = 0; - for(k = 0, surface = surfacesSorted[k]; k < numSurfaces; k++, surface = surfacesSorted[k]) - { - if(*surface->data == SF_FACE) - { - srfSurfaceFace_t *srf = (srfSurfaceFace_t *) surface->data; - - srf->firstVert = numVerts; - - if(srf->numVerts) - { - for(i = 0; i < srf->numVerts; i++) - { - CopyVert(&srf->verts[i], &verts[numVerts + i]); - } - - numVerts += srf->numVerts; - } - } - else if(*surface->data == SF_GRID) - { - srfGridMesh_t *srf = (srfGridMesh_t *) surface->data; - - srf->firstVert = numVerts; - - if(srf->numVerts) - { - for(i = 0; i < srf->numVerts; i++) - { - CopyVert(&srf->verts[i], &verts[numVerts + i]); - } - - numVerts += srf->numVerts; - } - } - else if(*surface->data == SF_TRIANGLES) - { - srfTriangles_t *srf = (srfTriangles_t *) surface->data; - - srf->firstVert = numVerts; - - if(srf->numVerts) - { - for(i = 0; i < srf->numVerts; i++) - { - CopyVert(&srf->verts[i], &verts[numVerts + i]); - } - - numVerts += srf->numVerts; - } - } - } - -#ifdef USE_VERT_TANGENT_SPACE - s_worldData.vbo = R_CreateVBO2(va("staticBspModel0_VBO %i", 0), numVerts, verts, - ATTR_POSITION | ATTR_TEXCOORD | ATTR_LIGHTCOORD | ATTR_TANGENT | ATTR_BITANGENT | - ATTR_NORMAL | ATTR_COLOR | ATTR_LIGHTDIRECTION, VBO_USAGE_STATIC); -#else - s_worldData.vbo = R_CreateVBO2(va("staticBspModel0_VBO %i", 0), numVerts, verts, - ATTR_POSITION | ATTR_TEXCOORD | ATTR_LIGHTCOORD | - ATTR_NORMAL | ATTR_COLOR | ATTR_LIGHTDIRECTION, VBO_USAGE_STATIC); -#endif - - s_worldData.ibo = R_CreateIBO2(va("staticBspModel0_IBO %i", 0), numTriangles, triangles, VBO_USAGE_STATIC); - - endTime = ri.Milliseconds(); - ri.Printf(PRINT_ALL, "world VBO calculation time = %5.2f seconds\n", (endTime - startTime) / 1000.0); - - // point triangle surfaces to world VBO - for(k = 0, surface = surfacesSorted[k]; k < numSurfaces; k++, surface = surfacesSorted[k]) - { - if(*surface->data == SF_FACE) - { - srfSurfaceFace_t *srf = (srfSurfaceFace_t *) surface->data; - - if( srf->numVerts && srf->numTriangles) - { - srf->vbo = s_worldData.vbo; - srf->ibo = s_worldData.ibo; - } - } - else if(*surface->data == SF_GRID) - { - srfGridMesh_t *srf = (srfGridMesh_t *) surface->data; - - if( srf->numVerts && srf->numTriangles) - { - srf->vbo = s_worldData.vbo; - srf->ibo = s_worldData.ibo; - } - } - else if(*surface->data == SF_TRIANGLES) - { - srfTriangles_t *srf = (srfTriangles_t *) surface->data; - - if( srf->numVerts && srf->numTriangles) - { - srf->vbo = s_worldData.vbo; - srf->ibo = s_worldData.ibo; - } - } - } - - - startTime = ri.Milliseconds(); - - ri.Free(surfacesSorted); - - ri.Hunk_FreeTempMemory(triangles); - ri.Hunk_FreeTempMemory(verts); -} - -/* -=============== -R_LoadSurfaces -=============== -*/ -static void R_LoadSurfaces( lump_t *surfs, lump_t *verts, lump_t *indexLump ) { - dsurface_t *in; - msurface_t *out; - drawVert_t *dv; - int *indexes; - int count; - int numFaces, numMeshes, numTriSurfs, numFlares; - int i; - float *hdrVertColors = NULL; - - numFaces = 0; - numMeshes = 0; - numTriSurfs = 0; - numFlares = 0; - - in = (void *)(fileBase + surfs->fileofs); - if (surfs->filelen % sizeof(*in)) - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - count = surfs->filelen / sizeof(*in); - - dv = (void *)(fileBase + verts->fileofs); - if (verts->filelen % sizeof(*dv)) - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - - indexes = (void *)(fileBase + indexLump->fileofs); - if ( indexLump->filelen % sizeof(*indexes)) - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - - out = ri.Hunk_Alloc ( count * sizeof(*out), h_low ); - - s_worldData.surfaces = out; - s_worldData.numsurfaces = count; - s_worldData.surfacesViewCount = ri.Hunk_Alloc ( count * sizeof(*s_worldData.surfacesViewCount), h_low ); - s_worldData.surfacesDlightBits = ri.Hunk_Alloc ( count * sizeof(*s_worldData.surfacesDlightBits), h_low ); - s_worldData.surfacesPshadowBits = ri.Hunk_Alloc ( count * sizeof(*s_worldData.surfacesPshadowBits), h_low ); - - // load hdr vertex colors - if (r_hdr->integer) - { - char filename[MAX_QPATH]; - int size; - - Com_sprintf( filename, sizeof( filename ), "maps/%s/vertlight.raw", s_worldData.baseName); - //ri.Printf(PRINT_ALL, "looking for %s\n", filename); - - size = ri.FS_ReadFile(filename, (void **)&hdrVertColors); - - if (hdrVertColors) - { - //ri.Printf(PRINT_ALL, "Found!\n"); - if (size != sizeof(float) * 3 * (verts->filelen / sizeof(*dv))) - ri.Error(ERR_DROP, "Bad size for %s (%i, expected %i)!\n", filename, size, (int)((sizeof(float)) * 3 * (verts->filelen / sizeof(*dv)))); - } - } - - - // Two passes, allocate surfaces first, then load them full of data - // This ensures surfaces are close together to reduce L2 cache misses when using VBOs, - // which don't actually use the verts and tris - in = (void *)(fileBase + surfs->fileofs); - out = s_worldData.surfaces; - for ( i = 0 ; i < count ; i++, in++, out++ ) { - switch ( LittleLong( in->surfaceType ) ) { - case MST_PATCH: - // FIXME: do this - break; - case MST_TRIANGLE_SOUP: - out->data = ri.Hunk_Alloc( sizeof(srfTriangles_t), h_low); - break; - case MST_PLANAR: - out->data = ri.Hunk_Alloc( sizeof(srfSurfaceFace_t), h_low); - break; - case MST_FLARE: - out->data = ri.Hunk_Alloc( sizeof(srfFlare_t), h_low); - break; - default: - break; - } - } - - in = (void *)(fileBase + surfs->fileofs); - out = s_worldData.surfaces; - for ( i = 0 ; i < count ; i++, in++, out++ ) { - switch ( LittleLong( in->surfaceType ) ) { - case MST_PATCH: - ParseMesh ( in, dv, hdrVertColors, out ); - { - srfGridMesh_t *surface = (srfGridMesh_t *)out->data; - - out->cullinfo.type = CULLINFO_BOX | CULLINFO_SPHERE; - VectorCopy(surface->meshBounds[0], out->cullinfo.bounds[0]); - VectorCopy(surface->meshBounds[1], out->cullinfo.bounds[1]); - VectorCopy(surface->localOrigin, out->cullinfo.localOrigin); - out->cullinfo.radius = surface->meshRadius; - } - numMeshes++; - break; - case MST_TRIANGLE_SOUP: - ParseTriSurf( in, dv, hdrVertColors, out, indexes ); - numTriSurfs++; - break; - case MST_PLANAR: - ParseFace( in, dv, hdrVertColors, out, indexes ); - numFaces++; - break; - case MST_FLARE: - ParseFlare( in, dv, out, indexes ); - { - out->cullinfo.type = CULLINFO_NONE; - } - numFlares++; - break; - default: - ri.Error( ERR_DROP, "Bad surfaceType" ); - } - } - - if (hdrVertColors) - { - ri.FS_FreeFile(hdrVertColors); - } - -#ifdef PATCH_STITCHING - R_StitchAllPatches(); -#endif - - R_FixSharedVertexLodError(); - -#ifdef PATCH_STITCHING - R_MovePatchSurfacesToHunk(); -#endif - - ri.Printf( PRINT_ALL, "...loaded %d faces, %i meshes, %i trisurfs, %i flares\n", - numFaces, numMeshes, numTriSurfs, numFlares ); -} - - - -/* -================= -R_LoadSubmodels -================= -*/ -static void R_LoadSubmodels( lump_t *l ) { - dmodel_t *in; - bmodel_t *out; - int i, j, count; - - in = (void *)(fileBase + l->fileofs); - if (l->filelen % sizeof(*in)) - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - count = l->filelen / sizeof(*in); - - s_worldData.numBModels = count; - s_worldData.bmodels = out = ri.Hunk_Alloc( count * sizeof(*out), h_low ); - - for ( i=0 ; itype = MOD_BRUSH; - model->bmodel = out; - Com_sprintf( model->name, sizeof( model->name ), "*%d", i ); - - for (j=0 ; j<3 ; j++) { - out->bounds[0][j] = LittleFloat (in->mins[j]); - out->bounds[1][j] = LittleFloat (in->maxs[j]); - } - - out->firstSurface = LittleLong( in->firstSurface ); - out->numSurfaces = LittleLong( in->numSurfaces ); - - if(i == 0) - { - // Tr3B: add this for limiting VBO surface creation - s_worldData.numWorldSurfaces = out->numSurfaces; - } - } -} - - - -//================================================================== - -/* -================= -R_SetParent -================= -*/ -static void R_SetParent (mnode_t *node, mnode_t *parent) -{ - node->parent = parent; - if (node->contents != -1) - return; - R_SetParent (node->children[0], node); - R_SetParent (node->children[1], node); -} - -/* -================= -R_LoadNodesAndLeafs -================= -*/ -static void R_LoadNodesAndLeafs (lump_t *nodeLump, lump_t *leafLump) { - int i, j, p; - dnode_t *in; - dleaf_t *inLeaf; - mnode_t *out; - int numNodes, numLeafs; - - in = (void *)(fileBase + nodeLump->fileofs); - if (nodeLump->filelen % sizeof(dnode_t) || - leafLump->filelen % sizeof(dleaf_t) ) { - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - } - numNodes = nodeLump->filelen / sizeof(dnode_t); - numLeafs = leafLump->filelen / sizeof(dleaf_t); - - out = ri.Hunk_Alloc ( (numNodes + numLeafs) * sizeof(*out), h_low); - - s_worldData.nodes = out; - s_worldData.numnodes = numNodes + numLeafs; - s_worldData.numDecisionNodes = numNodes; - - // load nodes - for ( i=0 ; imins[j] = LittleLong (in->mins[j]); - out->maxs[j] = LittleLong (in->maxs[j]); - } - - p = LittleLong(in->planeNum); - out->plane = s_worldData.planes + p; - - out->contents = CONTENTS_NODE; // differentiate from leafs - - for (j=0 ; j<2 ; j++) - { - p = LittleLong (in->children[j]); - if (p >= 0) - out->children[j] = s_worldData.nodes + p; - else - out->children[j] = s_worldData.nodes + numNodes + (-1 - p); - } - } - - // load leafs - inLeaf = (void *)(fileBase + leafLump->fileofs); - for ( i=0 ; imins[j] = LittleLong (inLeaf->mins[j]); - out->maxs[j] = LittleLong (inLeaf->maxs[j]); - } - - out->cluster = LittleLong(inLeaf->cluster); - out->area = LittleLong(inLeaf->area); - - if ( out->cluster >= s_worldData.numClusters ) { - s_worldData.numClusters = out->cluster + 1; - } - - out->firstmarksurface = LittleLong(inLeaf->firstLeafSurface); - out->nummarksurfaces = LittleLong(inLeaf->numLeafSurfaces); - } - - // chain decendants - R_SetParent (s_worldData.nodes, NULL); -} - -//============================================================================= - -/* -================= -R_LoadShaders -================= -*/ -static void R_LoadShaders( lump_t *l ) { - int i, count; - dshader_t *in, *out; - - in = (void *)(fileBase + l->fileofs); - if (l->filelen % sizeof(*in)) - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - count = l->filelen / sizeof(*in); - out = ri.Hunk_Alloc ( count*sizeof(*out), h_low ); - - s_worldData.shaders = out; - s_worldData.numShaders = count; - - Com_Memcpy( out, in, count*sizeof(*out) ); - - for ( i=0 ; ifileofs); - if (l->filelen % sizeof(*in)) - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - count = l->filelen / sizeof(*in); - out = ri.Hunk_Alloc ( count*sizeof(*out), h_low); - - s_worldData.marksurfaces = out; - s_worldData.nummarksurfaces = count; - - for ( i=0 ; ifileofs); - if (l->filelen % sizeof(*in)) - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - count = l->filelen / sizeof(*in); - out = ri.Hunk_Alloc ( count*2*sizeof(*out), h_low); - - s_worldData.planes = out; - s_worldData.numplanes = count; - - for ( i=0 ; inormal[j] = LittleFloat (in->normal[j]); - if (out->normal[j] < 0) { - bits |= 1<dist = LittleFloat (in->dist); - out->type = PlaneTypeForNormal( out->normal ); - out->signbits = bits; - } -} - -/* -================= -R_LoadFogs - -================= -*/ -static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) { - int i; - fog_t *out; - dfog_t *fogs; - dbrush_t *brushes, *brush; - dbrushside_t *sides; - int count, brushesCount, sidesCount; - int sideNum; - int planeNum; - shader_t *shader; - float d; - int firstSide; - - fogs = (void *)(fileBase + l->fileofs); - if (l->filelen % sizeof(*fogs)) { - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - } - count = l->filelen / sizeof(*fogs); - - // create fog strucutres for them - s_worldData.numfogs = count + 1; - s_worldData.fogs = ri.Hunk_Alloc ( s_worldData.numfogs*sizeof(*out), h_low); - out = s_worldData.fogs + 1; - - if ( !count ) { - return; - } - - brushes = (void *)(fileBase + brushesLump->fileofs); - if (brushesLump->filelen % sizeof(*brushes)) { - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - } - brushesCount = brushesLump->filelen / sizeof(*brushes); - - sides = (void *)(fileBase + sidesLump->fileofs); - if (sidesLump->filelen % sizeof(*sides)) { - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - } - sidesCount = sidesLump->filelen / sizeof(*sides); - - for ( i=0 ; ioriginalBrushNumber = LittleLong( fogs->brushNum ); - - if ( (unsigned)out->originalBrushNumber >= brushesCount ) { - ri.Error( ERR_DROP, "fog brushNumber out of range" ); - } - brush = brushes + out->originalBrushNumber; - - firstSide = LittleLong( brush->firstSide ); - - if ( (unsigned)firstSide > sidesCount - 6 ) { - ri.Error( ERR_DROP, "fog brush sideNumber out of range" ); - } - - // brushes are always sorted with the axial sides first - sideNum = firstSide + 0; - planeNum = LittleLong( sides[ sideNum ].planeNum ); - out->bounds[0][0] = -s_worldData.planes[ planeNum ].dist; - - sideNum = firstSide + 1; - planeNum = LittleLong( sides[ sideNum ].planeNum ); - out->bounds[1][0] = s_worldData.planes[ planeNum ].dist; - - sideNum = firstSide + 2; - planeNum = LittleLong( sides[ sideNum ].planeNum ); - out->bounds[0][1] = -s_worldData.planes[ planeNum ].dist; - - sideNum = firstSide + 3; - planeNum = LittleLong( sides[ sideNum ].planeNum ); - out->bounds[1][1] = s_worldData.planes[ planeNum ].dist; - - sideNum = firstSide + 4; - planeNum = LittleLong( sides[ sideNum ].planeNum ); - out->bounds[0][2] = -s_worldData.planes[ planeNum ].dist; - - sideNum = firstSide + 5; - planeNum = LittleLong( sides[ sideNum ].planeNum ); - out->bounds[1][2] = s_worldData.planes[ planeNum ].dist; - - // get information from the shader for fog parameters - shader = R_FindShader( fogs->shader, LIGHTMAP_NONE, qtrue ); - - out->parms = shader->fogParms; - - out->colorInt = ColorBytes4 ( shader->fogParms.color[0] * tr.identityLight, - shader->fogParms.color[1] * tr.identityLight, - shader->fogParms.color[2] * tr.identityLight, 1.0 ); - - d = shader->fogParms.depthForOpaque < 1 ? 1 : shader->fogParms.depthForOpaque; - out->tcScale = 1.0f / ( d * 8 ); - - // set the gradient vector - sideNum = LittleLong( fogs->visibleSide ); - - if ( sideNum == -1 ) { - out->hasSurface = qfalse; - } else { - out->hasSurface = qtrue; - planeNum = LittleLong( sides[ firstSide + sideNum ].planeNum ); - VectorSubtract( vec3_origin, s_worldData.planes[ planeNum ].normal, out->surface ); - out->surface[3] = -s_worldData.planes[ planeNum ].dist; - } - - out++; - } - -} - - -/* -================ -R_LoadLightGrid - -================ -*/ -void R_LoadLightGrid( lump_t *l ) { - int i; - vec3_t maxs; - int numGridPoints; - world_t *w; - float *wMins, *wMaxs; - - w = &s_worldData; - - w->lightGridInverseSize[0] = 1.0f / w->lightGridSize[0]; - w->lightGridInverseSize[1] = 1.0f / w->lightGridSize[1]; - w->lightGridInverseSize[2] = 1.0f / w->lightGridSize[2]; - - wMins = w->bmodels[0].bounds[0]; - wMaxs = w->bmodels[0].bounds[1]; - - for ( i = 0 ; i < 3 ; i++ ) { - w->lightGridOrigin[i] = w->lightGridSize[i] * ceil( wMins[i] / w->lightGridSize[i] ); - maxs[i] = w->lightGridSize[i] * floor( wMaxs[i] / w->lightGridSize[i] ); - w->lightGridBounds[i] = (maxs[i] - w->lightGridOrigin[i])/w->lightGridSize[i] + 1; - } - - numGridPoints = w->lightGridBounds[0] * w->lightGridBounds[1] * w->lightGridBounds[2]; - - if ( l->filelen != numGridPoints * 8 ) { - ri.Printf( PRINT_WARNING, "WARNING: light grid mismatch\n" ); - w->lightGridData = NULL; - return; - } - - w->lightGridData = ri.Hunk_Alloc( l->filelen, h_low ); - Com_Memcpy( w->lightGridData, (void *)(fileBase + l->fileofs), l->filelen ); - - // deal with overbright bits - for ( i = 0 ; i < numGridPoints ; i++ ) { - R_ColorShiftLightingBytes( &w->lightGridData[i*8], &w->lightGridData[i*8] ); - R_ColorShiftLightingBytes( &w->lightGridData[i*8+3], &w->lightGridData[i*8+3] ); - } - - // load hdr lightgrid - if (r_hdr->integer) - { - char filename[MAX_QPATH]; - float *hdrLightGrid; - int size; - - Com_sprintf( filename, sizeof( filename ), "maps/%s/lightgrid.raw", s_worldData.baseName); - //ri.Printf(PRINT_ALL, "looking for %s\n", filename); - - size = ri.FS_ReadFile(filename, (void **)&hdrLightGrid); - - if (hdrLightGrid) - { - float lightScale = pow(2, r_mapOverBrightBits->integer - tr.overbrightBits); - - //ri.Printf(PRINT_ALL, "found!\n"); - - if (size != sizeof(float) * 6 * numGridPoints) - { - ri.Error(ERR_DROP, "Bad size for %s (%i, expected %i)!\n", filename, size, (int)(sizeof(float)) * 6 * numGridPoints); - } - - w->hdrLightGrid = ri.Hunk_Alloc(size, h_low); - - for (i = 0; i < numGridPoints ; i++) - { - w->hdrLightGrid[i * 6 ] = hdrLightGrid[i * 6 ] * lightScale; - w->hdrLightGrid[i * 6 + 1] = hdrLightGrid[i * 6 + 1] * lightScale; - w->hdrLightGrid[i * 6 + 2] = hdrLightGrid[i * 6 + 2] * lightScale; - w->hdrLightGrid[i * 6 + 3] = hdrLightGrid[i * 6 + 3] * lightScale; - w->hdrLightGrid[i * 6 + 4] = hdrLightGrid[i * 6 + 4] * lightScale; - w->hdrLightGrid[i * 6 + 5] = hdrLightGrid[i * 6 + 5] * lightScale; - } - } - - if (hdrLightGrid) - ri.FS_FreeFile(hdrLightGrid); - } -} - -/* -================ -R_LoadEntities -================ -*/ -void R_LoadEntities( lump_t *l ) { - char *p, *token, *s; - char keyname[MAX_TOKEN_CHARS]; - char value[MAX_TOKEN_CHARS]; - world_t *w; - - w = &s_worldData; - w->lightGridSize[0] = 64; - w->lightGridSize[1] = 64; - w->lightGridSize[2] = 128; - - p = (char *)(fileBase + l->fileofs); - - // store for reference by the cgame - w->entityString = ri.Hunk_Alloc( l->filelen + 1, h_low ); - strcpy( w->entityString, p ); - w->entityParsePoint = w->entityString; - - token = COM_ParseExt( &p, qtrue ); - if (!*token || *token != '{') { - return; - } - - // only parse the world spawn - while ( 1 ) { - // parse key - token = COM_ParseExt( &p, qtrue ); - - if ( !*token || *token == '}' ) { - break; - } - Q_strncpyz(keyname, token, sizeof(keyname)); - - // parse value - token = COM_ParseExt( &p, qtrue ); - - if ( !*token || *token == '}' ) { - break; - } - Q_strncpyz(value, token, sizeof(value)); - - // check for remapping of shaders for vertex lighting - s = "vertexremapshader"; - if (!Q_strncmp(keyname, s, strlen(s)) ) { - s = strchr(value, ';'); - if (!s) { - ri.Printf( PRINT_WARNING, "WARNING: no semi colon in vertexshaderremap '%s'\n", value ); - break; - } - *s++ = 0; - if (r_vertexLight->integer) { - R_RemapShader(value, s, "0"); - } - continue; - } - // check for remapping of shaders - s = "remapshader"; - if (!Q_strncmp(keyname, s, strlen(s)) ) { - s = strchr(value, ';'); - if (!s) { - ri.Printf( PRINT_WARNING, "WARNING: no semi colon in shaderremap '%s'\n", value ); - break; - } - *s++ = 0; - R_RemapShader(value, s, "0"); - continue; - } - // check for a different grid size - if (!Q_stricmp(keyname, "gridsize")) { - sscanf(value, "%f %f %f", &w->lightGridSize[0], &w->lightGridSize[1], &w->lightGridSize[2] ); - continue; - } - - // check for auto exposure - if (!Q_stricmp(keyname, "autoExposureMinMax")) { - sscanf(value, "%f %f", &tr.autoExposureMinMax[0], &tr.autoExposureMinMax[1]); - continue; - } - } -} - -/* -================= -R_GetEntityToken -================= -*/ -qboolean R_GetEntityToken( char *buffer, int size ) { - const char *s; - - s = COM_Parse( &s_worldData.entityParsePoint ); - Q_strncpyz( buffer, s, size ); - if ( !s_worldData.entityParsePoint || !s[0] ) { - s_worldData.entityParsePoint = s_worldData.entityString; - return qfalse; - } else { - return qtrue; - } -} - - -/* -================= -R_MergeLeafSurfaces - -Merges surfaces that share a common leaf -================= -*/ -void R_MergeLeafSurfaces(void) -{ - int i, j, k; - int numWorldSurfaces; - int mergedSurfIndex; - int numMergedSurfaces; - int numUnmergedSurfaces; - IBO_t *ibo; - - msurface_t *mergedSurf; - - glIndex_t *iboIndexes, *outIboIndexes; - int numIboIndexes; - - int startTime, endTime; - - startTime = ri.Milliseconds(); - - numWorldSurfaces = s_worldData.numWorldSurfaces; - - // use viewcount to keep track of mergers - for (i = 0; i < numWorldSurfaces; i++) - { - s_worldData.surfacesViewCount[i] = -1; - } - - // create ibo - ibo = tr.ibos[tr.numIBOs++] = ri.Hunk_Alloc(sizeof(*ibo), h_low); - memset(ibo, 0, sizeof(*ibo)); - Q_strncpyz(ibo->name, "staticWorldMesh_IBO_mergedSurfs", sizeof(ibo->name)); - - // allocate more than we need - iboIndexes = outIboIndexes = ri.Malloc(s_worldData.ibo->indexesSize); - - // mark matching surfaces - for (i = 0; i < s_worldData.numnodes - s_worldData.numDecisionNodes; i++) - { - mnode_t *leaf = s_worldData.nodes + s_worldData.numDecisionNodes + i; - - for (j = 0; j < leaf->nummarksurfaces; j++) - { - msurface_t *surf1; - shader_t *shader1; - int fogIndex1; - int surfNum1; - - surfNum1 = *(s_worldData.marksurfaces + leaf->firstmarksurface + j); - - if (s_worldData.surfacesViewCount[surfNum1] != -1) - continue; - - surf1 = s_worldData.surfaces + surfNum1; - - if ((*surf1->data != SF_GRID) && (*surf1->data != SF_TRIANGLES) && (*surf1->data != SF_FACE)) - continue; - - shader1 = surf1->shader; - - if(shader1->isSky) - continue; - - if(shader1->isPortal) - continue; - - if(ShaderRequiresCPUDeforms(shader1)) - continue; - - fogIndex1 = surf1->fogIndex; - - s_worldData.surfacesViewCount[surfNum1] = surfNum1; - - for (k = j + 1; k < leaf->nummarksurfaces; k++) - { - msurface_t *surf2; - shader_t *shader2; - int fogIndex2; - int surfNum2; - - surfNum2 = *(s_worldData.marksurfaces + leaf->firstmarksurface + k); - - if (s_worldData.surfacesViewCount[surfNum2] != -1) - continue; - - surf2 = s_worldData.surfaces + surfNum2; - - if ((*surf2->data != SF_GRID) && (*surf2->data != SF_TRIANGLES) && (*surf2->data != SF_FACE)) - continue; - - shader2 = surf2->shader; - - if (shader1 != shader2) - continue; - - fogIndex2 = surf2->fogIndex; - - if (fogIndex1 != fogIndex2) - continue; - - s_worldData.surfacesViewCount[surfNum2] = surfNum1; - } - } - } - - // don't add surfaces that don't merge to any others to the merged list - for (i = 0; i < numWorldSurfaces; i++) - { - qboolean merges = qfalse; - - if (s_worldData.surfacesViewCount[i] != i) - continue; - - for (j = 0; j < numWorldSurfaces; j++) - { - if (j == i) - continue; - - if (s_worldData.surfacesViewCount[j] == i) - { - merges = qtrue; - break; - } - } - - if (!merges) - s_worldData.surfacesViewCount[i] = -1; - } - - // count merged/unmerged surfaces - numMergedSurfaces = 0; - numUnmergedSurfaces = 0; - for (i = 0; i < numWorldSurfaces; i++) - { - if (s_worldData.surfacesViewCount[i] == i) - { - numMergedSurfaces++; - } - else if (s_worldData.surfacesViewCount[i] == -1) - { - numUnmergedSurfaces++; - } - } - - // Allocate merged surfaces - s_worldData.mergedSurfaces = ri.Hunk_Alloc(sizeof(*s_worldData.mergedSurfaces) * numMergedSurfaces, h_low); - s_worldData.mergedSurfacesViewCount = ri.Hunk_Alloc(sizeof(*s_worldData.mergedSurfacesViewCount) * numMergedSurfaces, h_low); - s_worldData.mergedSurfacesDlightBits = ri.Hunk_Alloc(sizeof(*s_worldData.mergedSurfacesDlightBits) * numMergedSurfaces, h_low); - s_worldData.mergedSurfacesPshadowBits = ri.Hunk_Alloc(sizeof(*s_worldData.mergedSurfacesPshadowBits) * numMergedSurfaces, h_low); - s_worldData.numMergedSurfaces = numMergedSurfaces; - - // view surfaces are like mark surfaces, except negative ones represent merged surfaces - // -1 represents 0, -2 represents 1, and so on - s_worldData.viewSurfaces = ri.Hunk_Alloc(sizeof(*s_worldData.viewSurfaces) * s_worldData.nummarksurfaces, h_low); - - // copy view surfaces into mark surfaces - for (i = 0; i < s_worldData.nummarksurfaces; i++) - { - s_worldData.viewSurfaces[i] = s_worldData.marksurfaces[i]; - } - - // actually merge surfaces - numIboIndexes = 0; - mergedSurfIndex = 0; - mergedSurf = s_worldData.mergedSurfaces; - for (i = 0; i < numWorldSurfaces; i++) - { - msurface_t *surf1; - - vec3_t bounds[2]; - - int numSurfsToMerge; - int numTriangles; - int numVerts; - int firstIndex; - - srfVBOMesh_t *vboSurf; - - if (s_worldData.surfacesViewCount[i] != i) - continue; - - surf1 = s_worldData.surfaces + i; - - // count verts, indexes, and surfaces - numSurfsToMerge = 0; - numTriangles = 0; - numVerts = 0; - for (j = 0; j < numWorldSurfaces; j++) - { - msurface_t *surf2; - - if (s_worldData.surfacesViewCount[j] != i) - continue; - - surf2 = s_worldData.surfaces + j; - - switch(*surf2->data) - { - case SF_FACE: - { - srfSurfaceFace_t *face; - - face = (srfSurfaceFace_t *) surf2->data; - numTriangles += face->numTriangles; - numVerts += face->numVerts; - } - break; - - case SF_GRID: - { - srfGridMesh_t *grid; - - grid = (srfGridMesh_t *) surf2->data; - numTriangles += grid->numTriangles; - numVerts += grid->numVerts; - } - break; - - case SF_TRIANGLES: - { - srfTriangles_t *tris; - - tris = (srfTriangles_t *) surf2->data; - numTriangles += tris->numTriangles; - numVerts += tris->numVerts; - } - break; - - default: - break; - } - - numSurfsToMerge++; - } - - if (numVerts == 0 || numTriangles == 0 || numSurfsToMerge < 2) - { - continue; - } - - // Merge surfaces (indexes) and calculate bounds - ClearBounds(bounds[0], bounds[1]); - firstIndex = numIboIndexes; - for (j = 0; j < numWorldSurfaces; j++) - { - msurface_t *surf2; - - if (s_worldData.surfacesViewCount[j] != i) - continue; - - surf2 = s_worldData.surfaces + j; - - AddPointToBounds(surf2->cullinfo.bounds[0], bounds[0], bounds[1]); - AddPointToBounds(surf2->cullinfo.bounds[1], bounds[0], bounds[1]); - - switch(*surf2->data) - { - case SF_FACE: - { - srfSurfaceFace_t *face; - - face = (srfSurfaceFace_t *) surf2->data; - - for (k = 0; k < face->numTriangles; k++) - { - *outIboIndexes++ = face->triangles[k].indexes[0] + face->firstVert; - *outIboIndexes++ = face->triangles[k].indexes[1] + face->firstVert; - *outIboIndexes++ = face->triangles[k].indexes[2] + face->firstVert; - numIboIndexes += 3; - } - } - break; - - case SF_GRID: - { - srfGridMesh_t *grid; - - grid = (srfGridMesh_t *) surf2->data; - - for (k = 0; k < grid->numTriangles; k++) - { - *outIboIndexes++ = grid->triangles[k].indexes[0] + grid->firstVert; - *outIboIndexes++ = grid->triangles[k].indexes[1] + grid->firstVert; - *outIboIndexes++ = grid->triangles[k].indexes[2] + grid->firstVert; - numIboIndexes += 3; - } - } - break; - - case SF_TRIANGLES: - { - srfTriangles_t *tris; - - tris = (srfTriangles_t *) surf2->data; - - for (k = 0; k < tris->numTriangles; k++) - { - *outIboIndexes++ = tris->triangles[k].indexes[0] + tris->firstVert; - *outIboIndexes++ = tris->triangles[k].indexes[1] + tris->firstVert; - *outIboIndexes++ = tris->triangles[k].indexes[2] + tris->firstVert; - numIboIndexes += 3; - } - } - break; - - // never happens, but silences a compile warning - default: - break; - } - } - - vboSurf = ri.Hunk_Alloc(sizeof(*vboSurf), h_low); - memset(vboSurf, 0, sizeof(*vboSurf)); - vboSurf->surfaceType = SF_VBO_MESH; - - vboSurf->vbo = s_worldData.vbo; - vboSurf->ibo = ibo; - - vboSurf->numIndexes = numTriangles * 3; - vboSurf->numVerts = numVerts; - vboSurf->firstIndex = firstIndex; - - vboSurf->minIndex = *(iboIndexes + firstIndex); - vboSurf->maxIndex = *(iboIndexes + firstIndex); - - for (j = 1; j < numTriangles * 3; j++) - { - vboSurf->minIndex = MIN(vboSurf->minIndex, *(iboIndexes + firstIndex + j)); - vboSurf->maxIndex = MAX(vboSurf->maxIndex, *(iboIndexes + firstIndex + j)); - } - - vboSurf->shader = surf1->shader; - vboSurf->fogIndex = surf1->fogIndex; - - VectorCopy(bounds[0], vboSurf->bounds[0]); - VectorCopy(bounds[1], vboSurf->bounds[1]); - - VectorCopy(bounds[0], mergedSurf->cullinfo.bounds[0]); - VectorCopy(bounds[1], mergedSurf->cullinfo.bounds[1]); - - mergedSurf->cullinfo.type = CULLINFO_BOX; - mergedSurf->data = (surfaceType_t *)vboSurf; - mergedSurf->fogIndex = surf1->fogIndex; - mergedSurf->shader = surf1->shader; - - // redirect view surfaces to this surf - for (j = 0; j < numWorldSurfaces; j++) - { - if (s_worldData.surfacesViewCount[j] != i) - continue; - - for (k = 0; k < s_worldData.nummarksurfaces; k++) - { - int *mark = s_worldData.marksurfaces + k; - int *view = s_worldData.viewSurfaces + k; - - if (*mark == j) - *view = -(mergedSurfIndex + 1); - } - } - - mergedSurfIndex++; - mergedSurf++; - } - - // finish up the ibo - R_IssuePendingRenderCommands(); - - qglGenBuffersARB(1, &ibo->indexesVBO); - - R_BindIBO(ibo); - - qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, numIboIndexes * sizeof(*iboIndexes), iboIndexes, GL_STATIC_DRAW_ARB); - - R_BindNullIBO(); - - GL_CheckErrors(); - - ri.Free(iboIndexes); - - endTime = ri.Milliseconds(); - - ri.Printf(PRINT_ALL, "Processed %d surfaces into %d merged, %d unmerged in %5.2f seconds\n", - numWorldSurfaces, numMergedSurfaces, numUnmergedSurfaces, (endTime - startTime) / 1000.0f); - - // reset viewcounts - for (i = 0; i < numWorldSurfaces; i++) - { - s_worldData.surfacesViewCount[i] = -1; - } -} - - -void R_CalcVertexLightDirs( void ) -{ - int i, k; - msurface_t *surface; - - for(k = 0, surface = &s_worldData.surfaces[0]; k < s_worldData.numsurfaces /* s_worldData.numWorldSurfaces */; k++, surface++) - { - if(*surface->data == SF_FACE) - { - srfSurfaceFace_t *srf = (srfSurfaceFace_t *) surface->data; - - if(srf->numVerts) - { - for(i = 0; i < srf->numVerts; i++) - { - R_LightDirForPoint( srf->verts[i].xyz, srf->verts[i].lightdir, srf->verts[i].normal, &s_worldData ); - } - } - } - else if(*surface->data == SF_GRID) - { - srfGridMesh_t *srf = (srfGridMesh_t *) surface->data; - - if(srf->numVerts) - { - for(i = 0; i < srf->numVerts; i++) - { - R_LightDirForPoint( srf->verts[i].xyz, srf->verts[i].lightdir, srf->verts[i].normal, &s_worldData ); - } - } - } - else if(*surface->data == SF_TRIANGLES) - { - srfTriangles_t *srf = (srfTriangles_t *) surface->data; - - if(srf->numVerts) - { - for(i = 0; i < srf->numVerts; i++) - { - R_LightDirForPoint( srf->verts[i].xyz, srf->verts[i].lightdir, srf->verts[i].normal, &s_worldData ); - } - } - } - } -} - - -/* -================= -RE_LoadWorldMap - -Called directly from cgame -================= -*/ -void RE_LoadWorldMap( const char *name ) { - int i; - dheader_t *header; - union { - byte *b; - void *v; - } buffer; - byte *startMarker; - - if ( tr.worldMapLoaded ) { - ri.Error( ERR_DROP, "ERROR: attempted to redundantly load world map" ); - } - - // set default map light scale - tr.mapLightScale = 1.0f; - - // set default sun direction to be used if it isn't - // overridden by a shader - tr.sunDirection[0] = 0.45f; - tr.sunDirection[1] = 0.3f; - tr.sunDirection[2] = 0.9f; - - VectorNormalize( tr.sunDirection ); - - // set default autoexposure settings - tr.autoExposureMinMax[0] = -2.0f; - tr.autoExposureMinMax[1] = 2.0f; - - // set default tone mapping settings - tr.toneMinAvgMaxLevel[0] = -8.0f; - tr.toneMinAvgMaxLevel[1] = -2.0f; - tr.toneMinAvgMaxLevel[2] = 0.0f; - - tr.worldMapLoaded = qtrue; - - // load it - ri.FS_ReadFile( name, &buffer.v ); - if ( !buffer.b ) { - ri.Error (ERR_DROP, "RE_LoadWorldMap: %s not found", name); - } - - // clear tr.world so if the level fails to load, the next - // try will not look at the partially loaded version - tr.world = NULL; - - Com_Memset( &s_worldData, 0, sizeof( s_worldData ) ); - Q_strncpyz( s_worldData.name, name, sizeof( s_worldData.name ) ); - - Q_strncpyz( s_worldData.baseName, COM_SkipPath( s_worldData.name ), sizeof( s_worldData.name ) ); - COM_StripExtension(s_worldData.baseName, s_worldData.baseName, sizeof(s_worldData.baseName)); - - startMarker = ri.Hunk_Alloc(0, h_low); - c_gridVerts = 0; - - header = (dheader_t *)buffer.b; - fileBase = (byte *)header; - - i = LittleLong (header->version); - if ( i != BSP_VERSION ) { - ri.Error (ERR_DROP, "RE_LoadWorldMap: %s has wrong version number (%i should be %i)", - name, i, BSP_VERSION); - } - - // swap all the lumps - for (i=0 ; ilumps[LUMP_ENTITIES] ); - R_LoadShaders( &header->lumps[LUMP_SHADERS] ); - R_LoadLightmaps( &header->lumps[LUMP_LIGHTMAPS], &header->lumps[LUMP_SURFACES] ); - R_LoadPlanes (&header->lumps[LUMP_PLANES]); - R_LoadFogs( &header->lumps[LUMP_FOGS], &header->lumps[LUMP_BRUSHES], &header->lumps[LUMP_BRUSHSIDES] ); - R_LoadSurfaces( &header->lumps[LUMP_SURFACES], &header->lumps[LUMP_DRAWVERTS], &header->lumps[LUMP_DRAWINDEXES] ); - R_LoadMarksurfaces (&header->lumps[LUMP_LEAFSURFACES]); - R_LoadNodesAndLeafs (&header->lumps[LUMP_NODES], &header->lumps[LUMP_LEAFS]); - R_LoadSubmodels (&header->lumps[LUMP_MODELS]); - R_LoadVisibility( &header->lumps[LUMP_VISIBILITY] ); - R_LoadLightGrid( &header->lumps[LUMP_LIGHTGRID] ); - - // determine vertex light directions - R_CalcVertexLightDirs(); - - // create static VBOS from the world - R_CreateWorldVBO(); - if (r_mergeLeafSurfaces->integer) - { - R_MergeLeafSurfaces(); - } - - s_worldData.dataSize = (byte *)ri.Hunk_Alloc(0, h_low) - startMarker; - - // only set tr.world now that we know the entire level has loaded properly - tr.world = &s_worldData; - - // make sure the VBO glState entries are safe - R_BindNullVBO(); - R_BindNullIBO(); - - ri.FS_FreeFile( buffer.v ); -} diff --git a/src/rend2/tr_cmds.c b/src/rend2/tr_cmds.c deleted file mode 100644 index 622b401a..00000000 --- a/src/rend2/tr_cmds.c +++ /dev/null @@ -1,668 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#include "tr_local.h" - -volatile renderCommandList_t *renderCommandList; - -/* -===================== -R_PerformanceCounters -===================== -*/ -void R_PerformanceCounters( void ) { - if ( !r_speeds->integer ) { - // clear the counters even if we aren't printing - Com_Memset( &tr.pc, 0, sizeof( tr.pc ) ); - Com_Memset( &backEnd.pc, 0, sizeof( backEnd.pc ) ); - return; - } - - if (r_speeds->integer == 1) { - ri.Printf (PRINT_ALL, "%i/%i/%i shaders/batches/surfs %i leafs %i verts %i/%i tris %.2f mtex %.2f dc\n", - backEnd.pc.c_shaders, backEnd.pc.c_surfBatches, backEnd.pc.c_surfaces, tr.pc.c_leafs, backEnd.pc.c_vertexes, - backEnd.pc.c_indexes/3, backEnd.pc.c_totalIndexes/3, - R_SumOfUsedImages()/(1000000.0f), backEnd.pc.c_overDraw / (float)(glConfig.vidWidth * glConfig.vidHeight) ); - } else if (r_speeds->integer == 2) { - ri.Printf (PRINT_ALL, "(patch) %i sin %i sclip %i sout %i bin %i bclip %i bout\n", - tr.pc.c_sphere_cull_patch_in, tr.pc.c_sphere_cull_patch_clip, tr.pc.c_sphere_cull_patch_out, - tr.pc.c_box_cull_patch_in, tr.pc.c_box_cull_patch_clip, tr.pc.c_box_cull_patch_out ); - ri.Printf (PRINT_ALL, "(md3) %i sin %i sclip %i sout %i bin %i bclip %i bout\n", - tr.pc.c_sphere_cull_md3_in, tr.pc.c_sphere_cull_md3_clip, tr.pc.c_sphere_cull_md3_out, - tr.pc.c_box_cull_md3_in, tr.pc.c_box_cull_md3_clip, tr.pc.c_box_cull_md3_out ); - } else if (r_speeds->integer == 3) { - ri.Printf (PRINT_ALL, "viewcluster: %i\n", tr.viewCluster ); - } else if (r_speeds->integer == 4) { - if ( backEnd.pc.c_dlightVertexes ) { - ri.Printf (PRINT_ALL, "dlight srf:%i culled:%i verts:%i tris:%i\n", - tr.pc.c_dlightSurfaces, tr.pc.c_dlightSurfacesCulled, - backEnd.pc.c_dlightVertexes, backEnd.pc.c_dlightIndexes / 3 ); - } - } - else if (r_speeds->integer == 5 ) - { - ri.Printf( PRINT_ALL, "zFar: %.0f\n", tr.viewParms.zFar ); - } - else if (r_speeds->integer == 6 ) - { - ri.Printf( PRINT_ALL, "flare adds:%i tests:%i renders:%i\n", - backEnd.pc.c_flareAdds, backEnd.pc.c_flareTests, backEnd.pc.c_flareRenders ); - } - else if (r_speeds->integer == 7 ) - { - ri.Printf( PRINT_ALL, "VBO draws: static %i dynamic %i\nMultidraws: %i merged %i\n", - backEnd.pc.c_staticVboDraws, backEnd.pc.c_dynamicVboDraws, backEnd.pc.c_multidraws, backEnd.pc.c_multidrawsMerged ); - ri.Printf( PRINT_ALL, "GLSL binds: %i draws: gen %i light %i fog %i dlight %i\n", - backEnd.pc.c_glslShaderBinds, backEnd.pc.c_genericDraws, backEnd.pc.c_lightallDraws, backEnd.pc.c_fogDraws, backEnd.pc.c_dlightDraws); - } - - Com_Memset( &tr.pc, 0, sizeof( tr.pc ) ); - Com_Memset( &backEnd.pc, 0, sizeof( backEnd.pc ) ); -} - - -/* -==================== -R_IssueRenderCommands -==================== -*/ -void R_IssueRenderCommands( qboolean runPerformanceCounters ) { - renderCommandList_t *cmdList; - - cmdList = &backEndData->commands; - assert(cmdList); - // add an end-of-list command - *(int *)(cmdList->cmds + cmdList->used) = RC_END_OF_LIST; - - // clear it out, in case this is a sync and not a buffer flip - cmdList->used = 0; - - if ( runPerformanceCounters ) { - R_PerformanceCounters(); - } - - // actually start the commands going - if ( !r_skipBackEnd->integer ) { - // let it start on the new batch - RB_ExecuteRenderCommands( cmdList->cmds ); - } -} - - -/* -==================== -R_IssuePendingRenderCommands - -Issue any pending commands and wait for them to complete. -==================== -*/ -void R_IssuePendingRenderCommands( void ) { - if ( !tr.registered ) { - return; - } - R_IssueRenderCommands( qfalse ); -} - -/* -============ -R_GetCommandBuffer - -make sure there is enough command space -============ -*/ -void *R_GetCommandBuffer( int bytes ) { - renderCommandList_t *cmdList; - - cmdList = &backEndData->commands; - bytes = PAD(bytes, sizeof(void *)); - - // always leave room for the end of list command - if ( cmdList->used + bytes + 4 > MAX_RENDER_COMMANDS ) { - if ( bytes > MAX_RENDER_COMMANDS - 4 ) { - ri.Error( ERR_FATAL, "R_GetCommandBuffer: bad size %i", bytes ); - } - // if we run out of room, just start dropping commands - return NULL; - } - - cmdList->used += bytes; - - return cmdList->cmds + cmdList->used - bytes; -} - - -/* -============= -R_AddDrawSurfCmd - -============= -*/ -void R_AddDrawSurfCmd( drawSurf_t *drawSurfs, int numDrawSurfs ) { - drawSurfsCommand_t *cmd; - - cmd = R_GetCommandBuffer( sizeof( *cmd ) ); - if ( !cmd ) { - return; - } - cmd->commandId = RC_DRAW_SURFS; - - cmd->drawSurfs = drawSurfs; - cmd->numDrawSurfs = numDrawSurfs; - - cmd->refdef = tr.refdef; - cmd->viewParms = tr.viewParms; -} - - -/* -============= -R_AddCapShadowmapCmd - -============= -*/ -void R_AddCapShadowmapCmd( int map, int cubeSide ) { - capShadowmapCommand_t *cmd; - - cmd = R_GetCommandBuffer( sizeof( *cmd ) ); - if ( !cmd ) { - return; - } - cmd->commandId = RC_CAPSHADOWMAP; - - cmd->map = map; - cmd->cubeSide = cubeSide; -} - - -/* -============= -R_PostProcessingCmd - -============= -*/ -void R_AddPostProcessCmd( ) { - postProcessCommand_t *cmd; - - cmd = R_GetCommandBuffer( sizeof( *cmd ) ); - if ( !cmd ) { - return; - } - cmd->commandId = RC_POSTPROCESS; - - cmd->refdef = tr.refdef; - cmd->viewParms = tr.viewParms; -} - -/* -============= -RE_SetColor - -Passing NULL will set the color to white -============= -*/ -void RE_SetColor( const float *rgba ) { - setColorCommand_t *cmd; - - if ( !tr.registered ) { - return; - } - cmd = R_GetCommandBuffer( sizeof( *cmd ) ); - if ( !cmd ) { - return; - } - cmd->commandId = RC_SET_COLOR; - if ( !rgba ) { - static float colorWhite[4] = { 1, 1, 1, 1 }; - - rgba = colorWhite; - } - - cmd->color[0] = rgba[0]; - cmd->color[1] = rgba[1]; - cmd->color[2] = rgba[2]; - cmd->color[3] = rgba[3]; -} - -/* -============= -R_ClipRegion -============= -*/ -static qboolean R_ClipRegion ( float *x, float *y, float *w, float *h, - float *s1, float *t1, float *s2, float *t2 ) { - float left, top, right, bottom; - float _s1, _t1, _s2, _t2; - float clipLeft, clipTop, clipRight, clipBottom; - - if (tr.clipRegion[2] <= tr.clipRegion[0] || - tr.clipRegion[3] <= tr.clipRegion[1] ) { - return qfalse; - } - - left = *x; - top = *y; - right = *x + *w; - bottom = *y + *h; - - _s1 = *s1; - _t1 = *t1; - _s2 = *s2; - _t2 = *t2; - - clipLeft = tr.clipRegion[0]; - clipTop = tr.clipRegion[1]; - clipRight = tr.clipRegion[2]; - clipBottom = tr.clipRegion[3]; - - // Completely clipped away - if ( right <= clipLeft || left >= clipRight || - bottom <= clipTop || top >= clipBottom ) { - return qtrue; - } - - // Clip left edge - if ( left < clipLeft ) { - float f = ( clipLeft - left ) / ( right - left ); - *s1 = ( f * ( _s2 - _s1 ) ) + _s1; - *x = clipLeft; - *w -= ( clipLeft - left ); - } - - // Clip right edge - if ( right > clipRight ) { - float f = ( clipRight - right ) / ( left - right ); - *s2 = ( f * ( _s1 - _s2 ) ) + _s2; - *w = clipRight - *x; - } - - // Clip top edge - if ( top < clipTop ) { - float f = ( clipTop - top ) / ( bottom - top ); - *t1 = ( f * ( _t2 - _t1 ) ) + _t1; - *y = clipTop; - *h -= ( clipTop - top ); - } - - // Clip bottom edge - if ( bottom > clipBottom ) { - float f = ( clipBottom - bottom ) / ( top - bottom ); - *t2 = ( f * ( _t1 - _t2 ) ) + _t2; - *h = clipBottom - *y; - } - - return qfalse; -} - -/* -============= -RE_SetClipRegion -============= -*/ -void RE_SetClipRegion( const float *region ) { - if ( region == NULL ) { - Com_Memset( tr.clipRegion, 0, sizeof( vec4_t ) ); - } else { - Vector4Copy( region, tr.clipRegion ); - } -} - -/* -============= -RE_StretchPic -============= -*/ -void RE_StretchPic ( float x, float y, float w, float h, - float s1, float t1, float s2, float t2, qhandle_t hShader ) { - stretchPicCommand_t *cmd; - - if (!tr.registered) { - return; - } - if (R_ClipRegion(&x, &y, &w, &h, &s1, &t1, &s2, &t2)) { - return; - } - cmd = R_GetCommandBuffer( sizeof( *cmd ) ); - if ( !cmd ) { - return; - } - cmd->commandId = RC_STRETCH_PIC; - cmd->shader = R_GetShaderByHandle( hShader ); - cmd->x = x; - cmd->y = y; - cmd->w = w; - cmd->h = h; - cmd->s1 = s1; - cmd->t1 = t1; - cmd->s2 = s2; - cmd->t2 = t2; -} - -#define MODE_RED_CYAN 1 -#define MODE_RED_BLUE 2 -#define MODE_RED_GREEN 3 -#define MODE_GREEN_MAGENTA 4 -#define MODE_MAX MODE_GREEN_MAGENTA - -void R_SetColorMode(GLboolean *rgba, stereoFrame_t stereoFrame, int colormode) -{ - rgba[0] = rgba[1] = rgba[2] = rgba[3] = GL_TRUE; - - if(colormode > MODE_MAX) - { - if(stereoFrame == STEREO_LEFT) - stereoFrame = STEREO_RIGHT; - else if(stereoFrame == STEREO_RIGHT) - stereoFrame = STEREO_LEFT; - - colormode -= MODE_MAX; - } - - if(colormode == MODE_GREEN_MAGENTA) - { - if(stereoFrame == STEREO_LEFT) - rgba[0] = rgba[2] = GL_FALSE; - else if(stereoFrame == STEREO_RIGHT) - rgba[1] = GL_FALSE; - } - else - { - if(stereoFrame == STEREO_LEFT) - rgba[1] = rgba[2] = GL_FALSE; - else if(stereoFrame == STEREO_RIGHT) - { - rgba[0] = GL_FALSE; - - if(colormode == MODE_RED_BLUE) - rgba[1] = GL_FALSE; - else if(colormode == MODE_RED_GREEN) - rgba[2] = GL_FALSE; - } - } -} - - -/* -==================== -RE_BeginFrame - -If running in stereo, RE_BeginFrame will be called twice -for each RE_EndFrame -==================== -*/ -void RE_BeginFrame( stereoFrame_t stereoFrame ) { - drawBufferCommand_t *cmd = NULL; - colorMaskCommand_t *colcmd = NULL; - - if ( !tr.registered ) { - return; - } - glState.finishCalled = qfalse; - - tr.frameCount++; - tr.frameSceneNum = 0; - - // - // do overdraw measurement - // - if ( r_measureOverdraw->integer ) - { - if ( glConfig.stencilBits < 4 ) - { - ri.Printf( PRINT_ALL, "Warning: not enough stencil bits to measure overdraw: %d\n", glConfig.stencilBits ); - ri.Cvar_Set( "r_measureOverdraw", "0" ); - r_measureOverdraw->modified = qfalse; - } - else if ( r_shadows->integer == 2 ) - { - ri.Printf( PRINT_ALL, "Warning: stencil shadows and overdraw measurement are mutually exclusive\n" ); - ri.Cvar_Set( "r_measureOverdraw", "0" ); - r_measureOverdraw->modified = qfalse; - } - else - { - R_IssuePendingRenderCommands(); - qglEnable( GL_STENCIL_TEST ); - qglStencilMask( ~0U ); - qglClearStencil( 0U ); - qglStencilFunc( GL_ALWAYS, 0U, ~0U ); - qglStencilOp( GL_KEEP, GL_INCR, GL_INCR ); - } - r_measureOverdraw->modified = qfalse; - } - else - { - // this is only reached if it was on and is now off - if ( r_measureOverdraw->modified ) { - R_IssuePendingRenderCommands(); - qglDisable( GL_STENCIL_TEST ); - } - r_measureOverdraw->modified = qfalse; - } - - // - // texturemode stuff - // - if ( r_textureMode->modified ) { - R_IssuePendingRenderCommands(); - GL_TextureMode( r_textureMode->string ); - r_textureMode->modified = qfalse; - } - - // - // gamma stuff - // - if ( r_gamma->modified ) { - r_gamma->modified = qfalse; - - R_IssuePendingRenderCommands(); - R_SetColorMappings(); - } - - // check for errors - if ( !r_ignoreGLErrors->integer ) - { - int err; - - R_IssuePendingRenderCommands(); - if ((err = qglGetError()) != GL_NO_ERROR) - ri.Error(ERR_FATAL, "RE_BeginFrame() - glGetError() failed (0x%x)!", err); - } - - if (glConfig.stereoEnabled) { - if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) ) - return; - - cmd->commandId = RC_DRAW_BUFFER; - - if ( stereoFrame == STEREO_LEFT ) { - cmd->buffer = (int)GL_BACK_LEFT; - } else if ( stereoFrame == STEREO_RIGHT ) { - cmd->buffer = (int)GL_BACK_RIGHT; - } else { - ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame ); - } - } - else - { - if(r_anaglyphMode->integer) - { - if(r_anaglyphMode->modified) - { - // clear both, front and backbuffer. - qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - backEnd.colorMask[0] = GL_FALSE; - backEnd.colorMask[1] = GL_FALSE; - backEnd.colorMask[2] = GL_FALSE; - backEnd.colorMask[3] = GL_FALSE; - qglClearColor(0.0f, 0.0f, 0.0f, 1.0f); - - if (glRefConfig.framebufferObject) - { - // clear all framebuffers - if (tr.msaaResolveFbo) - { - FBO_Bind(tr.msaaResolveFbo); - qglClear(GL_COLOR_BUFFER_BIT); - } - - if (tr.renderFbo) - { - FBO_Bind(tr.renderFbo); - qglClear(GL_COLOR_BUFFER_BIT); - } - - if (tr.screenScratchFbo) - { - FBO_Bind(tr.screenScratchFbo); - qglClear(GL_COLOR_BUFFER_BIT); - } - - FBO_Bind(NULL); - } - - qglDrawBuffer(GL_FRONT); - qglClear(GL_COLOR_BUFFER_BIT); - qglDrawBuffer(GL_BACK); - qglClear(GL_COLOR_BUFFER_BIT); - - r_anaglyphMode->modified = qfalse; - } - - if(stereoFrame == STEREO_LEFT) - { - if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) ) - return; - - if( !(colcmd = R_GetCommandBuffer(sizeof(*colcmd))) ) - return; - } - else if(stereoFrame == STEREO_RIGHT) - { - clearDepthCommand_t *cldcmd; - - if( !(cldcmd = R_GetCommandBuffer(sizeof(*cldcmd))) ) - return; - - cldcmd->commandId = RC_CLEARDEPTH; - - if( !(colcmd = R_GetCommandBuffer(sizeof(*colcmd))) ) - return; - } - else - ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame ); - - R_SetColorMode(colcmd->rgba, stereoFrame, r_anaglyphMode->integer); - colcmd->commandId = RC_COLORMASK; - } - else - { - if(stereoFrame != STEREO_CENTER) - ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is disabled, but stereoFrame was %i", stereoFrame ); - - if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) ) - return; - } - - if(cmd) - { - cmd->commandId = RC_DRAW_BUFFER; - - if(r_anaglyphMode->modified) - { - qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - backEnd.colorMask[0] = 0; - backEnd.colorMask[1] = 0; - backEnd.colorMask[2] = 0; - backEnd.colorMask[3] = 0; - r_anaglyphMode->modified = qfalse; - } - - if (!Q_stricmp(r_drawBuffer->string, "GL_FRONT")) - cmd->buffer = (int)GL_FRONT; - else - cmd->buffer = (int)GL_BACK; - } - } - - tr.refdef.stereoFrame = stereoFrame; -} - - -/* -============= -RE_EndFrame - -Returns the number of msec spent in the back end -============= -*/ -void RE_EndFrame( int *frontEndMsec, int *backEndMsec ) { - swapBuffersCommand_t *cmd; - - if ( !tr.registered ) { - return; - } - cmd = R_GetCommandBuffer( sizeof( *cmd ) ); - if ( !cmd ) { - return; - } - cmd->commandId = RC_SWAP_BUFFERS; - - R_IssueRenderCommands( qtrue ); - - R_InitNextFrame(); - - if ( frontEndMsec ) { - *frontEndMsec = tr.frontEndMsec; - } - tr.frontEndMsec = 0; - if ( backEndMsec ) { - *backEndMsec = backEnd.pc.msec; - } - backEnd.pc.msec = 0; -} - -/* -============= -RE_TakeVideoFrame -============= -*/ -void RE_TakeVideoFrame( int width, int height, - byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg ) -{ - videoFrameCommand_t *cmd; - - if( !tr.registered ) { - return; - } - - cmd = R_GetCommandBuffer( sizeof( *cmd ) ); - if( !cmd ) { - return; - } - - cmd->commandId = RC_VIDEOFRAME; - - cmd->width = width; - cmd->height = height; - cmd->captureBuffer = captureBuffer; - cmd->encodeBuffer = encodeBuffer; - cmd->motionJpeg = motionJpeg; -} diff --git a/src/rend2/tr_curve.c b/src/rend2/tr_curve.c deleted file mode 100644 index 3d439257..00000000 --- a/src/rend2/tr_curve.c +++ /dev/null @@ -1,806 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "tr_local.h" - -/* - -This file does all of the processing necessary to turn a raw grid of points -read from the map file into a srfGridMesh_t ready for rendering. - -The level of detail solution is direction independent, based only on subdivided -distance from the true curve. - -Only a single entry point: - -srfGridMesh_t *R_SubdividePatchToGrid( int width, int height, - srfVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) { - -*/ - - -/* -============ -LerpDrawVert -============ -*/ -static void LerpDrawVert( srfVert_t *a, srfVert_t *b, srfVert_t *out ) { - out->xyz[0] = 0.5f * (a->xyz[0] + b->xyz[0]); - out->xyz[1] = 0.5f * (a->xyz[1] + b->xyz[1]); - out->xyz[2] = 0.5f * (a->xyz[2] + b->xyz[2]); - - out->st[0] = 0.5f * (a->st[0] + b->st[0]); - out->st[1] = 0.5f * (a->st[1] + b->st[1]); - - out->lightmap[0] = 0.5f * (a->lightmap[0] + b->lightmap[0]); - out->lightmap[1] = 0.5f * (a->lightmap[1] + b->lightmap[1]); - - out->vertexColors[0] = 0.5f * (a->vertexColors[0] + b->vertexColors[0]); - out->vertexColors[1] = 0.5f * (a->vertexColors[1] + b->vertexColors[1]); - out->vertexColors[2] = 0.5f * (a->vertexColors[2] + b->vertexColors[2]); - out->vertexColors[3] = 0.5f * (a->vertexColors[3] + b->vertexColors[3]); -} - -/* -============ -Transpose -============ -*/ -static void Transpose( int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { - int i, j; - srfVert_t temp; - - if ( width > height ) { - for ( i = 0 ; i < height ; i++ ) { - for ( j = i + 1 ; j < width ; j++ ) { - if ( j < height ) { - // swap the value - temp = ctrl[j][i]; - ctrl[j][i] = ctrl[i][j]; - ctrl[i][j] = temp; - } else { - // just copy - ctrl[j][i] = ctrl[i][j]; - } - } - } - } else { - for ( i = 0 ; i < width ; i++ ) { - for ( j = i + 1 ; j < height ; j++ ) { - if ( j < width ) { - // swap the value - temp = ctrl[i][j]; - ctrl[i][j] = ctrl[j][i]; - ctrl[j][i] = temp; - } else { - // just copy - ctrl[i][j] = ctrl[j][i]; - } - } - } - } - -} - - -/* -================= -MakeMeshNormals - -Handles all the complicated wrapping and degenerate cases -================= -*/ -static void MakeMeshNormals( int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { - int i, j, k, dist; - vec3_t normal; - vec3_t sum; - int count = 0; - vec3_t base; - vec3_t delta; - int x, y; - srfVert_t *dv; - vec3_t around[8], temp; - qboolean good[8]; - qboolean wrapWidth, wrapHeight; - float len; -static int neighbors[8][2] = { - {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1} - }; - - wrapWidth = qfalse; - for ( i = 0 ; i < height ; i++ ) { - VectorSubtract( ctrl[i][0].xyz, ctrl[i][width-1].xyz, delta ); - len = VectorLengthSquared( delta ); - if ( len > 1.0 ) { - break; - } - } - if ( i == height ) { - wrapWidth = qtrue; - } - - wrapHeight = qfalse; - for ( i = 0 ; i < width ; i++ ) { - VectorSubtract( ctrl[0][i].xyz, ctrl[height-1][i].xyz, delta ); - len = VectorLengthSquared( delta ); - if ( len > 1.0 ) { - break; - } - } - if ( i == width) { - wrapHeight = qtrue; - } - - - for ( i = 0 ; i < width ; i++ ) { - for ( j = 0 ; j < height ; j++ ) { - count = 0; - dv = &ctrl[j][i]; - VectorCopy( dv->xyz, base ); - for ( k = 0 ; k < 8 ; k++ ) { - VectorClear( around[k] ); - good[k] = qfalse; - - for ( dist = 1 ; dist <= 3 ; dist++ ) { - x = i + neighbors[k][0] * dist; - y = j + neighbors[k][1] * dist; - if ( wrapWidth ) { - if ( x < 0 ) { - x = width - 1 + x; - } else if ( x >= width ) { - x = 1 + x - width; - } - } - if ( wrapHeight ) { - if ( y < 0 ) { - y = height - 1 + y; - } else if ( y >= height ) { - y = 1 + y - height; - } - } - - if ( x < 0 || x >= width || y < 0 || y >= height ) { - break; // edge of patch - } - VectorSubtract( ctrl[y][x].xyz, base, temp ); - if ( VectorNormalize2( temp, temp ) == 0 ) { - continue; // degenerate edge, get more dist - } else { - good[k] = qtrue; - VectorCopy( temp, around[k] ); - break; // good edge - } - } - } - - VectorClear( sum ); - for ( k = 0 ; k < 8 ; k++ ) { - if ( !good[k] || !good[(k+1)&7] ) { - continue; // didn't get two points - } - CrossProduct( around[(k+1)&7], around[k], normal ); - if ( VectorNormalize2( normal, normal ) == 0 ) { - continue; - } - VectorAdd( normal, sum, sum ); - count++; - } - //if ( count == 0 ) { - // printf("bad normal\n"); - //} - VectorNormalize2( sum, dv->normal ); - } - } -} - -#ifdef USE_VERT_TANGENT_SPACE -static void MakeMeshTangentVectors(int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], int numTriangles, - srfTriangle_t triangles[(MAX_GRID_SIZE-1)*(MAX_GRID_SIZE-1)*2]) -{ - int i, j; - srfVert_t *dv[3]; - static srfVert_t ctrl2[MAX_GRID_SIZE * MAX_GRID_SIZE]; - srfTriangle_t *tri; - - // FIXME: use more elegant way - for(i = 0; i < width; i++) - { - for(j = 0; j < height; j++) - { - dv[0] = &ctrl2[j * width + i]; - *dv[0] = ctrl[j][i]; - } - } - - for(i = 0, tri = triangles; i < numTriangles; i++, tri++) - { - dv[0] = &ctrl2[tri->indexes[0]]; - dv[1] = &ctrl2[tri->indexes[1]]; - dv[2] = &ctrl2[tri->indexes[2]]; - - R_CalcTangentVectors(dv); - } - -#if 0 - for(i = 0; i < (width * height); i++) - { - dv0 = &ctrl2[i]; - - VectorNormalize(dv0->normal); -#if 0 - VectorNormalize(dv0->tangent); - VectorNormalize(dv0->bitangent); -#else - d = DotProduct(dv0->tangent, dv0->normal); - VectorMA(dv0->tangent, -d, dv0->normal, dv0->tangent); - VectorNormalize(dv0->tangent); - - d = DotProduct(dv0->bitangent, dv0->normal); - VectorMA(dv0->bitangent, -d, dv0->normal, dv0->bitangent); - VectorNormalize(dv0->bitangent); -#endif - } -#endif - - -#if 0 - // do another extra smoothing for normals to avoid flat shading - for(i = 0; i < (width * height); i++) - { - for(j = 0; j < (width * height); j++) - { - if(R_CompareVert(&ctrl2[i], &ctrl2[j], qfalse)) - { - VectorAdd(ctrl2[i].normal, ctrl2[j].normal, ctrl2[i].normal); - } - } - - VectorNormalize(ctrl2[i].normal); - } -#endif - - for(i = 0; i < width; i++) - { - for(j = 0; j < height; j++) - { - dv[0] = &ctrl2[j * width + i]; - dv[1] = &ctrl[j][i]; - - VectorCopy(dv[0]->tangent, dv[1]->tangent); - VectorCopy(dv[0]->bitangent, dv[1]->bitangent); - } - } -} -#endif - - -static int MakeMeshTriangles(int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], - srfTriangle_t triangles[(MAX_GRID_SIZE-1)*(MAX_GRID_SIZE-1)*2]) -{ - int i, j; - int numTriangles; - int w, h; - srfVert_t *dv; - static srfVert_t ctrl2[MAX_GRID_SIZE * MAX_GRID_SIZE]; - - h = height - 1; - w = width - 1; - numTriangles = 0; - for(i = 0; i < h; i++) - { - for(j = 0; j < w; j++) - { - int v1, v2, v3, v4; - - // vertex order to be reckognized as tristrips - v1 = i * width + j + 1; - v2 = v1 - 1; - v3 = v2 + width; - v4 = v3 + 1; - - triangles[numTriangles].indexes[0] = v2; - triangles[numTriangles].indexes[1] = v3; - triangles[numTriangles].indexes[2] = v1; - numTriangles++; - - triangles[numTriangles].indexes[0] = v1; - triangles[numTriangles].indexes[1] = v3; - triangles[numTriangles].indexes[2] = v4; - numTriangles++; - } - } - - R_CalcSurfaceTriangleNeighbors(numTriangles, triangles); - - // FIXME: use more elegant way - for(i = 0; i < width; i++) - { - for(j = 0; j < height; j++) - { - dv = &ctrl2[j * width + i]; - *dv = ctrl[j][i]; - } - } - - R_CalcSurfaceTrianglePlanes(numTriangles, triangles, ctrl2); - - return numTriangles; -} - - -/* -============ -InvertCtrl -============ -*/ -static void InvertCtrl( int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { - int i, j; - srfVert_t temp; - - for ( i = 0 ; i < height ; i++ ) { - for ( j = 0 ; j < width/2 ; j++ ) { - temp = ctrl[i][j]; - ctrl[i][j] = ctrl[i][width-1-j]; - ctrl[i][width-1-j] = temp; - } - } -} - - -/* -================= -InvertErrorTable -================= -*/ -static void InvertErrorTable( float errorTable[2][MAX_GRID_SIZE], int width, int height ) { - int i; - float copy[2][MAX_GRID_SIZE]; - - Com_Memcpy( copy, errorTable, sizeof( copy ) ); - - for ( i = 0 ; i < width ; i++ ) { - errorTable[1][i] = copy[0][i]; //[width-1-i]; - } - - for ( i = 0 ; i < height ; i++ ) { - errorTable[0][i] = copy[1][height-1-i]; - } - -} - -/* -================== -PutPointsOnCurve -================== -*/ -static void PutPointsOnCurve( srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], - int width, int height ) { - int i, j; - srfVert_t prev, next; - - for ( i = 0 ; i < width ; i++ ) { - for ( j = 1 ; j < height ; j += 2 ) { - LerpDrawVert( &ctrl[j][i], &ctrl[j+1][i], &prev ); - LerpDrawVert( &ctrl[j][i], &ctrl[j-1][i], &next ); - LerpDrawVert( &prev, &next, &ctrl[j][i] ); - } - } - - - for ( j = 0 ; j < height ; j++ ) { - for ( i = 1 ; i < width ; i += 2 ) { - LerpDrawVert( &ctrl[j][i], &ctrl[j][i+1], &prev ); - LerpDrawVert( &ctrl[j][i], &ctrl[j][i-1], &next ); - LerpDrawVert( &prev, &next, &ctrl[j][i] ); - } - } -} - -/* -================= -R_CreateSurfaceGridMesh -================= -*/ -srfGridMesh_t *R_CreateSurfaceGridMesh(int width, int height, - srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], float errorTable[2][MAX_GRID_SIZE], - int numTriangles, srfTriangle_t triangles[(MAX_GRID_SIZE-1)*(MAX_GRID_SIZE-1)*2]) { - int i, j, size; - srfVert_t *vert; - vec3_t tmpVec; - srfGridMesh_t *grid; - - // copy the results out to a grid - size = (width * height - 1) * sizeof( srfVert_t ) + sizeof( *grid ); - -#ifdef PATCH_STITCHING - grid = /*ri.Hunk_Alloc*/ ri.Malloc( size ); - Com_Memset(grid, 0, size); - - grid->widthLodError = /*ri.Hunk_Alloc*/ ri.Malloc( width * 4 ); - Com_Memcpy( grid->widthLodError, errorTable[0], width * 4 ); - - grid->heightLodError = /*ri.Hunk_Alloc*/ ri.Malloc( height * 4 ); - Com_Memcpy( grid->heightLodError, errorTable[1], height * 4 ); - - grid->numTriangles = numTriangles; - grid->triangles = ri.Malloc(grid->numTriangles * sizeof(srfTriangle_t)); - Com_Memcpy(grid->triangles, triangles, numTriangles * sizeof(srfTriangle_t)); - - grid->numVerts = (width * height); - grid->verts = ri.Malloc(grid->numVerts * sizeof(srfVert_t)); -#else - grid = ri.Hunk_Alloc( size ); - Com_Memset(grid, 0, size); - - grid->widthLodError = ri.Hunk_Alloc( width * 4 ); - Com_Memcpy( grid->widthLodError, errorTable[0], width * 4 ); - - grid->heightLodError = ri.Hunk_Alloc( height * 4 ); - Com_Memcpy( grid->heightLodError, errorTable[1], height * 4 ); - - grid->numTriangles = numTriangles; - grid->triangles = ri.Hunk_Alloc(grid->numTriangles * sizeof(srfTriangle_t), h_low); - Com_Memcpy(grid->triangles, triangles, numTriangles * sizeof(srfTriangle_t)); - - grid->numVerts = (width * height); - grid->verts = ri.Hunk_Alloc(grid->numVerts * sizeof(srfVert_t), h_low); -#endif - - grid->width = width; - grid->height = height; - grid->surfaceType = SF_GRID; - ClearBounds( grid->meshBounds[0], grid->meshBounds[1] ); - for ( i = 0 ; i < width ; i++ ) { - for ( j = 0 ; j < height ; j++ ) { - vert = &grid->verts[j*width+i]; - *vert = ctrl[j][i]; - AddPointToBounds( vert->xyz, grid->meshBounds[0], grid->meshBounds[1] ); - } - } - - // compute local origin and bounds - VectorAdd( grid->meshBounds[0], grid->meshBounds[1], grid->localOrigin ); - VectorScale( grid->localOrigin, 0.5f, grid->localOrigin ); - VectorSubtract( grid->meshBounds[0], grid->localOrigin, tmpVec ); - grid->meshRadius = VectorLength( tmpVec ); - - VectorCopy( grid->localOrigin, grid->lodOrigin ); - grid->lodRadius = grid->meshRadius; - // - return grid; -} - -/* -================= -R_FreeSurfaceGridMesh -================= -*/ -void R_FreeSurfaceGridMesh( srfGridMesh_t *grid ) { - ri.Free(grid->widthLodError); - ri.Free(grid->heightLodError); - ri.Free(grid->triangles); - ri.Free(grid->verts); - ri.Free(grid); -} - -/* -================= -R_SubdividePatchToGrid -================= -*/ -srfGridMesh_t *R_SubdividePatchToGrid( int width, int height, - srfVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) { - int i, j, k, l; - srfVert_t_cleared( prev ); - srfVert_t_cleared( next ); - srfVert_t_cleared( mid ); - float len, maxLen; - int dir; - int t; - srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; - float errorTable[2][MAX_GRID_SIZE]; - int numTriangles; - static srfTriangle_t triangles[(MAX_GRID_SIZE-1)*(MAX_GRID_SIZE-1)*2]; - int consecutiveComplete; - - for ( i = 0 ; i < width ; i++ ) { - for ( j = 0 ; j < height ; j++ ) { - ctrl[j][i] = points[j*width+i]; - } - } - - for ( dir = 0 ; dir < 2 ; dir++ ) { - - for ( j = 0 ; j < MAX_GRID_SIZE ; j++ ) { - errorTable[dir][j] = 0; - } - - consecutiveComplete = 0; - - // horizontal subdivisions - for ( j = 0 ; ; j = (j + 2) % (width - 1) ) { - // check subdivided midpoints against control points - - // FIXME: also check midpoints of adjacent patches against the control points - // this would basically stitch all patches in the same LOD group together. - - maxLen = 0; - for ( i = 0 ; i < height ; i++ ) { - vec3_t midxyz; - vec3_t midxyz2; - vec3_t dir; - vec3_t projected; - float d; - - // calculate the point on the curve - for ( l = 0 ; l < 3 ; l++ ) { - midxyz[l] = (ctrl[i][j].xyz[l] + ctrl[i][j+1].xyz[l] * 2 - + ctrl[i][j+2].xyz[l] ) * 0.25f; - } - - // see how far off the line it is - // using dist-from-line will not account for internal - // texture warping, but it gives a lot less polygons than - // dist-from-midpoint - VectorSubtract( midxyz, ctrl[i][j].xyz, midxyz ); - VectorSubtract( ctrl[i][j+2].xyz, ctrl[i][j].xyz, dir ); - VectorNormalize( dir ); - - d = DotProduct( midxyz, dir ); - VectorScale( dir, d, projected ); - VectorSubtract( midxyz, projected, midxyz2); - len = VectorLengthSquared( midxyz2 ); // we will do the sqrt later - if ( len > maxLen ) { - maxLen = len; - } - } - - maxLen = sqrt(maxLen); - - // if all the points are on the lines, remove the entire columns - if ( maxLen < 0.1f ) { - errorTable[dir][j+1] = 999; - // if we go over the whole grid twice without adding any columns, stop - if (++consecutiveComplete >= width) - break; - continue; - } - - // see if we want to insert subdivided columns - if ( width + 2 > MAX_GRID_SIZE ) { - errorTable[dir][j+1] = 1.0f/maxLen; - break; // can't subdivide any more - } - - if ( maxLen <= r_subdivisions->value ) { - errorTable[dir][j+1] = 1.0f/maxLen; - // if we go over the whole grid twice without adding any columns, stop - if (++consecutiveComplete >= width) - break; - continue; // didn't need subdivision - } - - errorTable[dir][j+2] = 1.0f/maxLen; - - consecutiveComplete = 0; - - // insert two columns and replace the peak - width += 2; - for ( i = 0 ; i < height ; i++ ) { - LerpDrawVert( &ctrl[i][j], &ctrl[i][j+1], &prev ); - LerpDrawVert( &ctrl[i][j+1], &ctrl[i][j+2], &next ); - LerpDrawVert( &prev, &next, &mid ); - - for ( k = width - 1 ; k > j + 3 ; k-- ) { - ctrl[i][k] = ctrl[i][k-2]; - } - ctrl[i][j + 1] = prev; - ctrl[i][j + 2] = mid; - ctrl[i][j + 3] = next; - } - - // skip the new one, we'll get it on the next pass - j += 2; - } - - Transpose( width, height, ctrl ); - t = width; - width = height; - height = t; - } - - - // put all the aproximating points on the curve - PutPointsOnCurve( ctrl, width, height ); - - // cull out any rows or columns that are colinear - for ( i = 1 ; i < width-1 ; i++ ) { - if ( errorTable[0][i] != 999 ) { - continue; - } - for ( j = i+1 ; j < width ; j++ ) { - for ( k = 0 ; k < height ; k++ ) { - ctrl[k][j-1] = ctrl[k][j]; - } - errorTable[0][j-1] = errorTable[0][j]; - } - width--; - } - - for ( i = 1 ; i < height-1 ; i++ ) { - if ( errorTable[1][i] != 999 ) { - continue; - } - for ( j = i+1 ; j < height ; j++ ) { - for ( k = 0 ; k < width ; k++ ) { - ctrl[j-1][k] = ctrl[j][k]; - } - errorTable[1][j-1] = errorTable[1][j]; - } - height--; - } - -#if 1 - // flip for longest tristrips as an optimization - // the results should be visually identical with or - // without this step - if ( height > width ) { - Transpose( width, height, ctrl ); - InvertErrorTable( errorTable, width, height ); - t = width; - width = height; - height = t; - InvertCtrl( width, height, ctrl ); - } -#endif - - // calculate triangles - numTriangles = MakeMeshTriangles(width, height, ctrl, triangles); - - // calculate normals - MakeMeshNormals( width, height, ctrl ); -#ifdef USE_VERT_TANGENT_SPACE - MakeMeshTangentVectors(width, height, ctrl, numTriangles, triangles); -#endif - - return R_CreateSurfaceGridMesh(width, height, ctrl, errorTable, numTriangles, triangles); -} - -/* -=============== -R_GridInsertColumn -=============== -*/ -srfGridMesh_t *R_GridInsertColumn( srfGridMesh_t *grid, int column, int row, vec3_t point, float loderror ) { - int i, j; - int width, height, oldwidth; - srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; - float errorTable[2][MAX_GRID_SIZE]; - float lodRadius; - vec3_t lodOrigin; - int numTriangles; - static srfTriangle_t triangles[(MAX_GRID_SIZE-1)*(MAX_GRID_SIZE-1)*2]; - - oldwidth = 0; - width = grid->width + 1; - if (width > MAX_GRID_SIZE) - return NULL; - height = grid->height; - for (i = 0; i < width; i++) { - if (i == column) { - //insert new column - for (j = 0; j < grid->height; j++) { - LerpDrawVert( &grid->verts[j * grid->width + i-1], &grid->verts[j * grid->width + i], &ctrl[j][i] ); - if (j == row) - VectorCopy(point, ctrl[j][i].xyz); - } - errorTable[0][i] = loderror; - continue; - } - errorTable[0][i] = grid->widthLodError[oldwidth]; - for (j = 0; j < grid->height; j++) { - ctrl[j][i] = grid->verts[j * grid->width + oldwidth]; - } - oldwidth++; - } - for (j = 0; j < grid->height; j++) { - errorTable[1][j] = grid->heightLodError[j]; - } - // put all the aproximating points on the curve - //PutPointsOnCurve( ctrl, width, height ); - - // calculate triangles - numTriangles = MakeMeshTriangles(width, height, ctrl, triangles); - - // calculate normals - MakeMeshNormals( width, height, ctrl ); - - VectorCopy(grid->lodOrigin, lodOrigin); - lodRadius = grid->lodRadius; - // free the old grid - R_FreeSurfaceGridMesh(grid); - // create a new grid - grid = R_CreateSurfaceGridMesh(width, height, ctrl, errorTable, numTriangles, triangles); - grid->lodRadius = lodRadius; - VectorCopy(lodOrigin, grid->lodOrigin); - return grid; -} - -/* -=============== -R_GridInsertRow -=============== -*/ -srfGridMesh_t *R_GridInsertRow( srfGridMesh_t *grid, int row, int column, vec3_t point, float loderror ) { - int i, j; - int width, height, oldheight; - srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; - float errorTable[2][MAX_GRID_SIZE]; - float lodRadius; - vec3_t lodOrigin; - int numTriangles; - static srfTriangle_t triangles[(MAX_GRID_SIZE-1)*(MAX_GRID_SIZE-1)*2]; - - oldheight = 0; - width = grid->width; - height = grid->height + 1; - if (height > MAX_GRID_SIZE) - return NULL; - for (i = 0; i < height; i++) { - if (i == row) { - //insert new row - for (j = 0; j < grid->width; j++) { - LerpDrawVert( &grid->verts[(i-1) * grid->width + j], &grid->verts[i * grid->width + j], &ctrl[i][j] ); - if (j == column) - VectorCopy(point, ctrl[i][j].xyz); - } - errorTable[1][i] = loderror; - continue; - } - errorTable[1][i] = grid->heightLodError[oldheight]; - for (j = 0; j < grid->width; j++) { - ctrl[i][j] = grid->verts[oldheight * grid->width + j]; - } - oldheight++; - } - for (j = 0; j < grid->width; j++) { - errorTable[0][j] = grid->widthLodError[j]; - } - // put all the aproximating points on the curve - //PutPointsOnCurve( ctrl, width, height ); - - // calculate triangles - numTriangles = MakeMeshTriangles(width, height, ctrl, triangles); - - // calculate normals - MakeMeshNormals( width, height, ctrl ); - - VectorCopy(grid->lodOrigin, lodOrigin); - lodRadius = grid->lodRadius; - // free the old grid - R_FreeSurfaceGridMesh(grid); - // create a new grid - grid = R_CreateSurfaceGridMesh(width, height, ctrl, errorTable, numTriangles, triangles); - grid->lodRadius = lodRadius; - VectorCopy(lodOrigin, grid->lodOrigin); - return grid; -} diff --git a/src/rend2/tr_extensions.c b/src/rend2/tr_extensions.c deleted file mode 100644 index 0b70d48b..00000000 --- a/src/rend2/tr_extensions.c +++ /dev/null @@ -1,667 +0,0 @@ -/* -=========================================================================== -Copyright (C) 2011 James Canete (use.less01@gmail.com) - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_extensions.c - extensions needed by the renderer not in sdl_glimp.c - -#ifdef USE_LOCAL_HEADERS -# include "SDL.h" -#else -# include -#endif - -#include "tr_local.h" - -// GL_EXT_draw_range_elements -void (APIENTRY * qglDrawRangeElementsEXT) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); - -// GL_EXT_multi_draw_arrays -void (APIENTRY * qglMultiDrawArraysEXT) (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount); -void (APIENTRY * qglMultiDrawElementsEXT) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid **indices, GLsizei primcount); - -// GL_ARB_vertex_shader -void (APIENTRY * qglBindAttribLocationARB) (GLhandleARB programObj, GLuint index, const GLcharARB * name); -void (APIENTRY * qglGetActiveAttribARB) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei * length, - GLint * size, GLenum * type, GLcharARB * name); -GLint(APIENTRY * qglGetAttribLocationARB) (GLhandleARB programObj, const GLcharARB * name); - -// GL_ARB_vertex_program -void (APIENTRY * qglVertexAttrib4fARB) (GLuint, GLfloat, GLfloat, GLfloat, GLfloat); -void (APIENTRY * qglVertexAttrib4fvARB) (GLuint, const GLfloat *); -void (APIENTRY * qglVertexAttribPointerARB) (GLuint index, GLint size, GLenum type, GLboolean normalized, - GLsizei stride, const GLvoid * pointer); -void (APIENTRY * qglEnableVertexAttribArrayARB) (GLuint index); -void (APIENTRY * qglDisableVertexAttribArrayARB) (GLuint index); - -// GL_ARB_vertex_buffer_object -void (APIENTRY * qglBindBufferARB) (GLenum target, GLuint buffer); -void (APIENTRY * qglDeleteBuffersARB) (GLsizei n, const GLuint * buffers); -void (APIENTRY * qglGenBuffersARB) (GLsizei n, GLuint * buffers); - -GLboolean(APIENTRY * qglIsBufferARB) (GLuint buffer); -void (APIENTRY * qglBufferDataARB) (GLenum target, GLsizeiptrARB size, const GLvoid * data, GLenum usage); -void (APIENTRY * qglBufferSubDataARB) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid * data); -void (APIENTRY * qglGetBufferSubDataARB) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid * data); - -void (APIENTRY * qglGetBufferParameterivARB) (GLenum target, GLenum pname, GLint * params); -void (APIENTRY * qglGetBufferPointervARB) (GLenum target, GLenum pname, GLvoid * *params); - -// GL_ARB_shader_objects -void (APIENTRY * qglDeleteObjectARB) (GLhandleARB obj); - -GLhandleARB(APIENTRY * qglGetHandleARB) (GLenum pname); -void (APIENTRY * qglDetachObjectARB) (GLhandleARB containerObj, GLhandleARB attachedObj); - -GLhandleARB(APIENTRY * qglCreateShaderObjectARB) (GLenum shaderType); -void (APIENTRY * qglShaderSourceARB) (GLhandleARB shaderObj, GLsizei count, const GLcharARB * *string, - const GLint * length); -void (APIENTRY * qglCompileShaderARB) (GLhandleARB shaderObj); - -GLhandleARB(APIENTRY * qglCreateProgramObjectARB) (void); -void (APIENTRY * qglAttachObjectARB) (GLhandleARB containerObj, GLhandleARB obj); -void (APIENTRY * qglLinkProgramARB) (GLhandleARB programObj); -void (APIENTRY * qglUseProgramObjectARB) (GLhandleARB programObj); -void (APIENTRY * qglValidateProgramARB) (GLhandleARB programObj); -void (APIENTRY * qglUniform1fARB) (GLint location, GLfloat v0); -void (APIENTRY * qglUniform2fARB) (GLint location, GLfloat v0, GLfloat v1); -void (APIENTRY * qglUniform3fARB) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); -void (APIENTRY * qglUniform4fARB) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); -void (APIENTRY * qglUniform1iARB) (GLint location, GLint v0); -void (APIENTRY * qglUniform2iARB) (GLint location, GLint v0, GLint v1); -void (APIENTRY * qglUniform3iARB) (GLint location, GLint v0, GLint v1, GLint v2); -void (APIENTRY * qglUniform4iARB) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); -void (APIENTRY * qglUniform1fvARB) (GLint location, GLsizei count, const GLfloat * value); -void (APIENTRY * qglUniform2fvARB) (GLint location, GLsizei count, const GLfloat * value); -void (APIENTRY * qglUniform3fvARB) (GLint location, GLsizei count, const GLfloat * value); -void (APIENTRY * qglUniform4fvARB) (GLint location, GLsizei count, const GLfloat * value); -void (APIENTRY * qglUniform2ivARB) (GLint location, GLsizei count, const GLint * value); -void (APIENTRY * qglUniform3ivARB) (GLint location, GLsizei count, const GLint * value); -void (APIENTRY * qglUniform4ivARB) (GLint location, GLsizei count, const GLint * value); -void (APIENTRY * qglUniformMatrix2fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); -void (APIENTRY * qglUniformMatrix3fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); -void (APIENTRY * qglUniformMatrix4fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); -void (APIENTRY * qglGetObjectParameterfvARB) (GLhandleARB obj, GLenum pname, GLfloat * params); -void (APIENTRY * qglGetObjectParameterivARB) (GLhandleARB obj, GLenum pname, GLint * params); -void (APIENTRY * qglGetInfoLogARB) (GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog); -void (APIENTRY * qglGetAttachedObjectsARB) (GLhandleARB containerObj, GLsizei maxCount, GLsizei * count, - GLhandleARB * obj); -GLint(APIENTRY * qglGetUniformLocationARB) (GLhandleARB programObj, const GLcharARB * name); -void (APIENTRY * qglGetActiveUniformARB) (GLhandleARB programObj, GLuint index, GLsizei maxIndex, GLsizei * length, - GLint * size, GLenum * type, GLcharARB * name); -void (APIENTRY * qglGetUniformfvARB) (GLhandleARB programObj, GLint location, GLfloat * params); -void (APIENTRY * qglGetUniformivARB) (GLhandleARB programObj, GLint location, GLint * params); -void (APIENTRY * qglGetShaderSourceARB) (GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * source); - -// GL_ARB_texture_compression -void (APIENTRY * qglCompressedTexImage3DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, - GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); -void (APIENTRY * qglCompressedTexImage2DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, - GLint border, GLsizei imageSize, const GLvoid *data); -void (APIENTRY * qglCompressedTexImage1DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, - GLsizei imageSize, const GLvoid *data); -void (APIENTRY * qglCompressedTexSubImage3DARB)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, - GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data); -void (APIENTRY * qglCompressedTexSubImage2DARB)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, - GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); -void (APIENTRY * qglCompressedTexSubImage1DARB)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, - GLsizei imageSize, const GLvoid *data); -void (APIENTRY * qglGetCompressedTexImageARB)(GLenum target, GLint lod, - GLvoid *img); - -// GL_EXT_framebuffer_object -GLboolean (APIENTRY * qglIsRenderbufferEXT)(GLuint renderbuffer); -void (APIENTRY * qglBindRenderbufferEXT)(GLenum target, GLuint renderbuffer); -void (APIENTRY * qglDeleteRenderbuffersEXT)(GLsizei n, const GLuint *renderbuffers); -void (APIENTRY * qglGenRenderbuffersEXT)(GLsizei n, GLuint *renderbuffers); - -void (APIENTRY * qglRenderbufferStorageEXT)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); - -void (APIENTRY * qglGetRenderbufferParameterivEXT)(GLenum target, GLenum pname, GLint *params); - -GLboolean (APIENTRY * qglIsFramebufferEXT)(GLuint framebuffer); -void (APIENTRY * qglBindFramebufferEXT)(GLenum target, GLuint framebuffer); -void (APIENTRY * qglDeleteFramebuffersEXT)(GLsizei n, const GLuint *framebuffers); -void (APIENTRY * qglGenFramebuffersEXT)(GLsizei n, GLuint *framebuffers); - -GLenum (APIENTRY * qglCheckFramebufferStatusEXT)(GLenum target); - -void (APIENTRY * qglFramebufferTexture1DEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, - GLint level); -void (APIENTRY * qglFramebufferTexture2DEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, - GLint level); -void (APIENTRY * qglFramebufferTexture3DEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, - GLint level, GLint zoffset); - -void (APIENTRY * qglFramebufferRenderbufferEXT)(GLenum target, GLenum attachment, GLenum renderbuffertarget, - GLuint renderbuffer); - -void (APIENTRY * qglGetFramebufferAttachmentParameterivEXT)(GLenum target, GLenum attachment, GLenum pname, GLint *params); - -void (APIENTRY * qglGenerateMipmapEXT)(GLenum target); - -// GL_ARB_occlusion_query -void (APIENTRY * qglGenQueriesARB)(GLsizei n, GLuint *ids); -void (APIENTRY * qglDeleteQueriesARB)(GLsizei n, const GLuint *ids); -GLboolean (APIENTRY * qglIsQueryARB)(GLuint id); -void (APIENTRY * qglBeginQueryARB)(GLenum target, GLuint id); -void (APIENTRY * qglEndQueryARB)(GLenum target); -void (APIENTRY * qglGetQueryivARB)(GLenum target, GLenum pname, GLint *params); -void (APIENTRY * qglGetQueryObjectivARB)(GLuint id, GLenum pname, GLint *params); -void (APIENTRY * qglGetQueryObjectuivARB)(GLuint id, GLenum pname, GLuint *params); - -// GL_EXT_framebuffer_blit -void (APIENTRY * qglBlitFramebufferEXT)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, - GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, - GLbitfield mask, GLenum filter); - -// GL_EXT_framebuffer_multisample -void (APIENTRY * qglRenderbufferStorageMultisampleEXT)(GLenum target, GLsizei samples, - GLenum internalformat, GLsizei width, GLsizei height); - -// GL_ARB_draw_buffers -void (APIENTRY * qglDrawBuffersARB)(GLsizei n, const GLenum *bufs); - -static qboolean GLimp_HaveExtension(const char *ext) -{ - const char *ptr = Q_stristr( glConfig.extensions_string, ext ); - if (ptr == NULL) - return qfalse; - ptr += strlen(ext); - return ((*ptr == ' ') || (*ptr == '\0')); // verify it's complete string. -} - -void GLimp_InitExtraExtensions() -{ - char *extension; - const char* result[3] = { "...ignoring %s\n", "...using %s\n", "...%s not found\n" }; - - // GL_EXT_draw_range_elements - extension = "GL_EXT_draw_range_elements"; - glRefConfig.drawRangeElements = qfalse; - qglMultiDrawArraysEXT = NULL; - qglMultiDrawElementsEXT = NULL; - if( GLimp_HaveExtension( extension ) ) - { - qglDrawRangeElementsEXT = (void *) SDL_GL_GetProcAddress("glDrawRangeElementsEXT"); - - if ( r_ext_draw_range_elements->integer) - glRefConfig.drawRangeElements = qtrue; - - ri.Printf(PRINT_ALL, result[glRefConfig.drawRangeElements], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - - // GL_EXT_multi_draw_arrays - extension = "GL_EXT_multi_draw_arrays"; - glRefConfig.multiDrawArrays = qfalse; - qglMultiDrawArraysEXT = NULL; - qglMultiDrawElementsEXT = NULL; - if( GLimp_HaveExtension( extension ) ) - { - qglMultiDrawArraysEXT = (PFNGLMULTIDRAWARRAYSEXTPROC) SDL_GL_GetProcAddress("glMultiDrawArraysEXT"); - qglMultiDrawElementsEXT = (PFNGLMULTIDRAWELEMENTSEXTPROC) SDL_GL_GetProcAddress("glMultiDrawElementsEXT"); - - if ( r_ext_multi_draw_arrays->integer ) - glRefConfig.multiDrawArrays = qtrue; - - ri.Printf(PRINT_ALL, result[glRefConfig.multiDrawArrays], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - - // GL_ARB_vertex_program - //glRefConfig.vertexProgram = qfalse; - extension = "GL_ARB_vertex_program"; - qglVertexAttrib4fARB = NULL; - qglVertexAttrib4fvARB = NULL; - qglVertexAttribPointerARB = NULL; - qglEnableVertexAttribArrayARB = NULL; - qglDisableVertexAttribArrayARB = NULL; - if( GLimp_HaveExtension( extension ) ) - { - qglVertexAttrib4fARB = (PFNGLVERTEXATTRIB4FARBPROC) SDL_GL_GetProcAddress("glVertexAttrib4fARB"); - qglVertexAttrib4fvARB = (PFNGLVERTEXATTRIB4FVARBPROC) SDL_GL_GetProcAddress("glVertexAttrib4fvARB"); - qglVertexAttribPointerARB = (PFNGLVERTEXATTRIBPOINTERARBPROC) SDL_GL_GetProcAddress("glVertexAttribPointerARB"); - qglEnableVertexAttribArrayARB = - (PFNGLENABLEVERTEXATTRIBARRAYARBPROC) SDL_GL_GetProcAddress("glEnableVertexAttribArrayARB"); - qglDisableVertexAttribArrayARB = - (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) SDL_GL_GetProcAddress("glDisableVertexAttribArrayARB"); - - ri.Printf(PRINT_ALL, result[1], extension); - //glRefConfig.vertexProgram = qtrue; - } - else - { - ri.Error(ERR_FATAL, result[2], extension); - } - - // GL_ARB_vertex_buffer_object - //glRefConfig.vertexBufferObject = qfalse; - extension = "GL_ARB_vertex_buffer_object"; - qglBindBufferARB = NULL; - qglDeleteBuffersARB = NULL; - qglGenBuffersARB = NULL; - qglIsBufferARB = NULL; - qglBufferDataARB = NULL; - qglBufferSubDataARB = NULL; - qglGetBufferSubDataARB = NULL; - qglGetBufferParameterivARB = NULL; - qglGetBufferPointervARB = NULL; - if( GLimp_HaveExtension( extension ) ) - { - qglBindBufferARB = (PFNGLBINDBUFFERARBPROC) SDL_GL_GetProcAddress("glBindBufferARB"); - qglDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) SDL_GL_GetProcAddress("glDeleteBuffersARB"); - qglGenBuffersARB = (PFNGLGENBUFFERSARBPROC) SDL_GL_GetProcAddress("glGenBuffersARB"); - qglIsBufferARB = (PFNGLISBUFFERARBPROC) SDL_GL_GetProcAddress("glIsBufferARB"); - qglBufferDataARB = (PFNGLBUFFERDATAARBPROC) SDL_GL_GetProcAddress("glBufferDataARB"); - qglBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC) SDL_GL_GetProcAddress("glBufferSubDataARB"); - qglGetBufferSubDataARB = (PFNGLGETBUFFERSUBDATAARBPROC) SDL_GL_GetProcAddress("glGetBufferSubDataARB"); - qglGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC) SDL_GL_GetProcAddress("glGetBufferParameterivARB"); - qglGetBufferPointervARB = (PFNGLGETBUFFERPOINTERVARBPROC) SDL_GL_GetProcAddress("glGetBufferPointervARB"); - ri.Printf(PRINT_ALL, result[1], extension); - //glRefConfig.vertexBufferObject = qtrue; - } - else - { - ri.Error(ERR_FATAL, result[2], extension); - } - - // GL_ARB_shader_objects - extension = "GL_ARB_shader_objects"; - //glRefConfig.shaderObjects = qfalse; - qglDeleteObjectARB = NULL; - qglGetHandleARB = NULL; - qglDetachObjectARB = NULL; - qglCreateShaderObjectARB = NULL; - qglShaderSourceARB = NULL; - qglCompileShaderARB = NULL; - qglCreateProgramObjectARB = NULL; - qglAttachObjectARB = NULL; - qglLinkProgramARB = NULL; - qglUseProgramObjectARB = NULL; - qglValidateProgramARB = NULL; - qglUniform1fARB = NULL; - qglUniform2fARB = NULL; - qglUniform3fARB = NULL; - qglUniform4fARB = NULL; - qglUniform1iARB = NULL; - qglUniform2iARB = NULL; - qglUniform3iARB = NULL; - qglUniform4iARB = NULL; - qglUniform1fvARB = NULL; - qglUniform2fvARB = NULL; - qglUniform3fvARB = NULL; - qglUniform4fvARB = NULL; - qglUniform2ivARB = NULL; - qglUniform3ivARB = NULL; - qglUniform4ivARB = NULL; - qglUniformMatrix2fvARB = NULL; - qglUniformMatrix3fvARB = NULL; - qglUniformMatrix4fvARB = NULL; - qglGetObjectParameterfvARB = NULL; - qglGetObjectParameterivARB = NULL; - qglGetInfoLogARB = NULL; - qglGetAttachedObjectsARB = NULL; - qglGetUniformLocationARB = NULL; - qglGetActiveUniformARB = NULL; - qglGetUniformfvARB = NULL; - qglGetUniformivARB = NULL; - qglGetShaderSourceARB = NULL; - if( GLimp_HaveExtension( extension ) ) - { - qglDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) SDL_GL_GetProcAddress("glDeleteObjectARB"); - qglGetHandleARB = (PFNGLGETHANDLEARBPROC) SDL_GL_GetProcAddress("glGetHandleARB"); - qglDetachObjectARB = (PFNGLDETACHOBJECTARBPROC) SDL_GL_GetProcAddress("glDetachObjectARB"); - qglCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) SDL_GL_GetProcAddress("glCreateShaderObjectARB"); - qglShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) SDL_GL_GetProcAddress("glShaderSourceARB"); - qglCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) SDL_GL_GetProcAddress("glCompileShaderARB"); - qglCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glCreateProgramObjectARB"); - qglAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) SDL_GL_GetProcAddress("glAttachObjectARB"); - qglLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) SDL_GL_GetProcAddress("glLinkProgramARB"); - qglUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glUseProgramObjectARB"); - qglValidateProgramARB = (PFNGLVALIDATEPROGRAMARBPROC) SDL_GL_GetProcAddress("glValidateProgramARB"); - qglUniform1fARB = (PFNGLUNIFORM1FARBPROC) SDL_GL_GetProcAddress("glUniform1fARB"); - qglUniform2fARB = (PFNGLUNIFORM2FARBPROC) SDL_GL_GetProcAddress("glUniform2fARB"); - qglUniform3fARB = (PFNGLUNIFORM3FARBPROC) SDL_GL_GetProcAddress("glUniform3fARB"); - qglUniform4fARB = (PFNGLUNIFORM4FARBPROC) SDL_GL_GetProcAddress("glUniform4fARB"); - qglUniform1iARB = (PFNGLUNIFORM1IARBPROC) SDL_GL_GetProcAddress("glUniform1iARB"); - qglUniform2iARB = (PFNGLUNIFORM2IARBPROC) SDL_GL_GetProcAddress("glUniform2iARB"); - qglUniform3iARB = (PFNGLUNIFORM3IARBPROC) SDL_GL_GetProcAddress("glUniform3iARB"); - qglUniform4iARB = (PFNGLUNIFORM4IARBPROC) SDL_GL_GetProcAddress("glUniform4iARB"); - qglUniform1fvARB = (PFNGLUNIFORM1FVARBPROC) SDL_GL_GetProcAddress("glUniform1fvARB"); - qglUniform2fvARB = (PFNGLUNIFORM2FVARBPROC) SDL_GL_GetProcAddress("glUniform2fvARB"); - qglUniform3fvARB = (PFNGLUNIFORM3FVARBPROC) SDL_GL_GetProcAddress("glUniform3fvARB"); - qglUniform4fvARB = (PFNGLUNIFORM4FVARBPROC) SDL_GL_GetProcAddress("glUniform4fvARB"); - qglUniform2ivARB = (PFNGLUNIFORM2IVARBPROC) SDL_GL_GetProcAddress("glUniform2ivARB"); - qglUniform3ivARB = (PFNGLUNIFORM3IVARBPROC) SDL_GL_GetProcAddress("glUniform3ivARB"); - qglUniform4ivARB = (PFNGLUNIFORM4IVARBPROC) SDL_GL_GetProcAddress("glUniform4ivARB"); - qglUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC) SDL_GL_GetProcAddress("glUniformMatrix2fvARB"); - qglUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC) SDL_GL_GetProcAddress("glUniformMatrix3fvARB"); - qglUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC) SDL_GL_GetProcAddress("glUniformMatrix4fvARB"); - qglGetObjectParameterfvARB = (PFNGLGETOBJECTPARAMETERFVARBPROC) SDL_GL_GetProcAddress("glGetObjectParameterfvARB"); - qglGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) SDL_GL_GetProcAddress("glGetObjectParameterivARB"); - qglGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) SDL_GL_GetProcAddress("glGetInfoLogARB"); - qglGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC) SDL_GL_GetProcAddress("glGetAttachedObjectsARB"); - qglGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) SDL_GL_GetProcAddress("glGetUniformLocationARB"); - qglGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC) SDL_GL_GetProcAddress("glGetActiveUniformARB"); - qglGetUniformfvARB = (PFNGLGETUNIFORMFVARBPROC) SDL_GL_GetProcAddress("glGetUniformfvARB"); - qglGetUniformivARB = (PFNGLGETUNIFORMIVARBPROC) SDL_GL_GetProcAddress("glGetUniformivARB"); - qglGetShaderSourceARB = (PFNGLGETSHADERSOURCEARBPROC) SDL_GL_GetProcAddress("glGetShaderSourceARB"); - ri.Printf(PRINT_ALL, result[1], extension); - //glRefConfig.shaderObjects = qtrue; - } - else - { - ri.Error(ERR_FATAL, result[2], extension); - } - - // GL_ARB_vertex_shader - //glRefConfig.vertexShader = qfalse; - extension = "GL_ARB_vertex_shader"; - qglBindAttribLocationARB = NULL; - qglGetActiveAttribARB = NULL; - qglGetAttribLocationARB = NULL; - if( GLimp_HaveExtension( extension ) ) - { - //int reservedComponents; - - //qglGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, &glConfig.maxVertexUniforms); - //qglGetIntegerv(GL_MAX_VARYING_FLOATS_ARB, &glConfig.maxVaryingFloats); - //qglGetIntegerv(GL_MAX_VERTEX_ATTRIBS_ARB, &glConfig.maxVertexAttribs); - - //reservedComponents = 16 * 10; // approximation how many uniforms we have besides the bone matrices - -#if 0 - if(glConfig.driverType == GLDRV_MESA) - { - // HACK - // restrict to number of vertex uniforms to 512 because of: - // xreal.x86_64: nv50_program.c:4181: nv50_program_validate_data: Assertion `p->param_nr <= 512' failed - - glConfig.maxVertexUniforms = Q_bound(0, glConfig.maxVertexUniforms, 512); - } -#endif - - //glConfig.maxVertexSkinningBones = (int) Q_bound(0.0, (Q_max(glConfig.maxVertexUniforms - reservedComponents, 0) / 16), MAX_BONES); - //glConfig.vboVertexSkinningAvailable = r_vboVertexSkinning->integer && ((glConfig.maxVertexSkinningBones >= 12) ? qtrue : qfalse); - - qglBindAttribLocationARB = (PFNGLBINDATTRIBLOCATIONARBPROC) SDL_GL_GetProcAddress("glBindAttribLocationARB"); - qglGetActiveAttribARB = (PFNGLGETACTIVEATTRIBARBPROC) SDL_GL_GetProcAddress("glGetActiveAttribARB"); - qglGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC) SDL_GL_GetProcAddress("glGetAttribLocationARB"); - ri.Printf(PRINT_ALL, result[1], extension); - //glRefConfig.vertexShader = qtrue; - } - else - { - ri.Error(ERR_FATAL, result[2], extension); - } - - // GL_ARB_shading_language_100 - extension = "GL_ARB_shading_language_100"; - glRefConfig.textureFloat = qfalse; - if( GLimp_HaveExtension( extension ) ) - { - char version[256]; - - Q_strncpyz( version, (char *) qglGetString (GL_SHADING_LANGUAGE_VERSION_ARB), sizeof( version ) ); - - sscanf(version, "%d.%d", &glRefConfig.glslMajorVersion, &glRefConfig.glslMinorVersion); - - ri.Printf(PRINT_ALL, "...using GLSL version %s\n", version); - } - else - { - ri.Error(ERR_FATAL, result[2], extension); - } - - glRefConfig.memInfo = MI_NONE; - - if( GLimp_HaveExtension( "GL_NVX_gpu_memory_info" ) ) - { - glRefConfig.memInfo = MI_NVX; - } - else if( GLimp_HaveExtension( "GL_ATI_meminfo" ) ) - { - glRefConfig.memInfo = MI_ATI; - } - - extension = "GL_ARB_texture_non_power_of_two"; - glRefConfig.textureNonPowerOfTwo = qfalse; - if( GLimp_HaveExtension( extension ) ) - { - if(1) //(r_ext_texture_non_power_of_two->integer) - { - glRefConfig.textureNonPowerOfTwo = qtrue; - } - - ri.Printf(PRINT_ALL, result[glRefConfig.textureNonPowerOfTwo], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - - // GL_ARB_texture_float - extension = "GL_ARB_texture_float"; - glRefConfig.textureFloat = qfalse; - if( GLimp_HaveExtension( extension ) ) - { - if( r_ext_texture_float->integer ) - { - glRefConfig.textureFloat = qtrue; - } - - ri.Printf(PRINT_ALL, result[glRefConfig.textureFloat], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - - // GL_ARB_half_float_pixel - extension = "GL_ARB_half_float_pixel"; - glRefConfig.halfFloatPixel = qfalse; - if( GLimp_HaveExtension( extension ) ) - { - if( r_arb_half_float_pixel->integer ) - glRefConfig.halfFloatPixel = qtrue; - - ri.Printf(PRINT_ALL, result[glRefConfig.halfFloatPixel], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - - // GL_EXT_framebuffer_object - extension = "GL_EXT_framebuffer_object"; - glRefConfig.framebufferObject = qfalse; - if( GLimp_HaveExtension( extension ) ) - { - glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE_EXT, &glRefConfig.maxRenderbufferSize); - glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &glRefConfig.maxColorAttachments); - - qglIsRenderbufferEXT = (PFNGLISRENDERBUFFEREXTPROC) SDL_GL_GetProcAddress("glIsRenderbufferEXT"); - qglBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC) SDL_GL_GetProcAddress("glBindRenderbufferEXT"); - qglDeleteRenderbuffersEXT = (PFNGLDELETERENDERBUFFERSEXTPROC) SDL_GL_GetProcAddress("glDeleteRenderbuffersEXT"); - qglGenRenderbuffersEXT = (PFNGLGENRENDERBUFFERSEXTPROC) SDL_GL_GetProcAddress("glGenRenderbuffersEXT"); - qglRenderbufferStorageEXT = (PFNGLRENDERBUFFERSTORAGEEXTPROC) SDL_GL_GetProcAddress("glRenderbufferStorageEXT"); - qglGetRenderbufferParameterivEXT = (PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) SDL_GL_GetProcAddress("glGetRenderbufferParameterivEXT"); - qglIsFramebufferEXT = (PFNGLISFRAMEBUFFEREXTPROC) SDL_GL_GetProcAddress("glIsFramebufferEXT"); - qglBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC) SDL_GL_GetProcAddress("glBindFramebufferEXT"); - qglDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC) SDL_GL_GetProcAddress("glDeleteFramebuffersEXT"); - qglGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC) SDL_GL_GetProcAddress("glGenFramebuffersEXT"); - qglCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) SDL_GL_GetProcAddress("glCheckFramebufferStatusEXT"); - qglFramebufferTexture1DEXT = (PFNGLFRAMEBUFFERTEXTURE1DEXTPROC) SDL_GL_GetProcAddress("glFramebufferTexture1DEXT"); - qglFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) SDL_GL_GetProcAddress("glFramebufferTexture2DEXT"); - qglFramebufferTexture3DEXT = (PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) SDL_GL_GetProcAddress("glFramebufferTexture3DEXT"); - qglFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) SDL_GL_GetProcAddress("glFramebufferRenderbufferEXT"); - qglGetFramebufferAttachmentParameterivEXT = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) SDL_GL_GetProcAddress("glGetFramebufferAttachmentParameterivEXT"); - qglGenerateMipmapEXT = (PFNGLGENERATEMIPMAPEXTPROC) SDL_GL_GetProcAddress("glGenerateMipmapEXT"); - - if(r_ext_framebuffer_object->value) - glRefConfig.framebufferObject = qtrue; - - ri.Printf(PRINT_ALL, result[glRefConfig.framebufferObject], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - - // GL_EXT_packed_depth_stencil - extension = "GL_EXT_packed_depth_stencil"; - glRefConfig.packedDepthStencil = qfalse; - if( GLimp_HaveExtension(extension)) - { - glRefConfig.packedDepthStencil = qtrue; - ri.Printf(PRINT_ALL, result[glRefConfig.packedDepthStencil], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - - // GL_ARB_occlusion_query - extension = "GL_ARB_occlusion_query"; - glRefConfig.occlusionQuery = qfalse; - if (GLimp_HaveExtension(extension)) - { - qglGenQueriesARB = (PFNGLGENQUERIESARBPROC) SDL_GL_GetProcAddress("glGenQueriesARB"); - qglDeleteQueriesARB = (PFNGLDELETEQUERIESARBPROC) SDL_GL_GetProcAddress("glDeleteQueriesARB"); - qglIsQueryARB = (PFNGLISQUERYARBPROC) SDL_GL_GetProcAddress("glIsQueryARB"); - qglBeginQueryARB = (PFNGLBEGINQUERYARBPROC) SDL_GL_GetProcAddress("glBeginQueryARB"); - qglEndQueryARB = (PFNGLENDQUERYARBPROC) SDL_GL_GetProcAddress("glEndQueryARB"); - qglGetQueryivARB = (PFNGLGETQUERYIVARBPROC) SDL_GL_GetProcAddress("glGetQueryivARB"); - qglGetQueryObjectivARB = (PFNGLGETQUERYOBJECTIVARBPROC) SDL_GL_GetProcAddress("glGetQueryObjectivARB"); - qglGetQueryObjectuivARB = (PFNGLGETQUERYOBJECTUIVARBPROC) SDL_GL_GetProcAddress("glGetQueryObjectuivARB"); - glRefConfig.occlusionQuery = qtrue; - ri.Printf(PRINT_ALL, result[glRefConfig.occlusionQuery], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - - // GL_EXT_framebuffer_blit - extension = "GL_EXT_framebuffer_blit"; - glRefConfig.framebufferBlit = qfalse; - if (GLimp_HaveExtension(extension)) - { - qglBlitFramebufferEXT = (void *)SDL_GL_GetProcAddress("glBlitFramebufferEXT"); - glRefConfig.framebufferBlit = qtrue; - ri.Printf(PRINT_ALL, result[glRefConfig.framebufferBlit], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - - // GL_EXT_framebuffer_multisample - extension = "GL_EXT_framebuffer_multisample"; - glRefConfig.framebufferMultisample = qfalse; - if (GLimp_HaveExtension(extension)) - { - qglRenderbufferStorageMultisampleEXT = (void *)SDL_GL_GetProcAddress("glRenderbufferStorageMultisampleEXT"); - glRefConfig.framebufferMultisample = qtrue; - ri.Printf(PRINT_ALL, result[glRefConfig.framebufferMultisample], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - - // GL_EXT_texture_sRGB - extension = "GL_EXT_texture_sRGB"; - glRefConfig.texture_srgb = qfalse; - if (GLimp_HaveExtension(extension)) - { - if (r_srgb->integer) - glRefConfig.texture_srgb = qtrue; - - ri.Printf(PRINT_ALL, result[glRefConfig.texture_srgb], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - - glRefConfig.textureCompression = TCR_NONE; - - // GL_EXT_texture_compression_latc - extension = "GL_EXT_texture_compression_latc"; - if (GLimp_HaveExtension(extension)) - { - if (r_ext_compressed_textures->integer) - glRefConfig.textureCompression |= TCR_LATC; - - ri.Printf(PRINT_ALL, result[r_ext_compressed_textures->integer ? 1 : 0], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - - // GL_ARB_texture_compression_bptc - extension = "GL_ARB_texture_compression_bptc"; - if (GLimp_HaveExtension(extension)) - { - if (r_ext_compressed_textures->integer >= 2) - glRefConfig.textureCompression |= TCR_BPTC; - - ri.Printf(PRINT_ALL, result[(r_ext_compressed_textures->integer >= 2) ? 1 : 0], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - - // GL_ARB_draw_buffers - extension = "GL_ARB_draw_buffers"; - qglDrawBuffersARB = NULL; - if( GLimp_HaveExtension( extension ) ) - { - qglDrawBuffersARB = (void *) SDL_GL_GetProcAddress("glDrawBuffersARB"); - - ri.Printf(PRINT_ALL, result[1], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } - - // GL_ARB_depth_clamp - extension = "GL_ARB_depth_clamp"; - glRefConfig.depthClamp = qfalse; - if( GLimp_HaveExtension( extension ) ) - { - glRefConfig.depthClamp = qtrue; - ri.Printf(PRINT_ALL, result[1], extension); - } - else - { - ri.Printf(PRINT_ALL, result[2], extension); - } -} diff --git a/src/rend2/tr_extramath.c b/src/rend2/tr_extramath.c deleted file mode 100644 index e989c7e9..00000000 --- a/src/rend2/tr_extramath.c +++ /dev/null @@ -1,233 +0,0 @@ -/* -=========================================================================== -Copyright (C) 2010 James Canete (use.less01@gmail.com) - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_extramath.c - extra math needed by the renderer not in qmath.c - -#include "tr_local.h" - -// Some matrix helper functions -// FIXME: do these already exist in ioq3 and I don't know about them? - -void Matrix16Zero( matrix_t out ) -{ - out[ 0] = 0.0f; out[ 4] = 0.0f; out[ 8] = 0.0f; out[12] = 0.0f; - out[ 1] = 0.0f; out[ 5] = 0.0f; out[ 9] = 0.0f; out[13] = 0.0f; - out[ 2] = 0.0f; out[ 6] = 0.0f; out[10] = 0.0f; out[14] = 0.0f; - out[ 3] = 0.0f; out[ 7] = 0.0f; out[11] = 0.0f; out[15] = 0.0f; -} - -void Matrix16Identity( matrix_t out ) -{ - out[ 0] = 1.0f; out[ 4] = 0.0f; out[ 8] = 0.0f; out[12] = 0.0f; - out[ 1] = 0.0f; out[ 5] = 1.0f; out[ 9] = 0.0f; out[13] = 0.0f; - out[ 2] = 0.0f; out[ 6] = 0.0f; out[10] = 1.0f; out[14] = 0.0f; - out[ 3] = 0.0f; out[ 7] = 0.0f; out[11] = 0.0f; out[15] = 1.0f; -} - -void Matrix16Copy( const matrix_t in, matrix_t out ) -{ - out[ 0] = in[ 0]; out[ 4] = in[ 4]; out[ 8] = in[ 8]; out[12] = in[12]; - out[ 1] = in[ 1]; out[ 5] = in[ 5]; out[ 9] = in[ 9]; out[13] = in[13]; - out[ 2] = in[ 2]; out[ 6] = in[ 6]; out[10] = in[10]; out[14] = in[14]; - out[ 3] = in[ 3]; out[ 7] = in[ 7]; out[11] = in[11]; out[15] = in[15]; -} - -void Matrix16Multiply( const matrix_t in1, const matrix_t in2, matrix_t out ) -{ - out[ 0] = in1[ 0] * in2[ 0] + in1[ 4] * in2[ 1] + in1[ 8] * in2[ 2] + in1[12] * in2[ 3]; - out[ 1] = in1[ 1] * in2[ 0] + in1[ 5] * in2[ 1] + in1[ 9] * in2[ 2] + in1[13] * in2[ 3]; - out[ 2] = in1[ 2] * in2[ 0] + in1[ 6] * in2[ 1] + in1[10] * in2[ 2] + in1[14] * in2[ 3]; - out[ 3] = in1[ 3] * in2[ 0] + in1[ 7] * in2[ 1] + in1[11] * in2[ 2] + in1[15] * in2[ 3]; - - out[ 4] = in1[ 0] * in2[ 4] + in1[ 4] * in2[ 5] + in1[ 8] * in2[ 6] + in1[12] * in2[ 7]; - out[ 5] = in1[ 1] * in2[ 4] + in1[ 5] * in2[ 5] + in1[ 9] * in2[ 6] + in1[13] * in2[ 7]; - out[ 6] = in1[ 2] * in2[ 4] + in1[ 6] * in2[ 5] + in1[10] * in2[ 6] + in1[14] * in2[ 7]; - out[ 7] = in1[ 3] * in2[ 4] + in1[ 7] * in2[ 5] + in1[11] * in2[ 6] + in1[15] * in2[ 7]; - - out[ 8] = in1[ 0] * in2[ 8] + in1[ 4] * in2[ 9] + in1[ 8] * in2[10] + in1[12] * in2[11]; - out[ 9] = in1[ 1] * in2[ 8] + in1[ 5] * in2[ 9] + in1[ 9] * in2[10] + in1[13] * in2[11]; - out[10] = in1[ 2] * in2[ 8] + in1[ 6] * in2[ 9] + in1[10] * in2[10] + in1[14] * in2[11]; - out[11] = in1[ 3] * in2[ 8] + in1[ 7] * in2[ 9] + in1[11] * in2[10] + in1[15] * in2[11]; - - out[12] = in1[ 0] * in2[12] + in1[ 4] * in2[13] + in1[ 8] * in2[14] + in1[12] * in2[15]; - out[13] = in1[ 1] * in2[12] + in1[ 5] * in2[13] + in1[ 9] * in2[14] + in1[13] * in2[15]; - out[14] = in1[ 2] * in2[12] + in1[ 6] * in2[13] + in1[10] * in2[14] + in1[14] * in2[15]; - out[15] = in1[ 3] * in2[12] + in1[ 7] * in2[13] + in1[11] * in2[14] + in1[15] * in2[15]; -} - -void Matrix16Transform( const matrix_t in1, const vec4_t in2, vec4_t out ) -{ - out[ 0] = in1[ 0] * in2[ 0] + in1[ 4] * in2[ 1] + in1[ 8] * in2[ 2] + in1[12] * in2[ 3]; - out[ 1] = in1[ 1] * in2[ 0] + in1[ 5] * in2[ 1] + in1[ 9] * in2[ 2] + in1[13] * in2[ 3]; - out[ 2] = in1[ 2] * in2[ 0] + in1[ 6] * in2[ 1] + in1[10] * in2[ 2] + in1[14] * in2[ 3]; - out[ 3] = in1[ 3] * in2[ 0] + in1[ 7] * in2[ 1] + in1[11] * in2[ 2] + in1[15] * in2[ 3]; -} - -qboolean Matrix16Compare( const matrix_t a, const matrix_t b ) -{ - return !(a[ 0] != b[ 0] || a[ 4] != b[ 4] || a[ 8] != b[ 8] || a[12] != b[12] || - a[ 1] != b[ 1] || a[ 5] != b[ 5] || a[ 9] != b[ 9] || a[13] != b[13] || - a[ 2] != b[ 2] || a[ 6] != b[ 6] || a[10] != b[10] || a[14] != b[14] || - a[ 3] != b[ 3] || a[ 7] != b[ 7] || a[11] != b[11] || a[15] != b[15]); -} - -void Matrix16Dump( const matrix_t in ) -{ - ri.Printf(PRINT_ALL, "%3.5f %3.5f %3.5f %3.5f\n", in[ 0], in[ 4], in[ 8], in[12]); - ri.Printf(PRINT_ALL, "%3.5f %3.5f %3.5f %3.5f\n", in[ 1], in[ 5], in[ 9], in[13]); - ri.Printf(PRINT_ALL, "%3.5f %3.5f %3.5f %3.5f\n", in[ 2], in[ 6], in[10], in[14]); - ri.Printf(PRINT_ALL, "%3.5f %3.5f %3.5f %3.5f\n", in[ 3], in[ 7], in[11], in[15]); -} - -void Matrix16Translation( vec3_t vec, matrix_t out ) -{ - out[ 0] = 1.0f; out[ 4] = 0.0f; out[ 8] = 0.0f; out[12] = vec[0]; - out[ 1] = 0.0f; out[ 5] = 1.0f; out[ 9] = 0.0f; out[13] = vec[1]; - out[ 2] = 0.0f; out[ 6] = 0.0f; out[10] = 1.0f; out[14] = vec[2]; - out[ 3] = 0.0f; out[ 7] = 0.0f; out[11] = 0.0f; out[15] = 1.0f; -} - -void Matrix16Ortho( float left, float right, float bottom, float top, float znear, float zfar, matrix_t out ) -{ - out[ 0] = 2.0f / (right - left); out[ 4] = 0.0f; out[ 8] = 0.0f; out[12] = -(right + left) / (right - left); - out[ 1] = 0.0f; out[ 5] = 2.0f / (top - bottom); out[ 9] = 0.0f; out[13] = -(top + bottom) / (top - bottom); - out[ 2] = 0.0f; out[ 6] = 0.0f; out[10] = 2.0f / (zfar - znear); out[14] = -(zfar + znear) / (zfar - znear); - out[ 3] = 0.0f; out[ 7] = 0.0f; out[11] = 0.0f; out[15] = 1.0f; -} - -void Matrix16View(vec3_t axes[3], vec3_t origin, matrix_t out) -{ - out[0] = axes[0][0]; - out[1] = axes[1][0]; - out[2] = axes[2][0]; - out[3] = 0; - - out[4] = axes[0][1]; - out[5] = axes[1][1]; - out[6] = axes[2][1]; - out[7] = 0; - - out[8] = axes[0][2]; - out[9] = axes[1][2]; - out[10] = axes[2][2]; - out[11] = 0; - - out[12] = -DotProduct(origin, axes[0]); - out[13] = -DotProduct(origin, axes[1]); - out[14] = -DotProduct(origin, axes[2]); - out[15] = 1; -} - -void Matrix16SimpleInverse( const matrix_t in, matrix_t out) -{ - vec3_t v; - float invSqrLen; - - VectorCopy(in + 0, v); - invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); - out[ 0] = v[0]; out[ 4] = v[1]; out[ 8] = v[2]; out[12] = -DotProduct(v, &in[12]); - - VectorCopy(in + 4, v); - invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); - out[ 1] = v[0]; out[ 5] = v[1]; out[ 9] = v[2]; out[13] = -DotProduct(v, &in[12]); - - VectorCopy(in + 8, v); - invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); - out[ 2] = v[0]; out[ 6] = v[1]; out[10] = v[2]; out[14] = -DotProduct(v, &in[12]); - - out[ 3] = 0.0f; out[ 7] = 0.0f; out[11] = 0.0f; out[15] = 1.0f; -} - -qboolean SpheresIntersect(vec3_t origin1, float radius1, vec3_t origin2, float radius2) -{ - float radiusSum = radius1 + radius2; - vec3_t diff; - - VectorSubtract(origin1, origin2, diff); - - if (DotProduct(diff, diff) <= radiusSum * radiusSum) - { - return qtrue; - } - - return qfalse; -} - -void BoundingSphereOfSpheres(vec3_t origin1, float radius1, vec3_t origin2, float radius2, vec3_t origin3, float *radius3) -{ - vec3_t diff; - - VectorScale(origin1, 0.5f, origin3); - VectorMA(origin3, 0.5f, origin2, origin3); - - VectorSubtract(origin1, origin2, diff); - *radius3 = VectorLength(diff) * 0.5f + MAX(radius1, radius2); -} - -int NextPowerOfTwo(int in) -{ - int out; - - for (out = 1; out < in; out <<= 1) - ; - - return out; -} - -unsigned short FloatToHalf(float in) -{ - unsigned short out; - - union - { - float f; - unsigned int i; - } f32; - - int sign, inExponent, inFraction; - int outExponent, outFraction; - - f32.f = in; - - sign = (f32.i & 0x80000000) >> 31; - inExponent = (f32.i & 0x7F800000) >> 23; - inFraction = f32.i & 0x007FFFFF; - - outExponent = CLAMP(inExponent - 127, -15, 16) + 15; - - outFraction = 0; - if (outExponent == 0x1F) - { - if (inExponent == 0xFF && inFraction != 0) - outFraction = 0x3FF; - } - else if (outExponent == 0x00) - { - if (inExponent == 0x00 && inFraction != 0) - outFraction = 0x3FF; - } - else - outFraction = inFraction >> 13; - - out = (sign << 15) | (outExponent << 10) | outFraction; - - return out; -} diff --git a/src/rend2/tr_extramath.h b/src/rend2/tr_extramath.h deleted file mode 100644 index b4ca6f3c..00000000 --- a/src/rend2/tr_extramath.h +++ /dev/null @@ -1,101 +0,0 @@ -/* -=========================================================================== -Copyright (C) 2010 James Canete (use.less01@gmail.com) - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_extramath.h - -#ifndef __TR_EXTRAMATH_H__ -#define __TR_EXTRAMATH_H__ - -typedef vec_t matrix_t[16]; -typedef int vec2i_t[2]; -typedef int vec3i_t[3]; -typedef int vec4i_t[4]; - -void Matrix16Zero( matrix_t out ); -void Matrix16Identity( matrix_t out ); -void Matrix16Copy( const matrix_t in, matrix_t out ); -void Matrix16Multiply( const matrix_t in1, const matrix_t in2, matrix_t out ); -void Matrix16Transform( const matrix_t in1, const vec4_t in2, vec4_t out ); -qboolean Matrix16Compare(const matrix_t a, const matrix_t b); -void Matrix16Dump( const matrix_t in ); -void Matrix16Translation( vec3_t vec, matrix_t out ); -void Matrix16Ortho( float left, float right, float bottom, float top, float znear, float zfar, matrix_t out ); -void Matrix16View(vec3_t axes[3], vec3_t origin, matrix_t out); -void Matrix16SimpleInverse( const matrix_t in, matrix_t out); - -#define VectorCopy2(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1]) - -#define VectorCopy4(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3]) -#define VectorSet4(v,x,y,z,w) ((v)[0]=(x),(v)[1]=(y),(v)[2]=(z),(v)[3]=(w)) -#define DotProduct4(a,b) ((a)[0]*(b)[0] + (a)[1]*(b)[1] + (a)[2]*(b)[2] + (a)[3]*(b)[3]) -#define VectorScale4(a,b,c) ((c)[0]=(a)[0]*(b),(c)[1]=(a)[1]*(b),(c)[2]=(a)[2]*(b),(c)[3]=(a)[3]*(b)) - -#define VectorCopy5(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3],(b)[4]=(a)[4]) - -#define OffsetByteToFloat(a) ((float)(a) * 1.0f/127.5f - 1.0f) -#define FloatToOffsetByte(a) (byte)(((a) + 1.0f) * 127.5f) -#define ByteToFloat(a) ((float)(a) * 1.0f/255.0f) -#define FloatToByte(a) (byte)((a) * 255.0f) - -#define RGBtosRGB(a) (((a) < 0.0031308f) ? (12.92f * (a)) : (1.055f * pow((a), 0.41666f) - 0.055f)) -#define sRGBtoRGB(a) (((a) <= 0.04045f) ? ((a) / 12.92f) : (pow((((a) + 0.055f) / 1.055f), 2.4)) ) - -static ID_INLINE int VectorCompare4(const vec4_t v1, const vec4_t v2) -{ - if(v1[0] != v2[0] || v1[1] != v2[1] || v1[2] != v2[2] || v1[3] != v2[3]) - { - return 0; - } - return 1; -} - -static ID_INLINE int VectorCompare5(const vec5_t v1, const vec5_t v2) -{ - if(v1[0] != v2[0] || v1[1] != v2[1] || v1[2] != v2[2] || v1[3] != v2[3] || v1[4] != v2[4]) - { - return 0; - } - return 1; -} - -qboolean SpheresIntersect(vec3_t origin1, float radius1, vec3_t origin2, float radius2); -void BoundingSphereOfSpheres(vec3_t origin1, float radius1, vec3_t origin2, float radius2, vec3_t origin3, float *radius3); - -#ifndef SGN -#define SGN(x) (((x) >= 0) ? !!(x) : -1) -#endif - -#ifndef MAX -#define MAX(a,b) ((a) > (b) ? (a) : (b)) -#endif - -#ifndef MIN -#define MIN(a,b) ((a) < (b) ? (a) : (b)) -#endif - -#ifndef CLAMP -#define CLAMP(a,b,c) MIN(MAX((a),(b)),(c)) -#endif - -int NextPowerOfTwo(int in); -unsigned short FloatToHalf(float in); - -#endif diff --git a/src/rend2/tr_extratypes.h b/src/rend2/tr_extratypes.h deleted file mode 100644 index b84cd5e4..00000000 --- a/src/rend2/tr_extratypes.h +++ /dev/null @@ -1,43 +0,0 @@ -/* -=========================================================================== -Copyright (C) 2009-2011 Andrei Drexler, Richard Allen, James Canete - -This file is part of Reaction source code. - -Reaction source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Reaction source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Reaction source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#ifndef __TR_EXTRATYPES_H__ -#define __TR_EXTRATYPES_H__ - -// tr_extratypes.h, for mods that want to extend tr_types.h without losing compatibility with original VMs - -// extra renderfx flags start at 0x0400 -#define RF_SUNFLARE 0x0400 - -// extra refdef flags start at 0x0008 -#define RDF_NOFOG 0x0008 // don't apply fog -#define RDF_EXTRA 0x0010 // Makro - refdefex_t to follow after refdef_t -#define RDF_SUNLIGHT 0x0020 // SmileTheory - render sunlight and shadows - -typedef struct { - float blurFactor; - float sunDir[3]; - float sunCol[3]; - float sunAmbCol[3]; -} refdefex_t; - -#endif diff --git a/src/rend2/tr_fbo.c b/src/rend2/tr_fbo.c deleted file mode 100644 index 0ad08b20..00000000 --- a/src/rend2/tr_fbo.c +++ /dev/null @@ -1,861 +0,0 @@ -/* -=========================================================================== -Copyright (C) 2006 Kirk Barnes -Copyright (C) 2006-2008 Robert Beckebans - -This file is part of XreaL source code. - -XreaL source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -XreaL source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with XreaL source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_fbo.c -#include "tr_local.h" - -/* -============= -R_CheckFBO -============= -*/ -qboolean R_CheckFBO(const FBO_t * fbo) -{ - int code; - int id; - - qglGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &id); - qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->frameBuffer); - - code = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); - - if(code == GL_FRAMEBUFFER_COMPLETE_EXT) - { - qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id); - return qtrue; - } - - // an error occured - switch (code) - { - case GL_FRAMEBUFFER_COMPLETE_EXT: - break; - - case GL_FRAMEBUFFER_UNSUPPORTED_EXT: - ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Unsupported framebuffer format\n", fbo->name); - break; - - case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: - ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete attachment\n", fbo->name); - break; - - case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: - ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, missing attachment\n", fbo->name); - break; - - //case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT: - // ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, duplicate attachment\n", fbo->name); - // break; - - case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: - ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, attached images must have same dimensions\n", - fbo->name); - break; - - case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: - ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, attached images must have same format\n", - fbo->name); - break; - - case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: - ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, missing draw buffer\n", fbo->name); - break; - - case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: - ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, missing read buffer\n", fbo->name); - break; - - default: - ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) unknown error 0x%X\n", fbo->name, code); - //ri.Error(ERR_FATAL, "R_CheckFBO: (%s) unknown error 0x%X", fbo->name, code); - //assert(0); - break; - } - - qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id); - - return qfalse; -} - -/* -============ -FBO_Create -============ -*/ -FBO_t *FBO_Create(const char *name, int width, int height) -{ - FBO_t *fbo; - - if(strlen(name) >= MAX_QPATH) - { - ri.Error(ERR_DROP, "FBO_Create: \"%s\" is too long\n", name); - } - - if(width <= 0 || width > glRefConfig.maxRenderbufferSize) - { - ri.Error(ERR_DROP, "FBO_Create: bad width %i", width); - } - - if(height <= 0 || height > glRefConfig.maxRenderbufferSize) - { - ri.Error(ERR_DROP, "FBO_Create: bad height %i", height); - } - - if(tr.numFBOs == MAX_FBOS) - { - ri.Error(ERR_DROP, "FBO_Create: MAX_FBOS hit"); - } - - fbo = tr.fbos[tr.numFBOs] = ri.Hunk_Alloc(sizeof(*fbo), h_low); - Q_strncpyz(fbo->name, name, sizeof(fbo->name)); - fbo->index = tr.numFBOs++; - fbo->width = width; - fbo->height = height; - - qglGenFramebuffersEXT(1, &fbo->frameBuffer); - - return fbo; -} - -void FBO_CreateBuffer(FBO_t *fbo, int format, int index, int multisample) -{ - uint32_t *pRenderBuffer; - GLenum attachment; - qboolean absent; - - switch(format) - { - case GL_RGB: - case GL_RGBA: - case GL_RGB8: - case GL_RGBA8: - case GL_RGB16F_ARB: - case GL_RGBA16F_ARB: - case GL_RGB32F_ARB: - case GL_RGBA32F_ARB: - fbo->colorFormat = format; - pRenderBuffer = &fbo->colorBuffers[index]; - attachment = GL_COLOR_ATTACHMENT0_EXT + index; - break; - - case GL_DEPTH_COMPONENT: - case GL_DEPTH_COMPONENT16_ARB: - case GL_DEPTH_COMPONENT24_ARB: - case GL_DEPTH_COMPONENT32_ARB: - fbo->depthFormat = format; - pRenderBuffer = &fbo->depthBuffer; - attachment = GL_DEPTH_ATTACHMENT_EXT; - break; - - case GL_STENCIL_INDEX: - case GL_STENCIL_INDEX1_EXT: - case GL_STENCIL_INDEX4_EXT: - case GL_STENCIL_INDEX8_EXT: - case GL_STENCIL_INDEX16_EXT: - fbo->stencilFormat = format; - pRenderBuffer = &fbo->stencilBuffer; - attachment = GL_STENCIL_ATTACHMENT_EXT; - break; - - case GL_DEPTH_STENCIL_EXT: - case GL_DEPTH24_STENCIL8_EXT: - fbo->packedDepthStencilFormat = format; - pRenderBuffer = &fbo->packedDepthStencilBuffer; - attachment = 0; // special for stencil and depth - break; - - default: - ri.Printf(PRINT_WARNING, "FBO_CreateBuffer: invalid format %d\n", format); - return; - } - - absent = *pRenderBuffer == 0; - if (absent) - qglGenRenderbuffersEXT(1, pRenderBuffer); - - qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, *pRenderBuffer); - if (multisample && glRefConfig.framebufferMultisample) - { - qglRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, multisample, format, fbo->width, fbo->height); - } - else - { - qglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, format, fbo->width, fbo->height); - } - - if(absent) - { - if (attachment == 0) - { - qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, *pRenderBuffer); - qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, *pRenderBuffer); - } - else - qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attachment, GL_RENDERBUFFER_EXT, *pRenderBuffer); - } -} - - -/* -================= -R_AttachFBOTexture1D -================= -*/ -void R_AttachFBOTexture1D(int texId, int index) -{ - if(index < 0 || index >= glRefConfig.maxColorAttachments) - { - ri.Printf(PRINT_WARNING, "R_AttachFBOTexture1D: invalid attachment index %i\n", index); - return; - } - - qglFramebufferTexture1DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, GL_TEXTURE_1D, texId, 0); -} - -/* -================= -R_AttachFBOTexture2D -================= -*/ -void R_AttachFBOTexture2D(int target, int texId, int index) -{ - if(target != GL_TEXTURE_2D && (target < GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB || target > GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB)) - { - ri.Printf(PRINT_WARNING, "R_AttachFBOTexture2D: invalid target %i\n", target); - return; - } - - if(index < 0 || index >= glRefConfig.maxColorAttachments) - { - ri.Printf(PRINT_WARNING, "R_AttachFBOTexture2D: invalid attachment index %i\n", index); - return; - } - - qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, target, texId, 0); -} - -/* -================= -R_AttachFBOTexture3D -================= -*/ -void R_AttachFBOTexture3D(int texId, int index, int zOffset) -{ - if(index < 0 || index >= glRefConfig.maxColorAttachments) - { - ri.Printf(PRINT_WARNING, "R_AttachFBOTexture3D: invalid attachment index %i\n", index); - return; - } - - qglFramebufferTexture3DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, GL_TEXTURE_3D_EXT, texId, 0, zOffset); -} - -/* -================= -R_AttachFBOTextureDepth -================= -*/ -void R_AttachFBOTextureDepth(int texId) -{ - qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, texId, 0); -} - -/* -================= -R_AttachFBOTexturePackedDepthStencil -================= -*/ -void R_AttachFBOTexturePackedDepthStencil(int texId) -{ - qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, texId, 0); - qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, texId, 0); -} - -void FBO_AttachTextureImage(image_t *img, int index) -{ - if (!glState.currentFBO) - { - ri.Printf(PRINT_WARNING, "FBO: attempted to attach a texture image with no FBO bound!\n"); - return; - } - - R_AttachFBOTexture2D(GL_TEXTURE_2D, img->texnum, index); - glState.currentFBO->colorImage[index] = img; -} - -/* -============ -FBO_Bind -============ -*/ -void FBO_Bind(FBO_t * fbo) -{ - if (glState.currentFBO == fbo) - return; - - if (r_logFile->integer) - { - // don't just call LogComment, or we will get a call to va() every frame! - if (fbo) - GLimp_LogComment(va("--- FBO_Bind( %s ) ---\n", fbo->name)); - else - GLimp_LogComment("--- FBO_Bind ( NULL ) ---\n"); - } - - if (!fbo) - { - qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - //qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); - glState.currentFBO = NULL; - - return; - } - - qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->frameBuffer); - - /* - if(fbo->colorBuffers[0]) - { - qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo->colorBuffers[0]); - } - */ - - /* - if(fbo->depthBuffer) - { - qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo->depthBuffer); - qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->depthBuffer); - } - */ - - glState.currentFBO = fbo; -} - -/* -============ -FBO_Init -============ -*/ -void FBO_Init(void) -{ - int i; - // int width, height, hdrFormat, multisample; - int hdrFormat, multisample; - - ri.Printf(PRINT_ALL, "------- FBO_Init -------\n"); - - if(!glRefConfig.framebufferObject) - return; - - tr.numFBOs = 0; - - GL_CheckErrors(); - - R_IssuePendingRenderCommands(); - -/* if(glRefConfig.textureNonPowerOfTwo) - { - width = glConfig.vidWidth; - height = glConfig.vidHeight; - } - else - { - width = NextPowerOfTwo(glConfig.vidWidth); - height = NextPowerOfTwo(glConfig.vidHeight); - } */ - - hdrFormat = GL_RGBA8; - if (r_hdr->integer && glRefConfig.framebufferObject && glRefConfig.textureFloat) - { - hdrFormat = GL_RGB16F_ARB; - } - - qglGetIntegerv(GL_MAX_SAMPLES_EXT, &multisample); - - if (r_ext_framebuffer_multisample->integer < multisample) - { - multisample = r_ext_framebuffer_multisample->integer; - } - - if (multisample < 2 || !glRefConfig.framebufferBlit) - multisample = 0; - - if (multisample != r_ext_framebuffer_multisample->integer) - { - ri.Cvar_SetValue("r_ext_framebuffer_multisample", (float)multisample); - } - - // only create a render FBO if we need to resolve MSAA or do HDR - // otherwise just render straight to the screen (tr.renderFbo = NULL) - if (multisample && glRefConfig.framebufferMultisample) - { - tr.renderFbo = FBO_Create("_render", tr.renderDepthImage->width, tr.renderDepthImage->height); - FBO_Bind(tr.renderFbo); - - FBO_CreateBuffer(tr.renderFbo, hdrFormat, 0, multisample); - FBO_CreateBuffer(tr.renderFbo, GL_DEPTH_COMPONENT24_ARB, 0, multisample); - - R_CheckFBO(tr.renderFbo); - - - tr.msaaResolveFbo = FBO_Create("_msaaResolve", tr.renderDepthImage->width, tr.renderDepthImage->height); - FBO_Bind(tr.msaaResolveFbo); - - //FBO_CreateBuffer(tr.msaaResolveFbo, hdrFormat, 0, 0); - FBO_AttachTextureImage(tr.renderImage, 0); - - //FBO_CreateBuffer(tr.msaaResolveFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0); - R_AttachFBOTextureDepth(tr.renderDepthImage->texnum); - - R_CheckFBO(tr.msaaResolveFbo); - } - else if (r_hdr->integer) - { - tr.renderFbo = FBO_Create("_render", tr.renderDepthImage->width, tr.renderDepthImage->height); - FBO_Bind(tr.renderFbo); - - //FBO_CreateBuffer(tr.renderFbo, hdrFormat, 0, 0); - FBO_AttachTextureImage(tr.renderImage, 0); - - //FBO_CreateBuffer(tr.renderFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0); - R_AttachFBOTextureDepth(tr.renderDepthImage->texnum); - - R_CheckFBO(tr.renderFbo); - } - - // clear render buffer - // this fixes the corrupt screen bug with r_hdr 1 on older hardware - if (tr.renderFbo) - { - FBO_Bind(tr.renderFbo); - qglClearColor( 1, 0, 0.5, 1 ); - qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - FBO_Bind(NULL); - } - - if (r_drawSunRays->integer) - { - tr.sunRaysFbo = FBO_Create("_sunRays", tr.renderDepthImage->width, tr.renderDepthImage->height); - FBO_Bind(tr.sunRaysFbo); - - FBO_AttachTextureImage(tr.sunRaysImage, 0); - - R_AttachFBOTextureDepth(tr.renderDepthImage->texnum); - - R_CheckFBO(tr.sunRaysFbo); - } - - // FIXME: Don't use separate color/depth buffers for a shadow buffer - for( i = 0; i < MAX_DRAWN_PSHADOWS; i++) - { - tr.pshadowFbos[i] = FBO_Create(va("_shadowmap%d", i), tr.pshadowMaps[i]->width, tr.pshadowMaps[i]->height); - FBO_Bind(tr.pshadowFbos[i]); - - //FBO_CreateBuffer(tr.pshadowFbos[i], GL_RGBA8, 0, 0); - FBO_AttachTextureImage(tr.pshadowMaps[i], 0); - - FBO_CreateBuffer(tr.pshadowFbos[i], GL_DEPTH_COMPONENT24_ARB, 0, 0); - //R_AttachFBOTextureDepth(tr.textureDepthImage->texnum); - - R_CheckFBO(tr.pshadowFbos[i]); - } - - for ( i = 0; i < 3; i++) - { - tr.sunShadowFbo[i] = FBO_Create("_sunshadowmap", tr.sunShadowDepthImage[i]->width, tr.sunShadowDepthImage[i]->height); - FBO_Bind(tr.sunShadowFbo[i]); - - //FBO_CreateBuffer(tr.sunShadowFbo[i], GL_RGBA8, 0, 0); - //FBO_AttachTextureImage(tr.sunShadowImage, 0); - qglDrawBuffer(GL_NONE); - qglReadBuffer(GL_NONE); - - //FBO_CreateBuffer(tr.sunShadowFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0); - R_AttachFBOTextureDepth(tr.sunShadowDepthImage[i]->texnum); - - R_CheckFBO(tr.sunShadowFbo[i]); - } - - for (i = 0; i < 2; i++) - { - tr.textureScratchFbo[i] = FBO_Create(va("_texturescratch%d", i), tr.textureScratchImage[i]->width, tr.textureScratchImage[i]->height); - FBO_Bind(tr.textureScratchFbo[i]); - - //FBO_CreateBuffer(tr.textureScratchFbo[i], GL_RGBA8, 0, 0); - FBO_AttachTextureImage(tr.textureScratchImage[i], 0); - - R_CheckFBO(tr.textureScratchFbo[i]); - } - - { - tr.calcLevelsFbo = FBO_Create("_calclevels", tr.calcLevelsImage->width, tr.calcLevelsImage->height); - FBO_Bind(tr.calcLevelsFbo); - - //FBO_CreateBuffer(tr.calcLevelsFbo, hdrFormat, 0, 0); - FBO_AttachTextureImage(tr.calcLevelsImage, 0); - - R_CheckFBO(tr.calcLevelsFbo); - } - - { - tr.targetLevelsFbo = FBO_Create("_targetlevels", tr.targetLevelsImage->width, tr.targetLevelsImage->height); - FBO_Bind(tr.targetLevelsFbo); - - //FBO_CreateBuffer(tr.targetLevelsFbo, hdrFormat, 0, 0); - FBO_AttachTextureImage(tr.targetLevelsImage, 0); - - R_CheckFBO(tr.targetLevelsFbo); - } - - if (r_softOverbright->integer) - { - //tr.screenScratchFbo = FBO_Create("_screenscratch", width, height); - tr.screenScratchFbo = FBO_Create("_screenscratch", tr.screenScratchImage->width, tr.screenScratchImage->height); - FBO_Bind(tr.screenScratchFbo); - - //FBO_CreateBuffer(tr.screenScratchFbo, format, 0, 0); - FBO_AttachTextureImage(tr.screenScratchImage, 0); - - // FIXME: hack: share zbuffer between render fbo and pre-screen fbo - //FBO_CreateBuffer(tr.screenScratchFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0); - R_AttachFBOTextureDepth(tr.renderDepthImage->texnum); - - R_CheckFBO(tr.screenScratchFbo); - } - - for (i = 0; i < 2; i++) - { - tr.quarterFbo[i] = FBO_Create(va("_quarter%d", i), tr.quarterImage[i]->width, tr.quarterImage[i]->height); - FBO_Bind(tr.quarterFbo[i]); - - //FBO_CreateBuffer(tr.quarterFbo[i], hdrFormat, 0, 0); - FBO_AttachTextureImage(tr.quarterImage[i], 0); - - R_CheckFBO(tr.quarterFbo[i]); - } - - { - tr.screenShadowFbo = FBO_Create("_screenshadow", tr.screenShadowImage->width, tr.screenShadowImage->height); - FBO_Bind(tr.screenShadowFbo); - - FBO_AttachTextureImage(tr.screenShadowImage, 0); - - R_CheckFBO(tr.screenShadowFbo); - } - - if (r_ssao->integer) - { - tr.hdrDepthFbo = FBO_Create("_hdrDepth", tr.hdrDepthImage->width, tr.hdrDepthImage->height); - FBO_Bind(tr.hdrDepthFbo); - - FBO_AttachTextureImage(tr.hdrDepthImage, 0); - - R_CheckFBO(tr.hdrDepthFbo); - - tr.screenSsaoFbo = FBO_Create("_screenssao", tr.screenSsaoImage->width, tr.screenSsaoImage->height); - FBO_Bind(tr.screenSsaoFbo); - - FBO_AttachTextureImage(tr.screenSsaoImage, 0); - - R_CheckFBO(tr.screenSsaoFbo); - } - - GL_CheckErrors(); - - FBO_Bind(NULL); -} - -/* -============ -FBO_Shutdown -============ -*/ -void FBO_Shutdown(void) -{ - int i, j; - FBO_t *fbo; - - ri.Printf(PRINT_ALL, "------- FBO_Shutdown -------\n"); - - if(!glRefConfig.framebufferObject) - return; - - FBO_Bind(NULL); - - for(i = 0; i < tr.numFBOs; i++) - { - fbo = tr.fbos[i]; - - for(j = 0; j < glRefConfig.maxColorAttachments; j++) - { - if(fbo->colorBuffers[j]) - qglDeleteRenderbuffersEXT(1, &fbo->colorBuffers[j]); - } - - if(fbo->depthBuffer) - qglDeleteRenderbuffersEXT(1, &fbo->depthBuffer); - - if(fbo->stencilBuffer) - qglDeleteRenderbuffersEXT(1, &fbo->stencilBuffer); - - if(fbo->frameBuffer) - qglDeleteFramebuffersEXT(1, &fbo->frameBuffer); - } -} - -/* -============ -R_FBOList_f -============ -*/ -void R_FBOList_f(void) -{ - int i; - FBO_t *fbo; - - if(!glRefConfig.framebufferObject) - { - ri.Printf(PRINT_ALL, "GL_EXT_framebuffer_object is not available.\n"); - return; - } - - ri.Printf(PRINT_ALL, " size name\n"); - ri.Printf(PRINT_ALL, "----------------------------------------------------------\n"); - - for(i = 0; i < tr.numFBOs; i++) - { - fbo = tr.fbos[i]; - - ri.Printf(PRINT_ALL, " %4i: %4i %4i %s\n", i, fbo->width, fbo->height, fbo->name); - } - - ri.Printf(PRINT_ALL, " %i FBOs\n", tr.numFBOs); -} - -// FIXME -extern void RB_SetGL2D (void); - -void FBO_BlitFromTexture(struct image_s *src, vec4i_t inSrcBox, vec2_t inSrcTexScale, FBO_t *dst, vec4i_t inDstBox, struct shaderProgram_s *shaderProgram, vec4_t inColor, int blend) -{ - vec4i_t dstBox, srcBox; - vec2_t srcTexScale; - vec4_t color; - vec4_t quadVerts[4]; - vec2_t texCoords[4]; - vec2_t invTexRes; - FBO_t *oldFbo = glState.currentFBO; - matrix_t projection; - int width, height; - - if (!src) - return; - - if (inSrcBox) - { - VectorSet4(srcBox, inSrcBox[0], inSrcBox[1], inSrcBox[0] + inSrcBox[2], inSrcBox[1] + inSrcBox[3]); - } - else - { - VectorSet4(srcBox, 0, 0, src->width, src->height); - } - - // framebuffers are 0 bottom, Y up. - if (inDstBox) - { - if (dst) - { - dstBox[0] = inDstBox[0]; - dstBox[1] = dst->height - inDstBox[1] - inDstBox[3]; - dstBox[2] = inDstBox[0] + inDstBox[2]; - dstBox[3] = dst->height - inDstBox[1]; - } - else - { - dstBox[0] = inDstBox[0]; - dstBox[1] = glConfig.vidHeight - inDstBox[1] - inDstBox[3]; - dstBox[2] = inDstBox[0] + inDstBox[2]; - dstBox[3] = glConfig.vidHeight - inDstBox[1]; - } - } - else if (dst) - { - VectorSet4(dstBox, 0, dst->height, dst->width, 0); - } - else - { - VectorSet4(dstBox, 0, glConfig.vidHeight, glConfig.vidWidth, 0); - } - - if (inSrcTexScale) - { - VectorCopy2(inSrcTexScale, srcTexScale); - } - else - { - srcTexScale[0] = srcTexScale[1] = 1.0f; - } - - if (inColor) - { - VectorCopy4(inColor, color); - } - else - { - color[0] = color[1] = color[2] = color[3] = 1.0f; - } - - if (!shaderProgram) - { - shaderProgram = &tr.textureColorShader; - } - - FBO_Bind(dst); - - if (glState.currentFBO) - { - width = glState.currentFBO->width; - height = glState.currentFBO->height; - } - else - { - width = glConfig.vidWidth; - height = glConfig.vidHeight; - } - - qglViewport( 0, 0, width, height ); - qglScissor( 0, 0, width, height ); - - Matrix16Ortho(0, width, height, 0, 0, 1, projection); - - qglDisable( GL_CULL_FACE ); - - GL_BindToTMU(src, TB_COLORMAP); - - VectorSet4(quadVerts[0], dstBox[0], dstBox[1], 0, 1); - VectorSet4(quadVerts[1], dstBox[2], dstBox[1], 0, 1); - VectorSet4(quadVerts[2], dstBox[2], dstBox[3], 0, 1); - VectorSet4(quadVerts[3], dstBox[0], dstBox[3], 0, 1); - - texCoords[0][0] = srcBox[0] / (float)src->width; texCoords[0][1] = 1.0f - srcBox[1] / (float)src->height; - texCoords[1][0] = srcBox[2] / (float)src->width; texCoords[1][1] = 1.0f - srcBox[1] / (float)src->height; - texCoords[2][0] = srcBox[2] / (float)src->width; texCoords[2][1] = 1.0f - srcBox[3] / (float)src->height; - texCoords[3][0] = srcBox[0] / (float)src->width; texCoords[3][1] = 1.0f - srcBox[3] / (float)src->height; - - invTexRes[0] = 1.0f / src->width * srcTexScale[0]; - invTexRes[1] = 1.0f / src->height * srcTexScale[1]; - - GL_State( blend ); - - GLSL_BindProgram(shaderProgram); - - GLSL_SetUniformMatrix16(shaderProgram, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, projection); - GLSL_SetUniformVec4(shaderProgram, TEXTURECOLOR_UNIFORM_COLOR, color); - GLSL_SetUniformVec2(shaderProgram, TEXTURECOLOR_UNIFORM_INVTEXRES, invTexRes); - GLSL_SetUniformVec2(shaderProgram, TEXTURECOLOR_UNIFORM_AUTOEXPOSUREMINMAX, tr.refdef.autoExposureMinMax); - GLSL_SetUniformVec3(shaderProgram, TEXTURECOLOR_UNIFORM_TONEMINAVGMAXLINEAR, tr.refdef.toneMinAvgMaxLinear); - - RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes); - - FBO_Bind(oldFbo); -} - -void FBO_Blit(FBO_t *src, vec4i_t inSrcBox, vec2_t srcTexScale, FBO_t *dst, vec4i_t dstBox, struct shaderProgram_s *shaderProgram, vec4_t color, int blend) -{ - vec4i_t srcBox; - - if (!src) - return; - - // framebuffers are 0 bottom, Y up. - if (inSrcBox) - { - srcBox[0] = inSrcBox[0]; - srcBox[1] = src->height - inSrcBox[1] - inSrcBox[3]; - srcBox[2] = inSrcBox[2]; - srcBox[3] = inSrcBox[3]; - } - else - { - VectorSet4(srcBox, 0, src->height, src->width, -src->height); - } - - FBO_BlitFromTexture(src->colorImage[0], srcBox, srcTexScale, dst, dstBox, shaderProgram, color, blend | GLS_DEPTHTEST_DISABLE); -} - -void FBO_FastBlit(FBO_t *src, vec4i_t srcBox, FBO_t *dst, vec4i_t dstBox, int buffers, int filter) -{ - vec4i_t srcBoxFinal, dstBoxFinal; - GLuint srcFb, dstFb; - - if (!glRefConfig.framebufferBlit) - { - FBO_Blit(src, srcBox, NULL, dst, dstBox, NULL, NULL, 0); - return; - } - - // get to a neutral state first - //FBO_Bind(NULL); - - srcFb = src ? src->frameBuffer : 0; - dstFb = dst ? dst->frameBuffer : 0; - - if (!srcBox) - { - if (src) - { - VectorSet4(srcBoxFinal, 0, 0, src->width, src->height); - } - else - { - VectorSet4(srcBoxFinal, 0, 0, glConfig.vidWidth, glConfig.vidHeight); - } - } - else - { - VectorSet4(srcBoxFinal, srcBox[0], srcBox[1], srcBox[0] + srcBox[2], srcBox[1] + srcBox[3]); - } - - if (!dstBox) - { - if (dst) - { - VectorSet4(dstBoxFinal, 0, 0, dst->width, dst->height); - } - else - { - VectorSet4(dstBoxFinal, 0, 0, glConfig.vidWidth, glConfig.vidHeight); - } - } - else - { - VectorSet4(dstBoxFinal, dstBox[0], dstBox[1], dstBox[0] + dstBox[2], dstBox[1] + dstBox[3]); - } - - qglBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, srcFb); - qglBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, dstFb); - qglBlitFramebufferEXT(srcBoxFinal[0], srcBoxFinal[1], srcBoxFinal[2], srcBoxFinal[3], - dstBoxFinal[0], dstBoxFinal[1], dstBoxFinal[2], dstBoxFinal[3], - buffers, filter); - - qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); - glState.currentFBO = NULL; -} diff --git a/src/rend2/tr_fbo.h b/src/rend2/tr_fbo.h deleted file mode 100644 index f0366251..00000000 --- a/src/rend2/tr_fbo.h +++ /dev/null @@ -1,64 +0,0 @@ -/* -=========================================================================== -Copyright (C) 2010 James Canete (use.less01@gmail.com) - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_fbo.h - -#ifndef __TR_FBO_H__ -#define __TR_FBO_H__ - -struct image_s; -struct shaderProgram_s; - -typedef struct FBO_s -{ - char name[MAX_QPATH]; - - int index; - - uint32_t frameBuffer; - - uint32_t colorBuffers[16]; - int colorFormat; - struct image_s *colorImage[16]; - - uint32_t depthBuffer; - int depthFormat; - - uint32_t stencilBuffer; - int stencilFormat; - - uint32_t packedDepthStencilBuffer; - int packedDepthStencilFormat; - - int width; - int height; -} FBO_t; - -void FBO_Bind(FBO_t *fbo); -void FBO_Init(void); -void FBO_Shutdown(void); - -void FBO_BlitFromTexture(struct image_s *src, vec4i_t srcBox, vec2_t srcTexScale, FBO_t *dst, vec4i_t dstBox, struct shaderProgram_s *shaderProgram, vec4_t color, int blend); -void FBO_Blit(FBO_t *src, vec4i_t srcBox, vec2_t srcTexScale, FBO_t *dst, vec4i_t dstBox, struct shaderProgram_s *shaderProgram, vec4_t color, int blend); -void FBO_FastBlit(FBO_t *src, vec4i_t srcBox, FBO_t *dst, vec4i_t dstBox, int buffers, int filter); - - -#endif diff --git a/src/rend2/tr_flares.c b/src/rend2/tr_flares.c deleted file mode 100644 index 8e6c321f..00000000 --- a/src/rend2/tr_flares.c +++ /dev/null @@ -1,532 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_flares.c - -#include "tr_local.h" - -/* -============================================================================= - -LIGHT FLARES - -A light flare is an effect that takes place inside the eye when bright light -sources are visible. The size of the flare reletive to the screen is nearly -constant, irrespective of distance, but the intensity should be proportional to the -projected area of the light source. - -A surface that has been flagged as having a light flare will calculate the depth -buffer value that its midpoint should have when the surface is added. - -After all opaque surfaces have been rendered, the depth buffer is read back for -each flare in view. If the point has not been obscured by a closer surface, the -flare should be drawn. - -Surfaces that have a repeated texture should never be flagged as flaring, because -there will only be a single flare added at the midpoint of the polygon. - -To prevent abrupt popping, the intensity of the flare is interpolated up and -down as it changes visibility. This involves scene to scene state, unlike almost -all other aspects of the renderer, and is complicated by the fact that a single -frame may have multiple scenes. - -RB_RenderFlares() will be called once per view (twice in a mirrored scene, potentially -up to five or more times in a frame with 3D status bar icons). - -============================================================================= -*/ - - -// flare states maintain visibility over multiple frames for fading -// layers: view, mirror, menu -typedef struct flare_s { - struct flare_s *next; // for active chain - - int addedFrame; - - qboolean inPortal; // true if in a portal view of the scene - int frameSceneNum; - void *surface; - int fogNum; - - int fadeTime; - - qboolean visible; // state of last test - float drawIntensity; // may be non 0 even if !visible due to fading - - int windowX, windowY; - float eyeZ; - - vec3_t origin; - vec3_t color; -} flare_t; - -#define MAX_FLARES 128 - -flare_t r_flareStructs[MAX_FLARES]; -flare_t *r_activeFlares, *r_inactiveFlares; - -int flareCoeff; - -/* -================== -R_ClearFlares -================== -*/ -void R_ClearFlares( void ) { - int i; - - Com_Memset( r_flareStructs, 0, sizeof( r_flareStructs ) ); - r_activeFlares = NULL; - r_inactiveFlares = NULL; - - for ( i = 0 ; i < MAX_FLARES ; i++ ) { - r_flareStructs[i].next = r_inactiveFlares; - r_inactiveFlares = &r_flareStructs[i]; - } -} - - -/* -================== -RB_AddFlare - -This is called at surface tesselation time -================== -*/ -void RB_AddFlare( void *surface, int fogNum, vec3_t point, vec3_t color, vec3_t normal ) { - int i; - flare_t *f; - vec3_t local; - float d = 1; - vec4_t eye, clip, normalized, window; - - backEnd.pc.c_flareAdds++; - - if(normal && (normal[0] || normal[1] || normal[2])) - { - VectorSubtract( backEnd.viewParms.or.origin, point, local ); - VectorNormalizeFast(local); - d = DotProduct(local, normal); - - // If the viewer is behind the flare don't add it. - if(d < 0) - return; - } - - // if the point is off the screen, don't bother adding it - // calculate screen coordinates and depth - R_TransformModelToClip( point, backEnd.or.modelMatrix, - backEnd.viewParms.projectionMatrix, eye, clip ); - - // check to see if the point is completely off screen - for ( i = 0 ; i < 3 ; i++ ) { - if ( clip[i] >= clip[3] || clip[i] <= -clip[3] ) { - return; - } - } - - R_TransformClipToWindow( clip, &backEnd.viewParms, normalized, window ); - - if ( window[0] < 0 || window[0] >= backEnd.viewParms.viewportWidth - || window[1] < 0 || window[1] >= backEnd.viewParms.viewportHeight ) { - return; // shouldn't happen, since we check the clip[] above, except for FP rounding - } - - // see if a flare with a matching surface, scene, and view exists - for ( f = r_activeFlares ; f ; f = f->next ) { - if ( f->surface == surface && f->frameSceneNum == backEnd.viewParms.frameSceneNum - && f->inPortal == backEnd.viewParms.isPortal ) { - break; - } - } - - // allocate a new one - if (!f ) { - if ( !r_inactiveFlares ) { - // the list is completely full - return; - } - f = r_inactiveFlares; - r_inactiveFlares = r_inactiveFlares->next; - f->next = r_activeFlares; - r_activeFlares = f; - - f->surface = surface; - f->frameSceneNum = backEnd.viewParms.frameSceneNum; - f->inPortal = backEnd.viewParms.isPortal; - f->addedFrame = -1; - } - - if ( f->addedFrame != backEnd.viewParms.frameCount - 1 ) { - f->visible = qfalse; - f->fadeTime = backEnd.refdef.time - 2000; - } - - f->addedFrame = backEnd.viewParms.frameCount; - f->fogNum = fogNum; - - VectorCopy(point, f->origin); - VectorCopy( color, f->color ); - - // fade the intensity of the flare down as the - // light surface turns away from the viewer - VectorScale( f->color, d, f->color ); - - // save info needed to test - f->windowX = backEnd.viewParms.viewportX + window[0]; - f->windowY = backEnd.viewParms.viewportY + window[1]; - - f->eyeZ = eye[2]; -} - -/* -================== -RB_AddDlightFlares -================== -*/ -void RB_AddDlightFlares( void ) { - dlight_t *l; - int i, j, k; - fog_t *fog = NULL; - - if ( !r_flares->integer ) { - return; - } - - l = backEnd.refdef.dlights; - - if(tr.world) - fog = tr.world->fogs; - - for (i=0 ; inumfogs ; j++ ) { - fog = &tr.world->fogs[j]; - for ( k = 0 ; k < 3 ; k++ ) { - if ( l->origin[k] < fog->bounds[0][k] || l->origin[k] > fog->bounds[1][k] ) { - break; - } - } - if ( k == 3 ) { - break; - } - } - if ( j == tr.world->numfogs ) { - j = 0; - } - } - else - j = 0; - - RB_AddFlare( (void *)l, j, l->origin, l->color, NULL ); - } -} - -/* -=============================================================================== - -FLARE BACK END - -=============================================================================== -*/ - -/* -================== -RB_TestFlare -================== -*/ -void RB_TestFlare( flare_t *f ) { - float depth; - qboolean visible; - float fade; - float screenZ; - - backEnd.pc.c_flareTests++; - - // doing a readpixels is as good as doing a glFinish(), so - // don't bother with another sync - glState.finishCalled = qfalse; - - // read back the z buffer contents - qglReadPixels( f->windowX, f->windowY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth ); - - screenZ = backEnd.viewParms.projectionMatrix[14] / - ( ( 2*depth - 1 ) * backEnd.viewParms.projectionMatrix[11] - backEnd.viewParms.projectionMatrix[10] ); - - visible = ( -f->eyeZ - -screenZ ) < 24; - - if ( visible ) { - if ( !f->visible ) { - f->visible = qtrue; - f->fadeTime = backEnd.refdef.time - 1; - } - fade = ( ( backEnd.refdef.time - f->fadeTime ) /1000.0f ) * r_flareFade->value; - } else { - if ( f->visible ) { - f->visible = qfalse; - f->fadeTime = backEnd.refdef.time - 1; - } - fade = 1.0f - ( ( backEnd.refdef.time - f->fadeTime ) / 1000.0f ) * r_flareFade->value; - } - - if ( fade < 0 ) { - fade = 0; - } - if ( fade > 1 ) { - fade = 1; - } - - f->drawIntensity = fade; -} - - -/* -================== -RB_RenderFlare -================== -*/ -void RB_RenderFlare( flare_t *f ) { - float size; - vec3_t color; - int iColor[3]; - float distance, intensity, factor; - byte fogFactors[3] = {255, 255, 255}; - - backEnd.pc.c_flareRenders++; - - // We don't want too big values anyways when dividing by distance. - if(f->eyeZ > -1.0f) - distance = 1.0f; - else - distance = -f->eyeZ; - - // calculate the flare size.. - size = backEnd.viewParms.viewportWidth * ( r_flareSize->value/640.0f + 8 / distance ); - -/* - * This is an alternative to intensity scaling. It changes the size of the flare on screen instead - * with growing distance. See in the description at the top why this is not the way to go. - // size will change ~ 1/r. - size = backEnd.viewParms.viewportWidth * (r_flareSize->value / (distance * -2.0f)); -*/ - -/* - * As flare sizes stay nearly constant with increasing distance we must decrease the intensity - * to achieve a reasonable visual result. The intensity is ~ (size^2 / distance^2) which can be - * got by considering the ratio of - * (flaresurface on screen) : (Surface of sphere defined by flare origin and distance from flare) - * An important requirement is: - * intensity <= 1 for all distances. - * - * The formula used here to compute the intensity is as follows: - * intensity = flareCoeff * size^2 / (distance + size*sqrt(flareCoeff))^2 - * As you can see, the intensity will have a max. of 1 when the distance is 0. - * The coefficient flareCoeff will determine the falloff speed with increasing distance. - */ - - factor = distance + size * sqrt(flareCoeff); - - intensity = flareCoeff * size * size / (factor * factor); - - VectorScale(f->color, f->drawIntensity * intensity, color); - -// Calculations for fogging - if(tr.world && f->fogNum < tr.world->numfogs) - { - tess.numVertexes = 1; - VectorCopy(f->origin, tess.xyz[0]); - tess.fogNum = f->fogNum; - - RB_CalcModulateColorsByFog(fogFactors); - - // We don't need to render the flare if colors are 0 anyways. - if(!(fogFactors[0] || fogFactors[1] || fogFactors[2])) - return; - } - - iColor[0] = color[0] * fogFactors[0]; - iColor[1] = color[1] * fogFactors[1]; - iColor[2] = color[2] * fogFactors[2]; - - RB_BeginSurface( tr.flareShader, f->fogNum ); - - // FIXME: use quadstamp? - tess.xyz[tess.numVertexes][0] = f->windowX - size; - tess.xyz[tess.numVertexes][1] = f->windowY - size; - tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 0; - tess.vertexColors[tess.numVertexes][0] = iColor[0] / 255.0f; - tess.vertexColors[tess.numVertexes][1] = iColor[1] / 255.0f; - tess.vertexColors[tess.numVertexes][2] = iColor[2] / 255.0f; - tess.vertexColors[tess.numVertexes][3] = 1.0f; - tess.numVertexes++; - - tess.xyz[tess.numVertexes][0] = f->windowX - size; - tess.xyz[tess.numVertexes][1] = f->windowY + size; - tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 1; - tess.vertexColors[tess.numVertexes][0] = iColor[0] / 255.0f; - tess.vertexColors[tess.numVertexes][1] = iColor[1] / 255.0f; - tess.vertexColors[tess.numVertexes][2] = iColor[2] / 255.0f; - tess.vertexColors[tess.numVertexes][3] = 1.0f; - tess.numVertexes++; - - tess.xyz[tess.numVertexes][0] = f->windowX + size; - tess.xyz[tess.numVertexes][1] = f->windowY + size; - tess.texCoords[tess.numVertexes][0][0] = 1; - tess.texCoords[tess.numVertexes][0][1] = 1; - tess.vertexColors[tess.numVertexes][0] = iColor[0] / 255.0f; - tess.vertexColors[tess.numVertexes][1] = iColor[1] / 255.0f; - tess.vertexColors[tess.numVertexes][2] = iColor[2] / 255.0f; - tess.vertexColors[tess.numVertexes][3] = 1.0f; - tess.numVertexes++; - - tess.xyz[tess.numVertexes][0] = f->windowX + size; - tess.xyz[tess.numVertexes][1] = f->windowY - size; - tess.texCoords[tess.numVertexes][0][0] = 1; - tess.texCoords[tess.numVertexes][0][1] = 0; - tess.vertexColors[tess.numVertexes][0] = iColor[0] / 255.0f; - tess.vertexColors[tess.numVertexes][1] = iColor[1] / 255.0f; - tess.vertexColors[tess.numVertexes][2] = iColor[2] / 255.0f; - tess.vertexColors[tess.numVertexes][3] = 1.0f; - tess.numVertexes++; - - tess.indexes[tess.numIndexes++] = 0; - tess.indexes[tess.numIndexes++] = 1; - tess.indexes[tess.numIndexes++] = 2; - tess.indexes[tess.numIndexes++] = 0; - tess.indexes[tess.numIndexes++] = 2; - tess.indexes[tess.numIndexes++] = 3; - - RB_EndSurface(); -} - -/* -================== -RB_RenderFlares - -Because flares are simulating an occular effect, they should be drawn after -everything (all views) in the entire frame has been drawn. - -Because of the way portals use the depth buffer to mark off areas, the -needed information would be lost after each view, so we are forced to draw -flares after each view. - -The resulting artifact is that flares in mirrors or portals don't dim properly -when occluded by something in the main view, and portal flares that should -extend past the portal edge will be overwritten. -================== -*/ -void RB_RenderFlares (void) { - flare_t *f; - flare_t **prev; - qboolean draw; - matrix_t oldmodelview, oldprojection, matrix; - - if ( !r_flares->integer ) { - return; - } - - if(r_flareCoeff->modified) - { - if(r_flareCoeff->value == 0.0f) - flareCoeff = atof(FLARE_STDCOEFF); - else - flareCoeff = r_flareCoeff->value; - - r_flareCoeff->modified = qfalse; - } - - // Reset currentEntity to world so that any previously referenced entities - // don't have influence on the rendering of these flares (i.e. RF_ renderer flags). - backEnd.currentEntity = &tr.worldEntity; - backEnd.or = backEnd.viewParms.world; - -// RB_AddDlightFlares(); - - // perform z buffer readback on each flare in this view - draw = qfalse; - prev = &r_activeFlares; - while ( ( f = *prev ) != NULL ) { - // throw out any flares that weren't added last frame - if ( f->addedFrame < backEnd.viewParms.frameCount - 1 ) { - *prev = f->next; - f->next = r_inactiveFlares; - r_inactiveFlares = f; - continue; - } - - // don't draw any here that aren't from this scene / portal - f->drawIntensity = 0; - if ( f->frameSceneNum == backEnd.viewParms.frameSceneNum - && f->inPortal == backEnd.viewParms.isPortal ) { - RB_TestFlare( f ); - if ( f->drawIntensity ) { - draw = qtrue; - } else { - // this flare has completely faded out, so remove it from the chain - *prev = f->next; - f->next = r_inactiveFlares; - r_inactiveFlares = f; - continue; - } - } - - prev = &f->next; - } - - if ( !draw ) { - return; // none visible - } - - if ( backEnd.viewParms.isPortal ) { - qglDisable (GL_CLIP_PLANE0); - } - - Matrix16Copy(glState.projection, oldprojection); - Matrix16Copy(glState.modelview, oldmodelview); - Matrix16Identity(matrix); - GL_SetModelviewMatrix(matrix); - Matrix16Ortho( backEnd.viewParms.viewportX, backEnd.viewParms.viewportX + backEnd.viewParms.viewportWidth, - backEnd.viewParms.viewportY, backEnd.viewParms.viewportY + backEnd.viewParms.viewportHeight, - -99999, 99999, matrix ); - GL_SetProjectionMatrix(matrix); - - for ( f = r_activeFlares ; f ; f = f->next ) { - if ( f->frameSceneNum == backEnd.viewParms.frameSceneNum - && f->inPortal == backEnd.viewParms.isPortal - && f->drawIntensity ) { - RB_RenderFlare( f ); - } - } - - GL_SetProjectionMatrix(oldprojection); - GL_SetModelviewMatrix(oldmodelview); -} - - - - - diff --git a/src/rend2/tr_font.c b/src/rend2/tr_font.c deleted file mode 100644 index 21226709..00000000 --- a/src/rend2/tr_font.c +++ /dev/null @@ -1,554 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_font.c -// -// -// The font system uses FreeType 2.x to render TrueType fonts for use within the game. -// As of this writing ( Nov, 2000 ) Team Arena uses these fonts for all of the ui and -// about 90% of the cgame presentation. A few areas of the CGAME were left uses the old -// fonts since the code is shared with standard Q3A. -// -// If you include this font rendering code in a commercial product you MUST include the -// following somewhere with your product, see www.freetype.org for specifics or changes. -// The Freetype code also uses some hinting techniques that MIGHT infringe on patents -// held by apple so be aware of that also. -// -// As of Q3A 1.25+ and Team Arena, we are shipping the game with the font rendering code -// disabled. This removes any potential patent issues and it keeps us from having to -// distribute an actual TrueTrype font which is 1. expensive to do and 2. seems to require -// an act of god to accomplish. -// -// What we did was pre-render the fonts using FreeType ( which is why we leave the FreeType -// credit in the credits ) and then saved off the glyph data and then hand touched up the -// font bitmaps so they scale a bit better in GL. -// -// There are limitations in the way fonts are saved and reloaded in that it is based on -// point size and not name. So if you pre-render Helvetica in 18 point and Impact in 18 point -// you will end up with a single 18 point data file and image set. Typically you will want to -// choose 3 sizes to best approximate the scaling you will be doing in the ui scripting system -// -// In the UI Scripting code, a scale of 1.0 is equal to a 48 point font. In Team Arena, we -// use three or four scales, most of them exactly equaling the specific rendered size. We -// rendered three sizes in Team Arena, 12, 16, and 20. -// -// To generate new font data you need to go through the following steps. -// 1. delete the fontImage_x_xx.tga files and fontImage_xx.dat files from the fonts path. -// 2. in a ui script, specificy a font, smallFont, and bigFont keyword with font name and -// point size. the original TrueType fonts must exist in fonts at this point. -// 3. run the game, you should see things normally. -// 4. Exit the game and there will be three dat files and at least three tga files. The -// tga's are in 256x256 pages so if it takes three images to render a 24 point font you -// will end up with fontImage_0_24.tga through fontImage_2_24.tga -// 5. In future runs of the game, the system looks for these images and data files when a s -// specific point sized font is rendered and loads them for use. -// 6. Because of the original beta nature of the FreeType code you will probably want to hand -// touch the font bitmaps. -// -// Currently a define in the project turns on or off the FreeType code which is currently -// defined out. To pre-render new fonts you need enable the define ( BUILD_FREETYPE ) and -// uncheck the exclude from build check box in the FreeType2 area of the Renderer project. - - -#include "tr_local.h" -#include "../qcommon/qcommon.h" - -#ifdef BUILD_FREETYPE -#include -#include FT_ERRORS_H -#include FT_SYSTEM_H -#include FT_IMAGE_H -#include FT_FREETYPE_H -#include FT_OUTLINE_H - -#define _FLOOR(x) ((x) & -64) -#define _CEIL(x) (((x)+63) & -64) -#define _TRUNC(x) ((x) >> 6) - -FT_Library ftLibrary = NULL; -#endif - -#define MAX_FONTS 6 -static int registeredFontCount = 0; -static fontInfo_t registeredFont[MAX_FONTS]; - -#ifdef BUILD_FREETYPE -void R_GetGlyphInfo(FT_GlyphSlot glyph, int *left, int *right, int *width, int *top, int *bottom, int *height, int *pitch) { - *left = _FLOOR( glyph->metrics.horiBearingX ); - *right = _CEIL( glyph->metrics.horiBearingX + glyph->metrics.width ); - *width = _TRUNC(*right - *left); - - *top = _CEIL( glyph->metrics.horiBearingY ); - *bottom = _FLOOR( glyph->metrics.horiBearingY - glyph->metrics.height ); - *height = _TRUNC( *top - *bottom ); - *pitch = ( qtrue ? (*width+3) & -4 : (*width+7) >> 3 ); -} - - -FT_Bitmap *R_RenderGlyph(FT_GlyphSlot glyph, glyphInfo_t* glyphOut) { - FT_Bitmap *bit2; - int left, right, width, top, bottom, height, pitch, size; - - R_GetGlyphInfo(glyph, &left, &right, &width, &top, &bottom, &height, &pitch); - - if ( glyph->format == ft_glyph_format_outline ) { - size = pitch*height; - - bit2 = ri.Malloc(sizeof(FT_Bitmap)); - - bit2->width = width; - bit2->rows = height; - bit2->pitch = pitch; - bit2->pixel_mode = ft_pixel_mode_grays; - //bit2->pixel_mode = ft_pixel_mode_mono; - bit2->buffer = ri.Malloc(pitch*height); - bit2->num_grays = 256; - - Com_Memset( bit2->buffer, 0, size ); - - FT_Outline_Translate( &glyph->outline, -left, -bottom ); - - FT_Outline_Get_Bitmap( ftLibrary, &glyph->outline, bit2 ); - - glyphOut->height = height; - glyphOut->pitch = pitch; - glyphOut->top = (glyph->metrics.horiBearingY >> 6) + 1; - glyphOut->bottom = bottom; - - return bit2; - } else { - ri.Printf(PRINT_ALL, "Non-outline fonts are not supported\n"); - } - return NULL; -} - -void WriteTGA (char *filename, byte *data, int width, int height) { - byte *buffer; - int i, c; - int row; - unsigned char *flip; - unsigned char *src, *dst; - - buffer = ri.Malloc(width*height*4 + 18); - Com_Memset (buffer, 0, 18); - buffer[2] = 2; // uncompressed type - buffer[12] = width&255; - buffer[13] = width>>8; - buffer[14] = height&255; - buffer[15] = height>>8; - buffer[16] = 32; // pixel size - - // swap rgb to bgr - c = 18 + width * height * 4; - for (i=18 ; iglyph, &glyph); - if (bitmap) { - glyph.xSkip = (face->glyph->metrics.horiAdvance >> 6) + 1; - } else { - return &glyph; - } - - if (glyph.height > *maxHeight) { - *maxHeight = glyph.height; - } - - if (calcHeight) { - ri.Free(bitmap->buffer); - ri.Free(bitmap); - return &glyph; - } - -/* - // need to convert to power of 2 sizes so we do not get - // any scaling from the gl upload - for (scaled_width = 1 ; scaled_width < glyph.pitch ; scaled_width<<=1) - ; - for (scaled_height = 1 ; scaled_height < glyph.height ; scaled_height<<=1) - ; -*/ - - scaled_width = glyph.pitch; - scaled_height = glyph.height; - - // we need to make sure we fit - if (*xOut + scaled_width + 1 >= 255) { - *xOut = 0; - *yOut += *maxHeight + 1; - } - - if (*yOut + *maxHeight + 1 >= 255) { - *yOut = -1; - *xOut = -1; - ri.Free(bitmap->buffer); - ri.Free(bitmap); - return &glyph; - } - - - src = bitmap->buffer; - dst = imageOut + (*yOut * 256) + *xOut; - - if (bitmap->pixel_mode == ft_pixel_mode_mono) { - for (i = 0; i < glyph.height; i++) { - int j; - unsigned char *_src = src; - unsigned char *_dst = dst; - unsigned char mask = 0x80; - unsigned char val = *_src; - for (j = 0; j < glyph.pitch; j++) { - if (mask == 0x80) { - val = *_src++; - } - if (val & mask) { - *_dst = 0xff; - } - mask >>= 1; - - if ( mask == 0 ) { - mask = 0x80; - } - _dst++; - } - - src += glyph.pitch; - dst += 256; - } - } else { - for (i = 0; i < glyph.height; i++) { - Com_Memcpy(dst, src, glyph.pitch); - src += glyph.pitch; - dst += 256; - } - } - - // we now have an 8 bit per pixel grey scale bitmap - // that is width wide and pf->ftSize->metrics.y_ppem tall - - glyph.imageHeight = scaled_height; - glyph.imageWidth = scaled_width; - glyph.s = (float)*xOut / 256; - glyph.t = (float)*yOut / 256; - glyph.s2 = glyph.s + (float)scaled_width / 256; - glyph.t2 = glyph.t + (float)scaled_height / 256; - - *xOut += scaled_width + 1; - - ri.Free(bitmap->buffer); - ri.Free(bitmap); - } - - return &glyph; -} -#endif - -static int fdOffset; -static byte *fdFile; - -int readInt( void ) { - int i = fdFile[fdOffset]+(fdFile[fdOffset+1]<<8)+(fdFile[fdOffset+2]<<16)+(fdFile[fdOffset+3]<<24); - fdOffset += 4; - return i; -} - -typedef union { - byte fred[4]; - float ffred; -} poor; - -float readFloat( void ) { - poor me; -#if defined Q3_BIG_ENDIAN - me.fred[0] = fdFile[fdOffset+3]; - me.fred[1] = fdFile[fdOffset+2]; - me.fred[2] = fdFile[fdOffset+1]; - me.fred[3] = fdFile[fdOffset+0]; -#elif defined Q3_LITTLE_ENDIAN - me.fred[0] = fdFile[fdOffset+0]; - me.fred[1] = fdFile[fdOffset+1]; - me.fred[2] = fdFile[fdOffset+2]; - me.fred[3] = fdFile[fdOffset+3]; -#endif - fdOffset += 4; - return me.ffred; -} - -void RE_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font) { -#ifdef BUILD_FREETYPE - FT_Face face; - int j, k, xOut, yOut, lastStart, imageNumber; - int scaledSize, newSize, maxHeight, left; - unsigned char *out, *imageBuff; - glyphInfo_t *glyph; - image_t *image; - qhandle_t h; - float max; - float dpi = 72; - float glyphScale; -#endif - void *faceData; - int i, len; - char name[1024]; - - if (!fontName) { - ri.Printf(PRINT_ALL, "RE_RegisterFont: called with empty name\n"); - return; - } - - if (pointSize <= 0) { - pointSize = 12; - } - - R_IssuePendingRenderCommands(); - - if (registeredFontCount >= MAX_FONTS) { - ri.Printf(PRINT_WARNING, "RE_RegisterFont: Too many fonts registered already.\n"); - return; - } - - Com_sprintf(name, sizeof(name), "fonts/fontImage_%i.dat",pointSize); - for (i = 0; i < registeredFontCount; i++) { - if (Q_stricmp(name, registeredFont[i].name) == 0) { - Com_Memcpy(font, ®isteredFont[i], sizeof(fontInfo_t)); - return; - } - } - - len = ri.FS_ReadFile(name, NULL); - if (len == sizeof(fontInfo_t)) { - ri.FS_ReadFile(name, &faceData); - fdOffset = 0; - fdFile = faceData; - for(i=0; iglyphs[i].height = readInt(); - font->glyphs[i].top = readInt(); - font->glyphs[i].bottom = readInt(); - font->glyphs[i].pitch = readInt(); - font->glyphs[i].xSkip = readInt(); - font->glyphs[i].imageWidth = readInt(); - font->glyphs[i].imageHeight = readInt(); - font->glyphs[i].s = readFloat(); - font->glyphs[i].t = readFloat(); - font->glyphs[i].s2 = readFloat(); - font->glyphs[i].t2 = readFloat(); - font->glyphs[i].glyph = readInt(); - Q_strncpyz(font->glyphs[i].shaderName, (const char *)&fdFile[fdOffset], sizeof(font->glyphs[i].shaderName)); - fdOffset += sizeof(font->glyphs[i].shaderName); - } - font->glyphScale = readFloat(); - Com_Memcpy(font->name, &fdFile[fdOffset], MAX_QPATH); - -// Com_Memcpy(font, faceData, sizeof(fontInfo_t)); - Q_strncpyz(font->name, name, sizeof(font->name)); - for (i = GLYPH_START; i < GLYPH_END; i++) { - font->glyphs[i].glyph = RE_RegisterShaderNoMip(font->glyphs[i].shaderName); - } - Com_Memcpy(®isteredFont[registeredFontCount++], font, sizeof(fontInfo_t)); - return; - } - -#ifndef BUILD_FREETYPE - ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType code not available\n"); -#else - if (ftLibrary == NULL) { - ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType not initialized.\n"); - return; - } - - len = ri.FS_ReadFile(fontName, &faceData); - if (len <= 0) { - ri.Printf(PRINT_WARNING, "RE_RegisterFont: Unable to read font file '%s'\n", fontName); - return; - } - - // allocate on the stack first in case we fail - if (FT_New_Memory_Face( ftLibrary, faceData, len, 0, &face )) { - ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType, unable to allocate new face.\n"); - return; - } - - - if (FT_Set_Char_Size( face, pointSize << 6, pointSize << 6, dpi, dpi)) { - ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType, unable to set face char size.\n"); - return; - } - - //*font = ®isteredFonts[registeredFontCount++]; - - // make a 256x256 image buffer, once it is full, register it, clean it and keep going - // until all glyphs are rendered - - out = ri.Malloc(1024*1024); - if (out == NULL) { - ri.Printf(PRINT_WARNING, "RE_RegisterFont: ri.Malloc failure during output image creation.\n"); - return; - } - Com_Memset(out, 0, 1024*1024); - - maxHeight = 0; - - for (i = GLYPH_START; i < GLYPH_END; i++) { - RE_ConstructGlyphInfo(out, &xOut, &yOut, &maxHeight, face, (unsigned char)i, qtrue); - } - - xOut = 0; - yOut = 0; - i = GLYPH_START; - lastStart = i; - imageNumber = 0; - - while ( i <= GLYPH_END ) { - - glyph = RE_ConstructGlyphInfo(out, &xOut, &yOut, &maxHeight, face, (unsigned char)i, qfalse); - - if (xOut == -1 || yOut == -1 || i == GLYPH_END) { - // ran out of room - // we need to create an image from the bitmap, set all the handles in the glyphs to this point - // - - scaledSize = 256*256; - newSize = scaledSize * 4; - imageBuff = ri.Malloc(newSize); - left = 0; - max = 0; - for ( k = 0; k < (scaledSize) ; k++ ) { - if (max < out[k]) { - max = out[k]; - } - } - - if (max > 0) { - max = 255/max; - } - - for ( k = 0; k < (scaledSize) ; k++ ) { - imageBuff[left++] = 255; - imageBuff[left++] = 255; - imageBuff[left++] = 255; - - imageBuff[left++] = ((float)out[k] * max); - } - - Com_sprintf (name, sizeof(name), "fonts/fontImage_%i_%i.tga", imageNumber++, pointSize); - if (r_saveFontData->integer) { - WriteTGA(name, imageBuff, 256, 256); - } - - //Com_sprintf (name, sizeof(name), "fonts/fontImage_%i_%i", imageNumber++, pointSize); - image = R_CreateImage(name, imageBuff, 256, 256, IMGTYPE_COLORALPHA, IMGFLAG_CLAMPTOEDGE, 0 ); - h = RE_RegisterShaderFromImage(name, LIGHTMAP_2D, image, qfalse); - for (j = lastStart; j < i; j++) { - font->glyphs[j].glyph = h; - Q_strncpyz(font->glyphs[j].shaderName, name, sizeof(font->glyphs[j].shaderName)); - } - lastStart = i; - Com_Memset(out, 0, 1024*1024); - xOut = 0; - yOut = 0; - ri.Free(imageBuff); - i++; - } else { - Com_Memcpy(&font->glyphs[i], glyph, sizeof(glyphInfo_t)); - i++; - } - } - - // change the scale to be relative to 1 based on 72 dpi ( so dpi of 144 means a scale of .5 ) - glyphScale = 72.0f / dpi; - - // we also need to adjust the scale based on point size relative to 48 points as the ui scaling is based on a 48 point font - glyphScale *= 48.0f / pointSize; - - registeredFont[registeredFontCount].glyphScale = glyphScale; - font->glyphScale = glyphScale; - Com_Memcpy(®isteredFont[registeredFontCount++], font, sizeof(fontInfo_t)); - - if (r_saveFontData->integer) { - ri.FS_WriteFile(va("fonts/fontImage_%i.dat", pointSize), font, sizeof(fontInfo_t)); - } - - ri.Free(out); - - ri.FS_FreeFile(faceData); -#endif -} - - - -void R_InitFreeType(void) { -#ifdef BUILD_FREETYPE - if (FT_Init_FreeType( &ftLibrary )) { - ri.Printf(PRINT_WARNING, "R_InitFreeType: Unable to initialize FreeType.\n"); - } -#endif - registeredFontCount = 0; -} - - -void R_DoneFreeType(void) { -#ifdef BUILD_FREETYPE - if (ftLibrary) { - FT_Done_FreeType( ftLibrary ); - ftLibrary = NULL; - } -#endif - registeredFontCount = 0; -} - diff --git a/src/rend2/tr_glsl.c b/src/rend2/tr_glsl.c deleted file mode 100644 index ccfea909..00000000 --- a/src/rend2/tr_glsl.c +++ /dev/null @@ -1,1932 +0,0 @@ -/* -=========================================================================== -Copyright (C) 2006-2009 Robert Beckebans - -This file is part of XreaL source code. - -XreaL source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -XreaL source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with XreaL source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_glsl.c -#include "tr_local.h" - -void GLSL_BindNullProgram(void); - -extern const char *fallbackShader_bokeh_vp; -extern const char *fallbackShader_bokeh_fp; -extern const char *fallbackShader_calclevels4x_vp; -extern const char *fallbackShader_calclevels4x_fp; -extern const char *fallbackShader_depthblur_vp; -extern const char *fallbackShader_depthblur_fp; -extern const char *fallbackShader_dlight_vp; -extern const char *fallbackShader_dlight_fp; -extern const char *fallbackShader_down4x_vp; -extern const char *fallbackShader_down4x_fp; -extern const char *fallbackShader_fogpass_vp; -extern const char *fallbackShader_fogpass_fp; -extern const char *fallbackShader_generic_vp; -extern const char *fallbackShader_generic_fp; -extern const char *fallbackShader_lightall_vp; -extern const char *fallbackShader_lightall_fp; -extern const char *fallbackShader_pshadow_vp; -extern const char *fallbackShader_pshadow_fp; -extern const char *fallbackShader_shadowfill_vp; -extern const char *fallbackShader_shadowfill_fp; -extern const char *fallbackShader_shadowmask_vp; -extern const char *fallbackShader_shadowmask_fp; -extern const char *fallbackShader_ssao_vp; -extern const char *fallbackShader_ssao_fp; -extern const char *fallbackShader_texturecolor_vp; -extern const char *fallbackShader_texturecolor_fp; -extern const char *fallbackShader_tonemap_vp; -extern const char *fallbackShader_tonemap_fp; - -static void GLSL_PrintInfoLog(GLhandleARB object, qboolean developerOnly) -{ - char *msg; - static char msgPart[1024]; - int maxLength = 0; - int i; - int printLevel = developerOnly ? PRINT_DEVELOPER : PRINT_ALL; - - qglGetObjectParameterivARB(object, GL_OBJECT_INFO_LOG_LENGTH_ARB, &maxLength); - - if (maxLength <= 0) - { - ri.Printf(printLevel, "No compile log.\n"); - return; - } - - ri.Printf(printLevel, "compile log:\n"); - - if (maxLength < 1023) - { - qglGetInfoLogARB(object, maxLength, &maxLength, msgPart); - - msgPart[maxLength + 1] = '\0'; - - ri.Printf(printLevel, "%s\n", msgPart); - } - else - { - msg = ri.Malloc(maxLength); - - qglGetInfoLogARB(object, maxLength, &maxLength, msg); - - for(i = 0; i < maxLength; i += 1024) - { - Q_strncpyz(msgPart, msg + i, sizeof(msgPart)); - - ri.Printf(printLevel, "%s\n", msgPart); - } - - ri.Free(msg); - } -} - -static void GLSL_PrintShaderSource(GLhandleARB object) -{ - char *msg; - static char msgPart[1024]; - int maxLength = 0; - int i; - - qglGetObjectParameterivARB(object, GL_OBJECT_SHADER_SOURCE_LENGTH_ARB, &maxLength); - - msg = ri.Malloc(maxLength); - - qglGetShaderSourceARB(object, maxLength, &maxLength, msg); - - for(i = 0; i < maxLength; i += 1024) - { - Q_strncpyz(msgPart, msg + i, sizeof(msgPart)); - ri.Printf(PRINT_ALL, "%s\n", msgPart); - } - - ri.Free(msg); -} - -static void GLSL_GetShaderHeader( GLenum shaderType, const GLcharARB *extra, char *dest, int size ) -{ - float fbufWidthScale, fbufHeightScale; - - dest[0] = '\0'; - - // HACK: abuse the GLSL preprocessor to turn GLSL 1.20 shaders into 1.30 ones - if(glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 30)) - { - Q_strcat(dest, size, "#version 130\n"); - - if(shaderType == GL_VERTEX_SHADER_ARB) - { - Q_strcat(dest, size, "#define attribute in\n"); - Q_strcat(dest, size, "#define varying out\n"); - } - else - { - Q_strcat(dest, size, "#define varying in\n"); - - Q_strcat(dest, size, "out vec4 out_Color;\n"); - Q_strcat(dest, size, "#define gl_FragColor out_Color\n"); - } - } - else - { - Q_strcat(dest, size, "#version 120\n"); - } - - // HACK: add some macros to avoid extra uniforms and save speed and code maintenance - //Q_strcat(dest, size, - // va("#ifndef r_SpecularExponent\n#define r_SpecularExponent %f\n#endif\n", r_specularExponent->value)); - //Q_strcat(dest, size, - // va("#ifndef r_SpecularScale\n#define r_SpecularScale %f\n#endif\n", r_specularScale->value)); - //Q_strcat(dest, size, - // va("#ifndef r_NormalScale\n#define r_NormalScale %f\n#endif\n", r_normalScale->value)); - - - Q_strcat(dest, size, "#ifndef M_PI\n#define M_PI 3.14159265358979323846f\n#endif\n"); - - //Q_strcat(dest, size, va("#ifndef MAX_SHADOWMAPS\n#define MAX_SHADOWMAPS %i\n#endif\n", MAX_SHADOWMAPS)); - - Q_strcat(dest, size, - va("#ifndef deformGen_t\n" - "#define deformGen_t\n" - "#define DGEN_WAVE_SIN %i\n" - "#define DGEN_WAVE_SQUARE %i\n" - "#define DGEN_WAVE_TRIANGLE %i\n" - "#define DGEN_WAVE_SAWTOOTH %i\n" - "#define DGEN_WAVE_INVERSE_SAWTOOTH %i\n" - "#define DGEN_BULGE %i\n" - "#define DGEN_MOVE %i\n" - "#endif\n", - DGEN_WAVE_SIN, - DGEN_WAVE_SQUARE, - DGEN_WAVE_TRIANGLE, - DGEN_WAVE_SAWTOOTH, - DGEN_WAVE_INVERSE_SAWTOOTH, - DGEN_BULGE, - DGEN_MOVE)); - - Q_strcat(dest, size, - va("#ifndef tcGen_t\n" - "#define tcGen_t\n" - "#define TCGEN_LIGHTMAP %i\n" - "#define TCGEN_TEXTURE %i\n" - "#define TCGEN_ENVIRONMENT_MAPPED %i\n" - "#define TCGEN_FOG %i\n" - "#define TCGEN_VECTOR %i\n" - "#endif\n", - TCGEN_LIGHTMAP, - TCGEN_TEXTURE, - TCGEN_ENVIRONMENT_MAPPED, - TCGEN_FOG, - TCGEN_VECTOR)); - - Q_strcat(dest, size, - va("#ifndef colorGen_t\n" - "#define colorGen_t\n" - "#define CGEN_LIGHTING_DIFFUSE %i\n" - "#endif\n", - CGEN_LIGHTING_DIFFUSE)); - - Q_strcat(dest, size, - va("#ifndef alphaGen_t\n" - "#define alphaGen_t\n" - "#define AGEN_LIGHTING_SPECULAR %i\n" - "#define AGEN_PORTAL %i\n" - "#define AGEN_FRESNEL %i\n" - "#endif\n", - AGEN_LIGHTING_SPECULAR, - AGEN_PORTAL, - AGEN_FRESNEL)); - - Q_strcat(dest, size, - va("#ifndef texenv_t\n" - "#define texenv_t\n" - "#define TEXENV_MODULATE %i\n" - "#define TEXENV_ADD %i\n" - "#define TEXENV_REPLACE %i\n" - "#endif\n", - GL_MODULATE, - GL_ADD, - GL_REPLACE)); - - fbufWidthScale = 1.0f / ((float)glConfig.vidWidth); - fbufHeightScale = 1.0f / ((float)glConfig.vidHeight); - Q_strcat(dest, size, - va("#ifndef r_FBufScale\n#define r_FBufScale vec2(%f, %f)\n#endif\n", fbufWidthScale, fbufHeightScale)); - - if (extra) - { - Q_strcat(dest, size, extra); - } - - // OK we added a lot of stuff but if we do something bad in the GLSL shaders then we want the proper line - // so we have to reset the line counting - Q_strcat(dest, size, "#line 0\n"); -} - -static int GLSL_CompileGPUShader(GLhandleARB program, GLhandleARB *prevShader, const GLcharARB *buffer, int size, GLenum shaderType) -{ - GLint compiled; - GLhandleARB shader; - - shader = qglCreateShaderObjectARB(shaderType); - - qglShaderSourceARB(shader, 1, (const GLcharARB **)&buffer, &size); - - // compile shader - qglCompileShaderARB(shader); - - // check if shader compiled - qglGetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &compiled); - if(!compiled) - { - GLSL_PrintShaderSource(shader); - GLSL_PrintInfoLog(shader, qfalse); - ri.Error(ERR_DROP, "Couldn't compile shader"); - return 0; - } - - //GLSL_PrintInfoLog(shader, qtrue); - //GLSL_PrintShaderSource(shader); - - if (*prevShader) - { - qglDetachObjectARB(program, *prevShader); - qglDeleteObjectARB(*prevShader); - } - - // attach shader to program - qglAttachObjectARB(program, shader); - - *prevShader = shader; - - return 1; -} - -static int GLSL_LoadGPUShaderText(const char *name, const char *fallback, - GLenum shaderType, char *dest, int destSize) -{ - char filename[MAX_QPATH]; - GLcharARB *buffer = NULL; - const GLcharARB *shaderText = NULL; - int size; - int result; - - if(shaderType == GL_VERTEX_SHADER_ARB) - { - Com_sprintf(filename, sizeof(filename), "glsl/%s_vp.glsl", name); - } - else - { - Com_sprintf(filename, sizeof(filename), "glsl/%s_fp.glsl", name); - } - - ri.Printf(PRINT_DEVELOPER, "...loading '%s'\n", filename); - size = ri.FS_ReadFile(filename, (void **)&buffer); - if(!buffer) - { - if (fallback) - { - ri.Printf(PRINT_DEVELOPER, "couldn't load, using fallback\n"); - shaderText = fallback; - size = strlen(shaderText); - } - else - { - ri.Printf(PRINT_DEVELOPER, "couldn't load!\n"); - return 0; - } - } - else - { - shaderText = buffer; - } - - if (size > destSize) - { - result = 0; - } - else - { - Q_strncpyz(dest, shaderText, size + 1); - result = 1; - } - - if (buffer) - { - ri.FS_FreeFile(buffer); - } - - return result; -} - -static void GLSL_LinkProgram(GLhandleARB program) -{ - GLint linked; - - qglLinkProgramARB(program); - - qglGetObjectParameterivARB(program, GL_OBJECT_LINK_STATUS_ARB, &linked); - if(!linked) - { - GLSL_PrintInfoLog(program, qfalse); - ri.Error(ERR_DROP, "\nshaders failed to link"); - } -} - -static void GLSL_ValidateProgram(GLhandleARB program) -{ - GLint validated; - - qglValidateProgramARB(program); - - qglGetObjectParameterivARB(program, GL_OBJECT_VALIDATE_STATUS_ARB, &validated); - if(!validated) - { - GLSL_PrintInfoLog(program, qfalse); - ri.Error(ERR_DROP, "\nshaders failed to validate"); - } -} - -static void GLSL_ShowProgramUniforms(GLhandleARB program) -{ - int i, count, size; - GLenum type; - char uniformName[1000]; - - // install the executables in the program object as part of current state. - qglUseProgramObjectARB(program); - - // check for GL Errors - - // query the number of active uniforms - qglGetObjectParameterivARB(program, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &count); - - // Loop over each of the active uniforms, and set their value - for(i = 0; i < count; i++) - { - qglGetActiveUniformARB(program, i, sizeof(uniformName), NULL, &size, &type, uniformName); - - ri.Printf(PRINT_DEVELOPER, "active uniform: '%s'\n", uniformName); - } - - qglUseProgramObjectARB(0); -} - -static int GLSL_InitGPUShader2(shaderProgram_t * program, const char *name, int attribs, const char *vpCode, const char *fpCode, int numUniforms) -{ - ri.Printf(PRINT_DEVELOPER, "------- GPU shader -------\n"); - - if(strlen(name) >= MAX_QPATH) - { - ri.Error(ERR_DROP, "GLSL_InitGPUShader2: \"%s\" is too long", name); - } - - Q_strncpyz(program->name, name, sizeof(program->name)); - - program->program = qglCreateProgramObjectARB(); - program->attribs = attribs; - - if (!(GLSL_CompileGPUShader(program->program, &program->vertexShader, vpCode, strlen(vpCode), GL_VERTEX_SHADER_ARB))) - { - ri.Printf(PRINT_ALL, "GLSL_InitGPUShader2: Unable to load \"%s\" as GL_VERTEX_SHADER_ARB\n", name); - qglDeleteObjectARB(program->program); - return 0; - } - - if(fpCode) - { - if(!(GLSL_CompileGPUShader(program->program, &program->fragmentShader, fpCode, strlen(fpCode), GL_FRAGMENT_SHADER_ARB))) - { - ri.Printf(PRINT_ALL, "GLSL_InitGPUShader2: Unable to load \"%s\" as GL_FRAGMENT_SHADER_ARB\n", name); - qglDeleteObjectARB(program->program); - return 0; - } - } - - if(attribs & ATTR_POSITION) - qglBindAttribLocationARB(program->program, ATTR_INDEX_POSITION, "attr_Position"); - - if(attribs & ATTR_TEXCOORD) - qglBindAttribLocationARB(program->program, ATTR_INDEX_TEXCOORD0, "attr_TexCoord0"); - - if(attribs & ATTR_LIGHTCOORD) - qglBindAttribLocationARB(program->program, ATTR_INDEX_TEXCOORD1, "attr_TexCoord1"); - -// if(attribs & ATTR_TEXCOORD2) -// qglBindAttribLocationARB(program->program, ATTR_INDEX_TEXCOORD2, "attr_TexCoord2"); - -// if(attribs & ATTR_TEXCOORD3) -// qglBindAttribLocationARB(program->program, ATTR_INDEX_TEXCOORD3, "attr_TexCoord3"); - -#ifdef USE_VERT_TANGENT_SPACE - if(attribs & ATTR_TANGENT) - qglBindAttribLocationARB(program->program, ATTR_INDEX_TANGENT, "attr_Tangent"); - - if(attribs & ATTR_BITANGENT) - qglBindAttribLocationARB(program->program, ATTR_INDEX_BITANGENT, "attr_Bitangent"); -#endif - - if(attribs & ATTR_NORMAL) - qglBindAttribLocationARB(program->program, ATTR_INDEX_NORMAL, "attr_Normal"); - - if(attribs & ATTR_COLOR) - qglBindAttribLocationARB(program->program, ATTR_INDEX_COLOR, "attr_Color"); - - if(attribs & ATTR_PAINTCOLOR) - qglBindAttribLocationARB(program->program, ATTR_INDEX_PAINTCOLOR, "attr_PaintColor"); - - if(attribs & ATTR_LIGHTDIRECTION) - qglBindAttribLocationARB(program->program, ATTR_INDEX_LIGHTDIRECTION, "attr_LightDirection"); - - if(attribs & ATTR_POSITION2) - qglBindAttribLocationARB(program->program, ATTR_INDEX_POSITION2, "attr_Position2"); - - if(attribs & ATTR_NORMAL2) - qglBindAttribLocationARB(program->program, ATTR_INDEX_NORMAL2, "attr_Normal2"); - -#ifdef USE_VERT_TANGENT_SPACE - if(attribs & ATTR_TANGENT2) - qglBindAttribLocationARB(program->program, ATTR_INDEX_TANGENT2, "attr_Tangent2"); - - if(attribs & ATTR_BITANGENT2) - qglBindAttribLocationARB(program->program, ATTR_INDEX_BITANGENT2, "attr_Bitangent2"); -#endif - - GLSL_LinkProgram(program->program); - - program->numUniforms = numUniforms; - - { - int i, size; - - size = sizeof(*program->uniforms) * numUniforms; - program->uniforms = ri.Malloc(size); - for (i = 0; i < numUniforms; i++) - { - program->uniforms[i] = -1; - } - - size = sizeof(*program->uniformTypes) * numUniforms; - program->uniformTypes = ri.Malloc(size); - memset(program->uniformTypes, 0, size); - - size = sizeof(*program->uniformBufferOffsets) * numUniforms; - program->uniformBufferOffsets = ri.Malloc(size); - memset(program->uniformBufferOffsets, 0, size); - } - - return 1; -} - -static int GLSL_InitGPUShader(shaderProgram_t * program, const char *name, - int attribs, qboolean fragmentShader, const GLcharARB *extra, qboolean addHeader, - const char *fallback_vp, const char *fallback_fp, int numUniforms) -{ - char vpCode[32000]; - char fpCode[32000]; - char *postHeader; - int size; - int result; - - size = sizeof(vpCode); - if (addHeader) - { - GLSL_GetShaderHeader(GL_VERTEX_SHADER_ARB, extra, vpCode, size); - postHeader = &vpCode[strlen(vpCode)]; - size -= strlen(vpCode); - } - else - { - postHeader = &vpCode[0]; - } - - if (!GLSL_LoadGPUShaderText(name, fallback_vp, GL_VERTEX_SHADER_ARB, postHeader, size)) - { - return 0; - } - - if (fragmentShader) - { - size = sizeof(fpCode); - if (addHeader) - { - GLSL_GetShaderHeader(GL_FRAGMENT_SHADER_ARB, extra, fpCode, size); - postHeader = &fpCode[strlen(fpCode)]; - size -= strlen(fpCode); - } - else - { - postHeader = &fpCode[0]; - } - - if (!GLSL_LoadGPUShaderText(name, fallback_fp, GL_FRAGMENT_SHADER_ARB, postHeader, size)) - { - return 0; - } - } - - result = GLSL_InitGPUShader2(program, name, attribs, vpCode, fragmentShader ? fpCode : NULL, numUniforms); - - return result; -} - -// intentionally deceiving the user here, not actually setting the names but getting their indexes. -void GLSL_AddUniform(shaderProgram_t *program, int uniformNum, const char *name, int type) -{ - GLint *uniforms = program->uniforms; - - uniforms[uniformNum] = qglGetUniformLocationARB(program->program, name); - program->uniformTypes[uniformNum] = type; -} - -void GLSL_EndUniforms(shaderProgram_t *program) -{ - if (program->numUniforms) - { - int i, size; - - size = 0; - for (i = 0; i < program->numUniforms; i++) - { - if (program->uniforms[i] != -1) - { - program->uniformBufferOffsets[i] = size; - - switch(program->uniformTypes[i]) - { - case GLSL_INT: - size += sizeof(GLint); - break; - case GLSL_FLOAT: - size += sizeof(GLfloat); - break; - case GLSL_FLOAT5: - size += sizeof(vec_t) * 5; - break; - case GLSL_VEC2: - size += sizeof(vec_t) * 2; - break; - case GLSL_VEC3: - size += sizeof(vec_t) * 3; - break; - case GLSL_VEC4: - size += sizeof(vec_t) * 4; - break; - case GLSL_MAT16: - size += sizeof(vec_t) * 16; - break; - default: - break; - } - } - } - - program->uniformBuffer = ri.Malloc(size); - - } -} - -void GLSL_FinishGPUShader(shaderProgram_t *program) -{ - GLSL_ValidateProgram(program->program); - GLSL_ShowProgramUniforms(program->program); - GL_CheckErrors(); -} - -void GLSL_SetUniformInt(shaderProgram_t *program, int uniformNum, GLint value) -{ - GLint *uniforms = program->uniforms; - GLint *compare = (GLint *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); - - if (uniforms[uniformNum] == -1) - return; - - if (program->uniformTypes[uniformNum] != GLSL_INT) - { - ri.Printf( PRINT_WARNING, "GLSL_SetUniformInt: wrong type for uniform %i in program %s\n", uniformNum, program->name); - return; - } - - if (value == *compare) - { - return; - } - - *compare = value; - - qglUniform1iARB(uniforms[uniformNum], value); -} - -void GLSL_SetUniformFloat(shaderProgram_t *program, int uniformNum, GLfloat value) -{ - GLint *uniforms = program->uniforms; - GLfloat *compare = (GLfloat *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); - - if (uniforms[uniformNum] == -1) - return; - - if (program->uniformTypes[uniformNum] != GLSL_FLOAT) - { - ri.Printf( PRINT_WARNING, "GLSL_SetUniformFloat: wrong type for uniform %i in program %s\n", uniformNum, program->name); - return; - } - - if (value == *compare) - { - return; - } - - *compare = value; - - qglUniform1fARB(uniforms[uniformNum], value); -} - -void GLSL_SetUniformVec2(shaderProgram_t *program, int uniformNum, const vec2_t v) -{ - GLint *uniforms = program->uniforms; - vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); - - if (uniforms[uniformNum] == -1) - return; - - if (program->uniformTypes[uniformNum] != GLSL_VEC2) - { - ri.Printf( PRINT_WARNING, "GLSL_SetUniformVec2: wrong type for uniform %i in program %s\n", uniformNum, program->name); - return; - } - - if (v[0] == compare[0] && v[1] == compare[1]) - { - return; - } - - compare[0] = v[0]; - compare[1] = v[1]; - - qglUniform2fARB(uniforms[uniformNum], v[0], v[1]); -} - -void GLSL_SetUniformVec3(shaderProgram_t *program, int uniformNum, const vec3_t v) -{ - GLint *uniforms = program->uniforms; - vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); - - if (uniforms[uniformNum] == -1) - return; - - if (program->uniformTypes[uniformNum] != GLSL_VEC3) - { - ri.Printf( PRINT_WARNING, "GLSL_SetUniformVec3: wrong type for uniform %i in program %s\n", uniformNum, program->name); - return; - } - - if (VectorCompare(v, compare)) - { - return; - } - - VectorCopy(v, compare); - - qglUniform3fARB(uniforms[uniformNum], v[0], v[1], v[2]); -} - -void GLSL_SetUniformVec4(shaderProgram_t *program, int uniformNum, const vec4_t v) -{ - GLint *uniforms = program->uniforms; - vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); - - if (uniforms[uniformNum] == -1) - return; - - if (program->uniformTypes[uniformNum] != GLSL_VEC4) - { - ri.Printf( PRINT_WARNING, "GLSL_SetUniformVec4: wrong type for uniform %i in program %s\n", uniformNum, program->name); - return; - } - - if (VectorCompare4(v, compare)) - { - return; - } - - VectorCopy4(v, compare); - - qglUniform4fARB(uniforms[uniformNum], v[0], v[1], v[2], v[3]); -} - -void GLSL_SetUniformFloat5(shaderProgram_t *program, int uniformNum, const vec5_t v) -{ - GLint *uniforms = program->uniforms; - vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); - - if (uniforms[uniformNum] == -1) - return; - - if (program->uniformTypes[uniformNum] != GLSL_FLOAT5) - { - ri.Printf( PRINT_WARNING, "GLSL_SetUniformFloat5: wrong type for uniform %i in program %s\n", uniformNum, program->name); - return; - } - - if (VectorCompare5(v, compare)) - { - return; - } - - VectorCopy5(v, compare); - - qglUniform1fvARB(uniforms[uniformNum], 5, v); -} - -void GLSL_SetUniformMatrix16(shaderProgram_t *program, int uniformNum, const matrix_t matrix) -{ - GLint *uniforms = program->uniforms; - vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); - - if (uniforms[uniformNum] == -1) - return; - - if (program->uniformTypes[uniformNum] != GLSL_MAT16) - { - ri.Printf( PRINT_WARNING, "GLSL_SetUniformMatrix16: wrong type for uniform %i in program %s\n", uniformNum, program->name); - return; - } - - if (Matrix16Compare(matrix, compare)) - { - return; - } - - Matrix16Copy(matrix, compare); - - qglUniformMatrix4fvARB(uniforms[uniformNum], 1, GL_FALSE, matrix); -} - -void GLSL_DeleteGPUShader(shaderProgram_t *program) -{ - if(program->program) - { - if (program->vertexShader) - { - qglDetachObjectARB(program->program, program->vertexShader); - qglDeleteObjectARB(program->vertexShader); - } - - if (program->fragmentShader) - { - qglDetachObjectARB(program->program, program->fragmentShader); - qglDeleteObjectARB(program->fragmentShader); - } - - qglDeleteObjectARB(program->program); - - if (program->uniforms) - { - ri.Free(program->uniforms); - } - - if (program->uniformTypes) - { - ri.Free(program->uniformTypes); - } - - if (program->uniformBuffer) - { - ri.Free(program->uniformBuffer); - } - - if (program->uniformBufferOffsets) - { - ri.Free(program->uniformBufferOffsets); - } - - Com_Memset(program, 0, sizeof(*program)); - } -} - -void GLSL_InitGPUShaders(void) -{ - int startTime, endTime; - int i; - char extradefines[1024]; - int attribs; - int numGenShaders = 0, numLightShaders = 0, numEtcShaders = 0; - - ri.Printf(PRINT_ALL, "------- GLSL_InitGPUShaders -------\n"); - - R_IssuePendingRenderCommands(); - - startTime = ri.Milliseconds(); - - for (i = 0; i < GENERICDEF_COUNT; i++) - { - attribs = ATTR_POSITION | ATTR_TEXCOORD | ATTR_LIGHTCOORD | ATTR_NORMAL | ATTR_COLOR; - extradefines[0] = '\0'; - - if (i & GENERICDEF_USE_DEFORM_VERTEXES) - Q_strcat(extradefines, 1024, "#define USE_DEFORM_VERTEXES\n"); - - if (i & GENERICDEF_USE_TCGEN_AND_TCMOD) - { - Q_strcat(extradefines, 1024, "#define USE_TCGEN\n"); - Q_strcat(extradefines, 1024, "#define USE_TCMOD\n"); - } - - if (i & GENERICDEF_USE_VERTEX_ANIMATION) - { - Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n"); - attribs |= ATTR_POSITION2 | ATTR_NORMAL2; - } - - if (i & GENERICDEF_USE_FOG) - Q_strcat(extradefines, 1024, "#define USE_FOG\n"); - - if (i & GENERICDEF_USE_RGBAGEN) - Q_strcat(extradefines, 1024, "#define USE_RGBAGEN\n"); - - if (i & GENERICDEF_USE_LIGHTMAP) - Q_strcat(extradefines, 1024, "#define USE_LIGHTMAP\n"); - - if (r_hdr->integer && !(glRefConfig.textureFloat && glRefConfig.halfFloatPixel)) - Q_strcat(extradefines, 1024, "#define RGBE_LIGHTMAP\n"); - - if (!GLSL_InitGPUShader(&tr.genericShader[i], "generic", attribs, qtrue, extradefines, qtrue, fallbackShader_generic_vp, fallbackShader_generic_fp, GENERIC_UNIFORM_COUNT)) - { - ri.Error(ERR_FATAL, "Could not load generic shader!"); - } - - // There's actually no need to filter these out, since they'll - // redirect to -1 if nonexistent, but it's more understandable this way. - - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); - - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_BASECOLOR, "u_BaseColor", GLSL_VEC4); - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_VERTCOLOR, "u_VertColor", GLSL_VEC4); - - if (i & GENERICDEF_USE_RGBAGEN) - { - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_COLORGEN, "u_ColorGen", GLSL_INT); - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_ALPHAGEN, "u_AlphaGen", GLSL_INT); - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_AMBIENTLIGHT, "u_AmbientLight", GLSL_VEC3); - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_DIRECTEDLIGHT, "u_DirectedLight", GLSL_VEC3); - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_LIGHTORIGIN, "u_LightOrigin", GLSL_VEC4); - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_PORTALRANGE, "u_PortalRange", GLSL_FLOAT); - } - - if (i & GENERICDEF_USE_TCGEN_AND_TCMOD) - { - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_TCGEN0, "u_TCGen0", GLSL_INT); - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_TCGEN0VECTOR0, "u_TCGen0Vector0", GLSL_VEC3); - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_TCGEN0VECTOR1, "u_TCGen0Vector1", GLSL_VEC3); - } - - if (i & GENERICDEF_USE_FOG) - { - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_FOGCOLORMASK, "u_FogColorMask", GLSL_VEC4); - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_FOGDISTANCE, "u_FogDistance", GLSL_VEC4); - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_FOGDEPTH, "u_FogDepth", GLSL_VEC4); - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_FOGEYET, "u_FogEyeT", GLSL_FLOAT); - } - - if (i & GENERICDEF_USE_DEFORM_VERTEXES) - { - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_DEFORMGEN, "u_DeformGen", GLSL_INT); - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_DEFORMPARAMS, "u_DeformParams", GLSL_FLOAT5); - } - - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_TIME, "u_Time", GLSL_FLOAT); - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_VIEWORIGIN, "u_ViewOrigin", GLSL_VEC3); - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_DIFFUSETEXMATRIX, "u_DiffuseTexMatrix", GLSL_VEC4); - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_DIFFUSETEXOFFTURB,"u_DiffuseTexOffTurb",GLSL_VEC4); - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_TEXTURE1ENV, "u_Texture1Env", GLSL_INT); - - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_DIFFUSEMAP, "u_DiffuseMap", GLSL_INT); - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_LIGHTMAP, "u_LightMap", GLSL_INT); - - - if (i & GENERICDEF_USE_VERTEX_ANIMATION) - { - GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_VERTEXLERP, "u_VertexLerp", GLSL_FLOAT); - } - - GLSL_EndUniforms(&tr.genericShader[i]); - - qglUseProgramObjectARB(tr.genericShader[i].program); - GLSL_SetUniformInt(&tr.genericShader[i], GENERIC_UNIFORM_DIFFUSEMAP, TB_DIFFUSEMAP); - GLSL_SetUniformInt(&tr.genericShader[i], GENERIC_UNIFORM_LIGHTMAP, TB_LIGHTMAP); - qglUseProgramObjectARB(0); - - GLSL_FinishGPUShader(&tr.genericShader[i]); - - numGenShaders++; - } - - - attribs = ATTR_POSITION | ATTR_TEXCOORD; - - if (!GLSL_InitGPUShader(&tr.textureColorShader, "texturecolor", attribs, qtrue, NULL, qfalse, fallbackShader_texturecolor_vp, fallbackShader_texturecolor_fp, TEXTURECOLOR_UNIFORM_COUNT)) - { - ri.Error(ERR_FATAL, "Could not load texturecolor shader!"); - } - - GLSL_AddUniform(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); - GLSL_AddUniform(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_COLOR, "u_Color", GLSL_VEC4); - GLSL_AddUniform(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_TEXTUREMAP, "u_DiffuseMap", GLSL_INT); - - GLSL_EndUniforms(&tr.textureColorShader); - - qglUseProgramObjectARB(tr.textureColorShader.program); - GLSL_SetUniformInt(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_TEXTUREMAP, TB_DIFFUSEMAP); - qglUseProgramObjectARB(0); - - GLSL_FinishGPUShader(&tr.textureColorShader); - - numEtcShaders++; - - for (i = 0; i < FOGDEF_COUNT; i++) - { - attribs = ATTR_POSITION | ATTR_POSITION2 | ATTR_NORMAL | ATTR_NORMAL2 | ATTR_TEXCOORD; - extradefines[0] = '\0'; - - if (i & FOGDEF_USE_DEFORM_VERTEXES) - Q_strcat(extradefines, 1024, "#define USE_DEFORM_VERTEXES\n"); - - if (i & FOGDEF_USE_VERTEX_ANIMATION) - Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n"); - - if (!GLSL_InitGPUShader(&tr.fogShader[i], "fogpass", attribs, qtrue, extradefines, qtrue, fallbackShader_fogpass_vp, fallbackShader_fogpass_fp, FOGPASS_UNIFORM_COUNT)) - { - ri.Error(ERR_FATAL, "Could not load fogpass shader!"); - } - - GLSL_AddUniform(&tr.fogShader[i], FOGPASS_UNIFORM_FOGDISTANCE, "u_FogDistance", GLSL_VEC4); - GLSL_AddUniform(&tr.fogShader[i], FOGPASS_UNIFORM_FOGDEPTH, "u_FogDepth", GLSL_VEC4); - GLSL_AddUniform(&tr.fogShader[i], FOGPASS_UNIFORM_FOGEYET, "u_FogEyeT", GLSL_FLOAT); - GLSL_AddUniform(&tr.fogShader[i], FOGPASS_UNIFORM_DEFORMGEN, "u_DeformGen", GLSL_INT); - GLSL_AddUniform(&tr.fogShader[i], FOGPASS_UNIFORM_DEFORMPARAMS, "u_DeformParams", GLSL_FLOAT5); - GLSL_AddUniform(&tr.fogShader[i], FOGPASS_UNIFORM_TIME, "u_Time", GLSL_FLOAT); - GLSL_AddUniform(&tr.fogShader[i], FOGPASS_UNIFORM_COLOR, "u_Color", GLSL_VEC4); - GLSL_AddUniform(&tr.fogShader[i], FOGPASS_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); - GLSL_AddUniform(&tr.fogShader[i], FOGPASS_UNIFORM_VERTEXLERP, "u_VertexLerp", GLSL_FLOAT); - - GLSL_EndUniforms(&tr.fogShader[i]); - GLSL_FinishGPUShader(&tr.fogShader[i]); - - numEtcShaders++; - } - - - for (i = 0; i < DLIGHTDEF_COUNT; i++) - { - attribs = ATTR_POSITION | ATTR_NORMAL | ATTR_TEXCOORD; - extradefines[0] = '\0'; - - if (i & DLIGHTDEF_USE_DEFORM_VERTEXES) - { - Q_strcat(extradefines, 1024, "#define USE_DEFORM_VERTEXES\n"); - } - - if (!GLSL_InitGPUShader(&tr.dlightShader[i], "dlight", attribs, qtrue, extradefines, qtrue, fallbackShader_dlight_vp, fallbackShader_dlight_fp, DLIGHT_UNIFORM_COUNT)) - { - ri.Error(ERR_FATAL, "Could not load dlight shader!"); - } - - GLSL_AddUniform(&tr.dlightShader[i], DLIGHT_UNIFORM_DLIGHTINFO, "u_DlightInfo", GLSL_VEC4); - GLSL_AddUniform(&tr.dlightShader[i], DLIGHT_UNIFORM_DEFORMGEN, "u_DeformGen", GLSL_INT); - GLSL_AddUniform(&tr.dlightShader[i], DLIGHT_UNIFORM_DEFORMPARAMS, "u_DeformParams", GLSL_FLOAT5); - GLSL_AddUniform(&tr.dlightShader[i], DLIGHT_UNIFORM_TIME, "u_Time", GLSL_FLOAT); - GLSL_AddUniform(&tr.dlightShader[i], DLIGHT_UNIFORM_COLOR, "u_Color", GLSL_VEC4); - GLSL_AddUniform(&tr.dlightShader[i], DLIGHT_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); - - GLSL_EndUniforms(&tr.dlightShader[i]); - - qglUseProgramObjectARB(tr.dlightShader[i].program); - GLSL_SetUniformInt(&tr.dlightShader[i], DLIGHT_UNIFORM_DIFFUSEMAP, TB_DIFFUSEMAP); - qglUseProgramObjectARB(0); - - GLSL_FinishGPUShader(&tr.dlightShader[i]); - - numEtcShaders++; - } - - - for (i = 0; i < LIGHTDEF_COUNT; i++) - { - // skip impossible combos - if ((i & LIGHTDEF_USE_NORMALMAP) && !r_normalMapping->integer) - continue; - - if ((i & LIGHTDEF_USE_PARALLAXMAP) && !r_parallaxMapping->integer) - continue; - - if ((i & LIGHTDEF_USE_SPECULARMAP) && !r_specularMapping->integer) - continue; - - if ((i & LIGHTDEF_USE_DELUXEMAP) && !r_deluxeMapping->integer) - continue; - - if (!((i & LIGHTDEF_LIGHTTYPE_MASK) == LIGHTDEF_USE_LIGHTMAP) && (i & LIGHTDEF_USE_DELUXEMAP)) - continue; - - if (!(i & LIGHTDEF_USE_NORMALMAP) && (i & LIGHTDEF_USE_PARALLAXMAP)) - continue; - - if (!((i & LIGHTDEF_LIGHTTYPE_MASK) == LIGHTDEF_USE_LIGHT_VECTOR)) - { - if (i & LIGHTDEF_USE_SHADOWMAP) - continue; - } - - attribs = ATTR_POSITION | ATTR_TEXCOORD | ATTR_COLOR | ATTR_NORMAL; - - extradefines[0] = '\0'; - - if (r_normalAmbient->value > 0.003f) - Q_strcat(extradefines, 1024, va("#define r_normalAmbient %f\n", r_normalAmbient->value)); - - if (r_dlightMode->integer >= 2) - Q_strcat(extradefines, 1024, "#define USE_SHADOWMAP\n"); - - if (1) - { - Q_strcat(extradefines, 1024, "#define SWIZZLE_NORMALMAP\n"); - } - - if (r_hdr->integer && !(glRefConfig.textureFloat && glRefConfig.halfFloatPixel)) - Q_strcat(extradefines, 1024, "#define RGBE_LIGHTMAP\n"); - - if (i & LIGHTDEF_LIGHTTYPE_MASK) - { - Q_strcat(extradefines, 1024, "#define USE_LIGHT\n"); - - if (r_normalMapping->integer == 0 && r_specularMapping->integer == 0) - Q_strcat(extradefines, 1024, "#define USE_FAST_LIGHT\n"); - - switch (i & LIGHTDEF_LIGHTTYPE_MASK) - { - case LIGHTDEF_USE_LIGHTMAP: - Q_strcat(extradefines, 1024, "#define USE_LIGHTMAP\n"); - attribs |= ATTR_LIGHTCOORD | ATTR_LIGHTDIRECTION; - break; - case LIGHTDEF_USE_LIGHT_VECTOR: - Q_strcat(extradefines, 1024, "#define USE_LIGHT_VECTOR\n"); - break; - case LIGHTDEF_USE_LIGHT_VERTEX: - Q_strcat(extradefines, 1024, "#define USE_LIGHT_VERTEX\n"); - attribs |= ATTR_LIGHTDIRECTION; - break; - default: - break; - } - } - - if ((i & LIGHTDEF_USE_NORMALMAP) && r_normalMapping->integer) - { - Q_strcat(extradefines, 1024, "#define USE_NORMALMAP\n"); - - if (r_normalMapping->integer == 2) - Q_strcat(extradefines, 1024, "#define USE_OREN_NAYAR\n"); - - if (r_normalMapping->integer == 3) - Q_strcat(extradefines, 1024, "#define USE_TRIACE_OREN_NAYAR\n"); - -#ifdef USE_VERT_TANGENT_SPACE - Q_strcat(extradefines, 1024, "#define USE_VERT_TANGENT_SPACE\n"); - attribs |= ATTR_TANGENT | ATTR_BITANGENT; -#endif - } - - if ((i & LIGHTDEF_USE_SPECULARMAP) && r_specularMapping->integer) - { - Q_strcat(extradefines, 1024, "#define USE_SPECULARMAP\n"); - - switch (r_specularMapping->integer) - { - case 1: - default: - Q_strcat(extradefines, 1024, "#define USE_TRIACE\n"); - break; - - case 2: - Q_strcat(extradefines, 1024, "#define USE_BLINN\n"); - break; - - case 3: - Q_strcat(extradefines, 1024, "#define USE_COOK_TORRANCE\n"); - break; - - case 4: - Q_strcat(extradefines, 1024, "#define USE_TORRANCE_SPARROW\n"); - break; - } - } - - if ((i & LIGHTDEF_USE_DELUXEMAP) && r_deluxeMapping->integer) - Q_strcat(extradefines, 1024, "#define USE_DELUXEMAP\n"); - - if ((i & LIGHTDEF_USE_PARALLAXMAP) && !(i & LIGHTDEF_ENTITY) && r_parallaxMapping->integer) - Q_strcat(extradefines, 1024, "#define USE_PARALLAXMAP\n"); - - if (i & LIGHTDEF_USE_SHADOWMAP) - Q_strcat(extradefines, 1024, "#define USE_SHADOWMAP\n"); - - if (i & LIGHTDEF_USE_TCGEN_AND_TCMOD) - { - Q_strcat(extradefines, 1024, "#define USE_TCGEN\n"); - Q_strcat(extradefines, 1024, "#define USE_TCMOD\n"); - } - - if (i & LIGHTDEF_ENTITY) - { - Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n#define USE_MODELMATRIX\n"); - attribs |= ATTR_POSITION2 | ATTR_NORMAL2; - -#ifdef USE_VERT_TANGENT_SPACE - if (i & LIGHTDEF_USE_NORMALMAP && r_normalMapping->integer) - { - attribs |= ATTR_TANGENT2 | ATTR_BITANGENT2; - } -#endif - } - - if (!GLSL_InitGPUShader(&tr.lightallShader[i], "lightall", attribs, qtrue, extradefines, qtrue, fallbackShader_lightall_vp, fallbackShader_lightall_fp, GENERIC_UNIFORM_COUNT)) - { - ri.Error(ERR_FATAL, "Could not load lightall shader!"); - } - - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_MODELMATRIX, "u_ModelMatrix", GLSL_MAT16); - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_DIFFUSETEXMATRIX, "u_DiffuseTexMatrix", GLSL_VEC4); - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_DIFFUSETEXOFFTURB, "u_DiffuseTexOffTurb", GLSL_VEC4); - //GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_NORMALTEXMATRIX, "u_NormalTexMatrix", GLSL_MAT16); - //GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_SPECULARTEXMATRIX, "u_SpecularTexMatrix", GLSL_MAT16); - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_VIEWORIGIN, "u_ViewOrigin", GLSL_VEC3); - - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_TCGEN0, "u_TCGen0", GLSL_INT); - - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_DIFFUSEMAP, "u_DiffuseMap", GLSL_INT); - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_LIGHTMAP, "u_LightMap", GLSL_INT); - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_NORMALMAP, "u_NormalMap", GLSL_INT); - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_DELUXEMAP, "u_DeluxeMap", GLSL_INT); - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_SPECULARMAP, "u_SpecularMap", GLSL_INT); - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_SHADOWMAP, "u_ShadowMap", GLSL_INT); - - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_AMBIENTLIGHT, "u_AmbientLight", GLSL_VEC3); - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_DIRECTEDLIGHT, "u_DirectedLight", GLSL_VEC3); - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_LIGHTORIGIN, "u_LightOrigin", GLSL_VEC4); - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_LIGHTRADIUS, "u_LightRadius", GLSL_FLOAT); - - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_MATERIALINFO, "u_MaterialInfo", GLSL_VEC2); - - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_BASECOLOR, "u_BaseColor", GLSL_VEC4); - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_VERTCOLOR, "u_VertColor", GLSL_VEC4); - GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_VERTEXLERP, "u_VertexLerp", GLSL_FLOAT); - - GLSL_EndUniforms(&tr.lightallShader[i]); - - qglUseProgramObjectARB(tr.lightallShader[i].program); - GLSL_SetUniformInt(&tr.lightallShader[i], GENERIC_UNIFORM_DIFFUSEMAP, TB_DIFFUSEMAP); - GLSL_SetUniformInt(&tr.lightallShader[i], GENERIC_UNIFORM_LIGHTMAP, TB_LIGHTMAP); - GLSL_SetUniformInt(&tr.lightallShader[i], GENERIC_UNIFORM_NORMALMAP, TB_NORMALMAP); - GLSL_SetUniformInt(&tr.lightallShader[i], GENERIC_UNIFORM_DELUXEMAP, TB_DELUXEMAP); - GLSL_SetUniformInt(&tr.lightallShader[i], GENERIC_UNIFORM_SPECULARMAP, TB_SPECULARMAP); - GLSL_SetUniformInt(&tr.lightallShader[i], GENERIC_UNIFORM_SHADOWMAP, TB_SHADOWMAP); - qglUseProgramObjectARB(0); - - GLSL_FinishGPUShader(&tr.lightallShader[i]); - - numLightShaders++; - } - - attribs = ATTR_POSITION | ATTR_POSITION2 | ATTR_NORMAL | ATTR_NORMAL2 | ATTR_TEXCOORD; - - extradefines[0] = '\0'; - - if (!GLSL_InitGPUShader(&tr.shadowmapShader, "shadowfill", attribs, qtrue, extradefines, qtrue, fallbackShader_shadowfill_vp, fallbackShader_shadowfill_fp, GENERIC_UNIFORM_COUNT)) - { - ri.Error(ERR_FATAL, "Could not load shadowfill shader!"); - } - - GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_DEFORMGEN, "u_DeformGen", GLSL_INT); - GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_DEFORMPARAMS, "u_DeformParams", GLSL_FLOAT5); - GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_TIME, "u_Time", GLSL_FLOAT); - GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); - GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_MODELMATRIX, "u_ModelMatrix", GLSL_MAT16); - GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_VERTEXLERP, "u_VertexLerp", GLSL_FLOAT); - - GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_LIGHTORIGIN, "u_LightOrigin", GLSL_VEC4); - GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_LIGHTRADIUS, "u_LightRadius", GLSL_FLOAT); - - GLSL_EndUniforms(&tr.shadowmapShader); - GLSL_FinishGPUShader(&tr.shadowmapShader); - - numEtcShaders++; - - attribs = ATTR_POSITION | ATTR_NORMAL; - extradefines[0] = '\0'; - - Q_strcat(extradefines, 1024, "#define USE_PCF\n#define USE_DISCARD\n"); - - if (!GLSL_InitGPUShader(&tr.pshadowShader, "pshadow", attribs, qtrue, extradefines, qtrue, fallbackShader_pshadow_vp, fallbackShader_pshadow_fp, PSHADOW_UNIFORM_COUNT)) - { - ri.Error(ERR_FATAL, "Could not load pshadow shader!"); - } - - GLSL_AddUniform(&tr.pshadowShader, PSHADOW_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); - GLSL_AddUniform(&tr.pshadowShader, PSHADOW_UNIFORM_LIGHTFORWARD, "u_LightForward", GLSL_VEC3); - GLSL_AddUniform(&tr.pshadowShader, PSHADOW_UNIFORM_LIGHTUP, "u_LightUp", GLSL_VEC3); - GLSL_AddUniform(&tr.pshadowShader, PSHADOW_UNIFORM_LIGHTRIGHT, "u_LightRight", GLSL_VEC3); - GLSL_AddUniform(&tr.pshadowShader, PSHADOW_UNIFORM_LIGHTORIGIN, "u_LightOrigin", GLSL_VEC4); - GLSL_AddUniform(&tr.pshadowShader, PSHADOW_UNIFORM_LIGHTRADIUS, "u_LightRadius", GLSL_FLOAT); - - GLSL_EndUniforms(&tr.pshadowShader); - - qglUseProgramObjectARB(tr.pshadowShader.program); - GLSL_SetUniformInt(&tr.pshadowShader, PSHADOW_UNIFORM_SHADOWMAP, TB_DIFFUSEMAP); - qglUseProgramObjectARB(0); - - GLSL_FinishGPUShader(&tr.pshadowShader); - - numEtcShaders++; - - - attribs = ATTR_POSITION | ATTR_TEXCOORD; - extradefines[0] = '\0'; - - if (!GLSL_InitGPUShader(&tr.down4xShader, "down4x", attribs, qtrue, extradefines, qtrue, fallbackShader_down4x_vp, fallbackShader_down4x_fp, TEXTURECOLOR_UNIFORM_COUNT)) - { - ri.Error(ERR_FATAL, "Could not load down4x shader!"); - } - - GLSL_AddUniform(&tr.down4xShader, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); - GLSL_AddUniform(&tr.down4xShader, TEXTURECOLOR_UNIFORM_INVTEXRES, "u_InvTexRes", GLSL_VEC2); - - GLSL_AddUniform(&tr.down4xShader, TEXTURECOLOR_UNIFORM_TEXTUREMAP, "u_TextureMap", GLSL_INT); - - GLSL_EndUniforms(&tr.down4xShader); - - qglUseProgramObjectARB(tr.down4xShader.program); - GLSL_SetUniformInt(&tr.down4xShader, TEXTURECOLOR_UNIFORM_TEXTUREMAP, TB_DIFFUSEMAP); - qglUseProgramObjectARB(0); - - GLSL_FinishGPUShader(&tr.down4xShader); - - numEtcShaders++; - - - attribs = ATTR_POSITION | ATTR_TEXCOORD; - extradefines[0] = '\0'; - - if (!GLSL_InitGPUShader(&tr.bokehShader, "bokeh", attribs, qtrue, extradefines, qtrue, fallbackShader_bokeh_vp, fallbackShader_bokeh_fp, TEXTURECOLOR_UNIFORM_COUNT)) - { - ri.Error(ERR_FATAL, "Could not load bokeh shader!"); - } - - GLSL_AddUniform(&tr.bokehShader, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); - GLSL_AddUniform(&tr.bokehShader, TEXTURECOLOR_UNIFORM_INVTEXRES, "u_InvTexRes", GLSL_VEC2); - GLSL_AddUniform(&tr.bokehShader, TEXTURECOLOR_UNIFORM_COLOR, "u_Color", GLSL_VEC4); - - GLSL_AddUniform(&tr.bokehShader, TEXTURECOLOR_UNIFORM_TEXTUREMAP, "u_TextureMap", GLSL_INT); - - GLSL_EndUniforms(&tr.bokehShader); - - qglUseProgramObjectARB(tr.bokehShader.program); - GLSL_SetUniformInt(&tr.bokehShader, TEXTURECOLOR_UNIFORM_TEXTUREMAP, TB_DIFFUSEMAP); - qglUseProgramObjectARB(0); - - GLSL_FinishGPUShader(&tr.bokehShader); - - numEtcShaders++; - - - attribs = ATTR_POSITION | ATTR_TEXCOORD; - extradefines[0] = '\0'; - - if (!GLSL_InitGPUShader(&tr.tonemapShader, "tonemap", attribs, qtrue, extradefines, qtrue, fallbackShader_tonemap_vp, fallbackShader_tonemap_fp, TEXTURECOLOR_UNIFORM_COUNT)) - { - ri.Error(ERR_FATAL, "Could not load tonemap shader!"); - } - - GLSL_AddUniform(&tr.tonemapShader, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); - GLSL_AddUniform(&tr.tonemapShader, TEXTURECOLOR_UNIFORM_INVTEXRES, "u_InvTexRes", GLSL_VEC2); - GLSL_AddUniform(&tr.tonemapShader, TEXTURECOLOR_UNIFORM_COLOR, "u_Color", GLSL_VEC4); - GLSL_AddUniform(&tr.tonemapShader, TEXTURECOLOR_UNIFORM_AUTOEXPOSUREMINMAX, "u_AutoExposureMinMax", GLSL_VEC2); - GLSL_AddUniform(&tr.tonemapShader, TEXTURECOLOR_UNIFORM_TONEMINAVGMAXLINEAR, "u_ToneMinAvgMaxLinear", GLSL_VEC3); - GLSL_AddUniform(&tr.tonemapShader, TEXTURECOLOR_UNIFORM_TEXTUREMAP, "u_TextureMap", GLSL_INT); - GLSL_AddUniform(&tr.tonemapShader, TEXTURECOLOR_UNIFORM_LEVELSMAP, "u_LevelsMap", GLSL_INT); - - GLSL_EndUniforms(&tr.tonemapShader); - - qglUseProgramObjectARB(tr.tonemapShader.program); - GLSL_SetUniformInt(&tr.tonemapShader, TEXTURECOLOR_UNIFORM_TEXTUREMAP, TB_COLORMAP); - GLSL_SetUniformInt(&tr.tonemapShader, TEXTURECOLOR_UNIFORM_LEVELSMAP, TB_LEVELSMAP); - qglUseProgramObjectARB(0); - - GLSL_FinishGPUShader(&tr.tonemapShader); - - numEtcShaders++; - - - for (i = 0; i < 2; i++) - { - attribs = ATTR_POSITION | ATTR_TEXCOORD; - extradefines[0] = '\0'; - - if (!i) - Q_strcat(extradefines, 1024, "#define FIRST_PASS\n"); - - if (!GLSL_InitGPUShader(&tr.calclevels4xShader[i], "calclevels4x", attribs, qtrue, extradefines, qtrue, fallbackShader_calclevels4x_vp, fallbackShader_calclevels4x_fp, TEXTURECOLOR_UNIFORM_COUNT)) - { - ri.Error(ERR_FATAL, "Could not load calclevels4x shader!"); - } - - GLSL_AddUniform(&tr.calclevels4xShader[i], TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); - GLSL_AddUniform(&tr.calclevels4xShader[i], TEXTURECOLOR_UNIFORM_INVTEXRES, "u_InvTexRes", GLSL_VEC2); - GLSL_AddUniform(&tr.calclevels4xShader[i], TEXTURECOLOR_UNIFORM_COLOR, "u_Color", GLSL_VEC4); - - GLSL_AddUniform(&tr.calclevels4xShader[i], TEXTURECOLOR_UNIFORM_TEXTUREMAP, "u_TextureMap", GLSL_INT); - - GLSL_EndUniforms(&tr.calclevels4xShader[i]); - - qglUseProgramObjectARB(tr.calclevels4xShader[i].program); - GLSL_SetUniformInt(&tr.calclevels4xShader[i], TEXTURECOLOR_UNIFORM_TEXTUREMAP, TB_DIFFUSEMAP); - qglUseProgramObjectARB(0); - - GLSL_FinishGPUShader(&tr.calclevels4xShader[i]); - - numEtcShaders++; - } - - - attribs = ATTR_POSITION | ATTR_TEXCOORD; - extradefines[0] = '\0'; - - if (r_shadowFilter->integer >= 1) - Q_strcat(extradefines, 1024, "#define USE_SHADOW_FILTER\n"); - - if (r_shadowFilter->integer >= 2) - Q_strcat(extradefines, 1024, "#define USE_SHADOW_FILTER2\n"); - - Q_strcat(extradefines, 1024, "#define USE_SHADOW_CASCADE\n"); - - Q_strcat(extradefines, 1024, va("#define r_shadowMapSize %d\n", r_shadowMapSize->integer)); - Q_strcat(extradefines, 1024, va("#define r_shadowCascadeZFar %f\n", r_shadowCascadeZFar->value)); - - - if (!GLSL_InitGPUShader(&tr.shadowmaskShader, "shadowmask", attribs, qtrue, extradefines, qtrue, fallbackShader_shadowmask_vp, fallbackShader_shadowmask_fp, SHADOWMASK_UNIFORM_COUNT)) - { - ri.Error(ERR_FATAL, "Could not load shadowmask shader!"); - } - - GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMVP, "u_ShadowMvp", GLSL_MAT16); - GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMVP2, "u_ShadowMvp2", GLSL_MAT16); - GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMVP3, "u_ShadowMvp3", GLSL_MAT16); - GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWORIGIN, "u_ViewOrigin", GLSL_VEC3); - GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWINFO, "u_ViewInfo", GLSL_VEC4); - GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWFORWARD,"u_ViewForward", GLSL_VEC3); - GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWLEFT, "u_ViewLeft", GLSL_VEC3); - GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWUP, "u_ViewUp", GLSL_VEC3); - - GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SCREENDEPTHMAP, "u_ScreenDepthMap", GLSL_INT); - GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMAP, "u_ShadowMap", GLSL_INT); - GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMAP2, "u_ShadowMap2", GLSL_INT); - GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMAP3, "u_ShadowMap3", GLSL_INT); - - GLSL_EndUniforms(&tr.shadowmaskShader); - - qglUseProgramObjectARB(tr.shadowmaskShader.program); - GLSL_SetUniformInt(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SCREENDEPTHMAP, TB_COLORMAP); - GLSL_SetUniformInt(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMAP, TB_SHADOWMAP); - GLSL_SetUniformInt(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMAP2, TB_SHADOWMAP2); - GLSL_SetUniformInt(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMAP3, TB_SHADOWMAP3); - qglUseProgramObjectARB(0); - - GLSL_FinishGPUShader(&tr.shadowmaskShader); - - numEtcShaders++; - - - attribs = ATTR_POSITION | ATTR_TEXCOORD; - extradefines[0] = '\0'; - - if (!GLSL_InitGPUShader(&tr.ssaoShader, "ssao", attribs, qtrue, extradefines, qtrue, fallbackShader_ssao_vp, fallbackShader_ssao_fp, SSAO_UNIFORM_COUNT)) - { - ri.Error(ERR_FATAL, "Could not load ssao shader!"); - } - - GLSL_AddUniform(&tr.ssaoShader, SSAO_UNIFORM_VIEWINFO, "u_ViewInfo", GLSL_VEC4); - - GLSL_AddUniform(&tr.ssaoShader, SSAO_UNIFORM_SCREENDEPTHMAP, "u_ScreenDepthMap", GLSL_INT); - - GLSL_EndUniforms(&tr.ssaoShader); - - qglUseProgramObjectARB(tr.ssaoShader.program); - GLSL_SetUniformInt(&tr.ssaoShader, SSAO_UNIFORM_SCREENDEPTHMAP, TB_COLORMAP); - qglUseProgramObjectARB(0); - - GLSL_FinishGPUShader(&tr.ssaoShader); - - numEtcShaders++; - - - for (i = 0; i < 2; i++) - { - attribs = ATTR_POSITION | ATTR_TEXCOORD; - extradefines[0] = '\0'; - - if (i & 1) - Q_strcat(extradefines, 1024, "#define USE_VERTICAL_BLUR\n"); - else - Q_strcat(extradefines, 1024, "#define USE_HORIZONTAL_BLUR\n"); - - - if (!GLSL_InitGPUShader(&tr.depthBlurShader[i], "depthBlur", attribs, qtrue, extradefines, qtrue, fallbackShader_depthblur_vp, fallbackShader_depthblur_fp, DEPTHBLUR_UNIFORM_COUNT)) - { - ri.Error(ERR_FATAL, "Could not load depthBlur shader!"); - } - - GLSL_AddUniform(&tr.depthBlurShader[i], DEPTHBLUR_UNIFORM_VIEWINFO, "u_ViewInfo", GLSL_VEC4); - - GLSL_AddUniform(&tr.depthBlurShader[i], DEPTHBLUR_UNIFORM_SCREENIMAGEMAP, "u_ScreenImageMap", GLSL_INT); - GLSL_AddUniform(&tr.depthBlurShader[i], DEPTHBLUR_UNIFORM_SCREENDEPTHMAP, "u_ScreenDepthMap", GLSL_INT); - - GLSL_EndUniforms(&tr.depthBlurShader[i]); - - qglUseProgramObjectARB(tr.depthBlurShader[i].program); - GLSL_SetUniformInt(&tr.depthBlurShader[i], DEPTHBLUR_UNIFORM_SCREENIMAGEMAP, TB_COLORMAP); - GLSL_SetUniformInt(&tr.depthBlurShader[i], DEPTHBLUR_UNIFORM_SCREENDEPTHMAP, TB_LIGHTMAP); - qglUseProgramObjectARB(0); - - GLSL_FinishGPUShader(&tr.depthBlurShader[i]); - - numEtcShaders++; - } - - - endTime = ri.Milliseconds(); - - ri.Printf(PRINT_ALL, "loaded %i GLSL shaders (%i gen %i light %i etc) in %5.2f seconds\n", - numGenShaders + numLightShaders + numEtcShaders, numGenShaders, numLightShaders, - numEtcShaders, (endTime - startTime) / 1000.0); -} - -void GLSL_ShutdownGPUShaders(void) -{ - int i; - - ri.Printf(PRINT_ALL, "------- GLSL_ShutdownGPUShaders -------\n"); - - qglDisableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD0); - qglDisableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD1); - qglDisableVertexAttribArrayARB(ATTR_INDEX_POSITION); - qglDisableVertexAttribArrayARB(ATTR_INDEX_POSITION2); - qglDisableVertexAttribArrayARB(ATTR_INDEX_NORMAL); -#ifdef USE_VERT_TANGENT_SPACE - qglDisableVertexAttribArrayARB(ATTR_INDEX_TANGENT); - qglDisableVertexAttribArrayARB(ATTR_INDEX_BITANGENT); -#endif - qglDisableVertexAttribArrayARB(ATTR_INDEX_NORMAL2); -#ifdef USE_VERT_TANGENT_SPACE - qglDisableVertexAttribArrayARB(ATTR_INDEX_TANGENT2); - qglDisableVertexAttribArrayARB(ATTR_INDEX_BITANGENT2); -#endif - qglDisableVertexAttribArrayARB(ATTR_INDEX_COLOR); - qglDisableVertexAttribArrayARB(ATTR_INDEX_LIGHTDIRECTION); - GLSL_BindNullProgram(); - - for ( i = 0; i < GENERICDEF_COUNT; i++) - GLSL_DeleteGPUShader(&tr.genericShader[i]); - - GLSL_DeleteGPUShader(&tr.textureColorShader); - - for ( i = 0; i < FOGDEF_COUNT; i++) - GLSL_DeleteGPUShader(&tr.fogShader[i]); - - for ( i = 0; i < DLIGHTDEF_COUNT; i++) - GLSL_DeleteGPUShader(&tr.dlightShader[i]); - - for ( i = 0; i < LIGHTDEF_COUNT; i++) - GLSL_DeleteGPUShader(&tr.lightallShader[i]); - - GLSL_DeleteGPUShader(&tr.shadowmapShader); - GLSL_DeleteGPUShader(&tr.pshadowShader); - GLSL_DeleteGPUShader(&tr.down4xShader); - - for ( i = 0; i < 2; i++) - GLSL_DeleteGPUShader(&tr.calclevels4xShader[i]); - - glState.currentProgram = 0; - qglUseProgramObjectARB(0); -} - - -void GLSL_BindProgram(shaderProgram_t * program) -{ - if(!program) - { - GLSL_BindNullProgram(); - return; - } - - if(r_logFile->integer) - { - // don't just call LogComment, or we will get a call to va() every frame! - GLimp_LogComment(va("--- GL_BindProgram( %s ) ---\n", program->name)); - } - - if(glState.currentProgram != program) - { - qglUseProgramObjectARB(program->program); - glState.currentProgram = program; - backEnd.pc.c_glslShaderBinds++; - } -} - - -void GLSL_BindNullProgram(void) -{ - if(r_logFile->integer) - { - GLimp_LogComment("--- GL_BindNullProgram ---\n"); - } - - if(glState.currentProgram) - { - qglUseProgramObjectARB(0); - glState.currentProgram = NULL; - } -} - - -void GLSL_VertexAttribsState(uint32_t stateBits) -{ - uint32_t diff; - - GLSL_VertexAttribPointers(stateBits); - - diff = stateBits ^ glState.vertexAttribsState; - if(!diff) - { - return; - } - - if(diff & ATTR_POSITION) - { - if(stateBits & ATTR_POSITION) - { - GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_POSITION )\n"); - qglEnableVertexAttribArrayARB(ATTR_INDEX_POSITION); - } - else - { - GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_POSITION )\n"); - qglDisableVertexAttribArrayARB(ATTR_INDEX_POSITION); - } - } - - if(diff & ATTR_TEXCOORD) - { - if(stateBits & ATTR_TEXCOORD) - { - GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_TEXCOORD )\n"); - qglEnableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD0); - } - else - { - GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_TEXCOORD )\n"); - qglDisableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD0); - } - } - - if(diff & ATTR_LIGHTCOORD) - { - if(stateBits & ATTR_LIGHTCOORD) - { - GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_LIGHTCOORD )\n"); - qglEnableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD1); - } - else - { - GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_LIGHTCOORD )\n"); - qglDisableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD1); - } - } - - if(diff & ATTR_NORMAL) - { - if(stateBits & ATTR_NORMAL) - { - GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_NORMAL )\n"); - qglEnableVertexAttribArrayARB(ATTR_INDEX_NORMAL); - } - else - { - GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_NORMAL )\n"); - qglDisableVertexAttribArrayARB(ATTR_INDEX_NORMAL); - } - } - -#ifdef USE_VERT_TANGENT_SPACE - if(diff & ATTR_TANGENT) - { - if(stateBits & ATTR_TANGENT) - { - GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_TANGENT )\n"); - qglEnableVertexAttribArrayARB(ATTR_INDEX_TANGENT); - } - else - { - GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_TANGENT )\n"); - qglDisableVertexAttribArrayARB(ATTR_INDEX_TANGENT); - } - } - - if(diff & ATTR_BITANGENT) - { - if(stateBits & ATTR_BITANGENT) - { - GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_BITANGENT )\n"); - qglEnableVertexAttribArrayARB(ATTR_INDEX_BITANGENT); - } - else - { - GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_BITANGENT )\n"); - qglDisableVertexAttribArrayARB(ATTR_INDEX_BITANGENT); - } - } -#endif - - if(diff & ATTR_COLOR) - { - if(stateBits & ATTR_COLOR) - { - GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_COLOR )\n"); - qglEnableVertexAttribArrayARB(ATTR_INDEX_COLOR); - } - else - { - GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_COLOR )\n"); - qglDisableVertexAttribArrayARB(ATTR_INDEX_COLOR); - } - } - - if(diff & ATTR_LIGHTDIRECTION) - { - if(stateBits & ATTR_LIGHTDIRECTION) - { - GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_LIGHTDIRECTION )\n"); - qglEnableVertexAttribArrayARB(ATTR_INDEX_LIGHTDIRECTION); - } - else - { - GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_LIGHTDIRECTION )\n"); - qglDisableVertexAttribArrayARB(ATTR_INDEX_LIGHTDIRECTION); - } - } - - if(diff & ATTR_POSITION2) - { - if(stateBits & ATTR_POSITION2) - { - GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_POSITION2 )\n"); - qglEnableVertexAttribArrayARB(ATTR_INDEX_POSITION2); - } - else - { - GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_POSITION2 )\n"); - qglDisableVertexAttribArrayARB(ATTR_INDEX_POSITION2); - } - } - - if(diff & ATTR_NORMAL2) - { - if(stateBits & ATTR_NORMAL2) - { - GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_NORMAL2 )\n"); - qglEnableVertexAttribArrayARB(ATTR_INDEX_NORMAL2); - } - else - { - GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_NORMAL2 )\n"); - qglDisableVertexAttribArrayARB(ATTR_INDEX_NORMAL2); - } - } - -#ifdef USE_VERT_TANGENT_SPACE - if(diff & ATTR_TANGENT2) - { - if(stateBits & ATTR_TANGENT2) - { - GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_TANGENT2 )\n"); - qglEnableVertexAttribArrayARB(ATTR_INDEX_TANGENT2); - } - else - { - GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_TANGENT2 )\n"); - qglDisableVertexAttribArrayARB(ATTR_INDEX_TANGENT2); - } - } - - if(diff & ATTR_BITANGENT2) - { - if(stateBits & ATTR_BITANGENT2) - { - GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_BITANGENT2 )\n"); - qglEnableVertexAttribArrayARB(ATTR_INDEX_BITANGENT2); - } - else - { - GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_BITANGENT2 )\n"); - qglDisableVertexAttribArrayARB(ATTR_INDEX_BITANGENT2); - } - } -#endif - - glState.vertexAttribsState = stateBits; -} - -void GLSL_VertexAttribPointers(uint32_t attribBits) -{ - if(!glState.currentVBO) - { - ri.Error(ERR_FATAL, "GL_VertexAttribPointers: no VBO bound"); - return; - } - - // don't just call LogComment, or we will get a call to va() every frame! - GLimp_LogComment(va("--- GL_VertexAttribPointers( %s ) ---\n", glState.currentVBO->name)); - - if((attribBits & ATTR_POSITION) && !(glState.vertexAttribPointersSet & ATTR_POSITION)) - { - GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_POSITION )\n"); - - qglVertexAttribPointerARB(ATTR_INDEX_POSITION, 3, GL_FLOAT, 0, glState.currentVBO->stride_xyz, BUFFER_OFFSET(glState.currentVBO->ofs_xyz + glState.vertexAttribsNewFrame * glState.currentVBO->size_xyz)); - glState.vertexAttribPointersSet |= ATTR_POSITION; - } - - if((attribBits & ATTR_TEXCOORD) && !(glState.vertexAttribPointersSet & ATTR_TEXCOORD)) - { - GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_TEXCOORD )\n"); - - qglVertexAttribPointerARB(ATTR_INDEX_TEXCOORD0, 2, GL_FLOAT, 0, glState.currentVBO->stride_st, BUFFER_OFFSET(glState.currentVBO->ofs_st)); - glState.vertexAttribPointersSet |= ATTR_TEXCOORD; - } - - if((attribBits & ATTR_LIGHTCOORD) && !(glState.vertexAttribPointersSet & ATTR_LIGHTCOORD)) - { - GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_LIGHTCOORD )\n"); - - qglVertexAttribPointerARB(ATTR_INDEX_TEXCOORD1, 2, GL_FLOAT, 0, glState.currentVBO->stride_lightmap, BUFFER_OFFSET(glState.currentVBO->ofs_lightmap)); - glState.vertexAttribPointersSet |= ATTR_LIGHTCOORD; - } - - if((attribBits & ATTR_NORMAL) && !(glState.vertexAttribPointersSet & ATTR_NORMAL)) - { - GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_NORMAL )\n"); - - qglVertexAttribPointerARB(ATTR_INDEX_NORMAL, 3, GL_FLOAT, 0, glState.currentVBO->stride_normal, BUFFER_OFFSET(glState.currentVBO->ofs_normal + glState.vertexAttribsNewFrame * glState.currentVBO->size_normal)); - glState.vertexAttribPointersSet |= ATTR_NORMAL; - } - -#ifdef USE_VERT_TANGENT_SPACE - if((attribBits & ATTR_TANGENT) && !(glState.vertexAttribPointersSet & ATTR_TANGENT)) - { - GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_TANGENT )\n"); - - qglVertexAttribPointerARB(ATTR_INDEX_TANGENT, 3, GL_FLOAT, 0, glState.currentVBO->stride_tangent, BUFFER_OFFSET(glState.currentVBO->ofs_tangent + glState.vertexAttribsNewFrame * glState.currentVBO->size_normal)); // FIXME - glState.vertexAttribPointersSet |= ATTR_TANGENT; - } - - if((attribBits & ATTR_BITANGENT) && !(glState.vertexAttribPointersSet & ATTR_BITANGENT)) - { - GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_BITANGENT )\n"); - - qglVertexAttribPointerARB(ATTR_INDEX_BITANGENT, 3, GL_FLOAT, 0, glState.currentVBO->stride_bitangent, BUFFER_OFFSET(glState.currentVBO->ofs_bitangent + glState.vertexAttribsNewFrame * glState.currentVBO->size_normal)); // FIXME - glState.vertexAttribPointersSet |= ATTR_BITANGENT; - } -#endif - - if((attribBits & ATTR_COLOR) && !(glState.vertexAttribPointersSet & ATTR_COLOR)) - { - GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_COLOR )\n"); - - qglVertexAttribPointerARB(ATTR_INDEX_COLOR, 4, GL_FLOAT, 0, glState.currentVBO->stride_vertexcolor, BUFFER_OFFSET(glState.currentVBO->ofs_vertexcolor)); - glState.vertexAttribPointersSet |= ATTR_COLOR; - } - - if((attribBits & ATTR_LIGHTDIRECTION) && !(glState.vertexAttribPointersSet & ATTR_LIGHTDIRECTION)) - { - GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_LIGHTDIRECTION )\n"); - - qglVertexAttribPointerARB(ATTR_INDEX_LIGHTDIRECTION, 3, GL_FLOAT, 0, glState.currentVBO->stride_lightdir, BUFFER_OFFSET(glState.currentVBO->ofs_lightdir)); - glState.vertexAttribPointersSet |= ATTR_LIGHTDIRECTION; - } - - if((attribBits & ATTR_POSITION2) && !(glState.vertexAttribPointersSet & ATTR_POSITION2)) - { - GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_POSITION2 )\n"); - - qglVertexAttribPointerARB(ATTR_INDEX_POSITION2, 3, GL_FLOAT, 0, glState.currentVBO->stride_xyz, BUFFER_OFFSET(glState.currentVBO->ofs_xyz + glState.vertexAttribsOldFrame * glState.currentVBO->size_xyz)); - glState.vertexAttribPointersSet |= ATTR_POSITION2; - } - - if((attribBits & ATTR_NORMAL2) && !(glState.vertexAttribPointersSet & ATTR_NORMAL2)) - { - GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_NORMAL2 )\n"); - - qglVertexAttribPointerARB(ATTR_INDEX_NORMAL2, 3, GL_FLOAT, 0, glState.currentVBO->stride_normal, BUFFER_OFFSET(glState.currentVBO->ofs_normal + glState.vertexAttribsOldFrame * glState.currentVBO->size_normal)); - glState.vertexAttribPointersSet |= ATTR_NORMAL2; - } - -#ifdef USE_VERT_TANGENT_SPACE - if((attribBits & ATTR_TANGENT2) && !(glState.vertexAttribPointersSet & ATTR_TANGENT2)) - { - GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_TANGENT2 )\n"); - - qglVertexAttribPointerARB(ATTR_INDEX_TANGENT2, 3, GL_FLOAT, 0, glState.currentVBO->stride_tangent, BUFFER_OFFSET(glState.currentVBO->ofs_tangent + glState.vertexAttribsOldFrame * glState.currentVBO->size_normal)); // FIXME - glState.vertexAttribPointersSet |= ATTR_TANGENT2; - } - - if((attribBits & ATTR_BITANGENT2) && !(glState.vertexAttribPointersSet & ATTR_BITANGENT2)) - { - GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_BITANGENT2 )\n"); - - qglVertexAttribPointerARB(ATTR_INDEX_BITANGENT2, 3, GL_FLOAT, 0, glState.currentVBO->stride_bitangent, BUFFER_OFFSET(glState.currentVBO->ofs_bitangent + glState.vertexAttribsOldFrame * glState.currentVBO->size_normal)); // FIXME - glState.vertexAttribPointersSet |= ATTR_BITANGENT2; - } -#endif - -} - -shaderProgram_t *GLSL_GetGenericShaderProgram(int stage) -{ - shaderStage_t *pStage = tess.xstages[stage]; - int shaderAttribs = 0; - - if (tess.fogNum && pStage->adjustColorsForFog) - { - shaderAttribs |= GENERICDEF_USE_FOG; - } - - if (pStage->bundle[1].image[0] && tess.shader->multitextureEnv) - { - shaderAttribs |= GENERICDEF_USE_LIGHTMAP; - } - - switch (pStage->rgbGen) - { - case CGEN_LIGHTING_DIFFUSE: - shaderAttribs |= GENERICDEF_USE_RGBAGEN; - break; - default: - break; - } - - switch (pStage->alphaGen) - { - case AGEN_LIGHTING_SPECULAR: - case AGEN_PORTAL: - case AGEN_FRESNEL: - shaderAttribs |= GENERICDEF_USE_RGBAGEN; - break; - default: - break; - } - - if (pStage->bundle[0].tcGen != TCGEN_TEXTURE) - { - shaderAttribs |= GENERICDEF_USE_TCGEN_AND_TCMOD; - } - - if (tess.shader->numDeforms && !ShaderRequiresCPUDeforms(tess.shader)) - { - shaderAttribs |= GENERICDEF_USE_DEFORM_VERTEXES; - } - - if (glState.vertexAttribsInterpolation > 0.0f && backEnd.currentEntity && backEnd.currentEntity != &tr.worldEntity) - { - shaderAttribs |= GENERICDEF_USE_VERTEX_ANIMATION; - } - - if (pStage->bundle[0].numTexMods) - { - shaderAttribs |= GENERICDEF_USE_TCGEN_AND_TCMOD; - } - - return &tr.genericShader[shaderAttribs]; -} diff --git a/src/rend2/tr_image.c b/src/rend2/tr_image.c deleted file mode 100644 index 8a1f574f..00000000 --- a/src/rend2/tr_image.c +++ /dev/null @@ -1,3462 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_image.c -#include "tr_local.h" - -static byte s_intensitytable[256]; -static unsigned char s_gammatable[256]; - -int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; -int gl_filter_max = GL_LINEAR; - -#define FILE_HASH_SIZE 1024 -static image_t* hashTable[FILE_HASH_SIZE]; - -/* -** R_GammaCorrect -*/ -void R_GammaCorrect( byte *buffer, int bufSize ) { - int i; - - for ( i = 0; i < bufSize; i++ ) { - buffer[i] = s_gammatable[buffer[i]]; - } -} - -typedef struct { - char *name; - int minimize, maximize; -} textureMode_t; - -textureMode_t modes[] = { - {"GL_NEAREST", GL_NEAREST, GL_NEAREST}, - {"GL_LINEAR", GL_LINEAR, GL_LINEAR}, - {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, - {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, - {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, - {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} -}; - -/* -================ -return a hash value for the filename -================ -*/ -static long generateHashValue( const char *fname ) { - int i; - long hash; - char letter; - - hash = 0; - i = 0; - while (fname[i] != '\0') { - letter = tolower(fname[i]); - if (letter =='.') break; // don't include extension - if (letter =='\\') letter = '/'; // damn path names - hash+=(long)(letter)*(i+119); - i++; - } - hash &= (FILE_HASH_SIZE-1); - return hash; -} - -/* -=============== -GL_TextureMode -=============== -*/ -void GL_TextureMode( const char *string ) { - int i; - image_t *glt; - - for ( i=0 ; i< 6 ; i++ ) { - if ( !Q_stricmp( modes[i].name, string ) ) { - break; - } - } - - // hack to prevent trilinear from being set on voodoo, - // because their driver freaks... - if ( i == 5 && glConfig.hardwareType == GLHW_3DFX_2D3D ) { - ri.Printf( PRINT_ALL, "Refusing to set trilinear on a voodoo.\n" ); - i = 3; - } - - - if ( i == 6 ) { - ri.Printf (PRINT_ALL, "bad filter name\n"); - return; - } - - gl_filter_min = modes[i].minimize; - gl_filter_max = modes[i].maximize; - - // change all the existing mipmap texture objects - for ( i = 0 ; i < tr.numImages ; i++ ) { - glt = tr.images[ i ]; - if ( glt->flags & IMGFLAG_MIPMAP ) { - GL_Bind (glt); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); - } - } -} - -/* -=============== -R_SumOfUsedImages -=============== -*/ -int R_SumOfUsedImages( void ) { - int total; - int i; - - total = 0; - for ( i = 0; i < tr.numImages; i++ ) { - if ( tr.images[i]->frameUsed == tr.frameCount ) { - total += tr.images[i]->uploadWidth * tr.images[i]->uploadHeight; - } - } - - return total; -} - -/* -=============== -R_ImageList_f -=============== -*/ -void R_ImageList_f( void ) { -#if 1 - int i; - int estTotalSize = 0; - - ri.Printf(PRINT_ALL, "\n -w-- -h-- type -size- --name-------\n"); - - for ( i = 0 ; i < tr.numImages ; i++ ) - { - image_t *image = tr.images[i]; - char *format = "???? "; - char *sizeSuffix; - int estSize; - int displaySize; - - estSize = image->uploadHeight * image->uploadWidth; - - switch(image->internalFormat) - { - case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: - format = "sDXT1"; - // 64 bits per 16 pixels, so 4 bits per pixel - estSize /= 2; - break; - case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: - format = "sDXT5"; - // 128 bits per 16 pixels, so 1 byte per pixel - break; - case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB: - format = "sBPTC"; - // 128 bits per 16 pixels, so 1 byte per pixel - break; - case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: - format = "LATC "; - // 128 bits per 16 pixels, so 1 byte per pixel - break; - case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: - format = "DXT1 "; - // 64 bits per 16 pixels, so 4 bits per pixel - estSize /= 2; - break; - case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - format = "DXT5 "; - // 128 bits per 16 pixels, so 1 byte per pixel - break; - case GL_COMPRESSED_RGBA_BPTC_UNORM_ARB: - format = "BPTC "; - // 128 bits per 16 pixels, so 1 byte per pixel - break; - case GL_RGB4_S3TC: - format = "S3TC "; - // same as DXT1? - estSize /= 2; - break; - case GL_RGBA4: - case GL_RGBA8: - case GL_RGBA: - format = "RGBA "; - // 4 bytes per pixel - estSize *= 4; - break; - case GL_LUMINANCE8: - case GL_LUMINANCE16: - case GL_LUMINANCE: - format = "L "; - // 1 byte per pixel? - break; - case GL_RGB5: - case GL_RGB8: - case GL_RGB: - format = "RGB "; - // 3 bytes per pixel? - estSize *= 3; - break; - case GL_LUMINANCE8_ALPHA8: - case GL_LUMINANCE16_ALPHA16: - case GL_LUMINANCE_ALPHA: - format = "LA "; - // 2 bytes per pixel? - estSize *= 2; - break; - case GL_SRGB_EXT: - case GL_SRGB8_EXT: - format = "sRGB "; - // 3 bytes per pixel? - estSize *= 3; - break; - case GL_SRGB_ALPHA_EXT: - case GL_SRGB8_ALPHA8_EXT: - format = "sRGBA"; - // 4 bytes per pixel? - estSize *= 4; - break; - case GL_SLUMINANCE_EXT: - case GL_SLUMINANCE8_EXT: - format = "sL "; - // 1 byte per pixel? - break; - case GL_SLUMINANCE_ALPHA_EXT: - case GL_SLUMINANCE8_ALPHA8_EXT: - format = "sLA "; - // 2 byte per pixel? - estSize *= 2; - break; - } - - // mipmap adds about 50% - if (image->flags & IMGFLAG_MIPMAP) - estSize += estSize / 2; - - sizeSuffix = "b "; - displaySize = estSize; - - if (displaySize > 1024) - { - displaySize /= 1024; - sizeSuffix = "kb"; - } - - if (displaySize > 1024) - { - displaySize /= 1024; - sizeSuffix = "Mb"; - } - - if (displaySize > 1024) - { - displaySize /= 1024; - sizeSuffix = "Gb"; - } - - ri.Printf(PRINT_ALL, "%4i: %4ix%4i %s %4i%s %s\n", i, image->uploadWidth, image->uploadHeight, format, displaySize, sizeSuffix, image->imgName); - estTotalSize += estSize; - } - - ri.Printf (PRINT_ALL, " ---------\n"); - ri.Printf (PRINT_ALL, " approx %i bytes\n", estTotalSize); - ri.Printf (PRINT_ALL, " %i total images\n\n", tr.numImages ); -#else - int i; - image_t *image; - int texels; - const char *yesno[] = { - "no ", "yes" - }; - - ri.Printf (PRINT_ALL, "\n -w-- -h-- -mm- -TMU- -if-- wrap --name-------\n"); - texels = 0; - - for ( i = 0 ; i < tr.numImages ; i++ ) { - image = tr.images[ i ]; - - texels += image->uploadWidth*image->uploadHeight; - ri.Printf (PRINT_ALL, "%4i: %4i %4i %s %d ", - i, image->uploadWidth, image->uploadHeight, yesno[(image->flags & IMGFLAG_MIPMAP) ? 1 : 0], image->TMU ); - switch ( image->internalFormat ) { - case 1: - ri.Printf( PRINT_ALL, "I " ); - break; - case 2: - ri.Printf( PRINT_ALL, "IA " ); - break; - case 3: - ri.Printf( PRINT_ALL, "RGB " ); - break; - case 4: - ri.Printf( PRINT_ALL, "RGBA " ); - break; - case GL_RGBA8: - ri.Printf( PRINT_ALL, "RGBA8" ); - break; - case GL_RGB8: - ri.Printf( PRINT_ALL, "RGB8" ); - break; - case GL_RGB4_S3TC: - ri.Printf( PRINT_ALL, "S3TC " ); - break; - case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: - ri.Printf( PRINT_ALL, "DXT1 " ); - break; - case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - ri.Printf( PRINT_ALL, "DXT5 " ); - break; - case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: - ri.Printf( PRINT_ALL, "LATC " ); - break; - case GL_RGBA4: - ri.Printf( PRINT_ALL, "RGBA4" ); - break; - case GL_RGB5: - ri.Printf( PRINT_ALL, "RGB5 " ); - break; - case GL_SRGB_EXT: - ri.Printf( PRINT_ALL, "sRGB " ); - break; - case GL_SRGB8_EXT: - ri.Printf( PRINT_ALL, "sRGB8" ); - break; - case GL_SRGB_ALPHA_EXT: - case GL_SRGB8_ALPHA8_EXT: - ri.Printf( PRINT_ALL, "sRGBA" ); - break; - /* - case GL_SLUMINANCE_EXT: - break; - case GL_SLUMINANCE8_EXT: - break; - case GL_SLUMINANCE_ALPHA_EXT: - break; - case GL_SLUMINANCE8_ALPHA8_EXT: - break; - */ - case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: - ri.Printf( PRINT_ALL, "sDXT1" ); - break; - case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: - ri.Printf( PRINT_ALL, "sDXT5" ); - break; - case GL_COMPRESSED_RGBA_BPTC_UNORM_ARB: - ri.Printf( PRINT_ALL, "BPTC " ); - break; - case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB: - ri.Printf( PRINT_ALL, "sBPTC" ); - break; - default: - ri.Printf( PRINT_ALL, "???? " ); - } - - if (image->flags & IMGFLAG_CLAMPTOEDGE) - ri.Printf( PRINT_ALL, "clmp " ); - else - ri.Printf( PRINT_ALL, "rept " ); - - ri.Printf( PRINT_ALL, " %s\n", image->imgName ); - } - ri.Printf (PRINT_ALL, " ---------\n"); - ri.Printf (PRINT_ALL, " %i total texels (not including mipmaps)\n", texels); - ri.Printf (PRINT_ALL, " %i total images\n\n", tr.numImages ); -#endif -} - -//======================================================================= - -/* -================ -ResampleTexture - -Used to resample images in a more general than quartering fashion. - -This will only be filtered properly if the resampled size -is greater than half the original size. - -If a larger shrinking is needed, use the mipmap function -before or after. -================ -*/ -static void ResampleTexture( byte *in, int inwidth, int inheight, byte *out, - int outwidth, int outheight ) { - int i, j; - byte *inrow, *inrow2; - int frac, fracstep; - int p1[2048], p2[2048]; - byte *pix1, *pix2, *pix3, *pix4; - - if (outwidth>2048) - ri.Error(ERR_DROP, "ResampleTexture: max width"); - - fracstep = inwidth*0x10000/outwidth; - - frac = fracstep>>2; - for ( i=0 ; i>16); - frac += fracstep; - } - frac = 3*(fracstep>>2); - for ( i=0 ; i>16); - frac += fracstep; - } - - for (i=0 ; i> 1; - for (j=0 ; j>2; - *out++ = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2; - *out++ = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2; - *out++ = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2; - } - } -} - -static void RGBAtoYCoCgA(const byte *in, byte *out, int width, int height) -{ - int x, y; - - for (y = 0; y < height; y++) - { - const byte *inbyte = in + y * width * 4; - byte *outbyte = out + y * width * 4; - - for (x = 0; x < width; x++) - { - byte r, g, b, a, rb2; - - r = *inbyte++; - g = *inbyte++; - b = *inbyte++; - a = *inbyte++; - rb2 = (r + b) >> 1; - - *outbyte++ = (g + rb2) >> 1; // Y = R/4 + G/2 + B/4 - *outbyte++ = (r - b + 256) >> 1; // Co = R/2 - B/2 - *outbyte++ = (g - rb2 + 256) >> 1; // Cg = -R/4 + G/2 - B/4 - *outbyte++ = a; - } - } -} - -static void YCoCgAtoRGBA(const byte *in, byte *out, int width, int height) -{ - int x, y; - - for (y = 0; y < height; y++) - { - const byte *inbyte = in + y * width * 4; - byte *outbyte = out + y * width * 4; - - for (x = 0; x < width; x++) - { - byte _Y, Co, Cg, a; - - _Y = *inbyte++; - Co = *inbyte++; - Cg = *inbyte++; - a = *inbyte++; - - *outbyte++ = CLAMP(_Y + Co - Cg, 0, 255); // R = Y + Co - Cg - *outbyte++ = CLAMP(_Y + Cg - 128, 0, 255); // G = Y + Cg - *outbyte++ = CLAMP(_Y - Co - Cg + 256, 0, 255); // B = Y - Co - Cg - *outbyte++ = a; - } - } -} - - -// uses a sobel filter to change a texture to a normal map -static void RGBAtoNormal(const byte *in, byte *out, int width, int height, qboolean clampToEdge) -{ - int x, y, max; - - // convert to heightmap, storing in alpha - // same as converting to Y in YCoCg - max = 1; - for (y = 0; y < height; y++) - { - const byte *inbyte = in + y * width * 4; - byte *outbyte = out + y * width * 4 + 3; - - for (x = 0; x < width; x++) - { - *outbyte = (inbyte[0] >> 2) + (inbyte[1] >> 1) + (inbyte[2] >> 2); - max = MAX(max, *outbyte); - outbyte += 4; - inbyte += 4; - } - } - - // level out heights - if (max < 255) - { - for (y = 0; y < height; y++) - { - byte *outbyte = out + y * width * 4 + 3; - - for (x = 0; x < width; x++) - { - *outbyte = *outbyte + (255 - max); - outbyte += 4; - } - } - } - - - // now run sobel filter over height values to generate X and Y - // then normalize - for (y = 0; y < height; y++) - { - byte *outbyte = out + y * width * 4; - - for (x = 0; x < width; x++) - { - // 0 1 2 - // 3 4 5 - // 6 7 8 - - byte s[9]; - int x2, y2, i; - vec3_t normal; - - i = 0; - for (y2 = -1; y2 <= 1; y2++) - { - int src_y = y + y2; - - if (clampToEdge) - { - src_y = CLAMP(src_y, 0, height - 1); - } - else - { - src_y = (src_y + height) % height; - } - - - for (x2 = -1; x2 <= 1; x2++) - { - int src_x = x + x2; - - if (clampToEdge) - { - src_x = CLAMP(src_x, 0, height - 1); - } - else - { - src_x = (src_x + height) % height; - } - - s[i++] = *(out + (src_y * width + src_x) * 4 + 3); - } - } - - normal[0] = s[0] - s[2] - + 2 * s[3] - 2 * s[5] - + s[6] - s[8]; - - normal[1] = s[0] + 2 * s[1] + s[2] - - - s[6] - 2 * s[7] - s[8]; - - normal[2] = s[4] * 4; - - if (!VectorNormalize2(normal, normal)) - { - VectorSet(normal, 0, 0, 1); - } - - *outbyte++ = FloatToOffsetByte(normal[0]); - *outbyte++ = FloatToOffsetByte(normal[1]); - *outbyte++ = FloatToOffsetByte(normal[2]); - outbyte++; - } - } -} - -#define COPYSAMPLE(a,b) *(unsigned int *)(a) = *(unsigned int *)(b) - -// based on Fast Curve Based Interpolation -// from Fast Artifacts-Free Image Interpolation (http://www.andreagiachetti.it/icbi/) -// assumes data has a 2 pixel thick border of clamped or wrapped data -// expects data to be a grid with even (0, 0), (2, 0), (0, 2), (2, 2) etc pixels filled -// only performs FCBI on specified component -static void DoFCBI(byte *in, byte *out, int width, int height, int component) -{ - int x, y; - byte *outbyte, *inbyte; - - // copy in to out - for (y = 2; y < height - 2; y += 2) - { - inbyte = in + (y * width + 2) * 4 + component; - outbyte = out + (y * width + 2) * 4 + component; - - for (x = 2; x < width - 2; x += 2) - { - *outbyte = *inbyte; - outbyte += 8; - inbyte += 8; - } - } - - for (y = 3; y < height - 3; y += 2) - { - // diagonals - // - // NWp - northwest interpolated pixel - // NEp - northeast interpolated pixel - // NWd - northwest first derivative - // NEd - northeast first derivative - // NWdd - northwest second derivative - // NEdd - northeast second derivative - // - // Uses these samples: - // - // 0 - // - - a - b - - - // - - - - - - - - // c - d - e - f - // 0 - - - - - - - - // g - h - i - j - // - - - - - - - - // - - k - l - - - // - // x+2 uses these samples: - // - // 0 - // - - - - a - b - - - // - - - - - - - - - - // - - c - d - e - f - // 0 - - - - - - - - - - // - - g - h - i - j - // - - - - - - - - - - // - - - - k - l - - - // - // so we can reuse 8 of them on next iteration - // - // a=b, c=d, d=e, e=f, g=h, h=i, i=j, k=l - // - // only b, f, j, and l need to be sampled on next iteration - - byte sa, sb, sc, sd, se, sf, sg, sh, si, sj, sk, sl; - byte *line1, *line2, *line3, *line4; - - x = 3; - - // optimization one - // SAMPLE2(sa, x-1, y-3); - //SAMPLE2(sc, x-3, y-1); SAMPLE2(sd, x-1, y-1); SAMPLE2(se, x+1, y-1); - //SAMPLE2(sg, x-3, y+1); SAMPLE2(sh, x-1, y+1); SAMPLE2(si, x+1, y+1); - // SAMPLE2(sk, x-1, y+3); - - // optimization two - line1 = in + ((y - 3) * width + (x - 1)) * 4 + component; - line2 = in + ((y - 1) * width + (x - 3)) * 4 + component; - line3 = in + ((y + 1) * width + (x - 3)) * 4 + component; - line4 = in + ((y + 3) * width + (x - 1)) * 4 + component; - - // COPYSAMPLE(sa, line1); line1 += 8; - //COPYSAMPLE(sc, line2); line2 += 8; COPYSAMPLE(sd, line2); line2 += 8; COPYSAMPLE(se, line2); line2 += 8; - //COPYSAMPLE(sg, line3); line3 += 8; COPYSAMPLE(sh, line3); line3 += 8; COPYSAMPLE(si, line3); line3 += 8; - // COPYSAMPLE(sk, line4); line4 += 8; - - sa = *line1; line1 += 8; - sc = *line2; line2 += 8; sd = *line2; line2 += 8; se = *line2; line2 += 8; - sg = *line3; line3 += 8; sh = *line3; line3 += 8; si = *line3; line3 += 8; - sk = *line4; line4 += 8; - - outbyte = out + (y * width + x) * 4 + component; - - for ( ; x < width - 3; x += 2) - { - int NWd, NEd, NWp, NEp; - - // original - // SAMPLE2(sa, x-1, y-3); SAMPLE2(sb, x+1, y-3); - //SAMPLE2(sc, x-3, y-1); SAMPLE2(sd, x-1, y-1); SAMPLE2(se, x+1, y-1); SAMPLE2(sf, x+3, y-1); - //SAMPLE2(sg, x-3, y+1); SAMPLE2(sh, x-1, y+1); SAMPLE2(si, x+1, y+1); SAMPLE2(sj, x+3, y+1); - // SAMPLE2(sk, x-1, y+3); SAMPLE2(sl, x+1, y+3); - - // optimization one - //SAMPLE2(sb, x+1, y-3); - //SAMPLE2(sf, x+3, y-1); - //SAMPLE2(sj, x+3, y+1); - //SAMPLE2(sl, x+1, y+3); - - // optimization two - //COPYSAMPLE(sb, line1); line1 += 8; - //COPYSAMPLE(sf, line2); line2 += 8; - //COPYSAMPLE(sj, line3); line3 += 8; - //COPYSAMPLE(sl, line4); line4 += 8; - - sb = *line1; line1 += 8; - sf = *line2; line2 += 8; - sj = *line3; line3 += 8; - sl = *line4; line4 += 8; - - NWp = sd + si; - NEp = se + sh; - NWd = abs(sd - si); - NEd = abs(se - sh); - - if (NWd > 100 || NEd > 100 || abs(NWp-NEp) > 200) - { - if (NWd < NEd) - *outbyte = NWp >> 1; - else - *outbyte = NEp >> 1; - } - else - { - int NWdd, NEdd; - - //NEdd = abs(sg + sd + sb - 3 * (se + sh) + sk + si + sf); - //NWdd = abs(sa + se + sj - 3 * (sd + si) + sc + sh + sl); - NEdd = abs(sg + sb - 3 * NEp + sk + sf + NWp); - NWdd = abs(sa + sj - 3 * NWp + sc + sl + NEp); - - if (NWdd > NEdd) - *outbyte = NWp >> 1; - else - *outbyte = NEp >> 1; - } - - outbyte += 8; - - // COPYSAMPLE(sa, sb); - //COPYSAMPLE(sc, sd); COPYSAMPLE(sd, se); COPYSAMPLE(se, sf); - //COPYSAMPLE(sg, sh); COPYSAMPLE(sh, si); COPYSAMPLE(si, sj); - // COPYSAMPLE(sk, sl); - - sa = sb; - sc = sd; sd = se; se = sf; - sg = sh; sh = si; si = sj; - sk = sl; - } - } - - // hack: copy out to in again - for (y = 3; y < height - 3; y += 2) - { - inbyte = out + (y * width + 3) * 4 + component; - outbyte = in + (y * width + 3) * 4 + component; - - for (x = 3; x < width - 3; x += 2) - { - *outbyte = *inbyte; - outbyte += 8; - inbyte += 8; - } - } - - for (y = 2; y < height - 3; y++) - { - // horizontal & vertical - // - // hp - horizontally interpolated pixel - // vp - vertically interpolated pixel - // hd - horizontal first derivative - // vd - vertical first derivative - // hdd - horizontal second derivative - // vdd - vertical second derivative - // Uses these samples: - // - // 0 - // - a - b - - // c - d - e - // 0 - f - g - - // h - i - j - // - k - l - - // - // x+2 uses these samples: - // - // 0 - // - - - a - b - - // - - c - d - e - // 0 - - - f - g - - // - - h - i - j - // - - - k - l - - // - // so we can reuse 7 of them on next iteration - // - // a=b, c=d, d=e, f=g, h=i, i=j, k=l - // - // only b, e, g, j, and l need to be sampled on next iteration - - byte sa, sb, sc, sd, se, sf, sg, sh, si, sj, sk, sl; - byte *line1, *line2, *line3, *line4, *line5; - - //x = (y + 1) % 2; - x = (y + 1) % 2 + 2; - - // optimization one - // SAMPLE2(sa, x-1, y-2); - //SAMPLE2(sc, x-2, y-1); SAMPLE2(sd, x, y-1); - // SAMPLE2(sf, x-1, y ); - //SAMPLE2(sh, x-2, y+1); SAMPLE2(si, x, y+1); - // SAMPLE2(sk, x-1, y+2); - - line1 = in + ((y - 2) * width + (x - 1)) * 4 + component; - line2 = in + ((y - 1) * width + (x - 2)) * 4 + component; - line3 = in + ((y ) * width + (x - 1)) * 4 + component; - line4 = in + ((y + 1) * width + (x - 2)) * 4 + component; - line5 = in + ((y + 2) * width + (x - 1)) * 4 + component; - - // COPYSAMPLE(sa, line1); line1 += 8; - //COPYSAMPLE(sc, line2); line2 += 8; COPYSAMPLE(sd, line2); line2 += 8; - // COPYSAMPLE(sf, line3); line3 += 8; - //COPYSAMPLE(sh, line4); line4 += 8; COPYSAMPLE(si, line4); line4 += 8; - // COPYSAMPLE(sk, line5); line5 += 8; - - sa = *line1; line1 += 8; - sc = *line2; line2 += 8; sd = *line2; line2 += 8; - sf = *line3; line3 += 8; - sh = *line4; line4 += 8; si = *line4; line4 += 8; - sk = *line5; line5 += 8; - - outbyte = out + (y * width + x) * 4 + component; - - for ( ; x < width - 3; x+=2) - { - int hd, vd, hp, vp; - - // SAMPLE2(sa, x-1, y-2); SAMPLE2(sb, x+1, y-2); - //SAMPLE2(sc, x-2, y-1); SAMPLE2(sd, x, y-1); SAMPLE2(se, x+2, y-1); - // SAMPLE2(sf, x-1, y ); SAMPLE2(sg, x+1, y ); - //SAMPLE2(sh, x-2, y+1); SAMPLE2(si, x, y+1); SAMPLE2(sj, x+2, y+1); - // SAMPLE2(sk, x-1, y+2); SAMPLE2(sl, x+1, y+2); - - // optimization one - //SAMPLE2(sb, x+1, y-2); - //SAMPLE2(se, x+2, y-1); - //SAMPLE2(sg, x+1, y ); - //SAMPLE2(sj, x+2, y+1); - //SAMPLE2(sl, x+1, y+2); - - //COPYSAMPLE(sb, line1); line1 += 8; - //COPYSAMPLE(se, line2); line2 += 8; - //COPYSAMPLE(sg, line3); line3 += 8; - //COPYSAMPLE(sj, line4); line4 += 8; - //COPYSAMPLE(sl, line5); line5 += 8; - - sb = *line1; line1 += 8; - se = *line2; line2 += 8; - sg = *line3; line3 += 8; - sj = *line4; line4 += 8; - sl = *line5; line5 += 8; - - hp = sf + sg; - vp = sd + si; - hd = abs(sf - sg); - vd = abs(sd - si); - - if (hd > 100 || vd > 100 || abs(hp-vp) > 200) - { - if (hd < vd) - *outbyte = hp >> 1; - else - *outbyte = vp >> 1; - } - else - { - int hdd, vdd; - - //hdd = abs(sc[i] + sd[i] + se[i] - 3 * (sf[i] + sg[i]) + sh[i] + si[i] + sj[i]); - //vdd = abs(sa[i] + sf[i] + sk[i] - 3 * (sd[i] + si[i]) + sb[i] + sg[i] + sl[i]); - - hdd = abs(sc + se - 3 * hp + sh + sj + vp); - vdd = abs(sa + sk - 3 * vp + sb + sl + hp); - - if (hdd > vdd) - *outbyte = hp >> 1; - else - *outbyte = vp >> 1; - } - - outbyte += 8; - - // COPYSAMPLE(sa, sb); - //COPYSAMPLE(sc, sd); COPYSAMPLE(sd, se); - // COPYSAMPLE(sf, sg); - //COPYSAMPLE(sh, si); COPYSAMPLE(si, sj); - // COPYSAMPLE(sk, sl); - sa = sb; - sc = sd; sd = se; - sf = sg; - sh = si; si = sj; - sk = sl; - } - } -} - -// Similar to FCBI, but throws out the second order derivatives for speed -static void DoFCBIQuick(byte *in, byte *out, int width, int height, int component) -{ - int x, y; - byte *outbyte, *inbyte; - - // copy in to out - for (y = 2; y < height - 2; y += 2) - { - inbyte = in + (y * width + 2) * 4 + component; - outbyte = out + (y * width + 2) * 4 + component; - - for (x = 2; x < width - 2; x += 2) - { - *outbyte = *inbyte; - outbyte += 8; - inbyte += 8; - } - } - - for (y = 3; y < height - 4; y += 2) - { - byte sd, se, sh, si; - byte *line2, *line3; - - x = 3; - - line2 = in + ((y - 1) * width + (x - 1)) * 4 + component; - line3 = in + ((y + 1) * width + (x - 1)) * 4 + component; - - sd = *line2; line2 += 8; - sh = *line3; line3 += 8; - - outbyte = out + (y * width + x) * 4 + component; - - for ( ; x < width - 4; x += 2) - { - int NWd, NEd, NWp, NEp; - - se = *line2; line2 += 8; - si = *line3; line3 += 8; - - NWp = sd + si; - NEp = se + sh; - NWd = abs(sd - si); - NEd = abs(se - sh); - - if (NWd < NEd) - *outbyte = NWp >> 1; - else - *outbyte = NEp >> 1; - - outbyte += 8; - - sd = se; - sh = si; - } - } - - // hack: copy out to in again - for (y = 3; y < height - 3; y += 2) - { - inbyte = out + (y * width + 3) * 4 + component; - outbyte = in + (y * width + 3) * 4 + component; - - for (x = 3; x < width - 3; x += 2) - { - *outbyte = *inbyte; - outbyte += 8; - inbyte += 8; - } - } - - for (y = 2; y < height - 3; y++) - { - byte sd, sf, sg, si; - byte *line2, *line3, *line4; - - x = (y + 1) % 2 + 2; - - line2 = in + ((y - 1) * width + (x )) * 4 + component; - line3 = in + ((y ) * width + (x - 1)) * 4 + component; - line4 = in + ((y + 1) * width + (x )) * 4 + component; - - outbyte = out + (y * width + x) * 4 + component; - - sf = *line3; line3 += 8; - - for ( ; x < width - 3; x+=2) - { - int hd, vd, hp, vp; - - sd = *line2; line2 += 8; - sg = *line3; line3 += 8; - si = *line4; line4 += 8; - - hp = sf + sg; - vp = sd + si; - hd = abs(sf - sg); - vd = abs(sd - si); - - if (hd < vd) - *outbyte = hp >> 1; - else - *outbyte = vp >> 1; - - outbyte += 8; - - sf = sg; - } - } -} - -// Similar to DoFCBIQuick, but just takes the average instead of checking derivatives -// as well, this operates on all four components -static void DoLinear(byte *in, byte *out, int width, int height) -{ - int x, y, i; - byte *outbyte, *inbyte; - - // copy in to out - for (y = 2; y < height - 2; y += 2) - { - x = 2; - - inbyte = in + (y * width + x) * 4; - outbyte = out + (y * width + x) * 4; - - for ( ; x < width - 2; x += 2) - { - COPYSAMPLE(outbyte, inbyte); - outbyte += 8; - inbyte += 8; - } - } - - for (y = 1; y < height - 1; y += 2) - { - byte sd[4], se[4], sh[4], si[4]; - byte *line2, *line3; - - x = 1; - - line2 = in + ((y - 1) * width + (x - 1)) * 4; - line3 = in + ((y + 1) * width + (x - 1)) * 4; - - COPYSAMPLE(sd, line2); line2 += 8; - COPYSAMPLE(sh, line3); line3 += 8; - - outbyte = out + (y * width + x) * 4; - - for ( ; x < width - 1; x += 2) - { - COPYSAMPLE(se, line2); line2 += 8; - COPYSAMPLE(si, line3); line3 += 8; - - for (i = 0; i < 4; i++) - { - *outbyte++ = (sd[i] + si[i] + se[i] + sh[i]) >> 2; - } - - outbyte += 4; - - COPYSAMPLE(sd, se); - COPYSAMPLE(sh, si); - } - } - - // hack: copy out to in again - for (y = 1; y < height - 1; y += 2) - { - x = 1; - - inbyte = out + (y * width + x) * 4; - outbyte = in + (y * width + x) * 4; - - for ( ; x < width - 1; x += 2) - { - COPYSAMPLE(outbyte, inbyte); - outbyte += 8; - inbyte += 8; - } - } - - for (y = 1; y < height - 1; y++) - { - byte sd[4], sf[4], sg[4], si[4]; - byte *line2, *line3, *line4; - - x = y % 2 + 1; - - line2 = in + ((y - 1) * width + (x )) * 4; - line3 = in + ((y ) * width + (x - 1)) * 4; - line4 = in + ((y + 1) * width + (x )) * 4; - - COPYSAMPLE(sf, line3); line3 += 8; - - outbyte = out + (y * width + x) * 4; - - for ( ; x < width - 1; x += 2) - { - COPYSAMPLE(sd, line2); line2 += 8; - COPYSAMPLE(sg, line3); line3 += 8; - COPYSAMPLE(si, line4); line4 += 8; - - for (i = 0; i < 4; i++) - { - *outbyte++ = (sf[i] + sg[i] + sd[i] + si[i]) >> 2; - } - - outbyte += 4; - - COPYSAMPLE(sf, sg); - } - } -} - - -static void ExpandHalfTextureToGrid( byte *data, int width, int height) -{ - int x, y; - - for (y = height / 2; y > 0; y--) - { - byte *outbyte = data + ((y * 2 - 1) * (width) - 2) * 4; - byte *inbyte = data + (y * (width / 2) - 1) * 4; - - for (x = width / 2; x > 0; x--) - { - COPYSAMPLE(outbyte, inbyte); - - outbyte -= 8; - inbyte -= 4; - } - } -} - -static void FillInNormalizedZ(const byte *in, byte *out, int width, int height) -{ - int x, y; - - for (y = 0; y < height; y++) - { - const byte *inbyte = in + y * width * 4; - byte *outbyte = out + y * width * 4; - - for (x = 0; x < width; x++) - { - byte nx, ny, nz, h; - float fnx, fny, fll, fnz; - - nx = *inbyte++; - ny = *inbyte++; - inbyte++; - h = *inbyte++; - - fnx = OffsetByteToFloat(nx); - fny = OffsetByteToFloat(ny); - fll = 1.0f - fnx * fnx - fny * fny; - if (fll >= 0.0f) - fnz = (float)sqrt(fll); - else - fnz = 0.0f; - - nz = FloatToOffsetByte(fnz); - - *outbyte++ = nx; - *outbyte++ = ny; - *outbyte++ = nz; - *outbyte++ = h; - } - } -} - - -// size must be even -#define WORKBLOCK_SIZE 128 -#define WORKBLOCK_BORDER 4 -#define WORKBLOCK_REALSIZE (WORKBLOCK_SIZE + WORKBLOCK_BORDER * 2) - -// assumes that data has already been expanded into a 2x2 grid -static void FCBIByBlock(byte *data, int width, int height, qboolean clampToEdge, qboolean normalized) -{ - byte workdata[WORKBLOCK_REALSIZE * WORKBLOCK_REALSIZE * 4]; - byte outdata[WORKBLOCK_REALSIZE * WORKBLOCK_REALSIZE * 4]; - byte *inbyte, *outbyte; - int x, y; - int srcx, srcy; - - ExpandHalfTextureToGrid(data, width, height); - - for (y = 0; y < height; y += WORKBLOCK_SIZE) - { - for (x = 0; x < width; x += WORKBLOCK_SIZE) - { - int x2, y2; - int workwidth, workheight, fullworkwidth, fullworkheight; - - workwidth = MIN(WORKBLOCK_SIZE, width - x); - workheight = MIN(WORKBLOCK_SIZE, height - y); - - fullworkwidth = workwidth + WORKBLOCK_BORDER * 2; - fullworkheight = workheight + WORKBLOCK_BORDER * 2; - - //memset(workdata, 0, WORKBLOCK_REALSIZE * WORKBLOCK_REALSIZE * 4); - - // fill in work block - for (y2 = 0; y2 < fullworkheight; y2 += 2) - { - srcy = y + y2 - WORKBLOCK_BORDER; - - if (clampToEdge) - { - srcy = CLAMP(srcy, 0, height - 2); - } - else - { - srcy = (srcy + height) % height; - } - - outbyte = workdata + y2 * fullworkwidth * 4; - inbyte = data + srcy * width * 4; - - for (x2 = 0; x2 < fullworkwidth; x2 += 2) - { - srcx = x + x2 - WORKBLOCK_BORDER; - - if (clampToEdge) - { - srcx = CLAMP(srcx, 0, width - 2); - } - else - { - srcx = (srcx + width) % width; - } - - COPYSAMPLE(outbyte, inbyte + srcx * 4); - outbyte += 8; - } - } - - // submit work block - DoLinear(workdata, outdata, fullworkwidth, fullworkheight); - - if (!normalized) - { - switch (r_imageUpsampleType->integer) - { - case 0: - break; - case 1: - DoFCBIQuick(workdata, outdata, fullworkwidth, fullworkheight, 0); - break; - case 2: - default: - DoFCBI(workdata, outdata, fullworkwidth, fullworkheight, 0); - break; - } - } - else - { - switch (r_imageUpsampleType->integer) - { - case 0: - break; - case 1: - DoFCBIQuick(workdata, outdata, fullworkwidth, fullworkheight, 0); - DoFCBIQuick(workdata, outdata, fullworkwidth, fullworkheight, 1); - break; - case 2: - default: - DoFCBI(workdata, outdata, fullworkwidth, fullworkheight, 0); - DoFCBI(workdata, outdata, fullworkwidth, fullworkheight, 1); - break; - } - } - - // copy back work block - for (y2 = 0; y2 < workheight; y2++) - { - inbyte = outdata + ((y2 + WORKBLOCK_BORDER) * fullworkwidth + WORKBLOCK_BORDER) * 4; - outbyte = data + ((y + y2) * width + x) * 4; - for (x2 = 0; x2 < workwidth; x2++) - { - COPYSAMPLE(outbyte, inbyte); - outbyte += 4; - inbyte += 4; - } - } - } - } -} -#undef COPYSAMPLE - -/* -================ -R_LightScaleTexture - -Scale up the pixel values in a texture to increase the -lighting range -================ -*/ -void R_LightScaleTexture (byte *in, int inwidth, int inheight, qboolean only_gamma ) -{ - if ( only_gamma ) - { - if ( !glConfig.deviceSupportsGamma ) - { - int i, c; - byte *p; - - p = in; - - c = inwidth*inheight; - for (i=0 ; i> 1; - outHeight = inHeight >> 1; - temp = ri.Hunk_AllocateTempMemory( outWidth * outHeight * 4 ); - - inWidthMask = inWidth - 1; - inHeightMask = inHeight - 1; - - for ( i = 0 ; i < outHeight ; i++ ) { - for ( j = 0 ; j < outWidth ; j++ ) { - outpix = (byte *) ( temp + i * outWidth + j ); - for ( k = 0 ; k < 4 ; k++ ) { - total = - 1 * (&in[ 4*(((i*2-1)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask)) ])[k] + - 2 * (&in[ 4*(((i*2-1)&inHeightMask)*inWidth + ((j*2 )&inWidthMask)) ])[k] + - 2 * (&in[ 4*(((i*2-1)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask)) ])[k] + - 1 * (&in[ 4*(((i*2-1)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask)) ])[k] + - - 2 * (&in[ 4*(((i*2 )&inHeightMask)*inWidth + ((j*2-1)&inWidthMask)) ])[k] + - 4 * (&in[ 4*(((i*2 )&inHeightMask)*inWidth + ((j*2 )&inWidthMask)) ])[k] + - 4 * (&in[ 4*(((i*2 )&inHeightMask)*inWidth + ((j*2+1)&inWidthMask)) ])[k] + - 2 * (&in[ 4*(((i*2 )&inHeightMask)*inWidth + ((j*2+2)&inWidthMask)) ])[k] + - - 2 * (&in[ 4*(((i*2+1)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask)) ])[k] + - 4 * (&in[ 4*(((i*2+1)&inHeightMask)*inWidth + ((j*2 )&inWidthMask)) ])[k] + - 4 * (&in[ 4*(((i*2+1)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask)) ])[k] + - 2 * (&in[ 4*(((i*2+1)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask)) ])[k] + - - 1 * (&in[ 4*(((i*2+2)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask)) ])[k] + - 2 * (&in[ 4*(((i*2+2)&inHeightMask)*inWidth + ((j*2 )&inWidthMask)) ])[k] + - 2 * (&in[ 4*(((i*2+2)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask)) ])[k] + - 1 * (&in[ 4*(((i*2+2)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask)) ])[k]; - outpix[k] = total / 36; - } - } - } - - Com_Memcpy( in, temp, outWidth * outHeight * 4 ); - ri.Hunk_FreeTempMemory( temp ); -} - - -static void R_MipMapsRGB( byte *in, int inWidth, int inHeight) -{ - int i, j, k; - int outWidth, outHeight; - byte *temp; - - outWidth = inWidth >> 1; - outHeight = inHeight >> 1; - temp = ri.Hunk_AllocateTempMemory( outWidth * outHeight * 4 ); - - for ( i = 0 ; i < outHeight ; i++ ) { - byte *outbyte = temp + ( i * outWidth ) * 4; - byte *inbyte1 = in + ( i * 2 * inWidth ) * 4; - byte *inbyte2 = in + ( (i * 2 + 1) * inWidth ) * 4; - for ( j = 0 ; j < outWidth ; j++ ) { - for ( k = 0 ; k < 3 ; k++ ) { - float total, current; - - current = ByteToFloat(inbyte1[0]); total = sRGBtoRGB(current); - current = ByteToFloat(inbyte1[4]); total += sRGBtoRGB(current); - current = ByteToFloat(inbyte2[0]); total += sRGBtoRGB(current); - current = ByteToFloat(inbyte2[4]); total += sRGBtoRGB(current); - - total *= 0.25f; - - inbyte1++; - inbyte2++; - - current = RGBtosRGB(total); - *outbyte++ = FloatToByte(current); - } - *outbyte++ = (inbyte1[0] + inbyte1[4] + inbyte2[0] + inbyte2[4]) >> 2; - inbyte1 += 5; - inbyte2 += 5; - } - } - - Com_Memcpy( in, temp, outWidth * outHeight * 4 ); - ri.Hunk_FreeTempMemory( temp ); -} - -/* -================ -R_MipMap - -Operates in place, quartering the size of the texture -================ -*/ -static void R_MipMap (byte *in, int width, int height) { - int i, j; - byte *out; - int row; - - if ( !r_simpleMipMaps->integer ) { - R_MipMap2( in, width, height ); - return; - } - - if ( width == 1 && height == 1 ) { - return; - } - - row = width * 4; - out = in; - width >>= 1; - height >>= 1; - - if ( width == 0 || height == 0 ) { - width += height; // get largest - for (i=0 ; i>1; - out[1] = ( in[1] + in[5] )>>1; - out[2] = ( in[2] + in[6] )>>1; - out[3] = ( in[3] + in[7] )>>1; - } - return; - } - - for (i=0 ; i>2; - out[1] = (in[1] + in[5] + in[row+1] + in[row+5])>>2; - out[2] = (in[2] + in[6] + in[row+2] + in[row+6])>>2; - out[3] = (in[3] + in[7] + in[row+3] + in[row+7])>>2; - } - } -} - - -static void R_MipMapLuminanceAlpha (const byte *in, byte *out, int width, int height) -{ - int i, j, row; - - if ( width == 1 && height == 1 ) { - return; - } - - row = width * 4; - width >>= 1; - height >>= 1; - - if ( width == 0 || height == 0 ) { - width += height; // get largest - for (i=0 ; i> 1; - out[3] = (in[3] + in[7]) >> 1; - } - return; - } - - for (i=0 ; i> 2; - out[3] = (in[3] + in[7] + in[row+3] + in[row+7]) >> 2; - } - } - -} - - -static void R_MipMapNormalHeight (const byte *in, byte *out, int width, int height, qboolean swizzle) -{ - int i, j; - int row; - int sx = swizzle ? 3 : 0; - int sa = swizzle ? 0 : 3; - - if ( width == 1 && height == 1 ) { - return; - } - - row = width * 4; - width >>= 1; - height >>= 1; - - for (i=0 ; i> 9; - data[1] = ( data[1] * inverseAlpha + premult[1] ) >> 9; - data[2] = ( data[2] * inverseAlpha + premult[2] ) >> 9; - } -} - -byte mipBlendColors[16][4] = { - {0,0,0,0}, - {255,0,0,128}, - {0,255,0,128}, - {0,0,255,128}, - {255,0,0,128}, - {0,255,0,128}, - {0,0,255,128}, - {255,0,0,128}, - {0,255,0,128}, - {0,0,255,128}, - {255,0,0,128}, - {0,255,0,128}, - {0,0,255,128}, - {255,0,0,128}, - {0,255,0,128}, - {0,0,255,128}, -}; - -static void RawImage_SwizzleRA( byte *data, int width, int height ) -{ - int i; - byte *ptr = data, swap; - - for (i=0; iinteger && scaled_width > width ) - scaled_width >>= 1; - if ( r_roundImagesDown->integer && scaled_height > height ) - scaled_height >>= 1; - - if ( picmip && data && resampledBuffer && r_imageUpsample->integer && - scaled_width < r_imageUpsampleMaxSize->integer && scaled_height < r_imageUpsampleMaxSize->integer) - { - int finalwidth, finalheight; - //int startTime, endTime; - - //startTime = ri.Milliseconds(); - - finalwidth = scaled_width << r_imageUpsample->integer; - finalheight = scaled_height << r_imageUpsample->integer; - - while ( finalwidth > r_imageUpsampleMaxSize->integer - || finalheight > r_imageUpsampleMaxSize->integer ) { - finalwidth >>= 1; - finalheight >>= 1; - } - - while ( finalwidth > glConfig.maxTextureSize - || finalheight > glConfig.maxTextureSize ) { - finalwidth >>= 1; - finalheight >>= 1; - } - - *resampledBuffer = ri.Hunk_AllocateTempMemory( finalwidth * finalheight * 4 ); - - if (scaled_width != width || scaled_height != height) - { - ResampleTexture (*data, width, height, *resampledBuffer, scaled_width, scaled_height); - } - else - { - byte *inbyte, *outbyte; - int i; - - inbyte = *data; - outbyte = *resampledBuffer; - - for (i = width * height * 4; i > 0; i--) - { - *outbyte++ = *inbyte++; - } - } - - if (type == IMGTYPE_COLORALPHA) - RGBAtoYCoCgA(*resampledBuffer, *resampledBuffer, scaled_width, scaled_height); - - while (scaled_width < finalwidth || scaled_height < finalheight) - { - scaled_width <<= 1; - scaled_height <<= 1; - - FCBIByBlock(*resampledBuffer, scaled_width, scaled_height, clampToEdge, (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT)); - } - - if (type == IMGTYPE_COLORALPHA) - { - YCoCgAtoRGBA(*resampledBuffer, *resampledBuffer, scaled_width, scaled_height); - } - else if (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT) - { - FillInNormalizedZ(*resampledBuffer, *resampledBuffer, scaled_width, scaled_height); - } - - - //endTime = ri.Milliseconds(); - - //ri.Printf(PRINT_ALL, "upsampled %dx%d to %dx%d in %dms\n", width, height, scaled_width, scaled_height, endTime - startTime); - - *data = *resampledBuffer; - width = scaled_width; - height = scaled_height; - } - else if ( scaled_width != width || scaled_height != height ) { - if (data && resampledBuffer) - { - *resampledBuffer = ri.Hunk_AllocateTempMemory( scaled_width * scaled_height * 4 ); - ResampleTexture (*data, width, height, *resampledBuffer, scaled_width, scaled_height); - *data = *resampledBuffer; - } - width = scaled_width; - height = scaled_height; - } - - // - // perform optional picmip operation - // - if ( picmip ) { - scaled_width >>= r_picmip->integer; - scaled_height >>= r_picmip->integer; - } - - // - // clamp to minimum size - // - if (scaled_width < 1) { - scaled_width = 1; - } - if (scaled_height < 1) { - scaled_height = 1; - } - - // - // clamp to the current upper OpenGL limit - // scale both axis down equally so we don't have to - // deal with a half mip resampling - // - while ( scaled_width > glConfig.maxTextureSize - || scaled_height > glConfig.maxTextureSize ) { - scaled_width >>= 1; - scaled_height >>= 1; - } - - *inout_width = width; - *inout_height = height; - *inout_scaled_width = scaled_width; - *inout_scaled_height = scaled_height; -} - - -static qboolean RawImage_HasAlpha(const byte *scan, int numPixels) -{ - int i; - - if (!scan) - return qtrue; - - for ( i = 0; i < numPixels; i++ ) - { - if ( scan[i*4 + 3] != 255 ) - { - return qtrue; - } - } - - return qfalse; -} - -static GLenum RawImage_GetFormat(const byte *data, int numPixels, qboolean lightMap, imgType_t type, imgFlags_t flags) -{ - int samples = 3; - GLenum internalFormat = GL_RGB; - qboolean forceNoCompression = (flags & IMGFLAG_NO_COMPRESSION); - qboolean normalmap = (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT); - - if(normalmap) - { - if ((!RawImage_HasAlpha(data, numPixels) || (type == IMGTYPE_NORMAL)) && !forceNoCompression && (glRefConfig.textureCompression & TCR_LATC)) - { - internalFormat = GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT; - } - else - { - if ( !forceNoCompression && glConfig.textureCompression == TC_S3TC_ARB ) - { - internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - } - else if ( r_texturebits->integer == 16 ) - { - internalFormat = GL_RGBA4; - } - else if ( r_texturebits->integer == 32 ) - { - internalFormat = GL_RGBA8; - } - else - { - internalFormat = GL_RGBA; - } - } - } - else if(lightMap) - { - samples = 4; - if(r_greyscale->integer) - internalFormat = GL_LUMINANCE; - else - internalFormat = GL_RGBA; - } - else - { - if (RawImage_HasAlpha(data, numPixels)) - { - samples = 4; - } - - // select proper internal format - if ( samples == 3 ) - { - if(r_greyscale->integer) - { - if(r_texturebits->integer == 16) - internalFormat = GL_LUMINANCE8; - else if(r_texturebits->integer == 32) - internalFormat = GL_LUMINANCE16; - else - internalFormat = GL_LUMINANCE; - } - else - { - if ( !forceNoCompression && (glRefConfig.textureCompression & TCR_BPTC) ) - { - internalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; - } - else if ( !forceNoCompression && glConfig.textureCompression == TC_S3TC_ARB ) - { - internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - } - else if ( !forceNoCompression && glConfig.textureCompression == TC_S3TC ) - { - internalFormat = GL_RGB4_S3TC; - } - else if ( r_texturebits->integer == 16 ) - { - internalFormat = GL_RGB5; - } - else if ( r_texturebits->integer == 32 ) - { - internalFormat = GL_RGB8; - } - else - { - internalFormat = GL_RGB; - } - } - } - else if ( samples == 4 ) - { - if(r_greyscale->integer) - { - if(r_texturebits->integer == 16) - internalFormat = GL_LUMINANCE8_ALPHA8; - else if(r_texturebits->integer == 32) - internalFormat = GL_LUMINANCE16_ALPHA16; - else - internalFormat = GL_LUMINANCE_ALPHA; - } - else - { - if ( !forceNoCompression && (glRefConfig.textureCompression & TCR_BPTC) ) - { - internalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; - } - else if ( !forceNoCompression && glConfig.textureCompression == TC_S3TC_ARB ) - { - internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - } - else if ( r_texturebits->integer == 16 ) - { - internalFormat = GL_RGBA4; - } - else if ( r_texturebits->integer == 32 ) - { - internalFormat = GL_RGBA8; - } - else - { - internalFormat = GL_RGBA; - } - } - } - - if (glRefConfig.texture_srgb && (flags & IMGFLAG_SRGB)) - { - switch(internalFormat) - { - case GL_RGB: - internalFormat = GL_SRGB_EXT; - break; - - case GL_RGB4: - case GL_RGB5: - case GL_RGB8: - internalFormat = GL_SRGB8_EXT; - break; - - case GL_RGBA: - internalFormat = GL_SRGB_ALPHA_EXT; - break; - - case GL_RGBA4: - case GL_RGBA8: - internalFormat = GL_SRGB8_ALPHA8_EXT; - break; - - case GL_LUMINANCE: - internalFormat = GL_SLUMINANCE_EXT; - break; - - case GL_LUMINANCE8: - case GL_LUMINANCE16: - internalFormat = GL_SLUMINANCE8_EXT; - break; - - case GL_LUMINANCE_ALPHA: - internalFormat = GL_SLUMINANCE_ALPHA_EXT; - break; - - case GL_LUMINANCE8_ALPHA8: - case GL_LUMINANCE16_ALPHA16: - internalFormat = GL_SLUMINANCE8_ALPHA8_EXT; - break; - - case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: - internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; - break; - - case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: - internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; - break; - - case GL_COMPRESSED_RGBA_BPTC_UNORM_ARB: - internalFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB; - break; - } - } - } - - return internalFormat; -} - - -static void RawImage_UploadTexture( byte *data, int x, int y, int width, int height, GLenum internalFormat, imgType_t type, imgFlags_t flags, qboolean subtexture ) -{ - int dataFormat, dataType; - - switch(internalFormat) - { - case GL_DEPTH_COMPONENT: - case GL_DEPTH_COMPONENT16_ARB: - case GL_DEPTH_COMPONENT24_ARB: - case GL_DEPTH_COMPONENT32_ARB: - dataFormat = GL_DEPTH_COMPONENT; - dataType = GL_UNSIGNED_BYTE; - break; - case GL_RGBA16F_ARB: - dataFormat = GL_RGBA; - dataType = GL_HALF_FLOAT_ARB; - break; - default: - dataFormat = GL_RGBA; - dataType = GL_UNSIGNED_BYTE; - break; - } - - if ( subtexture ) - qglTexSubImage2D( GL_TEXTURE_2D, 0, x, y, width, height, dataFormat, dataType, data ); - else - qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, width, height, 0, dataFormat, dataType, data ); - - if (flags & IMGFLAG_MIPMAP) - { - int miplevel; - - miplevel = 0; - while (width > 1 || height > 1) - { - if (data) - { - if (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT) - { - if (internalFormat == GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT) - { - R_MipMapLuminanceAlpha( data, data, width, height ); - } - else - { - R_MipMapNormalHeight( data, data, width, height, qtrue); - } - } - else if (flags & IMGFLAG_SRGB) - { - R_MipMapsRGB( data, width, height ); - } - else - { - R_MipMap( data, width, height ); - } - } - - width >>= 1; - height >>= 1; - if (width < 1) - width = 1; - if (height < 1) - height = 1; - miplevel++; - - if ( data && r_colorMipLevels->integer ) - R_BlendOverTexture( (byte *)data, width * height, mipBlendColors[miplevel] ); - - if ( subtexture ) - { - x >>= 1; - y >>= 1; - qglTexSubImage2D( GL_TEXTURE_2D, miplevel, x, y, width, height, dataFormat, dataType, data ); - } - else - { - qglTexImage2D (GL_TEXTURE_2D, miplevel, internalFormat, width, height, 0, dataFormat, dataType, data ); - } - } - } -} - - -/* -=============== -Upload32 - -=============== -*/ -extern qboolean charSet; -static void Upload32( byte *data, int width, int height, imgType_t type, imgFlags_t flags, - qboolean lightMap, GLenum internalFormat, int *pUploadWidth, int *pUploadHeight) -{ - byte *scaledBuffer = NULL; - byte *resampledBuffer = NULL; - int scaled_width, scaled_height; - int i, c; - byte *scan; - - RawImage_ScaleToPower2(&data, &width, &height, &scaled_width, &scaled_height, type, flags, &resampledBuffer); - - scaledBuffer = ri.Hunk_AllocateTempMemory( sizeof( unsigned ) * scaled_width * scaled_height ); - - // - // scan the texture for each channel's max values - // and verify if the alpha channel is being used or not - // - c = width*height; - scan = data; - - if( r_greyscale->integer ) - { - for ( i = 0; i < c; i++ ) - { - byte luma = LUMA(scan[i*4], scan[i*4 + 1], scan[i*4 + 2]); - scan[i*4] = luma; - scan[i*4 + 1] = luma; - scan[i*4 + 2] = luma; - } - } - else if( r_greyscale->value ) - { - for ( i = 0; i < c; i++ ) - { - float luma = LUMA(scan[i*4], scan[i*4 + 1], scan[i*4 + 2]); - scan[i*4] = LERP(scan[i*4], luma, r_greyscale->value); - scan[i*4 + 1] = LERP(scan[i*4 + 1], luma, r_greyscale->value); - scan[i*4 + 2] = LERP(scan[i*4 + 2], luma, r_greyscale->value); - } - } - - // Convert to RGB if sRGB textures aren't supported in hardware - if (!glRefConfig.texture_srgb && (flags & IMGFLAG_SRGB)) - { - byte *in = data; - int c = width * height; - while (c--) - { - for (i = 0; i < 3; i++) - { - float x = ByteToFloat(in[i]); - x = sRGBtoRGB(x); - in[i] = FloatToByte(x); - } - in += 4; - } - - // FIXME: Probably should mark the image as non-sRGB as well - flags &= ~IMGFLAG_SRGB; - } - - // normals are always swizzled - if (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT) - { - RawImage_SwizzleRA(data, width, height); - } - - // LATC2 is only used for normals - if (internalFormat == GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT) - { - byte *in = data; - int c = width * height; - while (c--) - { - in[0] = in[1]; - in[2] = in[1]; - in += 4; - } - } - - // copy or resample data as appropriate for first MIP level - if ( ( scaled_width == width ) && - ( scaled_height == height ) ) { - if (!(flags & IMGFLAG_MIPMAP)) - { - RawImage_UploadTexture( data, 0, 0, scaled_width, scaled_height, internalFormat, type, flags, qfalse ); - //qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - *pUploadWidth = scaled_width; - *pUploadHeight = scaled_height; - - goto done; - } - Com_Memcpy (scaledBuffer, data, width*height*4); - } - else - { - // use the normal mip-mapping function to go down from here - while ( width > scaled_width || height > scaled_height ) { - - if (flags & IMGFLAG_SRGB) - { - R_MipMapsRGB( (byte *)data, width, height ); - } - else - { - R_MipMap( (byte *)data, width, height ); - } - - width >>= 1; - height >>= 1; - if ( width < 1 ) { - width = 1; - } - if ( height < 1 ) { - height = 1; - } - } - Com_Memcpy( scaledBuffer, data, width * height * 4 ); - } - - if (!(flags & IMGFLAG_NOLIGHTSCALE)) - R_LightScaleTexture (scaledBuffer, scaled_width, scaled_height, !(flags & IMGFLAG_MIPMAP) ); - - *pUploadWidth = scaled_width; - *pUploadHeight = scaled_height; - - RawImage_UploadTexture(scaledBuffer, 0, 0, scaled_width, scaled_height, internalFormat, type, flags, qfalse); - -done: - - if (flags & IMGFLAG_MIPMAP) - { - if ( textureFilterAnisotropic ) - qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, - (GLint)Com_Clamp( 1, maxAnisotropy, r_ext_max_anisotropy->integer ) ); - - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); - } - else - { - if ( textureFilterAnisotropic ) - qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 ); - - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - } - - GL_CheckErrors(); - - if ( scaledBuffer != 0 ) - ri.Hunk_FreeTempMemory( scaledBuffer ); - if ( resampledBuffer != 0 ) - ri.Hunk_FreeTempMemory( resampledBuffer ); -} - - -static void EmptyTexture( int width, int height, imgType_t type, imgFlags_t flags, - qboolean lightMap, GLenum internalFormat, int *pUploadWidth, int *pUploadHeight ) -{ - int scaled_width, scaled_height; - - RawImage_ScaleToPower2(NULL, &width, &height, &scaled_width, &scaled_height, type, flags, NULL); - - *pUploadWidth = scaled_width; - *pUploadHeight = scaled_height; - - RawImage_UploadTexture(NULL, 0, 0, scaled_width, scaled_height, internalFormat, type, flags, qfalse); - - if (flags & IMGFLAG_MIPMAP) - { - if ( textureFilterAnisotropic ) - qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, - (GLint)Com_Clamp( 1, maxAnisotropy, r_ext_max_anisotropy->integer ) ); - - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); - } - else - { - if ( textureFilterAnisotropic ) - qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 ); - - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - } - - // Fix for sampling depth buffer on old nVidia cards - // from http://www.idevgames.com/forums/thread-4141-post-34844.html#pid34844 - switch(internalFormat) - { - case GL_DEPTH_COMPONENT: - case GL_DEPTH_COMPONENT16_ARB: - case GL_DEPTH_COMPONENT24_ARB: - case GL_DEPTH_COMPONENT32_ARB: - qglTexParameterf(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE ); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); - break; - default: - break; - } - - GL_CheckErrors(); -} - - -/* -================ -R_CreateImage - -This is the only way any image_t are created -================ -*/ -image_t *R_CreateImage( const char *name, byte *pic, int width, int height, imgType_t type, imgFlags_t flags, int internalFormat ) { - image_t *image; - qboolean isLightmap = qfalse; - long hash; - int glWrapClampMode; - - if (strlen(name) >= MAX_QPATH ) { - ri.Error (ERR_DROP, "R_CreateImage: \"%s\" is too long", name); - } - if ( !strncmp( name, "*lightmap", 9 ) ) { - isLightmap = qtrue; - } - - if ( tr.numImages == MAX_DRAWIMAGES ) { - ri.Error( ERR_DROP, "R_CreateImage: MAX_DRAWIMAGES hit"); - } - - image = tr.images[tr.numImages] = ri.Hunk_Alloc( sizeof( image_t ), h_low ); - image->texnum = 1024 + tr.numImages; - tr.numImages++; - - image->type = type; - image->flags = flags; - - strcpy (image->imgName, name); - - image->width = width; - image->height = height; - if (flags & IMGFLAG_CLAMPTOEDGE) - glWrapClampMode = GL_CLAMP_TO_EDGE; - else - glWrapClampMode = GL_REPEAT; - - if (!internalFormat) - { - if (image->flags & IMGFLAG_CUBEMAP) - internalFormat = GL_RGBA8; - else - internalFormat = RawImage_GetFormat(pic, width * height, isLightmap, image->type, image->flags); - } - - image->internalFormat = internalFormat; - - - // lightmaps are always allocated on TMU 1 - if ( qglActiveTextureARB && isLightmap ) { - image->TMU = 1; - } else { - image->TMU = 0; - } - - if ( qglActiveTextureARB ) { - GL_SelectTexture( image->TMU ); - } - - if (image->flags & IMGFLAG_CUBEMAP) - { - GL_BindCubemap(image); - qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - qglTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); - qglTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); - qglTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); - qglTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); - qglTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); - qglTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); - - image->uploadWidth = width; - image->uploadHeight = height; - } - else - { - GL_Bind(image); - - if (pic) - { - Upload32( pic, image->width, image->height, image->type, image->flags, - isLightmap, image->internalFormat, &image->uploadWidth, - &image->uploadHeight ); - } - else - { - EmptyTexture(image->width, image->height, image->type, image->flags, - isLightmap, image->internalFormat, &image->uploadWidth, - &image->uploadHeight ); - } - - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrapClampMode ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrapClampMode ); - } - - GL_SelectTexture( 0 ); - - hash = generateHashValue(name); - image->next = hashTable[hash]; - hashTable[hash] = image; - - return image; -} - -void R_UpdateSubImage( image_t *image, byte *pic, int x, int y, int width, int height ) -{ - byte *scaledBuffer = NULL; - byte *resampledBuffer = NULL; - int scaled_width, scaled_height, scaled_x, scaled_y; - byte *data = pic; - - // normals are always swizzled - if (image->type == IMGTYPE_NORMAL || image->type == IMGTYPE_NORMALHEIGHT) - { - RawImage_SwizzleRA(pic, width, height); - } - - // LATC2 is only used for normals - if (image->internalFormat == GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT) - { - byte *in = data; - int c = width * height; - while (c--) - { - in[0] = in[1]; - in[2] = in[1]; - in += 4; - } - } - - - RawImage_ScaleToPower2(&pic, &width, &height, &scaled_width, &scaled_height, image->type, image->flags, &resampledBuffer); - - scaledBuffer = ri.Hunk_AllocateTempMemory( sizeof( unsigned ) * scaled_width * scaled_height ); - - if ( qglActiveTextureARB ) { - GL_SelectTexture( image->TMU ); - } - - GL_Bind(image); - - // copy or resample data as appropriate for first MIP level - if ( ( scaled_width == width ) && - ( scaled_height == height ) ) { - if (!(image->flags & IMGFLAG_MIPMAP)) - { - scaled_x = x * scaled_width / width; - scaled_y = y * scaled_height / height; - RawImage_UploadTexture( data, scaled_x, scaled_y, scaled_width, scaled_height, image->internalFormat, image->type, image->flags, qtrue ); - //qglTexSubImage2D( GL_TEXTURE_2D, 0, scaled_x, scaled_y, scaled_width, scaled_height, GL_RGBA, GL_UNSIGNED_BYTE, data ); - - GL_CheckErrors(); - goto done; - } - Com_Memcpy (scaledBuffer, data, width*height*4); - } - else - { - // use the normal mip-mapping function to go down from here - while ( width > scaled_width || height > scaled_height ) { - - if (image->flags & IMGFLAG_SRGB) - { - R_MipMapsRGB( (byte *)data, width, height ); - } - else - { - R_MipMap( (byte *)data, width, height ); - } - - width >>= 1; - height >>= 1; - x >>= 1; - y >>= 1; - if ( width < 1 ) { - width = 1; - } - if ( height < 1 ) { - height = 1; - } - } - Com_Memcpy( scaledBuffer, data, width * height * 4 ); - } - - if (!(image->flags & IMGFLAG_NOLIGHTSCALE)) - R_LightScaleTexture (scaledBuffer, scaled_width, scaled_height, !(image->flags & IMGFLAG_MIPMAP) ); - - scaled_x = x * scaled_width / width; - scaled_y = y * scaled_height / height; - RawImage_UploadTexture( (byte *)data, scaled_x, scaled_y, scaled_width, scaled_height, image->internalFormat, image->type, image->flags, qtrue ); - -done: - - GL_SelectTexture( 0 ); - - GL_CheckErrors(); - - if ( scaledBuffer != 0 ) - ri.Hunk_FreeTempMemory( scaledBuffer ); - if ( resampledBuffer != 0 ) - ri.Hunk_FreeTempMemory( resampledBuffer ); -} - -//=================================================================== - -typedef struct -{ - char *ext; - void (*ImageLoader)( const char *, unsigned char **, int *, int * ); -} imageExtToLoaderMap_t; - -// Note that the ordering indicates the order of preference used -// when there are multiple images of different formats available -static imageExtToLoaderMap_t imageLoaders[ ] = -{ - { "tga", R_LoadTGA }, - { "jpg", R_LoadJPG }, - { "jpeg", R_LoadJPG }, - { "png", R_LoadPNG }, - { "pcx", R_LoadPCX }, - { "bmp", R_LoadBMP } -}; - -static int numImageLoaders = ARRAY_LEN( imageLoaders ); - -/* -================= -R_LoadImage - -Loads any of the supported image types into a cannonical -32 bit format. -================= -*/ -void R_LoadImage( const char *name, byte **pic, int *width, int *height ) -{ - qboolean orgNameFailed = qfalse; - int orgLoader = -1; - int i; - char localName[ MAX_QPATH ]; - const char *ext; - char *altName; - - *pic = NULL; - *width = 0; - *height = 0; - - Q_strncpyz( localName, name, MAX_QPATH ); - - ext = COM_GetExtension( localName ); - - if( *ext ) - { - // Look for the correct loader and use it - for( i = 0; i < numImageLoaders; i++ ) - { - if( !Q_stricmp( ext, imageLoaders[ i ].ext ) ) - { - // Load - imageLoaders[ i ].ImageLoader( localName, pic, width, height ); - break; - } - } - - // A loader was found - if( i < numImageLoaders ) - { - if( *pic == NULL ) - { - // Loader failed, most likely because the file isn't there; - // try again without the extension - orgNameFailed = qtrue; - orgLoader = i; - COM_StripExtension( name, localName, MAX_QPATH ); - } - else - { - // Something loaded - return; - } - } - } - - // Try and find a suitable match using all - // the image formats supported - for( i = 0; i < numImageLoaders; i++ ) - { - if (i == orgLoader) - continue; - - altName = va( "%s.%s", localName, imageLoaders[ i ].ext ); - - // Load - imageLoaders[ i ].ImageLoader( altName, pic, width, height ); - - if( *pic ) - { - if( orgNameFailed ) - { - ri.Printf( PRINT_DEVELOPER, "WARNING: %s not present, using %s instead\n", - name, altName ); - } - - break; - } - } -} - - -/* -=============== -R_FindImageFile - -Finds or loads the given image. -Returns NULL if it fails, not a default image. -============== -*/ -image_t *R_FindImageFile( const char *name, imgType_t type, imgFlags_t flags ) -{ - image_t *image; - int width, height; - byte *pic; - long hash; - - if (!name) { - return NULL; - } - - hash = generateHashValue(name); - - // - // see if the image is already loaded - // - for (image=hashTable[hash]; image; image=image->next) { - if ( !strcmp( name, image->imgName ) ) { - // the white image can be used with any set of parms, but other mismatches are errors - if ( strcmp( name, "*white" ) ) { - if ( image->flags != flags ) { - ri.Printf( PRINT_DEVELOPER, "WARNING: reused image %s with mixed flags (%i vs %i)\n", name, image->flags, flags ); - } - } - return image; - } - } - - // - // load the pic from disk - // - R_LoadImage( name, &pic, &width, &height ); - if ( pic == NULL ) { - return NULL; - } - - if (r_normalMapping->integer && !(type == IMGTYPE_NORMAL) && (flags & IMGFLAG_PICMIP) && (flags & IMGFLAG_MIPMAP) && (flags & IMGFLAG_GENNORMALMAP)) - { - char normalName[MAX_QPATH]; - image_t *normalImage; - int normalWidth, normalHeight; - imgFlags_t normalFlags; - - normalFlags = (flags & ~(IMGFLAG_GENNORMALMAP | IMGFLAG_SRGB)) | IMGFLAG_NOLIGHTSCALE; - - COM_StripExtension(name, normalName, MAX_QPATH); - Q_strcat(normalName, MAX_QPATH, "_n"); - - // find normalmap in case it's there - normalImage = R_FindImageFile(normalName, IMGTYPE_NORMAL, normalFlags); - - // if not, generate it - if (normalImage == NULL) - { - byte *normalPic; - int x, y; - - normalWidth = width; - normalHeight = height; - normalPic = ri.Malloc(width * height * 4); - RGBAtoNormal(pic, normalPic, width, height, flags & IMGFLAG_CLAMPTOEDGE); - - // Brighten up the original image to work with the normal map - RGBAtoYCoCgA(pic, pic, width, height); - for (y = 0; y < height; y++) - { - byte *picbyte = pic + y * width * 4; - byte *normbyte = normalPic + y * width * 4; - for (x = 0; x < width; x++) - { - int div = MAX(normbyte[2] - 127, 16); - picbyte[0] = CLAMP(picbyte[0] * 128 / div, 0, 255); - picbyte += 4; - normbyte += 4; - } - } - YCoCgAtoRGBA(pic, pic, width, height); - - R_CreateImage( normalName, normalPic, normalWidth, normalHeight, IMGTYPE_NORMAL, normalFlags, 0 ); - ri.Free( normalPic ); - } - } - - image = R_CreateImage( ( char * ) name, pic, width, height, type, flags, 0 ); - ri.Free( pic ); - return image; -} - - -/* -================ -R_CreateDlightImage -================ -*/ -#define DLIGHT_SIZE 16 -static void R_CreateDlightImage( void ) { - int x,y; - byte data[DLIGHT_SIZE][DLIGHT_SIZE][4]; - int b; - - // make a centered inverse-square falloff blob for dynamic lighting - for (x=0 ; x 255) { - b = 255; - } else if ( b < 75 ) { - b = 0; - } - data[y][x][0] = - data[y][x][1] = - data[y][x][2] = b; - data[y][x][3] = 255; - } - } - tr.dlightImage = R_CreateImage("*dlight", (byte *)data, DLIGHT_SIZE, DLIGHT_SIZE, IMGTYPE_COLORALPHA, IMGFLAG_CLAMPTOEDGE, 0 ); -} - - -/* -================= -R_InitFogTable -================= -*/ -void R_InitFogTable( void ) { - int i; - float d; - float exp; - - exp = 0.5; - - for ( i = 0 ; i < FOG_TABLE_SIZE ; i++ ) { - d = pow ( (float)i/(FOG_TABLE_SIZE-1), exp ); - - tr.fogTable[i] = d; - } -} - -/* -================ -R_FogFactor - -Returns a 0.0 to 1.0 fog density value -This is called for each texel of the fog texture on startup -and for each vertex of transparent shaders in fog dynamically -================ -*/ -float R_FogFactor( float s, float t ) { - float d; - - s -= 1.0/512; - if ( s < 0 ) { - return 0; - } - if ( t < 1.0/32 ) { - return 0; - } - if ( t < 31.0/32 ) { - s *= (t - 1.0f/32.0f) / (30.0f/32.0f); - } - - // we need to leave a lot of clamp range - s *= 8; - - if ( s > 1.0 ) { - s = 1.0; - } - - d = tr.fogTable[ (int)(s * (FOG_TABLE_SIZE-1)) ]; - - return d; -} - -/* -================ -R_CreateFogImage -================ -*/ -#define FOG_S 256 -#define FOG_T 32 -static void R_CreateFogImage( void ) { - int x,y; - byte *data; - float d; - float borderColor[4]; - - data = ri.Hunk_AllocateTempMemory( FOG_S * FOG_T * 4 ); - - // S is distance, T is depth - for (x=0 ; xinteger >= 2) - { - for( x = 0; x < MAX_DLIGHTS; x++) - { - tr.shadowCubemaps[x] = R_CreateImage(va("*shadowcubemap%i", x), (byte *)data, DEFAULT_SIZE, DEFAULT_SIZE, IMGTYPE_COLORALPHA, IMGFLAG_CLAMPTOEDGE | IMGFLAG_CUBEMAP, 0); - } - } - - // with overbright bits active, we need an image which is some fraction of full color, - // for default lightmaps, etc - for (x=0 ; xinteger && glRefConfig.framebufferObject && glRefConfig.textureFloat) - hdrFormat = GL_RGB16F_ARB; - - tr.renderImage = R_CreateImage("_render", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, hdrFormat); - - if (r_drawSunRays->integer) - tr.sunRaysImage = R_CreateImage("*sunRays", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_RGBA8); - - if (r_softOverbright->integer) - { - int format; - - format = GL_RGBA8; - - tr.screenScratchImage = R_CreateImage("*screenScratch", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, format); - } - - if (glRefConfig.framebufferObject) - { - tr.renderDepthImage = R_CreateImage("*renderdepth", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_DEPTH_COMPONENT24_ARB); - tr.textureDepthImage = R_CreateImage("*texturedepth", NULL, PSHADOW_MAP_SIZE, PSHADOW_MAP_SIZE, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_DEPTH_COMPONENT24_ARB); - } - - { - unsigned short sdata[4]; - void *p; - - if (hdrFormat == GL_RGB16F_ARB) - { - sdata[0] = FloatToHalf(0.0f); - sdata[1] = FloatToHalf(0.45f); - sdata[2] = FloatToHalf(1.0f); - sdata[3] = FloatToHalf(1.0f); - p = &sdata[0]; - } - else - { - data[0][0][0] = 0; - data[0][0][1] = 0.45f * 255; - data[0][0][2] = 255; - data[0][0][3] = 255; - p = data; - } - - tr.calcLevelsImage = R_CreateImage("*calcLevels", p, 1, 1, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, hdrFormat); - tr.targetLevelsImage = R_CreateImage("*targetLevels", p, 1, 1, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, hdrFormat); - tr.fixedLevelsImage = R_CreateImage("*fixedLevels", p, 1, 1, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, hdrFormat); - } - - for (x = 0; x < 2; x++) - { - tr.textureScratchImage[x] = R_CreateImage(va("*textureScratch%d", x), NULL, 256, 256, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_RGBA8); - } - for (x = 0; x < 2; x++) - { - tr.quarterImage[x] = R_CreateImage(va("*quarter%d", x), NULL, width / 2, height / 2, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_RGBA8); - } - - tr.screenShadowImage = R_CreateImage("*screenShadow", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_RGBA8); - - if (r_ssao->integer) - { - tr.screenSsaoImage = R_CreateImage("*screenSsao", NULL, width / 2, height / 2, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_RGBA8); - tr.hdrDepthImage = R_CreateImage("*hdrDepth", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_INTENSITY32F_ARB); - } - - for( x = 0; x < MAX_DRAWN_PSHADOWS; x++) - { - tr.pshadowMaps[x] = R_CreateImage(va("*shadowmap%i", x), NULL, PSHADOW_MAP_SIZE, PSHADOW_MAP_SIZE, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_RGBA8); - } - - for ( x = 0; x < 3; x++) - { - tr.sunShadowDepthImage[x] = R_CreateImage(va("*sunshadowdepth%i", x), NULL, r_shadowMapSize->integer, r_shadowMapSize->integer, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_DEPTH_COMPONENT24_ARB); - } - } -} - - -/* -=============== -R_SetColorMappings -=============== -*/ -void R_SetColorMappings( void ) { - int i, j; - float g; - int inf; - int shift; - - // setup the overbright lighting - tr.overbrightBits = r_overBrightBits->integer; - if ( !glConfig.deviceSupportsGamma ) { - tr.overbrightBits = 0; // need hardware gamma for overbright - } - - // never overbright in windowed mode without soft overbright - if ( !glConfig.isFullscreen && !r_softOverbright->integer ) - { - tr.overbrightBits = 0; - } - - // never overbright with tonemapping - if ( r_toneMap->integer && r_hdr->integer ) - { - tr.overbrightBits = 0; - } - - // allow 2 overbright bits in 24 bit, but only 1 in 16 bit - if ( glConfig.colorBits > 16 ) { - if ( tr.overbrightBits > 2 ) { - tr.overbrightBits = 2; - } - } else { - if ( tr.overbrightBits > 1 ) { - tr.overbrightBits = 1; - } - } - if ( tr.overbrightBits < 0 ) { - tr.overbrightBits = 0; - } - - tr.identityLight = 1.0f / ( 1 << tr.overbrightBits ); - tr.identityLightByte = 255 * tr.identityLight; - - - if ( r_intensity->value <= 1 ) { - ri.Cvar_Set( "r_intensity", "1" ); - } - - if ( r_gamma->value < 0.5f ) { - ri.Cvar_Set( "r_gamma", "0.5" ); - } else if ( r_gamma->value > 3.0f ) { - ri.Cvar_Set( "r_gamma", "3.0" ); - } - - g = r_gamma->value; - - shift = tr.overbrightBits; - - // no shift with soft overbright - if (r_softOverbright->integer) - { - shift = 0; - } - - for ( i = 0; i < 256; i++ ) { - int i2; - - if (r_srgb->integer) - { - i2 = 255 * RGBtosRGB(i/255.0f) + 0.5f; - } - else - { - i2 = i; - } - - if ( g == 1 ) { - inf = i2; - } else { - inf = 255 * pow ( i2/255.0f, 1.0f / g ) + 0.5f; - } - inf <<= shift; - if (inf < 0) { - inf = 0; - } - if (inf > 255) { - inf = 255; - } - s_gammatable[i] = inf; - } - - for (i=0 ; i<256 ; i++) { - j = i * r_intensity->value; - if (j > 255) { - j = 255; - } - s_intensitytable[i] = j; - } - - if ( glConfig.deviceSupportsGamma ) - { - GLimp_SetGamma( s_gammatable, s_gammatable, s_gammatable ); - } -} - -/* -=============== -R_InitImages -=============== -*/ -void R_InitImages( void ) { - Com_Memset(hashTable, 0, sizeof(hashTable)); - // build brightness translation tables - R_SetColorMappings(); - - // create default texture and white texture - R_CreateBuiltinImages(); -} - -/* -=============== -R_DeleteTextures -=============== -*/ -void R_DeleteTextures( void ) { - int i; - - for ( i=0; itexnum ); - } - Com_Memset( tr.images, 0, sizeof( tr.images ) ); - - tr.numImages = 0; - - Com_Memset( glState.currenttextures, 0, sizeof( glState.currenttextures ) ); - if ( qglActiveTextureARB ) { - GL_SelectTexture( 1 ); - qglBindTexture( GL_TEXTURE_2D, 0 ); - GL_SelectTexture( 0 ); - qglBindTexture( GL_TEXTURE_2D, 0 ); - } else { - qglBindTexture( GL_TEXTURE_2D, 0 ); - } -} - -/* -============================================================================ - -SKINS - -============================================================================ -*/ - -/* -================== -CommaParse - -This is unfortunate, but the skin files aren't -compatable with our normal parsing rules. -================== -*/ -static char *CommaParse( char **data_p ) { - int c = 0, len; - char *data; - static char com_token[MAX_TOKEN_CHARS]; - - data = *data_p; - len = 0; - com_token[0] = 0; - - // make sure incoming data is valid - if ( !data ) { - *data_p = NULL; - return com_token; - } - - while ( 1 ) { - // skip whitespace - while( (c = *data) <= ' ') { - if( !c ) { - break; - } - data++; - } - - - c = *data; - - // skip double slash comments - if ( c == '/' && data[1] == '/' ) - { - while (*data && *data != '\n') - data++; - } - // skip /* */ comments - else if ( c=='/' && data[1] == '*' ) - { - while ( *data && ( *data != '*' || data[1] != '/' ) ) - { - data++; - } - if ( *data ) - { - data += 2; - } - } - else - { - break; - } - } - - if ( c == 0 ) { - return ""; - } - - // handle quoted strings - if (c == '\"') - { - data++; - while (1) - { - c = *data++; - if (c=='\"' || !c) - { - com_token[len] = 0; - *data_p = ( char * ) data; - return com_token; - } - if (len < MAX_TOKEN_CHARS) - { - com_token[len] = c; - len++; - } - } - } - - // parse a regular word - do - { - if (len < MAX_TOKEN_CHARS) - { - com_token[len] = c; - len++; - } - data++; - c = *data; - } while (c>32 && c != ',' ); - - if (len == MAX_TOKEN_CHARS) - { -// ri.Printf (PRINT_DEVELOPER, "Token exceeded %i chars, discarded.\n", MAX_TOKEN_CHARS); - len = 0; - } - com_token[len] = 0; - - *data_p = ( char * ) data; - return com_token; -} - - -/* -=============== -RE_RegisterSkin - -=============== -*/ -qhandle_t RE_RegisterSkin( const char *name ) { - qhandle_t hSkin; - skin_t *skin; - skinSurface_t *surf; - union { - char *c; - void *v; - } text; - char *text_p; - char *token; - char surfName[MAX_QPATH]; - - if ( !name || !name[0] ) { - ri.Printf( PRINT_DEVELOPER, "Empty name passed to RE_RegisterSkin\n" ); - return 0; - } - - if ( strlen( name ) >= MAX_QPATH ) { - ri.Printf( PRINT_DEVELOPER, "Skin name exceeds MAX_QPATH\n" ); - return 0; - } - - - // see if the skin is already loaded - for ( hSkin = 1; hSkin < tr.numSkins ; hSkin++ ) { - skin = tr.skins[hSkin]; - if ( !Q_stricmp( skin->name, name ) ) { - if( skin->numSurfaces == 0 ) { - return 0; // default skin - } - return hSkin; - } - } - - // allocate a new skin - if ( tr.numSkins == MAX_SKINS ) { - ri.Printf( PRINT_WARNING, "WARNING: RE_RegisterSkin( '%s' ) MAX_SKINS hit\n", name ); - return 0; - } - tr.numSkins++; - skin = ri.Hunk_Alloc( sizeof( skin_t ), h_low ); - tr.skins[hSkin] = skin; - Q_strncpyz( skin->name, name, sizeof( skin->name ) ); - skin->numSurfaces = 0; - - R_IssuePendingRenderCommands(); - - // If not a .skin file, load as a single shader - if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) { - skin->numSurfaces = 1; - skin->surfaces[0] = ri.Hunk_Alloc( sizeof(skin->surfaces[0]), h_low ); - skin->surfaces[0]->shader = R_FindShader( name, LIGHTMAP_NONE, qtrue ); - return hSkin; - } - - // load and parse the skin file - ri.FS_ReadFile( name, &text.v ); - if ( !text.c ) { - return 0; - } - - text_p = text.c; - while ( text_p && *text_p ) { - // get surface name - token = CommaParse( &text_p ); - Q_strncpyz( surfName, token, sizeof( surfName ) ); - - if ( !token[0] ) { - break; - } - // lowercase the surface name so skin compares are faster - Q_strlwr( surfName ); - - if ( *text_p == ',' ) { - text_p++; - } - - if ( strstr( token, "tag_" ) ) { - continue; - } - - // parse the shader name - token = CommaParse( &text_p ); - - surf = skin->surfaces[ skin->numSurfaces ] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); - Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); - surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue ); - skin->numSurfaces++; - } - - ri.FS_FreeFile( text.v ); - - - // never let a skin have 0 shaders - if ( skin->numSurfaces == 0 ) { - return 0; // use default skin - } - - return hSkin; -} - - -/* -=============== -R_InitSkins -=============== -*/ -void R_InitSkins( void ) { - skin_t *skin; - - tr.numSkins = 1; - - // make the default skin have all default shaders - skin = tr.skins[0] = ri.Hunk_Alloc( sizeof( skin_t ), h_low ); - Q_strncpyz( skin->name, "", sizeof( skin->name ) ); - skin->numSurfaces = 1; - skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces ), h_low ); - skin->surfaces[0]->shader = tr.defaultShader; -} - -/* -=============== -R_GetSkinByHandle -=============== -*/ -skin_t *R_GetSkinByHandle( qhandle_t hSkin ) { - if ( hSkin < 1 || hSkin >= tr.numSkins ) { - return tr.skins[0]; - } - return tr.skins[ hSkin ]; -} - -/* -=============== -R_SkinList_f -=============== -*/ -void R_SkinList_f( void ) { - int i, j; - skin_t *skin; - - ri.Printf (PRINT_ALL, "------------------\n"); - - for ( i = 0 ; i < tr.numSkins ; i++ ) { - skin = tr.skins[i]; - - ri.Printf( PRINT_ALL, "%3i:%s\n", i, skin->name ); - for ( j = 0 ; j < skin->numSurfaces ; j++ ) { - ri.Printf( PRINT_ALL, " %s = %s\n", - skin->surfaces[j]->name, skin->surfaces[j]->shader->name ); - } - } - ri.Printf (PRINT_ALL, "------------------\n"); -} - - diff --git a/src/rend2/tr_image_bmp.c b/src/rend2/tr_image_bmp.c deleted file mode 100644 index 707bee3b..00000000 --- a/src/rend2/tr_image_bmp.c +++ /dev/null @@ -1,243 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "../qcommon/q_shared.h" -#include "../qcommon/qfiles.h" -#include "../qcommon/qcommon.h" -#include "../renderer/tr_public.h" -extern refimport_t ri; - -typedef struct -{ - char id[2]; - unsigned fileSize; - unsigned reserved0; - unsigned bitmapDataOffset; - unsigned bitmapHeaderSize; - unsigned width; - unsigned height; - unsigned short planes; - unsigned short bitsPerPixel; - unsigned compression; - unsigned bitmapDataSize; - unsigned hRes; - unsigned vRes; - unsigned colors; - unsigned importantColors; - unsigned char palette[256][4]; -} BMPHeader_t; - -void R_LoadBMP( const char *name, byte **pic, int *width, int *height ) -{ - int columns, rows; - unsigned numPixels; - byte *pixbuf; - int row, column; - byte *buf_p; - byte *end; - union { - byte *b; - void *v; - } buffer; - int length; - BMPHeader_t bmpHeader; - byte *bmpRGBA; - - *pic = NULL; - - if(width) - *width = 0; - - if(height) - *height = 0; - - // - // load the file - // - length = ri.FS_ReadFile( ( char * ) name, &buffer.v); - if (!buffer.b || length < 0) { - return; - } - - if (length < 54) - { - ri.Error( ERR_DROP, "LoadBMP: header too short (%s)", name ); - } - - buf_p = buffer.b; - end = buffer.b + length; - - bmpHeader.id[0] = *buf_p++; - bmpHeader.id[1] = *buf_p++; - bmpHeader.fileSize = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.reserved0 = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.bitmapDataOffset = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.bitmapHeaderSize = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.width = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.height = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.planes = LittleShort( * ( short * ) buf_p ); - buf_p += 2; - bmpHeader.bitsPerPixel = LittleShort( * ( short * ) buf_p ); - buf_p += 2; - bmpHeader.compression = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.bitmapDataSize = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.hRes = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.vRes = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.colors = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.importantColors = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - - if ( bmpHeader.bitsPerPixel == 8 ) - { - if (buf_p + sizeof(bmpHeader.palette) > end) - ri.Error( ERR_DROP, "LoadBMP: header too short (%s)", name ); - - Com_Memcpy( bmpHeader.palette, buf_p, sizeof( bmpHeader.palette ) ); - buf_p += sizeof(bmpHeader.palette); - } - - if (buffer.b + bmpHeader.bitmapDataOffset > end) - { - ri.Error( ERR_DROP, "LoadBMP: invalid offset value in header (%s)", name ); - } - - buf_p = buffer.b + bmpHeader.bitmapDataOffset; - - if ( bmpHeader.id[0] != 'B' && bmpHeader.id[1] != 'M' ) - { - ri.Error( ERR_DROP, "LoadBMP: only Windows-style BMP files supported (%s)", name ); - } - if ( bmpHeader.fileSize != length ) - { - ri.Error( ERR_DROP, "LoadBMP: header size does not match file size (%u vs. %u) (%s)", bmpHeader.fileSize, length, name ); - } - if ( bmpHeader.compression != 0 ) - { - ri.Error( ERR_DROP, "LoadBMP: only uncompressed BMP files supported (%s)", name ); - } - if ( bmpHeader.bitsPerPixel < 8 ) - { - ri.Error( ERR_DROP, "LoadBMP: monochrome and 4-bit BMP files not supported (%s)", name ); - } - - switch ( bmpHeader.bitsPerPixel ) - { - case 8: - case 16: - case 24: - case 32: - break; - default: - ri.Error( ERR_DROP, "LoadBMP: illegal pixel_size '%hu' in file '%s'", bmpHeader.bitsPerPixel, name ); - break; - } - - columns = bmpHeader.width; - rows = bmpHeader.height; - if ( rows < 0 ) - rows = -rows; - numPixels = columns * rows; - - if(columns <= 0 || !rows || numPixels > 0x1FFFFFFF // 4*1FFFFFFF == 0x7FFFFFFC < 0x7FFFFFFF - || ((numPixels * 4) / columns) / 4 != rows) - { - ri.Error (ERR_DROP, "LoadBMP: %s has an invalid image size", name); - } - if(buf_p + numPixels*bmpHeader.bitsPerPixel/8 > end) - { - ri.Error (ERR_DROP, "LoadBMP: file truncated (%s)", name); - } - - if ( width ) - *width = columns; - if ( height ) - *height = rows; - - bmpRGBA = ri.Malloc( numPixels * 4 ); - *pic = bmpRGBA; - - - for ( row = rows-1; row >= 0; row-- ) - { - pixbuf = bmpRGBA + row*columns*4; - - for ( column = 0; column < columns; column++ ) - { - unsigned char red, green, blue, alpha; - int palIndex; - unsigned short shortPixel; - - switch ( bmpHeader.bitsPerPixel ) - { - case 8: - palIndex = *buf_p++; - *pixbuf++ = bmpHeader.palette[palIndex][2]; - *pixbuf++ = bmpHeader.palette[palIndex][1]; - *pixbuf++ = bmpHeader.palette[palIndex][0]; - *pixbuf++ = 0xff; - break; - case 16: - shortPixel = * ( unsigned short * ) pixbuf; - pixbuf += 2; - *pixbuf++ = ( shortPixel & ( 31 << 10 ) ) >> 7; - *pixbuf++ = ( shortPixel & ( 31 << 5 ) ) >> 2; - *pixbuf++ = ( shortPixel & ( 31 ) ) << 3; - *pixbuf++ = 0xff; - break; - - case 24: - blue = *buf_p++; - green = *buf_p++; - red = *buf_p++; - *pixbuf++ = red; - *pixbuf++ = green; - *pixbuf++ = blue; - *pixbuf++ = 255; - break; - case 32: - blue = *buf_p++; - green = *buf_p++; - red = *buf_p++; - alpha = *buf_p++; - *pixbuf++ = red; - *pixbuf++ = green; - *pixbuf++ = blue; - *pixbuf++ = alpha; - break; - } - } - } - - ri.FS_FreeFile( buffer.v ); - -} diff --git a/src/rend2/tr_image_jpg.c b/src/rend2/tr_image_jpg.c deleted file mode 100644 index 494b4a28..00000000 --- a/src/rend2/tr_image_jpg.c +++ /dev/null @@ -1,441 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "../qcommon/q_shared.h" -#include "../qcommon/qfiles.h" -#include "../qcommon/qcommon.h" -#include "../renderer/tr_public.h" -extern refimport_t ri; - -/* - * Include file for users of JPEG library. - * You will need to have included system headers that define at least - * the typedefs FILE and size_t before you can include jpeglib.h. - * (stdio.h is sufficient on ANSI-conforming systems.) - * You may also wish to include "jerror.h". - */ - -#ifdef USE_INTERNAL_JPEG -# define JPEG_INTERNALS -#endif - -#include - -#ifndef USE_INTERNAL_JPEG -# if JPEG_LIB_VERSION < 80 -# error Need system libjpeg >= 80 -# endif -#endif - -static void R_JPGErrorExit(j_common_ptr cinfo) -{ - char buffer[JMSG_LENGTH_MAX]; - - (*cinfo->err->format_message) (cinfo, buffer); - - /* Let the memory manager delete any temp files before we die */ - jpeg_destroy(cinfo); - - ri.Error(ERR_FATAL, "%s", buffer); -} - -static void R_JPGOutputMessage(j_common_ptr cinfo) -{ - char buffer[JMSG_LENGTH_MAX]; - - /* Create the message */ - (*cinfo->err->format_message) (cinfo, buffer); - - /* Send it to stderr, adding a newline */ - ri.Printf(PRINT_ALL, "%s\n", buffer); -} - -void R_LoadJPG(const char *filename, unsigned char **pic, int *width, int *height) -{ - /* This struct contains the JPEG decompression parameters and pointers to - * working space (which is allocated as needed by the JPEG library). - */ - struct jpeg_decompress_struct cinfo = {NULL}; - /* We use our private extension JPEG error handler. - * Note that this struct must live as long as the main JPEG parameter - * struct, to avoid dangling-pointer problems. - */ - /* This struct represents a JPEG error handler. It is declared separately - * because applications often want to supply a specialized error handler - * (see the second half of this file for an example). But here we just - * take the easy way out and use the standard error handler, which will - * print a message on stderr and call exit() if compression fails. - * Note that this struct must live as long as the main JPEG parameter - * struct, to avoid dangling-pointer problems. - */ - struct jpeg_error_mgr jerr; - /* More stuff */ - JSAMPARRAY buffer; /* Output row buffer */ - unsigned int row_stride; /* physical row width in output buffer */ - unsigned int pixelcount, memcount; - unsigned int sindex, dindex; - byte *out; - int len; - union { - byte *b; - void *v; - } fbuffer; - byte *buf; - - /* In this example we want to open the input file before doing anything else, - * so that the setjmp() error recovery below can assume the file is open. - * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that - * requires it in order to read binary files. - */ - - len = ri.FS_ReadFile ( ( char * ) filename, &fbuffer.v); - if (!fbuffer.b || len < 0) { - return; - } - - /* Step 1: allocate and initialize JPEG decompression object */ - - /* We have to set up the error handler first, in case the initialization - * step fails. (Unlikely, but it could happen if you are out of memory.) - * This routine fills in the contents of struct jerr, and returns jerr's - * address which we place into the link field in cinfo. - */ - cinfo.err = jpeg_std_error(&jerr); - cinfo.err->error_exit = R_JPGErrorExit; - cinfo.err->output_message = R_JPGOutputMessage; - - /* Now we can initialize the JPEG decompression object. */ - jpeg_create_decompress(&cinfo); - - /* Step 2: specify data source (eg, a file) */ - - jpeg_mem_src(&cinfo, fbuffer.b, len); - - /* Step 3: read file parameters with jpeg_read_header() */ - - (void) jpeg_read_header(&cinfo, TRUE); - /* We can ignore the return value from jpeg_read_header since - * (a) suspension is not possible with the stdio data source, and - * (b) we passed TRUE to reject a tables-only JPEG file as an error. - * See libjpeg.doc for more info. - */ - - /* Step 4: set parameters for decompression */ - - /* - * Make sure it always converts images to RGB color space. This will - * automatically convert 8-bit greyscale images to RGB as well. - */ - cinfo.out_color_space = JCS_RGB; - - /* Step 5: Start decompressor */ - - (void) jpeg_start_decompress(&cinfo); - /* We can ignore the return value since suspension is not possible - * with the stdio data source. - */ - - /* We may need to do some setup of our own at this point before reading - * the data. After jpeg_start_decompress() we have the correct scaled - * output image dimensions available, as well as the output colormap - * if we asked for color quantization. - * In this example, we need to make an output work buffer of the right size. - */ - /* JSAMPLEs per row in output buffer */ - - pixelcount = cinfo.output_width * cinfo.output_height; - - if(!cinfo.output_width || !cinfo.output_height - || ((pixelcount * 4) / cinfo.output_width) / 4 != cinfo.output_height - || pixelcount > 0x1FFFFFFF || cinfo.output_components != 3 - ) - { - // Free the memory to make sure we don't leak memory - ri.FS_FreeFile (fbuffer.v); - jpeg_destroy_decompress(&cinfo); - - ri.Error(ERR_DROP, "LoadJPG: %s has an invalid image format: %dx%d*4=%d, components: %d", filename, - cinfo.output_width, cinfo.output_height, pixelcount * 4, cinfo.output_components); - } - - memcount = pixelcount * 4; - row_stride = cinfo.output_width * cinfo.output_components; - - out = ri.Malloc(memcount); - - *width = cinfo.output_width; - *height = cinfo.output_height; - - /* Step 6: while (scan lines remain to be read) */ - /* jpeg_read_scanlines(...); */ - - /* Here we use the library's state variable cinfo.output_scanline as the - * loop counter, so that we don't have to keep track ourselves. - */ - while (cinfo.output_scanline < cinfo.output_height) { - /* jpeg_read_scanlines expects an array of pointers to scanlines. - * Here the array is only one element long, but you could ask for - * more than one scanline at a time if that's more convenient. - */ - buf = ((out+(row_stride*cinfo.output_scanline))); - buffer = &buf; - (void) jpeg_read_scanlines(&cinfo, buffer, 1); - } - - buf = out; - - // Expand from RGB to RGBA - sindex = pixelcount * cinfo.output_components; - dindex = memcount; - - do - { - buf[--dindex] = 255; - buf[--dindex] = buf[--sindex]; - buf[--dindex] = buf[--sindex]; - buf[--dindex] = buf[--sindex]; - } while(sindex); - - *pic = out; - - /* Step 7: Finish decompression */ - - jpeg_finish_decompress(&cinfo); - /* We can ignore the return value since suspension is not possible - * with the stdio data source. - */ - - /* Step 8: Release JPEG decompression object */ - - /* This is an important step since it will release a good deal of memory. */ - jpeg_destroy_decompress(&cinfo); - - /* After finish_decompress, we can close the input file. - * Here we postpone it until after no more JPEG errors are possible, - * so as to simplify the setjmp error logic above. (Actually, I don't - * think that jpeg_destroy can do an error exit, but why assume anything...) - */ - ri.FS_FreeFile (fbuffer.v); - - /* At this point you may want to check to see whether any corrupt-data - * warnings occurred (test whether jerr.pub.num_warnings is nonzero). - */ - - /* And we're done! */ -} - - -/* Expanded data destination object for stdio output */ - -typedef struct { - struct jpeg_destination_mgr pub; /* public fields */ - - byte* outfile; /* target stream */ - int size; -} my_destination_mgr; - -typedef my_destination_mgr * my_dest_ptr; - - -/* - * Initialize destination --- called by jpeg_start_compress - * before any data is actually written. - */ - -static void -init_destination (j_compress_ptr cinfo) -{ - my_dest_ptr dest = (my_dest_ptr) cinfo->dest; - - dest->pub.next_output_byte = dest->outfile; - dest->pub.free_in_buffer = dest->size; -} - - -/* - * Empty the output buffer --- called whenever buffer fills up. - * - * In typical applications, this should write the entire output buffer - * (ignoring the current state of next_output_byte & free_in_buffer), - * reset the pointer & count to the start of the buffer, and return TRUE - * indicating that the buffer has been dumped. - * - * In applications that need to be able to suspend compression due to output - * overrun, a FALSE return indicates that the buffer cannot be emptied now. - * In this situation, the compressor will return to its caller (possibly with - * an indication that it has not accepted all the supplied scanlines). The - * application should resume compression after it has made more room in the - * output buffer. Note that there are substantial restrictions on the use of - * suspension --- see the documentation. - * - * When suspending, the compressor will back up to a convenient restart point - * (typically the start of the current MCU). next_output_byte & free_in_buffer - * indicate where the restart point will be if the current call returns FALSE. - * Data beyond this point will be regenerated after resumption, so do not - * write it out when emptying the buffer externally. - */ - -static boolean -empty_output_buffer (j_compress_ptr cinfo) -{ - my_dest_ptr dest = (my_dest_ptr) cinfo->dest; - - jpeg_destroy_compress(cinfo); - - // Make crash fatal or we would probably leak memory. - ri.Error(ERR_FATAL, "Output buffer for encoded JPEG image has insufficient size of %d bytes", - dest->size); - - return FALSE; -} - -/* - * Terminate destination --- called by jpeg_finish_compress - * after all data has been written. Usually needs to flush buffer. - * - * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding - * application must deal with any cleanup that should happen even - * for error exit. - */ - -static void term_destination(j_compress_ptr cinfo) -{ -} - - -/* - * Prepare for output to a stdio stream. - * The caller must have already opened the stream, and is responsible - * for closing it after finishing compression. - */ - -static void -jpegDest (j_compress_ptr cinfo, byte* outfile, int size) -{ - my_dest_ptr dest; - - /* The destination object is made permanent so that multiple JPEG images - * can be written to the same file without re-executing jpeg_stdio_dest. - * This makes it dangerous to use this manager and a different destination - * manager serially with the same JPEG object, because their private object - * sizes may be different. Caveat programmer. - */ - if (cinfo->dest == NULL) { /* first time for this JPEG object? */ - cinfo->dest = (struct jpeg_destination_mgr *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, - sizeof(my_destination_mgr)); - } - - dest = (my_dest_ptr) cinfo->dest; - dest->pub.init_destination = init_destination; - dest->pub.empty_output_buffer = empty_output_buffer; - dest->pub.term_destination = term_destination; - dest->outfile = outfile; - dest->size = size; -} - -/* -================= -SaveJPGToBuffer - -Encodes JPEG from image in image_buffer and writes to buffer. -Expects RGB input data -================= -*/ -size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality, - int image_width, int image_height, byte *image_buffer, int padding) -{ - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; - JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ - my_dest_ptr dest; - int row_stride; /* physical row width in image buffer */ - size_t outcount; - - /* Step 1: allocate and initialize JPEG compression object */ - cinfo.err = jpeg_std_error(&jerr); - cinfo.err->error_exit = R_JPGErrorExit; - cinfo.err->output_message = R_JPGOutputMessage; - - /* Now we can initialize the JPEG compression object. */ - jpeg_create_compress(&cinfo); - - /* Step 2: specify data destination (eg, a file) */ - /* Note: steps 2 and 3 can be done in either order. */ - jpegDest(&cinfo, buffer, bufSize); - - /* Step 3: set parameters for compression */ - cinfo.image_width = image_width; /* image width and height, in pixels */ - cinfo.image_height = image_height; - cinfo.input_components = 3; /* # of color components per pixel */ - cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ - - jpeg_set_defaults(&cinfo); - jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); - /* If quality is set high, disable chroma subsampling */ - if (quality >= 85) { - cinfo.comp_info[0].h_samp_factor = 1; - cinfo.comp_info[0].v_samp_factor = 1; - } - - /* Step 4: Start compressor */ - jpeg_start_compress(&cinfo, TRUE); - - /* Step 5: while (scan lines remain to be written) */ - /* jpeg_write_scanlines(...); */ - row_stride = image_width * cinfo.input_components + padding; /* JSAMPLEs per row in image_buffer */ - - while (cinfo.next_scanline < cinfo.image_height) { - /* jpeg_write_scanlines expects an array of pointers to scanlines. - * Here the array is only one element long, but you could pass - * more than one scanline at a time if that's more convenient. - */ - row_pointer[0] = &image_buffer[((cinfo.image_height-1)*row_stride)-cinfo.next_scanline * row_stride]; - (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); - } - - /* Step 6: Finish compression */ - jpeg_finish_compress(&cinfo); - - dest = (my_dest_ptr) cinfo.dest; - outcount = dest->size - dest->pub.free_in_buffer; - - /* Step 7: release JPEG compression object */ - jpeg_destroy_compress(&cinfo); - - /* And we're done! */ - return outcount; -} - -void RE_SaveJPG(char * filename, int quality, int image_width, int image_height, byte *image_buffer, int padding) -{ - byte *out; - size_t bufSize; - - bufSize = image_width * image_height * 3; - out = ri.Hunk_AllocateTempMemory(bufSize); - - bufSize = RE_SaveJPGToBuffer(out, bufSize, quality, image_width, image_height, image_buffer, padding); - ri.FS_WriteFile(filename, out, bufSize); - - ri.Hunk_FreeTempMemory(out); -} diff --git a/src/rend2/tr_image_pcx.c b/src/rend2/tr_image_pcx.c deleted file mode 100644 index d4dd575d..00000000 --- a/src/rend2/tr_image_pcx.c +++ /dev/null @@ -1,179 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - 2008 Ludwig Nussel - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "../qcommon/q_shared.h" -#include "../qcommon/qfiles.h" -#include "../qcommon/qcommon.h" -#include "../renderer/tr_public.h" -extern refimport_t ri; - -/* -======================================================================== - -PCX files are used for 8 bit images - -======================================================================== -*/ - -typedef struct { - char manufacturer; - char version; - char encoding; - char bits_per_pixel; - unsigned short xmin,ymin,xmax,ymax; - unsigned short hres,vres; - unsigned char palette[48]; - char reserved; - char color_planes; - unsigned short bytes_per_line; - unsigned short palette_type; - unsigned short hscreensize, vscreensize; - char filler[54]; - unsigned char data[]; -} pcx_t; - -void R_LoadPCX ( const char *filename, byte **pic, int *width, int *height) -{ - union { - byte *b; - void *v; - } raw; - byte *end; - pcx_t *pcx; - int len; - unsigned char dataByte = 0, runLength = 0; - byte *out, *pix; - unsigned short w, h; - byte *pic8; - byte *palette; - int i; - unsigned size = 0; - - if (width) - *width = 0; - if (height) - *height = 0; - *pic = NULL; - - // - // load the file - // - len = ri.FS_ReadFile( ( char * ) filename, &raw.v); - if (!raw.b || len < 0) { - return; - } - - if((unsigned)len < sizeof(pcx_t)) - { - ri.Printf (PRINT_ALL, "PCX truncated: %s\n", filename); - ri.FS_FreeFile (raw.v); - return; - } - - // - // parse the PCX file - // - pcx = (pcx_t *)raw.b; - end = raw.b+len; - - w = LittleShort(pcx->xmax)+1; - h = LittleShort(pcx->ymax)+1; - size = w*h; - - if (pcx->manufacturer != 0x0a - || pcx->version != 5 - || pcx->encoding != 1 - || pcx->color_planes != 1 - || pcx->bits_per_pixel != 8 - || w >= 1024 - || h >= 1024) - { - ri.Printf (PRINT_ALL, "Bad or unsupported pcx file %s (%dx%d@%d)\n", filename, w, h, pcx->bits_per_pixel); - return; - } - - pix = pic8 = ri.Malloc ( size ); - - raw.b = pcx->data; - // FIXME: should use bytes_per_line but original q3 didn't do that either - while(pix < pic8+size) - { - if(runLength > 0) { - *pix++ = dataByte; - --runLength; - continue; - } - - if(raw.b+1 > end) - break; - dataByte = *raw.b++; - - if((dataByte & 0xC0) == 0xC0) - { - if(raw.b+1 > end) - break; - runLength = dataByte & 0x3F; - dataByte = *raw.b++; - } - else - runLength = 1; - } - - if(pix < pic8+size) - { - ri.Printf (PRINT_ALL, "PCX file truncated: %s\n", filename); - ri.FS_FreeFile (pcx); - ri.Free (pic8); - } - - if (raw.b-(byte*)pcx >= end - (byte*)769 || end[-769] != 0x0c) - { - ri.Printf (PRINT_ALL, "PCX missing palette: %s\n", filename); - ri.FS_FreeFile (pcx); - ri.Free (pic8); - return; - } - - palette = end-768; - - pix = out = ri.Malloc(4 * size ); - for (i = 0 ; i < size ; i++) - { - unsigned char p = pic8[i]; - pix[0] = palette[p*3]; - pix[1] = palette[p*3 + 1]; - pix[2] = palette[p*3 + 2]; - pix[3] = 255; - pix += 4; - } - - if (width) - *width = w; - if (height) - *height = h; - - *pic = out; - - ri.FS_FreeFile (pcx); - ri.Free (pic8); -} diff --git a/src/rend2/tr_image_png.c b/src/rend2/tr_image_png.c deleted file mode 100644 index ee37aa75..00000000 --- a/src/rend2/tr_image_png.c +++ /dev/null @@ -1,2490 +0,0 @@ -/* -=========================================================================== -ioquake3 png decoder -Copyright (C) 2007,2008 Joerg Dietrich - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -=========================================================================== -*/ - -#include "../qcommon/q_shared.h" -#include "../qcommon/qfiles.h" -#include "../qcommon/qcommon.h" -#include "../renderer/tr_public.h" -extern refimport_t ri; - -#include "../qcommon/puff.h" - -// we could limit the png size to a lower value here -#ifndef INT_MAX -#define INT_MAX 0x1fffffff -#endif - -/* -================= -PNG LOADING -================= -*/ - -/* - * Quake 3 image format : RGBA - */ - -#define Q3IMAGE_BYTESPERPIXEL (4) - -/* - * PNG specifications - */ - -/* - * The first 8 Bytes of every PNG-File are a fixed signature - * to identify the file as a PNG. - */ - -#define PNG_Signature "\x89\x50\x4E\x47\xD\xA\x1A\xA" -#define PNG_Signature_Size (8) - -/* - * After the signature diverse chunks follow. - * A chunk consists of a header and if Length - * is bigger than 0 a body and a CRC of the body follow. - */ - -struct PNG_ChunkHeader -{ - uint32_t Length; - uint32_t Type; -}; - -#define PNG_ChunkHeader_Size (8) - -typedef uint32_t PNG_ChunkCRC; - -#define PNG_ChunkCRC_Size (4) - -/* - * We use the following ChunkTypes. - * All others are ignored. - */ - -#define MAKE_CHUNKTYPE(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | ((d))) - -#define PNG_ChunkType_IHDR MAKE_CHUNKTYPE('I', 'H', 'D', 'R') -#define PNG_ChunkType_PLTE MAKE_CHUNKTYPE('P', 'L', 'T', 'E') -#define PNG_ChunkType_IDAT MAKE_CHUNKTYPE('I', 'D', 'A', 'T') -#define PNG_ChunkType_IEND MAKE_CHUNKTYPE('I', 'E', 'N', 'D') -#define PNG_ChunkType_tRNS MAKE_CHUNKTYPE('t', 'R', 'N', 'S') - -/* - * Per specification the first chunk after the signature SHALL be IHDR. - */ - -struct PNG_Chunk_IHDR -{ - uint32_t Width; - uint32_t Height; - uint8_t BitDepth; - uint8_t ColourType; - uint8_t CompressionMethod; - uint8_t FilterMethod; - uint8_t InterlaceMethod; -}; - -#define PNG_Chunk_IHDR_Size (13) - -/* - * ColourTypes - */ - -#define PNG_ColourType_Grey (0) -#define PNG_ColourType_True (2) -#define PNG_ColourType_Indexed (3) -#define PNG_ColourType_GreyAlpha (4) -#define PNG_ColourType_TrueAlpha (6) - -/* - * number of colour components - * - * Grey : 1 grey - * True : 1 R, 1 G, 1 B - * Indexed : 1 index - * GreyAlpha : 1 grey, 1 alpha - * TrueAlpha : 1 R, 1 G, 1 B, 1 alpha - */ - -#define PNG_NumColourComponents_Grey (1) -#define PNG_NumColourComponents_True (3) -#define PNG_NumColourComponents_Indexed (1) -#define PNG_NumColourComponents_GreyAlpha (2) -#define PNG_NumColourComponents_TrueAlpha (4) - -/* - * For the different ColourTypes - * different BitDepths are specified. - */ - -#define PNG_BitDepth_1 ( 1) -#define PNG_BitDepth_2 ( 2) -#define PNG_BitDepth_4 ( 4) -#define PNG_BitDepth_8 ( 8) -#define PNG_BitDepth_16 (16) - -/* - * Only one valid CompressionMethod is standardized. - */ - -#define PNG_CompressionMethod_0 (0) - -/* - * Only one valid FilterMethod is currently standardized. - */ - -#define PNG_FilterMethod_0 (0) - -/* - * This FilterMethod defines 5 FilterTypes - */ - -#define PNG_FilterType_None (0) -#define PNG_FilterType_Sub (1) -#define PNG_FilterType_Up (2) -#define PNG_FilterType_Average (3) -#define PNG_FilterType_Paeth (4) - -/* - * Two InterlaceMethods are standardized : - * 0 - NonInterlaced - * 1 - Interlaced - */ - -#define PNG_InterlaceMethod_NonInterlaced (0) -#define PNG_InterlaceMethod_Interlaced (1) - -/* - * The Adam7 interlace method uses 7 passes. - */ - -#define PNG_Adam7_NumPasses (7) - -/* - * The compressed data starts with a header ... - */ - -struct PNG_ZlibHeader -{ - uint8_t CompressionMethod; - uint8_t Flags; -}; - -#define PNG_ZlibHeader_Size (2) - -/* - * ... and is followed by a check value - */ - -#define PNG_ZlibCheckValue_Size (4) - -/* - * Some support functions for buffered files follow. - */ - -/* - * buffered file representation - */ - -struct BufferedFile -{ - byte *Buffer; - int Length; - byte *Ptr; - int BytesLeft; -}; - -/* - * Read a file into a buffer. - */ - -static struct BufferedFile *ReadBufferedFile(const char *name) -{ - struct BufferedFile *BF; - union { - byte *b; - void *v; - } buffer; - - /* - * input verification - */ - - if(!name) - { - return(NULL); - } - - /* - * Allocate control struct. - */ - - BF = ri.Malloc(sizeof(struct BufferedFile)); - if(!BF) - { - return(NULL); - } - - /* - * Initialize the structs components. - */ - - BF->Length = 0; - BF->Buffer = NULL; - BF->Ptr = NULL; - BF->BytesLeft = 0; - - /* - * Read the file. - */ - - BF->Length = ri.FS_ReadFile((char *) name, &buffer.v); - BF->Buffer = buffer.b; - - /* - * Did we get it? Is it big enough? - */ - - if(!(BF->Buffer && (BF->Length > 0))) - { - ri.Free(BF); - - return(NULL); - } - - /* - * Set the pointers and counters. - */ - - BF->Ptr = BF->Buffer; - BF->BytesLeft = BF->Length; - - return(BF); -} - -/* - * Close a buffered file. - */ - -static void CloseBufferedFile(struct BufferedFile *BF) -{ - if(BF) - { - if(BF->Buffer) - { - ri.FS_FreeFile(BF->Buffer); - } - - ri.Free(BF); - } -} - -/* - * Get a pointer to the requested bytes. - */ - -static void *BufferedFileRead(struct BufferedFile *BF, unsigned Length) -{ - void *RetVal; - - /* - * input verification - */ - - if(!(BF && Length)) - { - return(NULL); - } - - /* - * not enough bytes left - */ - - if(Length > BF->BytesLeft) - { - return(NULL); - } - - /* - * the pointer to the requested data - */ - - RetVal = BF->Ptr; - - /* - * Raise the pointer and counter. - */ - - BF->Ptr += Length; - BF->BytesLeft -= Length; - - return(RetVal); -} - -/* - * Rewind the buffer. - */ - -static qboolean BufferedFileRewind(struct BufferedFile *BF, unsigned Offset) -{ - unsigned BytesRead; - - /* - * input verification - */ - - if(!BF) - { - return(qfalse); - } - - /* - * special trick to rewind to the beginning of the buffer - */ - - if(Offset == (unsigned)-1) - { - BF->Ptr = BF->Buffer; - BF->BytesLeft = BF->Length; - - return(qtrue); - } - - /* - * How many bytes do we have already read? - */ - - BytesRead = BF->Ptr - BF->Buffer; - - /* - * We can only rewind to the beginning of the BufferedFile. - */ - - if(Offset > BytesRead) - { - return(qfalse); - } - - /* - * lower the pointer and counter. - */ - - BF->Ptr -= Offset; - BF->BytesLeft += Offset; - - return(qtrue); -} - -/* - * Skip some bytes. - */ - -static qboolean BufferedFileSkip(struct BufferedFile *BF, unsigned Offset) -{ - /* - * input verification - */ - - if(!BF) - { - return(qfalse); - } - - /* - * We can only skip to the end of the BufferedFile. - */ - - if(Offset > BF->BytesLeft) - { - return(qfalse); - } - - /* - * lower the pointer and counter. - */ - - BF->Ptr += Offset; - BF->BytesLeft -= Offset; - - return(qtrue); -} - -/* - * Find a chunk - */ - -static qboolean FindChunk(struct BufferedFile *BF, uint32_t ChunkType) -{ - struct PNG_ChunkHeader *CH; - - uint32_t Length; - uint32_t Type; - - /* - * input verification - */ - - if(!BF) - { - return(qfalse); - } - - /* - * cycle trough the chunks - */ - - while(qtrue) - { - /* - * Read the chunk-header. - */ - - CH = BufferedFileRead(BF, PNG_ChunkHeader_Size); - if(!CH) - { - return(qfalse); - } - - /* - * Do not swap the original types - * they might be needed later. - */ - - Length = BigLong(CH->Length); - Type = BigLong(CH->Type); - - /* - * We found it! - */ - - if(Type == ChunkType) - { - /* - * Rewind to the start of the chunk. - */ - - BufferedFileRewind(BF, PNG_ChunkHeader_Size); - - break; - } - else - { - /* - * Skip the rest of the chunk. - */ - - if(Length) - { - if(!BufferedFileSkip(BF, Length + PNG_ChunkCRC_Size)) - { - return(qfalse); - } - } - } - } - - return(qtrue); -} - -/* - * Decompress all IDATs - */ - -static uint32_t DecompressIDATs(struct BufferedFile *BF, uint8_t **Buffer) -{ - uint8_t *DecompressedData; - uint32_t DecompressedDataLength; - - uint8_t *CompressedData; - uint8_t *CompressedDataPtr; - uint32_t CompressedDataLength; - - struct PNG_ChunkHeader *CH; - - uint32_t Length; - uint32_t Type; - - int BytesToRewind; - - int32_t puffResult; - uint8_t *puffDest; - uint32_t puffDestLen; - uint8_t *puffSrc; - uint32_t puffSrcLen; - - /* - * input verification - */ - - if(!(BF && Buffer)) - { - return(-1); - } - - /* - * some zeroing - */ - - DecompressedData = NULL; - DecompressedDataLength = 0; - *Buffer = DecompressedData; - - CompressedData = NULL; - CompressedDataLength = 0; - - BytesToRewind = 0; - - /* - * Find the first IDAT chunk. - */ - - if(!FindChunk(BF, PNG_ChunkType_IDAT)) - { - return(-1); - } - - /* - * Count the size of the uncompressed data - */ - - while(qtrue) - { - /* - * Read chunk header - */ - - CH = BufferedFileRead(BF, PNG_ChunkHeader_Size); - if(!CH) - { - /* - * Rewind to the start of this adventure - * and return unsuccessfull - */ - - BufferedFileRewind(BF, BytesToRewind); - - return(-1); - } - - /* - * Length and Type of chunk - */ - - Length = BigLong(CH->Length); - Type = BigLong(CH->Type); - - /* - * We have reached the end of the IDAT chunks - */ - - if(!(Type == PNG_ChunkType_IDAT)) - { - BufferedFileRewind(BF, PNG_ChunkHeader_Size); - - break; - } - - /* - * Add chunk header to count. - */ - - BytesToRewind += PNG_ChunkHeader_Size; - - /* - * Skip to next chunk - */ - - if(Length) - { - if(!BufferedFileSkip(BF, Length + PNG_ChunkCRC_Size)) - { - BufferedFileRewind(BF, BytesToRewind); - - return(-1); - } - - BytesToRewind += Length + PNG_ChunkCRC_Size; - CompressedDataLength += Length; - } - } - - BufferedFileRewind(BF, BytesToRewind); - - CompressedData = ri.Malloc(CompressedDataLength); - if(!CompressedData) - { - return(-1); - } - - CompressedDataPtr = CompressedData; - - /* - * Collect the compressed Data - */ - - while(qtrue) - { - /* - * Read chunk header - */ - - CH = BufferedFileRead(BF, PNG_ChunkHeader_Size); - if(!CH) - { - ri.Free(CompressedData); - - return(-1); - } - - /* - * Length and Type of chunk - */ - - Length = BigLong(CH->Length); - Type = BigLong(CH->Type); - - /* - * We have reached the end of the IDAT chunks - */ - - if(!(Type == PNG_ChunkType_IDAT)) - { - BufferedFileRewind(BF, PNG_ChunkHeader_Size); - - break; - } - - /* - * Copy the Data - */ - - if(Length) - { - uint8_t *OrigCompressedData; - - OrigCompressedData = BufferedFileRead(BF, Length); - if(!OrigCompressedData) - { - ri.Free(CompressedData); - - return(-1); - } - - if(!BufferedFileSkip(BF, PNG_ChunkCRC_Size)) - { - ri.Free(CompressedData); - - return(-1); - } - - memcpy(CompressedDataPtr, OrigCompressedData, Length); - CompressedDataPtr += Length; - } - } - - /* - * Let puff() calculate the decompressed data length. - */ - - puffDest = NULL; - puffDestLen = 0; - - /* - * The zlib header and checkvalue don't belong to the compressed data. - */ - - puffSrc = CompressedData + PNG_ZlibHeader_Size; - puffSrcLen = CompressedDataLength - PNG_ZlibHeader_Size - PNG_ZlibCheckValue_Size; - - /* - * first puff() to calculate the size of the uncompressed data - */ - - puffResult = puff(puffDest, &puffDestLen, puffSrc, &puffSrcLen); - if(!((puffResult == 0) && (puffDestLen > 0))) - { - ri.Free(CompressedData); - - return(-1); - } - - /* - * Allocate the buffer for the uncompressed data. - */ - - DecompressedData = ri.Malloc(puffDestLen); - if(!DecompressedData) - { - ri.Free(CompressedData); - - return(-1); - } - - /* - * Set the input again in case something was changed by the last puff() . - */ - - puffDest = DecompressedData; - puffSrc = CompressedData + PNG_ZlibHeader_Size; - puffSrcLen = CompressedDataLength - PNG_ZlibHeader_Size - PNG_ZlibCheckValue_Size; - - /* - * decompression puff() - */ - - puffResult = puff(puffDest, &puffDestLen, puffSrc, &puffSrcLen); - - /* - * The compressed data is not needed anymore. - */ - - ri.Free(CompressedData); - - /* - * Check if the last puff() was successfull. - */ - - if(!((puffResult == 0) && (puffDestLen > 0))) - { - ri.Free(DecompressedData); - - return(-1); - } - - /* - * Set the output of this function. - */ - - DecompressedDataLength = puffDestLen; - *Buffer = DecompressedData; - - return(DecompressedDataLength); -} - -/* - * the Paeth predictor - */ - -static uint8_t PredictPaeth(uint8_t a, uint8_t b, uint8_t c) -{ - /* - * a == Left - * b == Up - * c == UpLeft - */ - - uint8_t Pr; - int p; - int pa, pb, pc; - - p = ((int) a) + ((int) b) - ((int) c); - pa = abs(p - ((int) a)); - pb = abs(p - ((int) b)); - pc = abs(p - ((int) c)); - - if((pa <= pb) && (pa <= pc)) - { - Pr = a; - } - else if(pb <= pc) - { - Pr = b; - } - else - { - Pr = c; - } - - return(Pr); - -} - -/* - * Reverse the filters. - */ - -static qboolean UnfilterImage(uint8_t *DecompressedData, - uint32_t ImageHeight, - uint32_t BytesPerScanline, - uint32_t BytesPerPixel) -{ - uint8_t *DecompPtr; - uint8_t FilterType; - uint8_t *PixelLeft, *PixelUp, *PixelUpLeft; - uint32_t w, h, p; - - /* - * some zeros for the filters - */ - - uint8_t Zeros[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - - /* - * input verification - */ - - if(!(DecompressedData && BytesPerPixel)) - { - return(qfalse); - } - - /* - * ImageHeight and BytesPerScanline can be zero in small interlaced images. - */ - - if((!ImageHeight) || (!BytesPerScanline)) - { - return(qtrue); - } - - /* - * Set the pointer to the start of the decompressed Data. - */ - - DecompPtr = DecompressedData; - - /* - * Un-filtering is done in place. - */ - - /* - * Go trough all scanlines. - */ - - for(h = 0; h < ImageHeight; h++) - { - /* - * Every scanline starts with a FilterType byte. - */ - - FilterType = *DecompPtr; - DecompPtr++; - - /* - * Left pixel of the first byte in a scanline is zero. - */ - - PixelLeft = Zeros; - - /* - * Set PixelUp to previous line only if we are on the second line or above. - * - * Plus one byte for the FilterType - */ - - if(h > 0) - { - PixelUp = DecompPtr - (BytesPerScanline + 1); - } - else - { - PixelUp = Zeros; - } - - /* - * The pixel left to the first pixel of the previous scanline is zero too. - */ - - PixelUpLeft = Zeros; - - /* - * Cycle trough all pixels of the scanline. - */ - - for(w = 0; w < (BytesPerScanline / BytesPerPixel); w++) - { - /* - * Cycle trough the bytes of the pixel. - */ - - for(p = 0; p < BytesPerPixel; p++) - { - switch(FilterType) - { - case PNG_FilterType_None : - { - /* - * The byte is unfiltered. - */ - - break; - } - - case PNG_FilterType_Sub : - { - DecompPtr[p] += PixelLeft[p]; - - break; - } - - case PNG_FilterType_Up : - { - DecompPtr[p] += PixelUp[p]; - - break; - } - - case PNG_FilterType_Average : - { - DecompPtr[p] += ((uint8_t) ((((uint16_t) PixelLeft[p]) + ((uint16_t) PixelUp[p])) / 2)); - - break; - } - - case PNG_FilterType_Paeth : - { - DecompPtr[p] += PredictPaeth(PixelLeft[p], PixelUp[p], PixelUpLeft[p]); - - break; - } - - default : - { - return(qfalse); - } - } - } - - PixelLeft = DecompPtr; - - /* - * We only have an upleft pixel if we are on the second line or above. - */ - - if(h > 0) - { - PixelUpLeft = DecompPtr - (BytesPerScanline + 1); - } - - /* - * Skip to the next pixel. - */ - - DecompPtr += BytesPerPixel; - - /* - * We only have a previous line if we are on the second line and above. - */ - - if(h > 0) - { - PixelUp = DecompPtr - (BytesPerScanline + 1); - } - } - } - - return(qtrue); -} - -/* - * Convert a raw input pixel to Quake 3 RGA format. - */ - -static qboolean ConvertPixel(struct PNG_Chunk_IHDR *IHDR, - byte *OutPtr, - uint8_t *DecompPtr, - qboolean HasTransparentColour, - uint8_t *TransparentColour, - uint8_t *OutPal) -{ - /* - * input verification - */ - - if(!(IHDR && OutPtr && DecompPtr && TransparentColour && OutPal)) - { - return(qfalse); - } - - switch(IHDR->ColourType) - { - case PNG_ColourType_Grey : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_1 : - case PNG_BitDepth_2 : - case PNG_BitDepth_4 : - { - uint8_t Step; - uint8_t GreyValue; - - Step = 0xFF / ((1 << IHDR->BitDepth) - 1); - - GreyValue = DecompPtr[0] * Step; - - OutPtr[0] = GreyValue; - OutPtr[1] = GreyValue; - OutPtr[2] = GreyValue; - OutPtr[3] = 0xFF; - - /* - * Grey supports full transparency for one specified colour - */ - - if(HasTransparentColour) - { - if(TransparentColour[1] == DecompPtr[0]) - { - OutPtr[3] = 0x00; - } - } - - - break; - } - - case PNG_BitDepth_8 : - case PNG_BitDepth_16 : - { - OutPtr[0] = DecompPtr[0]; - OutPtr[1] = DecompPtr[0]; - OutPtr[2] = DecompPtr[0]; - OutPtr[3] = 0xFF; - - /* - * Grey supports full transparency for one specified colour - */ - - if(HasTransparentColour) - { - if(IHDR->BitDepth == PNG_BitDepth_8) - { - if(TransparentColour[1] == DecompPtr[0]) - { - OutPtr[3] = 0x00; - } - } - else - { - if((TransparentColour[0] == DecompPtr[0]) && (TransparentColour[1] == DecompPtr[1])) - { - OutPtr[3] = 0x00; - } - } - } - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_True : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_8 : - { - OutPtr[0] = DecompPtr[0]; - OutPtr[1] = DecompPtr[1]; - OutPtr[2] = DecompPtr[2]; - OutPtr[3] = 0xFF; - - /* - * True supports full transparency for one specified colour - */ - - if(HasTransparentColour) - { - if((TransparentColour[1] == DecompPtr[0]) && - (TransparentColour[3] == DecompPtr[1]) && - (TransparentColour[5] == DecompPtr[2])) - { - OutPtr[3] = 0x00; - } - } - - break; - } - - case PNG_BitDepth_16 : - { - /* - * We use only the upper byte. - */ - - OutPtr[0] = DecompPtr[0]; - OutPtr[1] = DecompPtr[2]; - OutPtr[2] = DecompPtr[4]; - OutPtr[3] = 0xFF; - - /* - * True supports full transparency for one specified colour - */ - - if(HasTransparentColour) - { - if((TransparentColour[0] == DecompPtr[0]) && (TransparentColour[1] == DecompPtr[1]) && - (TransparentColour[2] == DecompPtr[2]) && (TransparentColour[3] == DecompPtr[3]) && - (TransparentColour[4] == DecompPtr[4]) && (TransparentColour[5] == DecompPtr[5])) - { - OutPtr[3] = 0x00; - } - } - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_Indexed : - { - OutPtr[0] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 0]; - OutPtr[1] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 1]; - OutPtr[2] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 2]; - OutPtr[3] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 3]; - - break; - } - - case PNG_ColourType_GreyAlpha : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_8 : - { - OutPtr[0] = DecompPtr[0]; - OutPtr[1] = DecompPtr[0]; - OutPtr[2] = DecompPtr[0]; - OutPtr[3] = DecompPtr[1]; - - break; - } - - case PNG_BitDepth_16 : - { - /* - * We use only the upper byte. - */ - - OutPtr[0] = DecompPtr[0]; - OutPtr[1] = DecompPtr[0]; - OutPtr[2] = DecompPtr[0]; - OutPtr[3] = DecompPtr[2]; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_TrueAlpha : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_8 : - { - OutPtr[0] = DecompPtr[0]; - OutPtr[1] = DecompPtr[1]; - OutPtr[2] = DecompPtr[2]; - OutPtr[3] = DecompPtr[3]; - - break; - } - - case PNG_BitDepth_16 : - { - /* - * We use only the upper byte. - */ - - OutPtr[0] = DecompPtr[0]; - OutPtr[1] = DecompPtr[2]; - OutPtr[2] = DecompPtr[4]; - OutPtr[3] = DecompPtr[6]; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - default : - { - return(qfalse); - } - } - - return(qtrue); -} - - -/* - * Decode a non-interlaced image. - */ - -static qboolean DecodeImageNonInterlaced(struct PNG_Chunk_IHDR *IHDR, - byte *OutBuffer, - uint8_t *DecompressedData, - uint32_t DecompressedDataLength, - qboolean HasTransparentColour, - uint8_t *TransparentColour, - uint8_t *OutPal) -{ - uint32_t IHDR_Width; - uint32_t IHDR_Height; - uint32_t BytesPerScanline, BytesPerPixel, PixelsPerByte; - uint32_t w, h, p; - byte *OutPtr; - uint8_t *DecompPtr; - - /* - * input verification - */ - - if(!(IHDR && OutBuffer && DecompressedData && DecompressedDataLength && TransparentColour && OutPal)) - { - return(qfalse); - } - - /* - * byte swapping - */ - - IHDR_Width = BigLong(IHDR->Width); - IHDR_Height = BigLong(IHDR->Height); - - /* - * information for un-filtering - */ - - switch(IHDR->ColourType) - { - case PNG_ColourType_Grey : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_1 : - case PNG_BitDepth_2 : - case PNG_BitDepth_4 : - { - BytesPerPixel = 1; - PixelsPerByte = 8 / IHDR->BitDepth; - - break; - } - - case PNG_BitDepth_8 : - case PNG_BitDepth_16 : - { - BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_Grey; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_True : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_8 : - case PNG_BitDepth_16 : - { - BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_True; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_Indexed : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_1 : - case PNG_BitDepth_2 : - case PNG_BitDepth_4 : - { - BytesPerPixel = 1; - PixelsPerByte = 8 / IHDR->BitDepth; - - break; - } - - case PNG_BitDepth_8 : - { - BytesPerPixel = PNG_NumColourComponents_Indexed; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_GreyAlpha : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_8 : - case PNG_BitDepth_16 : - { - BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_GreyAlpha; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_TrueAlpha : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_8 : - case PNG_BitDepth_16 : - { - BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_TrueAlpha; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - default : - { - return(qfalse); - } - } - - /* - * Calculate the size of one scanline - */ - - BytesPerScanline = (IHDR_Width * BytesPerPixel + (PixelsPerByte - 1)) / PixelsPerByte; - - /* - * Check if we have enough data for the whole image. - */ - - if(!(DecompressedDataLength == ((BytesPerScanline + 1) * IHDR_Height))) - { - return(qfalse); - } - - /* - * Unfilter the image. - */ - - if(!UnfilterImage(DecompressedData, IHDR_Height, BytesPerScanline, BytesPerPixel)) - { - return(qfalse); - } - - /* - * Set the working pointers to the beginning of the buffers. - */ - - OutPtr = OutBuffer; - DecompPtr = DecompressedData; - - /* - * Create the output image. - */ - - for(h = 0; h < IHDR_Height; h++) - { - /* - * Count the pixels on the scanline for those multipixel bytes - */ - - uint32_t CurrPixel; - - /* - * skip FilterType - */ - - DecompPtr++; - - /* - * Reset the pixel count. - */ - - CurrPixel = 0; - - for(w = 0; w < (BytesPerScanline / BytesPerPixel); w++) - { - if(PixelsPerByte > 1) - { - uint8_t Mask; - uint32_t Shift; - uint8_t SinglePixel; - - for(p = 0; p < PixelsPerByte; p++) - { - if(CurrPixel < IHDR_Width) - { - Mask = (1 << IHDR->BitDepth) - 1; - Shift = (PixelsPerByte - 1 - p) * IHDR->BitDepth; - - SinglePixel = ((DecompPtr[0] & (Mask << Shift)) >> Shift); - - if(!ConvertPixel(IHDR, OutPtr, &SinglePixel, HasTransparentColour, TransparentColour, OutPal)) - { - return(qfalse); - } - - OutPtr += Q3IMAGE_BYTESPERPIXEL; - CurrPixel++; - } - } - - } - else - { - if(!ConvertPixel(IHDR, OutPtr, DecompPtr, HasTransparentColour, TransparentColour, OutPal)) - { - return(qfalse); - } - - - OutPtr += Q3IMAGE_BYTESPERPIXEL; - } - - DecompPtr += BytesPerPixel; - } - } - - return(qtrue); -} - -/* - * Decode an interlaced image. - */ - -static qboolean DecodeImageInterlaced(struct PNG_Chunk_IHDR *IHDR, - byte *OutBuffer, - uint8_t *DecompressedData, - uint32_t DecompressedDataLength, - qboolean HasTransparentColour, - uint8_t *TransparentColour, - uint8_t *OutPal) -{ - uint32_t IHDR_Width; - uint32_t IHDR_Height; - uint32_t BytesPerScanline[PNG_Adam7_NumPasses], BytesPerPixel, PixelsPerByte; - uint32_t PassWidth[PNG_Adam7_NumPasses], PassHeight[PNG_Adam7_NumPasses]; - uint32_t WSkip[PNG_Adam7_NumPasses], WOffset[PNG_Adam7_NumPasses], HSkip[PNG_Adam7_NumPasses], HOffset[PNG_Adam7_NumPasses]; - uint32_t w, h, p, a; - byte *OutPtr; - uint8_t *DecompPtr; - uint32_t TargetLength; - - /* - * input verification - */ - - if(!(IHDR && OutBuffer && DecompressedData && DecompressedDataLength && TransparentColour && OutPal)) - { - return(qfalse); - } - - /* - * byte swapping - */ - - IHDR_Width = BigLong(IHDR->Width); - IHDR_Height = BigLong(IHDR->Height); - - /* - * Skip and Offset for the passes. - */ - - WSkip[0] = 8; - WOffset[0] = 0; - HSkip[0] = 8; - HOffset[0] = 0; - - WSkip[1] = 8; - WOffset[1] = 4; - HSkip[1] = 8; - HOffset[1] = 0; - - WSkip[2] = 4; - WOffset[2] = 0; - HSkip[2] = 8; - HOffset[2] = 4; - - WSkip[3] = 4; - WOffset[3] = 2; - HSkip[3] = 4; - HOffset[3] = 0; - - WSkip[4] = 2; - WOffset[4] = 0; - HSkip[4] = 4; - HOffset[4] = 2; - - WSkip[5] = 2; - WOffset[5] = 1; - HSkip[5] = 2; - HOffset[5] = 0; - - WSkip[6] = 1; - WOffset[6] = 0; - HSkip[6] = 2; - HOffset[6] = 1; - - /* - * Calculate the sizes of the passes. - */ - - PassWidth[0] = (IHDR_Width + 7) / 8; - PassHeight[0] = (IHDR_Height + 7) / 8; - - PassWidth[1] = (IHDR_Width + 3) / 8; - PassHeight[1] = (IHDR_Height + 7) / 8; - - PassWidth[2] = (IHDR_Width + 3) / 4; - PassHeight[2] = (IHDR_Height + 3) / 8; - - PassWidth[3] = (IHDR_Width + 1) / 4; - PassHeight[3] = (IHDR_Height + 3) / 4; - - PassWidth[4] = (IHDR_Width + 1) / 2; - PassHeight[4] = (IHDR_Height + 1) / 4; - - PassWidth[5] = (IHDR_Width + 0) / 2; - PassHeight[5] = (IHDR_Height + 1) / 2; - - PassWidth[6] = (IHDR_Width + 0) / 1; - PassHeight[6] = (IHDR_Height + 0) / 2; - - /* - * information for un-filtering - */ - - switch(IHDR->ColourType) - { - case PNG_ColourType_Grey : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_1 : - case PNG_BitDepth_2 : - case PNG_BitDepth_4 : - { - BytesPerPixel = 1; - PixelsPerByte = 8 / IHDR->BitDepth; - - break; - } - - case PNG_BitDepth_8 : - case PNG_BitDepth_16 : - { - BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_Grey; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_True : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_8 : - case PNG_BitDepth_16 : - { - BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_True; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_Indexed : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_1 : - case PNG_BitDepth_2 : - case PNG_BitDepth_4 : - { - BytesPerPixel = 1; - PixelsPerByte = 8 / IHDR->BitDepth; - - break; - } - - case PNG_BitDepth_8 : - { - BytesPerPixel = PNG_NumColourComponents_Indexed; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_GreyAlpha : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_8 : - case PNG_BitDepth_16 : - { - BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_GreyAlpha; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_TrueAlpha : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_8 : - case PNG_BitDepth_16 : - { - BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_TrueAlpha; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - default : - { - return(qfalse); - } - } - - /* - * Calculate the size of the scanlines per pass - */ - - for(a = 0; a < PNG_Adam7_NumPasses; a++) - { - BytesPerScanline[a] = (PassWidth[a] * BytesPerPixel + (PixelsPerByte - 1)) / PixelsPerByte; - } - - /* - * Calculate the size of all passes - */ - - TargetLength = 0; - - for(a = 0; a < PNG_Adam7_NumPasses; a++) - { - TargetLength += ((BytesPerScanline[a] + (BytesPerScanline[a] ? 1 : 0)) * PassHeight[a]); - } - - /* - * Check if we have enough data for the whole image. - */ - - if(!(DecompressedDataLength == TargetLength)) - { - return(qfalse); - } - - /* - * Unfilter the image. - */ - - DecompPtr = DecompressedData; - - for(a = 0; a < PNG_Adam7_NumPasses; a++) - { - if(!UnfilterImage(DecompPtr, PassHeight[a], BytesPerScanline[a], BytesPerPixel)) - { - return(qfalse); - } - - DecompPtr += ((BytesPerScanline[a] + (BytesPerScanline[a] ? 1 : 0)) * PassHeight[a]); - } - - /* - * Set the working pointers to the beginning of the buffers. - */ - - DecompPtr = DecompressedData; - - /* - * Create the output image. - */ - - for(a = 0; a < PNG_Adam7_NumPasses; a++) - { - for(h = 0; h < PassHeight[a]; h++) - { - /* - * Count the pixels on the scanline for those multipixel bytes - */ - - uint32_t CurrPixel; - - /* - * skip FilterType - * but only when the pass has a width bigger than zero - */ - - if(BytesPerScanline[a]) - { - DecompPtr++; - } - - /* - * Reset the pixel count. - */ - - CurrPixel = 0; - - for(w = 0; w < (BytesPerScanline[a] / BytesPerPixel); w++) - { - if(PixelsPerByte > 1) - { - uint8_t Mask; - uint32_t Shift; - uint8_t SinglePixel; - - for(p = 0; p < PixelsPerByte; p++) - { - if(CurrPixel < PassWidth[a]) - { - Mask = (1 << IHDR->BitDepth) - 1; - Shift = (PixelsPerByte - 1 - p) * IHDR->BitDepth; - - SinglePixel = ((DecompPtr[0] & (Mask << Shift)) >> Shift); - - OutPtr = OutBuffer + (((((h * HSkip[a]) + HOffset[a]) * IHDR_Width) + ((CurrPixel * WSkip[a]) + WOffset[a])) * Q3IMAGE_BYTESPERPIXEL); - - if(!ConvertPixel(IHDR, OutPtr, &SinglePixel, HasTransparentColour, TransparentColour, OutPal)) - { - return(qfalse); - } - - CurrPixel++; - } - } - - } - else - { - OutPtr = OutBuffer + (((((h * HSkip[a]) + HOffset[a]) * IHDR_Width) + ((w * WSkip[a]) + WOffset[a])) * Q3IMAGE_BYTESPERPIXEL); - - if(!ConvertPixel(IHDR, OutPtr, DecompPtr, HasTransparentColour, TransparentColour, OutPal)) - { - return(qfalse); - } - } - - DecompPtr += BytesPerPixel; - } - } - } - - return(qtrue); -} - -/* - * The PNG loader - */ - -void R_LoadPNG(const char *name, byte **pic, int *width, int *height) -{ - struct BufferedFile *ThePNG; - byte *OutBuffer; - uint8_t *Signature; - struct PNG_ChunkHeader *CH; - uint32_t ChunkHeaderLength; - uint32_t ChunkHeaderType; - struct PNG_Chunk_IHDR *IHDR; - uint32_t IHDR_Width; - uint32_t IHDR_Height; - PNG_ChunkCRC *CRC; - uint8_t *InPal; - uint8_t *DecompressedData; - uint32_t DecompressedDataLength; - uint32_t i; - - /* - * palette with 256 RGBA entries - */ - - uint8_t OutPal[1024]; - - /* - * transparent colour from the tRNS chunk - */ - - qboolean HasTransparentColour = qfalse; - uint8_t TransparentColour[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - - /* - * input verification - */ - - if(!(name && pic)) - { - return; - } - - /* - * Zero out return values. - */ - - *pic = NULL; - - if(width) - { - *width = 0; - } - - if(height) - { - *height = 0; - } - - /* - * Read the file. - */ - - ThePNG = ReadBufferedFile(name); - if(!ThePNG) - { - return; - } - - /* - * Read the siganture of the file. - */ - - Signature = BufferedFileRead(ThePNG, PNG_Signature_Size); - if(!Signature) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Is it a PNG? - */ - - if(memcmp(Signature, PNG_Signature, PNG_Signature_Size)) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Read the first chunk-header. - */ - - CH = BufferedFileRead(ThePNG, PNG_ChunkHeader_Size); - if(!CH) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * PNG multi-byte types are in Big Endian - */ - - ChunkHeaderLength = BigLong(CH->Length); - ChunkHeaderType = BigLong(CH->Type); - - /* - * Check if the first chunk is an IHDR. - */ - - if(!((ChunkHeaderType == PNG_ChunkType_IHDR) && (ChunkHeaderLength == PNG_Chunk_IHDR_Size))) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Read the IHDR. - */ - - IHDR = BufferedFileRead(ThePNG, PNG_Chunk_IHDR_Size); - if(!IHDR) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Read the CRC for IHDR - */ - - CRC = BufferedFileRead(ThePNG, PNG_ChunkCRC_Size); - if(!CRC) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Here we could check the CRC if we wanted to. - */ - - /* - * multi-byte type swapping - */ - - IHDR_Width = BigLong(IHDR->Width); - IHDR_Height = BigLong(IHDR->Height); - - /* - * Check if Width and Height are valid. - */ - - if(!((IHDR_Width > 0) && (IHDR_Height > 0)) - || IHDR_Width > INT_MAX / Q3IMAGE_BYTESPERPIXEL / IHDR_Height) - { - CloseBufferedFile(ThePNG); - - ri.Printf( PRINT_WARNING, "%s: invalid image size\n", name ); - - return; - } - - /* - * Do we need to check if the dimensions of the image are valid for Quake3? - */ - - /* - * Check if CompressionMethod and FilterMethod are valid. - */ - - if(!((IHDR->CompressionMethod == PNG_CompressionMethod_0) && (IHDR->FilterMethod == PNG_FilterMethod_0))) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Check if InterlaceMethod is valid. - */ - - if(!((IHDR->InterlaceMethod == PNG_InterlaceMethod_NonInterlaced) || (IHDR->InterlaceMethod == PNG_InterlaceMethod_Interlaced))) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Read palette for an indexed image. - */ - - if(IHDR->ColourType == PNG_ColourType_Indexed) - { - /* - * We need the palette first. - */ - - if(!FindChunk(ThePNG, PNG_ChunkType_PLTE)) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Read the chunk-header. - */ - - CH = BufferedFileRead(ThePNG, PNG_ChunkHeader_Size); - if(!CH) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * PNG multi-byte types are in Big Endian - */ - - ChunkHeaderLength = BigLong(CH->Length); - ChunkHeaderType = BigLong(CH->Type); - - /* - * Check if the chunk is a PLTE. - */ - - if(!(ChunkHeaderType == PNG_ChunkType_PLTE)) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Check if Length is divisible by 3 - */ - - if(ChunkHeaderLength % 3) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Read the raw palette data - */ - - InPal = BufferedFileRead(ThePNG, ChunkHeaderLength); - if(!InPal) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Read the CRC for the palette - */ - - CRC = BufferedFileRead(ThePNG, PNG_ChunkCRC_Size); - if(!CRC) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Set some default values. - */ - - for(i = 0; i < 256; i++) - { - OutPal[i * Q3IMAGE_BYTESPERPIXEL + 0] = 0x00; - OutPal[i * Q3IMAGE_BYTESPERPIXEL + 1] = 0x00; - OutPal[i * Q3IMAGE_BYTESPERPIXEL + 2] = 0x00; - OutPal[i * Q3IMAGE_BYTESPERPIXEL + 3] = 0xFF; - } - - /* - * Convert to the Quake3 RGBA-format. - */ - - for(i = 0; i < (ChunkHeaderLength / 3); i++) - { - OutPal[i * Q3IMAGE_BYTESPERPIXEL + 0] = InPal[i*3+0]; - OutPal[i * Q3IMAGE_BYTESPERPIXEL + 1] = InPal[i*3+1]; - OutPal[i * Q3IMAGE_BYTESPERPIXEL + 2] = InPal[i*3+2]; - OutPal[i * Q3IMAGE_BYTESPERPIXEL + 3] = 0xFF; - } - } - - /* - * transparency information is sometimes stored in a tRNS chunk - */ - - /* - * Let's see if there is a tRNS chunk - */ - - if(FindChunk(ThePNG, PNG_ChunkType_tRNS)) - { - uint8_t *Trans; - - /* - * Read the chunk-header. - */ - - CH = BufferedFileRead(ThePNG, PNG_ChunkHeader_Size); - if(!CH) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * PNG multi-byte types are in Big Endian - */ - - ChunkHeaderLength = BigLong(CH->Length); - ChunkHeaderType = BigLong(CH->Type); - - /* - * Check if the chunk is a tRNS. - */ - - if(!(ChunkHeaderType == PNG_ChunkType_tRNS)) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Read the transparency information. - */ - - Trans = BufferedFileRead(ThePNG, ChunkHeaderLength); - if(!Trans) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Read the CRC. - */ - - CRC = BufferedFileRead(ThePNG, PNG_ChunkCRC_Size); - if(!CRC) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Only for Grey, True and Indexed ColourType should tRNS exist. - */ - - switch(IHDR->ColourType) - { - case PNG_ColourType_Grey : - { - if(!ChunkHeaderLength == 2) - { - CloseBufferedFile(ThePNG); - - return; - } - - HasTransparentColour = qtrue; - - /* - * Grey can have one colour which is completely transparent. - * This colour is always stored in 16 bits. - */ - - TransparentColour[0] = Trans[0]; - TransparentColour[1] = Trans[1]; - - break; - } - - case PNG_ColourType_True : - { - if(!ChunkHeaderLength == 6) - { - CloseBufferedFile(ThePNG); - - return; - } - - HasTransparentColour = qtrue; - - /* - * True can have one colour which is completely transparent. - * This colour is always stored in 16 bits. - */ - - TransparentColour[0] = Trans[0]; - TransparentColour[1] = Trans[1]; - TransparentColour[2] = Trans[2]; - TransparentColour[3] = Trans[3]; - TransparentColour[4] = Trans[4]; - TransparentColour[5] = Trans[5]; - - break; - } - - case PNG_ColourType_Indexed : - { - /* - * Maximum of 256 one byte transparency entries. - */ - - if(ChunkHeaderLength > 256) - { - CloseBufferedFile(ThePNG); - - return; - } - - HasTransparentColour = qtrue; - - /* - * alpha values for palette entries - */ - - for(i = 0; i < ChunkHeaderLength; i++) - { - OutPal[i * Q3IMAGE_BYTESPERPIXEL + 3] = Trans[i]; - } - - break; - } - - /* - * All other ColourTypes should not have tRNS chunks - */ - - default : - { - CloseBufferedFile(ThePNG); - - return; - } - } - } - - /* - * Rewind to the start of the file. - */ - - if(!BufferedFileRewind(ThePNG, -1)) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Skip the signature - */ - - if(!BufferedFileSkip(ThePNG, PNG_Signature_Size)) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Decompress all IDAT chunks - */ - - DecompressedDataLength = DecompressIDATs(ThePNG, &DecompressedData); - if(!(DecompressedDataLength && DecompressedData)) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Allocate output buffer. - */ - - OutBuffer = ri.Malloc(IHDR_Width * IHDR_Height * Q3IMAGE_BYTESPERPIXEL); - if(!OutBuffer) - { - ri.Free(DecompressedData); - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Interlaced and Non-interlaced images need to be handled differently. - */ - - switch(IHDR->InterlaceMethod) - { - case PNG_InterlaceMethod_NonInterlaced : - { - if(!DecodeImageNonInterlaced(IHDR, OutBuffer, DecompressedData, DecompressedDataLength, HasTransparentColour, TransparentColour, OutPal)) - { - ri.Free(OutBuffer); - ri.Free(DecompressedData); - CloseBufferedFile(ThePNG); - - return; - } - - break; - } - - case PNG_InterlaceMethod_Interlaced : - { - if(!DecodeImageInterlaced(IHDR, OutBuffer, DecompressedData, DecompressedDataLength, HasTransparentColour, TransparentColour, OutPal)) - { - ri.Free(OutBuffer); - ri.Free(DecompressedData); - CloseBufferedFile(ThePNG); - - return; - } - - break; - } - - default : - { - ri.Free(OutBuffer); - ri.Free(DecompressedData); - CloseBufferedFile(ThePNG); - - return; - } - } - - /* - * update the pointer to the image data - */ - - *pic = OutBuffer; - - /* - * Fill width and height. - */ - - if(width) - { - *width = IHDR_Width; - } - - if(height) - { - *height = IHDR_Height; - } - - /* - * DecompressedData is not needed anymore. - */ - - ri.Free(DecompressedData); - - /* - * We have all data, so close the file. - */ - - CloseBufferedFile(ThePNG); -} diff --git a/src/rend2/tr_image_tga.c b/src/rend2/tr_image_tga.c deleted file mode 100644 index 27ca0d7b..00000000 --- a/src/rend2/tr_image_tga.c +++ /dev/null @@ -1,324 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "../qcommon/q_shared.h" -#include "../qcommon/qfiles.h" -#include "../qcommon/qcommon.h" -#include "../renderer/tr_public.h" -extern refimport_t ri; - -/* -======================================================================== - -TGA files are used for 24/32 bit images - -======================================================================== -*/ - -typedef struct _TargaHeader { - unsigned char id_length, colormap_type, image_type; - unsigned short colormap_index, colormap_length; - unsigned char colormap_size; - unsigned short x_origin, y_origin, width, height; - unsigned char pixel_size, attributes; -} TargaHeader; - -void R_LoadTGA ( const char *name, byte **pic, int *width, int *height) -{ - unsigned columns, rows, numPixels; - byte *pixbuf; - int row, column; - byte *buf_p; - byte *end; - union { - byte *b; - void *v; - } buffer; - TargaHeader targa_header; - byte *targa_rgba; - int length; - - *pic = NULL; - - if(width) - *width = 0; - if(height) - *height = 0; - - // - // load the file - // - length = ri.FS_ReadFile ( ( char * ) name, &buffer.v); - if (!buffer.b || length < 0) { - return; - } - - if(length < 18) - { - ri.Error( ERR_DROP, "LoadTGA: header too short (%s)", name ); - } - - buf_p = buffer.b; - end = buffer.b + length; - - targa_header.id_length = buf_p[0]; - targa_header.colormap_type = buf_p[1]; - targa_header.image_type = buf_p[2]; - - memcpy(&targa_header.colormap_index, &buf_p[3], 2); - memcpy(&targa_header.colormap_length, &buf_p[5], 2); - targa_header.colormap_size = buf_p[7]; - memcpy(&targa_header.x_origin, &buf_p[8], 2); - memcpy(&targa_header.y_origin, &buf_p[10], 2); - memcpy(&targa_header.width, &buf_p[12], 2); - memcpy(&targa_header.height, &buf_p[14], 2); - targa_header.pixel_size = buf_p[16]; - targa_header.attributes = buf_p[17]; - - targa_header.colormap_index = LittleShort(targa_header.colormap_index); - targa_header.colormap_length = LittleShort(targa_header.colormap_length); - targa_header.x_origin = LittleShort(targa_header.x_origin); - targa_header.y_origin = LittleShort(targa_header.y_origin); - targa_header.width = LittleShort(targa_header.width); - targa_header.height = LittleShort(targa_header.height); - - buf_p += 18; - - if (targa_header.image_type!=2 - && targa_header.image_type!=10 - && targa_header.image_type != 3 ) - { - ri.Error (ERR_DROP, "LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported"); - } - - if ( targa_header.colormap_type != 0 ) - { - ri.Error( ERR_DROP, "LoadTGA: colormaps not supported" ); - } - - if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 ) - { - ri.Error (ERR_DROP, "LoadTGA: Only 32 or 24 bit images supported (no colormaps)"); - } - - columns = targa_header.width; - rows = targa_header.height; - numPixels = columns * rows * 4; - - if(!columns || !rows || numPixels > 0x7FFFFFFF || numPixels / columns / 4 != rows) - { - ri.Error (ERR_DROP, "LoadTGA: %s has an invalid image size", name); - } - - - targa_rgba = ri.Malloc (numPixels); - - if (targa_header.id_length != 0) - { - if (buf_p + targa_header.id_length > end) - ri.Error( ERR_DROP, "LoadTGA: header too short (%s)", name ); - - buf_p += targa_header.id_length; // skip TARGA image comment - } - - if ( targa_header.image_type==2 || targa_header.image_type == 3 ) - { - if(buf_p + columns*rows*targa_header.pixel_size/8 > end) - { - ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name); - } - - // Uncompressed RGB or gray scale image - for(row=rows-1; row>=0; row--) - { - pixbuf = targa_rgba + row*columns*4; - for(column=0; column=0; row--) { - pixbuf = targa_rgba + row*columns*4; - for(column=0; column end) - ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name); - packetHeader= *buf_p++; - packetSize = 1 + (packetHeader & 0x7f); - if (packetHeader & 0x80) { // run-length packet - if(buf_p + targa_header.pixel_size/8 > end) - ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name); - switch (targa_header.pixel_size) { - case 24: - blue = *buf_p++; - green = *buf_p++; - red = *buf_p++; - alphabyte = 255; - break; - case 32: - blue = *buf_p++; - green = *buf_p++; - red = *buf_p++; - alphabyte = *buf_p++; - break; - default: - ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'", targa_header.pixel_size, name ); - break; - } - - for(j=0;j0) - row--; - else - goto breakOut; - pixbuf = targa_rgba + row*columns*4; - } - } - } - else { // non run-length packet - - if(buf_p + targa_header.pixel_size/8*packetSize > end) - ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name); - for(j=0;j0) - row--; - else - goto breakOut; - pixbuf = targa_rgba + row*columns*4; - } - } - } - } - breakOut:; - } - } - -#if 0 - // TTimo: this is the chunk of code to ensure a behavior that meets TGA specs - // bit 5 set => top-down - if (targa_header.attributes & 0x20) { - unsigned char *flip = (unsigned char*)malloc (columns*4); - unsigned char *src, *dst; - - for (row = 0; row < rows/2; row++) { - src = targa_rgba + row * 4 * columns; - dst = targa_rgba + (rows - row - 1) * 4 * columns; - - memcpy (flip, src, columns*4); - memcpy (src, dst, columns*4); - memcpy (dst, flip, columns*4); - } - free (flip); - } -#endif - // instead we just print a warning - if (targa_header.attributes & 0x20) { - ri.Printf( PRINT_WARNING, "WARNING: '%s' TGA file header declares top-down image, ignoring\n", name); - } - - if (width) - *width = columns; - if (height) - *height = rows; - - *pic = targa_rgba; - - ri.FS_FreeFile (buffer.v); -} diff --git a/src/rend2/tr_init.c b/src/rend2/tr_init.c deleted file mode 100644 index 7228d01b..00000000 --- a/src/rend2/tr_init.c +++ /dev/null @@ -1,1480 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_init.c -- functions that are not called every frame - -#include "tr_local.h" - -glconfig_t glConfig; -glRefConfig_t glRefConfig; -qboolean textureFilterAnisotropic = qfalse; -int maxAnisotropy = 0; -float displayAspect = 0.0f; - -glstate_t glState; - -static void GfxInfo_f( void ); -static void GfxMemInfo_f( void ); - -#ifdef USE_RENDERER_DLOPEN -cvar_t *com_altivec; -#endif - -cvar_t *r_flareSize; -cvar_t *r_flareFade; -cvar_t *r_flareCoeff; - -cvar_t *r_railWidth; -cvar_t *r_railCoreWidth; -cvar_t *r_railSegmentLength; - -cvar_t *r_verbose; -cvar_t *r_ignore; - -cvar_t *r_detailTextures; - -cvar_t *r_znear; -cvar_t *r_zproj; -cvar_t *r_stereoSeparation; - -cvar_t *r_skipBackEnd; - -cvar_t *r_stereoEnabled; -cvar_t *r_anaglyphMode; - -cvar_t *r_greyscale; - -cvar_t *r_ignorehwgamma; -cvar_t *r_measureOverdraw; - -cvar_t *r_inGameVideo; -cvar_t *r_fastsky; -cvar_t *r_drawSun; -cvar_t *r_dynamiclight; -cvar_t *r_dlightBacks; - -cvar_t *r_lodbias; -cvar_t *r_lodscale; - -cvar_t *r_norefresh; -cvar_t *r_drawentities; -cvar_t *r_drawworld; -cvar_t *r_speeds; -cvar_t *r_fullbright; -cvar_t *r_novis; -cvar_t *r_nocull; -cvar_t *r_facePlaneCull; -cvar_t *r_showcluster; -cvar_t *r_nocurves; - -cvar_t *r_allowExtensions; - -cvar_t *r_ext_compressed_textures; -cvar_t *r_ext_multitexture; -cvar_t *r_ext_compiled_vertex_array; -cvar_t *r_ext_texture_env_add; -cvar_t *r_ext_texture_filter_anisotropic; -cvar_t *r_ext_max_anisotropy; - -cvar_t *r_ext_draw_range_elements; -cvar_t *r_ext_multi_draw_arrays; -cvar_t *r_ext_framebuffer_object; -cvar_t *r_ext_texture_float; -cvar_t *r_arb_half_float_pixel; -cvar_t *r_ext_framebuffer_multisample; - -cvar_t *r_mergeMultidraws; -cvar_t *r_mergeLeafSurfaces; - -cvar_t *r_cameraExposure; - -cvar_t *r_softOverbright; - -cvar_t *r_hdr; -cvar_t *r_postProcess; - -cvar_t *r_toneMap; -cvar_t *r_forceToneMap; -cvar_t *r_forceToneMapMin; -cvar_t *r_forceToneMapAvg; -cvar_t *r_forceToneMapMax; - -cvar_t *r_autoExposure; -cvar_t *r_forceAutoExposure; -cvar_t *r_forceAutoExposureMin; -cvar_t *r_forceAutoExposureMax; - -cvar_t *r_srgb; - -cvar_t *r_depthPrepass; -cvar_t *r_ssao; - -cvar_t *r_normalMapping; -cvar_t *r_specularMapping; -cvar_t *r_deluxeMapping; -cvar_t *r_parallaxMapping; -cvar_t *r_normalAmbient; -cvar_t *r_recalcMD3Normals; -cvar_t *r_mergeLightmaps; -cvar_t *r_dlightMode; -cvar_t *r_pshadowDist; -cvar_t *r_imageUpsample; -cvar_t *r_imageUpsampleMaxSize; -cvar_t *r_imageUpsampleType; -cvar_t *r_genNormalMaps; -cvar_t *r_forceSun; -cvar_t *r_forceSunMapLightScale; -cvar_t *r_forceSunLightScale; -cvar_t *r_forceSunAmbientScale; -cvar_t *r_drawSunRays; -cvar_t *r_sunShadows; -cvar_t *r_shadowFilter; -cvar_t *r_shadowMapSize; -cvar_t *r_shadowCascadeZNear; -cvar_t *r_shadowCascadeZFar; -cvar_t *r_shadowCascadeZBias; - -cvar_t *r_ignoreGLErrors; -cvar_t *r_logFile; - -cvar_t *r_stencilbits; -cvar_t *r_depthbits; -cvar_t *r_colorbits; -cvar_t *r_texturebits; -cvar_t *r_ext_multisample; - -cvar_t *r_drawBuffer; -cvar_t *r_lightmap; -cvar_t *r_vertexLight; -cvar_t *r_uiFullScreen; -cvar_t *r_shadows; -cvar_t *r_flares; -cvar_t *r_mode; -cvar_t *r_nobind; -cvar_t *r_singleShader; -cvar_t *r_roundImagesDown; -cvar_t *r_colorMipLevels; -cvar_t *r_picmip; -cvar_t *r_showtris; -cvar_t *r_showsky; -cvar_t *r_shownormals; -cvar_t *r_finish; -cvar_t *r_clear; -cvar_t *r_swapInterval; -cvar_t *r_textureMode; -cvar_t *r_offsetFactor; -cvar_t *r_offsetUnits; -cvar_t *r_gamma; -cvar_t *r_intensity; -cvar_t *r_lockpvs; -cvar_t *r_noportals; -cvar_t *r_portalOnly; - -cvar_t *r_subdivisions; -cvar_t *r_lodCurveError; - -cvar_t *r_fullscreen; -cvar_t *r_noborder; - -cvar_t *r_width; -cvar_t *r_height; -cvar_t *r_pixelAspect; - -cvar_t *r_overBrightBits; -cvar_t *r_mapOverBrightBits; - -cvar_t *r_debugSurface; -cvar_t *r_simpleMipMaps; - -cvar_t *r_showImages; - -cvar_t *r_ambientScale; -cvar_t *r_directedScale; -cvar_t *r_debugLight; -cvar_t *r_debugSort; -cvar_t *r_printShaders; -cvar_t *r_saveFontData; - -cvar_t *r_marksOnTriangleMeshes; - -cvar_t *r_aviMotionJpegQuality; -cvar_t *r_screenshotJpegQuality; - -cvar_t *r_maxpolys; -int max_polys; -cvar_t *r_maxpolyverts; -int max_polyverts; - -/* -** InitOpenGL -** -** This function is responsible for initializing a valid OpenGL subsystem. This -** is done by calling GLimp_Init (which gives us a working OGL subsystem) then -** setting variables, checking GL constants, and reporting the gfx system config -** to the user. -*/ -static void InitOpenGL( void ) -{ - char renderer_buffer[1024]; - - // - // initialize OS specific portions of the renderer - // - // GLimp_Init directly or indirectly references the following cvars: - // - r_fullscreen - // - r_mode - // - r_(color|depth|stencil)bits - // - r_ignorehwgamma - // - r_gamma - // - - if ( glConfig.vidWidth == 0 ) - { - GLint temp; - - GLimp_Init(); - GLimp_InitExtraExtensions(); - - strcpy( renderer_buffer, glConfig.renderer_string ); - Q_strlwr( renderer_buffer ); - - // OpenGL driver constants - qglGetIntegerv( GL_MAX_TEXTURE_SIZE, &temp ); - glConfig.maxTextureSize = temp; - - // stubbed or broken drivers may have reported 0... - if ( glConfig.maxTextureSize <= 0 ) - { - glConfig.maxTextureSize = 0; - } - } - - // set default state - GL_SetDefaultState(); -} - -/* -================== -GL_CheckErrors -================== -*/ -void GL_CheckErrs( char *file, int line ) { - int err; - char s[64]; - - err = qglGetError(); - if ( err == GL_NO_ERROR ) { - return; - } - if ( r_ignoreGLErrors->integer ) { - return; - } - switch( err ) { - case GL_INVALID_ENUM: - strcpy( s, "GL_INVALID_ENUM" ); - break; - case GL_INVALID_VALUE: - strcpy( s, "GL_INVALID_VALUE" ); - break; - case GL_INVALID_OPERATION: - strcpy( s, "GL_INVALID_OPERATION" ); - break; - case GL_STACK_OVERFLOW: - strcpy( s, "GL_STACK_OVERFLOW" ); - break; - case GL_STACK_UNDERFLOW: - strcpy( s, "GL_STACK_UNDERFLOW" ); - break; - case GL_OUT_OF_MEMORY: - strcpy( s, "GL_OUT_OF_MEMORY" ); - break; - default: - Com_sprintf( s, sizeof(s), "%i", err); - break; - } - - ri.Error( ERR_FATAL, "GL_CheckErrors: %s in %s at line %d", s , file, line); -} - - -/* -============================================================================== - - SCREEN SHOTS - -NOTE TTimo -some thoughts about the screenshots system: -screenshots get written in fs_homepath + fs_gamedir -vanilla q3 .. baseq3/screenshots/ *.tga -team arena .. missionpack/screenshots/ *.tga - -two commands: "screenshot" and "screenshotJPEG" -we use statics to store a count and start writing the first screenshot/screenshot????.tga (.jpg) available -(with FS_FileExists / FS_FOpenFileWrite calls) -FIXME: the statics don't get a reinit between fs_game changes - -============================================================================== -*/ - -/* -================== -RB_ReadPixels - -Reads an image but takes care of alignment issues for reading RGB images. - -Reads a minimum offset for where the RGB data starts in the image from -integer stored at pointer offset. When the function has returned the actual -offset was written back to address offset. This address will always have an -alignment of packAlign to ensure efficient copying. - -Stores the length of padding after a line of pixels to address padlen - -Return value must be freed with ri.Hunk_FreeTempMemory() -================== -*/ - -byte *RB_ReadPixels(int x, int y, int width, int height, size_t *offset, int *padlen) -{ - byte *buffer, *bufstart; - int padwidth, linelen; - GLint packAlign; - - qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); - - linelen = width * 3; - padwidth = PAD(linelen, packAlign); - - // Allocate a few more bytes so that we can choose an alignment we like - buffer = ri.Hunk_AllocateTempMemory(padwidth * height + *offset + packAlign - 1); - - bufstart = PADP((intptr_t) buffer + *offset, packAlign); - qglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, bufstart); - - *offset = bufstart - buffer; - *padlen = padwidth - linelen; - - return buffer; -} - -/* -================== -RB_TakeScreenshot -================== -*/ -void RB_TakeScreenshot(int x, int y, int width, int height, char *fileName) -{ - byte *allbuf, *buffer; - byte *srcptr, *destptr; - byte *endline, *endmem; - byte temp; - - int linelen, padlen; - size_t offset = 18, memcount; - - allbuf = RB_ReadPixels(x, y, width, height, &offset, &padlen); - buffer = allbuf + offset - 18; - - Com_Memset (buffer, 0, 18); - buffer[2] = 2; // uncompressed type - buffer[12] = width & 255; - buffer[13] = width >> 8; - buffer[14] = height & 255; - buffer[15] = height >> 8; - buffer[16] = 24; // pixel size - - // swap rgb to bgr and remove padding from line endings - linelen = width * 3; - - srcptr = destptr = allbuf + offset; - endmem = srcptr + (linelen + padlen) * height; - - while(srcptr < endmem) - { - endline = srcptr + linelen; - - while(srcptr < endline) - { - temp = srcptr[0]; - *destptr++ = srcptr[2]; - *destptr++ = srcptr[1]; - *destptr++ = temp; - - srcptr += 3; - } - - // Skip the pad - srcptr += padlen; - } - - memcount = linelen * height; - - // gamma correct - if(glConfig.deviceSupportsGamma) - R_GammaCorrect(allbuf + offset, memcount); - - ri.FS_WriteFile(fileName, buffer, memcount + 18); - - ri.Hunk_FreeTempMemory(allbuf); -} - -/* -================== -RB_TakeScreenshotJPEG -================== -*/ - -void RB_TakeScreenshotJPEG(int x, int y, int width, int height, char *fileName) -{ - byte *buffer; - size_t offset = 0, memcount; - int padlen; - - buffer = RB_ReadPixels(x, y, width, height, &offset, &padlen); - memcount = (width * 3 + padlen) * height; - - // gamma correct - if(glConfig.deviceSupportsGamma) - R_GammaCorrect(buffer + offset, memcount); - - RE_SaveJPG(fileName, r_screenshotJpegQuality->integer, width, height, buffer + offset, padlen); - ri.Hunk_FreeTempMemory(buffer); -} - -/* -================== -RB_TakeScreenshotCmd -================== -*/ -const void *RB_TakeScreenshotCmd( const void *data ) { - const screenshotCommand_t *cmd; - - cmd = (const screenshotCommand_t *)data; - - // finish any 2D drawing if needed - if(tess.numIndexes) - RB_EndSurface(); - - if (cmd->jpeg) - RB_TakeScreenshotJPEG( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName); - else - RB_TakeScreenshot( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName); - - return (const void *)(cmd + 1); -} - -/* -================== -R_TakeScreenshot -================== -*/ -void R_TakeScreenshot( int x, int y, int width, int height, char *name, qboolean jpeg ) { - static char fileName[MAX_OSPATH]; // bad things if two screenshots per frame? - screenshotCommand_t *cmd; - - cmd = R_GetCommandBuffer( sizeof( *cmd ) ); - if ( !cmd ) { - return; - } - cmd->commandId = RC_SCREENSHOT; - - cmd->x = x; - cmd->y = y; - cmd->width = width; - cmd->height = height; - Q_strncpyz( fileName, name, sizeof(fileName) ); - cmd->fileName = fileName; - cmd->jpeg = jpeg; -} - -/* -================== -R_ScreenshotFilename -================== -*/ -void R_ScreenshotFilename( int lastNumber, char *fileName ) { - int a,b,c,d; - - if ( lastNumber < 0 || lastNumber > 9999 ) { - Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot9999.tga" ); - return; - } - - a = lastNumber / 1000; - lastNumber -= a*1000; - b = lastNumber / 100; - lastNumber -= b*100; - c = lastNumber / 10; - lastNumber -= c*10; - d = lastNumber; - - Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot%i%i%i%i.tga" - , a, b, c, d ); -} - -/* -================== -R_ScreenshotFilename -================== -*/ -void R_ScreenshotFilenameJPEG( int lastNumber, char *fileName ) { - int a,b,c,d; - - if ( lastNumber < 0 || lastNumber > 9999 ) { - Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot9999.jpg" ); - return; - } - - a = lastNumber / 1000; - lastNumber -= a*1000; - b = lastNumber / 100; - lastNumber -= b*100; - c = lastNumber / 10; - lastNumber -= c*10; - d = lastNumber; - - Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot%i%i%i%i.jpg" - , a, b, c, d ); -} - -/* -==================== -R_LevelShot - -levelshots are specialized 128*128 thumbnails for -the menu system, sampled down from full screen distorted images -==================== -*/ -void R_LevelShot( void ) { - char checkname[MAX_OSPATH]; - byte *buffer; - byte *source, *allsource; - byte *src, *dst; - size_t offset = 0; - int padlen; - int x, y; - int r, g, b; - float xScale, yScale; - int xx, yy; - - Com_sprintf(checkname, sizeof(checkname), "levelshots/%s.tga", tr.world->baseName); - - allsource = RB_ReadPixels(0, 0, glConfig.vidWidth, glConfig.vidHeight, &offset, &padlen); - source = allsource + offset; - - buffer = ri.Hunk_AllocateTempMemory(128 * 128*3 + 18); - Com_Memset (buffer, 0, 18); - buffer[2] = 2; // uncompressed type - buffer[12] = 128; - buffer[14] = 128; - buffer[16] = 24; // pixel size - - // resample from source - xScale = glConfig.vidWidth / 512.0f; - yScale = glConfig.vidHeight / 384.0f; - for ( y = 0 ; y < 128 ; y++ ) { - for ( x = 0 ; x < 128 ; x++ ) { - r = g = b = 0; - for ( yy = 0 ; yy < 3 ; yy++ ) { - for ( xx = 0 ; xx < 4 ; xx++ ) { - src = source + (3 * glConfig.vidWidth + padlen) * (int)((y*3 + yy) * yScale) + - 3 * (int) ((x*4 + xx) * xScale); - r += src[0]; - g += src[1]; - b += src[2]; - } - } - dst = buffer + 18 + 3 * ( y * 128 + x ); - dst[0] = b / 12; - dst[1] = g / 12; - dst[2] = r / 12; - } - } - - // gamma correct - if ( glConfig.deviceSupportsGamma ) { - R_GammaCorrect( buffer + 18, 128 * 128 * 3 ); - } - - ri.FS_WriteFile( checkname, buffer, 128 * 128*3 + 18 ); - - ri.Hunk_FreeTempMemory(buffer); - ri.Hunk_FreeTempMemory(allsource); - - ri.Printf( PRINT_ALL, "Wrote %s\n", checkname ); -} - -/* -================== -R_ScreenShot_f - -screenshot -screenshot [silent] -screenshot [levelshot] -screenshot [filename] - -Doesn't print the pacifier message if there is a second arg -================== -*/ -void R_ScreenShot_f (void) { - char checkname[MAX_OSPATH]; - static int lastNumber = -1; - qboolean silent; - - if ( !strcmp( ri.Cmd_Argv(1), "levelshot" ) ) { - R_LevelShot(); - return; - } - - if ( !strcmp( ri.Cmd_Argv(1), "silent" ) ) { - silent = qtrue; - } else { - silent = qfalse; - } - - if ( ri.Cmd_Argc() == 2 && !silent ) { - // explicit filename - Com_sprintf( checkname, MAX_OSPATH, "screenshots/%s.tga", ri.Cmd_Argv( 1 ) ); - } else { - // scan for a free filename - - // if we have saved a previous screenshot, don't scan - // again, because recording demo avis can involve - // thousands of shots - if ( lastNumber == -1 ) { - lastNumber = 0; - } - // scan for a free number - for ( ; lastNumber <= 9999 ; lastNumber++ ) { - R_ScreenshotFilename( lastNumber, checkname ); - - if (!ri.FS_FileExists( checkname )) - { - break; // file doesn't exist - } - } - - if ( lastNumber >= 9999 ) { - ri.Printf (PRINT_ALL, "ScreenShot: Couldn't create a file\n"); - return; - } - - lastNumber++; - } - - R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, qfalse ); - - if ( !silent ) { - ri.Printf (PRINT_ALL, "Wrote %s\n", checkname); - } -} - -void R_ScreenShotJPEG_f (void) { - char checkname[MAX_OSPATH]; - static int lastNumber = -1; - qboolean silent; - - if ( !strcmp( ri.Cmd_Argv(1), "levelshot" ) ) { - R_LevelShot(); - return; - } - - if ( !strcmp( ri.Cmd_Argv(1), "silent" ) ) { - silent = qtrue; - } else { - silent = qfalse; - } - - if ( ri.Cmd_Argc() == 2 && !silent ) { - // explicit filename - Com_sprintf( checkname, MAX_OSPATH, "screenshots/%s.jpg", ri.Cmd_Argv( 1 ) ); - } else { - // scan for a free filename - - // if we have saved a previous screenshot, don't scan - // again, because recording demo avis can involve - // thousands of shots - if ( lastNumber == -1 ) { - lastNumber = 0; - } - // scan for a free number - for ( ; lastNumber <= 9999 ; lastNumber++ ) { - R_ScreenshotFilenameJPEG( lastNumber, checkname ); - - if (!ri.FS_FileExists( checkname )) - { - break; // file doesn't exist - } - } - - if ( lastNumber == 10000 ) { - ri.Printf (PRINT_ALL, "ScreenShot: Couldn't create a file\n"); - return; - } - - lastNumber++; - } - - R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, qtrue ); - - if ( !silent ) { - ri.Printf (PRINT_ALL, "Wrote %s\n", checkname); - } -} - -//============================================================================ - -/* -================== -RB_TakeVideoFrameCmd -================== -*/ -const void *RB_TakeVideoFrameCmd( const void *data ) -{ - const videoFrameCommand_t *cmd; - byte *cBuf; - size_t memcount, linelen; - int padwidth, avipadwidth, padlen, avipadlen; - GLint packAlign; - - // finish any 2D drawing if needed - if(tess.numIndexes) - RB_EndSurface(); - - cmd = (const videoFrameCommand_t *)data; - - qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); - - linelen = cmd->width * 3; - - // Alignment stuff for glReadPixels - padwidth = PAD(linelen, packAlign); - padlen = padwidth - linelen; - // AVI line padding - avipadwidth = PAD(linelen, AVI_LINE_PADDING); - avipadlen = avipadwidth - linelen; - - cBuf = PADP(cmd->captureBuffer, packAlign); - - qglReadPixels(0, 0, cmd->width, cmd->height, GL_RGB, - GL_UNSIGNED_BYTE, cBuf); - - memcount = padwidth * cmd->height; - - // gamma correct - if(glConfig.deviceSupportsGamma) - R_GammaCorrect(cBuf, memcount); - - if(cmd->motionJpeg) - { - memcount = RE_SaveJPGToBuffer(cmd->encodeBuffer, linelen * cmd->height, - r_aviMotionJpegQuality->integer, - cmd->width, cmd->height, cBuf, padlen); - ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, memcount); - } - else - { - byte *lineend, *memend; - byte *srcptr, *destptr; - - srcptr = cBuf; - destptr = cmd->encodeBuffer; - memend = srcptr + memcount; - - // swap R and B and remove line paddings - while(srcptr < memend) - { - lineend = srcptr + linelen; - while(srcptr < lineend) - { - *destptr++ = srcptr[2]; - *destptr++ = srcptr[1]; - *destptr++ = srcptr[0]; - srcptr += 3; - } - - Com_Memset(destptr, '\0', avipadlen); - destptr += avipadlen; - - srcptr += padlen; - } - - ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, avipadwidth * cmd->height); - } - - return (const void *)(cmd + 1); -} - -//============================================================================ - -/* -** GL_SetDefaultState -*/ -void GL_SetDefaultState( void ) -{ - qglClearDepth( 1.0f ); - - qglCullFace(GL_FRONT); - - qglColor4f (1,1,1,1); - - // initialize downstream texture unit if we're running - // in a multitexture environment - if ( qglActiveTextureARB ) { - GL_SelectTexture( 1 ); - GL_TextureMode( r_textureMode->string ); - GL_TexEnv( GL_MODULATE ); - qglDisable( GL_TEXTURE_2D ); - GL_SelectTexture( 0 ); - } - - qglEnable(GL_TEXTURE_2D); - GL_TextureMode( r_textureMode->string ); - GL_TexEnv( GL_MODULATE ); - - //qglShadeModel( GL_SMOOTH ); - qglDepthFunc( GL_LEQUAL ); - - // - // make sure our GL state vector is set correctly - // - glState.glStateBits = GLS_DEPTHTEST_DISABLE | GLS_DEPTHMASK_TRUE; - - glState.vertexAttribsState = 0; - glState.vertexAttribPointersSet = 0; - glState.currentProgram = 0; - qglUseProgramObjectARB(0); - - qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); - qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); - glState.currentVBO = NULL; - glState.currentIBO = NULL; - - qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); - qglDepthMask( GL_TRUE ); - qglDisable( GL_DEPTH_TEST ); - qglEnable( GL_SCISSOR_TEST ); - qglDisable( GL_CULL_FACE ); - qglDisable( GL_BLEND ); -} - -/* -================ -R_PrintLongString - -Workaround for ri.Printf's 1024 characters buffer limit. -================ -*/ -void R_PrintLongString(const char *string) { - char buffer[1024]; - const char *p; - int size = strlen(string); - - p = string; - while(size > 0) - { - Q_strncpyz(buffer, p, sizeof (buffer) ); - ri.Printf( PRINT_ALL, "%s", buffer ); - p += 1023; - size -= 1023; - } -} - -/* -================ -GfxInfo_f -================ -*/ -void GfxInfo_f( void ) -{ - const char *enablestrings[] = - { - "disabled", - "enabled" - }; - const char *fsstrings[] = - { - "windowed", - "fullscreen" - }; - - ri.Printf( PRINT_ALL, "\nGL_VENDOR: %s\n", glConfig.vendor_string ); - ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", glConfig.renderer_string ); - ri.Printf( PRINT_ALL, "GL_VERSION: %s\n", glConfig.version_string ); - ri.Printf( PRINT_ALL, "GL_EXTENSIONS: " ); - R_PrintLongString( glConfig.extensions_string ); - ri.Printf( PRINT_ALL, "\n" ); - ri.Printf( PRINT_ALL, "GL_MAX_TEXTURE_SIZE: %d\n", glConfig.maxTextureSize ); - ri.Printf( PRINT_ALL, "GL_MAX_TEXTURE_UNITS_ARB: %d\n", glConfig.numTextureUnits ); - ri.Printf( PRINT_ALL, "\nPIXELFORMAT: color(%d-bits) Z(%d-bit) stencil(%d-bits)\n", glConfig.colorBits, glConfig.depthBits, glConfig.stencilBits ); - ri.Printf( PRINT_ALL, "MODE: %d, %d x %d %s hz:", r_mode->integer, glConfig.vidWidth, glConfig.vidHeight, fsstrings[r_fullscreen->integer == 1] ); - if ( glConfig.displayFrequency ) - { - ri.Printf( PRINT_ALL, "%d\n", glConfig.displayFrequency ); - } - else - { - ri.Printf( PRINT_ALL, "N/A\n" ); - } - if ( glConfig.deviceSupportsGamma ) - { - ri.Printf( PRINT_ALL, "GAMMA: hardware w/ %d overbright bits\n", tr.overbrightBits ); - } - else - { - ri.Printf( PRINT_ALL, "GAMMA: software w/ %d overbright bits\n", tr.overbrightBits ); - } - - ri.Printf( PRINT_ALL, "texturemode: %s\n", r_textureMode->string ); - ri.Printf( PRINT_ALL, "picmip: %d\n", r_picmip->integer ); - ri.Printf( PRINT_ALL, "texture bits: %d\n", r_texturebits->integer ); - ri.Printf( PRINT_ALL, "multitexture: %s\n", enablestrings[qglActiveTextureARB != 0] ); - ri.Printf( PRINT_ALL, "compiled vertex arrays: %s\n", enablestrings[qglLockArraysEXT != 0 ] ); - ri.Printf( PRINT_ALL, "texenv add: %s\n", enablestrings[glConfig.textureEnvAddAvailable != 0] ); - ri.Printf( PRINT_ALL, "compressed textures: %s\n", enablestrings[glConfig.textureCompression!=TC_NONE] ); - if ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) - { - ri.Printf( PRINT_ALL, "HACK: using vertex lightmap approximation\n" ); - } - if ( glConfig.hardwareType == GLHW_RAGEPRO ) - { - ri.Printf( PRINT_ALL, "HACK: ragePro approximations\n" ); - } - if ( glConfig.hardwareType == GLHW_RIVA128 ) - { - ri.Printf( PRINT_ALL, "HACK: riva128 approximations\n" ); - } - if ( r_finish->integer ) { - ri.Printf( PRINT_ALL, "Forcing glFinish\n" ); - } -} - -/* -================ -GfxMemInfo_f -================ -*/ -void GfxMemInfo_f( void ) -{ - switch (glRefConfig.memInfo) - { - case MI_NONE: - { - ri.Printf(PRINT_ALL, "No extension found for GPU memory info.\n"); - } - break; - case MI_NVX: - { - int value; - - qglGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &value); - ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX: %ikb\n", value); - - qglGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &value); - ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX: %ikb\n", value); - - qglGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &value); - ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX: %ikb\n", value); - - qglGetIntegerv(GL_GPU_MEMORY_INFO_EVICTION_COUNT_NVX, &value); - ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_EVICTION_COUNT_NVX: %i\n", value); - - qglGetIntegerv(GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX, &value); - ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_EVICTED_MEMORY_NVX: %ikb\n", value); - } - break; - case MI_ATI: - { - // GL_ATI_meminfo - int value[4]; - - qglGetIntegerv(GL_VBO_FREE_MEMORY_ATI, &value[0]); - ri.Printf(PRINT_ALL, "VBO_FREE_MEMORY_ATI: %ikb total %ikb largest aux: %ikb total %ikb largest\n", value[0], value[1], value[2], value[3]); - - qglGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, &value[0]); - ri.Printf(PRINT_ALL, "TEXTURE_FREE_MEMORY_ATI: %ikb total %ikb largest aux: %ikb total %ikb largest\n", value[0], value[1], value[2], value[3]); - - qglGetIntegerv(GL_RENDERBUFFER_FREE_MEMORY_ATI, &value[0]); - ri.Printf(PRINT_ALL, "RENDERBUFFER_FREE_MEMORY_ATI: %ikb total %ikb largest aux: %ikb total %ikb largest\n", value[0], value[1], value[2], value[3]); - } - break; - } -} - -/* -=============== -R_Register -=============== -*/ -void R_Register( void ) -{ - #ifdef USE_RENDERER_DLOPEN - com_altivec = ri.Cvar_Get("com_altivec", "1", CVAR_ARCHIVE); - #endif - - // - // latched and archived variables - // - r_allowExtensions = ri.Cvar_Get( "r_allowExtensions", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_ext_compressed_textures = ri.Cvar_Get( "r_ext_compressed_textures", "0", CVAR_ARCHIVE | CVAR_LATCH ); - r_ext_multitexture = ri.Cvar_Get( "r_ext_multitexture", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_ext_compiled_vertex_array = ri.Cvar_Get( "r_ext_compiled_vertex_array", "1", CVAR_ARCHIVE | CVAR_LATCH); - r_ext_texture_env_add = ri.Cvar_Get( "r_ext_texture_env_add", "1", CVAR_ARCHIVE | CVAR_LATCH); - - r_ext_draw_range_elements = ri.Cvar_Get( "r_ext_draw_range_elements", "1", CVAR_ARCHIVE | CVAR_LATCH); - r_ext_multi_draw_arrays = ri.Cvar_Get( "r_ext_multi_draw_arrays", "1", CVAR_ARCHIVE | CVAR_LATCH); - r_ext_framebuffer_object = ri.Cvar_Get( "r_ext_framebuffer_object", "1", CVAR_ARCHIVE | CVAR_LATCH); - r_ext_texture_float = ri.Cvar_Get( "r_ext_texture_float", "1", CVAR_ARCHIVE | CVAR_LATCH); - r_arb_half_float_pixel = ri.Cvar_Get( "r_arb_half_float_pixel", "1", CVAR_ARCHIVE | CVAR_LATCH); - r_ext_framebuffer_multisample = ri.Cvar_Get( "r_ext_framebuffer_multisample", "0", CVAR_ARCHIVE | CVAR_LATCH); - - r_ext_texture_filter_anisotropic = ri.Cvar_Get( "r_ext_texture_filter_anisotropic", - "0", CVAR_ARCHIVE | CVAR_LATCH ); - r_ext_max_anisotropy = ri.Cvar_Get( "r_ext_max_anisotropy", "2", CVAR_ARCHIVE | CVAR_LATCH ); - - r_picmip = ri.Cvar_Get ("r_picmip", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_roundImagesDown = ri.Cvar_Get ("r_roundImagesDown", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_colorMipLevels = ri.Cvar_Get ("r_colorMipLevels", "0", CVAR_LATCH ); - ri.Cvar_CheckRange( r_picmip, 0, 16, qtrue ); - r_detailTextures = ri.Cvar_Get( "r_detailtextures", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_texturebits = ri.Cvar_Get( "r_texturebits", "0", CVAR_ARCHIVE | CVAR_LATCH ); - r_colorbits = ri.Cvar_Get( "r_colorbits", "0", CVAR_ARCHIVE | CVAR_LATCH ); - r_stencilbits = ri.Cvar_Get( "r_stencilbits", "8", CVAR_ARCHIVE | CVAR_LATCH ); - r_depthbits = ri.Cvar_Get( "r_depthbits", "0", CVAR_ARCHIVE | CVAR_LATCH ); - r_ext_multisample = ri.Cvar_Get( "r_ext_multisample", "0", CVAR_ARCHIVE | CVAR_LATCH ); - ri.Cvar_CheckRange( r_ext_multisample, 0, 4, qtrue ); - r_overBrightBits = ri.Cvar_Get ("r_overBrightBits", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_ignorehwgamma = ri.Cvar_Get( "r_ignorehwgamma", "0", CVAR_ARCHIVE | CVAR_LATCH); - r_mode = ri.Cvar_Get( "r_mode", "-2", CVAR_ARCHIVE | CVAR_LATCH ); - r_fullscreen = ri.Cvar_Get( "r_fullscreen", "1", CVAR_ARCHIVE ); - r_noborder = ri.Cvar_Get("r_noborder", "0", CVAR_ARCHIVE); - r_width = ri.Cvar_Get( "r_width", "640", CVAR_ARCHIVE | CVAR_LATCH ); - r_height = ri.Cvar_Get( "r_height", "480", CVAR_ARCHIVE | CVAR_LATCH ); - r_pixelAspect = ri.Cvar_Get( "r_pixelAspect", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_simpleMipMaps = ri.Cvar_Get( "r_simpleMipMaps", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_vertexLight = ri.Cvar_Get( "r_vertexLight", "0", CVAR_ARCHIVE | CVAR_LATCH ); - r_uiFullScreen = ri.Cvar_Get( "r_uifullscreen", "0", 0); - r_subdivisions = ri.Cvar_Get ("r_subdivisions", "4", CVAR_ARCHIVE | CVAR_LATCH); - r_stereoEnabled = ri.Cvar_Get( "r_stereoEnabled", "0", CVAR_ARCHIVE | CVAR_LATCH); - r_greyscale = ri.Cvar_Get("r_greyscale", "0", CVAR_ARCHIVE | CVAR_LATCH); - ri.Cvar_CheckRange(r_greyscale, 0, 1, qfalse); - - r_softOverbright = ri.Cvar_Get( "r_softOverbright", "1", CVAR_ARCHIVE | CVAR_LATCH ); - - r_hdr = ri.Cvar_Get( "r_hdr", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_postProcess = ri.Cvar_Get( "r_postProcess", "1", CVAR_ARCHIVE ); - - r_toneMap = ri.Cvar_Get( "r_toneMap", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_forceToneMap = ri.Cvar_Get( "r_forceToneMap", "0", CVAR_CHEAT ); - r_forceToneMapMin = ri.Cvar_Get( "r_forceToneMapMin", "-8.0", CVAR_CHEAT ); - r_forceToneMapAvg = ri.Cvar_Get( "r_forceToneMapAvg", "-2.0", CVAR_CHEAT ); - r_forceToneMapMax = ri.Cvar_Get( "r_forceToneMapMax", "0.0", CVAR_CHEAT ); - - r_autoExposure = ri.Cvar_Get( "r_autoExposure", "1", CVAR_ARCHIVE ); - r_forceAutoExposure = ri.Cvar_Get( "r_forceAutoExposure", "0", CVAR_CHEAT ); - r_forceAutoExposureMin = ri.Cvar_Get( "r_forceAutoExposureMin", "-2.0", CVAR_CHEAT ); - r_forceAutoExposureMax = ri.Cvar_Get( "r_forceAutoExposureMax", "2.0", CVAR_CHEAT ); - - r_cameraExposure = ri.Cvar_Get( "r_cameraExposure", "0", CVAR_CHEAT ); - - r_srgb = ri.Cvar_Get( "r_srgb", "0", CVAR_ARCHIVE | CVAR_LATCH ); - - r_depthPrepass = ri.Cvar_Get( "r_depthPrepass", "1", CVAR_ARCHIVE ); - r_ssao = ri.Cvar_Get( "r_ssao", "0", CVAR_LATCH | CVAR_ARCHIVE ); - - r_normalMapping = ri.Cvar_Get( "r_normalMapping", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_specularMapping = ri.Cvar_Get( "r_specularMapping", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_deluxeMapping = ri.Cvar_Get( "r_deluxeMapping", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_parallaxMapping = ri.Cvar_Get( "r_parallaxMapping", "0", CVAR_ARCHIVE | CVAR_LATCH ); - r_normalAmbient = ri.Cvar_Get( "r_normalAmbient", "0", CVAR_ARCHIVE | CVAR_LATCH ); - r_dlightMode = ri.Cvar_Get( "r_dlightMode", "0", CVAR_ARCHIVE | CVAR_LATCH ); - r_pshadowDist = ri.Cvar_Get( "r_pshadowDist", "128", CVAR_ARCHIVE ); - r_recalcMD3Normals = ri.Cvar_Get( "r_recalcMD3Normals", "0", CVAR_ARCHIVE | CVAR_LATCH ); - r_mergeLightmaps = ri.Cvar_Get( "r_mergeLightmaps", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_imageUpsample = ri.Cvar_Get( "r_imageUpsample", "0", CVAR_ARCHIVE | CVAR_LATCH ); - r_imageUpsampleMaxSize = ri.Cvar_Get( "r_imageUpsampleMaxSize", "1024", CVAR_ARCHIVE | CVAR_LATCH ); - r_imageUpsampleType = ri.Cvar_Get( "r_imageUpsampleType", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_genNormalMaps = ri.Cvar_Get( "r_genNormalMaps", "0", CVAR_ARCHIVE | CVAR_LATCH ); - - r_forceSun = ri.Cvar_Get( "r_forceSun", "0", CVAR_CHEAT ); - r_forceSunMapLightScale = ri.Cvar_Get( "r_forceSunMapLightScale", "0.5", CVAR_CHEAT ); - r_forceSunLightScale = ri.Cvar_Get( "r_forceSunLightScale", "0.5", CVAR_CHEAT ); - r_forceSunAmbientScale = ri.Cvar_Get( "r_forceSunAmbientScale", "0.2", CVAR_CHEAT ); - r_drawSunRays = ri.Cvar_Get( "r_drawSunRays", "0", CVAR_ARCHIVE | CVAR_LATCH ); - - r_sunShadows = ri.Cvar_Get( "r_sunShadows", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_shadowFilter = ri.Cvar_Get( "r_shadowFilter", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_shadowMapSize = ri.Cvar_Get( "r_shadowMapSize", "1024", CVAR_ARCHIVE | CVAR_LATCH ); - r_shadowCascadeZNear = ri.Cvar_Get( "r_shadowCascadeZNear", "4", CVAR_ARCHIVE | CVAR_LATCH ); - r_shadowCascadeZFar = ri.Cvar_Get( "r_shadowCascadeZFar", "3072", CVAR_ARCHIVE | CVAR_LATCH ); - r_shadowCascadeZBias = ri.Cvar_Get( "r_shadowCascadeZBias", "-320", CVAR_ARCHIVE | CVAR_LATCH ); - - // - // temporary latched variables that can only change over a restart - // - r_fullbright = ri.Cvar_Get ("r_fullbright", "0", CVAR_LATCH|CVAR_CHEAT ); - r_mapOverBrightBits = ri.Cvar_Get ("r_mapOverBrightBits", "2", CVAR_LATCH ); - r_intensity = ri.Cvar_Get ("r_intensity", "1", CVAR_LATCH ); - r_singleShader = ri.Cvar_Get ("r_singleShader", "0", CVAR_CHEAT | CVAR_LATCH ); - - // - // archived variables that can change at any time - // - r_lodCurveError = ri.Cvar_Get( "r_lodCurveError", "250", CVAR_ARCHIVE|CVAR_CHEAT ); - r_lodbias = ri.Cvar_Get( "r_lodbias", "0", CVAR_ARCHIVE ); - r_flares = ri.Cvar_Get ("r_flares", "0", CVAR_ARCHIVE ); - r_znear = ri.Cvar_Get( "r_znear", "4", CVAR_CHEAT ); - ri.Cvar_CheckRange( r_znear, 0.001f, 200, qfalse ); - r_zproj = ri.Cvar_Get( "r_zproj", "64", CVAR_ARCHIVE ); - r_stereoSeparation = ri.Cvar_Get( "r_stereoSeparation", "64", CVAR_ARCHIVE ); - r_ignoreGLErrors = ri.Cvar_Get( "r_ignoreGLErrors", "1", CVAR_ARCHIVE ); - r_fastsky = ri.Cvar_Get( "r_fastsky", "0", CVAR_ARCHIVE ); - r_inGameVideo = ri.Cvar_Get( "r_inGameVideo", "1", CVAR_ARCHIVE ); - r_drawSun = ri.Cvar_Get( "r_drawSun", "0", CVAR_ARCHIVE ); - r_dynamiclight = ri.Cvar_Get( "r_dynamiclight", "1", CVAR_ARCHIVE ); - r_dlightBacks = ri.Cvar_Get( "r_dlightBacks", "1", CVAR_ARCHIVE ); - r_finish = ri.Cvar_Get ("r_finish", "0", CVAR_ARCHIVE); - r_textureMode = ri.Cvar_Get( "r_textureMode", "GL_LINEAR_MIPMAP_NEAREST", CVAR_ARCHIVE ); - r_swapInterval = ri.Cvar_Get( "r_swapInterval", "0", - CVAR_ARCHIVE | CVAR_LATCH ); - r_gamma = ri.Cvar_Get( "r_gamma", "1", CVAR_ARCHIVE ); - r_facePlaneCull = ri.Cvar_Get ("r_facePlaneCull", "1", CVAR_ARCHIVE ); - - r_railWidth = ri.Cvar_Get( "r_railWidth", "16", CVAR_ARCHIVE ); - r_railCoreWidth = ri.Cvar_Get( "r_railCoreWidth", "6", CVAR_ARCHIVE ); - r_railSegmentLength = ri.Cvar_Get( "r_railSegmentLength", "32", CVAR_ARCHIVE ); - - r_ambientScale = ri.Cvar_Get( "r_ambientScale", "0.6", CVAR_CHEAT ); - r_directedScale = ri.Cvar_Get( "r_directedScale", "1", CVAR_CHEAT ); - - r_anaglyphMode = ri.Cvar_Get("r_anaglyphMode", "0", CVAR_ARCHIVE); - r_mergeMultidraws = ri.Cvar_Get("r_mergeMultidraws", "1", CVAR_ARCHIVE); - r_mergeLeafSurfaces = ri.Cvar_Get("r_mergeLeafSurfaces", "1", CVAR_ARCHIVE); - - // - // temporary variables that can change at any time - // - r_showImages = ri.Cvar_Get( "r_showImages", "0", CVAR_TEMP ); - - r_debugLight = ri.Cvar_Get( "r_debuglight", "0", CVAR_TEMP ); - r_debugSort = ri.Cvar_Get( "r_debugSort", "0", CVAR_CHEAT ); - r_printShaders = ri.Cvar_Get( "r_printShaders", "0", 0 ); - r_saveFontData = ri.Cvar_Get( "r_saveFontData", "0", 0 ); - - r_nocurves = ri.Cvar_Get ("r_nocurves", "0", CVAR_CHEAT ); - r_drawworld = ri.Cvar_Get ("r_drawworld", "1", CVAR_CHEAT ); - r_lightmap = ri.Cvar_Get ("r_lightmap", "0", 0 ); - r_portalOnly = ri.Cvar_Get ("r_portalOnly", "0", CVAR_CHEAT ); - - r_flareSize = ri.Cvar_Get ("r_flareSize", "40", CVAR_CHEAT); - r_flareFade = ri.Cvar_Get ("r_flareFade", "7", CVAR_CHEAT); - r_flareCoeff = ri.Cvar_Get ("r_flareCoeff", FLARE_STDCOEFF, CVAR_CHEAT); - - r_skipBackEnd = ri.Cvar_Get ("r_skipBackEnd", "0", CVAR_CHEAT); - - r_measureOverdraw = ri.Cvar_Get( "r_measureOverdraw", "0", CVAR_CHEAT ); - r_lodscale = ri.Cvar_Get( "r_lodscale", "5", CVAR_CHEAT ); - r_norefresh = ri.Cvar_Get ("r_norefresh", "0", CVAR_CHEAT); - r_drawentities = ri.Cvar_Get ("r_drawentities", "1", CVAR_CHEAT ); - r_ignore = ri.Cvar_Get( "r_ignore", "1", CVAR_CHEAT ); - r_nocull = ri.Cvar_Get ("r_nocull", "0", CVAR_CHEAT); - r_novis = ri.Cvar_Get ("r_novis", "0", CVAR_CHEAT); - r_showcluster = ri.Cvar_Get ("r_showcluster", "0", CVAR_CHEAT); - r_speeds = ri.Cvar_Get ("r_speeds", "0", CVAR_CHEAT); - r_verbose = ri.Cvar_Get( "r_verbose", "0", CVAR_CHEAT ); - r_logFile = ri.Cvar_Get( "r_logFile", "0", CVAR_CHEAT ); - r_debugSurface = ri.Cvar_Get ("r_debugSurface", "0", CVAR_CHEAT); - r_nobind = ri.Cvar_Get ("r_nobind", "0", CVAR_CHEAT); - r_showtris = ri.Cvar_Get ("r_showtris", "0", CVAR_CHEAT); - r_showsky = ri.Cvar_Get ("r_showsky", "0", CVAR_CHEAT); - r_shownormals = ri.Cvar_Get ("r_shownormals", "0", CVAR_CHEAT); - r_clear = ri.Cvar_Get ("r_clear", "0", CVAR_CHEAT); - r_offsetFactor = ri.Cvar_Get( "r_offsetfactor", "-1", CVAR_CHEAT ); - r_offsetUnits = ri.Cvar_Get( "r_offsetunits", "-2", CVAR_CHEAT ); - r_drawBuffer = ri.Cvar_Get( "r_drawBuffer", "GL_BACK", CVAR_CHEAT ); - r_lockpvs = ri.Cvar_Get ("r_lockpvs", "0", CVAR_CHEAT); - r_noportals = ri.Cvar_Get ("r_noportals", "0", CVAR_CHEAT); - r_shadows = ri.Cvar_Get( "cg_shadows", "1", 0 ); - - r_marksOnTriangleMeshes = ri.Cvar_Get("r_marksOnTriangleMeshes", "0", CVAR_ARCHIVE); - - r_aviMotionJpegQuality = ri.Cvar_Get("r_aviMotionJpegQuality", "90", CVAR_ARCHIVE); - r_screenshotJpegQuality = ri.Cvar_Get("r_screenshotJpegQuality", "90", CVAR_ARCHIVE); - - r_maxpolys = ri.Cvar_Get( "r_maxpolys", va("%d", MAX_POLYS), 0); - r_maxpolyverts = ri.Cvar_Get( "r_maxpolyverts", va("%d", MAX_POLYVERTS), 0); - - // make sure all the commands added here are also - // removed in R_Shutdown - ri.Cmd_AddCommand( "imagelist", R_ImageList_f ); - ri.Cmd_AddCommand( "shaderlist", R_ShaderList_f ); - ri.Cmd_AddCommand( "skinlist", R_SkinList_f ); - ri.Cmd_AddCommand( "modellist", R_Modellist_f ); - ri.Cmd_AddCommand( "screenshot", R_ScreenShot_f ); - ri.Cmd_AddCommand( "screenshotJPEG", R_ScreenShotJPEG_f ); - ri.Cmd_AddCommand( "gfxinfo", GfxInfo_f ); - ri.Cmd_AddCommand( "minimize", GLimp_Minimize ); - ri.Cmd_AddCommand( "gfxmeminfo", GfxMemInfo_f ); -} - -void R_InitQueries(void) -{ - if (!glRefConfig.occlusionQuery) - return; - - if (r_drawSunRays->integer) - qglGenQueriesARB(ARRAY_LEN(tr.sunFlareQuery), tr.sunFlareQuery); -} - -void R_ShutDownQueries(void) -{ - if (!glRefConfig.occlusionQuery) - return; - - if (r_drawSunRays->integer) - qglDeleteQueriesARB(ARRAY_LEN(tr.sunFlareQuery), tr.sunFlareQuery); -} - -/* -=============== -R_Init -=============== -*/ -void R_Init( void ) { - int err; - int i; - byte *ptr; - - ri.Printf( PRINT_ALL, "----- R_Init -----\n" ); - - // clear all our internal state - Com_Memset( &tr, 0, sizeof( tr ) ); - Com_Memset( &backEnd, 0, sizeof( backEnd ) ); - Com_Memset( &tess, 0, sizeof( tess ) ); - -// Swap_Init(); - - if ( (intptr_t)tess.xyz & 15 ) { - ri.Printf( PRINT_WARNING, "tess.xyz not 16 byte aligned\n" ); - } - //Com_Memset( tess.constantColor255, 255, sizeof( tess.constantColor255 ) ); - - // - // init function tables - // - for ( i = 0; i < FUNCTABLE_SIZE; i++ ) - { - tr.sinTable[i] = sin( DEG2RAD( i * 360.0f / ( ( float ) ( FUNCTABLE_SIZE - 1 ) ) ) ); - tr.squareTable[i] = ( i < FUNCTABLE_SIZE/2 ) ? 1.0f : -1.0f; - tr.sawToothTable[i] = (float)i / FUNCTABLE_SIZE; - tr.inverseSawToothTable[i] = 1.0f - tr.sawToothTable[i]; - - if ( i < FUNCTABLE_SIZE / 2 ) - { - if ( i < FUNCTABLE_SIZE / 4 ) - { - tr.triangleTable[i] = ( float ) i / ( FUNCTABLE_SIZE / 4 ); - } - else - { - tr.triangleTable[i] = 1.0f - tr.triangleTable[i-FUNCTABLE_SIZE / 4]; - } - } - else - { - tr.triangleTable[i] = -tr.triangleTable[i-FUNCTABLE_SIZE/2]; - } - } - - R_InitFogTable(); - - R_NoiseInit(); - - R_Register(); - - max_polys = r_maxpolys->integer; - if (max_polys < MAX_POLYS) - max_polys = MAX_POLYS; - - max_polyverts = r_maxpolyverts->integer; - if (max_polyverts < MAX_POLYVERTS) - max_polyverts = MAX_POLYVERTS; - - ptr = ri.Hunk_Alloc( sizeof( *backEndData ) + sizeof(srfPoly_t) * max_polys + sizeof(polyVert_t) * max_polyverts, h_low); - backEndData = (backEndData_t *) ptr; - backEndData->polys = (srfPoly_t *) ((char *) ptr + sizeof( *backEndData )); - backEndData->polyVerts = (polyVert_t *) ((char *) ptr + sizeof( *backEndData ) + sizeof(srfPoly_t) * max_polys); - R_InitNextFrame(); - - InitOpenGL(); - - R_InitImages(); - - if (glRefConfig.framebufferObject) - FBO_Init(); - - GLSL_InitGPUShaders(); - - R_InitVBOs(); - - R_InitShaders(); - - R_InitSkins(); - - R_ModelInit(); - - R_InitFreeType(); - - R_InitQueries(); - - - err = qglGetError(); - if ( err != GL_NO_ERROR ) - ri.Printf (PRINT_ALL, "glGetError() = 0x%x\n", err); - - // print info - GfxInfo_f(); - ri.Printf( PRINT_ALL, "----- finished R_Init -----\n" ); -} - -/* -=============== -RE_Shutdown -=============== -*/ -void RE_Shutdown( qboolean destroyWindow ) { - - ri.Printf( PRINT_ALL, "RE_Shutdown( %i )\n", destroyWindow ); - - ri.Cmd_RemoveCommand ("modellist"); - ri.Cmd_RemoveCommand ("screenshotJPEG"); - ri.Cmd_RemoveCommand ("screenshot"); - ri.Cmd_RemoveCommand ("imagelist"); - ri.Cmd_RemoveCommand ("shaderlist"); - ri.Cmd_RemoveCommand ("skinlist"); - ri.Cmd_RemoveCommand ("gfxinfo"); - ri.Cmd_RemoveCommand("minimize"); - ri.Cmd_RemoveCommand( "shaderstate" ); - ri.Cmd_RemoveCommand( "gfxmeminfo" ); - - - if ( tr.registered ) { - R_IssuePendingRenderCommands(); - R_ShutDownQueries(); - if (glRefConfig.framebufferObject) - FBO_Shutdown(); - R_DeleteTextures(); - R_ShutdownVBOs(); - GLSL_ShutdownGPUShaders(); - } - - R_DoneFreeType(); - - // shut down platform specific OpenGL stuff - if ( destroyWindow ) { - GLimp_Shutdown(); - } - - tr.registered = qfalse; -} - - -/* -============= -RE_EndRegistration - -Touch all images to make sure they are resident -============= -*/ -void RE_EndRegistration( void ) { - R_IssuePendingRenderCommands(); - if (!ri.Sys_LowPhysicalMemory()) { - RB_ShowImages(); - } -} - - -/* -@@@@@@@@@@@@@@@@@@@@@ -GetRefAPI - -@@@@@@@@@@@@@@@@@@@@@ -*/ -#ifdef USE_RENDERER_DLOPEN -Q_EXPORT refexport_t* QDECL GetRefAPI ( int apiVersion, refimport_t *rimp ) { -#else -refexport_t *GetRefAPI ( int apiVersion, refimport_t *rimp ) { -#endif - - static refexport_t re; - - ri = *rimp; - - Com_Memset( &re, 0, sizeof( re ) ); - - if ( apiVersion != REF_API_VERSION ) { - ri.Printf(PRINT_ALL, "Mismatched REF_API_VERSION: expected %i, got %i\n", - REF_API_VERSION, apiVersion ); - return NULL; - } - - // the RE_ functions are Renderer Entry points - - re.Shutdown = RE_Shutdown; - - re.BeginRegistration = RE_BeginRegistration; - re.RegisterModel = RE_RegisterModel; - re.RegisterSkin = RE_RegisterSkin; - re.RegisterShader = RE_RegisterShader; - re.RegisterShaderNoMip = RE_RegisterShaderNoMip; - re.LoadWorld = RE_LoadWorldMap; - re.SetWorldVisData = RE_SetWorldVisData; - re.EndRegistration = RE_EndRegistration; - - re.BeginFrame = RE_BeginFrame; - re.EndFrame = RE_EndFrame; - - re.MarkFragments = R_MarkFragments; - re.LerpTag = R_LerpTag; - re.ModelBounds = R_ModelBounds; - - re.ClearScene = RE_ClearScene; - re.AddRefEntityToScene = RE_AddRefEntityToScene; - re.AddPolyToScene = RE_AddPolyToScene; - re.LightForPoint = R_LightForPoint; - re.AddLightToScene = RE_AddLightToScene; - re.AddAdditiveLightToScene = RE_AddAdditiveLightToScene; - re.RenderScene = RE_RenderScene; - - re.SetColor = RE_SetColor; - re.SetClipRegion = RE_SetClipRegion; - re.DrawStretchPic = RE_StretchPic; - re.DrawStretchRaw = RE_StretchRaw; - re.UploadCinematic = RE_UploadCinematic; - - re.RegisterFont = RE_RegisterFont; - re.RemapShader = R_RemapShader; - re.GetEntityToken = R_GetEntityToken; - re.inPVS = R_inPVS; - - re.TakeVideoFrame = RE_TakeVideoFrame; - - return &re; -} diff --git a/src/rend2/tr_light.c b/src/rend2/tr_light.c deleted file mode 100644 index 4e90ce20..00000000 --- a/src/rend2/tr_light.c +++ /dev/null @@ -1,447 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_light.c - -#include "tr_local.h" - -#define DLIGHT_AT_RADIUS 16 -// at the edge of a dlight's influence, this amount of light will be added - -#define DLIGHT_MINIMUM_RADIUS 16 -// never calculate a range less than this to prevent huge light numbers - - -/* -=============== -R_TransformDlights - -Transforms the origins of an array of dlights. -Used by both the front end (for DlightBmodel) and -the back end (before doing the lighting calculation) -=============== -*/ -void R_TransformDlights( int count, dlight_t *dl, orientationr_t *or) { - int i; - vec3_t temp; - - for ( i = 0 ; i < count ; i++, dl++ ) { - VectorSubtract( dl->origin, or->origin, temp ); - dl->transformed[0] = DotProduct( temp, or->axis[0] ); - dl->transformed[1] = DotProduct( temp, or->axis[1] ); - dl->transformed[2] = DotProduct( temp, or->axis[2] ); - } -} - -/* -============= -R_DlightBmodel - -Determine which dynamic lights may effect this bmodel -============= -*/ -void R_DlightBmodel( bmodel_t *bmodel ) { - int i, j; - dlight_t *dl; - int mask; - msurface_t *surf; - - // transform all the lights - R_TransformDlights( tr.refdef.num_dlights, tr.refdef.dlights, &tr.or ); - - mask = 0; - for ( i=0 ; itransformed[j] - bmodel->bounds[1][j] > dl->radius ) { - break; - } - if ( bmodel->bounds[0][j] - dl->transformed[j] > dl->radius ) { - break; - } - } - if ( j < 3 ) { - continue; - } - - // we need to check this light - mask |= 1 << i; - } - - tr.currentEntity->needDlights = (mask != 0); - - // set the dlight bits in all the surfaces - for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) { - surf = tr.world->surfaces + bmodel->firstSurface + i; - - if ( *surf->data == SF_FACE ) { - ((srfSurfaceFace_t *)surf->data)->dlightBits = mask; - } else if ( *surf->data == SF_GRID ) { - ((srfGridMesh_t *)surf->data)->dlightBits = mask; - } else if ( *surf->data == SF_TRIANGLES ) { - ((srfTriangles_t *)surf->data)->dlightBits = mask; - } - } -} - - -/* -============================================================================= - -LIGHT SAMPLING - -============================================================================= -*/ - -extern cvar_t *r_ambientScale; -extern cvar_t *r_directedScale; -extern cvar_t *r_debugLight; - -/* -================= -R_SetupEntityLightingGrid - -================= -*/ -static void R_SetupEntityLightingGrid( trRefEntity_t *ent, world_t *world ) { - vec3_t lightOrigin; - int pos[3]; - int i, j; - byte *gridData; - float frac[3]; - int gridStep[3]; - vec3_t direction; - float totalFactor; - - if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) { - // seperate lightOrigins are needed so an object that is - // sinking into the ground can still be lit, and so - // multi-part models can be lit identically - VectorCopy( ent->e.lightingOrigin, lightOrigin ); - } else { - VectorCopy( ent->e.origin, lightOrigin ); - } - - VectorSubtract( lightOrigin, world->lightGridOrigin, lightOrigin ); - for ( i = 0 ; i < 3 ; i++ ) { - float v; - - v = lightOrigin[i]*world->lightGridInverseSize[i]; - pos[i] = floor( v ); - frac[i] = v - pos[i]; - if ( pos[i] < 0 ) { - pos[i] = 0; - } else if ( pos[i] >= world->lightGridBounds[i] - 1 ) { - pos[i] = world->lightGridBounds[i] - 1; - } - } - - VectorClear( ent->ambientLight ); - VectorClear( ent->directedLight ); - VectorClear( direction ); - - assert( world->lightGridData ); // NULL with -nolight maps - - // trilerp the light value - gridStep[0] = 8; - gridStep[1] = 8 * world->lightGridBounds[0]; - gridStep[2] = 8 * world->lightGridBounds[0] * world->lightGridBounds[1]; - gridData = world->lightGridData + pos[0] * gridStep[0] - + pos[1] * gridStep[1] + pos[2] * gridStep[2]; - - totalFactor = 0; - for ( i = 0 ; i < 8 ; i++ ) { - float factor; - byte *data; - int lat, lng; - vec3_t normal; - qboolean ignore; - #if idppc - float d0, d1, d2, d3, d4, d5; - #endif - factor = 1.0; - data = gridData; - ignore = qfalse; - for ( j = 0 ; j < 3 ; j++ ) { - if ( i & (1<= world->lightGridBounds[j] - 1) - { - ignore = qtrue; // ignore values outside lightgrid - } - factor *= frac[j]; - data += gridStep[j]; - } else { - factor *= (1.0f - frac[j]); - } - } - - if ( ignore ) - continue; - - if (world->hdrLightGrid) - { - float *hdrData = world->hdrLightGrid + (int)(data - world->lightGridData) / 8 * 6; - if (!(hdrData[0]+hdrData[1]+hdrData[2]+hdrData[3]+hdrData[4]+hdrData[5]) ) { - continue; // ignore samples in walls - } - } - else - { - if (!(data[0]+data[1]+data[2]+data[3]+data[4]+data[5]) ) { - continue; // ignore samples in walls - } - } - totalFactor += factor; - #if idppc - d0 = data[0]; d1 = data[1]; d2 = data[2]; - d3 = data[3]; d4 = data[4]; d5 = data[5]; - - ent->ambientLight[0] += factor * d0; - ent->ambientLight[1] += factor * d1; - ent->ambientLight[2] += factor * d2; - - ent->directedLight[0] += factor * d3; - ent->directedLight[1] += factor * d4; - ent->directedLight[2] += factor * d5; - #else - if (world->hdrLightGrid) - { - // FIXME: this is hideous - float *hdrData = world->hdrLightGrid + (int)(data - world->lightGridData) / 8 * 6; - - ent->ambientLight[0] += factor * hdrData[0]; - ent->ambientLight[1] += factor * hdrData[1]; - ent->ambientLight[2] += factor * hdrData[2]; - - ent->directedLight[0] += factor * hdrData[3]; - ent->directedLight[1] += factor * hdrData[4]; - ent->directedLight[2] += factor * hdrData[5]; - } - else - { - ent->ambientLight[0] += factor * data[0]; - ent->ambientLight[1] += factor * data[1]; - ent->ambientLight[2] += factor * data[2]; - - ent->directedLight[0] += factor * data[3]; - ent->directedLight[1] += factor * data[4]; - ent->directedLight[2] += factor * data[5]; - } - #endif - lat = data[7]; - lng = data[6]; - lat *= (FUNCTABLE_SIZE/256); - lng *= (FUNCTABLE_SIZE/256); - - // decode X as cos( lat ) * sin( long ) - // decode Y as sin( lat ) * sin( long ) - // decode Z as cos( long ) - - normal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; - normal[1] = tr.sinTable[lat] * tr.sinTable[lng]; - normal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; - - VectorMA( direction, factor, normal, direction ); - } - - if ( totalFactor > 0 && totalFactor < 0.99 ) { - totalFactor = 1.0f / totalFactor; - VectorScale( ent->ambientLight, totalFactor, ent->ambientLight ); - VectorScale( ent->directedLight, totalFactor, ent->directedLight ); - } - - VectorScale( ent->ambientLight, r_ambientScale->value, ent->ambientLight ); - VectorScale( ent->directedLight, r_directedScale->value, ent->directedLight ); - - VectorNormalize2( direction, ent->lightDir ); -} - - -/* -=============== -LogLight -=============== -*/ -static void LogLight( trRefEntity_t *ent ) { - int max1, max2; - - if ( !(ent->e.renderfx & RF_FIRST_PERSON ) ) { - return; - } - - max1 = ent->ambientLight[0]; - if ( ent->ambientLight[1] > max1 ) { - max1 = ent->ambientLight[1]; - } else if ( ent->ambientLight[2] > max1 ) { - max1 = ent->ambientLight[2]; - } - - max2 = ent->directedLight[0]; - if ( ent->directedLight[1] > max2 ) { - max2 = ent->directedLight[1]; - } else if ( ent->directedLight[2] > max2 ) { - max2 = ent->directedLight[2]; - } - - ri.Printf( PRINT_ALL, "amb:%i dir:%i\n", max1, max2 ); -} - -/* -================= -R_SetupEntityLighting - -Calculates all the lighting values that will be used -by the Calc_* functions -================= -*/ -void R_SetupEntityLighting( const trRefdef_t *refdef, trRefEntity_t *ent ) { - int i; - dlight_t *dl; - float power; - vec3_t dir; - float d; - vec3_t lightDir; - vec3_t lightOrigin; - - // lighting calculations - if ( ent->lightingCalculated ) { - return; - } - ent->lightingCalculated = qtrue; - - // - // trace a sample point down to find ambient light - // - if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) { - // seperate lightOrigins are needed so an object that is - // sinking into the ground can still be lit, and so - // multi-part models can be lit identically - VectorCopy( ent->e.lightingOrigin, lightOrigin ); - } else { - VectorCopy( ent->e.origin, lightOrigin ); - } - - // if NOWORLDMODEL, only use dynamic lights (menu system, etc) - if ( !(refdef->rdflags & RDF_NOWORLDMODEL ) - && tr.world->lightGridData ) { - R_SetupEntityLightingGrid( ent, tr.world ); - } else { - ent->ambientLight[0] = ent->ambientLight[1] = - ent->ambientLight[2] = tr.identityLight * 150; - ent->directedLight[0] = ent->directedLight[1] = - ent->directedLight[2] = tr.identityLight * 150; - VectorCopy( tr.sunDirection, ent->lightDir ); - } - - // bonus items and view weapons have a fixed minimum add - if ( !r_hdr->integer /* ent->e.renderfx & RF_MINLIGHT */ ) { - // give everything a minimum light add - ent->ambientLight[0] += tr.identityLight * 32; - ent->ambientLight[1] += tr.identityLight * 32; - ent->ambientLight[2] += tr.identityLight * 32; - } - - // - // modify the light by dynamic lights - // - d = VectorLength( ent->directedLight ); - VectorScale( ent->lightDir, d, lightDir ); - - for ( i = 0 ; i < refdef->num_dlights ; i++ ) { - dl = &refdef->dlights[i]; - VectorSubtract( dl->origin, lightOrigin, dir ); - d = VectorNormalize( dir ); - - power = DLIGHT_AT_RADIUS * ( dl->radius * dl->radius ); - if ( d < DLIGHT_MINIMUM_RADIUS ) { - d = DLIGHT_MINIMUM_RADIUS; - } - d = power / ( d * d ); - - VectorMA( ent->directedLight, d, dl->color, ent->directedLight ); - VectorMA( lightDir, d, dir, lightDir ); - } - - // clamp ambient - if ( !r_hdr->integer ) - { - for ( i = 0 ; i < 3 ; i++ ) { - if ( ent->ambientLight[i] > tr.identityLightByte ) { - ent->ambientLight[i] = tr.identityLightByte; - } - } - } - - if ( r_debugLight->integer ) { - LogLight( ent ); - } - - // save out the byte packet version - ((byte *)&ent->ambientLightInt)[0] = ri.ftol(ent->ambientLight[0]); - ((byte *)&ent->ambientLightInt)[1] = ri.ftol(ent->ambientLight[1]); - ((byte *)&ent->ambientLightInt)[2] = ri.ftol(ent->ambientLight[2]); - ((byte *)&ent->ambientLightInt)[3] = 0xff; - - // transform the direction to local space - // no need to do this if using lightentity glsl shader - VectorNormalize( lightDir ); - VectorCopy(lightDir, ent->lightDir); -} - -/* -================= -R_LightForPoint -================= -*/ -int R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ) -{ - trRefEntity_t ent; - - if ( tr.world->lightGridData == NULL ) - return qfalse; - - Com_Memset(&ent, 0, sizeof(ent)); - VectorCopy( point, ent.e.origin ); - R_SetupEntityLightingGrid( &ent, tr.world ); - VectorCopy(ent.ambientLight, ambientLight); - VectorCopy(ent.directedLight, directedLight); - VectorCopy(ent.lightDir, lightDir); - - return qtrue; -} - - -int R_LightDirForPoint( vec3_t point, vec3_t lightDir, vec3_t normal, world_t *world ) -{ - trRefEntity_t ent; - - if ( world->lightGridData == NULL ) - return qfalse; - - Com_Memset(&ent, 0, sizeof(ent)); - VectorCopy( point, ent.e.origin ); - R_SetupEntityLightingGrid( &ent, world ); - VectorCopy(ent.lightDir, lightDir); - - return qtrue; -} diff --git a/src/rend2/tr_local.h b/src/rend2/tr_local.h deleted file mode 100644 index a5d57d35..00000000 --- a/src/rend2/tr_local.h +++ /dev/null @@ -1,2856 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - - -#ifndef TR_LOCAL_H -#define TR_LOCAL_H - -#include "../qcommon/q_shared.h" -#include "../qcommon/qfiles.h" -#include "../qcommon/qcommon.h" -#include "../renderer/tr_public.h" -#include "tr_extratypes.h" -#include "tr_extramath.h" -#include "tr_fbo.h" -#include "tr_postprocess.h" -#include "qgl.h" -#include "../renderer/iqm.h" - -#define GL_INDEX_TYPE GL_UNSIGNED_INT -typedef unsigned int glIndex_t; - -#define BUFFER_OFFSET(i) ((char *)NULL + (i)) - -// 14 bits -// can't be increased without changing bit packing for drawsurfs -// see QSORT_SHADERNUM_SHIFT -#define SHADERNUM_BITS 14 -#define MAX_SHADERS (1<or.origin in local coordinates - float modelMatrix[16]; - float transformMatrix[16]; -} orientationr_t; - -typedef enum -{ - IMGTYPE_COLORALPHA, // for color, lightmap, diffuse, and specular - IMGTYPE_NORMAL, - IMGTYPE_NORMALHEIGHT, - IMGTYPE_DELUXE, // normals are swizzled, deluxe are not -} imgType_t; - -typedef enum -{ - IMGFLAG_NONE = 0x0000, - IMGFLAG_MIPMAP = 0x0001, - IMGFLAG_PICMIP = 0x0002, - IMGFLAG_CUBEMAP = 0x0004, - IMGFLAG_NO_COMPRESSION = 0x0010, - IMGFLAG_NOLIGHTSCALE = 0x0020, - IMGFLAG_CLAMPTOEDGE = 0x0040, - IMGFLAG_SRGB = 0x0080, - IMGFLAG_GENNORMALMAP = 0x0100, -} imgFlags_t; - -typedef struct image_s { - char imgName[MAX_QPATH]; // game path, including extension - int width, height; // source image - int uploadWidth, uploadHeight; // after power of two and picmip but not including clamp to MAX_TEXTURE_SIZE - GLuint texnum; // gl texture binding - - int frameUsed; // for texture usage in frame statistics - - int internalFormat; - int TMU; // only needed for voodoo2 - - imgType_t type; - imgFlags_t flags; - - struct image_s* next; -} image_t; - -typedef enum -{ - VBO_USAGE_STATIC, - VBO_USAGE_DYNAMIC -} vboUsage_t; - -typedef struct VBO_s -{ - char name[MAX_QPATH]; - - uint32_t vertexesVBO; - int vertexesSize; // amount of memory data allocated for all vertices in bytes - uint32_t ofs_xyz; - uint32_t ofs_normal; - uint32_t ofs_st; - uint32_t ofs_lightmap; - uint32_t ofs_vertexcolor; - uint32_t ofs_lightdir; -#ifdef USE_VERT_TANGENT_SPACE - uint32_t ofs_tangent; - uint32_t ofs_bitangent; -#endif - uint32_t stride_xyz; - uint32_t stride_normal; - uint32_t stride_st; - uint32_t stride_lightmap; - uint32_t stride_vertexcolor; - uint32_t stride_lightdir; -#ifdef USE_VERT_TANGENT_SPACE - uint32_t stride_tangent; - uint32_t stride_bitangent; -#endif - uint32_t size_xyz; - uint32_t size_normal; - - int attribs; -} VBO_t; - -typedef struct IBO_s -{ - char name[MAX_QPATH]; - - uint32_t indexesVBO; - int indexesSize; // amount of memory data allocated for all triangles in bytes -// uint32_t ofsIndexes; -} IBO_t; - -//=============================================================================== - -typedef enum { - SS_BAD, - SS_PORTAL, // mirrors, portals, viewscreens - SS_ENVIRONMENT, // sky box - SS_OPAQUE, // opaque - - SS_DECAL, // scorch marks, etc. - SS_SEE_THROUGH, // ladders, grates, grills that may have small blended edges - // in addition to alpha test - SS_BANNER, - - SS_FOG, - - SS_UNDERWATER, // for items that should be drawn in front of the water plane - - SS_BLEND0, // regular transparency and filters - SS_BLEND1, // generally only used for additive type effects - SS_BLEND2, - SS_BLEND3, - - SS_BLEND6, - SS_STENCIL_SHADOW, - SS_ALMOST_NEAREST, // gun smoke puffs - - SS_NEAREST // blood blobs -} shaderSort_t; - - -#define MAX_SHADER_STAGES 8 - -typedef enum { - GF_NONE, - - GF_SIN, - GF_SQUARE, - GF_TRIANGLE, - GF_SAWTOOTH, - GF_INVERSE_SAWTOOTH, - - GF_NOISE - -} genFunc_t; - - -typedef enum { - DEFORM_NONE, - DEFORM_WAVE, - DEFORM_NORMALS, - DEFORM_BULGE, - DEFORM_MOVE, - DEFORM_PROJECTION_SHADOW, - DEFORM_AUTOSPRITE, - DEFORM_AUTOSPRITE2, - DEFORM_TEXT0, - DEFORM_TEXT1, - DEFORM_TEXT2, - DEFORM_TEXT3, - DEFORM_TEXT4, - DEFORM_TEXT5, - DEFORM_TEXT6, - DEFORM_TEXT7 -} deform_t; - -// deformVertexes types that can be handled by the GPU -typedef enum -{ - // do not edit: same as genFunc_t - - DGEN_NONE, - DGEN_WAVE_SIN, - DGEN_WAVE_SQUARE, - DGEN_WAVE_TRIANGLE, - DGEN_WAVE_SAWTOOTH, - DGEN_WAVE_INVERSE_SAWTOOTH, - DGEN_WAVE_NOISE, - - // do not edit until this line - - DGEN_BULGE, - DGEN_MOVE -} deformGen_t; - -typedef enum { - AGEN_IDENTITY, - AGEN_SKIP, - AGEN_ENTITY, - AGEN_ONE_MINUS_ENTITY, - AGEN_VERTEX, - AGEN_ONE_MINUS_VERTEX, - AGEN_LIGHTING_SPECULAR, - AGEN_WAVEFORM, - AGEN_PORTAL, - AGEN_CONST, - AGEN_FRESNEL -} alphaGen_t; - -typedef enum { - CGEN_BAD, - CGEN_IDENTITY_LIGHTING, // tr.identityLight - CGEN_IDENTITY, // always (1,1,1,1) - CGEN_ENTITY, // grabbed from entity's modulate field - CGEN_ONE_MINUS_ENTITY, // grabbed from 1 - entity.modulate - CGEN_EXACT_VERTEX, // tess.vertexColors - CGEN_VERTEX, // tess.vertexColors * tr.identityLight - CGEN_EXACT_VERTEX_LIT, // like CGEN_EXACT_VERTEX but takes a light direction from the lightgrid - CGEN_VERTEX_LIT, // like CGEN_VERTEX but takes a light direction from the lightgrid - CGEN_ONE_MINUS_VERTEX, - CGEN_WAVEFORM, // programmatically generated - CGEN_LIGHTING_DIFFUSE, - CGEN_FOG, // standard fog - CGEN_CONST // fixed color -} colorGen_t; - -typedef enum { - TCGEN_BAD, - TCGEN_IDENTITY, // clear to 0,0 - TCGEN_LIGHTMAP, - TCGEN_TEXTURE, - TCGEN_ENVIRONMENT_MAPPED, - TCGEN_FOG, - TCGEN_VECTOR // S and T from world coordinates -} texCoordGen_t; - -typedef enum { - ACFF_NONE, - ACFF_MODULATE_RGB, - ACFF_MODULATE_RGBA, - ACFF_MODULATE_ALPHA -} acff_t; - -typedef struct { - genFunc_t func; - - float base; - float amplitude; - float phase; - float frequency; -} waveForm_t; - -#define TR_MAX_TEXMODS 4 - -typedef enum { - TMOD_NONE, - TMOD_TRANSFORM, - TMOD_TURBULENT, - TMOD_SCROLL, - TMOD_SCALE, - TMOD_STRETCH, - TMOD_ROTATE, - TMOD_ENTITY_TRANSLATE -} texMod_t; - -#define MAX_SHADER_DEFORMS 3 -typedef struct { - deform_t deformation; // vertex coordinate modification type - - vec3_t moveVector; - waveForm_t deformationWave; - float deformationSpread; - - float bulgeWidth; - float bulgeHeight; - float bulgeSpeed; -} deformStage_t; - - -typedef struct { - texMod_t type; - - // used for TMOD_TURBULENT and TMOD_STRETCH - waveForm_t wave; - - // used for TMOD_TRANSFORM - float matrix[2][2]; // s' = s * m[0][0] + t * m[1][0] + trans[0] - float translate[2]; // t' = s * m[0][1] + t * m[0][1] + trans[1] - - // used for TMOD_SCALE - float scale[2]; // s *= scale[0] - // t *= scale[1] - - // used for TMOD_SCROLL - float scroll[2]; // s' = s + scroll[0] * time - // t' = t + scroll[1] * time - - // + = clockwise - // - = counterclockwise - float rotateSpeed; - -} texModInfo_t; - - -#define MAX_IMAGE_ANIMATIONS 8 - -typedef struct { - image_t *image[MAX_IMAGE_ANIMATIONS]; - int numImageAnimations; - float imageAnimationSpeed; - - texCoordGen_t tcGen; - vec3_t tcGenVectors[2]; - - int numTexMods; - texModInfo_t *texMods; - - int videoMapHandle; - qboolean isLightmap; - qboolean vertexLightmap; - qboolean isVideoMap; -} textureBundle_t; - -enum -{ - TB_COLORMAP = 0, - TB_DIFFUSEMAP = 0, - TB_LIGHTMAP = 1, - TB_LEVELSMAP = 1, - TB_SHADOWMAP = 1, - TB_NORMALMAP = 2, - TB_DELUXEMAP = 3, - TB_SHADOWMAP2 = 3, - TB_SPECULARMAP = 4, - TB_SHADOWMAP3 = 5, - NUM_TEXTURE_BUNDLES = 6 -}; - -typedef enum -{ - // material shader stage types - ST_COLORMAP = 0, // vanilla Q3A style shader treatening - ST_DIFFUSEMAP = 0, // treat color and diffusemap the same - ST_NORMALMAP, - ST_NORMALPARALLAXMAP, - ST_SPECULARMAP, - ST_GLSL -} stageType_t; - -typedef struct { - qboolean active; - - textureBundle_t bundle[NUM_TEXTURE_BUNDLES]; - - waveForm_t rgbWave; - colorGen_t rgbGen; - - waveForm_t alphaWave; - alphaGen_t alphaGen; - - byte constantColor[4]; // for CGEN_CONST and AGEN_CONST - - unsigned stateBits; // GLS_xxxx mask - - acff_t adjustColorsForFog; - - qboolean isDetail; - - stageType_t type; - struct shaderProgram_s *glslShaderGroup; - int glslShaderIndex; - vec2_t materialInfo; -} shaderStage_t; - -struct shaderCommands_s; - -// any change in the LIGHTMAP_* defines here MUST be reflected in -// R_FindShader() in tr_bsp.c -#define LIGHTMAP_2D -4 // shader is for 2D rendering -#define LIGHTMAP_BY_VERTEX -3 // pre-lit triangle models -#define LIGHTMAP_WHITEIMAGE -2 -#define LIGHTMAP_NONE -1 - -typedef enum { - CT_FRONT_SIDED, - CT_BACK_SIDED, - CT_TWO_SIDED -} cullType_t; - -typedef enum { - FP_NONE, // surface is translucent and will just be adjusted properly - FP_EQUAL, // surface is opaque but possibly alpha tested - FP_LE // surface is trnaslucent, but still needs a fog pass (fog surface) -} fogPass_t; - -typedef struct { - float cloudHeight; - image_t *outerbox[6], *innerbox[6]; -} skyParms_t; - -typedef struct { - vec3_t color; - float depthForOpaque; -} fogParms_t; - - -typedef struct shader_s { - char name[MAX_QPATH]; // game path, including extension - int lightmapIndex; // for a shader to match, both name and lightmapIndex must match - - int index; // this shader == tr.shaders[index] - int sortedIndex; // this shader == tr.sortedShaders[sortedIndex] - - float sort; // lower numbered shaders draw before higher numbered - - qboolean defaultShader; // we want to return index 0 if the shader failed to - // load for some reason, but R_FindShader should - // still keep a name allocated for it, so if - // something calls RE_RegisterShader again with - // the same name, we don't try looking for it again - - qboolean explicitlyDefined; // found in a .shader file - - int surfaceFlags; // if explicitlyDefined, this will have SURF_* flags - int contentFlags; - - qboolean entityMergable; // merge across entites optimizable (smoke, blood) - - qboolean isSky; - skyParms_t sky; - fogParms_t fogParms; - - float portalRange; // distance to fog out at - qboolean isPortal; - - int multitextureEnv; // 0, GL_MODULATE, GL_ADD (FIXME: put in stage) - - cullType_t cullType; // CT_FRONT_SIDED, CT_BACK_SIDED, or CT_TWO_SIDED - qboolean polygonOffset; // set for decals and other items that must be offset - qboolean noMipMaps; // for console fonts, 2D elements, etc. - qboolean noPicMip; // for images that must always be full resolution - - fogPass_t fogPass; // draw a blended pass, possibly with depth test equals - - int vertexAttribs; // not all shaders will need all data to be gathered - - int numDeforms; - deformStage_t deforms[MAX_SHADER_DEFORMS]; - - int numUnfoggedPasses; - shaderStage_t *stages[MAX_SHADER_STAGES]; - - void (*optimalStageIteratorFunc)( void ); - - float clampTime; // time this shader is clamped to - float timeOffset; // current time offset for this shader - - int numStates; // if non-zero this is a state shader - struct shader_s *currentShader; // current state if this is a state shader - struct shader_s *parentShader; // current state if this is a state shader - int currentState; // current state index for cycle purposes - long expireTime; // time in milliseconds this expires - - struct shader_s *remappedShader; // current shader this one is remapped too - - int shaderStates[MAX_STATES_PER_SHADER]; // index to valid shader states - - struct shader_s *next; -} shader_t; - -static ID_INLINE qboolean ShaderRequiresCPUDeforms(const shader_t * shader) -{ - if(shader->numDeforms) - { - const deformStage_t *ds = &shader->deforms[0]; - - if (shader->numDeforms > 1) - return qtrue; - - switch (ds->deformation) - { - case DEFORM_WAVE: - case DEFORM_BULGE: - return qfalse; - - default: - return qtrue; - } - } - - return qfalse; -} - -typedef struct shaderState_s { - char shaderName[MAX_QPATH]; // name of shader this state belongs to - char name[MAX_STATE_NAME]; // name of this state - char stateShader[MAX_QPATH]; // shader this name invokes - int cycleTime; // time this cycle lasts, <= 0 is forever - shader_t *shader; -} shaderState_t; - -enum -{ - ATTR_INDEX_POSITION = 0, - ATTR_INDEX_TEXCOORD0 = 1, - ATTR_INDEX_TEXCOORD1 = 2, - ATTR_INDEX_TANGENT = 3, - ATTR_INDEX_BITANGENT = 4, - ATTR_INDEX_NORMAL = 5, - ATTR_INDEX_COLOR = 6, - ATTR_INDEX_PAINTCOLOR = 7, - ATTR_INDEX_LIGHTDIRECTION = 8, - ATTR_INDEX_BONE_INDEXES = 9, - ATTR_INDEX_BONE_WEIGHTS = 10, - - // GPU vertex animations - ATTR_INDEX_POSITION2 = 11, - ATTR_INDEX_TANGENT2 = 12, - ATTR_INDEX_BITANGENT2 = 13, - ATTR_INDEX_NORMAL2 = 14 -}; - -enum -{ - GLS_SRCBLEND_ZERO = (1 << 0), - GLS_SRCBLEND_ONE = (1 << 1), - GLS_SRCBLEND_DST_COLOR = (1 << 2), - GLS_SRCBLEND_ONE_MINUS_DST_COLOR = (1 << 3), - GLS_SRCBLEND_SRC_ALPHA = (1 << 4), - GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA = (1 << 5), - GLS_SRCBLEND_DST_ALPHA = (1 << 6), - GLS_SRCBLEND_ONE_MINUS_DST_ALPHA = (1 << 7), - GLS_SRCBLEND_ALPHA_SATURATE = (1 << 8), - - GLS_SRCBLEND_BITS = GLS_SRCBLEND_ZERO - | GLS_SRCBLEND_ONE - | GLS_SRCBLEND_DST_COLOR - | GLS_SRCBLEND_ONE_MINUS_DST_COLOR - | GLS_SRCBLEND_SRC_ALPHA - | GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA - | GLS_SRCBLEND_DST_ALPHA - | GLS_SRCBLEND_ONE_MINUS_DST_ALPHA - | GLS_SRCBLEND_ALPHA_SATURATE, - - GLS_DSTBLEND_ZERO = (1 << 9), - GLS_DSTBLEND_ONE = (1 << 10), - GLS_DSTBLEND_SRC_COLOR = (1 << 11), - GLS_DSTBLEND_ONE_MINUS_SRC_COLOR = (1 << 12), - GLS_DSTBLEND_SRC_ALPHA = (1 << 13), - GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA = (1 << 14), - GLS_DSTBLEND_DST_ALPHA = (1 << 15), - GLS_DSTBLEND_ONE_MINUS_DST_ALPHA = (1 << 16), - - GLS_DSTBLEND_BITS = GLS_DSTBLEND_ZERO - | GLS_DSTBLEND_ONE - | GLS_DSTBLEND_SRC_COLOR - | GLS_DSTBLEND_ONE_MINUS_SRC_COLOR - | GLS_DSTBLEND_SRC_ALPHA - | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA - | GLS_DSTBLEND_DST_ALPHA - | GLS_DSTBLEND_ONE_MINUS_DST_ALPHA, - - GLS_DEPTHMASK_TRUE = (1 << 17), - - GLS_POLYMODE_LINE = (1 << 18), - - GLS_DEPTHTEST_DISABLE = (1 << 19), - - GLS_DEPTHFUNC_LESS = (1 << 20), - GLS_DEPTHFUNC_EQUAL = (1 << 21), - - GLS_DEPTHFUNC_BITS = GLS_DEPTHFUNC_LESS - | GLS_DEPTHFUNC_EQUAL, - - GLS_ATEST_GT_0 = (1 << 22), - GLS_ATEST_LT_128 = (1 << 23), - GLS_ATEST_GE_128 = (1 << 24), -// GLS_ATEST_GE_CUSTOM = (1 << 25), - - GLS_ATEST_BITS = GLS_ATEST_GT_0 - | GLS_ATEST_LT_128 - | GLS_ATEST_GE_128, -// | GLS_ATEST_GT_CUSTOM, - - GLS_REDMASK_FALSE = (1 << 26), - GLS_GREENMASK_FALSE = (1 << 27), - GLS_BLUEMASK_FALSE = (1 << 28), - GLS_ALPHAMASK_FALSE = (1 << 29), - - GLS_COLORMASK_BITS = GLS_REDMASK_FALSE - | GLS_GREENMASK_FALSE - | GLS_BLUEMASK_FALSE - | GLS_ALPHAMASK_FALSE, - - GLS_STENCILTEST_ENABLE = (1 << 30), - - GLS_DEFAULT = GLS_DEPTHMASK_TRUE -}; - -enum -{ - ATTR_POSITION = 0x0001, - ATTR_TEXCOORD = 0x0002, - ATTR_LIGHTCOORD = 0x0004, - ATTR_TANGENT = 0x0008, - ATTR_BITANGENT = 0x0010, - ATTR_NORMAL = 0x0020, - ATTR_COLOR = 0x0040, - ATTR_PAINTCOLOR = 0x0080, - ATTR_LIGHTDIRECTION = 0x0100, - ATTR_BONE_INDEXES = 0x0200, - ATTR_BONE_WEIGHTS = 0x0400, - - // for .md3 interpolation - ATTR_POSITION2 = 0x0800, - ATTR_TANGENT2 = 0x1000, - ATTR_BITANGENT2 = 0x2000, - ATTR_NORMAL2 = 0x4000, - - ATTR_DEFAULT = ATTR_POSITION, - ATTR_BITS = ATTR_POSITION | - ATTR_TEXCOORD | - ATTR_LIGHTCOORD | - ATTR_TANGENT | - ATTR_BITANGENT | - ATTR_NORMAL | - ATTR_COLOR | - ATTR_PAINTCOLOR | - ATTR_LIGHTDIRECTION | - ATTR_BONE_INDEXES | - ATTR_BONE_WEIGHTS | - ATTR_POSITION2 | - ATTR_TANGENT2 | - ATTR_BITANGENT2 | - ATTR_NORMAL2 -}; - -enum -{ - GENERICDEF_USE_DEFORM_VERTEXES = 0x0001, - GENERICDEF_USE_TCGEN_AND_TCMOD = 0x0002, - GENERICDEF_USE_VERTEX_ANIMATION = 0x0004, - GENERICDEF_USE_FOG = 0x0008, - GENERICDEF_USE_RGBAGEN = 0x0010, - GENERICDEF_USE_LIGHTMAP = 0x0020, - GENERICDEF_ALL = 0x003F, - GENERICDEF_COUNT = 0x0040, -}; - -enum -{ - FOGDEF_USE_DEFORM_VERTEXES = 0x0001, - FOGDEF_USE_VERTEX_ANIMATION = 0x0002, - FOGDEF_ALL = 0x0003, - FOGDEF_COUNT = 0x0004, -}; - -enum -{ - DLIGHTDEF_USE_DEFORM_VERTEXES = 0x0001, - DLIGHTDEF_ALL = 0x0001, - DLIGHTDEF_COUNT = 0x0002, -}; - -enum -{ - LIGHTDEF_USE_LIGHTMAP = 0x0001, - LIGHTDEF_USE_LIGHT_VECTOR = 0x0002, - LIGHTDEF_USE_LIGHT_VERTEX = 0x0003, - LIGHTDEF_LIGHTTYPE_MASK = 0x0003, - LIGHTDEF_ENTITY = 0x0004, - LIGHTDEF_USE_TCGEN_AND_TCMOD = 0x0008, - LIGHTDEF_USE_NORMALMAP = 0x0010, - LIGHTDEF_USE_SPECULARMAP = 0x0020, - LIGHTDEF_USE_DELUXEMAP = 0x0040, - LIGHTDEF_USE_PARALLAXMAP = 0x0080, - LIGHTDEF_USE_SHADOWMAP = 0x0100, - LIGHTDEF_ALL = 0x01FF, - LIGHTDEF_COUNT = 0x0200 -}; - -enum -{ - GLSL_INT, - GLSL_FLOAT, - GLSL_FLOAT5, - GLSL_VEC2, - GLSL_VEC3, - GLSL_VEC4, - GLSL_MAT16 -}; - -// Tr3B - shaderProgram_t represents a pair of one -// GLSL vertex and one GLSL fragment shader -typedef struct shaderProgram_s -{ - char name[MAX_QPATH]; - - GLhandleARB program; - GLhandleARB vertexShader; - GLhandleARB fragmentShader; - uint32_t attribs; // vertex array attributes - - // uniform parameters - int numUniforms; - GLint *uniforms; - char *uniformTypes; // max 127 uniform types - short *uniformBufferOffsets; // max 32767/64=511 uniforms - char *uniformBuffer; -} shaderProgram_t; - - -enum -{ - TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX = 0, - TEXTURECOLOR_UNIFORM_INVTEXRES, - TEXTURECOLOR_UNIFORM_AUTOEXPOSUREMINMAX, - TEXTURECOLOR_UNIFORM_TONEMINAVGMAXLINEAR, - TEXTURECOLOR_UNIFORM_TEXTUREMAP, - TEXTURECOLOR_UNIFORM_LEVELSMAP, - TEXTURECOLOR_UNIFORM_COLOR, - TEXTURECOLOR_UNIFORM_COUNT -}; - - -enum -{ - FOGPASS_UNIFORM_FOGDISTANCE = 0, - FOGPASS_UNIFORM_FOGDEPTH, - FOGPASS_UNIFORM_FOGEYET, - FOGPASS_UNIFORM_DEFORMGEN, - FOGPASS_UNIFORM_DEFORMPARAMS, - FOGPASS_UNIFORM_TIME, - FOGPASS_UNIFORM_COLOR, - FOGPASS_UNIFORM_MODELVIEWPROJECTIONMATRIX, - FOGPASS_UNIFORM_VERTEXLERP, - FOGPASS_UNIFORM_COUNT -}; - - -enum -{ - DLIGHT_UNIFORM_DIFFUSEMAP = 0, - DLIGHT_UNIFORM_DLIGHTINFO, - DLIGHT_UNIFORM_DEFORMGEN, - DLIGHT_UNIFORM_DEFORMPARAMS, - DLIGHT_UNIFORM_TIME, - DLIGHT_UNIFORM_COLOR, - DLIGHT_UNIFORM_MODELVIEWPROJECTIONMATRIX, - DLIGHT_UNIFORM_VERTEXLERP, - DLIGHT_UNIFORM_COUNT -}; - - -enum -{ - PSHADOW_UNIFORM_SHADOWMAP = 0, - PSHADOW_UNIFORM_MODELVIEWPROJECTIONMATRIX, - PSHADOW_UNIFORM_LIGHTFORWARD, - PSHADOW_UNIFORM_LIGHTUP, - PSHADOW_UNIFORM_LIGHTRIGHT, - PSHADOW_UNIFORM_LIGHTORIGIN, - PSHADOW_UNIFORM_LIGHTRADIUS, - PSHADOW_UNIFORM_COUNT -}; - - -enum -{ - GENERIC_UNIFORM_DIFFUSEMAP = 0, - GENERIC_UNIFORM_LIGHTMAP, - GENERIC_UNIFORM_NORMALMAP, - GENERIC_UNIFORM_DELUXEMAP, - GENERIC_UNIFORM_SPECULARMAP, - GENERIC_UNIFORM_SHADOWMAP, - GENERIC_UNIFORM_DIFFUSETEXMATRIX, - GENERIC_UNIFORM_DIFFUSETEXOFFTURB, - //GENERIC_UNIFORM_NORMALTEXMATRIX, - //GENERIC_UNIFORM_SPECULARTEXMATRIX, - GENERIC_UNIFORM_TEXTURE1ENV, - GENERIC_UNIFORM_VIEWORIGIN, - GENERIC_UNIFORM_TCGEN0, - GENERIC_UNIFORM_TCGEN0VECTOR0, - GENERIC_UNIFORM_TCGEN0VECTOR1, - GENERIC_UNIFORM_DEFORMGEN, - GENERIC_UNIFORM_DEFORMPARAMS, - GENERIC_UNIFORM_COLORGEN, - GENERIC_UNIFORM_ALPHAGEN, - GENERIC_UNIFORM_BASECOLOR, - GENERIC_UNIFORM_VERTCOLOR, - GENERIC_UNIFORM_AMBIENTLIGHT, - GENERIC_UNIFORM_DIRECTEDLIGHT, - GENERIC_UNIFORM_LIGHTORIGIN, - GENERIC_UNIFORM_LIGHTRADIUS, - GENERIC_UNIFORM_PORTALRANGE, - GENERIC_UNIFORM_FOGDISTANCE, - GENERIC_UNIFORM_FOGDEPTH, - GENERIC_UNIFORM_FOGEYET, - GENERIC_UNIFORM_FOGCOLORMASK, - GENERIC_UNIFORM_MODELMATRIX, - GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, - GENERIC_UNIFORM_TIME, - GENERIC_UNIFORM_VERTEXLERP, - GENERIC_UNIFORM_MATERIALINFO, - GENERIC_UNIFORM_COUNT -}; - -enum -{ - SHADOWMASK_UNIFORM_SCREENDEPTHMAP = 0, - SHADOWMASK_UNIFORM_SHADOWMAP, - SHADOWMASK_UNIFORM_SHADOWMAP2, - SHADOWMASK_UNIFORM_SHADOWMAP3, - SHADOWMASK_UNIFORM_SHADOWMVP, - SHADOWMASK_UNIFORM_SHADOWMVP2, - SHADOWMASK_UNIFORM_SHADOWMVP3, - SHADOWMASK_UNIFORM_VIEWORIGIN, - SHADOWMASK_UNIFORM_VIEWINFO, // znear, zfar, width/2, height/2 - SHADOWMASK_UNIFORM_VIEWFORWARD, - SHADOWMASK_UNIFORM_VIEWLEFT, - SHADOWMASK_UNIFORM_VIEWUP, - SHADOWMASK_UNIFORM_COUNT -}; - -enum -{ - SSAO_UNIFORM_SCREENDEPTHMAP = 0, - SSAO_UNIFORM_VIEWINFO, // znear, zfar, width/2, height/2 - SSAO_UNIFORM_COUNT -}; - -enum -{ - DEPTHBLUR_UNIFORM_SCREENIMAGEMAP = 0, - DEPTHBLUR_UNIFORM_SCREENDEPTHMAP, - DEPTHBLUR_UNIFORM_VIEWINFO, // znear, zfar, width/2, height/2 - DEPTHBLUR_UNIFORM_COUNT -}; - -// -// Tr3B: these are fire wall functions to avoid expensive redundant glUniform* calls -//#define USE_UNIFORM_FIREWALL 1 -//#define LOG_GLSL_UNIFORMS 1 - -// trRefdef_t holds everything that comes in refdef_t, -// as well as the locally generated scene information -typedef struct { - int x, y, width, height; - float fov_x, fov_y; - vec3_t vieworg; - vec3_t viewaxis[3]; // transformation matrix - - stereoFrame_t stereoFrame; - - int time; // time in milliseconds for shader effects and other time dependent rendering issues - int rdflags; // RDF_NOWORLDMODEL, etc - - // 1 bits will prevent the associated area from rendering at all - byte areamask[MAX_MAP_AREA_BYTES]; - qboolean areamaskModified; // qtrue if areamask changed since last scene - - float floatTime; // tr.refdef.time / 1000.0 - - float blurFactor; - - // text messages for deform text shaders - char text[MAX_RENDER_STRINGS][MAX_RENDER_STRING_LENGTH]; - - int num_entities; - trRefEntity_t *entities; - - int num_dlights; - struct dlight_s *dlights; - - int numPolys; - struct srfPoly_s *polys; - - int numDrawSurfs; - struct drawSurf_s *drawSurfs; - - unsigned int dlightMask; - int num_pshadows; - struct pshadow_s *pshadows; - - float sunShadowMvp[3][16]; - float sunDir[4]; - float sunCol[4]; - float sunAmbCol[4]; - float colorScale; - - float autoExposureMinMax[2]; - float toneMinAvgMaxLinear[3]; -} trRefdef_t; - - -//================================================================================= - -// skins allow models to be retextured without modifying the model file -typedef struct { - char name[MAX_QPATH]; - shader_t *shader; -} skinSurface_t; - -typedef struct skin_s { - char name[MAX_QPATH]; // game path, including extension - int numSurfaces; - skinSurface_t *surfaces[MD3_MAX_SURFACES]; -} skin_t; - - -typedef struct { - int originalBrushNumber; - vec3_t bounds[2]; - - unsigned colorInt; // in packed byte format - float tcScale; // texture coordinate vector scales - fogParms_t parms; - - // for clipping distance in fog when outside - qboolean hasSurface; - float surface[4]; -} fog_t; - -typedef enum { - VPF_NONE = 0x00, - VPF_SHADOWMAP = 0x01, - VPF_DEPTHSHADOW = 0x02, - VPF_DEPTHCLAMP = 0x04, - VPF_ORTHOGRAPHIC = 0x08, - VPF_USESUNLIGHT = 0x10, - VPF_FARPLANEFRUSTUM = 0x20 -} viewParmFlags_t; - -typedef struct { - orientationr_t or; - orientationr_t world; - vec3_t pvsOrigin; // may be different than or.origin for portals - qboolean isPortal; // true if this view is through a portal - qboolean isMirror; // the portal is a mirror, invert the face culling - viewParmFlags_t flags; - int frameSceneNum; // copied from tr.frameSceneNum - int frameCount; // copied from tr.frameCount - cplane_t portalPlane; // clip anything behind this if mirroring - int viewportX, viewportY, viewportWidth, viewportHeight; - FBO_t *targetFbo; - float fovX, fovY; - float projectionMatrix[16]; - cplane_t frustum[5]; - vec3_t visBounds[2]; - float zFar; - float zNear; - stereoFrame_t stereoFrame; -} viewParms_t; - - -/* -============================================================================== - -SURFACES - -============================================================================== -*/ -typedef byte color4ub_t[4]; - -// any changes in surfaceType must be mirrored in rb_surfaceTable[] -typedef enum { - SF_BAD, - SF_SKIP, // ignore - SF_FACE, - SF_GRID, - SF_TRIANGLES, - SF_POLY, - SF_MDV, - SF_MD4, -#ifdef RAVENMD4 - SF_MDR, -#endif - SF_IQM, - SF_FLARE, - SF_ENTITY, // beams, rails, lightning, etc that can be determined by entity - SF_DISPLAY_LIST, - SF_VBO_MESH, - SF_VBO_MDVMESH, - - SF_NUM_SURFACE_TYPES, - SF_MAX = 0x7fffffff // ensures that sizeof( surfaceType_t ) == sizeof( int ) -} surfaceType_t; - -typedef struct drawSurf_s { - unsigned sort; // bit combination for fast compares - surfaceType_t *surface; // any of surface*_t -} drawSurf_t; - -#define MAX_FACE_POINTS 64 - -#define MAX_PATCH_SIZE 32 // max dimensions of a patch mesh in map file -#define MAX_GRID_SIZE 65 // max dimensions of a grid mesh in memory - -// when cgame directly specifies a polygon, it becomes a srfPoly_t -// as soon as it is called -typedef struct srfPoly_s { - surfaceType_t surfaceType; - qhandle_t hShader; - int fogIndex; - int numVerts; - polyVert_t *verts; -} srfPoly_t; - -typedef struct srfDisplayList_s { - surfaceType_t surfaceType; - int listNum; -} srfDisplayList_t; - - -typedef struct srfFlare_s { - surfaceType_t surfaceType; - vec3_t origin; - vec3_t normal; - vec3_t color; -} srfFlare_t; - -typedef struct -{ - vec3_t xyz; - vec2_t st; - vec2_t lightmap; - vec3_t normal; -#ifdef USE_VERT_TANGENT_SPACE - vec3_t tangent; - vec3_t bitangent; -#endif - vec3_t lightdir; - vec4_t vertexColors; - -#if DEBUG_OPTIMIZEVERTICES - unsigned int id; -#endif -} srfVert_t; - -#ifdef USE_VERT_TANGENT_SPACE -#define srfVert_t_cleared(x) srfVert_t (x) = {{0, 0, 0}, {0, 0}, {0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0, 0}} -#else -#define srfVert_t_cleared(x) srfVert_t (x) = {{0, 0, 0}, {0, 0}, {0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0, 0}} -#endif - -typedef struct -{ - int indexes[3]; - int neighbors[3]; - vec4_t plane; - qboolean facingLight; - qboolean degenerated; -} srfTriangle_t; - - -typedef struct srfGridMesh_s -{ - surfaceType_t surfaceType; - - // dynamic lighting information - int dlightBits; - int pshadowBits; - - // culling information - vec3_t meshBounds[2]; - vec3_t localOrigin; - float meshRadius; - - // lod information, which may be different - // than the culling information to allow for - // groups of curves that LOD as a unit - vec3_t lodOrigin; - float lodRadius; - int lodFixed; - int lodStitched; - - // vertexes - int width, height; - float *widthLodError; - float *heightLodError; - - int numTriangles; - srfTriangle_t *triangles; - - int numVerts; - srfVert_t *verts; - - // BSP VBO offsets - int firstVert; - int firstIndex; - glIndex_t minIndex; - glIndex_t maxIndex; - - // static render data - VBO_t *vbo; // points to bsp model VBO - IBO_t *ibo; -} srfGridMesh_t; - - -typedef struct -{ - surfaceType_t surfaceType; - - // dynamic lighting information - int dlightBits; - int pshadowBits; - - // culling information - cplane_t plane; -// vec3_t bounds[2]; - - // triangle definitions - int numTriangles; - srfTriangle_t *triangles; - - int numVerts; - srfVert_t *verts; - - // BSP VBO offsets - int firstVert; - int firstIndex; - glIndex_t minIndex; - glIndex_t maxIndex; - - // static render data - VBO_t *vbo; // points to bsp model VBO - IBO_t *ibo; -} srfSurfaceFace_t; - - -// misc_models in maps are turned into direct geometry by xmap -typedef struct -{ - surfaceType_t surfaceType; - - // dynamic lighting information - int dlightBits; - int pshadowBits; - - // culling information -// vec3_t bounds[2]; - - // triangle definitions - int numTriangles; - srfTriangle_t *triangles; - - int numVerts; - srfVert_t *verts; - - // BSP VBO offsets - int firstVert; - int firstIndex; - glIndex_t minIndex; - glIndex_t maxIndex; - - // static render data - VBO_t *vbo; // points to bsp model VBO - IBO_t *ibo; -} srfTriangles_t; - -// inter-quake-model -typedef struct { - int num_vertexes; - int num_triangles; - int num_frames; - int num_surfaces; - int num_joints; - struct srfIQModel_s *surfaces; - - float *positions; - float *texcoords; - float *normals; - float *tangents; - byte *blendIndexes; - byte *blendWeights; - byte *colors; - int *triangles; - - int *jointParents; - float *poseMats; - float *bounds; - char *names; -} iqmData_t; - -// inter-quake-model surface -typedef struct srfIQModel_s { - surfaceType_t surfaceType; - char name[MAX_QPATH]; - shader_t *shader; - iqmData_t *data; - int first_vertex, num_vertexes; - int first_triangle, num_triangles; -} srfIQModel_t; - -typedef struct srfVBOMesh_s -{ - surfaceType_t surfaceType; - - struct shader_s *shader; // FIXME move this to somewhere else - int fogIndex; - - // dynamic lighting information - int dlightBits; - int pshadowBits; - - // culling information - vec3_t bounds[2]; - - // backEnd stats - int numIndexes; - int numVerts; - int firstIndex; - glIndex_t minIndex; - glIndex_t maxIndex; - - // static render data - VBO_t *vbo; - IBO_t *ibo; -} srfVBOMesh_t; - -typedef struct srfVBOMDVMesh_s -{ - surfaceType_t surfaceType; - - struct mdvModel_s *mdvModel; - struct mdvSurface_s *mdvSurface; - - // backEnd stats - int numIndexes; - int numVerts; - glIndex_t minIndex; - glIndex_t maxIndex; - - // static render data - VBO_t *vbo; - IBO_t *ibo; -} srfVBOMDVMesh_t; - -extern void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])(void *); - -/* -============================================================================== - -SHADOWS - -============================================================================== -*/ - -typedef struct pshadow_s -{ - float sort; - - int numEntities; - int entityNums[8]; - vec3_t entityOrigins[8]; - float entityRadiuses[8]; - - float viewRadius; - vec3_t viewOrigin; - - vec3_t lightViewAxis[3]; - vec3_t lightOrigin; - float lightRadius; - cplane_t cullPlane; -} pshadow_t; - - -/* -============================================================================== - -BRUSH MODELS - -============================================================================== -*/ - - -// -// in memory representation -// - -#define SIDE_FRONT 0 -#define SIDE_BACK 1 -#define SIDE_ON 2 - -#define CULLINFO_NONE 0 -#define CULLINFO_BOX 1 -#define CULLINFO_SPHERE 2 -#define CULLINFO_PLANE 4 - -typedef struct cullinfo_s { - int type; - vec3_t bounds[2]; - vec3_t localOrigin; - float radius; - cplane_t plane; -} cullinfo_t; - -typedef struct msurface_s { - //int viewCount; // if == tr.viewCount, already added - struct shader_s *shader; - int fogIndex; - cullinfo_t cullinfo; - - surfaceType_t *data; // any of srf*_t -} msurface_t; - - -#define CONTENTS_NODE -1 -typedef struct mnode_s { - // common with leaf and node - int contents; // -1 for nodes, to differentiate from leafs - int visCounts[MAX_VISCOUNTS]; // node needs to be traversed if current - vec3_t mins, maxs; // for bounding box culling - struct mnode_s *parent; - - // node specific - cplane_t *plane; - struct mnode_s *children[2]; - - // leaf specific - int cluster; - int area; - - int firstmarksurface; - int nummarksurfaces; -} mnode_t; - -typedef struct { - vec3_t bounds[2]; // for culling - int firstSurface; - int numSurfaces; -} bmodel_t; - -typedef struct { - char name[MAX_QPATH]; // ie: maps/tim_dm2.bsp - char baseName[MAX_QPATH]; // ie: tim_dm2 - - int dataSize; - - int numShaders; - dshader_t *shaders; - - int numBModels; - bmodel_t *bmodels; - - int numplanes; - cplane_t *planes; - - int numnodes; // includes leafs - int numDecisionNodes; - mnode_t *nodes; - - VBO_t *vbo; - IBO_t *ibo; - - int numWorldSurfaces; - - int numsurfaces; - msurface_t *surfaces; - int *surfacesViewCount; - int *surfacesDlightBits; - int *surfacesPshadowBits; - - int numMergedSurfaces; - msurface_t *mergedSurfaces; - int *mergedSurfacesViewCount; - int *mergedSurfacesDlightBits; - int *mergedSurfacesPshadowBits; - - int nummarksurfaces; - int *marksurfaces; - int *viewSurfaces; - - int numfogs; - fog_t *fogs; - - vec3_t lightGridOrigin; - vec3_t lightGridSize; - vec3_t lightGridInverseSize; - int lightGridBounds[3]; - byte *lightGridData; - float *hdrLightGrid; - - - int numClusters; - int clusterBytes; - const byte *vis; // may be passed in by CM_LoadMap to save space - - byte *novis; // clusterBytes of 0xff - - char *entityString; - char *entityParsePoint; -} world_t; - - -/* -============================================================================== -MDV MODELS - meta format for vertex animation models like .md2, .md3, .mdc -============================================================================== -*/ -typedef struct -{ - float bounds[2][3]; - float localOrigin[3]; - float radius; -} mdvFrame_t; - -typedef struct -{ - float origin[3]; - float axis[3][3]; -} mdvTag_t; - -typedef struct -{ - char name[MAX_QPATH]; // tag name -} mdvTagName_t; - -typedef struct -{ - vec3_t xyz; - vec3_t normal; -#ifdef USE_VERT_TANGENT_SPACE - vec3_t tangent; - vec3_t bitangent; -#endif -} mdvVertex_t; - -typedef struct -{ - float st[2]; -} mdvSt_t; - -typedef struct mdvSurface_s -{ - surfaceType_t surfaceType; - - char name[MAX_QPATH]; // polyset name - - int numShaderIndexes; - int *shaderIndexes; - - int numVerts; - mdvVertex_t *verts; - mdvSt_t *st; - - int numTriangles; - srfTriangle_t *triangles; - - struct mdvModel_s *model; -} mdvSurface_t; - -typedef struct mdvModel_s -{ - int numFrames; - mdvFrame_t *frames; - - int numTags; - mdvTag_t *tags; - mdvTagName_t *tagNames; - - int numSurfaces; - mdvSurface_t *surfaces; - - int numVBOSurfaces; - srfVBOMDVMesh_t *vboSurfaces; - - int numSkins; -} mdvModel_t; - - -//====================================================================== - -typedef enum { - MOD_BAD, - MOD_BRUSH, - MOD_MESH, - MOD_MD4, -#ifdef RAVENMD4 - MOD_MDR, -#endif - MOD_IQM -} modtype_t; - -typedef struct model_s { - char name[MAX_QPATH]; - modtype_t type; - int index; // model = tr.models[model->index] - - int dataSize; // just for listing purposes - bmodel_t *bmodel; // only if type == MOD_BRUSH - mdvModel_t *mdv[MD3_MAX_LODS]; // only if type == MOD_MESH - void *modelData; // only if type == (MOD_MD4 | MOD_MDR | MOD_IQM) - - int numLods; -} model_t; - - -#define MAX_MOD_KNOWN 1024 - -void R_ModelInit (void); -model_t *R_GetModelByHandle( qhandle_t hModel ); -int R_LerpTag( orientation_t *tag, qhandle_t handle, int startFrame, int endFrame, - float frac, const char *tagName ); -void R_ModelBounds( qhandle_t handle, vec3_t mins, vec3_t maxs ); - -void R_Modellist_f (void); - -//==================================================== -extern refimport_t ri; - -#define MAX_DRAWIMAGES 2048 -#define MAX_SKINS 1024 - - -#define MAX_DRAWSURFS 0x10000 -#define DRAWSURF_MASK (MAX_DRAWSURFS-1) - -/* - -the drawsurf sort data is packed into a single 32 bit value so it can be -compared quickly during the qsorting process - -the bits are allocated as follows: - -0 - 1 : dlightmap index -//2 : used to be clipped flag REMOVED - 03.21.00 rad -2 - 6 : fog index -11 - 20 : entity index -21 - 31 : sorted shader index - - TTimo - 1.32 -0-1 : dlightmap index -2-6 : fog index -7-16 : entity index -17-30 : sorted shader index - - SmileTheory - for pshadows -17-31 : sorted shader index -7-16 : entity index -2-6 : fog index -1 : pshadow flag -0 : dlight flag -*/ -#define QSORT_FOGNUM_SHIFT 2 -#define QSORT_REFENTITYNUM_SHIFT 7 -#define QSORT_SHADERNUM_SHIFT (QSORT_REFENTITYNUM_SHIFT+REFENTITYNUM_BITS) -#if (QSORT_SHADERNUM_SHIFT+SHADERNUM_BITS) > 32 - #error "Need to update sorting, too many bits." -#endif -#define QSORT_PSHADOW_SHIFT 1 - -extern int gl_filter_min, gl_filter_max; - -/* -** performanceCounters_t -*/ -typedef struct { - int c_sphere_cull_patch_in, c_sphere_cull_patch_clip, c_sphere_cull_patch_out; - int c_box_cull_patch_in, c_box_cull_patch_clip, c_box_cull_patch_out; - int c_sphere_cull_md3_in, c_sphere_cull_md3_clip, c_sphere_cull_md3_out; - int c_box_cull_md3_in, c_box_cull_md3_clip, c_box_cull_md3_out; - - int c_leafs; - int c_dlightSurfaces; - int c_dlightSurfacesCulled; -} frontEndCounters_t; - -#define FOG_TABLE_SIZE 256 -#define FUNCTABLE_SIZE 1024 -#define FUNCTABLE_SIZE2 10 -#define FUNCTABLE_MASK (FUNCTABLE_SIZE-1) - - -// the renderer front end should never modify glstate_t -typedef struct { - int currenttextures[NUM_TEXTURE_BUNDLES]; - int currenttmu; - qboolean finishCalled; - int texEnv[2]; - int faceCulling; - unsigned long glStateBits; - uint32_t vertexAttribsState; - uint32_t vertexAttribPointersSet; - uint32_t vertexAttribsNewFrame; - uint32_t vertexAttribsOldFrame; - float vertexAttribsInterpolation; - shaderProgram_t *currentProgram; - FBO_t *currentFBO; - VBO_t *currentVBO; - IBO_t *currentIBO; - matrix_t modelview; - matrix_t projection; - matrix_t modelviewProjection; -} glstate_t; - -typedef enum { - MI_NONE, - MI_NVX, - MI_ATI -} memInfo_t; - -typedef enum { - TCR_NONE = 0x0000, - TCR_LATC = 0x0001, - TCR_BPTC = 0x0002, -} textureCompressionRef_t; - -// We can't change glConfig_t without breaking DLL/vms compatibility, so -// store extensions we have here. -typedef struct { - qboolean drawRangeElements; - qboolean multiDrawArrays; - qboolean occlusionQuery; - - int glslMajorVersion; - int glslMinorVersion; - - memInfo_t memInfo; - - qboolean framebufferObject; - int maxRenderbufferSize; - int maxColorAttachments; - - qboolean textureNonPowerOfTwo; - qboolean textureFloat; - qboolean halfFloatPixel; - qboolean packedDepthStencil; - textureCompressionRef_t textureCompression; - - qboolean framebufferMultisample; - qboolean framebufferBlit; - - qboolean texture_srgb; - - qboolean depthClamp; -} glRefConfig_t; - - -typedef struct { - int c_surfaces, c_shaders, c_vertexes, c_indexes, c_totalIndexes; - int c_surfBatches; - float c_overDraw; - - int c_vboVertexBuffers; - int c_vboIndexBuffers; - int c_vboVertexes; - int c_vboIndexes; - - int c_staticVboDraws; - int c_dynamicVboDraws; - - int c_multidraws; - int c_multidrawsMerged; - - int c_dlightVertexes; - int c_dlightIndexes; - - int c_flareAdds; - int c_flareTests; - int c_flareRenders; - - int c_glslShaderBinds; - int c_genericDraws; - int c_lightallDraws; - int c_fogDraws; - int c_dlightDraws; - - int msec; // total msec for backend run -} backEndCounters_t; - -// all state modified by the back end is seperated -// from the front end state -typedef struct { - trRefdef_t refdef; - viewParms_t viewParms; - orientationr_t or; - backEndCounters_t pc; - qboolean isHyperspace; - trRefEntity_t *currentEntity; - qboolean skyRenderedThisView; // flag for drawing sun - - qboolean projection2D; // if qtrue, drawstretchpic doesn't need to change modes - byte color2D[4]; - qboolean vertexes2D; // shader needs to be finished - trRefEntity_t entity2D; // currentEntity will point at this when doing 2D rendering - - FBO_t *last2DFBO; - qboolean colorMask[4]; - qboolean framePostProcessed; - qboolean depthFill; -} backEndState_t; - -/* -** trGlobals_t -** -** Most renderer globals are defined here. -** backend functions should never modify any of these fields, -** but may read fields that aren't dynamically modified -** by the frontend. -*/ -typedef struct { - qboolean registered; // cleared at shutdown, set at beginRegistration - - int visIndex; - int visClusters[MAX_VISCOUNTS]; - int visCounts[MAX_VISCOUNTS]; // incremented every time a new vis cluster is entered - - int frameCount; // incremented every frame - int sceneCount; // incremented every scene - int viewCount; // incremented every view (twice a scene if portaled) - // and every R_MarkFragments call - - int frameSceneNum; // zeroed at RE_BeginFrame - - qboolean worldMapLoaded; - qboolean worldDeluxeMapping; - vec2_t autoExposureMinMax; - vec3_t toneMinAvgMaxLevel; - world_t *world; - - const byte *externalVisData; // from RE_SetWorldVisData, shared with CM_Load - - image_t *defaultImage; - image_t *scratchImage[32]; - image_t *fogImage; - image_t *dlightImage; // inverse-quare highlight for projective adding - image_t *flareImage; - image_t *whiteImage; // full of 0xff - image_t *identityLightImage; // full of tr.identityLightByte - - image_t *shadowCubemaps[MAX_DLIGHTS]; - - - image_t *renderImage; - image_t *sunRaysImage; - image_t *renderDepthImage; - image_t *pshadowMaps[MAX_DRAWN_PSHADOWS]; - image_t *textureScratchImage[2]; - image_t *screenScratchImage; - image_t *quarterImage[2]; - image_t *calcLevelsImage; - image_t *targetLevelsImage; - image_t *fixedLevelsImage; - image_t *sunShadowDepthImage[3]; - image_t *screenShadowImage; - image_t *screenSsaoImage; - image_t *hdrDepthImage; - - image_t *textureDepthImage; - - FBO_t *renderFbo; - FBO_t *msaaResolveFbo; - FBO_t *sunRaysFbo; - FBO_t *depthFbo; - FBO_t *pshadowFbos[MAX_DRAWN_PSHADOWS]; - FBO_t *textureScratchFbo[2]; - FBO_t *screenScratchFbo; - FBO_t *quarterFbo[2]; - FBO_t *calcLevelsFbo; - FBO_t *targetLevelsFbo; - FBO_t *sunShadowFbo[3]; - FBO_t *screenShadowFbo; - FBO_t *screenSsaoFbo; - FBO_t *hdrDepthFbo; - - shader_t *defaultShader; - shader_t *shadowShader; - shader_t *projectionShadowShader; - - shader_t *flareShader; - shader_t *sunShader; - shader_t *sunFlareShader; - - int numLightmaps; - int lightmapSize; - image_t **lightmaps; - image_t **deluxemaps; - - int fatLightmapSize; - int fatLightmapStep; - - trRefEntity_t *currentEntity; - trRefEntity_t worldEntity; // point currentEntity at this when rendering world - int currentEntityNum; - int shiftedEntityNum; // currentEntityNum << QSORT_REFENTITYNUM_SHIFT - model_t *currentModel; - - // - // GPU shader programs - // - shaderProgram_t genericShader[GENERICDEF_COUNT]; - shaderProgram_t textureColorShader; - shaderProgram_t fogShader[FOGDEF_COUNT]; - shaderProgram_t dlightShader[DLIGHTDEF_COUNT]; - shaderProgram_t lightallShader[LIGHTDEF_COUNT]; - shaderProgram_t shadowmapShader; - shaderProgram_t pshadowShader; - shaderProgram_t down4xShader; - shaderProgram_t bokehShader; - shaderProgram_t tonemapShader; - shaderProgram_t calclevels4xShader[2]; - shaderProgram_t shadowmaskShader; - shaderProgram_t ssaoShader; - shaderProgram_t depthBlurShader[2]; - - - // ----------------------------------------- - - viewParms_t viewParms; - - float identityLight; // 1.0 / ( 1 << overbrightBits ) - int identityLightByte; // identityLight * 255 - int overbrightBits; // r_overbrightBits->integer, but set to 0 if no hw gamma - - orientationr_t or; // for current entity - - trRefdef_t refdef; - - int viewCluster; - - float mapLightScale; - - qboolean sunShadows; - vec3_t sunLight; // from the sky shader for this level - vec3_t sunAmbient; - vec3_t sunDirection; - - frontEndCounters_t pc; - int frontEndMsec; // not in pc due to clearing issue - - vec4_t clipRegion; // 2D clipping region - - // - // put large tables at the end, so most elements will be - // within the +/32K indexed range on risc processors - // - model_t *models[MAX_MOD_KNOWN]; - int numModels; - - int numImages; - image_t *images[MAX_DRAWIMAGES]; - - int numFBOs; - FBO_t *fbos[MAX_FBOS]; - - int numVBOs; - VBO_t *vbos[MAX_VBOS]; - - int numIBOs; - IBO_t *ibos[MAX_IBOS]; - - // shader indexes from other modules will be looked up in tr.shaders[] - // shader indexes from drawsurfs will be looked up in sortedShaders[] - // lower indexed sortedShaders must be rendered first (opaque surfaces before translucent) - int numShaders; - shader_t *shaders[MAX_SHADERS]; - shader_t *sortedShaders[MAX_SHADERS]; - - int numSkins; - skin_t *skins[MAX_SKINS]; - - GLuint sunFlareQuery[2]; - int sunFlareQueryIndex; - qboolean sunFlareQueryActive[2]; - - float sinTable[FUNCTABLE_SIZE]; - float squareTable[FUNCTABLE_SIZE]; - float triangleTable[FUNCTABLE_SIZE]; - float sawToothTable[FUNCTABLE_SIZE]; - float inverseSawToothTable[FUNCTABLE_SIZE]; - float fogTable[FOG_TABLE_SIZE]; -} trGlobals_t; - -extern backEndState_t backEnd; -extern trGlobals_t tr; -extern glconfig_t glConfig; // outside of TR since it shouldn't be cleared during ref re-init -extern glstate_t glState; // outside of TR since it shouldn't be cleared during ref re-init - -// These three variables should live inside glConfig but can't because of compatibility issues to the original ID vms. -// If you release a stand-alone game and your mod uses tr_types.h from this build you can safely move them to -// the glconfig_t struct. -extern qboolean textureFilterAnisotropic; -extern int maxAnisotropy; -extern glRefConfig_t glRefConfig; -extern float displayAspect; - - -// -// cvars -// -extern cvar_t *r_flareSize; -extern cvar_t *r_flareFade; -// coefficient for the flare intensity falloff function. -#define FLARE_STDCOEFF "150" -extern cvar_t *r_flareCoeff; - -extern cvar_t *r_railWidth; -extern cvar_t *r_railCoreWidth; -extern cvar_t *r_railSegmentLength; - -extern cvar_t *r_ignore; // used for debugging anything -extern cvar_t *r_verbose; // used for verbose debug spew - -extern cvar_t *r_znear; // near Z clip plane -extern cvar_t *r_zproj; // z distance of projection plane -extern cvar_t *r_stereoSeparation; // separation of cameras for stereo rendering - -extern cvar_t *r_stencilbits; // number of desired stencil bits -extern cvar_t *r_depthbits; // number of desired depth bits -extern cvar_t *r_colorbits; // number of desired color bits, only relevant for fullscreen -extern cvar_t *r_texturebits; // number of desired texture bits -extern cvar_t *r_ext_multisample; - // 0 = use framebuffer depth - // 16 = use 16-bit textures - // 32 = use 32-bit textures - // all else = error - -extern cvar_t *r_measureOverdraw; // enables stencil buffer overdraw measurement - -extern cvar_t *r_lodbias; // push/pull LOD transitions -extern cvar_t *r_lodscale; - -extern cvar_t *r_inGameVideo; // controls whether in game video should be draw -extern cvar_t *r_fastsky; // controls whether sky should be cleared or drawn -extern cvar_t *r_drawSun; // controls drawing of sun quad -extern cvar_t *r_dynamiclight; // dynamic lights enabled/disabled -extern cvar_t *r_dlightBacks; // dlight non-facing surfaces for continuity - -extern cvar_t *r_norefresh; // bypasses the ref rendering -extern cvar_t *r_drawentities; // disable/enable entity rendering -extern cvar_t *r_drawworld; // disable/enable world rendering -extern cvar_t *r_speeds; // various levels of information display -extern cvar_t *r_detailTextures; // enables/disables detail texturing stages -extern cvar_t *r_novis; // disable/enable usage of PVS -extern cvar_t *r_nocull; -extern cvar_t *r_facePlaneCull; // enables culling of planar surfaces with back side test -extern cvar_t *r_nocurves; -extern cvar_t *r_showcluster; - -extern cvar_t *r_mode; // video mode -extern cvar_t *r_fullscreen; -extern cvar_t *r_noborder; -extern cvar_t *r_gamma; -extern cvar_t *r_ignorehwgamma; // overrides hardware gamma capabilities - -extern cvar_t *r_allowExtensions; // global enable/disable of OpenGL extensions -extern cvar_t *r_ext_compressed_textures; // these control use of specific extensions -extern cvar_t *r_ext_multitexture; -extern cvar_t *r_ext_compiled_vertex_array; -extern cvar_t *r_ext_texture_env_add; - -extern cvar_t *r_ext_texture_filter_anisotropic; -extern cvar_t *r_ext_max_anisotropy; - -extern cvar_t *r_ext_draw_range_elements; -extern cvar_t *r_ext_multi_draw_arrays; -extern cvar_t *r_ext_framebuffer_object; -extern cvar_t *r_ext_texture_float; -extern cvar_t *r_arb_half_float_pixel; -extern cvar_t *r_ext_framebuffer_multisample; - -extern cvar_t *r_nobind; // turns off binding to appropriate textures -extern cvar_t *r_singleShader; // make most world faces use default shader -extern cvar_t *r_roundImagesDown; -extern cvar_t *r_colorMipLevels; // development aid to see texture mip usage -extern cvar_t *r_picmip; // controls picmip values -extern cvar_t *r_finish; -extern cvar_t *r_drawBuffer; -extern cvar_t *r_swapInterval; -extern cvar_t *r_textureMode; -extern cvar_t *r_offsetFactor; -extern cvar_t *r_offsetUnits; - -extern cvar_t *r_fullbright; // avoid lightmap pass -extern cvar_t *r_lightmap; // render lightmaps only -extern cvar_t *r_vertexLight; // vertex lighting mode for better performance -extern cvar_t *r_uiFullScreen; // ui is running fullscreen - -extern cvar_t *r_logFile; // number of frames to emit GL logs -extern cvar_t *r_showtris; // enables wireframe rendering of the world -extern cvar_t *r_showsky; // forces sky in front of all surfaces -extern cvar_t *r_shownormals; // draws wireframe normals -extern cvar_t *r_clear; // force screen clear every frame - -extern cvar_t *r_shadows; // controls shadows: 0 = none, 1 = blur, 2 = stencil, 3 = black planar projection -extern cvar_t *r_flares; // light flares - -extern cvar_t *r_intensity; - -extern cvar_t *r_lockpvs; -extern cvar_t *r_noportals; -extern cvar_t *r_portalOnly; - -extern cvar_t *r_subdivisions; -extern cvar_t *r_lodCurveError; -extern cvar_t *r_skipBackEnd; - -extern cvar_t *r_stereoEnabled; -extern cvar_t *r_anaglyphMode; - -extern cvar_t *r_mergeMultidraws; -extern cvar_t *r_mergeLeafSurfaces; - -extern cvar_t *r_softOverbright; - -extern cvar_t *r_hdr; -extern cvar_t *r_postProcess; - -extern cvar_t *r_toneMap; -extern cvar_t *r_forceToneMap; -extern cvar_t *r_forceToneMapMin; -extern cvar_t *r_forceToneMapAvg; -extern cvar_t *r_forceToneMapMax; - -extern cvar_t *r_autoExposure; -extern cvar_t *r_forceAutoExposure; -extern cvar_t *r_forceAutoExposureMin; -extern cvar_t *r_forceAutoExposureMax; - -extern cvar_t *r_cameraExposure; - -extern cvar_t *r_srgb; - -extern cvar_t *r_depthPrepass; -extern cvar_t *r_ssao; - -extern cvar_t *r_normalMapping; -extern cvar_t *r_specularMapping; -extern cvar_t *r_deluxeMapping; -extern cvar_t *r_parallaxMapping; -extern cvar_t *r_normalAmbient; -extern cvar_t *r_dlightMode; -extern cvar_t *r_pshadowDist; -extern cvar_t *r_recalcMD3Normals; -extern cvar_t *r_mergeLightmaps; -extern cvar_t *r_imageUpsample; -extern cvar_t *r_imageUpsampleMaxSize; -extern cvar_t *r_imageUpsampleType; -extern cvar_t *r_genNormalMaps; -extern cvar_t *r_forceSun; -extern cvar_t *r_forceSunMapLightScale; -extern cvar_t *r_forceSunLightScale; -extern cvar_t *r_forceSunAmbientScale; -extern cvar_t *r_drawSunRays; -extern cvar_t *r_sunShadows; -extern cvar_t *r_shadowFilter; -extern cvar_t *r_shadowMapSize; -extern cvar_t *r_shadowCascadeZNear; -extern cvar_t *r_shadowCascadeZFar; -extern cvar_t *r_shadowCascadeZBias; - -extern cvar_t *r_greyscale; - -extern cvar_t *r_ignoreGLErrors; - -extern cvar_t *r_overBrightBits; -extern cvar_t *r_mapOverBrightBits; - -extern cvar_t *r_debugSurface; -extern cvar_t *r_simpleMipMaps; - -extern cvar_t *r_showImages; -extern cvar_t *r_debugSort; - -extern cvar_t *r_printShaders; -extern cvar_t *r_saveFontData; - -extern cvar_t *r_marksOnTriangleMeshes; - -//==================================================================== - -float R_NoiseGet4f( float x, float y, float z, float t ); -void R_NoiseInit( void ); - -void R_SwapBuffers( int ); - -void R_RenderView( viewParms_t *parms ); -void R_RenderDlightCubemaps(const refdef_t *fd); -void R_RenderPshadowMaps(const refdef_t *fd); -void R_RenderSunShadowMaps(const refdef_t *fd, int level); - -void R_AddMD3Surfaces( trRefEntity_t *e ); -void R_AddNullModelSurfaces( trRefEntity_t *e ); -void R_AddBeamSurfaces( trRefEntity_t *e ); -void R_AddRailSurfaces( trRefEntity_t *e, qboolean isUnderwater ); -void R_AddLightningBoltSurfaces( trRefEntity_t *e ); - -void R_AddPolygonSurfaces( void ); - -void R_DecomposeSort( unsigned sort, int *entityNum, shader_t **shader, - int *fogNum, int *dlightMap, int *pshadowMap ); - -void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, - int fogIndex, int dlightMap, int pshadowMap ); - -void R_CalcTangentSpace(vec3_t tangent, vec3_t bitangent, vec3_t normal, - const vec3_t v0, const vec3_t v1, const vec3_t v2, const vec2_t t0, const vec2_t t1, const vec2_t t2); -qboolean R_CalcTangentVectors(srfVert_t * dv[3]); -void R_CalcSurfaceTriangleNeighbors(int numTriangles, srfTriangle_t * triangles); -void R_CalcSurfaceTrianglePlanes(int numTriangles, srfTriangle_t * triangles, srfVert_t * verts); - -#define CULL_IN 0 // completely unclipped -#define CULL_CLIP 1 // clipped by one or more planes -#define CULL_OUT 2 // completely outside the clipping planes -void R_LocalNormalToWorld (const vec3_t local, vec3_t world); -void R_LocalPointToWorld (const vec3_t local, vec3_t world); -int R_CullBox (vec3_t bounds[2]); -int R_CullLocalBox (vec3_t bounds[2]); -int R_CullPointAndRadiusEx( const vec3_t origin, float radius, const cplane_t* frustum, int numPlanes ); -int R_CullPointAndRadius( const vec3_t origin, float radius ); -int R_CullLocalPointAndRadius( const vec3_t origin, float radius ); - -void R_SetupProjection(viewParms_t *dest, float zProj, float zFar, qboolean computeFrustum); -void R_RotateForEntity( const trRefEntity_t *ent, const viewParms_t *viewParms, orientationr_t *or ); - -/* -** GL wrapper/helper functions -*/ -void GL_Bind( image_t *image ); -void GL_BindCubemap( image_t *image ); -void GL_BindToTMU( image_t *image, int tmu ); -void GL_SetDefaultState (void); -void GL_SelectTexture( int unit ); -void GL_TextureMode( const char *string ); -void GL_CheckErrs( char *file, int line ); -#define GL_CheckErrors(...) GL_CheckErrs(__FILE__, __LINE__) -void GL_State( unsigned long stateVector ); -void GL_SetProjectionMatrix(matrix_t matrix); -void GL_SetModelviewMatrix(matrix_t matrix); -void GL_TexEnv( int env ); -void GL_Cull( int cullType ); - -#define GLS_SRCBLEND_ZERO 0x00000001 -#define GLS_SRCBLEND_ONE 0x00000002 -#define GLS_SRCBLEND_DST_COLOR 0x00000003 -#define GLS_SRCBLEND_ONE_MINUS_DST_COLOR 0x00000004 -#define GLS_SRCBLEND_SRC_ALPHA 0x00000005 -#define GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA 0x00000006 -#define GLS_SRCBLEND_DST_ALPHA 0x00000007 -#define GLS_SRCBLEND_ONE_MINUS_DST_ALPHA 0x00000008 -#define GLS_SRCBLEND_ALPHA_SATURATE 0x00000009 -#define GLS_SRCBLEND_BITS 0x0000000f - -#define GLS_DSTBLEND_ZERO 0x00000010 -#define GLS_DSTBLEND_ONE 0x00000020 -#define GLS_DSTBLEND_SRC_COLOR 0x00000030 -#define GLS_DSTBLEND_ONE_MINUS_SRC_COLOR 0x00000040 -#define GLS_DSTBLEND_SRC_ALPHA 0x00000050 -#define GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA 0x00000060 -#define GLS_DSTBLEND_DST_ALPHA 0x00000070 -#define GLS_DSTBLEND_ONE_MINUS_DST_ALPHA 0x00000080 -#define GLS_DSTBLEND_BITS 0x000000f0 - -#define GLS_DEPTHMASK_TRUE 0x00000100 - -#define GLS_POLYMODE_LINE 0x00001000 - -#define GLS_DEPTHTEST_DISABLE 0x00010000 -#define GLS_DEPTHFUNC_EQUAL 0x00020000 -#define GLS_DEPTHFUNC_GREATER 0x00040000 -#define GLS_DEPTHFUNC_BITS 0x00060000 - -#define GLS_ATEST_GT_0 0x10000000 -#define GLS_ATEST_LT_80 0x20000000 -#define GLS_ATEST_GE_80 0x40000000 -#define GLS_ATEST_BITS 0x70000000 - -#define GLS_DEFAULT GLS_DEPTHMASK_TRUE - -void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty); -void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty); - -void RE_BeginFrame( stereoFrame_t stereoFrame ); -void RE_BeginRegistration( glconfig_t *glconfig ); -void RE_LoadWorldMap( const char *mapname ); -void RE_SetWorldVisData( const byte *vis ); -qhandle_t RE_RegisterModel( const char *name ); -qhandle_t RE_RegisterSkin( const char *name ); -void RE_Shutdown( qboolean destroyWindow ); - -qboolean R_GetEntityToken( char *buffer, int size ); - -model_t *R_AllocModel( void ); - -void R_Init( void ); -image_t *R_FindImageFile( const char *name, imgType_t type, imgFlags_t flags ); -image_t *R_CreateImage( const char *name, byte *pic, int width, int height, imgType_t type, imgFlags_t flags, int internalFormat ); -void R_UpdateSubImage( image_t *image, byte *pic, int x, int y, int width, int height ); -qboolean R_GetModeInfo( int *width, int *height, float *windowAspect, int mode ); - -void R_SetColorMappings( void ); -void R_GammaCorrect( byte *buffer, int bufSize ); - -void R_ImageList_f( void ); -void R_SkinList_f( void ); -// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=516 -const void *RB_TakeScreenshotCmd( const void *data ); -void R_ScreenShot_f( void ); - -void R_InitFogTable( void ); -float R_FogFactor( float s, float t ); -void R_InitImages( void ); -void R_DeleteTextures( void ); -int R_SumOfUsedImages( void ); -void R_InitSkins( void ); -skin_t *R_GetSkinByHandle( qhandle_t hSkin ); - -int R_ComputeLOD( trRefEntity_t *ent ); - -const void *RB_TakeVideoFrameCmd( const void *data ); - -// -// tr_shader.c -// -qhandle_t RE_RegisterShaderLightMap( const char *name, int lightmapIndex ); -qhandle_t RE_RegisterShader( const char *name ); -qhandle_t RE_RegisterShaderNoMip( const char *name ); -qhandle_t RE_RegisterShaderFromImage(const char *name, int lightmapIndex, image_t *image, qboolean mipRawImage); - -shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImage ); -shader_t *R_GetShaderByHandle( qhandle_t hShader ); -shader_t *R_GetShaderByState( int index, long *cycleTime ); -shader_t *R_FindShaderByName( const char *name ); -void R_InitShaders( void ); -void R_ShaderList_f( void ); -void R_RemapShader(const char *oldShader, const char *newShader, const char *timeOffset); - -/* -==================================================================== - -IMPLEMENTATION SPECIFIC FUNCTIONS - -==================================================================== -*/ - -void GLimp_Init( void ); -void GLimp_Shutdown( void ); -void GLimp_EndFrame( void ); - -void GLimp_LogComment( char *comment ); -void GLimp_Minimize(void); - -// NOTE TTimo linux works with float gamma value, not the gamma table -// the params won't be used, getting the r_gamma cvar directly -void GLimp_SetGamma( unsigned char red[256], - unsigned char green[256], - unsigned char blue[256] ); - - -void GLimp_InitExtraExtensions( void ); -/* -==================================================================== - -TESSELATOR/SHADER DECLARATIONS - -==================================================================== -*/ - -typedef struct stageVars -{ - color4ub_t colors[SHADER_MAX_VERTEXES]; - vec2_t texcoords[NUM_TEXTURE_BUNDLES][SHADER_MAX_VERTEXES]; -} stageVars_t; - -#define MAX_MULTIDRAW_PRIMITIVES 16384 - -typedef struct shaderCommands_s -{ - glIndex_t indexes[SHADER_MAX_INDEXES] QALIGN(16); - vec4_t xyz[SHADER_MAX_VERTEXES] QALIGN(16); - vec4_t normal[SHADER_MAX_VERTEXES] QALIGN(16); -#ifdef USE_VERT_TANGENT_SPACE - vec4_t tangent[SHADER_MAX_VERTEXES] QALIGN(16); - vec4_t bitangent[SHADER_MAX_VERTEXES] QALIGN(16); -#endif - vec2_t texCoords[SHADER_MAX_VERTEXES][2] QALIGN(16); - vec4_t vertexColors[SHADER_MAX_VERTEXES] QALIGN(16); - vec4_t lightdir[SHADER_MAX_VERTEXES] QALIGN(16); - //int vertexDlightBits[SHADER_MAX_VERTEXES] QALIGN(16); - - VBO_t *vbo; - IBO_t *ibo; - qboolean useInternalVBO; - - stageVars_t svars QALIGN(16); - - //color4ub_t constantColor255[SHADER_MAX_VERTEXES] QALIGN(16); - - shader_t *shader; - float shaderTime; - int fogNum; - - int dlightBits; // or together of all vertexDlightBits - int pshadowBits; - - int firstIndex; - int numIndexes; - int numVertexes; - glIndex_t minIndex; - glIndex_t maxIndex; - - int multiDrawPrimitives; - GLsizei multiDrawNumIndexes[MAX_MULTIDRAW_PRIMITIVES]; - glIndex_t *multiDrawFirstIndex[MAX_MULTIDRAW_PRIMITIVES]; - glIndex_t *multiDrawLastIndex[MAX_MULTIDRAW_PRIMITIVES]; - glIndex_t multiDrawMinIndex[MAX_MULTIDRAW_PRIMITIVES]; - glIndex_t multiDrawMaxIndex[MAX_MULTIDRAW_PRIMITIVES]; - - // info extracted from current shader - int numPasses; - void (*currentStageIteratorFunc)( void ); - shaderStage_t **xstages; -} shaderCommands_t; - -extern shaderCommands_t tess; - -void RB_BeginSurface(shader_t *shader, int fogNum ); -void RB_EndSurface(void); -void RB_CheckOverflow( int verts, int indexes ); -#define RB_CHECKOVERFLOW(v,i) if (tess.numVertexes + (v) >= SHADER_MAX_VERTEXES || tess.numIndexes + (i) >= SHADER_MAX_INDEXES ) {RB_CheckOverflow(v,i);} - -void R_DrawElementsVBO( int numIndexes, glIndex_t firstIndex, glIndex_t minIndex, glIndex_t maxIndex ); -void RB_StageIteratorGeneric( void ); -void RB_StageIteratorSky( void ); -void RB_StageIteratorVertexLitTexture( void ); -void RB_StageIteratorLightmappedMultitexture( void ); - -void RB_AddQuadStamp( vec3_t origin, vec3_t left, vec3_t up, float color[4] ); -void RB_AddQuadStampExt( vec3_t origin, vec3_t left, vec3_t up, float color[4], float s1, float t1, float s2, float t2 ); -void RB_InstantQuad( vec4_t quadVerts[4] ); -//void RB_InstantQuad2(vec4_t quadVerts[4], vec2_t texCoords[4], vec4_t color, shaderProgram_t *sp, vec2_t invTexRes); -void RB_InstantQuad2(vec4_t quadVerts[4], vec2_t texCoords[4]); - -void RB_ShowImages( void ); - - -/* -============================================================ - -WORLD MAP - -============================================================ -*/ - -void R_AddBrushModelSurfaces( trRefEntity_t *e ); -void R_AddWorldSurfaces( void ); -qboolean R_inPVS( const vec3_t p1, const vec3_t p2 ); - - -/* -============================================================ - -FLARES - -============================================================ -*/ - -void R_ClearFlares( void ); - -void RB_AddFlare( void *surface, int fogNum, vec3_t point, vec3_t color, vec3_t normal ); -void RB_AddDlightFlares( void ); -void RB_RenderFlares (void); - -/* -============================================================ - -LIGHTS - -============================================================ -*/ - -void R_DlightBmodel( bmodel_t *bmodel ); -void R_SetupEntityLighting( const trRefdef_t *refdef, trRefEntity_t *ent ); -void R_TransformDlights( int count, dlight_t *dl, orientationr_t *or ); -int R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ); -int R_LightDirForPoint( vec3_t point, vec3_t lightDir, vec3_t normal, world_t *world ); - - -/* -============================================================ - -SHADOWS - -============================================================ -*/ - -void RB_ShadowTessEnd( void ); -void RB_ShadowFinish( void ); -void RB_ProjectionShadowDeform( void ); - -/* -============================================================ - -SKIES - -============================================================ -*/ - -void R_BuildCloudData( shaderCommands_t *shader ); -void R_InitSkyTexCoords( float cloudLayerHeight ); -void R_DrawSkyBox( shaderCommands_t *shader ); -void RB_DrawSun( float scale, shader_t *shader ); -void RB_ClipSkyPolygons( shaderCommands_t *shader ); - -/* -============================================================ - -CURVE TESSELATION - -============================================================ -*/ - -#define PATCH_STITCHING - -srfGridMesh_t *R_SubdividePatchToGrid( int width, int height, - srfVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ); -srfGridMesh_t *R_GridInsertColumn( srfGridMesh_t *grid, int column, int row, vec3_t point, float loderror ); -srfGridMesh_t *R_GridInsertRow( srfGridMesh_t *grid, int row, int column, vec3_t point, float loderror ); -void R_FreeSurfaceGridMesh( srfGridMesh_t *grid ); - -/* -============================================================ - -MARKERS, POLYGON PROJECTION ON WORLD POLYGONS - -============================================================ -*/ - -int R_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection, - int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ); - - -/* -============================================================ - -VERTEX BUFFER OBJECTS - -============================================================ -*/ -VBO_t *R_CreateVBO(const char *name, byte * vertexes, int vertexesSize, vboUsage_t usage); -VBO_t *R_CreateVBO2(const char *name, int numVertexes, srfVert_t * vertexes, uint32_t stateBits, vboUsage_t usage); - -IBO_t *R_CreateIBO(const char *name, byte * indexes, int indexesSize, vboUsage_t usage); -IBO_t *R_CreateIBO2(const char *name, int numTriangles, srfTriangle_t * triangles, vboUsage_t usage); - -void R_BindVBO(VBO_t * vbo); -void R_BindNullVBO(void); - -void R_BindIBO(IBO_t * ibo); -void R_BindNullIBO(void); - -void R_InitVBOs(void); -void R_ShutdownVBOs(void); -void R_VBOList_f(void); - -void RB_UpdateVBOs(unsigned int attribBits); - - -/* -============================================================ - -GLSL - -============================================================ -*/ - -void GLSL_InitGPUShaders(void); -void GLSL_ShutdownGPUShaders(void); -void GLSL_VertexAttribsState(uint32_t stateBits); -void GLSL_VertexAttribPointers(uint32_t attribBits); -void GLSL_BindProgram(shaderProgram_t * program); -void GLSL_BindNullProgram(void); - -void GLSL_SetNumUniforms(shaderProgram_t *program, int numUniforms); -void GLSL_SetUniformName(shaderProgram_t *program, int uniformNum, const char *name); -void GLSL_SetUniformInt(shaderProgram_t *program, int uniformNum, GLint value); -void GLSL_SetUniformFloat(shaderProgram_t *program, int uniformNum, GLfloat value); -void GLSL_SetUniformFloat5(shaderProgram_t *program, int uniformNum, const vec5_t v); -void GLSL_SetUniformVec2(shaderProgram_t *program, int uniformNum, const vec2_t v); -void GLSL_SetUniformVec3(shaderProgram_t *program, int uniformNum, const vec3_t v); -void GLSL_SetUniformVec4(shaderProgram_t *program, int uniformNum, const vec4_t v); -void GLSL_SetUniformMatrix16(shaderProgram_t *program, int uniformNum, const matrix_t matrix); - -shaderProgram_t *GLSL_GetGenericShaderProgram(int stage); - -/* -============================================================ - -SCENE GENERATION - -============================================================ -*/ - -void R_InitNextFrame( void ); - -void RE_ClearScene( void ); -void RE_AddRefEntityToScene( const refEntity_t *ent ); -void RE_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts, int num ); -void RE_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ); -void RE_AddAdditiveLightToScene( const vec3_t org, float intensity, float r, float g, float b ); -void RE_RenderScene( const refdef_t *fd ); - -#ifdef RAVENMD4 -/* -============================================================= - -UNCOMPRESSING BONES - -============================================================= -*/ - -#define MC_BITS_X (16) -#define MC_BITS_Y (16) -#define MC_BITS_Z (16) -#define MC_BITS_VECT (16) - -#define MC_SCALE_X (1.0f/64) -#define MC_SCALE_Y (1.0f/64) -#define MC_SCALE_Z (1.0f/64) - -void MC_UnCompress(float mat[3][4],const unsigned char * comp); -#endif - -/* -============================================================= - -ANIMATED MODELS - -============================================================= -*/ - -// void R_MakeAnimModel( model_t *model ); haven't seen this one really, so not needed I guess. -void R_AddAnimSurfaces( trRefEntity_t *ent ); -void RB_SurfaceAnim( md4Surface_t *surfType ); -#ifdef RAVENMD4 -void R_MDRAddAnimSurfaces( trRefEntity_t *ent ); -void RB_MDRSurfaceAnim( md4Surface_t *surface ); -#endif -qboolean R_LoadIQM (model_t *mod, void *buffer, int filesize, const char *name ); -void R_AddIQMSurfaces( trRefEntity_t *ent ); -void RB_IQMSurfaceAnim( surfaceType_t *surface ); -int R_IQMLerpTag( orientation_t *tag, iqmData_t *data, - int startFrame, int endFrame, - float frac, const char *tagName ); - -/* -============================================================= - -IMAGE LOADERS - -============================================================= -*/ - -void R_LoadBMP( const char *name, byte **pic, int *width, int *height ); -void R_LoadJPG( const char *name, byte **pic, int *width, int *height ); -void R_LoadPCX( const char *name, byte **pic, int *width, int *height ); -void R_LoadPNG( const char *name, byte **pic, int *width, int *height ); -void R_LoadTGA( const char *name, byte **pic, int *width, int *height ); - -/* -============================================================= -============================================================= -*/ -void R_TransformModelToClip( const vec3_t src, const float *modelMatrix, const float *projectionMatrix, - vec4_t eye, vec4_t dst ); -void R_TransformClipToWindow( const vec4_t clip, const viewParms_t *view, vec4_t normalized, vec4_t window ); - -void RB_DeformTessGeometry( void ); - -void RB_CalcEnvironmentTexCoords( float *dstTexCoords ); -void RB_CalcFogTexCoords( float *dstTexCoords ); -void RB_CalcScrollTexCoords( const float scroll[2], float *dstTexCoords ); -void RB_CalcRotateTexCoords( float rotSpeed, float *dstTexCoords ); -void RB_CalcScaleTexCoords( const float scale[2], float *dstTexCoords ); -void RB_CalcTurbulentTexCoords( const waveForm_t *wf, float *dstTexCoords ); -void RB_CalcTransformTexCoords( const texModInfo_t *tmi, float *dstTexCoords ); - -void RB_CalcScaleTexMatrix( const float scale[2], float *matrix ); -void RB_CalcScrollTexMatrix( const float scrollSpeed[2], float *matrix ); -void RB_CalcRotateTexMatrix( float degsPerSecond, float *matrix ); -void RB_CalcTurbulentTexMatrix( const waveForm_t *wf, matrix_t matrix ); -void RB_CalcTransformTexMatrix( const texModInfo_t *tmi, float *matrix ); -void RB_CalcStretchTexMatrix( const waveForm_t *wf, float *matrix ); - -void RB_CalcModulateColorsByFog( unsigned char *dstColors ); -void RB_CalcModulateAlphasByFog( unsigned char *dstColors ); -void RB_CalcModulateRGBAsByFog( unsigned char *dstColors ); -void RB_CalcWaveAlpha( const waveForm_t *wf, unsigned char *dstColors ); -float RB_CalcWaveAlphaSingle( const waveForm_t *wf ); -void RB_CalcWaveColor( const waveForm_t *wf, unsigned char *dstColors ); -float RB_CalcWaveColorSingle( const waveForm_t *wf ); -void RB_CalcAlphaFromEntity( unsigned char *dstColors ); -void RB_CalcAlphaFromOneMinusEntity( unsigned char *dstColors ); -void RB_CalcStretchTexCoords( const waveForm_t *wf, float *texCoords ); -void RB_CalcColorFromEntity( unsigned char *dstColors ); -void RB_CalcColorFromOneMinusEntity( unsigned char *dstColors ); -void RB_CalcSpecularAlpha( unsigned char *alphas ); -void RB_CalcDiffuseColor( unsigned char *colors ); - -/* -============================================================= - -RENDERER BACK END FUNCTIONS - -============================================================= -*/ - -void RB_ExecuteRenderCommands( const void *data ); - -/* -============================================================= - -RENDERER BACK END COMMAND QUEUE - -============================================================= -*/ - -#define MAX_RENDER_COMMANDS 0x40000 - -typedef struct { - byte cmds[MAX_RENDER_COMMANDS]; - int used; -} renderCommandList_t; - -typedef struct { - int commandId; - float color[4]; -} setColorCommand_t; - -typedef struct { - int commandId; - int buffer; -} drawBufferCommand_t; - -typedef struct { - int commandId; - image_t *image; - int width; - int height; - void *data; -} subImageCommand_t; - -typedef struct { - int commandId; -} swapBuffersCommand_t; - -typedef struct { - int commandId; - int buffer; -} endFrameCommand_t; - -typedef struct { - int commandId; - shader_t *shader; - float x, y; - float w, h; - float s1, t1; - float s2, t2; -} stretchPicCommand_t; - -typedef struct { - int commandId; - trRefdef_t refdef; - viewParms_t viewParms; - drawSurf_t *drawSurfs; - int numDrawSurfs; -} drawSurfsCommand_t; - -typedef struct { - int commandId; - int x; - int y; - int width; - int height; - char *fileName; - qboolean jpeg; -} screenshotCommand_t; - -typedef struct { - int commandId; - int width; - int height; - byte *captureBuffer; - byte *encodeBuffer; - qboolean motionJpeg; -} videoFrameCommand_t; - -typedef struct -{ - int commandId; - - GLboolean rgba[4]; -} colorMaskCommand_t; - -typedef struct -{ - int commandId; -} clearDepthCommand_t; - -typedef struct { - int commandId; - int map; - int cubeSide; -} capShadowmapCommand_t; - -typedef struct { - int commandId; - trRefdef_t refdef; - viewParms_t viewParms; -} postProcessCommand_t; - -typedef enum { - RC_END_OF_LIST, - RC_SET_COLOR, - RC_STRETCH_PIC, - RC_DRAW_SURFS, - RC_DRAW_BUFFER, - RC_SWAP_BUFFERS, - RC_SCREENSHOT, - RC_VIDEOFRAME, - RC_COLORMASK, - RC_CLEARDEPTH, - RC_CAPSHADOWMAP, - RC_POSTPROCESS -} renderCommand_t; - - -// these are sort of arbitrary limits. -// the limits apply to the sum of all scenes in a frame -- -// the main view, all the 3D icons, etc -#define MAX_POLYS 600 -#define MAX_POLYVERTS 3000 - -// all of the information needed by the back end must be -// contained in a backEndData_t -typedef struct { - drawSurf_t drawSurfs[MAX_DRAWSURFS]; - dlight_t dlights[MAX_DLIGHTS]; - trRefEntity_t entities[MAX_REFENTITIES]; - srfPoly_t *polys;//[MAX_POLYS]; - polyVert_t *polyVerts;//[MAX_POLYVERTS]; - pshadow_t pshadows[MAX_CALC_PSHADOWS]; - renderCommandList_t commands; -} backEndData_t; - -extern int max_polys; -extern int max_polyverts; - -extern backEndData_t *backEndData; // the second one may not be allocated - -extern volatile renderCommandList_t *renderCommandList; - - -void *R_GetCommandBuffer( int bytes ); -void RB_ExecuteRenderCommands( const void *data ); - -void R_IssuePendingRenderCommands( void ); - -void R_AddDrawSurfCmd( drawSurf_t *drawSurfs, int numDrawSurfs ); -void R_AddCapShadowmapCmd( int dlight, int cubeSide ); -void R_AddPostProcessCmd (void); - -void RE_SetColor( const float *rgba ); -void RE_SetClipRegion( const float *region ); -void RE_StretchPic ( float x, float y, float w, float h, - float s1, float t1, float s2, float t2, qhandle_t hShader ); -void RE_BeginFrame( stereoFrame_t stereoFrame ); -void RE_EndFrame( int *frontEndMsec, int *backEndMsec ); -void RE_SaveJPG(char * filename, int quality, int image_width, int image_height, - unsigned char *image_buffer, int padding); -size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality, - int image_width, int image_height, byte *image_buffer, int padding); -void RE_TakeVideoFrame( int width, int height, - byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg ); - -// font stuff -void R_InitFreeType( void ); -void R_DoneFreeType( void ); -void RE_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font); - - -#endif //TR_LOCAL_H diff --git a/src/rend2/tr_main.c b/src/rend2/tr_main.c deleted file mode 100644 index 0935dce7..00000000 --- a/src/rend2/tr_main.c +++ /dev/null @@ -1,2882 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_main.c -- main control flow for each frame - -#include "tr_local.h" - -#include // memcpy - -trGlobals_t tr; - -static float s_flipMatrix[16] = { - // convert from our coordinate system (looking down X) - // to OpenGL's coordinate system (looking down -Z) - 0, 0, -1, 0, - -1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 0, 1 -}; - - -refimport_t ri; - -// entities that will have procedurally generated surfaces will just -// point at this for their sorting surface -surfaceType_t entitySurface = SF_ENTITY; - -/* -================ -R_CompareVert -================ -*/ -qboolean R_CompareVert(srfVert_t * v1, srfVert_t * v2, qboolean checkST) -{ - int i; - - for(i = 0; i < 3; i++) - { - if(floor(v1->xyz[i] + 0.1) != floor(v2->xyz[i] + 0.1)) - { - return qfalse; - } - - if(checkST && ((v1->st[0] != v2->st[0]) || (v1->st[1] != v2->st[1]))) - { - return qfalse; - } - } - - return qtrue; -} - -/* -============= -R_CalcNormalForTriangle -============= -*/ -void R_CalcNormalForTriangle(vec3_t normal, const vec3_t v0, const vec3_t v1, const vec3_t v2) -{ - vec3_t udir, vdir; - - // compute the face normal based on vertex points - VectorSubtract(v2, v0, udir); - VectorSubtract(v1, v0, vdir); - CrossProduct(udir, vdir, normal); - - VectorNormalize(normal); -} - -/* -============= -R_CalcTangentsForTriangle -http://members.rogers.com/deseric/tangentspace.htm -============= -*/ -void R_CalcTangentsForTriangle(vec3_t tangent, vec3_t bitangent, - const vec3_t v0, const vec3_t v1, const vec3_t v2, - const vec2_t t0, const vec2_t t1, const vec2_t t2) -{ - int i; - vec3_t planes[3]; - vec3_t u, v; - - for(i = 0; i < 3; i++) - { - VectorSet(u, v1[i] - v0[i], t1[0] - t0[0], t1[1] - t0[1]); - VectorSet(v, v2[i] - v0[i], t2[0] - t0[0], t2[1] - t0[1]); - - VectorNormalize(u); - VectorNormalize(v); - - CrossProduct(u, v, planes[i]); - } - - //So your tangent space will be defined by this : - //Normal = Normal of the triangle or Tangent X Bitangent (careful with the cross product, - // you have to make sure the normal points in the right direction) - //Tangent = ( dp(Fx(s,t)) / ds, dp(Fy(s,t)) / ds, dp(Fz(s,t)) / ds ) or ( -Bx/Ax, -By/Ay, - Bz/Az ) - //Bitangent = ( dp(Fx(s,t)) / dt, dp(Fy(s,t)) / dt, dp(Fz(s,t)) / dt ) or ( -Cx/Ax, -Cy/Ay, -Cz/Az ) - - // tangent... - tangent[0] = -planes[0][1] / planes[0][0]; - tangent[1] = -planes[1][1] / planes[1][0]; - tangent[2] = -planes[2][1] / planes[2][0]; - VectorNormalize(tangent); - - // bitangent... - bitangent[0] = -planes[0][2] / planes[0][0]; - bitangent[1] = -planes[1][2] / planes[1][0]; - bitangent[2] = -planes[2][2] / planes[2][0]; - VectorNormalize(bitangent); -} - - - - -/* -============= -R_CalcTangentSpace -============= -*/ -void R_CalcTangentSpace(vec3_t tangent, vec3_t bitangent, vec3_t normal, - const vec3_t v0, const vec3_t v1, const vec3_t v2, const vec2_t t0, const vec2_t t1, const vec2_t t2) -{ - vec3_t cp, u, v; - vec3_t faceNormal; - - VectorSet(u, v1[0] - v0[0], t1[0] - t0[0], t1[1] - t0[1]); - VectorSet(v, v2[0] - v0[0], t2[0] - t0[0], t2[1] - t0[1]); - - CrossProduct(u, v, cp); - if(fabs(cp[0]) > 10e-6) - { - tangent[0] = -cp[1] / cp[0]; - bitangent[0] = -cp[2] / cp[0]; - } - - u[0] = v1[1] - v0[1]; - v[0] = v2[1] - v0[1]; - - CrossProduct(u, v, cp); - if(fabs(cp[0]) > 10e-6) - { - tangent[1] = -cp[1] / cp[0]; - bitangent[1] = -cp[2] / cp[0]; - } - - u[0] = v1[2] - v0[2]; - v[0] = v2[2] - v0[2]; - - CrossProduct(u, v, cp); - if(fabs(cp[0]) > 10e-6) - { - tangent[2] = -cp[1] / cp[0]; - bitangent[2] = -cp[2] / cp[0]; - } - - VectorNormalize(tangent); - VectorNormalize(bitangent); - - // compute the face normal based on vertex points - if ( normal[0] == 0.0f && normal[1] == 0.0f && normal[2] == 0.0f ) - { - VectorSubtract(v2, v0, u); - VectorSubtract(v1, v0, v); - CrossProduct(u, v, faceNormal); - } - else - { - VectorCopy(normal, faceNormal); - } - - VectorNormalize(faceNormal); - -#if 1 - // Gram-Schmidt orthogonalize - //tangent[a] = (t - n * Dot(n, t)).Normalize(); - VectorMA(tangent, -DotProduct(faceNormal, tangent), faceNormal, tangent); - VectorNormalize(tangent); - - // compute the cross product B=NxT - //CrossProduct(normal, tangent, bitangent); -#else - // normal, compute the cross product N=TxB - CrossProduct(tangent, bitangent, normal); - VectorNormalize(normal); - - if(DotProduct(normal, faceNormal) < 0) - { - //VectorInverse(normal); - //VectorInverse(tangent); - //VectorInverse(bitangent); - - // compute the cross product T=BxN - CrossProduct(bitangent, faceNormal, tangent); - - // compute the cross product B=NxT - //CrossProduct(normal, tangent, bitangent); - } -#endif - - VectorCopy(faceNormal, normal); -} - -void R_CalcTangentSpaceFast(vec3_t tangent, vec3_t bitangent, vec3_t normal, - const vec3_t v0, const vec3_t v1, const vec3_t v2, const vec2_t t0, const vec2_t t1, const vec2_t t2) -{ - vec3_t cp, u, v; - vec3_t faceNormal; - - VectorSet(u, v1[0] - v0[0], t1[0] - t0[0], t1[1] - t0[1]); - VectorSet(v, v2[0] - v0[0], t2[0] - t0[0], t2[1] - t0[1]); - - CrossProduct(u, v, cp); - if(fabs(cp[0]) > 10e-6) - { - tangent[0] = -cp[1] / cp[0]; - bitangent[0] = -cp[2] / cp[0]; - } - - u[0] = v1[1] - v0[1]; - v[0] = v2[1] - v0[1]; - - CrossProduct(u, v, cp); - if(fabs(cp[0]) > 10e-6) - { - tangent[1] = -cp[1] / cp[0]; - bitangent[1] = -cp[2] / cp[0]; - } - - u[0] = v1[2] - v0[2]; - v[0] = v2[2] - v0[2]; - - CrossProduct(u, v, cp); - if(fabs(cp[0]) > 10e-6) - { - tangent[2] = -cp[1] / cp[0]; - bitangent[2] = -cp[2] / cp[0]; - } - - VectorNormalizeFast(tangent); - VectorNormalizeFast(bitangent); - - // compute the face normal based on vertex points - VectorSubtract(v2, v0, u); - VectorSubtract(v1, v0, v); - CrossProduct(u, v, faceNormal); - - VectorNormalizeFast(faceNormal); - -#if 0 - // normal, compute the cross product N=TxB - CrossProduct(tangent, bitangent, normal); - VectorNormalizeFast(normal); - - if(DotProduct(normal, faceNormal) < 0) - { - VectorInverse(normal); - //VectorInverse(tangent); - //VectorInverse(bitangent); - - CrossProduct(normal, tangent, bitangent); - } - - VectorCopy(faceNormal, normal); -#else - // Gram-Schmidt orthogonalize - //tangent[a] = (t - n * Dot(n, t)).Normalize(); - VectorMA(tangent, -DotProduct(faceNormal, tangent), faceNormal, tangent); - VectorNormalizeFast(tangent); -#endif - - VectorCopy(faceNormal, normal); -} - -/* -http://www.terathon.com/code/tangent.html -*/ -void R_CalcTBN(vec3_t tangent, vec3_t bitangent, vec3_t normal, - const vec3_t v1, const vec3_t v2, const vec3_t v3, const vec2_t w1, const vec2_t w2, const vec2_t w3) -{ - vec3_t u, v; - float x1, x2, y1, y2, z1, z2; - float s1, s2, t1, t2; - float r, dot; - - x1 = v2[0] - v1[0]; - x2 = v3[0] - v1[0]; - y1 = v2[1] - v1[1]; - y2 = v3[1] - v1[1]; - z1 = v2[2] - v1[2]; - z2 = v3[2] - v1[2]; - - s1 = w2[0] - w1[0]; - s2 = w3[0] - w1[0]; - t1 = w2[1] - w1[1]; - t2 = w3[1] - w1[1]; - - r = 1.0f / (s1 * t2 - s2 * t1); - - VectorSet(tangent, (t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); - VectorSet(bitangent, (s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); - - // compute the face normal based on vertex points - VectorSubtract(v3, v1, u); - VectorSubtract(v2, v1, v); - CrossProduct(u, v, normal); - - VectorNormalize(normal); - - // Gram-Schmidt orthogonalize - //tangent[a] = (t - n * Dot(n, t)).Normalize(); - dot = DotProduct(normal, tangent); - VectorMA(tangent, -dot, normal, tangent); - VectorNormalize(tangent); - - // B=NxT - //CrossProduct(normal, tangent, bitangent); -} - -void R_CalcTBN2(vec3_t tangent, vec3_t bitangent, vec3_t normal, - const vec3_t v1, const vec3_t v2, const vec3_t v3, const vec2_t t1, const vec2_t t2, const vec2_t t3) -{ - vec3_t v2v1; - vec3_t v3v1; - - float c2c1_T; - float c2c1_B; - - float c3c1_T; - float c3c1_B; - - float denominator; - float scale1, scale2; - - vec3_t T, B, N, C; - - - // Calculate the tangent basis for each vertex of the triangle - // UPDATE: In the 3rd edition of the accompanying article, the for-loop located here has - // been removed as it was redundant (the entire TBN matrix was calculated three times - // instead of just one). - // - // Please note, that this function relies on the fact that the input geometry are triangles - // and the tangent basis for each vertex thus is identical! - // - - // Calculate the vectors from the current vertex to the two other vertices in the triangle - VectorSubtract(v2, v1, v2v1); - VectorSubtract(v3, v1, v3v1); - - // The equation presented in the article states that: - // c2c1_T = V2.texcoord.x - V1.texcoord.x - // c2c1_B = V2.texcoord.y - V1.texcoord.y - // c3c1_T = V3.texcoord.x - V1.texcoord.x - // c3c1_B = V3.texcoord.y - V1.texcoord.y - - // Calculate c2c1_T and c2c1_B - c2c1_T = t2[0] - t1[0]; - c2c1_B = t2[1] - t2[1]; - - // Calculate c3c1_T and c3c1_B - c3c1_T = t3[0] - t1[0]; - c3c1_B = t3[1] - t1[1]; - - denominator = c2c1_T * c3c1_B - c3c1_T * c2c1_B; - //if(ROUNDOFF(fDenominator) == 0.0f) - if(denominator == 0.0f) - { - // We won't risk a divide by zero, so set the tangent matrix to the identity matrix - VectorSet(tangent, 1, 0, 0); - VectorSet(bitangent, 0, 1, 0); - VectorSet(normal, 0, 0, 1); - } - else - { - // Calculate the reciprocal value once and for all (to achieve speed) - scale1 = 1.0f / denominator; - - // T and B are calculated just as the equation in the article states - VectorSet(T, (c3c1_B * v2v1[0] - c2c1_B * v3v1[0]) * scale1, - (c3c1_B * v2v1[1] - c2c1_B * v3v1[1]) * scale1, - (c3c1_B * v2v1[2] - c2c1_B * v3v1[2]) * scale1); - - VectorSet(B, (-c3c1_T * v2v1[0] + c2c1_T * v3v1[0]) * scale1, - (-c3c1_T * v2v1[1] + c2c1_T * v3v1[1]) * scale1, - (-c3c1_T * v2v1[2] + c2c1_T * v3v1[2]) * scale1); - - // The normal N is calculated as the cross product between T and B - CrossProduct(T, B, N); - -#if 0 - VectorCopy(T, tangent); - VectorCopy(B, bitangent); - VectorCopy(N, normal); -#else - // Calculate the reciprocal value once and for all (to achieve speed) - scale2 = 1.0f / ((T[0] * B[1] * N[2] - T[2] * B[1] * N[0]) + - (B[0] * N[1] * T[2] - B[2] * N[1] * T[0]) + - (N[0] * T[1] * B[2] - N[2] * T[1] * B[0])); - - // Calculate the inverse if the TBN matrix using the formula described in the article. - // We store the basis vectors directly in the provided TBN matrix: pvTBNMatrix - CrossProduct(B, N, C); tangent[0] = C[0] * scale2; - CrossProduct(N, T, C); tangent[1] = -C[0] * scale2; - CrossProduct(T, B, C); tangent[2] = C[0] * scale2; - VectorNormalize(tangent); - - CrossProduct(B, N, C); bitangent[0] = -C[1] * scale2; - CrossProduct(N, T, C); bitangent[1] = C[1] * scale2; - CrossProduct(T, B, C); bitangent[2] = -C[1] * scale2; - VectorNormalize(bitangent); - - CrossProduct(B, N, C); normal[0] = C[2] * scale2; - CrossProduct(N, T, C); normal[1] = -C[2] * scale2; - CrossProduct(T, B, C); normal[2] = C[2] * scale2; - VectorNormalize(normal); -#endif - } -} - - -#ifdef USE_VERT_TANGENT_SPACE -qboolean R_CalcTangentVectors(srfVert_t * dv[3]) -{ - int i; - float bb, s, t; - vec3_t bary; - - - /* calculate barycentric basis for the triangle */ - bb = (dv[1]->st[0] - dv[0]->st[0]) * (dv[2]->st[1] - dv[0]->st[1]) - (dv[2]->st[0] - dv[0]->st[0]) * (dv[1]->st[1] - dv[0]->st[1]); - if(fabs(bb) < 0.00000001f) - return qfalse; - - /* do each vertex */ - for(i = 0; i < 3; i++) - { - // calculate s tangent vector - s = dv[i]->st[0] + 10.0f; - t = dv[i]->st[1]; - bary[0] = ((dv[1]->st[0] - s) * (dv[2]->st[1] - t) - (dv[2]->st[0] - s) * (dv[1]->st[1] - t)) / bb; - bary[1] = ((dv[2]->st[0] - s) * (dv[0]->st[1] - t) - (dv[0]->st[0] - s) * (dv[2]->st[1] - t)) / bb; - bary[2] = ((dv[0]->st[0] - s) * (dv[1]->st[1] - t) - (dv[1]->st[0] - s) * (dv[0]->st[1] - t)) / bb; - - dv[i]->tangent[0] = bary[0] * dv[0]->xyz[0] + bary[1] * dv[1]->xyz[0] + bary[2] * dv[2]->xyz[0]; - dv[i]->tangent[1] = bary[0] * dv[0]->xyz[1] + bary[1] * dv[1]->xyz[1] + bary[2] * dv[2]->xyz[1]; - dv[i]->tangent[2] = bary[0] * dv[0]->xyz[2] + bary[1] * dv[1]->xyz[2] + bary[2] * dv[2]->xyz[2]; - - VectorSubtract(dv[i]->tangent, dv[i]->xyz, dv[i]->tangent); - VectorNormalize(dv[i]->tangent); - - // calculate t tangent vector - s = dv[i]->st[0]; - t = dv[i]->st[1] + 10.0f; - bary[0] = ((dv[1]->st[0] - s) * (dv[2]->st[1] - t) - (dv[2]->st[0] - s) * (dv[1]->st[1] - t)) / bb; - bary[1] = ((dv[2]->st[0] - s) * (dv[0]->st[1] - t) - (dv[0]->st[0] - s) * (dv[2]->st[1] - t)) / bb; - bary[2] = ((dv[0]->st[0] - s) * (dv[1]->st[1] - t) - (dv[1]->st[0] - s) * (dv[0]->st[1] - t)) / bb; - - dv[i]->bitangent[0] = bary[0] * dv[0]->xyz[0] + bary[1] * dv[1]->xyz[0] + bary[2] * dv[2]->xyz[0]; - dv[i]->bitangent[1] = bary[0] * dv[0]->xyz[1] + bary[1] * dv[1]->xyz[1] + bary[2] * dv[2]->xyz[1]; - dv[i]->bitangent[2] = bary[0] * dv[0]->xyz[2] + bary[1] * dv[1]->xyz[2] + bary[2] * dv[2]->xyz[2]; - - VectorSubtract(dv[i]->bitangent, dv[i]->xyz, dv[i]->bitangent); - VectorNormalize(dv[i]->bitangent); - - // debug code - //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i, - //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] ); - } - - return qtrue; -} -#endif - - -/* -================= -R_FindSurfaceTriangleWithEdge -Tr3B - recoded from Q2E -================= -*/ -static int R_FindSurfaceTriangleWithEdge(int numTriangles, srfTriangle_t * triangles, int start, int end, int ignore) -{ - srfTriangle_t *tri; - int count, match; - int i; - - count = 0; - match = -1; - - for(i = 0, tri = triangles; i < numTriangles; i++, tri++) - { - if((tri->indexes[0] == start && tri->indexes[1] == end) || - (tri->indexes[1] == start && tri->indexes[2] == end) || (tri->indexes[2] == start && tri->indexes[0] == end)) - { - if(i != ignore) - { - match = i; - } - - count++; - } - else if((tri->indexes[1] == start && tri->indexes[0] == end) || - (tri->indexes[2] == start && tri->indexes[1] == end) || (tri->indexes[0] == start && tri->indexes[2] == end)) - { - count++; - } - } - - // detect edges shared by three triangles and make them seams - if(count > 2) - { - match = -1; - } - - return match; -} - - -/* -================= -R_CalcSurfaceTriangleNeighbors -Tr3B - recoded from Q2E -================= -*/ -void R_CalcSurfaceTriangleNeighbors(int numTriangles, srfTriangle_t * triangles) -{ - int i; - srfTriangle_t *tri; - - for(i = 0, tri = triangles; i < numTriangles; i++, tri++) - { - tri->neighbors[0] = R_FindSurfaceTriangleWithEdge(numTriangles, triangles, tri->indexes[1], tri->indexes[0], i); - tri->neighbors[1] = R_FindSurfaceTriangleWithEdge(numTriangles, triangles, tri->indexes[2], tri->indexes[1], i); - tri->neighbors[2] = R_FindSurfaceTriangleWithEdge(numTriangles, triangles, tri->indexes[0], tri->indexes[2], i); - } -} - -/* -================= -R_CalcSurfaceTrianglePlanes -================= -*/ -void R_CalcSurfaceTrianglePlanes(int numTriangles, srfTriangle_t * triangles, srfVert_t * verts) -{ - int i; - srfTriangle_t *tri; - - for(i = 0, tri = triangles; i < numTriangles; i++, tri++) - { - float *v1, *v2, *v3; - vec3_t d1, d2; - - v1 = verts[tri->indexes[0]].xyz; - v2 = verts[tri->indexes[1]].xyz; - v3 = verts[tri->indexes[2]].xyz; - - VectorSubtract(v2, v1, d1); - VectorSubtract(v3, v1, d2); - - CrossProduct(d2, d1, tri->plane); - tri->plane[3] = DotProduct(tri->plane, v1); - } -} - - -/* -================= -R_CullLocalBox - -Returns CULL_IN, CULL_CLIP, or CULL_OUT -================= -*/ -int R_CullLocalBox(vec3_t localBounds[2]) { -#if 0 - int i, j; - vec3_t transformed[8]; - float dists[8]; - vec3_t v; - cplane_t *frust; - int anyBack; - int front, back; - - if ( r_nocull->integer ) { - return CULL_CLIP; - } - - // transform into world space - for (i = 0 ; i < 8 ; i++) { - v[0] = bounds[i&1][0]; - v[1] = bounds[(i>>1)&1][1]; - v[2] = bounds[(i>>2)&1][2]; - - VectorCopy( tr.or.origin, transformed[i] ); - VectorMA( transformed[i], v[0], tr.or.axis[0], transformed[i] ); - VectorMA( transformed[i], v[1], tr.or.axis[1], transformed[i] ); - VectorMA( transformed[i], v[2], tr.or.axis[2], transformed[i] ); - } - - // check against frustum planes - anyBack = 0; - for (i = 0 ; i < 4 ; i++) { - frust = &tr.viewParms.frustum[i]; - - front = back = 0; - for (j = 0 ; j < 8 ; j++) { - dists[j] = DotProduct(transformed[j], frust->normal); - if ( dists[j] > frust->dist ) { - front = 1; - if ( back ) { - break; // a point is in front - } - } else { - back = 1; - } - } - if ( !front ) { - // all points were behind one of the planes - return CULL_OUT; - } - anyBack |= back; - } - - if ( !anyBack ) { - return CULL_IN; // completely inside frustum - } - - return CULL_CLIP; // partially clipped -#else - int j; - vec3_t transformed; - vec3_t v; - vec3_t worldBounds[2]; - - if(r_nocull->integer) - { - return CULL_CLIP; - } - - // transform into world space - ClearBounds(worldBounds[0], worldBounds[1]); - - for(j = 0; j < 8; j++) - { - v[0] = localBounds[j & 1][0]; - v[1] = localBounds[(j >> 1) & 1][1]; - v[2] = localBounds[(j >> 2) & 1][2]; - - R_LocalPointToWorld(v, transformed); - - AddPointToBounds(transformed, worldBounds[0], worldBounds[1]); - } - - return R_CullBox(worldBounds); -#endif -} - -/* -================= -R_CullBox - -Returns CULL_IN, CULL_CLIP, or CULL_OUT -================= -*/ -int R_CullBox(vec3_t worldBounds[2]) { - int i; - cplane_t *frust; - qboolean anyClip; - int r, numPlanes; - - numPlanes = (tr.viewParms.flags & VPF_FARPLANEFRUSTUM) ? 5 : 4; - - // check against frustum planes - anyClip = qfalse; - for(i = 0; i < numPlanes; i++) - { - frust = &tr.viewParms.frustum[i]; - - r = BoxOnPlaneSide(worldBounds[0], worldBounds[1], frust); - - if(r == 2) - { - // completely outside frustum - return CULL_OUT; - } - if(r == 3) - { - anyClip = qtrue; - } - } - - if(!anyClip) - { - // completely inside frustum - return CULL_IN; - } - - // partially clipped - return CULL_CLIP; -} - -/* -** R_CullLocalPointAndRadius -*/ -int R_CullLocalPointAndRadius( const vec3_t pt, float radius ) -{ - vec3_t transformed; - - R_LocalPointToWorld( pt, transformed ); - - return R_CullPointAndRadius( transformed, radius ); -} - -/* -** R_CullPointAndRadius -*/ -int R_CullPointAndRadiusEx( const vec3_t pt, float radius, const cplane_t* frustum, int numPlanes ) -{ - int i; - float dist; - const cplane_t *frust; - qboolean mightBeClipped = qfalse; - - if ( r_nocull->integer ) { - return CULL_CLIP; - } - - // check against frustum planes - for (i = 0 ; i < numPlanes ; i++) - { - frust = &frustum[i]; - - dist = DotProduct( pt, frust->normal) - frust->dist; - if ( dist < -radius ) - { - return CULL_OUT; - } - else if ( dist <= radius ) - { - mightBeClipped = qtrue; - } - } - - if ( mightBeClipped ) - { - return CULL_CLIP; - } - - return CULL_IN; // completely inside frustum -} - -/* -** R_CullPointAndRadius -*/ -int R_CullPointAndRadius( const vec3_t pt, float radius ) -{ - return R_CullPointAndRadiusEx(pt, radius, tr.viewParms.frustum, (tr.viewParms.flags & VPF_FARPLANEFRUSTUM) ? 5 : 4); -} - -/* -================= -R_LocalNormalToWorld - -================= -*/ -void R_LocalNormalToWorld (const vec3_t local, vec3_t world) { - world[0] = local[0] * tr.or.axis[0][0] + local[1] * tr.or.axis[1][0] + local[2] * tr.or.axis[2][0]; - world[1] = local[0] * tr.or.axis[0][1] + local[1] * tr.or.axis[1][1] + local[2] * tr.or.axis[2][1]; - world[2] = local[0] * tr.or.axis[0][2] + local[1] * tr.or.axis[1][2] + local[2] * tr.or.axis[2][2]; -} - -/* -================= -R_LocalPointToWorld - -================= -*/ -void R_LocalPointToWorld (const vec3_t local, vec3_t world) { - world[0] = local[0] * tr.or.axis[0][0] + local[1] * tr.or.axis[1][0] + local[2] * tr.or.axis[2][0] + tr.or.origin[0]; - world[1] = local[0] * tr.or.axis[0][1] + local[1] * tr.or.axis[1][1] + local[2] * tr.or.axis[2][1] + tr.or.origin[1]; - world[2] = local[0] * tr.or.axis[0][2] + local[1] * tr.or.axis[1][2] + local[2] * tr.or.axis[2][2] + tr.or.origin[2]; -} - -/* -================= -R_WorldToLocal - -================= -*/ -void R_WorldToLocal (const vec3_t world, vec3_t local) { - local[0] = DotProduct(world, tr.or.axis[0]); - local[1] = DotProduct(world, tr.or.axis[1]); - local[2] = DotProduct(world, tr.or.axis[2]); -} - -/* -========================== -R_TransformModelToClip - -========================== -*/ -void R_TransformModelToClip( const vec3_t src, const float *modelMatrix, const float *projectionMatrix, - vec4_t eye, vec4_t dst ) { - int i; - - for ( i = 0 ; i < 4 ; i++ ) { - eye[i] = - src[0] * modelMatrix[ i + 0 * 4 ] + - src[1] * modelMatrix[ i + 1 * 4 ] + - src[2] * modelMatrix[ i + 2 * 4 ] + - 1 * modelMatrix[ i + 3 * 4 ]; - } - - for ( i = 0 ; i < 4 ; i++ ) { - dst[i] = - eye[0] * projectionMatrix[ i + 0 * 4 ] + - eye[1] * projectionMatrix[ i + 1 * 4 ] + - eye[2] * projectionMatrix[ i + 2 * 4 ] + - eye[3] * projectionMatrix[ i + 3 * 4 ]; - } -} - -/* -========================== -R_TransformClipToWindow - -========================== -*/ -void R_TransformClipToWindow( const vec4_t clip, const viewParms_t *view, vec4_t normalized, vec4_t window ) { - normalized[0] = clip[0] / clip[3]; - normalized[1] = clip[1] / clip[3]; - normalized[2] = ( clip[2] + clip[3] ) / ( 2 * clip[3] ); - - window[0] = 0.5f * ( 1.0f + normalized[0] ) * view->viewportWidth; - window[1] = 0.5f * ( 1.0f + normalized[1] ) * view->viewportHeight; - window[2] = normalized[2]; - - window[0] = (int) ( window[0] + 0.5 ); - window[1] = (int) ( window[1] + 0.5 ); -} - - -/* -========================== -myGlMultMatrix - -========================== -*/ -void myGlMultMatrix( const float *a, const float *b, float *out ) { - int i, j; - - for ( i = 0 ; i < 4 ; i++ ) { - for ( j = 0 ; j < 4 ; j++ ) { - out[ i * 4 + j ] = - a [ i * 4 + 0 ] * b [ 0 * 4 + j ] - + a [ i * 4 + 1 ] * b [ 1 * 4 + j ] - + a [ i * 4 + 2 ] * b [ 2 * 4 + j ] - + a [ i * 4 + 3 ] * b [ 3 * 4 + j ]; - } - } -} - -/* -================= -R_RotateForEntity - -Generates an orientation for an entity and viewParms -Does NOT produce any GL calls -Called by both the front end and the back end -================= -*/ -void R_RotateForEntity( const trRefEntity_t *ent, const viewParms_t *viewParms, - orientationr_t *or ) { - float glMatrix[16]; - vec3_t delta; - float axisLength; - - if ( ent->e.reType != RT_MODEL ) { - *or = viewParms->world; - return; - } - - VectorCopy( ent->e.origin, or->origin ); - - VectorCopy( ent->e.axis[0], or->axis[0] ); - VectorCopy( ent->e.axis[1], or->axis[1] ); - VectorCopy( ent->e.axis[2], or->axis[2] ); - - glMatrix[0] = or->axis[0][0]; - glMatrix[4] = or->axis[1][0]; - glMatrix[8] = or->axis[2][0]; - glMatrix[12] = or->origin[0]; - - glMatrix[1] = or->axis[0][1]; - glMatrix[5] = or->axis[1][1]; - glMatrix[9] = or->axis[2][1]; - glMatrix[13] = or->origin[1]; - - glMatrix[2] = or->axis[0][2]; - glMatrix[6] = or->axis[1][2]; - glMatrix[10] = or->axis[2][2]; - glMatrix[14] = or->origin[2]; - - glMatrix[3] = 0; - glMatrix[7] = 0; - glMatrix[11] = 0; - glMatrix[15] = 1; - - Matrix16Copy(glMatrix, or->transformMatrix); - myGlMultMatrix( glMatrix, viewParms->world.modelMatrix, or->modelMatrix ); - - // calculate the viewer origin in the model's space - // needed for fog, specular, and environment mapping - VectorSubtract( viewParms->or.origin, or->origin, delta ); - - // compensate for scale in the axes if necessary - if ( ent->e.nonNormalizedAxes ) { - axisLength = VectorLength( ent->e.axis[0] ); - if ( !axisLength ) { - axisLength = 0; - } else { - axisLength = 1.0f / axisLength; - } - } else { - axisLength = 1.0f; - } - - or->viewOrigin[0] = DotProduct( delta, or->axis[0] ) * axisLength; - or->viewOrigin[1] = DotProduct( delta, or->axis[1] ) * axisLength; - or->viewOrigin[2] = DotProduct( delta, or->axis[2] ) * axisLength; -} - -/* -================= -R_RotateForViewer - -Sets up the modelview matrix for a given viewParm -================= -*/ -void R_RotateForViewer (void) -{ - float viewerMatrix[16]; - vec3_t origin; - - Com_Memset (&tr.or, 0, sizeof(tr.or)); - tr.or.axis[0][0] = 1; - tr.or.axis[1][1] = 1; - tr.or.axis[2][2] = 1; - VectorCopy (tr.viewParms.or.origin, tr.or.viewOrigin); - - // transform by the camera placement - VectorCopy( tr.viewParms.or.origin, origin ); - - viewerMatrix[0] = tr.viewParms.or.axis[0][0]; - viewerMatrix[4] = tr.viewParms.or.axis[0][1]; - viewerMatrix[8] = tr.viewParms.or.axis[0][2]; - viewerMatrix[12] = -origin[0] * viewerMatrix[0] + -origin[1] * viewerMatrix[4] + -origin[2] * viewerMatrix[8]; - - viewerMatrix[1] = tr.viewParms.or.axis[1][0]; - viewerMatrix[5] = tr.viewParms.or.axis[1][1]; - viewerMatrix[9] = tr.viewParms.or.axis[1][2]; - viewerMatrix[13] = -origin[0] * viewerMatrix[1] + -origin[1] * viewerMatrix[5] + -origin[2] * viewerMatrix[9]; - - viewerMatrix[2] = tr.viewParms.or.axis[2][0]; - viewerMatrix[6] = tr.viewParms.or.axis[2][1]; - viewerMatrix[10] = tr.viewParms.or.axis[2][2]; - viewerMatrix[14] = -origin[0] * viewerMatrix[2] + -origin[1] * viewerMatrix[6] + -origin[2] * viewerMatrix[10]; - - viewerMatrix[3] = 0; - viewerMatrix[7] = 0; - viewerMatrix[11] = 0; - viewerMatrix[15] = 1; - - // convert from our coordinate system (looking down X) - // to OpenGL's coordinate system (looking down -Z) - myGlMultMatrix( viewerMatrix, s_flipMatrix, tr.or.modelMatrix ); - - tr.viewParms.world = tr.or; - -} - -/* -** SetFarClip -*/ -static void R_SetFarClip( void ) -{ - float farthestCornerDistance = 0; - int i; - - // if not rendering the world (icons, menus, etc) - // set a 2k far clip plane - if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { - tr.viewParms.zFar = 2048; - return; - } - - // - // set far clipping planes dynamically - // - farthestCornerDistance = 0; - for ( i = 0; i < 8; i++ ) - { - vec3_t v; - vec3_t vecTo; - float distance; - - if ( i & 1 ) - { - v[0] = tr.viewParms.visBounds[0][0]; - } - else - { - v[0] = tr.viewParms.visBounds[1][0]; - } - - if ( i & 2 ) - { - v[1] = tr.viewParms.visBounds[0][1]; - } - else - { - v[1] = tr.viewParms.visBounds[1][1]; - } - - if ( i & 4 ) - { - v[2] = tr.viewParms.visBounds[0][2]; - } - else - { - v[2] = tr.viewParms.visBounds[1][2]; - } - - VectorSubtract( v, tr.viewParms.or.origin, vecTo ); - - distance = vecTo[0] * vecTo[0] + vecTo[1] * vecTo[1] + vecTo[2] * vecTo[2]; - - if ( distance > farthestCornerDistance ) - { - farthestCornerDistance = distance; - } - } - tr.viewParms.zFar = sqrt( farthestCornerDistance ); -} - -/* -================= -R_SetupFrustum - -Set up the culling frustum planes for the current view using the results we got from computing the first two rows of -the projection matrix. -================= -*/ -void R_SetupFrustum (viewParms_t *dest, float xmin, float xmax, float ymax, float zProj, float zFar, float stereoSep) -{ - vec3_t ofsorigin; - float oppleg, adjleg, length; - int i; - - if(stereoSep == 0 && xmin == -xmax) - { - // symmetric case can be simplified - VectorCopy(dest->or.origin, ofsorigin); - - length = sqrt(xmax * xmax + zProj * zProj); - oppleg = xmax / length; - adjleg = zProj / length; - - VectorScale(dest->or.axis[0], oppleg, dest->frustum[0].normal); - VectorMA(dest->frustum[0].normal, adjleg, dest->or.axis[1], dest->frustum[0].normal); - - VectorScale(dest->or.axis[0], oppleg, dest->frustum[1].normal); - VectorMA(dest->frustum[1].normal, -adjleg, dest->or.axis[1], dest->frustum[1].normal); - } - else - { - // In stereo rendering, due to the modification of the projection matrix, dest->or.origin is not the - // actual origin that we're rendering so offset the tip of the view pyramid. - VectorMA(dest->or.origin, stereoSep, dest->or.axis[1], ofsorigin); - - oppleg = xmax + stereoSep; - length = sqrt(oppleg * oppleg + zProj * zProj); - VectorScale(dest->or.axis[0], oppleg / length, dest->frustum[0].normal); - VectorMA(dest->frustum[0].normal, zProj / length, dest->or.axis[1], dest->frustum[0].normal); - - oppleg = xmin + stereoSep; - length = sqrt(oppleg * oppleg + zProj * zProj); - VectorScale(dest->or.axis[0], -oppleg / length, dest->frustum[1].normal); - VectorMA(dest->frustum[1].normal, -zProj / length, dest->or.axis[1], dest->frustum[1].normal); - } - - length = sqrt(ymax * ymax + zProj * zProj); - oppleg = ymax / length; - adjleg = zProj / length; - - VectorScale(dest->or.axis[0], oppleg, dest->frustum[2].normal); - VectorMA(dest->frustum[2].normal, adjleg, dest->or.axis[2], dest->frustum[2].normal); - - VectorScale(dest->or.axis[0], oppleg, dest->frustum[3].normal); - VectorMA(dest->frustum[3].normal, -adjleg, dest->or.axis[2], dest->frustum[3].normal); - - for (i=0 ; i<4 ; i++) { - dest->frustum[i].type = PLANE_NON_AXIAL; - dest->frustum[i].dist = DotProduct (ofsorigin, dest->frustum[i].normal); - SetPlaneSignbits( &dest->frustum[i] ); - } - - if (zFar != 0.0f) - { - vec3_t farpoint; - - VectorMA(ofsorigin, zFar, dest->or.axis[0], farpoint); - VectorScale(dest->or.axis[0], -1.0f, dest->frustum[4].normal); - - dest->frustum[4].type = PLANE_NON_AXIAL; - dest->frustum[4].dist = DotProduct (farpoint, dest->frustum[4].normal); - SetPlaneSignbits( &dest->frustum[4] ); - dest->flags |= VPF_FARPLANEFRUSTUM; - } -} - -/* -=============== -R_SetupProjection -=============== -*/ -void R_SetupProjection(viewParms_t *dest, float zProj, float zFar, qboolean computeFrustum) -{ - float xmin, xmax, ymin, ymax; - float width, height, stereoSep = r_stereoSeparation->value; - - /* - * offset the view origin of the viewer for stereo rendering - * by setting the projection matrix appropriately. - */ - - if(stereoSep != 0) - { - if(dest->stereoFrame == STEREO_LEFT) - stereoSep = zProj / stereoSep; - else if(dest->stereoFrame == STEREO_RIGHT) - stereoSep = zProj / -stereoSep; - else - stereoSep = 0; - } - - ymax = zProj * tan(dest->fovY * M_PI / 360.0f); - ymin = -ymax; - - xmax = zProj * tan(dest->fovX * M_PI / 360.0f); - xmin = -xmax; - - width = xmax - xmin; - height = ymax - ymin; - - dest->projectionMatrix[0] = 2 * zProj / width; - dest->projectionMatrix[4] = 0; - dest->projectionMatrix[8] = (xmax + xmin + 2 * stereoSep) / width; - dest->projectionMatrix[12] = 2 * zProj * stereoSep / width; - - dest->projectionMatrix[1] = 0; - dest->projectionMatrix[5] = 2 * zProj / height; - dest->projectionMatrix[9] = ( ymax + ymin ) / height; // normally 0 - dest->projectionMatrix[13] = 0; - - dest->projectionMatrix[3] = 0; - dest->projectionMatrix[7] = 0; - dest->projectionMatrix[11] = -1; - dest->projectionMatrix[15] = 0; - - // Now that we have all the data for the projection matrix we can also setup the view frustum. - if(computeFrustum) - R_SetupFrustum(dest, xmin, xmax, ymax, zProj, zFar, stereoSep); -} - -/* -=============== -R_SetupProjectionZ - -Sets the z-component transformation part in the projection matrix -=============== -*/ -void R_SetupProjectionZ(viewParms_t *dest) -{ - float zNear, zFar, depth; - - zNear = r_znear->value; - zFar = dest->zFar; - - depth = zFar - zNear; - - dest->projectionMatrix[2] = 0; - dest->projectionMatrix[6] = 0; - dest->projectionMatrix[10] = -( zFar + zNear ) / depth; - dest->projectionMatrix[14] = -2 * zFar * zNear / depth; - - if (dest->isPortal) - { - float plane[4]; - float plane2[4]; - vec4_t q, c; - - // transform portal plane into camera space - plane[0] = dest->portalPlane.normal[0]; - plane[1] = dest->portalPlane.normal[1]; - plane[2] = dest->portalPlane.normal[2]; - plane[3] = dest->portalPlane.dist; - - plane2[0] = -DotProduct (dest->or.axis[1], plane); - plane2[1] = DotProduct (dest->or.axis[2], plane); - plane2[2] = -DotProduct (dest->or.axis[0], plane); - plane2[3] = DotProduct (plane, dest->or.origin) - plane[3]; - - // Lengyel, Eric. "Modifying the Projection Matrix to Perform Oblique Near-plane Clipping". - // Terathon Software 3D Graphics Library, 2004. http://www.terathon.com/code/oblique.html - q[0] = (SGN(plane2[0]) + dest->projectionMatrix[8]) / dest->projectionMatrix[0]; - q[1] = (SGN(plane2[1]) + dest->projectionMatrix[9]) / dest->projectionMatrix[5]; - q[2] = -1.0f; - q[3] = (1.0f + dest->projectionMatrix[10]) / dest->projectionMatrix[14]; - - VectorScale4(plane2, 2.0f / DotProduct4(plane2, q), c); - - dest->projectionMatrix[2] = c[0]; - dest->projectionMatrix[6] = c[1]; - dest->projectionMatrix[10] = c[2] + 1.0f; - dest->projectionMatrix[14] = c[3]; - - } - -} - -/* -=============== -R_SetupProjectionOrtho -=============== -*/ -void R_SetupProjectionOrtho(viewParms_t *dest, vec3_t viewBounds[2]) -{ - float xmin, xmax, ymin, ymax, znear, zfar; - //viewParms_t *dest = &tr.viewParms; - int i; - vec3_t pop; - - // Quake3: Projection: - // - // Z X Y Z - // | / | / - // |/ |/ - // Y--+ +--X - - xmin = viewBounds[0][1]; - xmax = viewBounds[1][1]; - ymin = -viewBounds[1][2]; - ymax = -viewBounds[0][2]; - znear = viewBounds[0][0]; - zfar = viewBounds[1][0]; - - dest->projectionMatrix[0] = 2 / (xmax - xmin); - dest->projectionMatrix[4] = 0; - dest->projectionMatrix[8] = 0; - dest->projectionMatrix[12] = (xmax + xmin) / (xmax - xmin); - - dest->projectionMatrix[1] = 0; - dest->projectionMatrix[5] = 2 / (ymax - ymin); - dest->projectionMatrix[9] = 0; - dest->projectionMatrix[13] = (ymax + ymin) / (ymax - ymin); - - dest->projectionMatrix[2] = 0; - dest->projectionMatrix[6] = 0; - dest->projectionMatrix[10] = -2 / (zfar - znear); - dest->projectionMatrix[14] = -(zfar + znear) / (zfar - znear); - - dest->projectionMatrix[3] = 0; - dest->projectionMatrix[7] = 0; - dest->projectionMatrix[11] = 0; - dest->projectionMatrix[15] = 1; - - VectorScale(dest->or.axis[1], 1.0f, dest->frustum[0].normal); - VectorMA(dest->or.origin, viewBounds[0][1], dest->frustum[0].normal, pop); - dest->frustum[0].dist = DotProduct(pop, dest->frustum[0].normal); - - VectorScale(dest->or.axis[1], -1.0f, dest->frustum[1].normal); - VectorMA(dest->or.origin, -viewBounds[1][1], dest->frustum[1].normal, pop); - dest->frustum[1].dist = DotProduct(pop, dest->frustum[1].normal); - - VectorScale(dest->or.axis[2], 1.0f, dest->frustum[2].normal); - VectorMA(dest->or.origin, viewBounds[0][2], dest->frustum[2].normal, pop); - dest->frustum[2].dist = DotProduct(pop, dest->frustum[2].normal); - - VectorScale(dest->or.axis[2], -1.0f, dest->frustum[3].normal); - VectorMA(dest->or.origin, -viewBounds[1][2], dest->frustum[3].normal, pop); - dest->frustum[3].dist = DotProduct(pop, dest->frustum[3].normal); - - VectorScale(dest->or.axis[0], -1.0f, dest->frustum[4].normal); - VectorMA(dest->or.origin, -viewBounds[1][0], dest->frustum[4].normal, pop); - dest->frustum[4].dist = DotProduct(pop, dest->frustum[4].normal); - - for (i = 0; i < 5; i++) - { - dest->frustum[i].type = PLANE_NON_AXIAL; - SetPlaneSignbits (&dest->frustum[i]); - } - - dest->flags |= VPF_FARPLANEFRUSTUM; -} - -/* -================= -R_MirrorPoint -================= -*/ -void R_MirrorPoint (vec3_t in, orientation_t *surface, orientation_t *camera, vec3_t out) { - int i; - vec3_t local; - vec3_t transformed; - float d; - - VectorSubtract( in, surface->origin, local ); - - VectorClear( transformed ); - for ( i = 0 ; i < 3 ; i++ ) { - d = DotProduct(local, surface->axis[i]); - VectorMA( transformed, d, camera->axis[i], transformed ); - } - - VectorAdd( transformed, camera->origin, out ); -} - -void R_MirrorVector (vec3_t in, orientation_t *surface, orientation_t *camera, vec3_t out) { - int i; - float d; - - VectorClear( out ); - for ( i = 0 ; i < 3 ; i++ ) { - d = DotProduct(in, surface->axis[i]); - VectorMA( out, d, camera->axis[i], out ); - } -} - - -/* -============= -R_PlaneForSurface -============= -*/ -void R_PlaneForSurface (surfaceType_t *surfType, cplane_t *plane) { - srfTriangles_t *tri; - srfPoly_t *poly; - srfVert_t *v1, *v2, *v3; - vec4_t plane4; - - if (!surfType) { - Com_Memset (plane, 0, sizeof(*plane)); - plane->normal[0] = 1; - return; - } - switch (*surfType) { - case SF_FACE: - *plane = ((srfSurfaceFace_t *)surfType)->plane; - return; - case SF_TRIANGLES: - tri = (srfTriangles_t *)surfType; - v1 = tri->verts + tri->triangles[0].indexes[0]; - v2 = tri->verts + tri->triangles[0].indexes[1]; - v3 = tri->verts + tri->triangles[0].indexes[2]; - PlaneFromPoints( plane4, v1->xyz, v2->xyz, v3->xyz ); - VectorCopy( plane4, plane->normal ); - plane->dist = plane4[3]; - return; - case SF_POLY: - poly = (srfPoly_t *)surfType; - PlaneFromPoints( plane4, poly->verts[0].xyz, poly->verts[1].xyz, poly->verts[2].xyz ); - VectorCopy( plane4, plane->normal ); - plane->dist = plane4[3]; - return; - default: - Com_Memset (plane, 0, sizeof(*plane)); - plane->normal[0] = 1; - return; - } -} - -/* -================= -R_GetPortalOrientation - -entityNum is the entity that the portal surface is a part of, which may -be moving and rotating. - -Returns qtrue if it should be mirrored -================= -*/ -qboolean R_GetPortalOrientations( drawSurf_t *drawSurf, int entityNum, - orientation_t *surface, orientation_t *camera, - vec3_t pvsOrigin, qboolean *mirror ) { - int i; - cplane_t originalPlane, plane; - trRefEntity_t *e; - float d; - vec3_t transformed; - - // create plane axis for the portal we are seeing - R_PlaneForSurface( drawSurf->surface, &originalPlane ); - - // rotate the plane if necessary - if ( entityNum != REFENTITYNUM_WORLD ) { - tr.currentEntityNum = entityNum; - tr.currentEntity = &tr.refdef.entities[entityNum]; - - // get the orientation of the entity - R_RotateForEntity( tr.currentEntity, &tr.viewParms, &tr.or ); - - // rotate the plane, but keep the non-rotated version for matching - // against the portalSurface entities - R_LocalNormalToWorld( originalPlane.normal, plane.normal ); - plane.dist = originalPlane.dist + DotProduct( plane.normal, tr.or.origin ); - - // translate the original plane - originalPlane.dist = originalPlane.dist + DotProduct( originalPlane.normal, tr.or.origin ); - } else { - plane = originalPlane; - } - - VectorCopy( plane.normal, surface->axis[0] ); - PerpendicularVector( surface->axis[1], surface->axis[0] ); - CrossProduct( surface->axis[0], surface->axis[1], surface->axis[2] ); - - // locate the portal entity closest to this plane. - // origin will be the origin of the portal, origin2 will be - // the origin of the camera - for ( i = 0 ; i < tr.refdef.num_entities ; i++ ) { - e = &tr.refdef.entities[i]; - if ( e->e.reType != RT_PORTALSURFACE ) { - continue; - } - - d = DotProduct( e->e.origin, originalPlane.normal ) - originalPlane.dist; - if ( d > 64 || d < -64) { - continue; - } - - // get the pvsOrigin from the entity - VectorCopy( e->e.oldorigin, pvsOrigin ); - - // if the entity is just a mirror, don't use as a camera point - if ( e->e.oldorigin[0] == e->e.origin[0] && - e->e.oldorigin[1] == e->e.origin[1] && - e->e.oldorigin[2] == e->e.origin[2] ) { - VectorScale( plane.normal, plane.dist, surface->origin ); - VectorCopy( surface->origin, camera->origin ); - VectorSubtract( vec3_origin, surface->axis[0], camera->axis[0] ); - VectorCopy( surface->axis[1], camera->axis[1] ); - VectorCopy( surface->axis[2], camera->axis[2] ); - - *mirror = qtrue; - return qtrue; - } - - // project the origin onto the surface plane to get - // an origin point we can rotate around - d = DotProduct( e->e.origin, plane.normal ) - plane.dist; - VectorMA( e->e.origin, -d, surface->axis[0], surface->origin ); - - // now get the camera origin and orientation - VectorCopy( e->e.oldorigin, camera->origin ); - AxisCopy( e->e.axis, camera->axis ); - VectorSubtract( vec3_origin, camera->axis[0], camera->axis[0] ); - VectorSubtract( vec3_origin, camera->axis[1], camera->axis[1] ); - - // optionally rotate - if ( e->e.oldframe ) { - // if a speed is specified - if ( e->e.frame ) { - // continuous rotate - d = (tr.refdef.time/1000.0f) * e->e.frame; - VectorCopy( camera->axis[1], transformed ); - RotatePointAroundVector( camera->axis[1], camera->axis[0], transformed, d ); - CrossProduct( camera->axis[0], camera->axis[1], camera->axis[2] ); - } else { - // bobbing rotate, with skinNum being the rotation offset - d = sin( tr.refdef.time * 0.003f ); - d = e->e.skinNum + d * 4; - VectorCopy( camera->axis[1], transformed ); - RotatePointAroundVector( camera->axis[1], camera->axis[0], transformed, d ); - CrossProduct( camera->axis[0], camera->axis[1], camera->axis[2] ); - } - } - else if ( e->e.skinNum ) { - d = e->e.skinNum; - VectorCopy( camera->axis[1], transformed ); - RotatePointAroundVector( camera->axis[1], camera->axis[0], transformed, d ); - CrossProduct( camera->axis[0], camera->axis[1], camera->axis[2] ); - } - *mirror = qfalse; - return qtrue; - } - - // if we didn't locate a portal entity, don't render anything. - // We don't want to just treat it as a mirror, because without a - // portal entity the server won't have communicated a proper entity set - // in the snapshot - - // unfortunately, with local movement prediction it is easily possible - // to see a surface before the server has communicated the matching - // portal surface entity, so we don't want to print anything here... - - //ri.Printf( PRINT_ALL, "Portal surface without a portal entity\n" ); - - return qfalse; -} - -static qboolean IsMirror( const drawSurf_t *drawSurf, int entityNum ) -{ - int i; - cplane_t originalPlane, plane; - trRefEntity_t *e; - float d; - - // create plane axis for the portal we are seeing - R_PlaneForSurface( drawSurf->surface, &originalPlane ); - - // rotate the plane if necessary - if ( entityNum != REFENTITYNUM_WORLD ) - { - tr.currentEntityNum = entityNum; - tr.currentEntity = &tr.refdef.entities[entityNum]; - - // get the orientation of the entity - R_RotateForEntity( tr.currentEntity, &tr.viewParms, &tr.or ); - - // rotate the plane, but keep the non-rotated version for matching - // against the portalSurface entities - R_LocalNormalToWorld( originalPlane.normal, plane.normal ); - plane.dist = originalPlane.dist + DotProduct( plane.normal, tr.or.origin ); - - // translate the original plane - originalPlane.dist = originalPlane.dist + DotProduct( originalPlane.normal, tr.or.origin ); - } - else - { - plane = originalPlane; - } - - // locate the portal entity closest to this plane. - // origin will be the origin of the portal, origin2 will be - // the origin of the camera - for ( i = 0 ; i < tr.refdef.num_entities ; i++ ) - { - e = &tr.refdef.entities[i]; - if ( e->e.reType != RT_PORTALSURFACE ) { - continue; - } - - d = DotProduct( e->e.origin, originalPlane.normal ) - originalPlane.dist; - if ( d > 64 || d < -64) { - continue; - } - - // if the entity is just a mirror, don't use as a camera point - if ( e->e.oldorigin[0] == e->e.origin[0] && - e->e.oldorigin[1] == e->e.origin[1] && - e->e.oldorigin[2] == e->e.origin[2] ) - { - return qtrue; - } - - return qfalse; - } - return qfalse; -} - -/* -** SurfIsOffscreen -** -** Determines if a surface is completely offscreen. -*/ -static qboolean SurfIsOffscreen( const drawSurf_t *drawSurf, vec4_t clipDest[128] ) { - float shortest = 100000000; - int entityNum; - int numTriangles; - shader_t *shader; - int fogNum; - int dlighted; - int pshadowed; - vec4_t clip, eye; - int i; - unsigned int pointOr = 0; - unsigned int pointAnd = (unsigned int)~0; - - R_RotateForViewer(); - - R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted, &pshadowed ); - RB_BeginSurface( shader, fogNum ); - rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface ); - - assert( tess.numVertexes < 128 ); - - for ( i = 0; i < tess.numVertexes; i++ ) - { - int j; - unsigned int pointFlags = 0; - - R_TransformModelToClip( tess.xyz[i], tr.or.modelMatrix, tr.viewParms.projectionMatrix, eye, clip ); - - for ( j = 0; j < 3; j++ ) - { - if ( clip[j] >= clip[3] ) - { - pointFlags |= (1 << (j*2)); - } - else if ( clip[j] <= -clip[3] ) - { - pointFlags |= ( 1 << (j*2+1)); - } - } - pointAnd &= pointFlags; - pointOr |= pointFlags; - } - - // trivially reject - if ( pointAnd ) - { - return qtrue; - } - - // determine if this surface is backfaced and also determine the distance - // to the nearest vertex so we can cull based on portal range. Culling - // based on vertex distance isn't 100% correct (we should be checking for - // range to the surface), but it's good enough for the types of portals - // we have in the game right now. - numTriangles = tess.numIndexes / 3; - - for ( i = 0; i < tess.numIndexes; i += 3 ) - { - vec3_t normal; - float len; - - VectorSubtract( tess.xyz[tess.indexes[i]], tr.viewParms.or.origin, normal ); - - len = VectorLengthSquared( normal ); // lose the sqrt - if ( len < shortest ) - { - shortest = len; - } - - if ( DotProduct( normal, tess.normal[tess.indexes[i]] ) >= 0 ) - { - numTriangles--; - } - } - if ( !numTriangles ) - { - return qtrue; - } - - // mirrors can early out at this point, since we don't do a fade over distance - // with them (although we could) - if ( IsMirror( drawSurf, entityNum ) ) - { - return qfalse; - } - - if ( shortest > (tess.shader->portalRange*tess.shader->portalRange) ) - { - return qtrue; - } - - return qfalse; -} - -/* -======================== -R_MirrorViewBySurface - -Returns qtrue if another view has been rendered -======================== -*/ -qboolean R_MirrorViewBySurface (drawSurf_t *drawSurf, int entityNum) { - vec4_t clipDest[128]; - viewParms_t newParms; - viewParms_t oldParms; - orientation_t surface, camera; - - // don't recursively mirror - if (tr.viewParms.isPortal) { - ri.Printf( PRINT_DEVELOPER, "WARNING: recursive mirror/portal found\n" ); - return qfalse; - } - - if ( r_noportals->integer || (r_fastsky->integer == 1) ) { - return qfalse; - } - - // trivially reject portal/mirror - if ( SurfIsOffscreen( drawSurf, clipDest ) ) { - return qfalse; - } - - // save old viewParms so we can return to it after the mirror view - oldParms = tr.viewParms; - - newParms = tr.viewParms; - newParms.isPortal = qtrue; - newParms.zFar = 0.0f; - newParms.flags &= ~VPF_FARPLANEFRUSTUM; - if ( !R_GetPortalOrientations( drawSurf, entityNum, &surface, &camera, - newParms.pvsOrigin, &newParms.isMirror ) ) { - return qfalse; // bad portal, no portalentity - } - - R_MirrorPoint (oldParms.or.origin, &surface, &camera, newParms.or.origin ); - - VectorSubtract( vec3_origin, camera.axis[0], newParms.portalPlane.normal ); - newParms.portalPlane.dist = DotProduct( camera.origin, newParms.portalPlane.normal ); - - R_MirrorVector (oldParms.or.axis[0], &surface, &camera, newParms.or.axis[0]); - R_MirrorVector (oldParms.or.axis[1], &surface, &camera, newParms.or.axis[1]); - R_MirrorVector (oldParms.or.axis[2], &surface, &camera, newParms.or.axis[2]); - - // OPTIMIZE: restrict the viewport on the mirrored view - - // render the mirror view - R_RenderView (&newParms); - - tr.viewParms = oldParms; - - return qtrue; -} - -/* -================= -R_SpriteFogNum - -See if a sprite is inside a fog volume -================= -*/ -int R_SpriteFogNum( trRefEntity_t *ent ) { - int i, j; - fog_t *fog; - - if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { - return 0; - } - - for ( i = 1 ; i < tr.world->numfogs ; i++ ) { - fog = &tr.world->fogs[i]; - for ( j = 0 ; j < 3 ; j++ ) { - if ( ent->e.origin[j] - ent->e.radius >= fog->bounds[1][j] ) { - break; - } - if ( ent->e.origin[j] + ent->e.radius <= fog->bounds[0][j] ) { - break; - } - } - if ( j == 3 ) { - return i; - } - } - - return 0; -} - -/* -========================================================================================== - -DRAWSURF SORTING - -========================================================================================== -*/ - -/* -=============== -R_Radix -=============== -*/ -static ID_INLINE void R_Radix( int byte, int size, drawSurf_t *source, drawSurf_t *dest ) -{ - int count[ 256 ] = { 0 }; - int index[ 256 ]; - int i; - unsigned char *sortKey = NULL; - unsigned char *end = NULL; - - sortKey = ( (unsigned char *)&source[ 0 ].sort ) + byte; - end = sortKey + ( size * sizeof( drawSurf_t ) ); - for( ; sortKey < end; sortKey += sizeof( drawSurf_t ) ) - ++count[ *sortKey ]; - - index[ 0 ] = 0; - - for( i = 1; i < 256; ++i ) - index[ i ] = index[ i - 1 ] + count[ i - 1 ]; - - sortKey = ( (unsigned char *)&source[ 0 ].sort ) + byte; - for( i = 0; i < size; ++i, sortKey += sizeof( drawSurf_t ) ) - dest[ index[ *sortKey ]++ ] = source[ i ]; -} - -/* -=============== -R_RadixSort - -Radix sort with 4 byte size buckets -=============== -*/ -static void R_RadixSort( drawSurf_t *source, int size ) -{ - static drawSurf_t scratch[ MAX_DRAWSURFS ]; -#ifdef Q3_LITTLE_ENDIAN - R_Radix( 0, size, source, scratch ); - R_Radix( 1, size, scratch, source ); - R_Radix( 2, size, source, scratch ); - R_Radix( 3, size, scratch, source ); -#else - R_Radix( 3, size, source, scratch ); - R_Radix( 2, size, scratch, source ); - R_Radix( 1, size, source, scratch ); - R_Radix( 0, size, scratch, source ); -#endif //Q3_LITTLE_ENDIAN -} - -//========================================================================================== - -/* -================= -R_AddDrawSurf -================= -*/ -void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, - int fogIndex, int dlightMap, int pshadowMap ) { - int index; - - // instead of checking for overflow, we just mask the index - // so it wraps around - index = tr.refdef.numDrawSurfs & DRAWSURF_MASK; - // the sort data is packed into a single 32 bit value so it can be - // compared quickly during the qsorting process - tr.refdef.drawSurfs[index].sort = (shader->sortedIndex << QSORT_SHADERNUM_SHIFT) - | tr.shiftedEntityNum | ( fogIndex << QSORT_FOGNUM_SHIFT ) - | ((int)pshadowMap << QSORT_PSHADOW_SHIFT) | (int)dlightMap; - tr.refdef.drawSurfs[index].surface = surface; - tr.refdef.numDrawSurfs++; -} - -/* -================= -R_DecomposeSort -================= -*/ -void R_DecomposeSort( unsigned sort, int *entityNum, shader_t **shader, - int *fogNum, int *dlightMap, int *pshadowMap ) { - *fogNum = ( sort >> QSORT_FOGNUM_SHIFT ) & 31; - *shader = tr.sortedShaders[ ( sort >> QSORT_SHADERNUM_SHIFT ) & (MAX_SHADERS-1) ]; - *entityNum = ( sort >> QSORT_REFENTITYNUM_SHIFT ) & REFENTITYNUM_MASK; - *pshadowMap = (sort >> QSORT_PSHADOW_SHIFT ) & 1; - *dlightMap = sort & 1; -} - -/* -================= -R_SortDrawSurfs -================= -*/ -void R_SortDrawSurfs( drawSurf_t *drawSurfs, int numDrawSurfs ) { - shader_t *shader; - int fogNum; - int entityNum; - int dlighted; - int pshadowed; - int i; - - //ri.Printf(PRINT_ALL, "firstDrawSurf %d numDrawSurfs %d\n", (int)(drawSurfs - tr.refdef.drawSurfs), numDrawSurfs); - - // it is possible for some views to not have any surfaces - if ( numDrawSurfs < 1 ) { - // we still need to add it for hyperspace cases - R_AddDrawSurfCmd( drawSurfs, numDrawSurfs ); - return; - } - - // if we overflowed MAX_DRAWSURFS, the drawsurfs - // wrapped around in the buffer and we will be missing - // the first surfaces, not the last ones - if ( numDrawSurfs > MAX_DRAWSURFS ) { - numDrawSurfs = MAX_DRAWSURFS; - } - - // sort the drawsurfs by sort type, then orientation, then shader - R_RadixSort( drawSurfs, numDrawSurfs ); - - // skip pass through drawing if rendering a shadow map - if (tr.viewParms.flags & (VPF_SHADOWMAP | VPF_DEPTHSHADOW)) - { - R_AddDrawSurfCmd( drawSurfs, numDrawSurfs ); - return; - } - - // check for any pass through drawing, which - // may cause another view to be rendered first - for ( i = 0 ; i < numDrawSurfs ; i++ ) { - R_DecomposeSort( (drawSurfs+i)->sort, &entityNum, &shader, &fogNum, &dlighted, &pshadowed ); - - if ( shader->sort > SS_PORTAL ) { - break; - } - - // no shader should ever have this sort type - if ( shader->sort == SS_BAD ) { - ri.Error (ERR_DROP, "Shader '%s'with sort == SS_BAD", shader->name ); - } - - // if the mirror was completely clipped away, we may need to check another surface - if ( R_MirrorViewBySurface( (drawSurfs+i), entityNum) ) { - // this is a debug option to see exactly what is being mirrored - if ( r_portalOnly->integer ) { - return; - } - break; // only one mirror view at a time - } - } - - R_AddDrawSurfCmd( drawSurfs, numDrawSurfs ); -} - -static void R_AddEntitySurface (int entityNum) -{ - trRefEntity_t *ent; - shader_t *shader; - - tr.currentEntityNum = entityNum; - - ent = tr.currentEntity = &tr.refdef.entities[tr.currentEntityNum]; - - ent->needDlights = qfalse; - - // preshift the value we are going to OR into the drawsurf sort - tr.shiftedEntityNum = tr.currentEntityNum << QSORT_REFENTITYNUM_SHIFT; - - // - // the weapon model must be handled special -- - // we don't want the hacked weapon position showing in - // mirrors, because the true body position will already be drawn - // - if ( (ent->e.renderfx & RF_FIRST_PERSON) && (tr.viewParms.isPortal - || (tr.viewParms.flags & (VPF_SHADOWMAP | VPF_DEPTHSHADOW))) ) { - return; - } - - // simple generated models, like sprites and beams, are not culled - switch ( ent->e.reType ) { - case RT_PORTALSURFACE: - break; // don't draw anything - case RT_SPRITE: - case RT_BEAM: - case RT_LIGHTNING: - case RT_RAIL_CORE: - case RT_RAIL_RINGS: - // self blood sprites, talk balloons, etc should not be drawn in the primary - // view. We can't just do this check for all entities, because md3 - // entities may still want to cast shadows from them - if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal) { - return; - } - shader = R_GetShaderByHandle( ent->e.customShader ); - R_AddDrawSurf( &entitySurface, shader, R_SpriteFogNum( ent ), 0, 0 ); - break; - - case RT_MODEL: - // we must set up parts of tr.or for model culling - R_RotateForEntity( ent, &tr.viewParms, &tr.or ); - - tr.currentModel = R_GetModelByHandle( ent->e.hModel ); - if (!tr.currentModel) { - R_AddDrawSurf( &entitySurface, tr.defaultShader, 0, 0, 0 ); - } else { - switch ( tr.currentModel->type ) { - case MOD_MESH: - R_AddMD3Surfaces( ent ); - break; - case MOD_MD4: - R_AddAnimSurfaces( ent ); - break; -#ifdef RAVENMD4 - case MOD_MDR: - R_MDRAddAnimSurfaces( ent ); - break; -#endif - case MOD_IQM: - R_AddIQMSurfaces( ent ); - break; - case MOD_BRUSH: - R_AddBrushModelSurfaces( ent ); - break; - case MOD_BAD: // null model axis - if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal) { - break; - } - R_AddDrawSurf( &entitySurface, tr.defaultShader, 0, 0, 0 ); - break; - default: - ri.Error( ERR_DROP, "R_AddEntitySurfaces: Bad modeltype" ); - break; - } - } - break; - default: - ri.Error( ERR_DROP, "R_AddEntitySurfaces: Bad reType" ); - } -} - -/* -============= -R_AddEntitySurfaces -============= -*/ -void R_AddEntitySurfaces (void) { - int i; - - if ( !r_drawentities->integer ) { - return; - } - - for ( i = 0; i < tr.refdef.num_entities; i++) - R_AddEntitySurface(i); -} - - -/* -==================== -R_GenerateDrawSurfs -==================== -*/ -void R_GenerateDrawSurfs( void ) { - R_AddWorldSurfaces (); - - R_AddPolygonSurfaces(); - - // set the projection matrix with the minimum zfar - // now that we have the world bounded - // this needs to be done before entities are - // added, because they use the projection - // matrix for lod calculation - - // dynamically compute far clip plane distance - if (!(tr.viewParms.flags & VPF_SHADOWMAP)) - { - R_SetFarClip(); - } - - // we know the size of the clipping volume. Now set the rest of the projection matrix. - R_SetupProjectionZ (&tr.viewParms); - - R_AddEntitySurfaces (); -} - -/* -================ -R_DebugPolygon -================ -*/ -void R_DebugPolygon( int color, int numPoints, float *points ) { - // FIXME: implement this -#if 0 - int i; - - GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); - - // draw solid shade - - qglColor3f( color&1, (color>>1)&1, (color>>2)&1 ); - qglBegin( GL_POLYGON ); - for ( i = 0 ; i < numPoints ; i++ ) { - qglVertex3fv( points + i * 3 ); - } - qglEnd(); - - // draw wireframe outline - GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); - qglDepthRange( 0, 0 ); - qglColor3f( 1, 1, 1 ); - qglBegin( GL_POLYGON ); - for ( i = 0 ; i < numPoints ; i++ ) { - qglVertex3fv( points + i * 3 ); - } - qglEnd(); - qglDepthRange( 0, 1 ); -#endif -} - -/* -==================== -R_DebugGraphics - -Visualization aid for movement clipping debugging -==================== -*/ -void R_DebugGraphics( void ) { - if ( !r_debugSurface->integer ) { - return; - } - - R_IssuePendingRenderCommands(); - - GL_Bind( tr.whiteImage); - GL_Cull( CT_FRONT_SIDED ); - ri.CM_DrawDebugSurface( R_DebugPolygon ); -} - - -/* -================ -R_RenderView - -A view may be either the actual camera view, -or a mirror / remote location -================ -*/ -void R_RenderView (viewParms_t *parms) { - int firstDrawSurf; - - if ( parms->viewportWidth <= 0 || parms->viewportHeight <= 0 ) { - return; - } - - tr.viewCount++; - - tr.viewParms = *parms; - tr.viewParms.frameSceneNum = tr.frameSceneNum; - tr.viewParms.frameCount = tr.frameCount; - - firstDrawSurf = tr.refdef.numDrawSurfs; - - tr.viewCount++; - - // set viewParms.world - R_RotateForViewer (); - - R_SetupProjection(&tr.viewParms, r_zproj->value, tr.viewParms.zFar, qtrue); - - R_GenerateDrawSurfs(); - - R_SortDrawSurfs( tr.refdef.drawSurfs + firstDrawSurf, tr.refdef.numDrawSurfs - firstDrawSurf ); - - // draw main system development information (surface outlines, etc) - R_DebugGraphics(); -} - - -void R_RenderDlightCubemaps(const refdef_t *fd) -{ - int i; - - for (i = 0; i < tr.refdef.num_dlights; i++) - { - viewParms_t shadowParms; - int j; - - // use previous frame to determine visible dlights - if ((1 << i) & tr.refdef.dlightMask) - continue; - - Com_Memset( &shadowParms, 0, sizeof( shadowParms ) ); - - shadowParms.viewportX = tr.refdef.x; - shadowParms.viewportY = glConfig.vidHeight - ( tr.refdef.y + PSHADOW_MAP_SIZE ); - shadowParms.viewportWidth = PSHADOW_MAP_SIZE; - shadowParms.viewportHeight = PSHADOW_MAP_SIZE; - shadowParms.isPortal = qfalse; - shadowParms.isMirror = qtrue; // because it is - - shadowParms.fovX = 90; - shadowParms.fovY = 90; - - shadowParms.flags = VPF_SHADOWMAP | VPF_DEPTHSHADOW; - shadowParms.zFar = tr.refdef.dlights[i].radius; - - VectorCopy( tr.refdef.dlights[i].origin, shadowParms.or.origin ); - - for (j = 0; j < 6; j++) - { - switch(j) - { - case 0: - // -X - VectorSet( shadowParms.or.axis[0], -1, 0, 0); - VectorSet( shadowParms.or.axis[1], 0, 0, -1); - VectorSet( shadowParms.or.axis[2], 0, 1, 0); - break; - case 1: - // +X - VectorSet( shadowParms.or.axis[0], 1, 0, 0); - VectorSet( shadowParms.or.axis[1], 0, 0, 1); - VectorSet( shadowParms.or.axis[2], 0, 1, 0); - break; - case 2: - // -Y - VectorSet( shadowParms.or.axis[0], 0, -1, 0); - VectorSet( shadowParms.or.axis[1], 1, 0, 0); - VectorSet( shadowParms.or.axis[2], 0, 0, -1); - break; - case 3: - // +Y - VectorSet( shadowParms.or.axis[0], 0, 1, 0); - VectorSet( shadowParms.or.axis[1], 1, 0, 0); - VectorSet( shadowParms.or.axis[2], 0, 0, 1); - break; - case 4: - // -Z - VectorSet( shadowParms.or.axis[0], 0, 0, -1); - VectorSet( shadowParms.or.axis[1], 1, 0, 0); - VectorSet( shadowParms.or.axis[2], 0, 1, 0); - break; - case 5: - // +Z - VectorSet( shadowParms.or.axis[0], 0, 0, 1); - VectorSet( shadowParms.or.axis[1], -1, 0, 0); - VectorSet( shadowParms.or.axis[2], 0, 1, 0); - break; - } - - R_RenderView(&shadowParms); - R_AddCapShadowmapCmd( i, j ); - } - } -} - - -void R_RenderPshadowMaps(const refdef_t *fd) -{ - viewParms_t shadowParms; - int i; - - // first, make a list of shadows - for ( i = 0; i < tr.refdef.num_entities; i++) - { - trRefEntity_t *ent = &tr.refdef.entities[i]; - - if((ent->e.renderfx & (RF_FIRST_PERSON | RF_NOSHADOW))) - continue; - - //if((ent->e.renderfx & RF_THIRD_PERSON)) - //continue; - - if (ent->e.reType == RT_MODEL) - { - model_t *model = R_GetModelByHandle( ent->e.hModel ); - pshadow_t shadow; - float radius = 0.0f; - float scale = 1.0f; - vec3_t diff; - int j; - - if (!model) - continue; - - if (ent->e.nonNormalizedAxes) - { - scale = VectorLength( ent->e.axis[0] ); - } - - switch (model->type) - { - case MOD_MESH: - { - mdvFrame_t *frame = &model->mdv[0]->frames[ent->e.frame]; - - radius = frame->radius * scale; - } - break; - - case MOD_MD4: - { - // FIXME: actually calculate the radius and bounds, this is a horrible hack - radius = r_pshadowDist->value / 2.0f; - } - break; -#ifdef RAVENMD4 - case MOD_MDR: - { - // FIXME: never actually tested this - mdrHeader_t *header = model->modelData; - int frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] ); - mdrFrame_t *frame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.frame); - - radius = frame->radius; - } - break; -#endif - case MOD_IQM: - { - // FIXME: never actually tested this - iqmData_t *data = model->modelData; - vec3_t diag; - float *framebounds; - - framebounds = data->bounds + 6*ent->e.frame; - VectorSubtract( framebounds+3, framebounds, diag ); - radius = 0.5f * VectorLength( diag ); - } - break; - - default: - break; - } - - if (!radius) - continue; - - // Cull entities that are behind the viewer by more than lightRadius - VectorSubtract(ent->e.origin, fd->vieworg, diff); - if (DotProduct(diff, fd->viewaxis[0]) < -r_pshadowDist->value) - continue; - - memset(&shadow, 0, sizeof(shadow)); - - shadow.numEntities = 1; - shadow.entityNums[0] = i; - shadow.viewRadius = radius; - shadow.lightRadius = r_pshadowDist->value; - VectorCopy(ent->e.origin, shadow.viewOrigin); - shadow.sort = DotProduct(diff, diff) / (radius * radius); - VectorCopy(ent->e.origin, shadow.entityOrigins[0]); - shadow.entityRadiuses[0] = radius; - - for (j = 0; j < MAX_CALC_PSHADOWS; j++) - { - pshadow_t swap; - - if (j + 1 > tr.refdef.num_pshadows) - { - tr.refdef.num_pshadows = j + 1; - tr.refdef.pshadows[j] = shadow; - break; - } - - // sort shadows by distance from camera divided by radius - // FIXME: sort better - if (tr.refdef.pshadows[j].sort <= shadow.sort) - continue; - - swap = tr.refdef.pshadows[j]; - tr.refdef.pshadows[j] = shadow; - shadow = swap; - } - } - } - - // next, merge touching pshadows - for ( i = 0; i < tr.refdef.num_pshadows; i++) - { - pshadow_t *ps1 = &tr.refdef.pshadows[i]; - int j; - - for (j = i + 1; j < tr.refdef.num_pshadows; j++) - { - pshadow_t *ps2 = &tr.refdef.pshadows[j]; - int k; - qboolean touch; - - if (ps1->numEntities == 8) - break; - - touch = qfalse; - if (SpheresIntersect(ps1->viewOrigin, ps1->viewRadius, ps2->viewOrigin, ps2->viewRadius)) - { - for (k = 0; k < ps1->numEntities; k++) - { - if (SpheresIntersect(ps1->entityOrigins[k], ps1->entityRadiuses[k], ps2->viewOrigin, ps2->viewRadius)) - { - touch = qtrue; - break; - } - } - } - - if (touch) - { - vec3_t newOrigin; - float newRadius; - - BoundingSphereOfSpheres(ps1->viewOrigin, ps1->viewRadius, ps2->viewOrigin, ps2->viewRadius, newOrigin, &newRadius); - VectorCopy(newOrigin, ps1->viewOrigin); - ps1->viewRadius = newRadius; - - ps1->entityNums[ps1->numEntities] = ps2->entityNums[0]; - VectorCopy(ps2->viewOrigin, ps1->entityOrigins[ps1->numEntities]); - ps1->entityRadiuses[ps1->numEntities] = ps2->viewRadius; - - ps1->numEntities++; - - for (k = j; k < tr.refdef.num_pshadows - 1; k++) - { - tr.refdef.pshadows[k] = tr.refdef.pshadows[k + 1]; - } - - j--; - tr.refdef.num_pshadows--; - } - } - } - - // cap number of drawn pshadows - if (tr.refdef.num_pshadows > MAX_DRAWN_PSHADOWS) - { - tr.refdef.num_pshadows = MAX_DRAWN_PSHADOWS; - } - - // next, fill up the rest of the shadow info - for ( i = 0; i < tr.refdef.num_pshadows; i++) - { - pshadow_t *shadow = &tr.refdef.pshadows[i]; - vec3_t up; - vec3_t ambientLight, directedLight, lightDir; - - VectorSet(lightDir, 0.57735f, 0.57735f, 0.57735f); -#if 1 - R_LightForPoint(shadow->viewOrigin, ambientLight, directedLight, lightDir); - - // sometimes there's no light - if (DotProduct(lightDir, lightDir) < 0.9f) - VectorSet(lightDir, 0.0f, 0.0f, 1.0f); -#endif - - if (shadow->viewRadius * 3.0f > shadow->lightRadius) - { - shadow->lightRadius = shadow->viewRadius * 3.0f; - } - - VectorMA(shadow->viewOrigin, shadow->viewRadius, lightDir, shadow->lightOrigin); - - // make up a projection, up doesn't matter - VectorScale(lightDir, -1.0f, shadow->lightViewAxis[0]); - VectorSet(up, 0, 0, -1); - - if ( abs(DotProduct(up, shadow->lightViewAxis[0])) > 0.9f ) - { - VectorSet(up, -1, 0, 0); - } - - CrossProduct(shadow->lightViewAxis[0], up, shadow->lightViewAxis[1]); - VectorNormalize(shadow->lightViewAxis[1]); - CrossProduct(shadow->lightViewAxis[0], shadow->lightViewAxis[1], shadow->lightViewAxis[2]); - - VectorCopy(shadow->lightViewAxis[0], shadow->cullPlane.normal); - shadow->cullPlane.dist = DotProduct(shadow->cullPlane.normal, shadow->lightOrigin); - shadow->cullPlane.type = PLANE_NON_AXIAL; - SetPlaneSignbits(&shadow->cullPlane); - } - - // next, render shadowmaps - for ( i = 0; i < tr.refdef.num_pshadows; i++) - { - int firstDrawSurf; - pshadow_t *shadow = &tr.refdef.pshadows[i]; - int j; - - Com_Memset( &shadowParms, 0, sizeof( shadowParms ) ); - - if (glRefConfig.framebufferObject) - { - shadowParms.viewportX = 0; - shadowParms.viewportY = 0; - } - else - { - shadowParms.viewportX = tr.refdef.x; - shadowParms.viewportY = glConfig.vidHeight - ( tr.refdef.y + PSHADOW_MAP_SIZE ); - } - shadowParms.viewportWidth = PSHADOW_MAP_SIZE; - shadowParms.viewportHeight = PSHADOW_MAP_SIZE; - shadowParms.isPortal = qfalse; - shadowParms.isMirror = qfalse; - - shadowParms.fovX = 90; - shadowParms.fovY = 90; - - if (glRefConfig.framebufferObject) - shadowParms.targetFbo = tr.pshadowFbos[i]; - - shadowParms.flags = VPF_SHADOWMAP | VPF_DEPTHSHADOW; - shadowParms.zFar = shadow->lightRadius; - - VectorCopy(shadow->lightOrigin, shadowParms.or.origin); - - VectorCopy(shadow->lightViewAxis[0], shadowParms.or.axis[0]); - VectorCopy(shadow->lightViewAxis[1], shadowParms.or.axis[1]); - VectorCopy(shadow->lightViewAxis[2], shadowParms.or.axis[2]); - - { - tr.viewCount++; - - tr.viewParms = shadowParms; - tr.viewParms.frameSceneNum = tr.frameSceneNum; - tr.viewParms.frameCount = tr.frameCount; - - firstDrawSurf = tr.refdef.numDrawSurfs; - - tr.viewCount++; - - // set viewParms.world - R_RotateForViewer (); - - { - float xmin, xmax, ymin, ymax, znear, zfar; - viewParms_t *dest = &tr.viewParms; - vec3_t pop; - - xmin = ymin = -shadow->viewRadius; - xmax = ymax = shadow->viewRadius; - znear = 0; - zfar = shadow->lightRadius; - - dest->projectionMatrix[0] = 2 / (xmax - xmin); - dest->projectionMatrix[4] = 0; - dest->projectionMatrix[8] = (xmax + xmin) / (xmax - xmin); - dest->projectionMatrix[12] =0; - - dest->projectionMatrix[1] = 0; - dest->projectionMatrix[5] = 2 / (ymax - ymin); - dest->projectionMatrix[9] = ( ymax + ymin ) / (ymax - ymin); // normally 0 - dest->projectionMatrix[13] = 0; - - dest->projectionMatrix[2] = 0; - dest->projectionMatrix[6] = 0; - dest->projectionMatrix[10] = 2 / (zfar - znear); - dest->projectionMatrix[14] = 0; - - dest->projectionMatrix[3] = 0; - dest->projectionMatrix[7] = 0; - dest->projectionMatrix[11] = 0; - dest->projectionMatrix[15] = 1; - - VectorScale(dest->or.axis[1], 1.0f, dest->frustum[0].normal); - VectorMA(dest->or.origin, -shadow->viewRadius, dest->frustum[0].normal, pop); - dest->frustum[0].dist = DotProduct(pop, dest->frustum[0].normal); - - VectorScale(dest->or.axis[1], -1.0f, dest->frustum[1].normal); - VectorMA(dest->or.origin, -shadow->viewRadius, dest->frustum[1].normal, pop); - dest->frustum[1].dist = DotProduct(pop, dest->frustum[1].normal); - - VectorScale(dest->or.axis[2], 1.0f, dest->frustum[2].normal); - VectorMA(dest->or.origin, -shadow->viewRadius, dest->frustum[2].normal, pop); - dest->frustum[2].dist = DotProduct(pop, dest->frustum[2].normal); - - VectorScale(dest->or.axis[2], -1.0f, dest->frustum[3].normal); - VectorMA(dest->or.origin, -shadow->viewRadius, dest->frustum[3].normal, pop); - dest->frustum[3].dist = DotProduct(pop, dest->frustum[3].normal); - - VectorScale(dest->or.axis[0], -1.0f, dest->frustum[4].normal); - VectorMA(dest->or.origin, -shadow->lightRadius, dest->frustum[4].normal, pop); - dest->frustum[4].dist = DotProduct(pop, dest->frustum[4].normal); - - for (j = 0; j < 5; j++) - { - dest->frustum[j].type = PLANE_NON_AXIAL; - SetPlaneSignbits (&dest->frustum[j]); - } - - dest->flags |= VPF_FARPLANEFRUSTUM; - } - - for (j = 0; j < shadow->numEntities; j++) - { - R_AddEntitySurface(shadow->entityNums[j]); - } - - R_SortDrawSurfs( tr.refdef.drawSurfs + firstDrawSurf, tr.refdef.numDrawSurfs - firstDrawSurf ); - - if (!glRefConfig.framebufferObject) - R_AddCapShadowmapCmd( i, -1 ); - } - } -} - -static float CalcSplit(float n, float f, float i, float m) -{ - return (n * pow(f / n, i / m) + (f - n) * i / m) / 2.0f; -} - - -void R_RenderSunShadowMaps(const refdef_t *fd, int level) -{ - viewParms_t shadowParms; - vec4_t lightDir, lightCol; - vec3_t lightViewAxis[3]; - vec3_t lightOrigin; - float splitZNear, splitZFar, splitBias; - float viewZNear, viewZFar; - vec3_t lightviewBounds[2]; - qboolean lightViewIndependentOfCameraView = qfalse; - - if (r_forceSun->integer == 2) - { - int scale = 32768; - float angle = (fd->time % scale) / (float)scale * M_PI; - lightDir[0] = cos(angle); - lightDir[1] = sin(35.0f * M_PI / 180.0f); - lightDir[2] = sin(angle) * cos(35.0f * M_PI / 180.0f); - lightDir[3] = 0.0f; - - if (1) //((fd->time % (scale * 2)) < scale) - { - lightCol[0] = - lightCol[1] = - lightCol[2] = CLAMP(sin(angle) * 2.0f, 0.0f, 1.0f) * 2.0f; - lightCol[3] = 1.0f; - } - else - { - lightCol[0] = - lightCol[1] = - lightCol[2] = CLAMP(sin(angle) * 2.0f * 0.1f, 0.0f, 0.1f); - lightCol[3] = 1.0f; - } - - VectorCopy4(lightDir, tr.refdef.sunDir); - VectorCopy4(lightCol, tr.refdef.sunCol); - VectorScale4(lightCol, 0.2f, tr.refdef.sunAmbCol); - } - else - { - VectorCopy4(tr.refdef.sunDir, lightDir); - } - - viewZNear = r_shadowCascadeZNear->value; - viewZFar = r_shadowCascadeZFar->value; - splitBias = r_shadowCascadeZBias->value; - - switch(level) - { - case 0: - default: - //splitZNear = r_znear->value; - //splitZFar = 256; - splitZNear = viewZNear; - splitZFar = CalcSplit(viewZNear, viewZFar, 1, 3) + splitBias; - break; - case 1: - splitZNear = CalcSplit(viewZNear, viewZFar, 1, 3) + splitBias; - splitZFar = CalcSplit(viewZNear, viewZFar, 2, 3) + splitBias; - //splitZNear = 256; - //splitZFar = 896; - break; - case 2: - splitZNear = CalcSplit(viewZNear, viewZFar, 2, 3) + splitBias; - splitZFar = viewZFar; - //splitZNear = 896; - //splitZFar = 3072; - break; - } - - VectorCopy(fd->vieworg, lightOrigin); - - - // Make up a projection - VectorScale(lightDir, -1.0f, lightViewAxis[0]); - - if (lightViewIndependentOfCameraView) - { - // Use world up as light view up - VectorSet(lightViewAxis[2], 0, 0, 1); - } - else if (level == 0) - { - // Level 0 tries to use a diamond texture orientation relative to camera view - // Use halfway between camera view forward and left for light view up - VectorAdd(fd->viewaxis[0], fd->viewaxis[1], lightViewAxis[2]); - } - else - { - // Use camera view up as light view up - VectorCopy(fd->viewaxis[2], lightViewAxis[2]); - } - - // Check if too close to parallel to light direction - if (abs(DotProduct(lightViewAxis[2], lightViewAxis[0])) > 0.9f) - { - if (lightViewIndependentOfCameraView) - { - // Use world left as light view up - VectorSet(lightViewAxis[2], 0, 1, 0); - } - else if (level == 0) - { - // Level 0 tries to use a diamond texture orientation relative to camera view - // Use halfway between camera view forward and up for light view up - VectorAdd(fd->viewaxis[0], fd->viewaxis[2], lightViewAxis[2]); - } - else - { - // Use camera view left as light view up - VectorCopy(fd->viewaxis[1], lightViewAxis[2]); - } - } - - // clean axes - CrossProduct(lightViewAxis[2], lightViewAxis[0], lightViewAxis[1]); - VectorNormalize(lightViewAxis[1]); - CrossProduct(lightViewAxis[0], lightViewAxis[1], lightViewAxis[2]); - - // Create bounds for light projection using slice of view projection - { - matrix_t lightViewMatrix; - vec4_t point, base, lightViewPoint; - float lx, ly; - - base[3] = 1; - point[3] = 1; - lightViewPoint[3] = 1; - - Matrix16View(lightViewAxis, lightOrigin, lightViewMatrix); - - ClearBounds(lightviewBounds[0], lightviewBounds[1]); - - // add view near plane - lx = splitZNear * tan(fd->fov_x * M_PI / 360.0f); - ly = splitZNear * tan(fd->fov_y * M_PI / 360.0f); - VectorMA(fd->vieworg, splitZNear, fd->viewaxis[0], base); - - VectorMA(base, lx, fd->viewaxis[1], point); - VectorMA(point, ly, fd->viewaxis[2], point); - Matrix16Transform(lightViewMatrix, point, lightViewPoint); - AddPointToBounds(lightViewPoint, lightviewBounds[0], lightviewBounds[1]); - - VectorMA(base, -lx, fd->viewaxis[1], point); - VectorMA(point, ly, fd->viewaxis[2], point); - Matrix16Transform(lightViewMatrix, point, lightViewPoint); - AddPointToBounds(lightViewPoint, lightviewBounds[0], lightviewBounds[1]); - - VectorMA(base, lx, fd->viewaxis[1], point); - VectorMA(point, -ly, fd->viewaxis[2], point); - Matrix16Transform(lightViewMatrix, point, lightViewPoint); - AddPointToBounds(lightViewPoint, lightviewBounds[0], lightviewBounds[1]); - - VectorMA(base, -lx, fd->viewaxis[1], point); - VectorMA(point, -ly, fd->viewaxis[2], point); - Matrix16Transform(lightViewMatrix, point, lightViewPoint); - AddPointToBounds(lightViewPoint, lightviewBounds[0], lightviewBounds[1]); - - - // add view far plane - lx = splitZFar * tan(fd->fov_x * M_PI / 360.0f); - ly = splitZFar * tan(fd->fov_y * M_PI / 360.0f); - VectorMA(fd->vieworg, splitZFar, fd->viewaxis[0], base); - - VectorMA(base, lx, fd->viewaxis[1], point); - VectorMA(point, ly, fd->viewaxis[2], point); - Matrix16Transform(lightViewMatrix, point, lightViewPoint); - AddPointToBounds(lightViewPoint, lightviewBounds[0], lightviewBounds[1]); - - VectorMA(base, -lx, fd->viewaxis[1], point); - VectorMA(point, ly, fd->viewaxis[2], point); - Matrix16Transform(lightViewMatrix, point, lightViewPoint); - AddPointToBounds(lightViewPoint, lightviewBounds[0], lightviewBounds[1]); - - VectorMA(base, lx, fd->viewaxis[1], point); - VectorMA(point, -ly, fd->viewaxis[2], point); - Matrix16Transform(lightViewMatrix, point, lightViewPoint); - AddPointToBounds(lightViewPoint, lightviewBounds[0], lightviewBounds[1]); - - VectorMA(base, -lx, fd->viewaxis[1], point); - VectorMA(point, -ly, fd->viewaxis[2], point); - Matrix16Transform(lightViewMatrix, point, lightViewPoint); - AddPointToBounds(lightViewPoint, lightviewBounds[0], lightviewBounds[1]); - - if (!glRefConfig.depthClamp) - lightviewBounds[0][0] = lightviewBounds[1][0] - 8192; - - // Moving the Light in Texel-Sized Increments - // from http://msdn.microsoft.com/en-us/library/windows/desktop/ee416324%28v=vs.85%29.aspx - // - if (lightViewIndependentOfCameraView) - { - float cascadeBound, worldUnitsPerTexel, invWorldUnitsPerTexel; - - cascadeBound = MAX(lightviewBounds[1][0] - lightviewBounds[0][0], lightviewBounds[1][1] - lightviewBounds[0][1]); - cascadeBound = MAX(cascadeBound, lightviewBounds[1][2] - lightviewBounds[0][2]); - worldUnitsPerTexel = cascadeBound / tr.sunShadowFbo[level]->width; - invWorldUnitsPerTexel = 1.0f / worldUnitsPerTexel; - - VectorScale(lightviewBounds[0], invWorldUnitsPerTexel, lightviewBounds[0]); - lightviewBounds[0][0] = floor(lightviewBounds[0][0]); - lightviewBounds[0][1] = floor(lightviewBounds[0][1]); - lightviewBounds[0][2] = floor(lightviewBounds[0][2]); - VectorScale(lightviewBounds[0], worldUnitsPerTexel, lightviewBounds[0]); - - VectorScale(lightviewBounds[1], invWorldUnitsPerTexel, lightviewBounds[1]); - lightviewBounds[1][0] = floor(lightviewBounds[1][0]); - lightviewBounds[1][1] = floor(lightviewBounds[1][1]); - lightviewBounds[1][2] = floor(lightviewBounds[1][2]); - VectorScale(lightviewBounds[1], worldUnitsPerTexel, lightviewBounds[1]); - } - - //ri.Printf(PRINT_ALL, "znear %f zfar %f\n", lightviewBounds[0][0], lightviewBounds[1][0]); - //ri.Printf(PRINT_ALL, "fovx %f fovy %f xmin %f xmax %f ymin %f ymax %f\n", fd->fov_x, fd->fov_y, xmin, xmax, ymin, ymax); - } - - - { - int firstDrawSurf; - - Com_Memset( &shadowParms, 0, sizeof( shadowParms ) ); - - if (glRefConfig.framebufferObject) - { - shadowParms.viewportX = 0; - shadowParms.viewportY = 0; - } - else - { - shadowParms.viewportX = tr.refdef.x; - shadowParms.viewportY = glConfig.vidHeight - ( tr.refdef.y + tr.sunShadowFbo[level]->height ); - } - shadowParms.viewportWidth = tr.sunShadowFbo[level]->width; - shadowParms.viewportHeight = tr.sunShadowFbo[level]->height; - shadowParms.isPortal = qfalse; - shadowParms.isMirror = qfalse; - - shadowParms.fovX = 90; - shadowParms.fovY = 90; - - if (glRefConfig.framebufferObject) - shadowParms.targetFbo = tr.sunShadowFbo[level]; - - shadowParms.flags = VPF_DEPTHSHADOW | VPF_DEPTHCLAMP | VPF_ORTHOGRAPHIC; - shadowParms.zFar = lightviewBounds[1][0]; - - VectorCopy(lightOrigin, shadowParms.or.origin); - - VectorCopy(lightViewAxis[0], shadowParms.or.axis[0]); - VectorCopy(lightViewAxis[1], shadowParms.or.axis[1]); - VectorCopy(lightViewAxis[2], shadowParms.or.axis[2]); - - VectorCopy(lightOrigin, shadowParms.pvsOrigin ); - - { - tr.viewCount++; - - tr.viewParms = shadowParms; - tr.viewParms.frameSceneNum = tr.frameSceneNum; - tr.viewParms.frameCount = tr.frameCount; - - firstDrawSurf = tr.refdef.numDrawSurfs; - - tr.viewCount++; - - // set viewParms.world - R_RotateForViewer (); - - R_SetupProjectionOrtho(&tr.viewParms, lightviewBounds); - - R_AddWorldSurfaces (); - - R_AddPolygonSurfaces(); - - R_AddEntitySurfaces (); - - R_SortDrawSurfs( tr.refdef.drawSurfs + firstDrawSurf, tr.refdef.numDrawSurfs - firstDrawSurf ); - } - - Matrix16Multiply(tr.viewParms.projectionMatrix, tr.viewParms.world.modelMatrix, tr.refdef.sunShadowMvp[level]); - } -} diff --git a/src/rend2/tr_marks.c b/src/rend2/tr_marks.c deleted file mode 100644 index f24459e2..00000000 --- a/src/rend2/tr_marks.c +++ /dev/null @@ -1,466 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_marks.c -- polygon projection on the world polygons - -#include "tr_local.h" -//#include "assert.h" - -#define MAX_VERTS_ON_POLY 64 - -#define MARKER_OFFSET 0 // 1 - -/* -============= -R_ChopPolyBehindPlane - -Out must have space for two more vertexes than in -============= -*/ -#define SIDE_FRONT 0 -#define SIDE_BACK 1 -#define SIDE_ON 2 -static void R_ChopPolyBehindPlane( int numInPoints, vec3_t inPoints[MAX_VERTS_ON_POLY], - int *numOutPoints, vec3_t outPoints[MAX_VERTS_ON_POLY], - vec3_t normal, vec_t dist, vec_t epsilon) { - float dists[MAX_VERTS_ON_POLY+4]; - int sides[MAX_VERTS_ON_POLY+4]; - int counts[3]; - float dot; - int i, j; - float *p1, *p2, *clip; - float d; - - // don't clip if it might overflow - if ( numInPoints >= MAX_VERTS_ON_POLY - 2 ) { - *numOutPoints = 0; - return; - } - - counts[0] = counts[1] = counts[2] = 0; - - // determine sides for each point - for ( i = 0 ; i < numInPoints ; i++ ) { - dot = DotProduct( inPoints[i], normal ); - dot -= dist; - dists[i] = dot; - if ( dot > epsilon ) { - sides[i] = SIDE_FRONT; - } else if ( dot < -epsilon ) { - sides[i] = SIDE_BACK; - } else { - sides[i] = SIDE_ON; - } - counts[sides[i]]++; - } - sides[i] = sides[0]; - dists[i] = dists[0]; - - *numOutPoints = 0; - - if ( !counts[0] ) { - return; - } - if ( !counts[1] ) { - *numOutPoints = numInPoints; - Com_Memcpy( outPoints, inPoints, numInPoints * sizeof(vec3_t) ); - return; - } - - for ( i = 0 ; i < numInPoints ; i++ ) { - p1 = inPoints[i]; - clip = outPoints[ *numOutPoints ]; - - if ( sides[i] == SIDE_ON ) { - VectorCopy( p1, clip ); - (*numOutPoints)++; - continue; - } - - if ( sides[i] == SIDE_FRONT ) { - VectorCopy( p1, clip ); - (*numOutPoints)++; - clip = outPoints[ *numOutPoints ]; - } - - if ( sides[i+1] == SIDE_ON || sides[i+1] == sides[i] ) { - continue; - } - - // generate a split point - p2 = inPoints[ (i+1) % numInPoints ]; - - d = dists[i] - dists[i+1]; - if ( d == 0 ) { - dot = 0; - } else { - dot = dists[i] / d; - } - - // clip xyz - - for (j=0 ; j<3 ; j++) { - clip[j] = p1[j] + dot * ( p2[j] - p1[j] ); - } - - (*numOutPoints)++; - } -} - -/* -================= -R_BoxSurfaces_r - -================= -*/ -void R_BoxSurfaces_r(mnode_t *node, vec3_t mins, vec3_t maxs, surfaceType_t **list, int listsize, int *listlength, vec3_t dir) { - - int s, c; - msurface_t *surf; - int *mark; - - // do the tail recursion in a loop - while ( node->contents == -1 ) { - s = BoxOnPlaneSide( mins, maxs, node->plane ); - if (s == 1) { - node = node->children[0]; - } else if (s == 2) { - node = node->children[1]; - } else { - R_BoxSurfaces_r(node->children[0], mins, maxs, list, listsize, listlength, dir); - node = node->children[1]; - } - } - - // add the individual surfaces - mark = tr.world->marksurfaces + node->firstmarksurface; - c = node->nummarksurfaces; - while (c--) { - int *surfViewCount; - // - if (*listlength >= listsize) break; - // - surfViewCount = &tr.world->surfacesViewCount[*mark]; - surf = tr.world->surfaces + *mark; - // check if the surface has NOIMPACT or NOMARKS set - if ( ( surf->shader->surfaceFlags & ( SURF_NOIMPACT | SURF_NOMARKS ) ) - || ( surf->shader->contentFlags & CONTENTS_FOG ) ) { - *surfViewCount = tr.viewCount; - } - // extra check for surfaces to avoid list overflows - else if (*(surf->data) == SF_FACE) { - // the face plane should go through the box - s = BoxOnPlaneSide( mins, maxs, &surf->cullinfo.plane ); - if (s == 1 || s == 2) { - *surfViewCount = tr.viewCount; - } else if (DotProduct(surf->cullinfo.plane.normal, dir) > -0.5) { - // don't add faces that make sharp angles with the projection direction - *surfViewCount = tr.viewCount; - } - } - else if (*(surf->data) != SF_GRID && - *(surf->data) != SF_TRIANGLES) - *surfViewCount = tr.viewCount; - // check the viewCount because the surface may have - // already been added if it spans multiple leafs - if (*surfViewCount != tr.viewCount) { - *surfViewCount = tr.viewCount; - list[*listlength] = surf->data; - (*listlength)++; - } - mark++; - } -} - -/* -================= -R_AddMarkFragments - -================= -*/ -void R_AddMarkFragments(int numClipPoints, vec3_t clipPoints[2][MAX_VERTS_ON_POLY], - int numPlanes, vec3_t *normals, float *dists, - int maxPoints, vec3_t pointBuffer, - int maxFragments, markFragment_t *fragmentBuffer, - int *returnedPoints, int *returnedFragments, - vec3_t mins, vec3_t maxs) { - int pingPong, i; - markFragment_t *mf; - - // chop the surface by all the bounding planes of the to be projected polygon - pingPong = 0; - - for ( i = 0 ; i < numPlanes ; i++ ) { - - R_ChopPolyBehindPlane( numClipPoints, clipPoints[pingPong], - &numClipPoints, clipPoints[!pingPong], - normals[i], dists[i], 0.5 ); - pingPong ^= 1; - if ( numClipPoints == 0 ) { - break; - } - } - // completely clipped away? - if ( numClipPoints == 0 ) { - return; - } - - // add this fragment to the returned list - if ( numClipPoints + (*returnedPoints) > maxPoints ) { - return; // not enough space for this polygon - } - /* - // all the clip points should be within the bounding box - for ( i = 0 ; i < numClipPoints ; i++ ) { - int j; - for ( j = 0 ; j < 3 ; j++ ) { - if (clipPoints[pingPong][i][j] < mins[j] - 0.5) break; - if (clipPoints[pingPong][i][j] > maxs[j] + 0.5) break; - } - if (j < 3) break; - } - if (i < numClipPoints) return; - */ - - mf = fragmentBuffer + (*returnedFragments); - mf->firstPoint = (*returnedPoints); - mf->numPoints = numClipPoints; - Com_Memcpy( pointBuffer + (*returnedPoints) * 3, clipPoints[pingPong], numClipPoints * sizeof(vec3_t) ); - - (*returnedPoints) += numClipPoints; - (*returnedFragments)++; -} - -/* -================= -R_MarkFragments - -================= -*/ -int R_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection, - int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ) { - int numsurfaces, numPlanes; - int i, j, k, m, n; - surfaceType_t *surfaces[64]; - vec3_t mins, maxs; - int returnedFragments; - int returnedPoints; - vec3_t normals[MAX_VERTS_ON_POLY+2]; - float dists[MAX_VERTS_ON_POLY+2]; - vec3_t clipPoints[2][MAX_VERTS_ON_POLY]; - int numClipPoints; - float *v; - srfGridMesh_t *cv; - srfTriangle_t *tri; - srfVert_t *dv; - vec3_t normal; - vec3_t projectionDir; - vec3_t v1, v2; - - if (numPoints <= 0) { - return 0; - } - - //increment view count for double check prevention - tr.viewCount++; - - // - VectorNormalize2( projection, projectionDir ); - // find all the brushes that are to be considered - ClearBounds( mins, maxs ); - for ( i = 0 ; i < numPoints ; i++ ) { - vec3_t temp; - - AddPointToBounds( points[i], mins, maxs ); - VectorAdd( points[i], projection, temp ); - AddPointToBounds( temp, mins, maxs ); - // make sure we get all the leafs (also the one(s) in front of the hit surface) - VectorMA( points[i], -20, projectionDir, temp ); - AddPointToBounds( temp, mins, maxs ); - } - - if (numPoints > MAX_VERTS_ON_POLY) numPoints = MAX_VERTS_ON_POLY; - // create the bounding planes for the to be projected polygon - for ( i = 0 ; i < numPoints ; i++ ) { - VectorSubtract(points[(i+1)%numPoints], points[i], v1); - VectorAdd(points[i], projection, v2); - VectorSubtract(points[i], v2, v2); - CrossProduct(v1, v2, normals[i]); - VectorNormalizeFast(normals[i]); - dists[i] = DotProduct(normals[i], points[i]); - } - // add near and far clipping planes for projection - VectorCopy(projectionDir, normals[numPoints]); - dists[numPoints] = DotProduct(normals[numPoints], points[0]) - 32; - VectorCopy(projectionDir, normals[numPoints+1]); - VectorInverse(normals[numPoints+1]); - dists[numPoints+1] = DotProduct(normals[numPoints+1], points[0]) - 20; - numPlanes = numPoints + 2; - - numsurfaces = 0; - R_BoxSurfaces_r(tr.world->nodes, mins, maxs, surfaces, 64, &numsurfaces, projectionDir); - //assert(numsurfaces <= 64); - //assert(numsurfaces != 64); - - returnedPoints = 0; - returnedFragments = 0; - - for ( i = 0 ; i < numsurfaces ; i++ ) { - - if (*surfaces[i] == SF_GRID) { - - cv = (srfGridMesh_t *) surfaces[i]; - for ( m = 0 ; m < cv->height - 1 ; m++ ) { - for ( n = 0 ; n < cv->width - 1 ; n++ ) { - // We triangulate the grid and chop all triangles within - // the bounding planes of the to be projected polygon. - // LOD is not taken into account, not such a big deal though. - // - // It's probably much nicer to chop the grid itself and deal - // with this grid as a normal SF_GRID surface so LOD will - // be applied. However the LOD of that chopped grid must - // be synced with the LOD of the original curve. - // One way to do this; the chopped grid shares vertices with - // the original curve. When LOD is applied to the original - // curve the unused vertices are flagged. Now the chopped curve - // should skip the flagged vertices. This still leaves the - // problems with the vertices at the chopped grid edges. - // - // To avoid issues when LOD applied to "hollow curves" (like - // the ones around many jump pads) we now just add a 2 unit - // offset to the triangle vertices. - // The offset is added in the vertex normal vector direction - // so all triangles will still fit together. - // The 2 unit offset should avoid pretty much all LOD problems. - - numClipPoints = 3; - - dv = cv->verts + m * cv->width + n; - - VectorCopy(dv[0].xyz, clipPoints[0][0]); - VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[0].normal, clipPoints[0][0]); - VectorCopy(dv[cv->width].xyz, clipPoints[0][1]); - VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]); - VectorCopy(dv[1].xyz, clipPoints[0][2]); - VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[1].normal, clipPoints[0][2]); - // check the normal of this triangle - VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1); - VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2); - CrossProduct(v1, v2, normal); - VectorNormalizeFast(normal); - if (DotProduct(normal, projectionDir) < -0.1) { - // add the fragments of this triangle - R_AddMarkFragments(numClipPoints, clipPoints, - numPlanes, normals, dists, - maxPoints, pointBuffer, - maxFragments, fragmentBuffer, - &returnedPoints, &returnedFragments, mins, maxs); - - if ( returnedFragments == maxFragments ) { - return returnedFragments; // not enough space for more fragments - } - } - - VectorCopy(dv[1].xyz, clipPoints[0][0]); - VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[1].normal, clipPoints[0][0]); - VectorCopy(dv[cv->width].xyz, clipPoints[0][1]); - VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]); - VectorCopy(dv[cv->width+1].xyz, clipPoints[0][2]); - VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[cv->width+1].normal, clipPoints[0][2]); - // check the normal of this triangle - VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1); - VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2); - CrossProduct(v1, v2, normal); - VectorNormalizeFast(normal); - if (DotProduct(normal, projectionDir) < -0.05) { - // add the fragments of this triangle - R_AddMarkFragments(numClipPoints, clipPoints, - numPlanes, normals, dists, - maxPoints, pointBuffer, - maxFragments, fragmentBuffer, - &returnedPoints, &returnedFragments, mins, maxs); - - if ( returnedFragments == maxFragments ) { - return returnedFragments; // not enough space for more fragments - } - } - } - } - } - else if (*surfaces[i] == SF_FACE) { - - srfSurfaceFace_t *surf = ( srfSurfaceFace_t * ) surfaces[i]; - - // check the normal of this face - if (DotProduct(surf->plane.normal, projectionDir) > -0.5) { - continue; - } - - for(k = 0, tri = surf->triangles; k < surf->numTriangles; k++, tri++) - { - for(j = 0; j < 3; j++) - { - v = surf->verts[tri->indexes[j]].xyz; - VectorMA(v, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j]); - } - - // add the fragments of this face - R_AddMarkFragments( 3 , clipPoints, - numPlanes, normals, dists, - maxPoints, pointBuffer, - maxFragments, fragmentBuffer, - &returnedPoints, &returnedFragments, mins, maxs); - if ( returnedFragments == maxFragments ) { - return returnedFragments; // not enough space for more fragments - } - } - } - else if(*surfaces[i] == SF_TRIANGLES && r_marksOnTriangleMeshes->integer) { - - srfTriangles_t *surf = (srfTriangles_t *) surfaces[i]; - - for(k = 0, tri = surf->triangles; k < surf->numTriangles; k++, tri++) - { - for(j = 0; j < 3; j++) - { - v = surf->verts[tri->indexes[j]].xyz; - VectorMA(v, MARKER_OFFSET, surf->verts[tri->indexes[j]].normal, clipPoints[0][j]); - } - - // add the fragments of this face - R_AddMarkFragments(3, clipPoints, - numPlanes, normals, dists, - maxPoints, pointBuffer, - maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs); - if(returnedFragments == maxFragments) - { - return returnedFragments; // not enough space for more fragments - } - } - } - } - return returnedFragments; -} - - - - - diff --git a/src/rend2/tr_mesh.c b/src/rend2/tr_mesh.c deleted file mode 100644 index 342854bf..00000000 --- a/src/rend2/tr_mesh.c +++ /dev/null @@ -1,405 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_mesh.c: triangle model functions - -#include "tr_local.h" - -static float ProjectRadius( float r, vec3_t location ) -{ - float pr; - float dist; - float c; - vec3_t p; - float projected[4]; - - c = DotProduct( tr.viewParms.or.axis[0], tr.viewParms.or.origin ); - dist = DotProduct( tr.viewParms.or.axis[0], location ) - c; - - if ( dist <= 0 ) - return 0; - - p[0] = 0; - p[1] = fabs( r ); - p[2] = -dist; - - projected[0] = p[0] * tr.viewParms.projectionMatrix[0] + - p[1] * tr.viewParms.projectionMatrix[4] + - p[2] * tr.viewParms.projectionMatrix[8] + - tr.viewParms.projectionMatrix[12]; - - projected[1] = p[0] * tr.viewParms.projectionMatrix[1] + - p[1] * tr.viewParms.projectionMatrix[5] + - p[2] * tr.viewParms.projectionMatrix[9] + - tr.viewParms.projectionMatrix[13]; - - projected[2] = p[0] * tr.viewParms.projectionMatrix[2] + - p[1] * tr.viewParms.projectionMatrix[6] + - p[2] * tr.viewParms.projectionMatrix[10] + - tr.viewParms.projectionMatrix[14]; - - projected[3] = p[0] * tr.viewParms.projectionMatrix[3] + - p[1] * tr.viewParms.projectionMatrix[7] + - p[2] * tr.viewParms.projectionMatrix[11] + - tr.viewParms.projectionMatrix[15]; - - - pr = projected[1] / projected[3]; - - if ( pr > 1.0f ) - pr = 1.0f; - - return pr; -} - -/* -============= -R_CullModel -============= -*/ -static int R_CullModel( mdvModel_t *model, trRefEntity_t *ent ) { - vec3_t bounds[2]; - mdvFrame_t *oldFrame, *newFrame; - int i; - - // compute frame pointers - newFrame = model->frames + ent->e.frame; - oldFrame = model->frames + ent->e.oldframe; - - // cull bounding sphere ONLY if this is not an upscaled entity - if ( !ent->e.nonNormalizedAxes ) - { - if ( ent->e.frame == ent->e.oldframe ) - { - switch ( R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ) ) - { - case CULL_OUT: - tr.pc.c_sphere_cull_md3_out++; - return CULL_OUT; - - case CULL_IN: - tr.pc.c_sphere_cull_md3_in++; - return CULL_IN; - - case CULL_CLIP: - tr.pc.c_sphere_cull_md3_clip++; - break; - } - } - else - { - int sphereCull, sphereCullB; - - sphereCull = R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ); - if ( newFrame == oldFrame ) { - sphereCullB = sphereCull; - } else { - sphereCullB = R_CullLocalPointAndRadius( oldFrame->localOrigin, oldFrame->radius ); - } - - if ( sphereCull == sphereCullB ) - { - if ( sphereCull == CULL_OUT ) - { - tr.pc.c_sphere_cull_md3_out++; - return CULL_OUT; - } - else if ( sphereCull == CULL_IN ) - { - tr.pc.c_sphere_cull_md3_in++; - return CULL_IN; - } - else - { - tr.pc.c_sphere_cull_md3_clip++; - } - } - } - } - - // calculate a bounding box in the current coordinate system - for (i = 0 ; i < 3 ; i++) { - bounds[0][i] = oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i]; - bounds[1][i] = oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i]; - } - - switch ( R_CullLocalBox( bounds ) ) - { - case CULL_IN: - tr.pc.c_box_cull_md3_in++; - return CULL_IN; - case CULL_CLIP: - tr.pc.c_box_cull_md3_clip++; - return CULL_CLIP; - case CULL_OUT: - default: - tr.pc.c_box_cull_md3_out++; - return CULL_OUT; - } -} - - -/* -================= -R_ComputeLOD - -================= -*/ -int R_ComputeLOD( trRefEntity_t *ent ) { - float radius; - float flod, lodscale; - float projectedRadius; - mdvFrame_t *frame; -#ifdef RAVENMD4 - mdrHeader_t *mdr; - mdrFrame_t *mdrframe; -#endif - int lod; - - if ( tr.currentModel->numLods < 2 ) - { - // model has only 1 LOD level, skip computations and bias - lod = 0; - } - else - { - // multiple LODs exist, so compute projected bounding sphere - // and use that as a criteria for selecting LOD - -#ifdef RAVENMD4 - if(tr.currentModel->type == MOD_MDR) - { - int frameSize; - mdr = (mdrHeader_t *) tr.currentModel->modelData; - frameSize = (size_t) (&((mdrFrame_t *)0)->bones[mdr->numBones]); - - mdrframe = (mdrFrame_t *) ((byte *) mdr + mdr->ofsFrames + frameSize * ent->e.frame); - - radius = RadiusFromBounds(mdrframe->bounds[0], mdrframe->bounds[1]); - } - else -#endif - { - //frame = ( md3Frame_t * ) ( ( ( unsigned char * ) tr.currentModel->md3[0] ) + tr.currentModel->md3[0]->ofsFrames ); - frame = tr.currentModel->mdv[0]->frames; - - frame += ent->e.frame; - - radius = RadiusFromBounds( frame->bounds[0], frame->bounds[1] ); - } - - if ( ( projectedRadius = ProjectRadius( radius, ent->e.origin ) ) != 0 ) - { - lodscale = r_lodscale->value; - if (lodscale > 20) lodscale = 20; - flod = 1.0f - projectedRadius * lodscale; - } - else - { - // object intersects near view plane, e.g. view weapon - flod = 0; - } - - flod *= tr.currentModel->numLods; - lod = ri.ftol(flod); - - if ( lod < 0 ) - { - lod = 0; - } - else if ( lod >= tr.currentModel->numLods ) - { - lod = tr.currentModel->numLods - 1; - } - } - - lod += r_lodbias->integer; - - if ( lod >= tr.currentModel->numLods ) - lod = tr.currentModel->numLods - 1; - if ( lod < 0 ) - lod = 0; - - return lod; -} - -/* -================= -R_ComputeFogNum - -================= -*/ -int R_ComputeFogNum( mdvModel_t *model, trRefEntity_t *ent ) { - int i, j; - fog_t *fog; - mdvFrame_t *mdvFrame; - vec3_t localOrigin; - - if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { - return 0; - } - - // FIXME: non-normalized axis issues - mdvFrame = model->frames + ent->e.frame; - VectorAdd( ent->e.origin, mdvFrame->localOrigin, localOrigin ); - for ( i = 1 ; i < tr.world->numfogs ; i++ ) { - fog = &tr.world->fogs[i]; - for ( j = 0 ; j < 3 ; j++ ) { - if ( localOrigin[j] - mdvFrame->radius >= fog->bounds[1][j] ) { - break; - } - if ( localOrigin[j] + mdvFrame->radius <= fog->bounds[0][j] ) { - break; - } - } - if ( j == 3 ) { - return i; - } - } - - return 0; -} - -/* -================= -R_AddMD3Surfaces - -================= -*/ -void R_AddMD3Surfaces( trRefEntity_t *ent ) { - int i; - mdvModel_t *model = NULL; - mdvSurface_t *surface = NULL; - shader_t *shader = NULL; - int cull; - int lod; - int fogNum; - qboolean personalModel; - - // don't add third_person objects if not in a portal - personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !(tr.viewParms.isPortal - || (tr.viewParms.flags & (VPF_SHADOWMAP | VPF_DEPTHSHADOW))); - - if ( ent->e.renderfx & RF_WRAP_FRAMES ) { - ent->e.frame %= tr.currentModel->mdv[0]->numFrames; - ent->e.oldframe %= tr.currentModel->mdv[0]->numFrames; - } - - // - // Validate the frames so there is no chance of a crash. - // This will write directly into the entity structure, so - // when the surfaces are rendered, they don't need to be - // range checked again. - // - if ( (ent->e.frame >= tr.currentModel->mdv[0]->numFrames) - || (ent->e.frame < 0) - || (ent->e.oldframe >= tr.currentModel->mdv[0]->numFrames) - || (ent->e.oldframe < 0) ) { - ri.Printf( PRINT_DEVELOPER, "R_AddMD3Surfaces: no such frame %d to %d for '%s'\n", - ent->e.oldframe, ent->e.frame, - tr.currentModel->name ); - ent->e.frame = 0; - ent->e.oldframe = 0; - } - - // - // compute LOD - // - lod = R_ComputeLOD( ent ); - - model = tr.currentModel->mdv[lod]; - - // - // cull the entire model if merged bounding box of both frames - // is outside the view frustum. - // - cull = R_CullModel ( model, ent ); - if ( cull == CULL_OUT ) { - return; - } - - // - // set up lighting now that we know we aren't culled - // - if ( !personalModel || r_shadows->integer > 1 ) { - R_SetupEntityLighting( &tr.refdef, ent ); - } - - // - // see if we are in a fog volume - // - fogNum = R_ComputeFogNum( model, ent ); - - // - // draw all surfaces - // - surface = model->surfaces; - for ( i = 0 ; i < model->numSurfaces ; i++ ) { - - if ( ent->e.customShader ) { - shader = R_GetShaderByHandle( ent->e.customShader ); - } else if ( ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins ) { - skin_t *skin; - int j; - - skin = R_GetSkinByHandle( ent->e.customSkin ); - - // match the surface name to something in the skin file - shader = tr.defaultShader; - for ( j = 0 ; j < skin->numSurfaces ; j++ ) { - // the names have both been lowercased - if ( !strcmp( skin->surfaces[j]->name, surface->name ) ) { - shader = skin->surfaces[j]->shader; - break; - } - } - if (shader == tr.defaultShader) { - ri.Printf( PRINT_DEVELOPER, "WARNING: no shader for surface %s in skin %s\n", surface->name, skin->name); - } - else if (shader->defaultShader) { - ri.Printf( PRINT_DEVELOPER, "WARNING: shader %s in skin %s not found\n", shader->name, skin->name); - } - //} else if ( surface->numShaders <= 0 ) { - //shader = tr.defaultShader; - } else { - //md3Shader = (md3Shader_t *) ( (byte *)surface + surface->ofsShaders ); - //md3Shader += ent->e.skinNum % surface->numShaders; - //shader = tr.shaders[ md3Shader->shaderIndex ]; - shader = tr.shaders[ surface->shaderIndexes[ ent->e.skinNum % surface->numShaderIndexes ] ]; - } - - // don't add third_person objects if not viewing through a portal - if(!personalModel) - { - srfVBOMDVMesh_t *vboSurface = &model->vboSurfaces[i]; - - R_AddDrawSurf((void *)vboSurface, shader, fogNum, qfalse, qfalse ); - } - - surface++; - } - -} - - - - - diff --git a/src/rend2/tr_model.c b/src/rend2/tr_model.c deleted file mode 100644 index 5aeab8ef..00000000 --- a/src/rend2/tr_model.c +++ /dev/null @@ -1,1583 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_models.c -- model loading and caching - -#include "tr_local.h" - -#define LL(x) x=LittleLong(x) - -static qboolean R_LoadMD3(model_t *mod, int lod, void *buffer, int bufferSize, const char *modName); -static qboolean R_LoadMD4(model_t *mod, void *buffer, const char *name ); -#ifdef RAVENMD4 -static qboolean R_LoadMDR(model_t *mod, void *buffer, int filesize, const char *name ); -#endif - -/* -==================== -R_RegisterMD3 -==================== -*/ -qhandle_t R_RegisterMD3(const char *name, model_t *mod) -{ - union { - unsigned *u; - void *v; - } buf; - int size; - int lod; - int ident; - qboolean loaded = qfalse; - int numLoaded; - char filename[MAX_QPATH], namebuf[MAX_QPATH+20]; - char *fext, defex[] = "md3"; - - numLoaded = 0; - - strcpy(filename, name); - - fext = strchr(filename, '.'); - if(!fext) - fext = defex; - else - { - *fext = '\0'; - fext++; - } - - for (lod = MD3_MAX_LODS - 1 ; lod >= 0 ; lod--) - { - if(lod) - Com_sprintf(namebuf, sizeof(namebuf), "%s_%d.%s", filename, lod, fext); - else - Com_sprintf(namebuf, sizeof(namebuf), "%s.%s", filename, fext); - - size = ri.FS_ReadFile( namebuf, &buf.v ); - if(!buf.u) - continue; - - ident = LittleLong(* (unsigned *) buf.u); - if (ident == MD4_IDENT) - loaded = R_LoadMD4(mod, buf.u, name); - else - { - if (ident == MD3_IDENT) - loaded = R_LoadMD3(mod, lod, buf.u, size, name); - else - ri.Printf(PRINT_WARNING,"R_RegisterMD3: unknown fileid for %s\n", name); - } - - ri.FS_FreeFile(buf.v); - - if(loaded) - { - mod->numLods++; - numLoaded++; - } - else - break; - } - - if(numLoaded) - { - // duplicate into higher lod spots that weren't - // loaded, in case the user changes r_lodbias on the fly - for(lod--; lod >= 0; lod--) - { - mod->numLods++; - mod->mdv[lod] = mod->mdv[lod + 1]; - } - - return mod->index; - } - -#ifdef _DEBUG - ri.Printf(PRINT_WARNING,"R_RegisterMD3: couldn't load %s\n", name); -#endif - - mod->type = MOD_BAD; - return 0; -} - -#ifdef RAVENMD4 -/* -==================== -R_RegisterMDR -==================== -*/ -qhandle_t R_RegisterMDR(const char *name, model_t *mod) -{ - union { - unsigned *u; - void *v; - } buf; - int ident; - qboolean loaded = qfalse; - int filesize; - - filesize = ri.FS_ReadFile(name, (void **) &buf.v); - if(!buf.u) - { - mod->type = MOD_BAD; - return 0; - } - - ident = LittleLong(*(unsigned *)buf.u); - if(ident == MDR_IDENT) - loaded = R_LoadMDR(mod, buf.u, filesize, name); - - ri.FS_FreeFile (buf.v); - - if(!loaded) - { - ri.Printf(PRINT_WARNING,"R_RegisterMDR: couldn't load mdr file %s\n", name); - mod->type = MOD_BAD; - return 0; - } - - return mod->index; -} -#endif - -/* -==================== -R_RegisterIQM -==================== -*/ -qhandle_t R_RegisterIQM(const char *name, model_t *mod) -{ - union { - unsigned *u; - void *v; - } buf; - qboolean loaded = qfalse; - int filesize; - - filesize = ri.FS_ReadFile(name, (void **) &buf.v); - if(!buf.u) - { - mod->type = MOD_BAD; - return 0; - } - - loaded = R_LoadIQM(mod, buf.u, filesize, name); - - ri.FS_FreeFile (buf.v); - - if(!loaded) - { - ri.Printf(PRINT_WARNING,"R_RegisterIQM: couldn't load iqm file %s\n", name); - mod->type = MOD_BAD; - return 0; - } - - return mod->index; -} - - -typedef struct -{ - char *ext; - qhandle_t (*ModelLoader)( const char *, model_t * ); -} modelExtToLoaderMap_t; - -// Note that the ordering indicates the order of preference used -// when there are multiple models of different formats available -static modelExtToLoaderMap_t modelLoaders[ ] = -{ - { "iqm", R_RegisterIQM }, -#ifdef RAVENMD4 - { "mdr", R_RegisterMDR }, -#endif - { "md4", R_RegisterMD3 }, - { "md3", R_RegisterMD3 } -}; - -static int numModelLoaders = ARRAY_LEN(modelLoaders); - -//=============================================================================== - -/* -** R_GetModelByHandle -*/ -model_t *R_GetModelByHandle( qhandle_t index ) { - model_t *mod; - - // out of range gets the defualt model - if ( index < 1 || index >= tr.numModels ) { - return tr.models[0]; - } - - mod = tr.models[index]; - - return mod; -} - -//=============================================================================== - -/* -** R_AllocModel -*/ -model_t *R_AllocModel( void ) { - model_t *mod; - - if ( tr.numModels == MAX_MOD_KNOWN ) { - return NULL; - } - - mod = ri.Hunk_Alloc( sizeof( *tr.models[tr.numModels] ), h_low ); - mod->index = tr.numModels; - tr.models[tr.numModels] = mod; - tr.numModels++; - - return mod; -} - -/* -==================== -RE_RegisterModel - -Loads in a model for the given name - -Zero will be returned if the model fails to load. -An entry will be retained for failed models as an -optimization to prevent disk rescanning if they are -asked for again. -==================== -*/ -qhandle_t RE_RegisterModel( const char *name ) { - model_t *mod; - qhandle_t hModel; - qboolean orgNameFailed = qfalse; - int orgLoader = -1; - int i; - char localName[ MAX_QPATH ]; - const char *ext; - char altName[ MAX_QPATH ]; - - if ( !name || !name[0] ) { - ri.Printf( PRINT_ALL, "RE_RegisterModel: NULL name\n" ); - return 0; - } - - if ( strlen( name ) >= MAX_QPATH ) { - ri.Printf( PRINT_ALL, "Model name exceeds MAX_QPATH\n" ); - return 0; - } - - // - // search the currently loaded models - // - for ( hModel = 1 ; hModel < tr.numModels; hModel++ ) { - mod = tr.models[hModel]; - if ( !strcmp( mod->name, name ) ) { - if( mod->type == MOD_BAD ) { - return 0; - } - return hModel; - } - } - - // allocate a new model_t - - if ( ( mod = R_AllocModel() ) == NULL ) { - ri.Printf( PRINT_WARNING, "RE_RegisterModel: R_AllocModel() failed for '%s'\n", name); - return 0; - } - - // only set the name after the model has been successfully loaded - Q_strncpyz( mod->name, name, sizeof( mod->name ) ); - - - R_IssuePendingRenderCommands(); - - mod->type = MOD_BAD; - mod->numLods = 0; - - // - // load the files - // - Q_strncpyz( localName, name, MAX_QPATH ); - - ext = COM_GetExtension( localName ); - - if( *ext ) - { - // Look for the correct loader and use it - for( i = 0; i < numModelLoaders; i++ ) - { - if( !Q_stricmp( ext, modelLoaders[ i ].ext ) ) - { - // Load - hModel = modelLoaders[ i ].ModelLoader( localName, mod ); - break; - } - } - - // A loader was found - if( i < numModelLoaders ) - { - if( !hModel ) - { - // Loader failed, most likely because the file isn't there; - // try again without the extension - orgNameFailed = qtrue; - orgLoader = i; - COM_StripExtension( name, localName, MAX_QPATH ); - } - else - { - // Something loaded - return mod->index; - } - } - } - - // Try and find a suitable match using all - // the model formats supported - for( i = 0; i < numModelLoaders; i++ ) - { - if (i == orgLoader) - continue; - - Com_sprintf( altName, sizeof (altName), "%s.%s", localName, modelLoaders[ i ].ext ); - - // Load - hModel = modelLoaders[ i ].ModelLoader( altName, mod ); - - if( hModel ) - { - if( orgNameFailed ) - { - ri.Printf( PRINT_DEVELOPER, "WARNING: %s not present, using %s instead\n", - name, altName ); - } - - break; - } - } - - return hModel; -} - -/* -================= -R_LoadMD3 -================= -*/ -static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, int bufferSize, const char *modName) -{ - int f, i, j, k; - - md3Header_t *md3Model; - md3Frame_t *md3Frame; - md3Surface_t *md3Surf; - md3Shader_t *md3Shader; - md3Triangle_t *md3Tri; - md3St_t *md3st; - md3XyzNormal_t *md3xyz; - md3Tag_t *md3Tag; - - mdvModel_t *mdvModel; - mdvFrame_t *frame; - mdvSurface_t *surf;//, *surface; - int *shaderIndex; - srfTriangle_t *tri; - mdvVertex_t *v; - mdvSt_t *st; - mdvTag_t *tag; - mdvTagName_t *tagName; - - int version; - int size; - - md3Model = (md3Header_t *) buffer; - - version = LittleLong(md3Model->version); - if(version != MD3_VERSION) - { - ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has wrong version (%i should be %i)\n", modName, version, MD3_VERSION); - return qfalse; - } - - mod->type = MOD_MESH; - size = LittleLong(md3Model->ofsEnd); - mod->dataSize += size; - mdvModel = mod->mdv[lod] = ri.Hunk_Alloc(sizeof(mdvModel_t), h_low); - -// Com_Memcpy(mod->md3[lod], buffer, LittleLong(md3Model->ofsEnd)); - - LL(md3Model->ident); - LL(md3Model->version); - LL(md3Model->numFrames); - LL(md3Model->numTags); - LL(md3Model->numSurfaces); - LL(md3Model->ofsFrames); - LL(md3Model->ofsTags); - LL(md3Model->ofsSurfaces); - LL(md3Model->ofsEnd); - - if(md3Model->numFrames < 1) - { - ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has no frames\n", modName); - return qfalse; - } - - // swap all the frames - mdvModel->numFrames = md3Model->numFrames; - mdvModel->frames = frame = ri.Hunk_Alloc(sizeof(*frame) * md3Model->numFrames, h_low); - - md3Frame = (md3Frame_t *) ((byte *) md3Model + md3Model->ofsFrames); - for(i = 0; i < md3Model->numFrames; i++, frame++, md3Frame++) - { - frame->radius = LittleFloat(md3Frame->radius); - for(j = 0; j < 3; j++) - { - frame->bounds[0][j] = LittleFloat(md3Frame->bounds[0][j]); - frame->bounds[1][j] = LittleFloat(md3Frame->bounds[1][j]); - frame->localOrigin[j] = LittleFloat(md3Frame->localOrigin[j]); - } - } - - // swap all the tags - mdvModel->numTags = md3Model->numTags; - mdvModel->tags = tag = ri.Hunk_Alloc(sizeof(*tag) * (md3Model->numTags * md3Model->numFrames), h_low); - - md3Tag = (md3Tag_t *) ((byte *) md3Model + md3Model->ofsTags); - for(i = 0; i < md3Model->numTags * md3Model->numFrames; i++, tag++, md3Tag++) - { - for(j = 0; j < 3; j++) - { - tag->origin[j] = LittleFloat(md3Tag->origin[j]); - tag->axis[0][j] = LittleFloat(md3Tag->axis[0][j]); - tag->axis[1][j] = LittleFloat(md3Tag->axis[1][j]); - tag->axis[2][j] = LittleFloat(md3Tag->axis[2][j]); - } - } - - - mdvModel->tagNames = tagName = ri.Hunk_Alloc(sizeof(*tagName) * (md3Model->numTags), h_low); - - md3Tag = (md3Tag_t *) ((byte *) md3Model + md3Model->ofsTags); - for(i = 0; i < md3Model->numTags; i++, tagName++, md3Tag++) - { - Q_strncpyz(tagName->name, md3Tag->name, sizeof(tagName->name)); - } - - // swap all the surfaces - mdvModel->numSurfaces = md3Model->numSurfaces; - mdvModel->surfaces = surf = ri.Hunk_Alloc(sizeof(*surf) * md3Model->numSurfaces, h_low); - - md3Surf = (md3Surface_t *) ((byte *) md3Model + md3Model->ofsSurfaces); - for(i = 0; i < md3Model->numSurfaces; i++) - { - LL(md3Surf->ident); - LL(md3Surf->flags); - LL(md3Surf->numFrames); - LL(md3Surf->numShaders); - LL(md3Surf->numTriangles); - LL(md3Surf->ofsTriangles); - LL(md3Surf->numVerts); - LL(md3Surf->ofsShaders); - LL(md3Surf->ofsSt); - LL(md3Surf->ofsXyzNormals); - LL(md3Surf->ofsEnd); - - if(md3Surf->numVerts > SHADER_MAX_VERTEXES) - { - ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has more than %i verts on a surface (%i)", - modName, SHADER_MAX_VERTEXES, md3Surf->numVerts); - return qfalse; - } - if(md3Surf->numTriangles * 3 > SHADER_MAX_INDEXES) - { - ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has more than %i triangles on a surface (%i)", - modName, SHADER_MAX_INDEXES / 3, md3Surf->numTriangles); - return qfalse; - } - - // change to surface identifier - surf->surfaceType = SF_MDV; - - // give pointer to model for Tess_SurfaceMDX - surf->model = mdvModel; - - // copy surface name - Q_strncpyz(surf->name, md3Surf->name, sizeof(surf->name)); - - // lowercase the surface name so skin compares are faster - Q_strlwr(surf->name); - - // strip off a trailing _1 or _2 - // this is a crutch for q3data being a mess - j = strlen(surf->name); - if(j > 2 && surf->name[j - 2] == '_') - { - surf->name[j - 2] = 0; - } - - // register the shaders - surf->numShaderIndexes = md3Surf->numShaders; - surf->shaderIndexes = shaderIndex = ri.Hunk_Alloc(sizeof(*shaderIndex) * md3Surf->numShaders, h_low); - - md3Shader = (md3Shader_t *) ((byte *) md3Surf + md3Surf->ofsShaders); - for(j = 0; j < md3Surf->numShaders; j++, shaderIndex++, md3Shader++) - { - shader_t *sh; - - sh = R_FindShader(md3Shader->name, LIGHTMAP_NONE, qtrue); - if(sh->defaultShader) - { - *shaderIndex = 0; - } - else - { - *shaderIndex = sh->index; - } - } - - // swap all the triangles - surf->numTriangles = md3Surf->numTriangles; - surf->triangles = tri = ri.Hunk_Alloc(sizeof(*tri) * md3Surf->numTriangles, h_low); - - md3Tri = (md3Triangle_t *) ((byte *) md3Surf + md3Surf->ofsTriangles); - for(j = 0; j < md3Surf->numTriangles; j++, tri++, md3Tri++) - { - tri->indexes[0] = LittleLong(md3Tri->indexes[0]); - tri->indexes[1] = LittleLong(md3Tri->indexes[1]); - tri->indexes[2] = LittleLong(md3Tri->indexes[2]); - } - - R_CalcSurfaceTriangleNeighbors(surf->numTriangles, surf->triangles); - - // swap all the XyzNormals - surf->numVerts = md3Surf->numVerts; - surf->verts = v = ri.Hunk_Alloc(sizeof(*v) * (md3Surf->numVerts * md3Surf->numFrames), h_low); - - md3xyz = (md3XyzNormal_t *) ((byte *) md3Surf + md3Surf->ofsXyzNormals); - for(j = 0; j < md3Surf->numVerts * md3Surf->numFrames; j++, md3xyz++, v++) - { - unsigned lat, lng; - unsigned short normal; - - v->xyz[0] = LittleShort(md3xyz->xyz[0]) * MD3_XYZ_SCALE; - v->xyz[1] = LittleShort(md3xyz->xyz[1]) * MD3_XYZ_SCALE; - v->xyz[2] = LittleShort(md3xyz->xyz[2]) * MD3_XYZ_SCALE; - - normal = LittleShort(md3xyz->normal); - - lat = ( normal >> 8 ) & 0xff; - lng = ( normal & 0xff ); - lat *= (FUNCTABLE_SIZE/256); - lng *= (FUNCTABLE_SIZE/256); - - // decode X as cos( lat ) * sin( long ) - // decode Y as sin( lat ) * sin( long ) - // decode Z as cos( long ) - - v->normal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; - v->normal[1] = tr.sinTable[lat] * tr.sinTable[lng]; - v->normal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; - } - - // swap all the ST - surf->st = st = ri.Hunk_Alloc(sizeof(*st) * md3Surf->numVerts, h_low); - - md3st = (md3St_t *) ((byte *) md3Surf + md3Surf->ofsSt); - for(j = 0; j < md3Surf->numVerts; j++, md3st++, st++) - { - st->st[0] = LittleFloat(md3st->st[0]); - st->st[1] = LittleFloat(md3st->st[1]); - } - -#ifdef USE_VERT_TANGENT_SPACE - // calc tangent spaces - { - // Valgrind complaints: Conditional jump or move depends on uninitialised value(s) - // So lets Initialize them. - const float *v0 = NULL, *v1 = NULL, *v2 = NULL; - const float *t0 = NULL, *t1 = NULL, *t2 = NULL; - vec3_t tangent = { 0, 0, 0 }; - vec3_t bitangent = { 0, 0, 0 }; - vec3_t normal = { 0, 0, 0 }; - - for(j = 0, v = surf->verts; j < (surf->numVerts * mdvModel->numFrames); j++, v++) - { - VectorClear(v->tangent); - VectorClear(v->bitangent); - if (r_recalcMD3Normals->integer) - VectorClear(v->normal); - } - - for(f = 0; f < mdvModel->numFrames; f++) - { - for(j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) - { - v0 = surf->verts[surf->numVerts * f + tri->indexes[0]].xyz; - v1 = surf->verts[surf->numVerts * f + tri->indexes[1]].xyz; - v2 = surf->verts[surf->numVerts * f + tri->indexes[2]].xyz; - - t0 = surf->st[tri->indexes[0]].st; - t1 = surf->st[tri->indexes[1]].st; - t2 = surf->st[tri->indexes[2]].st; - - if (!r_recalcMD3Normals->integer) - VectorCopy(v->normal, normal); - else - VectorClear(normal); - - #if 1 - R_CalcTangentSpace(tangent, bitangent, normal, v0, v1, v2, t0, t1, t2); - #else - R_CalcNormalForTriangle(normal, v0, v1, v2); - R_CalcTangentsForTriangle(tangent, bitangent, v0, v1, v2, t0, t1, t2); - #endif - - for(k = 0; k < 3; k++) - { - float *v; - - v = surf->verts[surf->numVerts * f + tri->indexes[k]].tangent; - VectorAdd(v, tangent, v); - - v = surf->verts[surf->numVerts * f + tri->indexes[k]].bitangent; - VectorAdd(v, bitangent, v); - - if (r_recalcMD3Normals->integer) - { - v = surf->verts[surf->numVerts * f + tri->indexes[k]].normal; - VectorAdd(v, normal, v); - } - } - } - } - - for(j = 0, v = surf->verts; j < (surf->numVerts * mdvModel->numFrames); j++, v++) - { - VectorNormalize(v->tangent); - VectorNormalize(v->bitangent); - VectorNormalize(v->normal); - } - } -#endif - - // find the next surface - md3Surf = (md3Surface_t *) ((byte *) md3Surf + md3Surf->ofsEnd); - surf++; - } - - { - srfVBOMDVMesh_t *vboSurf; - - mdvModel->numVBOSurfaces = mdvModel->numSurfaces; - mdvModel->vboSurfaces = ri.Hunk_Alloc(sizeof(*mdvModel->vboSurfaces) * mdvModel->numSurfaces, h_low); - - vboSurf = mdvModel->vboSurfaces; - surf = mdvModel->surfaces; - for (i = 0; i < mdvModel->numSurfaces; i++, vboSurf++, surf++) - { - vec3_t *verts; - vec3_t *normals; - vec2_t *texcoords; -#ifdef USE_VERT_TANGENT_SPACE - vec3_t *tangents; - vec3_t *bitangents; -#endif - - byte *data; - int dataSize; - - int ofs_xyz, ofs_normal, ofs_st; -#ifdef USE_VERT_TANGENT_SPACE - int ofs_tangent, ofs_bitangent; -#endif - - dataSize = 0; - - ofs_xyz = dataSize; - dataSize += surf->numVerts * mdvModel->numFrames * sizeof(*verts); - - ofs_normal = dataSize; - dataSize += surf->numVerts * mdvModel->numFrames * sizeof(*normals); - -#ifdef USE_VERT_TANGENT_SPACE - ofs_tangent = dataSize; - dataSize += surf->numVerts * mdvModel->numFrames * sizeof(*tangents); - - ofs_bitangent = dataSize; - dataSize += surf->numVerts * mdvModel->numFrames * sizeof(*bitangents); -#endif - - ofs_st = dataSize; - dataSize += surf->numVerts * sizeof(*texcoords); - - data = ri.Malloc(dataSize); - - verts = (void *)(data + ofs_xyz); - normals = (void *)(data + ofs_normal); -#ifdef USE_VERT_TANGENT_SPACE - tangents = (void *)(data + ofs_tangent); - bitangents = (void *)(data + ofs_bitangent); -#endif - texcoords = (void *)(data + ofs_st); - - v = surf->verts; - for ( j = 0; j < surf->numVerts * mdvModel->numFrames ; j++, v++ ) - { - VectorCopy(v->xyz, verts[j]); - VectorCopy(v->normal, normals[j]); -#ifdef USE_VERT_TANGENT_SPACE - VectorCopy(v->tangent, tangents[j]); - VectorCopy(v->bitangent, bitangents[j]); -#endif - } - - st = surf->st; - for ( j = 0 ; j < surf->numVerts ; j++, st++ ) { - texcoords[j][0] = st->st[0]; - texcoords[j][1] = st->st[1]; - } - - vboSurf->surfaceType = SF_VBO_MDVMESH; - vboSurf->mdvModel = mdvModel; - vboSurf->mdvSurface = surf; - vboSurf->numIndexes = surf->numTriangles * 3; - vboSurf->numVerts = surf->numVerts; - - vboSurf->minIndex = 0; - vboSurf->maxIndex = surf->numVerts; - - vboSurf->vbo = R_CreateVBO(va("staticMD3Mesh_VBO '%s'", surf->name), data, dataSize, VBO_USAGE_STATIC); - - vboSurf->vbo->ofs_xyz = ofs_xyz; - vboSurf->vbo->ofs_normal = ofs_normal; -#ifdef USE_VERT_TANGENT_SPACE - vboSurf->vbo->ofs_tangent = ofs_tangent; - vboSurf->vbo->ofs_bitangent = ofs_bitangent; -#endif - vboSurf->vbo->ofs_st = ofs_st; - - vboSurf->vbo->stride_xyz = sizeof(*verts); - vboSurf->vbo->stride_normal = sizeof(*normals); -#ifdef USE_VERT_TANGENT_SPACE - vboSurf->vbo->stride_tangent = sizeof(*tangents); - vboSurf->vbo->stride_bitangent = sizeof(*bitangents); -#endif - vboSurf->vbo->stride_st = sizeof(*st); - - vboSurf->vbo->size_xyz = sizeof(*verts) * surf->numVerts; - vboSurf->vbo->size_normal = sizeof(*normals) * surf->numVerts; - - ri.Free(data); - - vboSurf->ibo = R_CreateIBO2(va("staticMD3Mesh_IBO %s", surf->name), surf->numTriangles, surf->triangles, VBO_USAGE_STATIC); - } - } - - return qtrue; -} - - -#ifdef RAVENMD4 - -/* -================= -R_LoadMDR -================= -*/ -static qboolean R_LoadMDR( model_t *mod, void *buffer, int filesize, const char *mod_name ) -{ - int i, j, k, l; - mdrHeader_t *pinmodel, *mdr; - mdrFrame_t *frame; - mdrLOD_t *lod, *curlod; - mdrSurface_t *surf, *cursurf; - mdrTriangle_t *tri, *curtri; - mdrVertex_t *v, *curv; - mdrWeight_t *weight, *curweight; - mdrTag_t *tag, *curtag; - int size; - shader_t *sh; - - pinmodel = (mdrHeader_t *)buffer; - - pinmodel->version = LittleLong(pinmodel->version); - if (pinmodel->version != MDR_VERSION) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has wrong version (%i should be %i)\n", mod_name, pinmodel->version, MDR_VERSION); - return qfalse; - } - - size = LittleLong(pinmodel->ofsEnd); - - if(size > filesize) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: Header of %s is broken. Wrong filesize declared!\n", mod_name); - return qfalse; - } - - mod->type = MOD_MDR; - - LL(pinmodel->numFrames); - LL(pinmodel->numBones); - LL(pinmodel->ofsFrames); - - // This is a model that uses some type of compressed Bones. We don't want to uncompress every bone for each rendered frame - // over and over again, we'll uncompress it in this function already, so we must adjust the size of the target md4. - if(pinmodel->ofsFrames < 0) - { - // mdrFrame_t is larger than mdrCompFrame_t: - size += pinmodel->numFrames * sizeof(frame->name); - // now add enough space for the uncompressed bones. - size += pinmodel->numFrames * pinmodel->numBones * ((sizeof(mdrBone_t) - sizeof(mdrCompBone_t))); - } - - // simple bounds check - if(pinmodel->numBones < 0 || - sizeof(*mdr) + pinmodel->numFrames * (sizeof(*frame) + (pinmodel->numBones - 1) * sizeof(*frame->bones)) > size) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); - return qfalse; - } - - mod->dataSize += size; - mod->modelData = mdr = ri.Hunk_Alloc( size, h_low ); - - // Copy all the values over from the file and fix endian issues in the process, if necessary. - - mdr->ident = LittleLong(pinmodel->ident); - mdr->version = pinmodel->version; // Don't need to swap byte order on this one, we already did above. - Q_strncpyz(mdr->name, pinmodel->name, sizeof(mdr->name)); - mdr->numFrames = pinmodel->numFrames; - mdr->numBones = pinmodel->numBones; - mdr->numLODs = LittleLong(pinmodel->numLODs); - mdr->numTags = LittleLong(pinmodel->numTags); - // We don't care about the other offset values, we'll generate them ourselves while loading. - - mod->numLods = mdr->numLODs; - - if ( mdr->numFrames < 1 ) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has no frames\n", mod_name); - return qfalse; - } - - /* The first frame will be put into the first free space after the header */ - frame = (mdrFrame_t *)(mdr + 1); - mdr->ofsFrames = (int)((byte *) frame - (byte *) mdr); - - if (pinmodel->ofsFrames < 0) - { - mdrCompFrame_t *cframe; - - // compressed model... - cframe = (mdrCompFrame_t *)((byte *) pinmodel - pinmodel->ofsFrames); - - for(i = 0; i < mdr->numFrames; i++) - { - for(j = 0; j < 3; j++) - { - frame->bounds[0][j] = LittleFloat(cframe->bounds[0][j]); - frame->bounds[1][j] = LittleFloat(cframe->bounds[1][j]); - frame->localOrigin[j] = LittleFloat(cframe->localOrigin[j]); - } - - frame->radius = LittleFloat(cframe->radius); - frame->name[0] = '\0'; // No name supplied in the compressed version. - - for(j = 0; j < mdr->numBones; j++) - { - for(k = 0; k < (sizeof(cframe->bones[j].Comp) / 2); k++) - { - // Do swapping for the uncompressing functions. They seem to use shorts - // values only, so I assume this will work. Never tested it on other - // platforms, though. - - ((unsigned short *)(cframe->bones[j].Comp))[k] = - LittleShort( ((unsigned short *)(cframe->bones[j].Comp))[k] ); - } - - /* Now do the actual uncompressing */ - MC_UnCompress(frame->bones[j].matrix, cframe->bones[j].Comp); - } - - // Next Frame... - cframe = (mdrCompFrame_t *) &cframe->bones[j]; - frame = (mdrFrame_t *) &frame->bones[j]; - } - } - else - { - mdrFrame_t *curframe; - - // uncompressed model... - // - - curframe = (mdrFrame_t *)((byte *) pinmodel + pinmodel->ofsFrames); - - // swap all the frames - for ( i = 0 ; i < mdr->numFrames ; i++) - { - for(j = 0; j < 3; j++) - { - frame->bounds[0][j] = LittleFloat(curframe->bounds[0][j]); - frame->bounds[1][j] = LittleFloat(curframe->bounds[1][j]); - frame->localOrigin[j] = LittleFloat(curframe->localOrigin[j]); - } - - frame->radius = LittleFloat(curframe->radius); - Q_strncpyz(frame->name, curframe->name, sizeof(frame->name)); - - for (j = 0; j < (int) (mdr->numBones * sizeof(mdrBone_t) / 4); j++) - { - ((float *)frame->bones)[j] = LittleFloat( ((float *)curframe->bones)[j] ); - } - - curframe = (mdrFrame_t *) &curframe->bones[mdr->numBones]; - frame = (mdrFrame_t *) &frame->bones[mdr->numBones]; - } - } - - // frame should now point to the first free address after all frames. - lod = (mdrLOD_t *) frame; - mdr->ofsLODs = (int) ((byte *) lod - (byte *)mdr); - - curlod = (mdrLOD_t *)((byte *) pinmodel + LittleLong(pinmodel->ofsLODs)); - - // swap all the LOD's - for ( l = 0 ; l < mdr->numLODs ; l++) - { - // simple bounds check - if((byte *) (lod + 1) > (byte *) mdr + size) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); - return qfalse; - } - - lod->numSurfaces = LittleLong(curlod->numSurfaces); - - // swap all the surfaces - surf = (mdrSurface_t *) (lod + 1); - lod->ofsSurfaces = (int)((byte *) surf - (byte *) lod); - cursurf = (mdrSurface_t *) ((byte *)curlod + LittleLong(curlod->ofsSurfaces)); - - for ( i = 0 ; i < lod->numSurfaces ; i++) - { - // simple bounds check - if((byte *) (surf + 1) > (byte *) mdr + size) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); - return qfalse; - } - - // first do some copying stuff - - surf->ident = SF_MDR; - Q_strncpyz(surf->name, cursurf->name, sizeof(surf->name)); - Q_strncpyz(surf->shader, cursurf->shader, sizeof(surf->shader)); - - surf->ofsHeader = (byte *) mdr - (byte *) surf; - - surf->numVerts = LittleLong(cursurf->numVerts); - surf->numTriangles = LittleLong(cursurf->numTriangles); - // numBoneReferences and BoneReferences generally seem to be unused - - // now do the checks that may fail. - if ( surf->numVerts > SHADER_MAX_VERTEXES ) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has more than %i verts on a surface (%i).\n", - mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); - return qfalse; - } - if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has more than %i triangles on a surface (%i).\n", - mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); - return qfalse; - } - // lowercase the surface name so skin compares are faster - Q_strlwr( surf->name ); - - // register the shaders - sh = R_FindShader(surf->shader, LIGHTMAP_NONE, qtrue); - if ( sh->defaultShader ) { - surf->shaderIndex = 0; - } else { - surf->shaderIndex = sh->index; - } - - // now copy the vertexes. - v = (mdrVertex_t *) (surf + 1); - surf->ofsVerts = (int)((byte *) v - (byte *) surf); - curv = (mdrVertex_t *) ((byte *)cursurf + LittleLong(cursurf->ofsVerts)); - - for(j = 0; j < surf->numVerts; j++) - { - LL(curv->numWeights); - - // simple bounds check - if(curv->numWeights < 0 || (byte *) (v + 1) + (curv->numWeights - 1) * sizeof(*weight) > (byte *) mdr + size) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); - return qfalse; - } - - v->normal[0] = LittleFloat(curv->normal[0]); - v->normal[1] = LittleFloat(curv->normal[1]); - v->normal[2] = LittleFloat(curv->normal[2]); - - v->texCoords[0] = LittleFloat(curv->texCoords[0]); - v->texCoords[1] = LittleFloat(curv->texCoords[1]); - - v->numWeights = curv->numWeights; - weight = &v->weights[0]; - curweight = &curv->weights[0]; - - // Now copy all the weights - for(k = 0; k < v->numWeights; k++) - { - weight->boneIndex = LittleLong(curweight->boneIndex); - weight->boneWeight = LittleFloat(curweight->boneWeight); - - weight->offset[0] = LittleFloat(curweight->offset[0]); - weight->offset[1] = LittleFloat(curweight->offset[1]); - weight->offset[2] = LittleFloat(curweight->offset[2]); - - weight++; - curweight++; - } - - v = (mdrVertex_t *) weight; - curv = (mdrVertex_t *) curweight; - } - - // we know the offset to the triangles now: - tri = (mdrTriangle_t *) v; - surf->ofsTriangles = (int)((byte *) tri - (byte *) surf); - curtri = (mdrTriangle_t *)((byte *) cursurf + LittleLong(cursurf->ofsTriangles)); - - // simple bounds check - if(surf->numTriangles < 0 || (byte *) (tri + surf->numTriangles) > (byte *) mdr + size) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); - return qfalse; - } - - for(j = 0; j < surf->numTriangles; j++) - { - tri->indexes[0] = LittleLong(curtri->indexes[0]); - tri->indexes[1] = LittleLong(curtri->indexes[1]); - tri->indexes[2] = LittleLong(curtri->indexes[2]); - - tri++; - curtri++; - } - - // tri now points to the end of the surface. - surf->ofsEnd = (byte *) tri - (byte *) surf; - surf = (mdrSurface_t *) tri; - - // find the next surface. - cursurf = (mdrSurface_t *) ((byte *) cursurf + LittleLong(cursurf->ofsEnd)); - } - - // surf points to the next lod now. - lod->ofsEnd = (int)((byte *) surf - (byte *) lod); - lod = (mdrLOD_t *) surf; - - // find the next LOD. - curlod = (mdrLOD_t *)((byte *) curlod + LittleLong(curlod->ofsEnd)); - } - - // lod points to the first tag now, so update the offset too. - tag = (mdrTag_t *) lod; - mdr->ofsTags = (int)((byte *) tag - (byte *) mdr); - curtag = (mdrTag_t *) ((byte *)pinmodel + LittleLong(pinmodel->ofsTags)); - - // simple bounds check - if(mdr->numTags < 0 || (byte *) (tag + mdr->numTags) > (byte *) mdr + size) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); - return qfalse; - } - - for (i = 0 ; i < mdr->numTags ; i++) - { - tag->boneIndex = LittleLong(curtag->boneIndex); - Q_strncpyz(tag->name, curtag->name, sizeof(tag->name)); - - tag++; - curtag++; - } - - // And finally we know the real offset to the end. - mdr->ofsEnd = (int)((byte *) tag - (byte *) mdr); - - // phew! we're done. - - return qtrue; -} -#endif - -/* -================= -R_LoadMD4 -================= -*/ - -static qboolean R_LoadMD4( model_t *mod, void *buffer, const char *mod_name ) { - int i, j, k, lodindex; - md4Header_t *pinmodel, *md4; - md4Frame_t *frame; - md4LOD_t *lod; - md4Surface_t *surf; - md4Triangle_t *tri; - md4Vertex_t *v; - int version; - int size; - shader_t *sh; - int frameSize; - - pinmodel = (md4Header_t *)buffer; - - version = LittleLong (pinmodel->version); - if (version != MD4_VERSION) { - ri.Printf( PRINT_WARNING, "R_LoadMD4: %s has wrong version (%i should be %i)\n", - mod_name, version, MD4_VERSION); - return qfalse; - } - - mod->type = MOD_MD4; - size = LittleLong(pinmodel->ofsEnd); - mod->dataSize += size; - mod->modelData = md4 = ri.Hunk_Alloc( size, h_low ); - - Com_Memcpy(md4, buffer, size); - - LL(md4->ident); - LL(md4->version); - LL(md4->numFrames); - LL(md4->numBones); - LL(md4->numLODs); - LL(md4->ofsFrames); - LL(md4->ofsLODs); - md4->ofsEnd = size; - - if ( md4->numFrames < 1 ) { - ri.Printf( PRINT_WARNING, "R_LoadMD4: %s has no frames\n", mod_name ); - return qfalse; - } - - // we don't need to swap tags in the renderer, they aren't used - - // swap all the frames - frameSize = (size_t)( &((md4Frame_t *)0)->bones[ md4->numBones ] ); - for ( i = 0 ; i < md4->numFrames ; i++) { - frame = (md4Frame_t *) ( (byte *)md4 + md4->ofsFrames + i * frameSize ); - frame->radius = LittleFloat( frame->radius ); - for ( j = 0 ; j < 3 ; j++ ) { - frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] ); - frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] ); - frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] ); - } - for ( j = 0 ; j < md4->numBones * sizeof( md4Bone_t ) / 4 ; j++ ) { - ((float *)frame->bones)[j] = LittleFloat( ((float *)frame->bones)[j] ); - } - } - - // swap all the LOD's - lod = (md4LOD_t *) ( (byte *)md4 + md4->ofsLODs ); - for ( lodindex = 0 ; lodindex < md4->numLODs ; lodindex++ ) { - - // swap all the surfaces - surf = (md4Surface_t *) ( (byte *)lod + lod->ofsSurfaces ); - for ( i = 0 ; i < lod->numSurfaces ; i++) { - LL(surf->ident); - LL(surf->numTriangles); - LL(surf->ofsTriangles); - LL(surf->numVerts); - LL(surf->ofsVerts); - LL(surf->ofsEnd); - - if ( surf->numVerts > SHADER_MAX_VERTEXES ) { - ri.Printf(PRINT_WARNING, "R_LoadMD4: %s has more than %i verts on a surface (%i).\n", - mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); - return qfalse; - } - if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) { - ri.Printf(PRINT_WARNING, "R_LoadMD4: %s has more than %i triangles on a surface (%i).\n", - mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); - return qfalse; - } - - // change to surface identifier - surf->ident = SF_MD4; - - // lowercase the surface name so skin compares are faster - Q_strlwr( surf->name ); - - // register the shaders - sh = R_FindShader( surf->shader, LIGHTMAP_NONE, qtrue ); - if ( sh->defaultShader ) { - surf->shaderIndex = 0; - } else { - surf->shaderIndex = sh->index; - } - - // swap all the triangles - tri = (md4Triangle_t *) ( (byte *)surf + surf->ofsTriangles ); - for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) { - LL(tri->indexes[0]); - LL(tri->indexes[1]); - LL(tri->indexes[2]); - } - - // swap all the vertexes - // FIXME - // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left - // in for reference. - //v = (md4Vertex_t *) ( (byte *)surf + surf->ofsVerts + 12); - v = (md4Vertex_t *) ( (byte *)surf + surf->ofsVerts); - for ( j = 0 ; j < surf->numVerts ; j++ ) { - v->normal[0] = LittleFloat( v->normal[0] ); - v->normal[1] = LittleFloat( v->normal[1] ); - v->normal[2] = LittleFloat( v->normal[2] ); - - v->texCoords[0] = LittleFloat( v->texCoords[0] ); - v->texCoords[1] = LittleFloat( v->texCoords[1] ); - - v->numWeights = LittleLong( v->numWeights ); - - for ( k = 0 ; k < v->numWeights ; k++ ) { - v->weights[k].boneIndex = LittleLong( v->weights[k].boneIndex ); - v->weights[k].boneWeight = LittleFloat( v->weights[k].boneWeight ); - v->weights[k].offset[0] = LittleFloat( v->weights[k].offset[0] ); - v->weights[k].offset[1] = LittleFloat( v->weights[k].offset[1] ); - v->weights[k].offset[2] = LittleFloat( v->weights[k].offset[2] ); - } - // FIXME - // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left - // in for reference. - //v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights] + 12 ); - v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights]); - } - - // find the next surface - surf = (md4Surface_t *)( (byte *)surf + surf->ofsEnd ); - } - - // find the next LOD - lod = (md4LOD_t *)( (byte *)lod + lod->ofsEnd ); - } - - return qtrue; -} - - - -//============================================================================= - -/* -** RE_BeginRegistration -*/ -void RE_BeginRegistration( glconfig_t *glconfigOut ) { - - R_Init(); - - *glconfigOut = glConfig; - - R_IssuePendingRenderCommands(); - - tr.visIndex = 0; - memset(tr.visClusters, -2, sizeof(tr.visClusters)); // force markleafs to regenerate - - R_ClearFlares(); - RE_ClearScene(); - - tr.registered = qtrue; - - // NOTE: this sucks, for some reason the first stretch pic is never drawn - // without this we'd see a white flash on a level load because the very - // first time the level shot would not be drawn -// RE_StretchPic(0, 0, 0, 0, 0, 0, 1, 1, 0); -} - -//============================================================================= - -/* -=============== -R_ModelInit -=============== -*/ -void R_ModelInit( void ) { - model_t *mod; - - // leave a space for NULL model - tr.numModels = 0; - - mod = R_AllocModel(); - mod->type = MOD_BAD; -} - - -/* -================ -R_Modellist_f -================ -*/ -void R_Modellist_f( void ) { - int i, j; - model_t *mod; - int total; - int lods; - - total = 0; - for ( i = 1 ; i < tr.numModels; i++ ) { - mod = tr.models[i]; - lods = 1; - for ( j = 1 ; j < MD3_MAX_LODS ; j++ ) { - if ( mod->mdv[j] && mod->mdv[j] != mod->mdv[j-1] ) { - lods++; - } - } - ri.Printf( PRINT_ALL, "%8i : (%i) %s\n",mod->dataSize, lods, mod->name ); - total += mod->dataSize; - } - ri.Printf( PRINT_ALL, "%8i : Total models\n", total ); - -#if 0 // not working right with new hunk - if ( tr.world ) { - ri.Printf( PRINT_ALL, "\n%8i : %s\n", tr.world->dataSize, tr.world->name ); - } -#endif -} - - -//============================================================================= - - -/* -================ -R_GetTag -================ -*/ -static mdvTag_t *R_GetTag( mdvModel_t *mod, int frame, const char *_tagName ) { - int i; - mdvTag_t *tag; - mdvTagName_t *tagName; - - if ( frame >= mod->numFrames ) { - // it is possible to have a bad frame while changing models, so don't error - frame = mod->numFrames - 1; - } - - tag = mod->tags + frame * mod->numTags; - tagName = mod->tagNames; - for(i = 0; i < mod->numTags; i++, tag++, tagName++) - { - if(!strcmp(tagName->name, _tagName)) - { - return tag; - } - } - - return NULL; -} - -#ifdef RAVENMD4 -void R_GetAnimTag( mdrHeader_t *mod, int framenum, const char *tagName, md3Tag_t * dest) -{ - int i, j, k; - int frameSize; - mdrFrame_t *frame; - mdrTag_t *tag; - - if ( framenum >= mod->numFrames ) - { - // it is possible to have a bad frame while changing models, so don't error - framenum = mod->numFrames - 1; - } - - tag = (mdrTag_t *)((byte *)mod + mod->ofsTags); - for ( i = 0 ; i < mod->numTags ; i++, tag++ ) - { - if ( !strcmp( tag->name, tagName ) ) - { - Q_strncpyz(dest->name, tag->name, sizeof(dest->name)); - - // uncompressed model... - // - frameSize = (intptr_t)( &((mdrFrame_t *)0)->bones[ mod->numBones ] ); - frame = (mdrFrame_t *)((byte *)mod + mod->ofsFrames + framenum * frameSize ); - - for (j = 0; j < 3; j++) - { - for (k = 0; k < 3; k++) - dest->axis[j][k]=frame->bones[tag->boneIndex].matrix[k][j]; - } - - dest->origin[0]=frame->bones[tag->boneIndex].matrix[0][3]; - dest->origin[1]=frame->bones[tag->boneIndex].matrix[1][3]; - dest->origin[2]=frame->bones[tag->boneIndex].matrix[2][3]; - - return; - } - } - - AxisClear( dest->axis ); - VectorClear( dest->origin ); - strcpy(dest->name,""); -} -#endif - -/* -================ -R_LerpTag -================ -*/ -int R_LerpTag( orientation_t *tag, qhandle_t handle, int startFrame, int endFrame, - float frac, const char *tagName ) { - mdvTag_t *start, *end; -#ifdef RAVENMD4 - md3Tag_t start_space, end_space; -#endif - int i; - float frontLerp, backLerp; - model_t *model; - - model = R_GetModelByHandle( handle ); - if ( !model->mdv[0] ) - { -#ifdef RAVENMD4 - if(model->type == MOD_MDR) - { - start = &start_space; - end = &end_space; - R_GetAnimTag((mdrHeader_t *) model->modelData, startFrame, tagName, start); - R_GetAnimTag((mdrHeader_t *) model->modelData, endFrame, tagName, end); - } - else -#endif - if( model->type == MOD_IQM ) { - return R_IQMLerpTag( tag, model->modelData, - startFrame, endFrame, - frac, tagName ); - } else { - - AxisClear( tag->axis ); - VectorClear( tag->origin ); - return qfalse; - - } - } - else - { - start = R_GetTag( model->mdv[0], startFrame, tagName ); - end = R_GetTag( model->mdv[0], endFrame, tagName ); - if ( !start || !end ) { - AxisClear( tag->axis ); - VectorClear( tag->origin ); - return qfalse; - } - } - - frontLerp = frac; - backLerp = 1.0f - frac; - - for ( i = 0 ; i < 3 ; i++ ) { - tag->origin[i] = start->origin[i] * backLerp + end->origin[i] * frontLerp; - tag->axis[0][i] = start->axis[0][i] * backLerp + end->axis[0][i] * frontLerp; - tag->axis[1][i] = start->axis[1][i] * backLerp + end->axis[1][i] * frontLerp; - tag->axis[2][i] = start->axis[2][i] * backLerp + end->axis[2][i] * frontLerp; - } - VectorNormalize( tag->axis[0] ); - VectorNormalize( tag->axis[1] ); - VectorNormalize( tag->axis[2] ); - return qtrue; -} - - -/* -==================== -R_ModelBounds -==================== -*/ -void R_ModelBounds( qhandle_t handle, vec3_t mins, vec3_t maxs ) { - model_t *model; - - model = R_GetModelByHandle( handle ); - - if(model->type == MOD_BRUSH) { - VectorCopy( model->bmodel->bounds[0], mins ); - VectorCopy( model->bmodel->bounds[1], maxs ); - - return; - } else if (model->type == MOD_MESH) { - mdvModel_t *header; - mdvFrame_t *frame; - - header = model->mdv[0]; - frame = header->frames; - - VectorCopy( frame->bounds[0], mins ); - VectorCopy( frame->bounds[1], maxs ); - - return; - } else if (model->type == MOD_MD4) { - md4Header_t *header; - md4Frame_t *frame; - - header = (md4Header_t *)model->modelData; - frame = (md4Frame_t *) ((byte *)header + header->ofsFrames); - - VectorCopy( frame->bounds[0], mins ); - VectorCopy( frame->bounds[1], maxs ); - - return; -#ifdef RAVENMD4 - } else if (model->type == MOD_MDR) { - mdrHeader_t *header; - mdrFrame_t *frame; - - header = (mdrHeader_t *)model->modelData; - frame = (mdrFrame_t *) ((byte *)header + header->ofsFrames); - - VectorCopy( frame->bounds[0], mins ); - VectorCopy( frame->bounds[1], maxs ); - - return; -#endif - } else if(model->type == MOD_IQM) { - iqmData_t *iqmData; - - iqmData = model->modelData; - - if(iqmData->bounds) - { - VectorCopy(iqmData->bounds, mins); - VectorCopy(iqmData->bounds + 3, maxs); - return; - } - } - - VectorClear( mins ); - VectorClear( maxs ); -} diff --git a/src/rend2/tr_model_iqm.c b/src/rend2/tr_model_iqm.c deleted file mode 100644 index 1f1bf747..00000000 --- a/src/rend2/tr_model_iqm.c +++ /dev/null @@ -1,1058 +0,0 @@ -/* -=========================================================================== -Copyright (C) 2011 Thilo Schulz -Copyright (C) 2011 Matthias Bentrup - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "tr_local.h" - -#define LL(x) x=LittleLong(x) - -static qboolean IQM_CheckRange( iqmHeader_t *header, int offset, - int count,int size ) { - // return true if the range specified by offset, count and size - // doesn't fit into the file - return ( count <= 0 || - offset < 0 || - offset > header->filesize || - offset + count * size < 0 || - offset + count * size > header->filesize ); -} -// "multiply" 3x4 matrices, these are assumed to be the top 3 rows -// of a 4x4 matrix with the last row = (0 0 0 1) -static void Matrix34Multiply( float *a, float *b, float *out ) { - out[ 0] = a[0] * b[0] + a[1] * b[4] + a[ 2] * b[ 8]; - out[ 1] = a[0] * b[1] + a[1] * b[5] + a[ 2] * b[ 9]; - out[ 2] = a[0] * b[2] + a[1] * b[6] + a[ 2] * b[10]; - out[ 3] = a[0] * b[3] + a[1] * b[7] + a[ 2] * b[11] + a[ 3]; - out[ 4] = a[4] * b[0] + a[5] * b[4] + a[ 6] * b[ 8]; - out[ 5] = a[4] * b[1] + a[5] * b[5] + a[ 6] * b[ 9]; - out[ 6] = a[4] * b[2] + a[5] * b[6] + a[ 6] * b[10]; - out[ 7] = a[4] * b[3] + a[5] * b[7] + a[ 6] * b[11] + a[ 7]; - out[ 8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[ 8]; - out[ 9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[ 9]; - out[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10]; - out[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11]; -} -static void InterpolateMatrix( float *a, float *b, float lerp, float *mat ) { - float unLerp = 1.0f - lerp; - - mat[ 0] = a[ 0] * unLerp + b[ 0] * lerp; - mat[ 1] = a[ 1] * unLerp + b[ 1] * lerp; - mat[ 2] = a[ 2] * unLerp + b[ 2] * lerp; - mat[ 3] = a[ 3] * unLerp + b[ 3] * lerp; - mat[ 4] = a[ 4] * unLerp + b[ 4] * lerp; - mat[ 5] = a[ 5] * unLerp + b[ 5] * lerp; - mat[ 6] = a[ 6] * unLerp + b[ 6] * lerp; - mat[ 7] = a[ 7] * unLerp + b[ 7] * lerp; - mat[ 8] = a[ 8] * unLerp + b[ 8] * lerp; - mat[ 9] = a[ 9] * unLerp + b[ 9] * lerp; - mat[10] = a[10] * unLerp + b[10] * lerp; - mat[11] = a[11] * unLerp + b[11] * lerp; -} -static void JointToMatrix( vec4_t rot, vec3_t scale, vec3_t trans, - float *mat ) { - float xx = 2.0f * rot[0] * rot[0]; - float yy = 2.0f * rot[1] * rot[1]; - float zz = 2.0f * rot[2] * rot[2]; - float xy = 2.0f * rot[0] * rot[1]; - float xz = 2.0f * rot[0] * rot[2]; - float yz = 2.0f * rot[1] * rot[2]; - float wx = 2.0f * rot[3] * rot[0]; - float wy = 2.0f * rot[3] * rot[1]; - float wz = 2.0f * rot[3] * rot[2]; - - mat[ 0] = scale[0] * (1.0f - (yy + zz)); - mat[ 1] = scale[0] * (xy - wz); - mat[ 2] = scale[0] * (xz + wy); - mat[ 3] = trans[0]; - mat[ 4] = scale[1] * (xy + wz); - mat[ 5] = scale[1] * (1.0f - (xx + zz)); - mat[ 6] = scale[1] * (yz - wx); - mat[ 7] = trans[1]; - mat[ 8] = scale[2] * (xz - wy); - mat[ 9] = scale[2] * (yz + wx); - mat[10] = scale[2] * (1.0f - (xx + yy)); - mat[11] = trans[2]; -} -static void Matrix34Invert( float *inMat, float *outMat ) -{ - vec3_t trans; - float invSqrLen, *v; - - outMat[ 0] = inMat[ 0]; outMat[ 1] = inMat[ 4]; outMat[ 2] = inMat[ 8]; - outMat[ 4] = inMat[ 1]; outMat[ 5] = inMat[ 5]; outMat[ 6] = inMat[ 9]; - outMat[ 8] = inMat[ 2]; outMat[ 9] = inMat[ 6]; outMat[10] = inMat[10]; - - v = outMat + 0; invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); - v = outMat + 4; invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); - v = outMat + 8; invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); - - trans[0] = inMat[ 3]; - trans[1] = inMat[ 7]; - trans[2] = inMat[11]; - - outMat[ 3] = -DotProduct(outMat + 0, trans); - outMat[ 7] = -DotProduct(outMat + 4, trans); - outMat[11] = -DotProduct(outMat + 8, trans); -} - -/* -================= -R_LoadIQM - -Load an IQM model and compute the joint matrices for every frame. -================= -*/ -qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_name ) { - iqmHeader_t *header; - iqmVertexArray_t *vertexarray; - iqmTriangle_t *triangle; - iqmMesh_t *mesh; - iqmJoint_t *joint; - iqmPose_t *pose; - iqmBounds_t *bounds; - unsigned short *framedata; - char *str; - int i, j; - float jointMats[IQM_MAX_JOINTS * 2 * 12]; - float *mat; - size_t size, joint_names; - iqmData_t *iqmData; - srfIQModel_t *surface; - - if( filesize < sizeof(iqmHeader_t) ) { - return qfalse; - } - - header = (iqmHeader_t *)buffer; - if( Q_strncmp( header->magic, IQM_MAGIC, sizeof(header->magic) ) ) { - return qfalse; - } - - LL( header->version ); - if( header->version != IQM_VERSION ) { - ri.Printf(PRINT_WARNING, "R_LoadIQM: %s is a unsupported IQM version (%d), only version %d is supported.\n", - mod_name, header->version, IQM_VERSION); - return qfalse; - } - - LL( header->filesize ); - if( header->filesize > filesize || header->filesize > 16<<20 ) { - return qfalse; - } - - LL( header->flags ); - LL( header->num_text ); - LL( header->ofs_text ); - LL( header->num_meshes ); - LL( header->ofs_meshes ); - LL( header->num_vertexarrays ); - LL( header->num_vertexes ); - LL( header->ofs_vertexarrays ); - LL( header->num_triangles ); - LL( header->ofs_triangles ); - LL( header->ofs_adjacency ); - LL( header->num_joints ); - LL( header->ofs_joints ); - LL( header->num_poses ); - LL( header->ofs_poses ); - LL( header->num_anims ); - LL( header->ofs_anims ); - LL( header->num_frames ); - LL( header->num_framechannels ); - LL( header->ofs_frames ); - LL( header->ofs_bounds ); - LL( header->num_comment ); - LL( header->ofs_comment ); - LL( header->num_extensions ); - LL( header->ofs_extensions ); - - // check ioq3 joint limit - if ( header->num_joints > IQM_MAX_JOINTS ) { - ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %d joints (%d).\n", - mod_name, IQM_MAX_JOINTS, header->num_joints); - return qfalse; - } - - // check and swap vertex arrays - if( IQM_CheckRange( header, header->ofs_vertexarrays, - header->num_vertexarrays, - sizeof(iqmVertexArray_t) ) ) { - return qfalse; - } - vertexarray = (iqmVertexArray_t *)((byte *)header + header->ofs_vertexarrays); - for( i = 0; i < header->num_vertexarrays; i++, vertexarray++ ) { - int j, n, *intPtr; - - if( vertexarray->size <= 0 || vertexarray->size > 4 ) { - return qfalse; - } - - // total number of values - n = header->num_vertexes * vertexarray->size; - - switch( vertexarray->format ) { - case IQM_BYTE: - case IQM_UBYTE: - // 1 byte, no swapping necessary - if( IQM_CheckRange( header, vertexarray->offset, - n, sizeof(byte) ) ) { - return qfalse; - } - break; - case IQM_INT: - case IQM_UINT: - case IQM_FLOAT: - // 4-byte swap - if( IQM_CheckRange( header, vertexarray->offset, - n, sizeof(float) ) ) { - return qfalse; - } - intPtr = (int *)((byte *)header + vertexarray->offset); - for( j = 0; j < n; j++, intPtr++ ) { - LL( *intPtr ); - } - break; - default: - // not supported - return qfalse; - break; - } - - switch( vertexarray->type ) { - case IQM_POSITION: - case IQM_NORMAL: - if( vertexarray->format != IQM_FLOAT || - vertexarray->size != 3 ) { - return qfalse; - } - break; - case IQM_TANGENT: - if( vertexarray->format != IQM_FLOAT || - vertexarray->size != 4 ) { - return qfalse; - } - break; - case IQM_TEXCOORD: - if( vertexarray->format != IQM_FLOAT || - vertexarray->size != 2 ) { - return qfalse; - } - break; - case IQM_BLENDINDEXES: - case IQM_BLENDWEIGHTS: - if( vertexarray->format != IQM_UBYTE || - vertexarray->size != 4 ) { - return qfalse; - } - break; - case IQM_COLOR: - if( vertexarray->format != IQM_UBYTE || - vertexarray->size != 4 ) { - return qfalse; - } - break; - } - } - - // check and swap triangles - if( IQM_CheckRange( header, header->ofs_triangles, - header->num_triangles, sizeof(iqmTriangle_t) ) ) { - return qfalse; - } - triangle = (iqmTriangle_t *)((byte *)header + header->ofs_triangles); - for( i = 0; i < header->num_triangles; i++, triangle++ ) { - LL( triangle->vertex[0] ); - LL( triangle->vertex[1] ); - LL( triangle->vertex[2] ); - - if( triangle->vertex[0] > header->num_vertexes || - triangle->vertex[1] > header->num_vertexes || - triangle->vertex[2] > header->num_vertexes ) { - return qfalse; - } - } - - // check and swap meshes - if( IQM_CheckRange( header, header->ofs_meshes, - header->num_meshes, sizeof(iqmMesh_t) ) ) { - return qfalse; - } - mesh = (iqmMesh_t *)((byte *)header + header->ofs_meshes); - for( i = 0; i < header->num_meshes; i++, mesh++) { - LL( mesh->name ); - LL( mesh->material ); - LL( mesh->first_vertex ); - LL( mesh->num_vertexes ); - LL( mesh->first_triangle ); - LL( mesh->num_triangles ); - - // check ioq3 limits - if ( mesh->num_vertexes > SHADER_MAX_VERTEXES ) - { - ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i verts on a surface (%i).\n", - mod_name, SHADER_MAX_VERTEXES, mesh->num_vertexes ); - return qfalse; - } - if ( mesh->num_triangles*3 > SHADER_MAX_INDEXES ) - { - ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i triangles on a surface (%i).\n", - mod_name, SHADER_MAX_INDEXES / 3, mesh->num_triangles ); - return qfalse; - } - - if( mesh->first_vertex >= header->num_vertexes || - mesh->first_vertex + mesh->num_vertexes > header->num_vertexes || - mesh->first_triangle >= header->num_triangles || - mesh->first_triangle + mesh->num_triangles > header->num_triangles || - mesh->name >= header->num_text || - mesh->material >= header->num_text ) { - return qfalse; - } - } - - // check and swap joints - if( IQM_CheckRange( header, header->ofs_joints, - header->num_joints, sizeof(iqmJoint_t) ) ) { - return qfalse; - } - joint = (iqmJoint_t *)((byte *)header + header->ofs_joints); - joint_names = 0; - for( i = 0; i < header->num_joints; i++, joint++ ) { - LL( joint->name ); - LL( joint->parent ); - LL( joint->translate[0] ); - LL( joint->translate[1] ); - LL( joint->translate[2] ); - LL( joint->rotate[0] ); - LL( joint->rotate[1] ); - LL( joint->rotate[2] ); - LL( joint->rotate[3] ); - LL( joint->scale[0] ); - LL( joint->scale[1] ); - LL( joint->scale[2] ); - - if( joint->parent < -1 || - joint->parent >= (int)header->num_joints || - joint->name >= (int)header->num_text ) { - return qfalse; - } - joint_names += strlen( (char *)header + header->ofs_text + - joint->name ) + 1; - } - - // check and swap poses - if( header->num_poses != header->num_joints ) { - return qfalse; - } - if( IQM_CheckRange( header, header->ofs_poses, - header->num_poses, sizeof(iqmPose_t) ) ) { - return qfalse; - } - pose = (iqmPose_t *)((byte *)header + header->ofs_poses); - for( i = 0; i < header->num_poses; i++, pose++ ) { - LL( pose->parent ); - LL( pose->mask ); - LL( pose->channeloffset[0] ); - LL( pose->channeloffset[1] ); - LL( pose->channeloffset[2] ); - LL( pose->channeloffset[3] ); - LL( pose->channeloffset[4] ); - LL( pose->channeloffset[5] ); - LL( pose->channeloffset[6] ); - LL( pose->channeloffset[7] ); - LL( pose->channeloffset[8] ); - LL( pose->channeloffset[9] ); - LL( pose->channelscale[0] ); - LL( pose->channelscale[1] ); - LL( pose->channelscale[2] ); - LL( pose->channelscale[3] ); - LL( pose->channelscale[4] ); - LL( pose->channelscale[5] ); - LL( pose->channelscale[6] ); - LL( pose->channelscale[7] ); - LL( pose->channelscale[8] ); - LL( pose->channelscale[9] ); - } - - if (header->ofs_bounds) - { - // check and swap model bounds - if(IQM_CheckRange(header, header->ofs_bounds, - header->num_frames, sizeof(*bounds))) - { - return qfalse; - } - bounds = (iqmBounds_t *) ((byte *) header + header->ofs_bounds); - for(i = 0; i < header->num_frames; i++) - { - LL(bounds->bbmin[0]); - LL(bounds->bbmin[1]); - LL(bounds->bbmin[2]); - LL(bounds->bbmax[0]); - LL(bounds->bbmax[1]); - LL(bounds->bbmax[2]); - - bounds++; - } - } - - // allocate the model and copy the data - size = sizeof(iqmData_t); - size += header->num_meshes * sizeof( srfIQModel_t ); - size += header->num_joints * header->num_frames * 12 * sizeof( float ); - if(header->ofs_bounds) - size += header->num_frames * 6 * sizeof(float); // model bounds - size += header->num_vertexes * 3 * sizeof(float); // positions - size += header->num_vertexes * 2 * sizeof(float); // texcoords - size += header->num_vertexes * 3 * sizeof(float); // normals - size += header->num_vertexes * 4 * sizeof(float); // tangents - size += header->num_vertexes * 4 * sizeof(byte); // blendIndexes - size += header->num_vertexes * 4 * sizeof(byte); // blendWeights - size += header->num_vertexes * 4 * sizeof(byte); // colors - size += header->num_joints * sizeof(int); // parents - size += header->num_triangles * 3 * sizeof(int); // triangles - size += joint_names; // joint names - - mod->type = MOD_IQM; - iqmData = (iqmData_t *)ri.Hunk_Alloc( size, h_low ); - mod->modelData = iqmData; - - // fill header - iqmData->num_vertexes = header->num_vertexes; - iqmData->num_triangles = header->num_triangles; - iqmData->num_frames = header->num_frames; - iqmData->num_surfaces = header->num_meshes; - iqmData->num_joints = header->num_joints; - iqmData->surfaces = (srfIQModel_t *)(iqmData + 1); - iqmData->poseMats = (float *) (iqmData->surfaces + iqmData->num_surfaces); - if(header->ofs_bounds) - { - iqmData->bounds = iqmData->poseMats + 12 * header->num_joints * header->num_frames; - iqmData->positions = iqmData->bounds + 6 * header->num_frames; - } - else - iqmData->positions = iqmData->poseMats + 12 * header->num_joints * header->num_frames; - iqmData->texcoords = iqmData->positions + 3 * header->num_vertexes; - iqmData->normals = iqmData->texcoords + 2 * header->num_vertexes; - iqmData->tangents = iqmData->normals + 3 * header->num_vertexes; - iqmData->blendIndexes = (byte *)(iqmData->tangents + 4 * header->num_vertexes); - iqmData->blendWeights = iqmData->blendIndexes + 4 * header->num_vertexes; - iqmData->colors = iqmData->blendWeights + 4 * header->num_vertexes; - iqmData->jointParents = (int *)(iqmData->colors + 4 * header->num_vertexes); - iqmData->triangles = iqmData->jointParents + header->num_joints; - iqmData->names = (char *)(iqmData->triangles + 3 * header->num_triangles); - - // calculate joint matrices and their inverses - // they are needed only until the pose matrices are calculated - mat = jointMats; - joint = (iqmJoint_t *)((byte *)header + header->ofs_joints); - for( i = 0; i < header->num_joints; i++, joint++ ) { - float baseFrame[12], invBaseFrame[12]; - - JointToMatrix( joint->rotate, joint->scale, joint->translate, baseFrame ); - Matrix34Invert( baseFrame, invBaseFrame ); - - if ( joint->parent >= 0 ) - { - Matrix34Multiply( jointMats + 2 * 12 * joint->parent, baseFrame, mat ); - mat += 12; - Matrix34Multiply( invBaseFrame, jointMats + 2 * 12 * joint->parent + 12, mat ); - mat += 12; - } - else - { - Com_Memcpy( mat, baseFrame, sizeof(baseFrame) ); - mat += 12; - Com_Memcpy( mat, invBaseFrame, sizeof(invBaseFrame) ); - mat += 12; - } - } - - // calculate pose matrices - framedata = (unsigned short *)((byte *)header + header->ofs_frames); - mat = iqmData->poseMats; - for( i = 0; i < header->num_frames; i++ ) { - pose = (iqmPose_t *)((byte *)header + header->ofs_poses); - for( j = 0; j < header->num_poses; j++, pose++ ) { - vec3_t translate; - vec4_t rotate; - vec3_t scale; - float mat1[12], mat2[12]; - - translate[0] = pose->channeloffset[0]; - if( pose->mask & 0x001) - translate[0] += *framedata++ * pose->channelscale[0]; - translate[1] = pose->channeloffset[1]; - if( pose->mask & 0x002) - translate[1] += *framedata++ * pose->channelscale[1]; - translate[2] = pose->channeloffset[2]; - if( pose->mask & 0x004) - translate[2] += *framedata++ * pose->channelscale[2]; - - rotate[0] = pose->channeloffset[3]; - if( pose->mask & 0x008) - rotate[0] += *framedata++ * pose->channelscale[3]; - rotate[1] = pose->channeloffset[4]; - if( pose->mask & 0x010) - rotate[1] += *framedata++ * pose->channelscale[4]; - rotate[2] = pose->channeloffset[5]; - if( pose->mask & 0x020) - rotate[2] += *framedata++ * pose->channelscale[5]; - rotate[3] = pose->channeloffset[6]; - if( pose->mask & 0x040) - rotate[3] += *framedata++ * pose->channelscale[6]; - - scale[0] = pose->channeloffset[7]; - if( pose->mask & 0x080) - scale[0] += *framedata++ * pose->channelscale[7]; - scale[1] = pose->channeloffset[8]; - if( pose->mask & 0x100) - scale[1] += *framedata++ * pose->channelscale[8]; - scale[2] = pose->channeloffset[9]; - if( pose->mask & 0x200) - scale[2] += *framedata++ * pose->channelscale[9]; - - // construct transformation matrix - JointToMatrix( rotate, scale, translate, mat1 ); - - if( pose->parent >= 0 ) { - Matrix34Multiply( jointMats + 12 * 2 * pose->parent, - mat1, mat2 ); - } else { - Com_Memcpy( mat2, mat1, sizeof(mat1) ); - } - - Matrix34Multiply( mat2, jointMats + 12 * (2 * j + 1), mat ); - mat += 12; - } - } - - // register shaders - // overwrite the material offset with the shader index - mesh = (iqmMesh_t *)((byte *)header + header->ofs_meshes); - surface = iqmData->surfaces; - str = (char *)header + header->ofs_text; - for( i = 0; i < header->num_meshes; i++, mesh++, surface++ ) { - surface->surfaceType = SF_IQM; - Q_strncpyz(surface->name, str + mesh->name, sizeof (surface->name)); - Q_strlwr(surface->name); // lowercase the surface name so skin compares are faster - surface->shader = R_FindShader( str + mesh->material, LIGHTMAP_NONE, qtrue ); - if( surface->shader->defaultShader ) - surface->shader = tr.defaultShader; - surface->data = iqmData; - surface->first_vertex = mesh->first_vertex; - surface->num_vertexes = mesh->num_vertexes; - surface->first_triangle = mesh->first_triangle; - surface->num_triangles = mesh->num_triangles; - } - - // copy vertexarrays and indexes - vertexarray = (iqmVertexArray_t *)((byte *)header + header->ofs_vertexarrays); - for( i = 0; i < header->num_vertexarrays; i++, vertexarray++ ) { - int n; - - // total number of values - n = header->num_vertexes * vertexarray->size; - - switch( vertexarray->type ) { - case IQM_POSITION: - Com_Memcpy( iqmData->positions, - (byte *)header + vertexarray->offset, - n * sizeof(float) ); - break; - case IQM_NORMAL: - Com_Memcpy( iqmData->normals, - (byte *)header + vertexarray->offset, - n * sizeof(float) ); - break; - case IQM_TANGENT: - Com_Memcpy( iqmData->tangents, - (byte *)header + vertexarray->offset, - n * sizeof(float) ); - break; - case IQM_TEXCOORD: - Com_Memcpy( iqmData->texcoords, - (byte *)header + vertexarray->offset, - n * sizeof(float) ); - break; - case IQM_BLENDINDEXES: - Com_Memcpy( iqmData->blendIndexes, - (byte *)header + vertexarray->offset, - n * sizeof(byte) ); - break; - case IQM_BLENDWEIGHTS: - Com_Memcpy( iqmData->blendWeights, - (byte *)header + vertexarray->offset, - n * sizeof(byte) ); - break; - case IQM_COLOR: - Com_Memcpy( iqmData->colors, - (byte *)header + vertexarray->offset, - n * sizeof(byte) ); - break; - } - } - - // copy joint parents - joint = (iqmJoint_t *)((byte *)header + header->ofs_joints); - for( i = 0; i < header->num_joints; i++, joint++ ) { - iqmData->jointParents[i] = joint->parent; - } - - // copy triangles - triangle = (iqmTriangle_t *)((byte *)header + header->ofs_triangles); - for( i = 0; i < header->num_triangles; i++, triangle++ ) { - iqmData->triangles[3*i+0] = triangle->vertex[0]; - iqmData->triangles[3*i+1] = triangle->vertex[1]; - iqmData->triangles[3*i+2] = triangle->vertex[2]; - } - - // copy joint names - str = iqmData->names; - joint = (iqmJoint_t *)((byte *)header + header->ofs_joints); - for( i = 0; i < header->num_joints; i++, joint++ ) { - char *name = (char *)header + header->ofs_text + - joint->name; - int len = strlen( name ) + 1; - Com_Memcpy( str, name, len ); - str += len; - } - - // copy model bounds - if(header->ofs_bounds) - { - mat = iqmData->bounds; - bounds = (iqmBounds_t *) ((byte *) header + header->ofs_bounds); - for(i = 0; i < header->num_frames; i++) - { - mat[0] = bounds->bbmin[0]; - mat[1] = bounds->bbmin[1]; - mat[2] = bounds->bbmin[2]; - mat[3] = bounds->bbmax[0]; - mat[4] = bounds->bbmax[1]; - mat[5] = bounds->bbmax[2]; - - mat += 6; - bounds++; - } - } - - return qtrue; -} - -/* -============= -R_CullIQM -============= -*/ -static int R_CullIQM( iqmData_t *data, trRefEntity_t *ent ) { - vec3_t bounds[2]; - vec_t *oldBounds, *newBounds; - int i; - - if (!data->bounds) { - tr.pc.c_box_cull_md3_clip++; - return CULL_CLIP; - } - - // compute bounds pointers - oldBounds = data->bounds + 6*ent->e.oldframe; - newBounds = data->bounds + 6*ent->e.frame; - - // calculate a bounding box in the current coordinate system - for (i = 0 ; i < 3 ; i++) { - bounds[0][i] = oldBounds[i] < newBounds[i] ? oldBounds[i] : newBounds[i]; - bounds[1][i] = oldBounds[i+3] > newBounds[i+3] ? oldBounds[i+3] : newBounds[i+3]; - } - - switch ( R_CullLocalBox( bounds ) ) - { - case CULL_IN: - tr.pc.c_box_cull_md3_in++; - return CULL_IN; - case CULL_CLIP: - tr.pc.c_box_cull_md3_clip++; - return CULL_CLIP; - case CULL_OUT: - default: - tr.pc.c_box_cull_md3_out++; - return CULL_OUT; - } -} - -/* -================= -R_ComputeIQMFogNum - -================= -*/ -int R_ComputeIQMFogNum( iqmData_t *data, trRefEntity_t *ent ) { - int i, j; - fog_t *fog; - const vec_t *bounds; - const vec_t defaultBounds[6] = { -8, -8, -8, 8, 8, 8 }; - vec3_t diag, center; - vec3_t localOrigin; - vec_t radius; - - if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { - return 0; - } - - // FIXME: non-normalized axis issues - if (data->bounds) { - bounds = data->bounds + 6*ent->e.frame; - } else { - bounds = defaultBounds; - } - VectorSubtract( bounds+3, bounds, diag ); - VectorMA( bounds, 0.5f, diag, center ); - VectorAdd( ent->e.origin, center, localOrigin ); - radius = 0.5f * VectorLength( diag ); - - for ( i = 1 ; i < tr.world->numfogs ; i++ ) { - fog = &tr.world->fogs[i]; - for ( j = 0 ; j < 3 ; j++ ) { - if ( localOrigin[j] - radius >= fog->bounds[1][j] ) { - break; - } - if ( localOrigin[j] + radius <= fog->bounds[0][j] ) { - break; - } - } - if ( j == 3 ) { - return i; - } - } - - return 0; -} - -/* -================= -R_AddIQMSurfaces - -Add all surfaces of this model -================= -*/ -void R_AddIQMSurfaces( trRefEntity_t *ent ) { - iqmData_t *data; - srfIQModel_t *surface; - int i, j; - qboolean personalModel; - int cull; - int fogNum; - shader_t *shader; - skin_t *skin; - - data = tr.currentModel->modelData; - surface = data->surfaces; - - // don't add third_person objects if not in a portal - personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal; - - if ( ent->e.renderfx & RF_WRAP_FRAMES ) { - ent->e.frame %= data->num_frames; - ent->e.oldframe %= data->num_frames; - } - - // - // Validate the frames so there is no chance of a crash. - // This will write directly into the entity structure, so - // when the surfaces are rendered, they don't need to be - // range checked again. - // - if ( (ent->e.frame >= data->num_frames) - || (ent->e.frame < 0) - || (ent->e.oldframe >= data->num_frames) - || (ent->e.oldframe < 0) ) { - ri.Printf( PRINT_DEVELOPER, "R_AddIQMSurfaces: no such frame %d to %d for '%s'\n", - ent->e.oldframe, ent->e.frame, - tr.currentModel->name ); - ent->e.frame = 0; - ent->e.oldframe = 0; - } - - // - // cull the entire model if merged bounding box of both frames - // is outside the view frustum. - // - cull = R_CullIQM ( data, ent ); - if ( cull == CULL_OUT ) { - return; - } - - // - // set up lighting now that we know we aren't culled - // - if ( !personalModel || r_shadows->integer > 1 ) { - R_SetupEntityLighting( &tr.refdef, ent ); - } - - // - // see if we are in a fog volume - // - fogNum = R_ComputeIQMFogNum( data, ent ); - - for ( i = 0 ; i < data->num_surfaces ; i++ ) { - if(ent->e.customShader) - shader = R_GetShaderByHandle( ent->e.customShader ); - else if(ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins) - { - skin = R_GetSkinByHandle(ent->e.customSkin); - shader = tr.defaultShader; - - for(j = 0; j < skin->numSurfaces; j++) - { - if (!strcmp(skin->surfaces[j]->name, surface->name)) - { - shader = skin->surfaces[j]->shader; - break; - } - } - } else { - shader = surface->shader; - } - - // we will add shadows even if the main object isn't visible in the view - - // stencil shadows can't do personal models unless I polyhedron clip - if ( !personalModel - && r_shadows->integer == 2 - && fogNum == 0 - && !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) - && shader->sort == SS_OPAQUE ) { - R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, 0, 0 ); - } - - // projection shadows work fine with personal models - if ( r_shadows->integer == 3 - && fogNum == 0 - && (ent->e.renderfx & RF_SHADOW_PLANE ) - && shader->sort == SS_OPAQUE ) { - R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, 0, 0 ); - } - - if( !personalModel ) { - R_AddDrawSurf( (void *)surface, shader, fogNum, 0, 0 ); - } - - surface++; - } -} - - -static void ComputeJointMats( iqmData_t *data, int frame, int oldframe, - float backlerp, float *mat ) { - float *mat1, *mat2; - int *joint = data->jointParents; - int i; - - if ( oldframe == frame ) { - mat1 = data->poseMats + 12 * data->num_joints * frame; - for( i = 0; i < data->num_joints; i++, joint++ ) { - if( *joint >= 0 ) { - Matrix34Multiply( mat + 12 * *joint, - mat1 + 12*i, mat + 12*i ); - } else { - Com_Memcpy( mat + 12*i, mat1 + 12*i, 12 * sizeof(float) ); - } - } - } else { - mat1 = data->poseMats + 12 * data->num_joints * frame; - mat2 = data->poseMats + 12 * data->num_joints * oldframe; - - for( i = 0; i < data->num_joints; i++, joint++ ) { - if( *joint >= 0 ) { - float tmpMat[12]; - InterpolateMatrix( mat1 + 12*i, mat2 + 12*i, - backlerp, tmpMat ); - Matrix34Multiply( mat + 12 * *joint, - tmpMat, mat + 12*i ); - - } else { - InterpolateMatrix( mat1 + 12*i, mat2 + 12*i, - backlerp, mat ); - } - } - } -} - - -/* -================= -RB_AddIQMSurfaces - -Compute vertices for this model surface -================= -*/ -void RB_IQMSurfaceAnim( surfaceType_t *surface ) { - srfIQModel_t *surf = (srfIQModel_t *)surface; - iqmData_t *data = surf->data; - float jointMats[IQM_MAX_JOINTS * 12]; - int i; - - vec4_t *outXYZ = &tess.xyz[tess.numVertexes]; - vec4_t *outNormal = &tess.normal[tess.numVertexes]; - vec2_t (*outTexCoord)[2] = &tess.texCoords[tess.numVertexes]; - vec4_t *outColor = &tess.vertexColors[tess.numVertexes]; - - int frame = backEnd.currentEntity->e.frame % data->num_frames; - int oldframe = backEnd.currentEntity->e.oldframe % data->num_frames; - float backlerp = backEnd.currentEntity->e.backlerp; - - int *tri; - glIndex_t *ptr; - glIndex_t base; - - RB_CHECKOVERFLOW( surf->num_vertexes, surf->num_triangles * 3 ); - - // compute interpolated joint matrices - ComputeJointMats( data, frame, oldframe, backlerp, jointMats ); - - // transform vertexes and fill other data - for( i = 0; i < surf->num_vertexes; - i++, outXYZ++, outNormal++, outTexCoord++, outColor++ ) { - int j, k; - float vtxMat[12]; - float nrmMat[9]; - int vtx = i + surf->first_vertex; - - // compute the vertex matrix by blending the up to - // four blend weights - for( k = 0; k < 12; k++ ) - vtxMat[k] = data->blendWeights[4*vtx] - * jointMats[12*data->blendIndexes[4*vtx] + k]; - for( j = 1; j < 4; j++ ) { - if( data->blendWeights[4*vtx + j] <= 0 ) - break; - for( k = 0; k < 12; k++ ) - vtxMat[k] += data->blendWeights[4*vtx + j] - * jointMats[12*data->blendIndexes[4*vtx + j] + k]; - } - for( k = 0; k < 12; k++ ) - vtxMat[k] *= 1.0f / 255.0f; - - // compute the normal matrix as transpose of the adjoint - // of the vertex matrix - nrmMat[ 0] = vtxMat[ 5]*vtxMat[10] - vtxMat[ 6]*vtxMat[ 9]; - nrmMat[ 1] = vtxMat[ 6]*vtxMat[ 8] - vtxMat[ 4]*vtxMat[10]; - nrmMat[ 2] = vtxMat[ 4]*vtxMat[ 9] - vtxMat[ 5]*vtxMat[ 8]; - nrmMat[ 3] = vtxMat[ 2]*vtxMat[ 9] - vtxMat[ 1]*vtxMat[10]; - nrmMat[ 4] = vtxMat[ 0]*vtxMat[10] - vtxMat[ 2]*vtxMat[ 8]; - nrmMat[ 5] = vtxMat[ 1]*vtxMat[ 8] - vtxMat[ 0]*vtxMat[ 9]; - nrmMat[ 6] = vtxMat[ 1]*vtxMat[ 6] - vtxMat[ 2]*vtxMat[ 5]; - nrmMat[ 7] = vtxMat[ 2]*vtxMat[ 4] - vtxMat[ 0]*vtxMat[ 6]; - nrmMat[ 8] = vtxMat[ 0]*vtxMat[ 5] - vtxMat[ 1]*vtxMat[ 4]; - - (*outTexCoord)[0][0] = data->texcoords[2*vtx + 0]; - (*outTexCoord)[0][1] = data->texcoords[2*vtx + 1]; - (*outTexCoord)[1][0] = (*outTexCoord)[0][0]; - (*outTexCoord)[1][1] = (*outTexCoord)[0][1]; - - (*outXYZ)[0] = - vtxMat[ 0] * data->positions[3*vtx+0] + - vtxMat[ 1] * data->positions[3*vtx+1] + - vtxMat[ 2] * data->positions[3*vtx+2] + - vtxMat[ 3]; - (*outXYZ)[1] = - vtxMat[ 4] * data->positions[3*vtx+0] + - vtxMat[ 5] * data->positions[3*vtx+1] + - vtxMat[ 6] * data->positions[3*vtx+2] + - vtxMat[ 7]; - (*outXYZ)[2] = - vtxMat[ 8] * data->positions[3*vtx+0] + - vtxMat[ 9] * data->positions[3*vtx+1] + - vtxMat[10] * data->positions[3*vtx+2] + - vtxMat[11]; - (*outXYZ)[3] = 1.0f; - - (*outNormal)[0] = - nrmMat[ 0] * data->normals[3*vtx+0] + - nrmMat[ 1] * data->normals[3*vtx+1] + - nrmMat[ 2] * data->normals[3*vtx+2]; - (*outNormal)[1] = - nrmMat[ 3] * data->normals[3*vtx+0] + - nrmMat[ 4] * data->normals[3*vtx+1] + - nrmMat[ 5] * data->normals[3*vtx+2]; - (*outNormal)[2] = - nrmMat[ 6] * data->normals[3*vtx+0] + - nrmMat[ 7] * data->normals[3*vtx+1] + - nrmMat[ 8] * data->normals[3*vtx+2]; - (*outNormal)[3] = 0.0f; - - (*outColor)[0] = data->colors[4*vtx+0] / 255.0f; - (*outColor)[1] = data->colors[4*vtx+1] / 255.0f; - (*outColor)[2] = data->colors[4*vtx+2] / 255.0f; - (*outColor)[3] = data->colors[4*vtx+3] / 255.0f; - } - - tri = data->triangles + 3 * surf->first_triangle; - ptr = &tess.indexes[tess.numIndexes]; - base = tess.numVertexes; - - for( i = 0; i < surf->num_triangles; i++ ) { - *ptr++ = base + (*tri++ - surf->first_vertex); - *ptr++ = base + (*tri++ - surf->first_vertex); - *ptr++ = base + (*tri++ - surf->first_vertex); - } - - tess.numIndexes += 3 * surf->num_triangles; - tess.numVertexes += surf->num_vertexes; -} - -int R_IQMLerpTag( orientation_t *tag, iqmData_t *data, - int startFrame, int endFrame, - float frac, const char *tagName ) { - float jointMats[IQM_MAX_JOINTS * 12]; - int joint; - char *names = data->names; - - // get joint number by reading the joint names - for( joint = 0; joint < data->num_joints; joint++ ) { - if( !strcmp( tagName, names ) ) - break; - names += strlen( names ) + 1; - } - if( joint >= data->num_joints ) { - AxisClear( tag->axis ); - VectorClear( tag->origin ); - return qfalse; - } - - ComputeJointMats( data, startFrame, endFrame, frac, jointMats ); - - tag->axis[0][0] = jointMats[12 * joint + 0]; - tag->axis[1][0] = jointMats[12 * joint + 1]; - tag->axis[2][0] = jointMats[12 * joint + 2]; - tag->origin[0] = jointMats[12 * joint + 3]; - tag->axis[0][1] = jointMats[12 * joint + 4]; - tag->axis[1][1] = jointMats[12 * joint + 5]; - tag->axis[2][1] = jointMats[12 * joint + 6]; - tag->origin[1] = jointMats[12 * joint + 7]; - tag->axis[0][2] = jointMats[12 * joint + 8]; - tag->axis[1][2] = jointMats[12 * joint + 9]; - tag->axis[2][2] = jointMats[12 * joint + 10]; - tag->origin[2] = jointMats[12 * joint + 11]; - - return qtrue; -} diff --git a/src/rend2/tr_noise.c b/src/rend2/tr_noise.c deleted file mode 100644 index 40eafc33..00000000 --- a/src/rend2/tr_noise.c +++ /dev/null @@ -1,93 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_noise.c -#include "../qcommon/q_shared.h" -#include "../qcommon/qfiles.h" -#include "../qcommon/qcommon.h" - -#define NOISE_SIZE 256 -#define NOISE_MASK ( NOISE_SIZE - 1 ) - -#define VAL( a ) s_noise_perm[ ( a ) & ( NOISE_MASK )] -#define INDEX( x, y, z, t ) VAL( x + VAL( y + VAL( z + VAL( t ) ) ) ) - -static float s_noise_table[NOISE_SIZE]; -static int s_noise_perm[NOISE_SIZE]; - -static float GetNoiseValue( int x, int y, int z, int t ) -{ - int index = INDEX( ( int ) x, ( int ) y, ( int ) z, ( int ) t ); - - return s_noise_table[index]; -} - -void R_NoiseInit( void ) -{ - int i; - - for ( i = 0; i < NOISE_SIZE; i++ ) - { - s_noise_table[i] = ( float ) ( ( ( rand() / ( float ) RAND_MAX ) * 2.0 - 1.0 ) ); - s_noise_perm[i] = ( unsigned char ) ( rand() / ( float ) RAND_MAX * 255 ); - } -} - -float R_NoiseGet4f( float x, float y, float z, float t ) -{ - int i; - int ix, iy, iz, it; - float fx, fy, fz, ft; - float front[4]; - float back[4]; - float fvalue, bvalue, value[2], finalvalue; - - ix = ( int ) floor( x ); - fx = x - ix; - iy = ( int ) floor( y ); - fy = y - iy; - iz = ( int ) floor( z ); - fz = z - iz; - it = ( int ) floor( t ); - ft = t - it; - - for ( i = 0; i < 2; i++ ) - { - front[0] = GetNoiseValue( ix, iy, iz, it + i ); - front[1] = GetNoiseValue( ix+1, iy, iz, it + i ); - front[2] = GetNoiseValue( ix, iy+1, iz, it + i ); - front[3] = GetNoiseValue( ix+1, iy+1, iz, it + i ); - - back[0] = GetNoiseValue( ix, iy, iz + 1, it + i ); - back[1] = GetNoiseValue( ix+1, iy, iz + 1, it + i ); - back[2] = GetNoiseValue( ix, iy+1, iz + 1, it + i ); - back[3] = GetNoiseValue( ix+1, iy+1, iz + 1, it + i ); - - fvalue = LERP( LERP( front[0], front[1], fx ), LERP( front[2], front[3], fx ), fy ); - bvalue = LERP( LERP( back[0], back[1], fx ), LERP( back[2], back[3], fx ), fy ); - - value[i] = LERP( fvalue, bvalue, fz ); - } - - finalvalue = LERP( value[0], value[1], ft ); - - return finalvalue; -} diff --git a/src/rend2/tr_postprocess.c b/src/rend2/tr_postprocess.c deleted file mode 100644 index a15c9b7a..00000000 --- a/src/rend2/tr_postprocess.c +++ /dev/null @@ -1,488 +0,0 @@ -/* -=========================================================================== -Copyright (C) 2011 Andrei Drexler, Richard Allen, James Canete - -This file is part of Reaction source code. - -Reaction source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Reaction source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Reaction source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "tr_local.h" - -void RB_ToneMap(FBO_t *hdrFbo, int autoExposure) -{ - vec4i_t srcBox, dstBox; - vec4_t color; - static int lastFrameCount = 0; - - if (autoExposure) - { - if (lastFrameCount == 0 || tr.frameCount < lastFrameCount || tr.frameCount - lastFrameCount > 5) - { - // determine average log luminance - FBO_t *srcFbo, *dstFbo, *tmp; - int size = 256; - - lastFrameCount = tr.frameCount; - - VectorSet4(dstBox, 0, 0, size, size); - - srcFbo = hdrFbo; - dstFbo = tr.textureScratchFbo[0]; - FBO_Blit(srcFbo, NULL, NULL, dstFbo, dstBox, &tr.calclevels4xShader[0], NULL, 0); - - srcFbo = tr.textureScratchFbo[0]; - dstFbo = tr.textureScratchFbo[1]; - - // downscale to 1x1 texture - while (size > 1) - { - VectorSet4(srcBox, 0, 0, size, size); - //size >>= 2; - size >>= 1; - VectorSet4(dstBox, 0, 0, size, size); - - if (size == 1) - dstFbo = tr.targetLevelsFbo; - - //FBO_Blit(targetFbo, srcBox, NULL, tr.textureScratchFbo[nextScratch], dstBox, &tr.calclevels4xShader[1], NULL, 0); - FBO_FastBlit(srcFbo, srcBox, dstFbo, dstBox, GL_COLOR_BUFFER_BIT, GL_LINEAR); - - tmp = srcFbo; - srcFbo = dstFbo; - dstFbo = tmp; - } - } - - // blend with old log luminance for gradual change - VectorSet4(srcBox, 0, 0, 0, 0); - - color[0] = - color[1] = - color[2] = 1.0f; - if (glRefConfig.textureFloat) - color[3] = 0.03f; - else - color[3] = 0.1f; - - FBO_Blit(tr.targetLevelsFbo, srcBox, NULL, tr.calcLevelsFbo, NULL, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); - } - - // tonemap - color[0] = - color[1] = - color[2] = pow(2, r_cameraExposure->value); //exp2(r_cameraExposure->value); - color[3] = 1.0f; - - if (autoExposure) - GL_BindToTMU(tr.calcLevelsImage, TB_LEVELSMAP); - else - GL_BindToTMU(tr.fixedLevelsImage, TB_LEVELSMAP); - - FBO_Blit(hdrFbo, NULL, NULL, tr.screenScratchFbo, NULL, &tr.tonemapShader, color, 0); -} - - -void RB_BokehBlur(float blur) -{ -// vec4i_t srcBox, dstBox; - vec4_t color; - - blur *= 10.0f; - - if (blur < 0.004f) - return; - - if (glRefConfig.framebufferObject) - { - // bokeh blur - if (blur > 0.0f) - { - // create a quarter texture - //FBO_Blit(NULL, NULL, NULL, tr.quarterFbo[0], NULL, NULL, NULL, 0); - FBO_FastBlit(tr.screenScratchFbo, NULL, tr.quarterFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR); - } - -#ifndef HQ_BLUR - if (blur > 1.0f) - { - // create a 1/16th texture - //FBO_Blit(tr.quarterFbo[0], NULL, NULL, tr.textureScratchFbo[0], NULL, NULL, NULL, 0); - FBO_FastBlit(tr.quarterFbo[0], NULL, tr.textureScratchFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR); - } -#endif - - if (blur > 0.0f && blur <= 1.0f) - { - // Crossfade original with quarter texture - VectorSet4(color, 1, 1, 1, blur); - - FBO_Blit(tr.quarterFbo[0], NULL, NULL, tr.screenScratchFbo, NULL, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); - } -#ifndef HQ_BLUR - // ok blur, but can see some pixelization - else if (blur > 1.0f && blur <= 2.0f) - { - // crossfade quarter texture with 1/16th texture - FBO_Blit(tr.quarterFbo[0], NULL, NULL, tr.screenScratchFbo, NULL, NULL, NULL, 0); - - VectorSet4(color, 1, 1, 1, blur - 1.0f); - - FBO_Blit(tr.textureScratchFbo[0], NULL, NULL, tr.screenScratchFbo, NULL, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); - } - else if (blur > 2.0f) - { - // blur 1/16th texture then replace - int i; - - for (i = 0; i < 2; i++) - { - vec2_t blurTexScale; - float subblur; - - subblur = ((blur - 2.0f) / 2.0f) / 3.0f * (float)(i + 1); - - blurTexScale[0] = - blurTexScale[1] = subblur; - - color[0] = - color[1] = - color[2] = 0.5f; - color[3] = 1.0f; - - if (i != 0) - FBO_Blit(tr.textureScratchFbo[0], NULL, blurTexScale, tr.textureScratchFbo[1], NULL, &tr.bokehShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE); - else - FBO_Blit(tr.textureScratchFbo[0], NULL, blurTexScale, tr.textureScratchFbo[1], NULL, &tr.bokehShader, color, 0); - } - - FBO_Blit(tr.textureScratchFbo[1], NULL, NULL, tr.screenScratchFbo, NULL, &tr.textureColorShader, NULL, 0); - } -#else // higher quality blur, but slower - else if (blur > 1.0f) - { - // blur quarter texture then replace - int i; - - src = tr.quarterFbo[0]; - dst = tr.quarterFbo[1]; - - VectorSet4(color, 0.5f, 0.5f, 0.5f, 1); - - for (i = 0; i < 2; i++) - { - vec2_t blurTexScale; - float subblur; - - subblur = (blur - 1.0f) / 2.0f * (float)(i + 1); - - blurTexScale[0] = - blurTexScale[1] = subblur; - - color[0] = - color[1] = - color[2] = 1.0f; - if (i != 0) - color[3] = 1.0f; - else - color[3] = 0.5f; - - FBO_Blit(tr.quarterFbo[0], NULL, blurTexScale, tr.quarterFbo[1], NULL, &tr.bokehShader, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); - } - - FBO_Blit(tr.quarterFbo[1], NULL, NULL, tr.screenScratchFbo, NULL, &tr.textureColorShader, NULL, 0); - } -#endif - } -} - - -static void RB_RadialBlur(FBO_t *srcFbo, FBO_t *dstFbo, int passes, float stretch, float x, float y, float w, float h, float xcenter, float ycenter, float alpha) -{ - vec4i_t srcBox, dstBox; - vec4_t color; - const float inc = 1.f / passes; - const float mul = powf(stretch, inc); - float scale; - - { - vec2_t texScale; - - texScale[0] = - texScale[1] = 1.0f; - - alpha *= inc; - VectorSet4(color, alpha, alpha, alpha, 1.0f); - - VectorSet4(srcBox, 0, 0, srcFbo->width, srcFbo->height); - VectorSet4(dstBox, x, y, w, h); - FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, 0); - - --passes; - scale = mul; - while (passes > 0) - { - float iscale = 1.f / scale; - float s0 = xcenter * (1.f - iscale); - float t0 = (1.0f - ycenter) * (1.f - iscale); - float s1 = iscale + s0; - float t1 = iscale + t0; - - srcBox[0] = s0 * srcFbo->width; - srcBox[1] = t0 * srcFbo->height; - srcBox[2] = (s1 - s0) * srcFbo->width; - srcBox[3] = (t1 - t0) * srcFbo->height; - - FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); - - scale *= mul; - --passes; - } - } -} - - -static qboolean RB_UpdateSunFlareVis(void) -{ - GLuint sampleCount = 0; - if (!glRefConfig.occlusionQuery) - return qtrue; - - tr.sunFlareQueryIndex ^= 1; - if (!tr.sunFlareQueryActive[tr.sunFlareQueryIndex]) - return qtrue; - - /* debug code */ - if (0) - { - int iter; - for (iter=0 ; ; ++iter) - { - GLint available = 0; - qglGetQueryObjectivARB(tr.sunFlareQuery[tr.sunFlareQueryIndex], GL_QUERY_RESULT_AVAILABLE_ARB, &available); - if (available) - break; - } - - ri.Printf(PRINT_DEVELOPER, "Waited %d iterations\n", iter); - } - - qglGetQueryObjectuivARB(tr.sunFlareQuery[tr.sunFlareQueryIndex], GL_QUERY_RESULT_ARB, &sampleCount); - return sampleCount > 0; -} - -void RB_SunRays(void) -{ - vec4i_t srcBox, dstBox; - vec4_t color; - float dot; - const float cutoff = 0.25f; - qboolean colorize = qtrue; - -// float w, h, w2, h2; - matrix_t mvp; - vec4_t pos, hpos; - - dot = DotProduct(tr.sunDirection, backEnd.viewParms.or.axis[0]); - if (dot < cutoff) - return; - - if (!RB_UpdateSunFlareVis()) - return; - - // From RB_DrawSun() - { - float dist; - matrix_t trans, model, mvp; - - Matrix16Translation( backEnd.viewParms.or.origin, trans ); - Matrix16Multiply( backEnd.viewParms.world.modelMatrix, trans, model ); - Matrix16Multiply(backEnd.viewParms.projectionMatrix, model, mvp); - - dist = backEnd.viewParms.zFar / 1.75; // div sqrt(3) - - VectorScale( tr.sunDirection, dist, pos ); - } - - // project sun point - //Matrix16Multiply(backEnd.viewParms.projectionMatrix, backEnd.viewParms.world.modelMatrix, mvp); - Matrix16Transform(mvp, pos, hpos); - - // transform to UV coords - hpos[3] = 0.5f / hpos[3]; - - pos[0] = 0.5f + hpos[0] * hpos[3]; - pos[1] = 0.5f - hpos[1] * hpos[3]; - - // viewport dimensions - // JBravo: Apparently not used -/* w = glConfig.vidWidth; - h = glConfig.vidHeight; - w2 = glConfig.vidWidth / 2; - h2 = glConfig.vidHeight / 2; */ - - // initialize quarter buffers - { - float mul = 1.f; - vec2_t texScale; - - texScale[0] = - texScale[1] = 1.0f; - - VectorSet4(color, mul, mul, mul, 1); - - // first, downsample the framebuffer - if (colorize) - { - FBO_FastBlit(tr.screenScratchFbo, NULL, tr.quarterFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR); - FBO_Blit(tr.sunRaysFbo, NULL, NULL, tr.quarterFbo[0], NULL, NULL, color, GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO); - } - else - { - FBO_FastBlit(tr.sunRaysFbo, NULL, tr.quarterFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR); - } - } - - // radial blur passes, ping-ponging between the two quarter-size buffers - { - const float stretch_add = 2.f/3.f; - float stretch = 1.f + stretch_add; - int i; - for (i=0; i<2; ++i) - { - RB_RadialBlur(tr.quarterFbo[i&1], tr.quarterFbo[(~i) & 1], 5, stretch, 0.f, 0.f, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height, pos[0], pos[1], 1.125f); - stretch += stretch_add; - } - } - - // add result back on top of the main buffer - { - float mul = 1.f; - vec2_t texScale; - - texScale[0] = - texScale[1] = 1.0f; - - VectorSet4(color, mul, mul, mul, 1); - - VectorSet4(srcBox, 0, 0, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height); - VectorSet4(dstBox, 0, 0, glConfig.vidWidth, glConfig.vidHeight); - FBO_Blit(tr.quarterFbo[0], srcBox, texScale, tr.screenScratchFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE); - } -} - -static void RB_BlurAxis(FBO_t *srcFbo, FBO_t *dstFbo, float strength, qboolean horizontal) -{ - float dx, dy; - float xmul, ymul; - float weights[3] = { - 0.227027027f, - 0.316216216f, - 0.070270270f, - }; - float offsets[3] = { - 0.f, - 1.3846153846f, - 3.2307692308f, - }; - - xmul = horizontal; - ymul = 1.f - xmul; - - xmul *= strength; - ymul *= strength; - - { - vec4i_t srcBox, dstBox; - vec4_t color; - vec2_t texScale; - - texScale[0] = - texScale[1] = 1.0f; - - VectorSet4(color, weights[0], weights[0], weights[0], 1.0f); - VectorSet4(srcBox, 0, 0, srcFbo->width, srcFbo->height); - VectorSet4(dstBox, 0, 0, dstFbo->width, dstFbo->height); - FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, 0 ); - - VectorSet4(color, weights[1], weights[1], weights[1], 1.0f); - dx = offsets[1] * xmul; - dy = offsets[1] * ymul; - VectorSet4(srcBox, dx, dy, srcFbo->width, srcFbo->height); - FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); - VectorSet4(srcBox, -dx, -dy, srcFbo->width, srcFbo->height); - FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); - - VectorSet4(color, weights[2], weights[2], weights[2], 1.0f); - dx = offsets[2] * xmul; - dy = offsets[2] * ymul; - VectorSet4(srcBox, dx, dy, srcFbo->width, srcFbo->height); - FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); - VectorSet4(srcBox, -dx, -dy, srcFbo->width, srcFbo->height); - FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); - } -} - -static void RB_HBlur(FBO_t *srcFbo, FBO_t *dstFbo, float strength) -{ - RB_BlurAxis(srcFbo, dstFbo, strength, qtrue); -} - -static void RB_VBlur(FBO_t *srcFbo, FBO_t *dstFbo, float strength) -{ - RB_BlurAxis(srcFbo, dstFbo, strength, qfalse); -} - -void RB_GaussianBlur(float blur) -{ - //float mul = 1.f; - float factor = Com_Clamp(0.f, 1.f, blur); - - if (factor <= 0.f) - return; - - { - vec4i_t srcBox, dstBox; - vec4_t color; - vec2_t texScale; - - texScale[0] = - texScale[1] = 1.0f; - - VectorSet4(color, 1, 1, 1, 1); - - // first, downsample the framebuffer - FBO_FastBlit(tr.screenScratchFbo, NULL, tr.quarterFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR); - FBO_FastBlit(tr.quarterFbo[0], NULL, tr.textureScratchFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR); - - // set the alpha channel - VectorSet4(srcBox, 0, 0, tr.whiteImage->width, tr.whiteImage->height); - VectorSet4(dstBox, 0, 0, tr.textureScratchFbo[0]->width, tr.textureScratchFbo[0]->height); - qglColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); - FBO_BlitFromTexture(tr.whiteImage, srcBox, texScale, tr.textureScratchFbo[0], dstBox, &tr.textureColorShader, color, GLS_DEPTHTEST_DISABLE); - qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - // blur the tiny buffer horizontally and vertically - RB_HBlur(tr.textureScratchFbo[0], tr.textureScratchFbo[1], factor); - RB_VBlur(tr.textureScratchFbo[1], tr.textureScratchFbo[0], factor); - - // finally, merge back to framebuffer - VectorSet4(srcBox, 0, 0, tr.textureScratchFbo[0]->width, tr.textureScratchFbo[0]->height); - VectorSet4(dstBox, 0, 0, glConfig.vidWidth, glConfig.vidHeight); - color[3] = factor; - FBO_Blit(tr.textureScratchFbo[0], srcBox, texScale, tr.screenScratchFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); - } -} diff --git a/src/rend2/tr_postprocess.h b/src/rend2/tr_postprocess.h deleted file mode 100644 index 1e5ebefe..00000000 --- a/src/rend2/tr_postprocess.h +++ /dev/null @@ -1,33 +0,0 @@ -/* -=========================================================================== -Copyright (C) 2011 Andrei Drexler, Richard Allen, James Canete - -This file is part of Reaction source code. - -Reaction source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Reaction source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Reaction source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#ifndef TR_POSTPROCESS_H -#define TR_POSTPROCESS_H - -#include "tr_fbo.h" - -void RB_ToneMap(FBO_t *hdrFbo, int autoExposure); -void RB_BokehBlur(float blur); -void RB_SunRays(void); -void RB_GaussianBlur(float blur); - -#endif diff --git a/src/rend2/tr_scene.c b/src/rend2/tr_scene.c deleted file mode 100644 index 14c18339..00000000 --- a/src/rend2/tr_scene.c +++ /dev/null @@ -1,520 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "tr_local.h" - -int r_firstSceneDrawSurf; - -int r_numdlights; -int r_firstSceneDlight; - -int r_numentities; -int r_firstSceneEntity; - -int r_numpolys; -int r_firstScenePoly; - -int r_numpolyverts; - - -/* -==================== -R_InitNextFrame - -==================== -*/ -void R_InitNextFrame( void ) { - backEndData->commands.used = 0; - - r_firstSceneDrawSurf = 0; - - r_numdlights = 0; - r_firstSceneDlight = 0; - - r_numentities = 0; - r_firstSceneEntity = 0; - - r_numpolys = 0; - r_firstScenePoly = 0; - - r_numpolyverts = 0; -} - - -/* -==================== -RE_ClearScene - -==================== -*/ -void RE_ClearScene( void ) { - r_firstSceneDlight = r_numdlights; - r_firstSceneEntity = r_numentities; - r_firstScenePoly = r_numpolys; -} - -/* -=========================================================================== - -DISCRETE POLYS - -=========================================================================== -*/ - -/* -===================== -R_AddPolygonSurfaces - -Adds all the scene's polys into this view's drawsurf list -===================== -*/ -void R_AddPolygonSurfaces( void ) { - int i; - shader_t *sh; - srfPoly_t *poly; -// JBravo: Fog fixes - int fogMask; - - tr.currentEntityNum = REFENTITYNUM_WORLD; - tr.shiftedEntityNum = tr.currentEntityNum << QSORT_REFENTITYNUM_SHIFT; - fogMask = -((tr.refdef.rdflags & RDF_NOFOG) == 0); - - for ( i = 0, poly = tr.refdef.polys; i < tr.refdef.numPolys ; i++, poly++ ) { - sh = R_GetShaderByHandle( poly->hShader ); - R_AddDrawSurf( ( void * )poly, sh, poly->fogIndex & fogMask, qfalse, qfalse ); - } -} - -/* -===================== -RE_AddPolyToScene - -===================== -*/ -void RE_AddPolyToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ) { - srfPoly_t *poly; - int i, j; - int fogIndex; - fog_t *fog; - vec3_t bounds[2]; - - if ( !tr.registered ) { - return; - } - - if ( !hShader ) { - // This isn't a useful warning, and an hShader of zero isn't a null shader, it's - // the default shader. - //ri.Printf( PRINT_WARNING, "WARNING: RE_AddPolyToScene: NULL poly shader\n"); - //return; - } - - for ( j = 0; j < numPolys; j++ ) { - if ( r_numpolyverts + numVerts > max_polyverts || r_numpolys >= max_polys ) { - /* - NOTE TTimo this was initially a PRINT_WARNING - but it happens a lot with high fighting scenes and particles - since we don't plan on changing the const and making for room for those effects - simply cut this message to developer only - */ - ri.Printf( PRINT_DEVELOPER, "WARNING: RE_AddPolyToScene: r_max_polys or r_max_polyverts reached\n"); - return; - } - - poly = &backEndData->polys[r_numpolys]; - poly->surfaceType = SF_POLY; - poly->hShader = hShader; - poly->numVerts = numVerts; - poly->verts = &backEndData->polyVerts[r_numpolyverts]; - - Com_Memcpy( poly->verts, &verts[numVerts*j], numVerts * sizeof( *verts ) ); - - if ( glConfig.hardwareType == GLHW_RAGEPRO ) { - poly->verts->modulate[0] = 255; - poly->verts->modulate[1] = 255; - poly->verts->modulate[2] = 255; - poly->verts->modulate[3] = 255; - } - // done. - r_numpolys++; - r_numpolyverts += numVerts; - - // if no world is loaded - if ( tr.world == NULL ) { - fogIndex = 0; - } - // see if it is in a fog volume - else if ( tr.world->numfogs == 1 ) { - fogIndex = 0; - } else { - // find which fog volume the poly is in - VectorCopy( poly->verts[0].xyz, bounds[0] ); - VectorCopy( poly->verts[0].xyz, bounds[1] ); - for ( i = 1 ; i < poly->numVerts ; i++ ) { - AddPointToBounds( poly->verts[i].xyz, bounds[0], bounds[1] ); - } - for ( fogIndex = 1 ; fogIndex < tr.world->numfogs ; fogIndex++ ) { - fog = &tr.world->fogs[fogIndex]; - if ( bounds[1][0] >= fog->bounds[0][0] - && bounds[1][1] >= fog->bounds[0][1] - && bounds[1][2] >= fog->bounds[0][2] - && bounds[0][0] <= fog->bounds[1][0] - && bounds[0][1] <= fog->bounds[1][1] - && bounds[0][2] <= fog->bounds[1][2] ) { - break; - } - } - if ( fogIndex == tr.world->numfogs ) { - fogIndex = 0; - } - } - poly->fogIndex = fogIndex; - } -} - - -//================================================================================= - - -/* -===================== -RE_AddRefEntityToScene - -===================== -*/ -void RE_AddRefEntityToScene( const refEntity_t *ent ) { - // JBravo: Mirrored models - vec3_t cross; - - if ( !tr.registered ) { - return; - } - if ( r_numentities >= MAX_REFENTITIES ) { - ri.Printf(PRINT_DEVELOPER, "RE_AddRefEntityToScene: Dropping refEntity, reached MAX_REFENTITIES\n"); - return; - } - if ( Q_isnan(ent->origin[0]) || Q_isnan(ent->origin[1]) || Q_isnan(ent->origin[2]) ) { - static qboolean firstTime = qtrue; - if (firstTime) { - firstTime = qfalse; - ri.Printf( PRINT_WARNING, "RE_AddRefEntityToScene passed a refEntity which has an origin with a NaN component\n"); - } - return; - } - if ( (int)ent->reType < 0 || ent->reType >= RT_MAX_REF_ENTITY_TYPE ) { - ri.Error( ERR_DROP, "RE_AddRefEntityToScene: bad reType %i", ent->reType ); - } - - backEndData->entities[r_numentities].e = *ent; - backEndData->entities[r_numentities].lightingCalculated = qfalse; - - // JBravo: Mirrored models - CrossProduct(ent->axis[0], ent->axis[1], cross); - backEndData->entities[r_numentities].mirrored = (DotProduct(ent->axis[2], cross) < 0.f); - - r_numentities++; -} - - -/* -===================== -RE_AddDynamicLightToScene - -===================== -*/ -void RE_AddDynamicLightToScene( const vec3_t org, float intensity, float r, float g, float b, int additive ) { - dlight_t *dl; - - if ( !tr.registered ) { - return; - } - if ( r_numdlights >= MAX_DLIGHTS ) { - return; - } - if ( intensity <= 0 ) { - return; - } - // these cards don't have the correct blend mode - if ( glConfig.hardwareType == GLHW_RIVA128 || glConfig.hardwareType == GLHW_PERMEDIA2 ) { - return; - } - dl = &backEndData->dlights[r_numdlights++]; - VectorCopy (org, dl->origin); - dl->radius = intensity; - dl->color[0] = r; - dl->color[1] = g; - dl->color[2] = b; - dl->additive = additive; -} - -/* -===================== -RE_AddLightToScene - -===================== -*/ -void RE_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ) { - RE_AddDynamicLightToScene( org, intensity, r, g, b, qfalse ); -} - -/* -===================== -RE_AddAdditiveLightToScene - -===================== -*/ -void RE_AddAdditiveLightToScene( const vec3_t org, float intensity, float r, float g, float b ) { - RE_AddDynamicLightToScene( org, intensity, r, g, b, qtrue ); -} - -/* -@@@@@@@@@@@@@@@@@@@@@ -RE_RenderScene - -Draw a 3D view into a part of the window, then return -to 2D drawing. - -Rendering a scene may require multiple views to be rendered -to handle mirrors, -@@@@@@@@@@@@@@@@@@@@@ -*/ -void RE_RenderScene( const refdef_t *fd ) { - viewParms_t parms; - int startTime; - - if ( !tr.registered ) { - return; - } - GLimp_LogComment( "====== RE_RenderScene =====\n" ); - - if ( r_norefresh->integer ) { - return; - } - - startTime = ri.Milliseconds(); - - if (!tr.world && !( fd->rdflags & RDF_NOWORLDMODEL ) ) { - ri.Error (ERR_DROP, "R_RenderScene: NULL worldmodel"); - } - - Com_Memcpy( tr.refdef.text, fd->text, sizeof( tr.refdef.text ) ); - - tr.refdef.x = fd->x; - tr.refdef.y = fd->y; - tr.refdef.width = fd->width; - tr.refdef.height = fd->height; - tr.refdef.fov_x = fd->fov_x; - tr.refdef.fov_y = fd->fov_y; - - VectorCopy( fd->vieworg, tr.refdef.vieworg ); - VectorCopy( fd->viewaxis[0], tr.refdef.viewaxis[0] ); - VectorCopy( fd->viewaxis[1], tr.refdef.viewaxis[1] ); - VectorCopy( fd->viewaxis[2], tr.refdef.viewaxis[2] ); - - tr.refdef.time = fd->time; - tr.refdef.rdflags = fd->rdflags; - - // copy the areamask data over and note if it has changed, which - // will force a reset of the visible leafs even if the view hasn't moved - tr.refdef.areamaskModified = qfalse; - if ( ! (tr.refdef.rdflags & RDF_NOWORLDMODEL) ) { - int areaDiff; - int i; - - // compare the area bits - areaDiff = 0; - for (i = 0 ; i < MAX_MAP_AREA_BYTES/4 ; i++) { - areaDiff |= ((int *)tr.refdef.areamask)[i] ^ ((int *)fd->areamask)[i]; - ((int *)tr.refdef.areamask)[i] = ((int *)fd->areamask)[i]; - } - - if ( areaDiff ) { - // a door just opened or something - tr.refdef.areamaskModified = qtrue; - } - } - - tr.refdef.sunDir[3] = 0.0f; - tr.refdef.sunCol[3] = 1.0f; - tr.refdef.sunAmbCol[3] = 1.0f; - - VectorCopy(tr.sunDirection, tr.refdef.sunDir); - if ( (tr.refdef.rdflags & RDF_NOWORLDMODEL) || !(r_depthPrepass->value) ){ - tr.refdef.colorScale = 1.0f; - VectorSet(tr.refdef.sunCol, 0, 0, 0); - VectorSet(tr.refdef.sunAmbCol, 0, 0, 0); - } - else if (r_forceSun->integer == 1) - { - float scale = pow(2, r_mapOverBrightBits->integer - tr.overbrightBits - 8); - tr.refdef.colorScale = r_forceSunMapLightScale->value; - VectorScale(tr.sunLight, scale * r_forceSunLightScale->value, tr.refdef.sunCol); - VectorScale(tr.sunLight, scale * r_forceSunAmbientScale->value, tr.refdef.sunAmbCol); - } - else - { - float scale = pow(2, r_mapOverBrightBits->integer - tr.overbrightBits - 8); - tr.refdef.colorScale = tr.mapLightScale; - VectorScale(tr.sunLight, scale, tr.refdef.sunCol); - VectorScale(tr.sunAmbient, scale, tr.refdef.sunAmbCol); - } - - if (r_forceAutoExposure->integer) - { - tr.refdef.autoExposureMinMax[0] = r_forceAutoExposureMin->value; - tr.refdef.autoExposureMinMax[1] = r_forceAutoExposureMax->value; - } - else - { - tr.refdef.autoExposureMinMax[0] = tr.autoExposureMinMax[0]; - tr.refdef.autoExposureMinMax[1] = tr.autoExposureMinMax[1]; - } - - if (r_forceToneMap->integer) - { - tr.refdef.toneMinAvgMaxLinear[0] = pow(2, r_forceToneMapMin->value); - tr.refdef.toneMinAvgMaxLinear[1] = pow(2, r_forceToneMapAvg->value); - tr.refdef.toneMinAvgMaxLinear[2] = pow(2, r_forceToneMapMax->value); - } - else - { - tr.refdef.toneMinAvgMaxLinear[0] = pow(2, tr.toneMinAvgMaxLevel[0]); - tr.refdef.toneMinAvgMaxLinear[1] = pow(2, tr.toneMinAvgMaxLevel[1]); - tr.refdef.toneMinAvgMaxLinear[2] = pow(2, tr.toneMinAvgMaxLevel[2]); - } - - // Makro - copy exta info if present - if (fd->rdflags & RDF_EXTRA) { - const refdefex_t* extra = (const refdefex_t*) (fd+1); - - tr.refdef.blurFactor = extra->blurFactor; - - if (fd->rdflags & RDF_SUNLIGHT) - { - VectorCopy(extra->sunDir, tr.refdef.sunDir); - VectorCopy(extra->sunCol, tr.refdef.sunCol); - VectorCopy(extra->sunAmbCol, tr.refdef.sunAmbCol); - } - } - else - { - tr.refdef.blurFactor = 0.0f; - } - - // derived info - - tr.refdef.floatTime = tr.refdef.time * 0.001f; - - tr.refdef.numDrawSurfs = r_firstSceneDrawSurf; - tr.refdef.drawSurfs = backEndData->drawSurfs; - - tr.refdef.num_entities = r_numentities - r_firstSceneEntity; - tr.refdef.entities = &backEndData->entities[r_firstSceneEntity]; - - tr.refdef.num_dlights = r_numdlights - r_firstSceneDlight; - tr.refdef.dlights = &backEndData->dlights[r_firstSceneDlight]; - - tr.refdef.numPolys = r_numpolys - r_firstScenePoly; - tr.refdef.polys = &backEndData->polys[r_firstScenePoly]; - - tr.refdef.num_pshadows = 0; - tr.refdef.pshadows = &backEndData->pshadows[0]; - - // turn off dynamic lighting globally by clearing all the - // dlights if it needs to be disabled or if vertex lighting is enabled - if ( r_dynamiclight->integer == 0 || - r_vertexLight->integer == 1 || - glConfig.hardwareType == GLHW_PERMEDIA2 ) { - tr.refdef.num_dlights = 0; - } - - // a single frame may have multiple scenes draw inside it -- - // a 3D game view, 3D status bar renderings, 3D menus, etc. - // They need to be distinguished by the light flare code, because - // the visibility state for a given surface may be different in - // each scene / view. - tr.frameSceneNum++; - tr.sceneCount++; - - // SmileTheory: playing with shadow mapping - if (!( fd->rdflags & RDF_NOWORLDMODEL ) && tr.refdef.num_dlights && r_dlightMode->integer >= 2) - { - R_RenderDlightCubemaps(fd); - } - - /* playing with more shadows */ - if(glRefConfig.framebufferObject && !( fd->rdflags & RDF_NOWORLDMODEL ) && r_shadows->integer == 4) - { - R_RenderPshadowMaps(fd); - } - - // playing with even more shadows - if(glRefConfig.framebufferObject && !( fd->rdflags & RDF_NOWORLDMODEL ) && (r_forceSun->integer || tr.sunShadows)) - { - R_RenderSunShadowMaps(fd, 0); - R_RenderSunShadowMaps(fd, 1); - R_RenderSunShadowMaps(fd, 2); - } - - // setup view parms for the initial view - // - // set up viewport - // The refdef takes 0-at-the-top y coordinates, so - // convert to GL's 0-at-the-bottom space - // - Com_Memset( &parms, 0, sizeof( parms ) ); - parms.viewportX = tr.refdef.x; - parms.viewportY = glConfig.vidHeight - ( tr.refdef.y + tr.refdef.height ); - parms.viewportWidth = tr.refdef.width; - parms.viewportHeight = tr.refdef.height; - parms.isPortal = qfalse; - - parms.fovX = tr.refdef.fov_x; - parms.fovY = tr.refdef.fov_y; - - parms.stereoFrame = tr.refdef.stereoFrame; - - VectorCopy( fd->vieworg, parms.or.origin ); - VectorCopy( fd->viewaxis[0], parms.or.axis[0] ); - VectorCopy( fd->viewaxis[1], parms.or.axis[1] ); - VectorCopy( fd->viewaxis[2], parms.or.axis[2] ); - - VectorCopy( fd->vieworg, parms.pvsOrigin ); - - if(!( fd->rdflags & RDF_NOWORLDMODEL ) && r_depthPrepass->value && ((r_forceSun->integer) || tr.sunShadows)) - { - parms.flags = VPF_USESUNLIGHT; - } - - R_RenderView( &parms ); - - if(!( fd->rdflags & RDF_NOWORLDMODEL )) - R_AddPostProcessCmd(); - - // the next scene rendered in this frame will tack on after this one - r_firstSceneDrawSurf = tr.refdef.numDrawSurfs; - r_firstSceneEntity = r_numentities; - r_firstSceneDlight = r_numdlights; - r_firstScenePoly = r_numpolys; - - tr.frontEndMsec += ri.Milliseconds() - startTime; -} diff --git a/src/rend2/tr_shade.c b/src/rend2/tr_shade.c deleted file mode 100644 index b4758a86..00000000 --- a/src/rend2/tr_shade.c +++ /dev/null @@ -1,1866 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_shade.c - -#include "tr_local.h" -#if idppc_altivec && !defined(MACOS_X) -#include -#endif - -/* - - THIS ENTIRE FILE IS BACK END - - This file deals with applying shaders to surface data in the tess struct. -*/ - - -/* -================== -R_DrawElements - -================== -*/ - -void R_DrawElementsVBO( int numIndexes, glIndex_t firstIndex, glIndex_t minIndex, glIndex_t maxIndex ) -{ - if (glRefConfig.drawRangeElements) - qglDrawRangeElementsEXT(GL_TRIANGLES, minIndex, maxIndex, numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(firstIndex * sizeof(GL_INDEX_TYPE))); - else - qglDrawElements(GL_TRIANGLES, numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(firstIndex * sizeof(GL_INDEX_TYPE))); - -} - - -static void R_DrawMultiElementsVBO( int multiDrawPrimitives, glIndex_t *multiDrawMinIndex, glIndex_t *multiDrawMaxIndex, - GLsizei *multiDrawNumIndexes, glIndex_t **multiDrawFirstIndex) -{ - if (glRefConfig.multiDrawArrays) - { - qglMultiDrawElementsEXT(GL_TRIANGLES, multiDrawNumIndexes, GL_INDEX_TYPE, (const GLvoid **)multiDrawFirstIndex, multiDrawPrimitives); - } - else - { - int i; - - if (glRefConfig.drawRangeElements) - { - for (i = 0; i < multiDrawPrimitives; i++) - { - qglDrawRangeElementsEXT(GL_TRIANGLES, multiDrawMinIndex[i], multiDrawMaxIndex[i], multiDrawNumIndexes[i], GL_INDEX_TYPE, multiDrawFirstIndex[i]); - } - } - else - { - for (i = 0; i < multiDrawPrimitives; i++) - { - qglDrawElements(GL_TRIANGLES, multiDrawNumIndexes[i], GL_INDEX_TYPE, multiDrawFirstIndex[i]); - } - } - } -} - - -/* -============================================================= - -SURFACE SHADERS - -============================================================= -*/ - -shaderCommands_t tess; - - -/* -================= -R_BindAnimatedImageToTMU - -================= -*/ -static void R_BindAnimatedImageToTMU( textureBundle_t *bundle, int tmu ) { - int index; - - if ( bundle->isVideoMap ) { - int oldtmu = glState.currenttmu; - GL_SelectTexture(tmu); - ri.CIN_RunCinematic(bundle->videoMapHandle); - ri.CIN_UploadCinematic(bundle->videoMapHandle); - GL_SelectTexture(oldtmu); - return; - } - - if ( bundle->numImageAnimations <= 1 ) { - GL_BindToTMU( bundle->image[0], tmu); - return; - } - - // it is necessary to do this messy calc to make sure animations line up - // exactly with waveforms of the same frequency - index = ri.ftol(tess.shaderTime * bundle->imageAnimationSpeed * FUNCTABLE_SIZE); - index >>= FUNCTABLE_SIZE2; - - if ( index < 0 ) { - index = 0; // may happen with shader time offsets - } - index %= bundle->numImageAnimations; - - GL_BindToTMU( bundle->image[ index ], tmu ); -} - - -/* -================ -DrawTris - -Draws triangle outlines for debugging -================ -*/ -static void DrawTris (shaderCommands_t *input) { - GL_Bind( tr.whiteImage ); - - GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE ); - qglDepthRange( 0, 0 ); - - { - shaderProgram_t *sp = &tr.textureColorShader; - vec4_t color; - - GLSL_VertexAttribsState(ATTR_POSITION); - GLSL_BindProgram(sp); - - GLSL_SetUniformMatrix16(sp, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); - VectorSet4(color, 1, 1, 1, 1); - GLSL_SetUniformVec4(sp, TEXTURECOLOR_UNIFORM_COLOR, color); - - if (input->multiDrawPrimitives) - { - R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawMinIndex, input->multiDrawMaxIndex, input->multiDrawNumIndexes, input->multiDrawFirstIndex); - } - else - { - R_DrawElementsVBO(input->numIndexes, input->firstIndex, input->minIndex, input->maxIndex); - } - } - - qglDepthRange( 0, 1 ); -} - - -/* -================ -DrawNormals - -Draws vertex normals for debugging -================ -*/ -static void DrawNormals (shaderCommands_t *input) { - //FIXME: implement this -} - -/* -============== -RB_BeginSurface - -We must set some things up before beginning any tesselation, -because a surface may be forced to perform a RB_End due -to overflow. -============== -*/ -void RB_BeginSurface( shader_t *shader, int fogNum ) { - - shader_t *state = (shader->remappedShader) ? shader->remappedShader : shader; - - tess.numIndexes = 0; - tess.firstIndex = 0; - tess.numVertexes = 0; - tess.multiDrawPrimitives = 0; - tess.shader = state; - tess.fogNum = fogNum; - tess.dlightBits = 0; // will be OR'd in by surface functions - tess.pshadowBits = 0; // will be OR'd in by surface functions - tess.xstages = state->stages; - tess.numPasses = state->numUnfoggedPasses; - tess.currentStageIteratorFunc = state->optimalStageIteratorFunc; - tess.useInternalVBO = qtrue; - - tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; - if (tess.shader->clampTime && tess.shaderTime >= tess.shader->clampTime) { - tess.shaderTime = tess.shader->clampTime; - } - - if (backEnd.viewParms.flags & VPF_SHADOWMAP) - { - tess.currentStageIteratorFunc = RB_StageIteratorGeneric; - } -} - - - -extern float EvalWaveForm( const waveForm_t *wf ); -extern float EvalWaveFormClamped( const waveForm_t *wf ); - - -static void ComputeTexMatrix( shaderStage_t *pStage, int bundleNum, float *outmatrix) -{ - int tm; - float matrix[16], currentmatrix[16]; - textureBundle_t *bundle = &pStage->bundle[bundleNum]; - - Matrix16Identity(outmatrix); - Matrix16Identity(currentmatrix); - - for ( tm = 0; tm < bundle->numTexMods ; tm++ ) { - switch ( bundle->texMods[tm].type ) - { - - case TMOD_NONE: - tm = TR_MAX_TEXMODS; // break out of for loop - break; - - case TMOD_TURBULENT: - RB_CalcTurbulentTexMatrix( &bundle->texMods[tm].wave, - matrix ); - outmatrix[12] = matrix[12]; - outmatrix[13] = matrix[13]; - Matrix16Copy(outmatrix, currentmatrix); - break; - - case TMOD_ENTITY_TRANSLATE: - RB_CalcScrollTexMatrix( backEnd.currentEntity->e.shaderTexCoord, - matrix ); - Matrix16Multiply(matrix, currentmatrix, outmatrix); - Matrix16Copy(outmatrix, currentmatrix); - break; - - case TMOD_SCROLL: - RB_CalcScrollTexMatrix( bundle->texMods[tm].scroll, - matrix ); - Matrix16Multiply(matrix, currentmatrix, outmatrix); - Matrix16Copy(outmatrix, currentmatrix); - break; - - case TMOD_SCALE: - RB_CalcScaleTexMatrix( bundle->texMods[tm].scale, - matrix ); - Matrix16Multiply(matrix, currentmatrix, outmatrix); - Matrix16Copy(outmatrix, currentmatrix); - break; - - case TMOD_STRETCH: - RB_CalcStretchTexMatrix( &bundle->texMods[tm].wave, - matrix ); - Matrix16Multiply(matrix, currentmatrix, outmatrix); - Matrix16Copy(outmatrix, currentmatrix); - break; - - case TMOD_TRANSFORM: - RB_CalcTransformTexMatrix( &bundle->texMods[tm], - matrix ); - Matrix16Multiply(matrix, currentmatrix, outmatrix); - Matrix16Copy(outmatrix, currentmatrix); - break; - - case TMOD_ROTATE: - RB_CalcRotateTexMatrix( bundle->texMods[tm].rotateSpeed, - matrix ); - Matrix16Multiply(matrix, currentmatrix, outmatrix); - Matrix16Copy(outmatrix, currentmatrix); - break; - - default: - ri.Error( ERR_DROP, "ERROR: unknown texmod '%d' in shader '%s'", bundle->texMods[tm].type, tess.shader->name ); - break; - } - } -} - - -static void ComputeDeformValues(int *deformGen, vec5_t deformParams) -{ - // u_DeformGen - *deformGen = DGEN_NONE; - if(!ShaderRequiresCPUDeforms(tess.shader)) - { - deformStage_t *ds; - - // only support the first one - ds = &tess.shader->deforms[0]; - - switch (ds->deformation) - { - case DEFORM_WAVE: - *deformGen = ds->deformationWave.func; - - deformParams[0] = ds->deformationWave.base; - deformParams[1] = ds->deformationWave.amplitude; - deformParams[2] = ds->deformationWave.phase; - deformParams[3] = ds->deformationWave.frequency; - deformParams[4] = ds->deformationSpread; - break; - - case DEFORM_BULGE: - *deformGen = DGEN_BULGE; - - deformParams[0] = 0; - deformParams[1] = ds->bulgeHeight; // amplitude - deformParams[2] = ds->bulgeWidth; // phase - deformParams[3] = ds->bulgeSpeed; // frequency - deformParams[4] = 0; - break; - - default: - break; - } - } -} - - -static void ProjectDlightTexture( void ) { - int l; - vec3_t origin; - float scale; - float radius; - int deformGen; - vec5_t deformParams; - - if ( !backEnd.refdef.num_dlights ) { - return; - } - - ComputeDeformValues(&deformGen, deformParams); - - for ( l = 0 ; l < backEnd.refdef.num_dlights ; l++ ) { - dlight_t *dl; - shaderProgram_t *sp; - vec4_t vector; - - if ( !( tess.dlightBits & ( 1 << l ) ) ) { - continue; // this surface definately doesn't have any of this light - } - - dl = &backEnd.refdef.dlights[l]; - VectorCopy( dl->transformed, origin ); - radius = dl->radius; - scale = 1.0f / radius; - - sp = &tr.dlightShader[deformGen == DGEN_NONE ? 0 : 1]; - - backEnd.pc.c_dlightDraws++; - - GLSL_BindProgram(sp); - - GLSL_SetUniformMatrix16(sp, DLIGHT_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); - - GLSL_SetUniformFloat(sp, DLIGHT_UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); - - GLSL_SetUniformInt(sp, DLIGHT_UNIFORM_DEFORMGEN, deformGen); - if (deformGen != DGEN_NONE) - { - GLSL_SetUniformFloat5(sp, DLIGHT_UNIFORM_DEFORMPARAMS, deformParams); - GLSL_SetUniformFloat(sp, DLIGHT_UNIFORM_TIME, tess.shaderTime); - } - - vector[0] = dl->color[0]; - vector[1] = dl->color[1]; - vector[2] = dl->color[2]; - vector[3] = 1.0f; - GLSL_SetUniformVec4(sp, DLIGHT_UNIFORM_COLOR, vector); - - vector[0] = origin[0]; - vector[1] = origin[1]; - vector[2] = origin[2]; - vector[3] = scale; - GLSL_SetUniformVec4(sp, DLIGHT_UNIFORM_DLIGHTINFO, vector); - - GL_Bind( tr.dlightImage ); - - // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light - // where they aren't rendered - if ( dl->additive ) { - GL_State( GLS_ATEST_GT_0 | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); - } - else { - GL_State( GLS_ATEST_GT_0 | GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); - } - - if (tess.multiDrawPrimitives) - { - shaderCommands_t *input = &tess; - R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawMinIndex, input->multiDrawMaxIndex, input->multiDrawNumIndexes, input->multiDrawFirstIndex); - } - else - { - R_DrawElementsVBO(tess.numIndexes, tess.firstIndex, tess.minIndex, tess.maxIndex); - } - - backEnd.pc.c_totalIndexes += tess.numIndexes; - backEnd.pc.c_dlightIndexes += tess.numIndexes; - } -} - - -static void ComputeShaderColors( shaderStage_t *pStage, vec4_t baseColor, vec4_t vertColor ) -{ - // - // rgbGen - // - switch ( pStage->rgbGen ) - { - case CGEN_IDENTITY: - baseColor[0] = - baseColor[1] = - baseColor[2] = - baseColor[3] = 1.0f; - - vertColor[0] = - vertColor[1] = - vertColor[2] = - vertColor[3] = 0.0f; - break; - case CGEN_IDENTITY_LIGHTING: - baseColor[0] = - baseColor[1] = - baseColor[2] = tr.identityLight; - baseColor[3] = 1.0f; - - vertColor[0] = - vertColor[1] = - vertColor[2] = - vertColor[3] = 0.0f; - break; - case CGEN_EXACT_VERTEX: - baseColor[0] = - baseColor[1] = - baseColor[2] = - baseColor[3] = 0.0f; - - vertColor[0] = - vertColor[1] = - vertColor[2] = - vertColor[3] = 1.0f; - break; - case CGEN_EXACT_VERTEX_LIT: - baseColor[0] = - baseColor[1] = - baseColor[2] = - baseColor[3] = 0.0f; - - vertColor[0] = - vertColor[1] = - vertColor[2] = - vertColor[3] = 1.0f; - break; - case CGEN_CONST: - baseColor[0] = pStage->constantColor[0] / 255.0f; - baseColor[1] = pStage->constantColor[1] / 255.0f; - baseColor[2] = pStage->constantColor[2] / 255.0f; - baseColor[3] = pStage->constantColor[3] / 255.0f; - - vertColor[0] = - vertColor[1] = - vertColor[2] = - vertColor[3] = 0.0f; - break; - case CGEN_VERTEX: - baseColor[0] = - baseColor[1] = - baseColor[2] = - baseColor[3] = 0.0f; - - vertColor[0] = - vertColor[1] = - vertColor[2] = tr.identityLight; - vertColor[3] = 1.0f; - break; - case CGEN_VERTEX_LIT: - baseColor[0] = - baseColor[1] = - baseColor[2] = - baseColor[3] = 0.0f; - - vertColor[0] = - vertColor[1] = - vertColor[2] = - vertColor[3] = tr.identityLight; - break; - case CGEN_ONE_MINUS_VERTEX: - baseColor[0] = - baseColor[1] = - baseColor[2] = tr.identityLight; - baseColor[3] = 1.0f; - - vertColor[0] = - vertColor[1] = - vertColor[2] = -tr.identityLight; - vertColor[3] = 0.0f; - break; - case CGEN_FOG: - { - fog_t *fog; - - fog = tr.world->fogs + tess.fogNum; - - baseColor[0] = ((unsigned char *)(&fog->colorInt))[0] / 255.0f; - baseColor[1] = ((unsigned char *)(&fog->colorInt))[1] / 255.0f; - baseColor[2] = ((unsigned char *)(&fog->colorInt))[2] / 255.0f; - baseColor[3] = ((unsigned char *)(&fog->colorInt))[3] / 255.0f; - } - - vertColor[0] = - vertColor[1] = - vertColor[2] = - vertColor[3] = 0.0f; - break; - case CGEN_WAVEFORM: - baseColor[0] = - baseColor[1] = - baseColor[2] = RB_CalcWaveColorSingle( &pStage->rgbWave ); - baseColor[3] = 1.0f; - - vertColor[0] = - vertColor[1] = - vertColor[2] = - vertColor[3] = 0.0f; - break; - case CGEN_ENTITY: - if (backEnd.currentEntity) - { - baseColor[0] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[0] / 255.0f; - baseColor[1] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[1] / 255.0f; - baseColor[2] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[2] / 255.0f; - baseColor[3] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; - } - - vertColor[0] = - vertColor[1] = - vertColor[2] = - vertColor[3] = 0.0f; - break; - case CGEN_ONE_MINUS_ENTITY: - if (backEnd.currentEntity) - { - baseColor[0] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[0] / 255.0f; - baseColor[1] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[1] / 255.0f; - baseColor[2] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[2] / 255.0f; - baseColor[3] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; - } - - vertColor[0] = - vertColor[1] = - vertColor[2] = - vertColor[3] = 0.0f; - break; - case CGEN_LIGHTING_DIFFUSE: - case CGEN_BAD: - baseColor[0] = - baseColor[1] = - baseColor[2] = - baseColor[3] = 1.0f; - - vertColor[0] = - vertColor[1] = - vertColor[2] = - vertColor[3] = 0.0f; - break; - } - - // - // alphaGen - // - switch ( pStage->alphaGen ) - { - case AGEN_SKIP: - break; - case AGEN_IDENTITY: - baseColor[3] = 1.0f; - vertColor[3] = 0.0f; - break; - case AGEN_CONST: - baseColor[3] = pStage->constantColor[3] / 255.0f; - vertColor[3] = 0.0f; - break; - case AGEN_WAVEFORM: - baseColor[3] = RB_CalcWaveAlphaSingle( &pStage->alphaWave ); - vertColor[3] = 0.0f; - break; - case AGEN_ENTITY: - if (backEnd.currentEntity) - { - baseColor[3] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; - } - vertColor[3] = 0.0f; - break; - case AGEN_ONE_MINUS_ENTITY: - if (backEnd.currentEntity) - { - baseColor[3] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; - } - vertColor[3] = 0.0f; - break; - case AGEN_VERTEX: - baseColor[3] = 0.0f; - vertColor[3] = 1.0f; - break; - case AGEN_ONE_MINUS_VERTEX: - baseColor[3] = 1.0f; - vertColor[3] = -1.0f; - break; - case AGEN_LIGHTING_SPECULAR: - case AGEN_PORTAL: - case AGEN_FRESNEL: - // Done entirely in vertex program - baseColor[3] = 1.0f; - vertColor[3] = 0.0f; - break; - } - - // FIXME: find some way to implement this. -#if 0 - // if in greyscale rendering mode turn all color values into greyscale. - if(r_greyscale->integer) - { - int scale; - - for(i = 0; i < tess.numVertexes; i++) - { - scale = (tess.svars.colors[i][0] + tess.svars.colors[i][1] + tess.svars.colors[i][2]) / 3; - tess.svars.colors[i][0] = tess.svars.colors[i][1] = tess.svars.colors[i][2] = scale; - } - } -#endif -} - - -static void ComputeFogValues(vec4_t fogDistanceVector, vec4_t fogDepthVector, float *eyeT) -{ - // from RB_CalcFogTexCoords() - fog_t *fog; - vec3_t local; - - if (!tess.fogNum) - return; - - fog = tr.world->fogs + tess.fogNum; - - VectorSubtract( backEnd.or.origin, backEnd.viewParms.or.origin, local ); - fogDistanceVector[0] = -backEnd.or.modelMatrix[2]; - fogDistanceVector[1] = -backEnd.or.modelMatrix[6]; - fogDistanceVector[2] = -backEnd.or.modelMatrix[10]; - fogDistanceVector[3] = DotProduct( local, backEnd.viewParms.or.axis[0] ); - - // scale the fog vectors based on the fog's thickness - VectorScale4(fogDistanceVector, fog->tcScale, fogDistanceVector); - - // rotate the gradient vector for this orientation - if ( fog->hasSurface ) { - fogDepthVector[0] = fog->surface[0] * backEnd.or.axis[0][0] + - fog->surface[1] * backEnd.or.axis[0][1] + fog->surface[2] * backEnd.or.axis[0][2]; - fogDepthVector[1] = fog->surface[0] * backEnd.or.axis[1][0] + - fog->surface[1] * backEnd.or.axis[1][1] + fog->surface[2] * backEnd.or.axis[1][2]; - fogDepthVector[2] = fog->surface[0] * backEnd.or.axis[2][0] + - fog->surface[1] * backEnd.or.axis[2][1] + fog->surface[2] * backEnd.or.axis[2][2]; - fogDepthVector[3] = -fog->surface[3] + DotProduct( backEnd.or.origin, fog->surface ); - - *eyeT = DotProduct( backEnd.or.viewOrigin, fogDepthVector ) + fogDepthVector[3]; - } else { - *eyeT = 1; // non-surface fog always has eye inside - } -} - - -static void ComputeFogColorMask( shaderStage_t *pStage, vec4_t fogColorMask ) -{ - switch(pStage->adjustColorsForFog) - { - case ACFF_MODULATE_RGB: - fogColorMask[0] = - fogColorMask[1] = - fogColorMask[2] = 1.0f; - fogColorMask[3] = 0.0f; - break; - case ACFF_MODULATE_ALPHA: - fogColorMask[0] = - fogColorMask[1] = - fogColorMask[2] = 0.0f; - fogColorMask[3] = 1.0f; - break; - case ACFF_MODULATE_RGBA: - fogColorMask[0] = - fogColorMask[1] = - fogColorMask[2] = - fogColorMask[3] = 1.0f; - break; - default: - fogColorMask[0] = - fogColorMask[1] = - fogColorMask[2] = - fogColorMask[3] = 0.0f; - break; - } -} - - -static void ForwardDlight( void ) { - int l; - //vec3_t origin; - //float scale; - float radius; - - int deformGen; - vec5_t deformParams; - - vec4_t fogDistanceVector, fogDepthVector = {0, 0, 0, 0}; - float eyeT = 0; - - shaderCommands_t *input = &tess; - shaderStage_t *pStage = tess.xstages[0]; - - if ( !backEnd.refdef.num_dlights ) { - return; - } - - ComputeDeformValues(&deformGen, deformParams); - - ComputeFogValues(fogDistanceVector, fogDepthVector, &eyeT); - - for ( l = 0 ; l < backEnd.refdef.num_dlights ; l++ ) { - dlight_t *dl; - shaderProgram_t *sp; - vec4_t vector; - matrix_t matrix; - - if ( !( tess.dlightBits & ( 1 << l ) ) ) { - continue; // this surface definately doesn't have any of this light - } - - dl = &backEnd.refdef.dlights[l]; - //VectorCopy( dl->transformed, origin ); - radius = dl->radius; - //scale = 1.0f / radius; - - //if (pStage->glslShaderGroup == tr.lightallShader) - { - int index = pStage->glslShaderIndex; - - index &= ~(LIGHTDEF_LIGHTTYPE_MASK | LIGHTDEF_USE_DELUXEMAP); - index |= LIGHTDEF_USE_LIGHT_VECTOR; - - sp = &tr.lightallShader[index]; - } - - backEnd.pc.c_lightallDraws++; - - GLSL_BindProgram(sp); - - GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); - GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_VIEWORIGIN, backEnd.viewParms.or.origin); - - GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); - - GLSL_SetUniformInt(sp, GENERIC_UNIFORM_DEFORMGEN, deformGen); - if (deformGen != DGEN_NONE) - { - GLSL_SetUniformFloat5(sp, GENERIC_UNIFORM_DEFORMPARAMS, deformParams); - GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_TIME, tess.shaderTime); - } - - if ( input->fogNum ) { - vec4_t fogColorMask; - - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGDISTANCE, fogDistanceVector); - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGDEPTH, fogDepthVector); - GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_FOGEYET, eyeT); - - ComputeFogColorMask(pStage, fogColorMask); - - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGCOLORMASK, fogColorMask); - } - - { - vec4_t baseColor; - vec4_t vertColor; - - ComputeShaderColors(pStage, baseColor, vertColor); - - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_BASECOLOR, baseColor); - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_VERTCOLOR, vertColor); - } - - if (pStage->alphaGen == AGEN_PORTAL) - { - GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_PORTALRANGE, tess.shader->portalRange); - } - - GLSL_SetUniformInt(sp, GENERIC_UNIFORM_COLORGEN, pStage->rgbGen); - GLSL_SetUniformInt(sp, GENERIC_UNIFORM_ALPHAGEN, pStage->alphaGen); - - GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_DIRECTEDLIGHT, dl->color); - - VectorSet(vector, 0, 0, 0); - GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_AMBIENTLIGHT, vector); - - VectorCopy(dl->origin, vector); - vector[3] = 1.0f; - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_LIGHTORIGIN, vector); - - GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_LIGHTRADIUS, radius); - - GLSL_SetUniformVec2(sp, GENERIC_UNIFORM_MATERIALINFO, pStage->materialInfo); - - // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light - // where they aren't rendered - GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); - - GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELMATRIX, backEnd.or.transformMatrix); - - if (pStage->bundle[TB_DIFFUSEMAP].image[0]) - R_BindAnimatedImageToTMU( &pStage->bundle[TB_DIFFUSEMAP], TB_DIFFUSEMAP); - - if (pStage->bundle[TB_NORMALMAP].image[0]) - R_BindAnimatedImageToTMU( &pStage->bundle[TB_NORMALMAP], TB_NORMALMAP); - - if (pStage->bundle[TB_SPECULARMAP].image[0]) - R_BindAnimatedImageToTMU( &pStage->bundle[TB_SPECULARMAP], TB_SPECULARMAP); - - if (r_dlightMode->integer >= 2) - { - GL_SelectTexture(TB_SHADOWMAP); - GL_BindCubemap(tr.shadowCubemaps[l]); - GL_SelectTexture(0); - } - - ComputeTexMatrix( pStage, TB_DIFFUSEMAP, matrix ); - - VectorSet4(vector, matrix[0], matrix[1], matrix[4], matrix[5]); - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_DIFFUSETEXMATRIX, vector); - - VectorSet4(vector, matrix[8], matrix[9], matrix[12], matrix[13]); - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_DIFFUSETEXOFFTURB, vector); - - GLSL_SetUniformInt(sp, GENERIC_UNIFORM_TCGEN0, pStage->bundle[0].tcGen); - - // - // draw - // - - if (input->multiDrawPrimitives) - { - R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawMinIndex, input->multiDrawMaxIndex, input->multiDrawNumIndexes, input->multiDrawFirstIndex); - } - else - { - R_DrawElementsVBO(input->numIndexes, input->firstIndex, input->minIndex, input->maxIndex); - } - - backEnd.pc.c_totalIndexes += tess.numIndexes; - backEnd.pc.c_dlightIndexes += tess.numIndexes; - } -} - - -static void ForwardSunlight( void ) { -// int l; - //vec3_t origin; - //float scale; - int stage; - int stageGlState[2]; - qboolean alphaOverride = qfalse; - - int deformGen; - vec5_t deformParams; - - vec4_t fogDistanceVector, fogDepthVector = {0, 0, 0, 0}; - float eyeT = 0; - - shaderCommands_t *input = &tess; - - ComputeDeformValues(&deformGen, deformParams); - - ComputeFogValues(fogDistanceVector, fogDepthVector, &eyeT); - - // deal with vertex alpha blended surfaces - if (input->xstages[0] && input->xstages[1] && - (input->xstages[1]->alphaGen == AGEN_VERTEX || input->xstages[1]->alphaGen == AGEN_ONE_MINUS_VERTEX)) - { - stageGlState[0] = input->xstages[0]->stateBits & (GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS); - - if (stageGlState[0] == 0 || stageGlState[0] == (GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO)) - { - stageGlState[1] = input->xstages[1]->stateBits & (GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS); - - if (stageGlState[1] == (GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA)) - { - alphaOverride = qtrue; - stageGlState[0] = GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL; - stageGlState[1] = GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL; - } - else if (stageGlState[1] == (GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA | GLS_DSTBLEND_SRC_ALPHA)) - { - alphaOverride = qtrue; - stageGlState[0] = GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL; - stageGlState[1] = GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL; - } - } - } - - if (!alphaOverride) - { - stageGlState[0] = - stageGlState[1] = GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL; - } - - for ( stage = 0; stage < 2 /*MAX_SHADER_STAGES */; stage++ ) - { - shaderStage_t *pStage = input->xstages[stage]; - shaderProgram_t *sp; - vec4_t vector; - matrix_t matrix; - - if ( !pStage ) - { - break; - } - - //VectorCopy( dl->transformed, origin ); - - //if (pStage->glslShaderGroup == tr.lightallShader) - { - int index = pStage->glslShaderIndex; - - index &= ~(LIGHTDEF_LIGHTTYPE_MASK | LIGHTDEF_USE_DELUXEMAP); - index |= LIGHTDEF_USE_LIGHT_VECTOR | LIGHTDEF_USE_SHADOWMAP; - - if (backEnd.currentEntity && backEnd.currentEntity != &tr.worldEntity) - { - index |= LIGHTDEF_ENTITY; - } - - sp = &tr.lightallShader[index]; - } - - backEnd.pc.c_lightallDraws++; - - GLSL_BindProgram(sp); - - GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); - GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_VIEWORIGIN, backEnd.viewParms.or.origin); - - GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); - - GLSL_SetUniformInt(sp, GENERIC_UNIFORM_DEFORMGEN, deformGen); - if (deformGen != DGEN_NONE) - { - GLSL_SetUniformFloat5(sp, GENERIC_UNIFORM_DEFORMPARAMS, deformParams); - GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_TIME, tess.shaderTime); - } - - if ( input->fogNum ) { - vec4_t fogColorMask; - - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGDISTANCE, fogDistanceVector); - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGDEPTH, fogDepthVector); - GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_FOGEYET, eyeT); - - ComputeFogColorMask(pStage, fogColorMask); - - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGCOLORMASK, fogColorMask); - } - - { - vec4_t baseColor; - vec4_t vertColor; - - ComputeShaderColors(pStage, baseColor, vertColor); - - if (alphaOverride) - { - if (input->xstages[1]->alphaGen == AGEN_VERTEX) - { - baseColor[3] = 0.0f; - vertColor[3] = 1.0f; - } - else if (input->xstages[1]->alphaGen == AGEN_ONE_MINUS_VERTEX) - { - baseColor[3] = 1.0f; - vertColor[3] = -1.0f; - } - } - - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_BASECOLOR, baseColor); - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_VERTCOLOR, vertColor); - } - - if (pStage->alphaGen == AGEN_PORTAL) - { - GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_PORTALRANGE, tess.shader->portalRange); - } - - GLSL_SetUniformInt(sp, GENERIC_UNIFORM_COLORGEN, pStage->rgbGen); - GLSL_SetUniformInt(sp, GENERIC_UNIFORM_ALPHAGEN, pStage->alphaGen); - - GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_DIRECTEDLIGHT, backEnd.refdef.sunCol); - GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_AMBIENTLIGHT, backEnd.refdef.sunAmbCol); - - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_LIGHTORIGIN, backEnd.refdef.sunDir); - - GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_LIGHTRADIUS, 9999999999.9f); - - GLSL_SetUniformVec2(sp, GENERIC_UNIFORM_MATERIALINFO, pStage->materialInfo); - - GL_State( stageGlState[stage] ); - - GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELMATRIX, backEnd.or.transformMatrix); - - if (pStage->bundle[TB_DIFFUSEMAP].image[0]) - R_BindAnimatedImageToTMU( &pStage->bundle[TB_DIFFUSEMAP], TB_DIFFUSEMAP); - - if (pStage->bundle[TB_NORMALMAP].image[0]) - R_BindAnimatedImageToTMU( &pStage->bundle[TB_NORMALMAP], TB_NORMALMAP); - - if (pStage->bundle[TB_SPECULARMAP].image[0]) - R_BindAnimatedImageToTMU( &pStage->bundle[TB_SPECULARMAP], TB_SPECULARMAP); - - /* - { - GL_BindToTMU(tr.sunShadowDepthImage[0], TB_SHADOWMAP); - GL_BindToTMU(tr.sunShadowDepthImage[1], TB_SHADOWMAP2); - GL_BindToTMU(tr.sunShadowDepthImage[2], TB_SHADOWMAP3); - GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_SHADOWMVP, backEnd.refdef.sunShadowMvp[0]); - GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_SHADOWMVP2, backEnd.refdef.sunShadowMvp[1]); - GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_SHADOWMVP3, backEnd.refdef.sunShadowMvp[2]); - } - */ - GL_BindToTMU(tr.screenShadowImage, TB_SHADOWMAP); - - ComputeTexMatrix( pStage, TB_DIFFUSEMAP, matrix ); - - VectorSet4(vector, matrix[0], matrix[1], matrix[4], matrix[5]); - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_DIFFUSETEXMATRIX, vector); - - VectorSet4(vector, matrix[8], matrix[9], matrix[12], matrix[13]); - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_DIFFUSETEXOFFTURB, vector); - - GLSL_SetUniformInt(sp, GENERIC_UNIFORM_TCGEN0, pStage->bundle[0].tcGen); - - // - // draw - // - - if (input->multiDrawPrimitives) - { - R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawMinIndex, input->multiDrawMaxIndex, input->multiDrawNumIndexes, input->multiDrawFirstIndex); - } - else - { - R_DrawElementsVBO(input->numIndexes, input->firstIndex, input->minIndex, input->maxIndex); - } - - backEnd.pc.c_totalIndexes += tess.numIndexes; - backEnd.pc.c_dlightIndexes += tess.numIndexes; - } -} - - -static void ProjectPshadowVBOGLSL( void ) { - int l; - vec3_t origin; - float radius; - - int deformGen; - vec5_t deformParams; - - shaderCommands_t *input = &tess; - - if ( !backEnd.refdef.num_pshadows ) { - return; - } - - ComputeDeformValues(&deformGen, deformParams); - - for ( l = 0 ; l < backEnd.refdef.num_pshadows ; l++ ) { - pshadow_t *ps; - shaderProgram_t *sp; - vec4_t vector; - - if ( !( tess.pshadowBits & ( 1 << l ) ) ) { - continue; // this surface definately doesn't have any of this shadow - } - - ps = &backEnd.refdef.pshadows[l]; - VectorCopy( ps->lightOrigin, origin ); - radius = ps->lightRadius; - - sp = &tr.pshadowShader; - - GLSL_BindProgram(sp); - - GLSL_SetUniformMatrix16(sp, PSHADOW_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); - - VectorCopy(origin, vector); - vector[3] = 1.0f; - GLSL_SetUniformVec4(sp, PSHADOW_UNIFORM_LIGHTORIGIN, vector); - - VectorScale(ps->lightViewAxis[0], 1.0f / ps->viewRadius, vector); - GLSL_SetUniformVec3(sp, PSHADOW_UNIFORM_LIGHTFORWARD, vector); - - VectorScale(ps->lightViewAxis[1], 1.0f / ps->viewRadius, vector); - GLSL_SetUniformVec3(sp, PSHADOW_UNIFORM_LIGHTRIGHT, vector); - - VectorScale(ps->lightViewAxis[2], 1.0f / ps->viewRadius, vector); - GLSL_SetUniformVec3(sp, PSHADOW_UNIFORM_LIGHTUP, vector); - - GLSL_SetUniformFloat(sp, PSHADOW_UNIFORM_LIGHTRADIUS, radius); - - // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light - // where they aren't rendered - GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_EQUAL ); - - GL_BindToTMU( tr.pshadowMaps[l], TB_DIFFUSEMAP ); - - // - // draw - // - - if (input->multiDrawPrimitives) - { - R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawMinIndex, input->multiDrawMaxIndex, input->multiDrawNumIndexes, input->multiDrawFirstIndex); - } - else - { - R_DrawElementsVBO(input->numIndexes, input->firstIndex, input->minIndex, input->maxIndex); - } - - backEnd.pc.c_totalIndexes += tess.numIndexes; - //backEnd.pc.c_dlightIndexes += tess.numIndexes; - } -} - - - -/* -=================== -RB_FogPass - -Blends a fog texture on top of everything else -=================== -*/ -static void RB_FogPass( void ) { - fog_t *fog; - vec4_t color; - vec4_t fogDistanceVector, fogDepthVector = {0, 0, 0, 0}; - float eyeT = 0; - shaderProgram_t *sp; - - int deformGen; - vec5_t deformParams; - - ComputeDeformValues(&deformGen, deformParams); - - { - int index = 0; - - if (deformGen != DGEN_NONE) - index |= FOGDEF_USE_DEFORM_VERTEXES; - - if (glState.vertexAttribsInterpolation) - index |= FOGDEF_USE_VERTEX_ANIMATION; - - sp = &tr.fogShader[index]; - } - - backEnd.pc.c_fogDraws++; - - GLSL_BindProgram(sp); - - fog = tr.world->fogs + tess.fogNum; - - GLSL_SetUniformMatrix16(sp, FOGPASS_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); - - GLSL_SetUniformFloat(sp, FOGPASS_UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); - - GLSL_SetUniformInt(sp, FOGPASS_UNIFORM_DEFORMGEN, deformGen); - if (deformGen != DGEN_NONE) - { - GLSL_SetUniformFloat5(sp, FOGPASS_UNIFORM_DEFORMPARAMS, deformParams); - GLSL_SetUniformFloat(sp, FOGPASS_UNIFORM_TIME, tess.shaderTime); - } - - color[0] = ((unsigned char *)(&fog->colorInt))[0] / 255.0f; - color[1] = ((unsigned char *)(&fog->colorInt))[1] / 255.0f; - color[2] = ((unsigned char *)(&fog->colorInt))[2] / 255.0f; - color[3] = ((unsigned char *)(&fog->colorInt))[3] / 255.0f; - GLSL_SetUniformVec4(sp, FOGPASS_UNIFORM_COLOR, color); - - ComputeFogValues(fogDistanceVector, fogDepthVector, &eyeT); - - GLSL_SetUniformVec4(sp, FOGPASS_UNIFORM_FOGDISTANCE, fogDistanceVector); - GLSL_SetUniformVec4(sp, FOGPASS_UNIFORM_FOGDEPTH, fogDepthVector); - GLSL_SetUniformFloat(sp, FOGPASS_UNIFORM_FOGEYET, eyeT); - - if ( tess.shader->fogPass == FP_EQUAL ) { - GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_EQUAL ); - } else { - GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); - } - - if (tess.multiDrawPrimitives) - { - shaderCommands_t *input = &tess; - R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawMinIndex, input->multiDrawMaxIndex, input->multiDrawNumIndexes, input->multiDrawFirstIndex); - } - else - { - R_DrawElementsVBO(tess.numIndexes, tess.firstIndex, tess.minIndex, tess.maxIndex); - } -} - - -static unsigned int RB_CalcShaderVertexAttribs( shaderCommands_t *input ) -{ - unsigned int vertexAttribs = input->shader->vertexAttribs; - - if(glState.vertexAttribsInterpolation > 0.0f) - { - vertexAttribs |= ATTR_POSITION2; - if (vertexAttribs & ATTR_NORMAL) - { - vertexAttribs |= ATTR_NORMAL2; -#ifdef USE_VERT_TANGENT_SPACE - vertexAttribs |= ATTR_TANGENT2; - vertexAttribs |= ATTR_BITANGENT2; -#endif - } - } - - return vertexAttribs; -} - -static void RB_IterateStagesGeneric( shaderCommands_t *input ) -{ - int stage; - matrix_t matrix; - - vec4_t fogDistanceVector, fogDepthVector = {0, 0, 0, 0}; - float eyeT = 0; - - int deformGen; - vec5_t deformParams; - - ComputeDeformValues(&deformGen, deformParams); - - ComputeFogValues(fogDistanceVector, fogDepthVector, &eyeT); - - for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) - { - shaderStage_t *pStage = input->xstages[stage]; - shaderProgram_t *sp; - - if ( !pStage ) - { - break; - } - - if (backEnd.depthFill) - { - if (pStage->glslShaderGroup) - { - int index = 0; - - if (backEnd.currentEntity && backEnd.currentEntity != &tr.worldEntity) - { - index |= LIGHTDEF_ENTITY; - } - - if (pStage->stateBits & GLS_ATEST_BITS) - { - index |= LIGHTDEF_USE_TCGEN_AND_TCMOD; - } - - sp = &pStage->glslShaderGroup[index]; - } - else - { - int shaderAttribs = 0; - - if (tess.shader->numDeforms && !ShaderRequiresCPUDeforms(tess.shader)) - { - shaderAttribs |= GENERICDEF_USE_DEFORM_VERTEXES; - } - - if (glState.vertexAttribsInterpolation > 0.0f && backEnd.currentEntity && backEnd.currentEntity != &tr.worldEntity) - { - shaderAttribs |= GENERICDEF_USE_VERTEX_ANIMATION; - } - - if (pStage->stateBits & GLS_ATEST_BITS) - { - shaderAttribs |= GENERICDEF_USE_TCGEN_AND_TCMOD; - } - - sp = &tr.genericShader[shaderAttribs]; - } - } - else if (pStage->glslShaderGroup) - { - int index = pStage->glslShaderIndex; - - if (backEnd.currentEntity && backEnd.currentEntity != &tr.worldEntity) - { - index |= LIGHTDEF_ENTITY; - } - - if (r_lightmap->integer && index & LIGHTDEF_USE_LIGHTMAP) - { - index = LIGHTDEF_USE_LIGHTMAP; - } - - sp = &pStage->glslShaderGroup[index]; - - if (pStage->glslShaderGroup == tr.lightallShader) - { - backEnd.pc.c_lightallDraws++; - } - } - else - { - sp = GLSL_GetGenericShaderProgram(stage); - - backEnd.pc.c_genericDraws++; - } - - GLSL_BindProgram(sp); - - GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); - GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_VIEWORIGIN, backEnd.viewParms.or.origin); - - GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); - - GLSL_SetUniformInt(sp, GENERIC_UNIFORM_DEFORMGEN, deformGen); - if (deformGen != DGEN_NONE) - { - GLSL_SetUniformFloat5(sp, GENERIC_UNIFORM_DEFORMPARAMS, deformParams); - GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_TIME, tess.shaderTime); - } - - if ( input->fogNum ) { - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGDISTANCE, fogDistanceVector); - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGDEPTH, fogDepthVector); - GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_FOGEYET, eyeT); - } - - GL_State( pStage->stateBits ); - - { - vec4_t baseColor; - vec4_t vertColor; - qboolean tint = qtrue; - int stage2; - - ComputeShaderColors(pStage, baseColor, vertColor); - - for ( stage2 = stage + 1; stage2 < MAX_SHADER_STAGES; stage2++ ) - { - shaderStage_t *pStage2 = input->xstages[stage2]; - unsigned int srcBlendBits; - //unsigned int dstBlendBits; - - if ( !pStage2 ) - { - break; - } - - srcBlendBits = pStage2->stateBits & GLS_SRCBLEND_BITS; - //dstBlendBits = pStage2->stateBits & GLS_DSTBLEND_BITS; - - if (srcBlendBits == GLS_SRCBLEND_DST_COLOR) - { - tint = qfalse; - break; - } - } - - if (!((tr.sunShadows || r_forceSun->integer) && tess.shader->sort <= SS_OPAQUE - && !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) && tess.xstages[0]->glslShaderGroup == tr.lightallShader)) - { - tint = qfalse; - } - - if (tint) - { - // use VectorScale to only scale first three values, not alpha - VectorScale(baseColor, backEnd.refdef.colorScale, baseColor); - VectorScale(vertColor, backEnd.refdef.colorScale, vertColor); - } - - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_BASECOLOR, baseColor); - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_VERTCOLOR, vertColor); - } - - if (pStage->rgbGen == CGEN_LIGHTING_DIFFUSE) - { - vec4_t vec; - - VectorScale(backEnd.currentEntity->ambientLight, 1.0f / 255.0f, vec); - GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_AMBIENTLIGHT, vec); - - VectorScale(backEnd.currentEntity->directedLight, 1.0f / 255.0f, vec); - GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_DIRECTEDLIGHT, vec); - - VectorCopy(backEnd.currentEntity->lightDir, vec); - vec[3] = 0.0f; - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_LIGHTORIGIN, vec); - - GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_LIGHTRADIUS, 999999.0f); - } - - if (pStage->alphaGen == AGEN_PORTAL) - { - GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_PORTALRANGE, tess.shader->portalRange); - } - - GLSL_SetUniformInt(sp, GENERIC_UNIFORM_COLORGEN, pStage->rgbGen); - GLSL_SetUniformInt(sp, GENERIC_UNIFORM_ALPHAGEN, pStage->alphaGen); - - if ( input->fogNum ) - { - vec4_t fogColorMask; - - ComputeFogColorMask(pStage, fogColorMask); - - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGCOLORMASK, fogColorMask); - } - - ComputeTexMatrix( pStage, TB_DIFFUSEMAP, matrix ); - - { - vec4_t vector; - VectorSet4(vector, matrix[0], matrix[1], matrix[4], matrix[5]); - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_DIFFUSETEXMATRIX, vector); - - VectorSet4(vector, matrix[8], matrix[9], matrix[12], matrix[13]); - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_DIFFUSETEXOFFTURB, vector); - } - - GLSL_SetUniformInt(sp, GENERIC_UNIFORM_TCGEN0, pStage->bundle[0].tcGen); - if (pStage->bundle[0].tcGen == TCGEN_VECTOR) - { - vec3_t vec; - - VectorCopy(pStage->bundle[0].tcGenVectors[0], vec); - GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_TCGEN0VECTOR0, vec); - VectorCopy(pStage->bundle[0].tcGenVectors[1], vec); - GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_TCGEN0VECTOR1, vec); - } - - GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELMATRIX, backEnd.or.transformMatrix); - - GLSL_SetUniformVec2(sp, GENERIC_UNIFORM_MATERIALINFO, pStage->materialInfo); - - //GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_MAPLIGHTSCALE, backEnd.refdef.mapLightScale); - - // - // do multitexture - // - if ( backEnd.depthFill ) - { - if (!(pStage->stateBits & GLS_ATEST_BITS)) - GL_BindToTMU( tr.whiteImage, 0 ); - else if ( pStage->bundle[TB_COLORMAP].image[0] != 0 ) - R_BindAnimatedImageToTMU( &pStage->bundle[TB_COLORMAP], TB_COLORMAP ); - } - else if ( pStage->glslShaderGroup ) - { - int i; - - if ((r_lightmap->integer == 1 || r_lightmap->integer == 2) && pStage->bundle[TB_LIGHTMAP].image[0]) - { - for (i = 0; i < NUM_TEXTURE_BUNDLES; i++) - { - if (i == TB_LIGHTMAP) - { - R_BindAnimatedImageToTMU( &pStage->bundle[i], i); - } - else if (pStage->bundle[i].image[0]) - { - GL_BindToTMU( tr.whiteImage, i); - } - } - } - else if (r_lightmap->integer == 3 && pStage->bundle[TB_DELUXEMAP].image[0]) - { - for (i = 0; i < NUM_TEXTURE_BUNDLES; i++) - { - if (i == TB_LIGHTMAP) - { - R_BindAnimatedImageToTMU( &pStage->bundle[TB_DELUXEMAP], i); - } - else if (pStage->bundle[i].image[0]) - { - GL_BindToTMU( tr.whiteImage, i); - } - } - } - else - { - for (i = 0; i < NUM_TEXTURE_BUNDLES; i++) - { - if (pStage->bundle[i].image[0]) - { - R_BindAnimatedImageToTMU( &pStage->bundle[i], i); - } - } - } - } - else if ( pStage->bundle[1].image[0] != 0 ) - { - R_BindAnimatedImageToTMU( &pStage->bundle[0], 0 ); - - // - // lightmap/secondary pass - // - if ( r_lightmap->integer ) { - GLSL_SetUniformInt(sp, GENERIC_UNIFORM_TEXTURE1ENV, GL_REPLACE); - } else { - GLSL_SetUniformInt(sp, GENERIC_UNIFORM_TEXTURE1ENV, tess.shader->multitextureEnv); - } - - R_BindAnimatedImageToTMU( &pStage->bundle[1], 1 ); - } - else - { - // - // set state - // - if ( pStage->bundle[0].vertexLightmap && ( (r_vertexLight->integer && !r_uiFullScreen->integer) || glConfig.hardwareType == GLHW_PERMEDIA2 ) && r_lightmap->integer ) - { - GL_BindToTMU( tr.whiteImage, 0 ); - } - else - R_BindAnimatedImageToTMU( &pStage->bundle[0], 0 ); - - GLSL_SetUniformInt(sp, GENERIC_UNIFORM_TEXTURE1ENV, 0); - } - - // - // draw - // - if (input->multiDrawPrimitives) - { - R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawMinIndex, input->multiDrawMaxIndex, input->multiDrawNumIndexes, input->multiDrawFirstIndex); - } - else - { - R_DrawElementsVBO(input->numIndexes, input->firstIndex, input->minIndex, input->maxIndex); - } - - // allow skipping out to show just lightmaps during development - if ( r_lightmap->integer && ( pStage->bundle[0].isLightmap || pStage->bundle[1].isLightmap || pStage->bundle[0].vertexLightmap ) ) - { - break; - } - - if (backEnd.depthFill) - break; - } -} - - -static void RB_RenderShadowmap( shaderCommands_t *input ) -{ - int deformGen; - vec5_t deformParams; - - ComputeDeformValues(&deformGen, deformParams); - - { - shaderProgram_t *sp = &tr.shadowmapShader; - - vec4_t vector; - - GLSL_BindProgram(sp); - - GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); - - GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELMATRIX, backEnd.or.transformMatrix); - - GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); - - GLSL_SetUniformInt(sp, GENERIC_UNIFORM_DEFORMGEN, deformGen); - if (deformGen != DGEN_NONE) - { - GLSL_SetUniformFloat5(sp, GENERIC_UNIFORM_DEFORMPARAMS, deformParams); - GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_TIME, tess.shaderTime); - } - - VectorCopy(backEnd.viewParms.or.origin, vector); - vector[3] = 1.0f; - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_LIGHTORIGIN, vector); - GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_LIGHTRADIUS, backEnd.viewParms.zFar); - - GL_State( 0 ); - - // - // do multitexture - // - //if ( pStage->glslShaderGroup ) - { - // - // draw - // - - if (input->multiDrawPrimitives) - { - R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawMinIndex, input->multiDrawMaxIndex, input->multiDrawNumIndexes, input->multiDrawFirstIndex); - } - else - { - R_DrawElementsVBO(input->numIndexes, input->firstIndex, input->minIndex, input->maxIndex); - } - } - } -} - - - -/* -** RB_StageIteratorGeneric -*/ -void RB_StageIteratorGeneric( void ) -{ - shaderCommands_t *input; - unsigned int vertexAttribs = 0; - - input = &tess; - - if (!input->numVertexes || !input->numIndexes) - { - return; - } - - if (tess.useInternalVBO) - { - RB_DeformTessGeometry(); - } - - vertexAttribs = RB_CalcShaderVertexAttribs( input ); - - if (tess.useInternalVBO) - { - RB_UpdateVBOs(vertexAttribs); - } - else - { - backEnd.pc.c_staticVboDraws++; - } - - // - // log this call - // - if ( r_logFile->integer ) - { - // don't just call LogComment, or we will get - // a call to va() every frame! - GLimp_LogComment( va("--- RB_StageIteratorGeneric( %s ) ---\n", tess.shader->name) ); - } - - // - // set face culling appropriately - // - if ((backEnd.viewParms.flags & VPF_DEPTHSHADOW)) - { - //GL_Cull( CT_TWO_SIDED ); - - if (input->shader->cullType == CT_TWO_SIDED) - GL_Cull( CT_TWO_SIDED ); - else if (input->shader->cullType == CT_FRONT_SIDED) - GL_Cull( CT_BACK_SIDED ); - else - GL_Cull( CT_FRONT_SIDED ); - - } - else - GL_Cull( input->shader->cullType ); - - // set polygon offset if necessary - if ( input->shader->polygonOffset ) - { - qglEnable( GL_POLYGON_OFFSET_FILL ); - qglPolygonOffset( r_offsetFactor->value, r_offsetUnits->value ); - } - - // - // Set vertex attribs and pointers - // - GLSL_VertexAttribsState(vertexAttribs); - - // - // render depth if in depthfill mode - // - if (backEnd.depthFill) - { - RB_IterateStagesGeneric( input ); - - // - // reset polygon offset - // - if ( input->shader->polygonOffset ) - { - qglDisable( GL_POLYGON_OFFSET_FILL ); - } - - return; - } - - // - // render shadowmap if in shadowmap mode - // - if (backEnd.viewParms.flags & VPF_SHADOWMAP) - { - if ( input->shader->sort == SS_OPAQUE ) - { - RB_RenderShadowmap( input ); - } - // - // reset polygon offset - // - if ( input->shader->polygonOffset ) - { - qglDisable( GL_POLYGON_OFFSET_FILL ); - } - - return; - } - - // - // - // call shader function - // - RB_IterateStagesGeneric( input ); - - // - // pshadows! - // - if (glRefConfig.framebufferObject && tess.pshadowBits && tess.shader->sort <= SS_OPAQUE - && !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) ) { - ProjectPshadowVBOGLSL(); - } - - - // - // now do any dynamic lighting needed - // - if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE - && !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) ) { - if (tess.shader->numUnfoggedPasses == 1 && tess.xstages[0]->glslShaderGroup == tr.lightallShader - && (tess.xstages[0]->glslShaderIndex & LIGHTDEF_LIGHTTYPE_MASK) && r_dlightMode->integer) - { - ForwardDlight(); - } - else - { - ProjectDlightTexture(); - } - } - - if ((backEnd.viewParms.flags & VPF_USESUNLIGHT) && tess.shader->sort <= SS_OPAQUE - //if ((tr.sunShadows || r_forceSunlight->value > 0.0f) && tess.shader->sort <= SS_OPAQUE - && !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) && tess.xstages[0]->glslShaderGroup == tr.lightallShader) { - ForwardSunlight(); - } - - // - // now do fog - // - if ( tess.fogNum && tess.shader->fogPass ) { - RB_FogPass(); - } - - // - // reset polygon offset - // - if ( input->shader->polygonOffset ) - { - qglDisable( GL_POLYGON_OFFSET_FILL ); - } -} - - -/* -** RB_EndSurface -*/ -void RB_EndSurface( void ) { - shaderCommands_t *input; - - input = &tess; - - if (input->numIndexes == 0 || input->numVertexes == 0) { - return; - } - - if (input->indexes[SHADER_MAX_INDEXES-1] != 0) { - ri.Error (ERR_DROP, "RB_EndSurface() - SHADER_MAX_INDEXES hit"); - } - if (input->xyz[SHADER_MAX_VERTEXES-1][0] != 0) { - ri.Error (ERR_DROP, "RB_EndSurface() - SHADER_MAX_VERTEXES hit"); - } - - if ( tess.shader == tr.shadowShader ) { - RB_ShadowTessEnd(); - return; - } - - // for debugging of sort order issues, stop rendering after a given sort value - if ( r_debugSort->integer && r_debugSort->integer < tess.shader->sort ) { - return; - } - - // - // update performance counters - // - backEnd.pc.c_shaders++; - backEnd.pc.c_vertexes += tess.numVertexes; - backEnd.pc.c_indexes += tess.numIndexes; - backEnd.pc.c_totalIndexes += tess.numIndexes * tess.numPasses; - - // - // call off to shader specific tess end function - // - tess.currentStageIteratorFunc(); - - // - // draw debugging stuff - // - if ( r_showtris->integer ) { - DrawTris (input); - } - if ( r_shownormals->integer ) { - DrawNormals (input); - } - // clear shader so we can tell we don't have any unclosed surfaces - tess.numIndexes = 0; - tess.numVertexes = 0; - tess.firstIndex = 0; - tess.multiDrawPrimitives = 0; - - GLimp_LogComment( "----------\n" ); -} diff --git a/src/rend2/tr_shade_calc.c b/src/rend2/tr_shade_calc.c deleted file mode 100644 index 9421f646..00000000 --- a/src/rend2/tr_shade_calc.c +++ /dev/null @@ -1,1339 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_shade_calc.c - -#include "tr_local.h" -#if idppc_altivec && !defined(MACOS_X) -#include -#endif - - -#define WAVEVALUE( table, base, amplitude, phase, freq ) ((base) + table[ ri.ftol( ( ( (phase) + tess.shaderTime * (freq) ) * FUNCTABLE_SIZE ) ) & FUNCTABLE_MASK ] * (amplitude)) - -static float *TableForFunc( genFunc_t func ) -{ - switch ( func ) - { - case GF_SIN: - return tr.sinTable; - case GF_TRIANGLE: - return tr.triangleTable; - case GF_SQUARE: - return tr.squareTable; - case GF_SAWTOOTH: - return tr.sawToothTable; - case GF_INVERSE_SAWTOOTH: - return tr.inverseSawToothTable; - case GF_NONE: - default: - break; - } - - ri.Error( ERR_DROP, "TableForFunc called with invalid function '%d' in shader '%s'", func, tess.shader->name ); - return NULL; -} - -/* -** EvalWaveForm -** -** Evaluates a given waveForm_t, referencing backEnd.refdef.time directly -*/ -static float EvalWaveForm( const waveForm_t *wf ) -{ - float *table; - - table = TableForFunc( wf->func ); - - return WAVEVALUE( table, wf->base, wf->amplitude, wf->phase, wf->frequency ); -} - -static float EvalWaveFormClamped( const waveForm_t *wf ) -{ - float glow = EvalWaveForm( wf ); - - if ( glow < 0 ) - { - return 0; - } - - if ( glow > 1 ) - { - return 1; - } - - return glow; -} - -/* -** RB_CalcStretchTexCoords -*/ -void RB_CalcStretchTexCoords( const waveForm_t *wf, float *st ) -{ - float p; - texModInfo_t tmi; - - p = 1.0f / EvalWaveForm( wf ); - - tmi.matrix[0][0] = p; - tmi.matrix[1][0] = 0; - tmi.translate[0] = 0.5f - 0.5f * p; - - tmi.matrix[0][1] = 0; - tmi.matrix[1][1] = p; - tmi.translate[1] = 0.5f - 0.5f * p; - - RB_CalcTransformTexCoords( &tmi, st ); -} - -void RB_CalcStretchTexMatrix( const waveForm_t *wf, float *matrix ) -{ - float p; - texModInfo_t tmi; - - p = 1.0f / EvalWaveForm( wf ); - - tmi.matrix[0][0] = p; - tmi.matrix[1][0] = 0; - tmi.translate[0] = 0.5f - 0.5f * p; - - tmi.matrix[0][1] = 0; - tmi.matrix[1][1] = p; - tmi.translate[1] = 0.5f - 0.5f * p; - - RB_CalcTransformTexMatrix( &tmi, matrix ); -} - -/* -==================================================================== - -DEFORMATIONS - -==================================================================== -*/ - -/* -======================== -RB_CalcDeformVertexes - -======================== -*/ -void RB_CalcDeformVertexes( deformStage_t *ds ) -{ - int i; - vec3_t offset; - float scale; - float *xyz = ( float * ) tess.xyz; - float *normal = ( float * ) tess.normal; - float *table; - - if ( ds->deformationWave.frequency == 0 ) - { - scale = EvalWaveForm( &ds->deformationWave ); - - for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 ) - { - VectorScale( normal, scale, offset ); - - xyz[0] += offset[0]; - xyz[1] += offset[1]; - xyz[2] += offset[2]; - } - } - else - { - table = TableForFunc( ds->deformationWave.func ); - - for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 ) - { - float off = ( xyz[0] + xyz[1] + xyz[2] ) * ds->deformationSpread; - - scale = WAVEVALUE( table, ds->deformationWave.base, - ds->deformationWave.amplitude, - ds->deformationWave.phase + off, - ds->deformationWave.frequency ); - - VectorScale( normal, scale, offset ); - - xyz[0] += offset[0]; - xyz[1] += offset[1]; - xyz[2] += offset[2]; - } - } -} - -/* -========================= -RB_CalcDeformNormals - -Wiggle the normals for wavy environment mapping -========================= -*/ -void RB_CalcDeformNormals( deformStage_t *ds ) { - int i; - float scale; - float *xyz = ( float * ) tess.xyz; - float *normal = ( float * ) tess.normal; - - for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 ) { - scale = 0.98f; - scale = R_NoiseGet4f( xyz[0] * scale, xyz[1] * scale, xyz[2] * scale, - tess.shaderTime * ds->deformationWave.frequency ); - normal[ 0 ] += ds->deformationWave.amplitude * scale; - - scale = 0.98f; - scale = R_NoiseGet4f( 100 + xyz[0] * scale, xyz[1] * scale, xyz[2] * scale, - tess.shaderTime * ds->deformationWave.frequency ); - normal[ 1 ] += ds->deformationWave.amplitude * scale; - - scale = 0.98f; - scale = R_NoiseGet4f( 200 + xyz[0] * scale, xyz[1] * scale, xyz[2] * scale, - tess.shaderTime * ds->deformationWave.frequency ); - normal[ 2 ] += ds->deformationWave.amplitude * scale; - - VectorNormalizeFast( normal ); - } -} - -/* -======================== -RB_CalcBulgeVertexes - -======================== -*/ -void RB_CalcBulgeVertexes( deformStage_t *ds ) { - int i; - const float *st = ( const float * ) tess.texCoords[0]; - float *xyz = ( float * ) tess.xyz; - float *normal = ( float * ) tess.normal; - float now; - - now = backEnd.refdef.time * ds->bulgeSpeed * 0.001f; - - for ( i = 0; i < tess.numVertexes; i++, xyz += 4, st += 4, normal += 4 ) { - int off; - float scale; - - off = (float)( FUNCTABLE_SIZE / (M_PI*2) ) * ( st[0] * ds->bulgeWidth + now ); - - scale = tr.sinTable[ off & FUNCTABLE_MASK ] * ds->bulgeHeight; - - xyz[0] += normal[0] * scale; - xyz[1] += normal[1] * scale; - xyz[2] += normal[2] * scale; - } -} - - -/* -====================== -RB_CalcMoveVertexes - -A deformation that can move an entire surface along a wave path -====================== -*/ -void RB_CalcMoveVertexes( deformStage_t *ds ) { - int i; - float *xyz; - float *table; - float scale; - vec3_t offset; - - table = TableForFunc( ds->deformationWave.func ); - - scale = WAVEVALUE( table, ds->deformationWave.base, - ds->deformationWave.amplitude, - ds->deformationWave.phase, - ds->deformationWave.frequency ); - - VectorScale( ds->moveVector, scale, offset ); - - xyz = ( float * ) tess.xyz; - for ( i = 0; i < tess.numVertexes; i++, xyz += 4 ) { - VectorAdd( xyz, offset, xyz ); - } -} - - -/* -============= -DeformText - -Change a polygon into a bunch of text polygons -============= -*/ -void DeformText( const char *text ) { - int i; - vec3_t origin, width, height; - int len; - int ch; - float color[4]; - float bottom, top; - vec3_t mid; - - height[0] = 0; - height[1] = 0; - height[2] = -1; - CrossProduct( tess.normal[0], height, width ); - - // find the midpoint of the box - VectorClear( mid ); - bottom = 999999; - top = -999999; - for ( i = 0 ; i < 4 ; i++ ) { - VectorAdd( tess.xyz[i], mid, mid ); - if ( tess.xyz[i][2] < bottom ) { - bottom = tess.xyz[i][2]; - } - if ( tess.xyz[i][2] > top ) { - top = tess.xyz[i][2]; - } - } - VectorScale( mid, 0.25f, origin ); - - // determine the individual character size - height[0] = 0; - height[1] = 0; - height[2] = ( top - bottom ) * 0.5f; - - VectorScale( width, height[2] * -0.75f, width ); - - // determine the starting position - len = strlen( text ); - VectorMA( origin, (len-1), width, origin ); - - // clear the shader indexes - tess.numIndexes = 0; - tess.numVertexes = 0; - tess.firstIndex = 0; - - color[0] = color[1] = color[2] = color[3] = 1.0f; - - // draw each character - for ( i = 0 ; i < len ; i++ ) { - ch = text[i]; - ch &= 255; - - if ( ch != ' ' ) { - int row, col; - float frow, fcol, size; - - row = ch>>4; - col = ch&15; - - frow = row*0.0625f; - fcol = col*0.0625f; - size = 0.0625f; - - RB_AddQuadStampExt( origin, width, height, color, fcol, frow, fcol + size, frow + size ); - } - VectorMA( origin, -2, width, origin ); - } -} - -/* -================== -GlobalVectorToLocal -================== -*/ -static void GlobalVectorToLocal( const vec3_t in, vec3_t out ) { - out[0] = DotProduct( in, backEnd.or.axis[0] ); - out[1] = DotProduct( in, backEnd.or.axis[1] ); - out[2] = DotProduct( in, backEnd.or.axis[2] ); -} - -/* -===================== -AutospriteDeform - -Assuming all the triangles for this shader are independant -quads, rebuild them as forward facing sprites -===================== -*/ -static void AutospriteDeform( void ) { - int i; - int oldVerts; - float *xyz; - vec3_t mid, delta; - float radius; - vec3_t left, up; - vec3_t leftDir, upDir; - - if ( tess.numVertexes & 3 ) { - ri.Printf( PRINT_WARNING, "Autosprite shader %s had odd vertex count\n", tess.shader->name ); - } - if ( tess.numIndexes != ( tess.numVertexes >> 2 ) * 6 ) { - ri.Printf( PRINT_WARNING, "Autosprite shader %s had odd index count\n", tess.shader->name ); - } - - oldVerts = tess.numVertexes; - tess.numVertexes = 0; - tess.numIndexes = 0; - tess.firstIndex = 0; - - if ( backEnd.currentEntity != &tr.worldEntity ) { - GlobalVectorToLocal( backEnd.viewParms.or.axis[1], leftDir ); - GlobalVectorToLocal( backEnd.viewParms.or.axis[2], upDir ); - } else { - VectorCopy( backEnd.viewParms.or.axis[1], leftDir ); - VectorCopy( backEnd.viewParms.or.axis[2], upDir ); - } - - for ( i = 0 ; i < oldVerts ; i+=4 ) { - // find the midpoint - xyz = tess.xyz[i]; - - mid[0] = 0.25f * (xyz[0] + xyz[4] + xyz[8] + xyz[12]); - mid[1] = 0.25f * (xyz[1] + xyz[5] + xyz[9] + xyz[13]); - mid[2] = 0.25f * (xyz[2] + xyz[6] + xyz[10] + xyz[14]); - - VectorSubtract( xyz, mid, delta ); - radius = VectorLength( delta ) * 0.707f; // / sqrt(2) - - VectorScale( leftDir, radius, left ); - VectorScale( upDir, radius, up ); - - if ( backEnd.viewParms.isMirror ) { - VectorSubtract( vec3_origin, left, left ); - } - - // compensate for scale in the axes if necessary - if ( backEnd.currentEntity->e.nonNormalizedAxes ) { - float axisLength; - axisLength = VectorLength( backEnd.currentEntity->e.axis[0] ); - if ( !axisLength ) { - axisLength = 0; - } else { - axisLength = 1.0f / axisLength; - } - VectorScale(left, axisLength, left); - VectorScale(up, axisLength, up); - } - - RB_AddQuadStamp( mid, left, up, tess.vertexColors[i] ); - } -} - - -/* -===================== -Autosprite2Deform - -Autosprite2 will pivot a rectangular quad along the center of its long axis -===================== -*/ -int edgeVerts[6][2] = { - { 0, 1 }, - { 0, 2 }, - { 0, 3 }, - { 1, 2 }, - { 1, 3 }, - { 2, 3 } -}; - -static void Autosprite2Deform( void ) { - int i, j, k; - int indexes; - float *xyz; - vec3_t forward; - - if ( tess.numVertexes & 3 ) { - ri.Printf( PRINT_WARNING, "Autosprite2 shader %s had odd vertex count", tess.shader->name ); - } - if ( tess.numIndexes != ( tess.numVertexes >> 2 ) * 6 ) { - ri.Printf( PRINT_WARNING, "Autosprite2 shader %s had odd index count", tess.shader->name ); - } - - if ( backEnd.currentEntity != &tr.worldEntity ) { - GlobalVectorToLocal( backEnd.viewParms.or.axis[0], forward ); - } else { - VectorCopy( backEnd.viewParms.or.axis[0], forward ); - } - - // this is a lot of work for two triangles... - // we could precalculate a lot of it is an issue, but it would mess up - // the shader abstraction - for ( i = 0, indexes = 0 ; i < tess.numVertexes ; i+=4, indexes+=6 ) { - float lengths[2]; - int nums[2]; - vec3_t mid[2]; - vec3_t major, minor; - float *v1, *v2; - - // find the midpoint - xyz = tess.xyz[i]; - - // identify the two shortest edges - nums[0] = nums[1] = 0; - lengths[0] = lengths[1] = 999999; - - for ( j = 0 ; j < 6 ; j++ ) { - float l; - vec3_t temp; - - v1 = xyz + 4 * edgeVerts[j][0]; - v2 = xyz + 4 * edgeVerts[j][1]; - - VectorSubtract( v1, v2, temp ); - - l = DotProduct( temp, temp ); - if ( l < lengths[0] ) { - nums[1] = nums[0]; - lengths[1] = lengths[0]; - nums[0] = j; - lengths[0] = l; - } else if ( l < lengths[1] ) { - nums[1] = j; - lengths[1] = l; - } - } - - for ( j = 0 ; j < 2 ; j++ ) { - v1 = xyz + 4 * edgeVerts[nums[j]][0]; - v2 = xyz + 4 * edgeVerts[nums[j]][1]; - - mid[j][0] = 0.5f * (v1[0] + v2[0]); - mid[j][1] = 0.5f * (v1[1] + v2[1]); - mid[j][2] = 0.5f * (v1[2] + v2[2]); - } - - // find the vector of the major axis - VectorSubtract( mid[1], mid[0], major ); - - // cross this with the view direction to get minor axis - CrossProduct( major, forward, minor ); - VectorNormalize( minor ); - - // re-project the points - for ( j = 0 ; j < 2 ; j++ ) { - float l; - - v1 = xyz + 4 * edgeVerts[nums[j]][0]; - v2 = xyz + 4 * edgeVerts[nums[j]][1]; - - l = 0.5 * sqrt( lengths[j] ); - - // we need to see which direction this edge - // is used to determine direction of projection - for ( k = 0 ; k < 5 ; k++ ) { - if ( tess.indexes[ indexes + k ] == i + edgeVerts[nums[j]][0] - && tess.indexes[ indexes + k + 1 ] == i + edgeVerts[nums[j]][1] ) { - break; - } - } - - if ( k == 5 ) { - VectorMA( mid[j], l, minor, v1 ); - VectorMA( mid[j], -l, minor, v2 ); - } else { - VectorMA( mid[j], -l, minor, v1 ); - VectorMA( mid[j], l, minor, v2 ); - } - } - } -} - - -/* -===================== -RB_DeformTessGeometry - -===================== -*/ -void RB_DeformTessGeometry( void ) { - int i; - deformStage_t *ds; - - if(!ShaderRequiresCPUDeforms(tess.shader)) - { - // we don't need the following CPU deforms - return; - } - - for ( i = 0 ; i < tess.shader->numDeforms ; i++ ) { - ds = &tess.shader->deforms[ i ]; - - switch ( ds->deformation ) { - case DEFORM_NONE: - break; - case DEFORM_NORMALS: - RB_CalcDeformNormals( ds ); - break; - case DEFORM_WAVE: - RB_CalcDeformVertexes( ds ); - break; - case DEFORM_BULGE: - RB_CalcBulgeVertexes( ds ); - break; - case DEFORM_MOVE: - RB_CalcMoveVertexes( ds ); - break; - case DEFORM_PROJECTION_SHADOW: - RB_ProjectionShadowDeform(); - break; - case DEFORM_AUTOSPRITE: - AutospriteDeform(); - break; - case DEFORM_AUTOSPRITE2: - Autosprite2Deform(); - break; - case DEFORM_TEXT0: - case DEFORM_TEXT1: - case DEFORM_TEXT2: - case DEFORM_TEXT3: - case DEFORM_TEXT4: - case DEFORM_TEXT5: - case DEFORM_TEXT6: - case DEFORM_TEXT7: - DeformText( backEnd.refdef.text[ds->deformation - DEFORM_TEXT0] ); - break; - } - } -} - -/* -==================================================================== - -COLORS - -==================================================================== -*/ - - -/* -** RB_CalcColorFromEntity -*/ -void RB_CalcColorFromEntity( unsigned char *dstColors ) -{ - int i; - int *pColors = ( int * ) dstColors; - int c; - - if ( !backEnd.currentEntity ) - return; - - c = * ( int * ) backEnd.currentEntity->e.shaderRGBA; - - for ( i = 0; i < tess.numVertexes; i++, pColors++ ) - { - *pColors = c; - } -} - -/* -** RB_CalcColorFromOneMinusEntity -*/ -void RB_CalcColorFromOneMinusEntity( unsigned char *dstColors ) -{ - int i; - int *pColors = ( int * ) dstColors; - unsigned char invModulate[4]; - int c; - - if ( !backEnd.currentEntity ) - return; - - invModulate[0] = 255 - backEnd.currentEntity->e.shaderRGBA[0]; - invModulate[1] = 255 - backEnd.currentEntity->e.shaderRGBA[1]; - invModulate[2] = 255 - backEnd.currentEntity->e.shaderRGBA[2]; - invModulate[3] = 255 - backEnd.currentEntity->e.shaderRGBA[3]; // this trashes alpha, but the AGEN block fixes it - - c = * ( int * ) invModulate; - - for ( i = 0; i < tess.numVertexes; i++, pColors++ ) - { - *pColors = c; - } -} - -/* -** RB_CalcAlphaFromEntity -*/ -void RB_CalcAlphaFromEntity( unsigned char *dstColors ) -{ - int i; - - if ( !backEnd.currentEntity ) - return; - - dstColors += 3; - - for ( i = 0; i < tess.numVertexes; i++, dstColors += 4 ) - { - *dstColors = backEnd.currentEntity->e.shaderRGBA[3]; - } -} - -/* -** RB_CalcAlphaFromOneMinusEntity -*/ -void RB_CalcAlphaFromOneMinusEntity( unsigned char *dstColors ) -{ - int i; - - if ( !backEnd.currentEntity ) - return; - - dstColors += 3; - - for ( i = 0; i < tess.numVertexes; i++, dstColors += 4 ) - { - *dstColors = 0xff - backEnd.currentEntity->e.shaderRGBA[3]; - } -} - -/* -** RB_CalcWaveColorSingle -*/ -float RB_CalcWaveColorSingle( const waveForm_t *wf ) -{ - float glow; - - if ( wf->func == GF_NOISE ) { - glow = wf->base + R_NoiseGet4f( 0, 0, 0, ( tess.shaderTime + wf->phase ) * wf->frequency ) * wf->amplitude; - } else { - glow = EvalWaveForm( wf ) * tr.identityLight; - } - - if ( glow < 0 ) { - glow = 0; - } - else if ( glow > 1 ) { - glow = 1; - } - - return glow; -} - -/* -** RB_CalcWaveColor -*/ -void RB_CalcWaveColor( const waveForm_t *wf, unsigned char *dstColors ) -{ - int i; - int v; - float glow; - int *colors = ( int * ) dstColors; - byte color[4]; - - glow = RB_CalcWaveColorSingle( wf ); - - v = ri.ftol(255 * glow); - color[0] = color[1] = color[2] = v; - color[3] = 255; - v = *(int *)color; - - for ( i = 0; i < tess.numVertexes; i++, colors++ ) { - *colors = v; - } -} - -/* -** RB_CalcWaveAlphaSingle -*/ -float RB_CalcWaveAlphaSingle( const waveForm_t *wf ) -{ - return EvalWaveFormClamped( wf ); -} - -/* -** RB_CalcWaveAlpha -*/ -void RB_CalcWaveAlpha( const waveForm_t *wf, unsigned char *dstColors ) -{ - int i; - int v; - float glow; - - glow = EvalWaveFormClamped( wf ); - - v = 255 * glow; - - for ( i = 0; i < tess.numVertexes; i++, dstColors += 4 ) - { - dstColors[3] = v; - } -} - -/* -** RB_CalcModulateColorsByFog -*/ -void RB_CalcModulateColorsByFog( unsigned char *colors ) { - int i; - float texCoords[SHADER_MAX_VERTEXES][2]; - - // calculate texcoords so we can derive density - // this is not wasted, because it would only have - // been previously called if the surface was opaque - RB_CalcFogTexCoords( texCoords[0] ); - - for ( i = 0; i < tess.numVertexes; i++, colors += 4 ) { - float f = 1.0 - R_FogFactor( texCoords[i][0], texCoords[i][1] ); - colors[0] *= f; - colors[1] *= f; - colors[2] *= f; - } -} - -/* -** RB_CalcModulateAlphasByFog -*/ -void RB_CalcModulateAlphasByFog( unsigned char *colors ) { - int i; - float texCoords[SHADER_MAX_VERTEXES][2]; - - // calculate texcoords so we can derive density - // this is not wasted, because it would only have - // been previously called if the surface was opaque - RB_CalcFogTexCoords( texCoords[0] ); - - for ( i = 0; i < tess.numVertexes; i++, colors += 4 ) { - float f = 1.0 - R_FogFactor( texCoords[i][0], texCoords[i][1] ); - colors[3] *= f; - } -} - -/* -** RB_CalcModulateRGBAsByFog -*/ -void RB_CalcModulateRGBAsByFog( unsigned char *colors ) { - int i; - float texCoords[SHADER_MAX_VERTEXES][2]; - - // calculate texcoords so we can derive density - // this is not wasted, because it would only have - // been previously called if the surface was opaque - RB_CalcFogTexCoords( texCoords[0] ); - - for ( i = 0; i < tess.numVertexes; i++, colors += 4 ) { - float f = 1.0 - R_FogFactor( texCoords[i][0], texCoords[i][1] ); - colors[0] *= f; - colors[1] *= f; - colors[2] *= f; - colors[3] *= f; - } -} - - -/* -==================================================================== - -TEX COORDS - -==================================================================== -*/ - -/* -======================== -RB_CalcFogTexCoords - -To do the clipped fog plane really correctly, we should use -projected textures, but I don't trust the drivers and it -doesn't fit our shader data. -======================== -*/ -void RB_CalcFogTexCoords( float *st ) { - int i; - float *v; - float s, t; - float eyeT; - qboolean eyeOutside; - fog_t *fog; - vec3_t local; - vec4_t fogDistanceVector, fogDepthVector = {0, 0, 0, 0}; - - fog = tr.world->fogs + tess.fogNum; - - // all fogging distance is based on world Z units - VectorSubtract( backEnd.or.origin, backEnd.viewParms.or.origin, local ); - fogDistanceVector[0] = -backEnd.or.modelMatrix[2]; - fogDistanceVector[1] = -backEnd.or.modelMatrix[6]; - fogDistanceVector[2] = -backEnd.or.modelMatrix[10]; - fogDistanceVector[3] = DotProduct( local, backEnd.viewParms.or.axis[0] ); - - // scale the fog vectors based on the fog's thickness - fogDistanceVector[0] *= fog->tcScale; - fogDistanceVector[1] *= fog->tcScale; - fogDistanceVector[2] *= fog->tcScale; - fogDistanceVector[3] *= fog->tcScale; - - // rotate the gradient vector for this orientation - if ( fog->hasSurface ) { - fogDepthVector[0] = fog->surface[0] * backEnd.or.axis[0][0] + - fog->surface[1] * backEnd.or.axis[0][1] + fog->surface[2] * backEnd.or.axis[0][2]; - fogDepthVector[1] = fog->surface[0] * backEnd.or.axis[1][0] + - fog->surface[1] * backEnd.or.axis[1][1] + fog->surface[2] * backEnd.or.axis[1][2]; - fogDepthVector[2] = fog->surface[0] * backEnd.or.axis[2][0] + - fog->surface[1] * backEnd.or.axis[2][1] + fog->surface[2] * backEnd.or.axis[2][2]; - fogDepthVector[3] = -fog->surface[3] + DotProduct( backEnd.or.origin, fog->surface ); - - eyeT = DotProduct( backEnd.or.viewOrigin, fogDepthVector ) + fogDepthVector[3]; - } else { - eyeT = 1; // non-surface fog always has eye inside - } - - // see if the viewpoint is outside - // this is needed for clipping distance even for constant fog - - if ( eyeT < 0 ) { - eyeOutside = qtrue; - } else { - eyeOutside = qfalse; - } - - fogDistanceVector[3] += 1.0/512; - - // calculate density for each point - for (i = 0, v = tess.xyz[0] ; i < tess.numVertexes ; i++, v += 4) { - // calculate the length in fog - s = DotProduct( v, fogDistanceVector ) + fogDistanceVector[3]; - t = DotProduct( v, fogDepthVector ) + fogDepthVector[3]; - - // partially clipped fogs use the T axis - if ( eyeOutside ) { - if ( t < 1.0 ) { - t = 1.0/32; // point is outside, so no fogging - } else { - t = 1.0/32 + 30.0/32 * t / ( t - eyeT ); // cut the distance at the fog plane - } - } else { - if ( t < 0 ) { - t = 1.0/32; // point is outside, so no fogging - } else { - t = 31.0/32; - } - } - - st[0] = s; - st[1] = t; - st += 2; - } -} - - - -/* -** RB_CalcEnvironmentTexCoords -*/ -void RB_CalcEnvironmentTexCoords( float *st ) -{ - int i; - float *v, *normal; - vec3_t viewer, reflected; - float d; - - v = tess.xyz[0]; - normal = tess.normal[0]; - - for (i = 0 ; i < tess.numVertexes ; i++, v += 4, normal += 4, st += 2 ) - { - VectorSubtract (backEnd.or.viewOrigin, v, viewer); - VectorNormalizeFast (viewer); - - d = DotProduct (normal, viewer); - - reflected[0] = normal[0]*2*d - viewer[0]; - reflected[1] = normal[1]*2*d - viewer[1]; - reflected[2] = normal[2]*2*d - viewer[2]; - - st[0] = 0.5 + reflected[1] * 0.5; - st[1] = 0.5 - reflected[2] * 0.5; - } -} - -/* -** RB_CalcTurbulentTexCoords -*/ -void RB_CalcTurbulentTexCoords( const waveForm_t *wf, float *st ) -{ - int i; - float now; - - now = ( wf->phase + tess.shaderTime * wf->frequency ); - - for ( i = 0; i < tess.numVertexes; i++, st += 2 ) - { - float s = st[0]; - float t = st[1]; - - st[0] = s + tr.sinTable[ ( ( int ) ( ( ( tess.xyz[i][0] + tess.xyz[i][2] )* 1.0/128 * 0.125 + now ) * FUNCTABLE_SIZE ) ) & ( FUNCTABLE_MASK ) ] * wf->amplitude; - st[1] = t + tr.sinTable[ ( ( int ) ( ( tess.xyz[i][1] * 1.0/128 * 0.125 + now ) * FUNCTABLE_SIZE ) ) & ( FUNCTABLE_MASK ) ] * wf->amplitude; - } -} - -void RB_CalcTurbulentTexMatrix( const waveForm_t *wf, matrix_t matrix ) -{ - float now; - - now = ( wf->phase + tess.shaderTime * wf->frequency ); - - // bit of a hack here, hide amplitude and now in the matrix - // the vertex program will extract them and perform a turbulent pass last if it's nonzero - - matrix[ 0] = 1.0f; matrix[ 4] = 0.0f; matrix[ 8] = 0.0f; matrix[12] = wf->amplitude; - matrix[ 1] = 0.0f; matrix[ 5] = 1.0f; matrix[ 9] = 0.0f; matrix[13] = now; - matrix[ 2] = 0.0f; matrix[ 6] = 0.0f; matrix[10] = 1.0f; matrix[14] = 0.0f; - matrix[ 3] = 0.0f; matrix[ 7] = 0.0f; matrix[11] = 0.0f; matrix[15] = 1.0f; -} - -/* -** RB_CalcScaleTexCoords -*/ -void RB_CalcScaleTexCoords( const float scale[2], float *st ) -{ - int i; - - for ( i = 0; i < tess.numVertexes; i++, st += 2 ) - { - st[0] *= scale[0]; - st[1] *= scale[1]; - } -} - -void RB_CalcScaleTexMatrix( const float scale[2], float *matrix ) -{ - matrix[ 0] = scale[0]; matrix[ 4] = 0.0f; matrix[ 8] = 0.0f; matrix[12] = 0.0f; - matrix[ 1] = 0.0f; matrix[ 5] = scale[1]; matrix[ 9] = 0.0f; matrix[13] = 0.0f; - matrix[ 2] = 0.0f; matrix[ 6] = 0.0f; matrix[10] = 1.0f; matrix[14] = 0.0f; - matrix[ 3] = 0.0f; matrix[ 7] = 0.0f; matrix[11] = 0.0f; matrix[15] = 1.0f; -} - -/* -** RB_CalcScrollTexCoords -*/ -void RB_CalcScrollTexCoords( const float scrollSpeed[2], float *st ) -{ - int i; - float timeScale = tess.shaderTime; - float adjustedScrollS, adjustedScrollT; - - adjustedScrollS = scrollSpeed[0] * timeScale; - adjustedScrollT = scrollSpeed[1] * timeScale; - - // clamp so coordinates don't continuously get larger, causing problems - // with hardware limits - adjustedScrollS = adjustedScrollS - floor( adjustedScrollS ); - adjustedScrollT = adjustedScrollT - floor( adjustedScrollT ); - - for ( i = 0; i < tess.numVertexes; i++, st += 2 ) - { - st[0] += adjustedScrollS; - st[1] += adjustedScrollT; - } -} - -void RB_CalcScrollTexMatrix( const float scrollSpeed[2], float *matrix ) -{ - float timeScale = tess.shaderTime; - float adjustedScrollS, adjustedScrollT; - - adjustedScrollS = scrollSpeed[0] * timeScale; - adjustedScrollT = scrollSpeed[1] * timeScale; - - // clamp so coordinates don't continuously get larger, causing problems - // with hardware limits - adjustedScrollS = adjustedScrollS - floor( adjustedScrollS ); - adjustedScrollT = adjustedScrollT - floor( adjustedScrollT ); - - - matrix[ 0] = 1.0f; matrix[ 4] = 0.0f; matrix[ 8] = adjustedScrollS; matrix[12] = 0.0f; - matrix[ 1] = 0.0f; matrix[ 5] = 1.0f; matrix[ 9] = adjustedScrollT; matrix[13] = 0.0f; - matrix[ 2] = 0.0f; matrix[ 6] = 0.0f; matrix[10] = 1.0f; matrix[14] = 0.0f; - matrix[ 3] = 0.0f; matrix[ 7] = 0.0f; matrix[11] = 0.0f; matrix[15] = 1.0f; -} - -/* -** RB_CalcTransformTexCoords -*/ -void RB_CalcTransformTexCoords( const texModInfo_t *tmi, float *st ) -{ - int i; - - for ( i = 0; i < tess.numVertexes; i++, st += 2 ) - { - float s = st[0]; - float t = st[1]; - - st[0] = s * tmi->matrix[0][0] + t * tmi->matrix[1][0] + tmi->translate[0]; - st[1] = s * tmi->matrix[0][1] + t * tmi->matrix[1][1] + tmi->translate[1]; - } -} - -void RB_CalcTransformTexMatrix( const texModInfo_t *tmi, float *matrix ) -{ - matrix[ 0] = tmi->matrix[0][0]; matrix[ 4] = tmi->matrix[1][0]; matrix[ 8] = tmi->translate[0]; matrix[12] = 0.0f; - matrix[ 1] = tmi->matrix[0][1]; matrix[ 5] = tmi->matrix[1][1]; matrix[ 9] = tmi->translate[1]; matrix[13] = 0.0f; - matrix[ 2] = 0.0f; matrix[ 6] = 0.0f; matrix[10] = 1.0f; matrix[14] = 0.0f; - matrix[ 3] = 0.0f; matrix[ 7] = 0.0f; matrix[11] = 0.0f; matrix[15] = 1.0f; -} - -/* -** RB_CalcRotateTexCoords -*/ -void RB_CalcRotateTexCoords( float degsPerSecond, float *st ) -{ - float timeScale = tess.shaderTime; - float degs; - int index; - float sinValue, cosValue; - texModInfo_t tmi; - - degs = -degsPerSecond * timeScale; - index = degs * ( FUNCTABLE_SIZE / 360.0f ); - - sinValue = tr.sinTable[ index & FUNCTABLE_MASK ]; - cosValue = tr.sinTable[ ( index + FUNCTABLE_SIZE / 4 ) & FUNCTABLE_MASK ]; - - tmi.matrix[0][0] = cosValue; - tmi.matrix[1][0] = -sinValue; - tmi.translate[0] = 0.5 - 0.5 * cosValue + 0.5 * sinValue; - - tmi.matrix[0][1] = sinValue; - tmi.matrix[1][1] = cosValue; - tmi.translate[1] = 0.5 - 0.5 * sinValue - 0.5 * cosValue; - - RB_CalcTransformTexCoords( &tmi, st ); -} - -void RB_CalcRotateTexMatrix( float degsPerSecond, float *matrix ) -{ - float timeScale = tess.shaderTime; - float degs; - int index; - float sinValue, cosValue; - texModInfo_t tmi; - - degs = -degsPerSecond * timeScale; - index = degs * ( FUNCTABLE_SIZE / 360.0f ); - - sinValue = tr.sinTable[ index & FUNCTABLE_MASK ]; - cosValue = tr.sinTable[ ( index + FUNCTABLE_SIZE / 4 ) & FUNCTABLE_MASK ]; - - tmi.matrix[0][0] = cosValue; - tmi.matrix[1][0] = -sinValue; - tmi.translate[0] = 0.5 - 0.5 * cosValue + 0.5 * sinValue; - - tmi.matrix[0][1] = sinValue; - tmi.matrix[1][1] = cosValue; - tmi.translate[1] = 0.5 - 0.5 * sinValue - 0.5 * cosValue; - - RB_CalcTransformTexMatrix( &tmi, matrix ); -} -/* -** RB_CalcSpecularAlpha -** -** Calculates specular coefficient and places it in the alpha channel -*/ -vec3_t lightOrigin = { -960, 1980, 96 }; // FIXME: track dynamically - -void RB_CalcSpecularAlpha( unsigned char *alphas ) { - int i; - float *v, *normal; - vec3_t viewer, reflected; - float l, d; - int b; - vec3_t lightDir; - int numVertexes; - - v = tess.xyz[0]; - normal = tess.normal[0]; - - alphas += 3; - - numVertexes = tess.numVertexes; - for (i = 0 ; i < numVertexes ; i++, v += 4, normal += 4, alphas += 4) { - float ilength; - - VectorSubtract( lightOrigin, v, lightDir ); -// ilength = Q_rsqrt( DotProduct( lightDir, lightDir ) ); - VectorNormalizeFast( lightDir ); - - // calculate the specular color - d = DotProduct (normal, lightDir); -// d *= ilength; - - // we don't optimize for the d < 0 case since this tends to - // cause visual artifacts such as faceted "snapping" - reflected[0] = normal[0]*2*d - lightDir[0]; - reflected[1] = normal[1]*2*d - lightDir[1]; - reflected[2] = normal[2]*2*d - lightDir[2]; - - VectorSubtract (backEnd.or.viewOrigin, v, viewer); - ilength = Q_rsqrt( DotProduct( viewer, viewer ) ); - l = DotProduct (reflected, viewer); - l *= ilength; - - if (l < 0) { - b = 0; - } else { - l = l*l; - l = l*l; - b = l * 255; - if (b > 255) { - b = 255; - } - } - - *alphas = b; - } -} - -/* -** RB_CalcDiffuseColor -** -** The basic vertex lighting calc -*/ -#if idppc_altivec -static void RB_CalcDiffuseColor_altivec( unsigned char *colors ) -{ - int i; - float *v, *normal; - trRefEntity_t *ent; - int ambientLightInt; - vec3_t lightDir; - int numVertexes; - vector unsigned char vSel = VECCONST_UINT8(0x00, 0x00, 0x00, 0xff, - 0x00, 0x00, 0x00, 0xff, - 0x00, 0x00, 0x00, 0xff, - 0x00, 0x00, 0x00, 0xff); - vector float ambientLightVec; - vector float directedLightVec; - vector float lightDirVec; - vector float normalVec0, normalVec1; - vector float incomingVec0, incomingVec1, incomingVec2; - vector float zero, jVec; - vector signed int jVecInt; - vector signed short jVecShort; - vector unsigned char jVecChar, normalPerm; - ent = backEnd.currentEntity; - ambientLightInt = ent->ambientLightInt; - // A lot of this could be simplified if we made sure - // entities light info was 16-byte aligned. - jVecChar = vec_lvsl(0, ent->ambientLight); - ambientLightVec = vec_ld(0, (vector float *)ent->ambientLight); - jVec = vec_ld(11, (vector float *)ent->ambientLight); - ambientLightVec = vec_perm(ambientLightVec,jVec,jVecChar); - - jVecChar = vec_lvsl(0, ent->directedLight); - directedLightVec = vec_ld(0,(vector float *)ent->directedLight); - jVec = vec_ld(11,(vector float *)ent->directedLight); - directedLightVec = vec_perm(directedLightVec,jVec,jVecChar); - - jVecChar = vec_lvsl(0, ent->lightDir); - lightDirVec = vec_ld(0,(vector float *)ent->lightDir); - jVec = vec_ld(11,(vector float *)ent->lightDir); - lightDirVec = vec_perm(lightDirVec,jVec,jVecChar); - - zero = (vector float)vec_splat_s8(0); - VectorCopy( ent->lightDir, lightDir ); - - v = tess.xyz[0]; - normal = tess.normal[0]; - - normalPerm = vec_lvsl(0,normal); - numVertexes = tess.numVertexes; - for (i = 0 ; i < numVertexes ; i++, v += 4, normal += 4) { - normalVec0 = vec_ld(0,(vector float *)normal); - normalVec1 = vec_ld(11,(vector float *)normal); - normalVec0 = vec_perm(normalVec0,normalVec1,normalPerm); - incomingVec0 = vec_madd(normalVec0, lightDirVec, zero); - incomingVec1 = vec_sld(incomingVec0,incomingVec0,4); - incomingVec2 = vec_add(incomingVec0,incomingVec1); - incomingVec1 = vec_sld(incomingVec1,incomingVec1,4); - incomingVec2 = vec_add(incomingVec2,incomingVec1); - incomingVec0 = vec_splat(incomingVec2,0); - incomingVec0 = vec_max(incomingVec0,zero); - normalPerm = vec_lvsl(12,normal); - jVec = vec_madd(incomingVec0, directedLightVec, ambientLightVec); - jVecInt = vec_cts(jVec,0); // RGBx - jVecShort = vec_pack(jVecInt,jVecInt); // RGBxRGBx - jVecChar = vec_packsu(jVecShort,jVecShort); // RGBxRGBxRGBxRGBx - jVecChar = vec_sel(jVecChar,vSel,vSel); // RGBARGBARGBARGBA replace alpha with 255 - vec_ste((vector unsigned int)jVecChar,0,(unsigned int *)&colors[i*4]); // store color - } -} -#endif - -static void RB_CalcDiffuseColor_scalar( unsigned char *colors ) -{ - int i, j; - float *v, *normal; - float incoming; - trRefEntity_t *ent; - int ambientLightInt; - vec3_t ambientLight; - vec3_t lightDir; - vec3_t directedLight; - int numVertexes; - ent = backEnd.currentEntity; - ambientLightInt = ent->ambientLightInt; - VectorCopy( ent->ambientLight, ambientLight ); - VectorCopy( ent->directedLight, directedLight ); - VectorCopy( ent->lightDir, lightDir ); - - v = tess.xyz[0]; - normal = tess.normal[0]; - - numVertexes = tess.numVertexes; - for (i = 0 ; i < numVertexes ; i++, v += 4, normal += 4) { - incoming = DotProduct (normal, lightDir); - if ( incoming <= 0 ) { - *(int *)&colors[i*4] = ambientLightInt; - continue; - } - j = ri.ftol(ambientLight[0] + incoming * directedLight[0]); - if ( j > 255 ) { - j = 255; - } - colors[i*4+0] = j; - - j = ri.ftol(ambientLight[1] + incoming * directedLight[1]); - if ( j > 255 ) { - j = 255; - } - colors[i*4+1] = j; - - j = ri.ftol(ambientLight[2] + incoming * directedLight[2]); - if ( j > 255 ) { - j = 255; - } - colors[i*4+2] = j; - - colors[i*4+3] = 255; - } -} - -void RB_CalcDiffuseColor( unsigned char *colors ) -{ -#if idppc_altivec - if (com_altivec->integer) { - // must be in a seperate function or G3 systems will crash. - RB_CalcDiffuseColor_altivec( colors ); - return; - } -#endif - RB_CalcDiffuseColor_scalar( colors ); -} - - - - - diff --git a/src/rend2/tr_shader.c b/src/rend2/tr_shader.c deleted file mode 100644 index 8cad4b6b..00000000 --- a/src/rend2/tr_shader.c +++ /dev/null @@ -1,3741 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#include "tr_local.h" - -// tr_shader.c -- this file deals with the parsing and definition of shaders - -static char *s_shaderText; - -// the shader is parsed into these global variables, then copied into -// dynamically allocated memory if it is valid. -static shaderStage_t stages[MAX_SHADER_STAGES]; -static shader_t shader; -static texModInfo_t texMods[MAX_SHADER_STAGES][TR_MAX_TEXMODS]; - -#define FILE_HASH_SIZE 1024 -static shader_t* hashTable[FILE_HASH_SIZE]; - -#define MAX_SHADERTEXT_HASH 2048 -static char **shaderTextHashTable[MAX_SHADERTEXT_HASH]; - -/* -================ -return a hash value for the filename -================ -*/ -#ifdef __GNUCC__ - #warning TODO: check if long is ok here -#endif -static long generateHashValue( const char *fname, const int size ) { - int i; - long hash; - char letter; - - hash = 0; - i = 0; - while (fname[i] != '\0') { - letter = tolower(fname[i]); - if (letter =='.') break; // don't include extension - if (letter =='\\') letter = '/'; // damn path names - if (letter == PATH_SEP) letter = '/'; // damn path names - hash+=(long)(letter)*(i+119); - i++; - } - hash = (hash ^ (hash >> 10) ^ (hash >> 20)); - hash &= (size-1); - return hash; -} - -void R_RemapShader(const char *shaderName, const char *newShaderName, const char *timeOffset) { - char strippedName[MAX_QPATH]; - int hash; - shader_t *sh, *sh2; - qhandle_t h; - - sh = R_FindShaderByName( shaderName ); - if (sh == NULL || sh == tr.defaultShader) { - h = RE_RegisterShaderLightMap(shaderName, 0); - sh = R_GetShaderByHandle(h); - } - if (sh == NULL || sh == tr.defaultShader) { - ri.Printf( PRINT_WARNING, "WARNING: R_RemapShader: shader %s not found\n", shaderName ); - return; - } - - sh2 = R_FindShaderByName( newShaderName ); - if (sh2 == NULL || sh2 == tr.defaultShader) { - h = RE_RegisterShaderLightMap(newShaderName, 0); - sh2 = R_GetShaderByHandle(h); - } - - if (sh2 == NULL || sh2 == tr.defaultShader) { - ri.Printf( PRINT_WARNING, "WARNING: R_RemapShader: new shader %s not found\n", newShaderName ); - return; - } - - // remap all the shaders with the given name - // even tho they might have different lightmaps - COM_StripExtension(shaderName, strippedName, sizeof(strippedName)); - hash = generateHashValue(strippedName, FILE_HASH_SIZE); - for (sh = hashTable[hash]; sh; sh = sh->next) { - if (Q_stricmp(sh->name, strippedName) == 0) { - if (sh != sh2) { - sh->remappedShader = sh2; - } else { - sh->remappedShader = NULL; - } - } - } - if (timeOffset) { - sh2->timeOffset = atof(timeOffset); - } -} - -/* -=============== -ParseVector -=============== -*/ -static qboolean ParseVector( char **text, int count, float *v ) { - char *token; - int i; - - // FIXME: spaces are currently required after parens, should change parseext... - token = COM_ParseExt( text, qfalse ); - if ( strcmp( token, "(" ) ) { - ri.Printf( PRINT_WARNING, "WARNING: missing parenthesis in shader '%s'\n", shader.name ); - return qfalse; - } - - for ( i = 0 ; i < count ; i++ ) { - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) { - ri.Printf( PRINT_WARNING, "WARNING: missing vector element in shader '%s'\n", shader.name ); - return qfalse; - } - v[i] = atof( token ); - } - - token = COM_ParseExt( text, qfalse ); - if ( strcmp( token, ")" ) ) { - ri.Printf( PRINT_WARNING, "WARNING: missing parenthesis in shader '%s'\n", shader.name ); - return qfalse; - } - - return qtrue; -} - - -/* -=============== -NameToAFunc -=============== -*/ -static unsigned NameToAFunc( const char *funcname ) -{ - if ( !Q_stricmp( funcname, "GT0" ) ) - { - return GLS_ATEST_GT_0; - } - else if ( !Q_stricmp( funcname, "LT128" ) ) - { - return GLS_ATEST_LT_80; - } - else if ( !Q_stricmp( funcname, "GE128" ) ) - { - return GLS_ATEST_GE_80; - } - - ri.Printf( PRINT_WARNING, "WARNING: invalid alphaFunc name '%s' in shader '%s'\n", funcname, shader.name ); - return 0; -} - - -/* -=============== -NameToSrcBlendMode -=============== -*/ -static int NameToSrcBlendMode( const char *name ) -{ - if ( !Q_stricmp( name, "GL_ONE" ) ) - { - return GLS_SRCBLEND_ONE; - } - else if ( !Q_stricmp( name, "GL_ZERO" ) ) - { - return GLS_SRCBLEND_ZERO; - } - else if ( !Q_stricmp( name, "GL_DST_COLOR" ) ) - { - return GLS_SRCBLEND_DST_COLOR; - } - else if ( !Q_stricmp( name, "GL_ONE_MINUS_DST_COLOR" ) ) - { - return GLS_SRCBLEND_ONE_MINUS_DST_COLOR; - } - else if ( !Q_stricmp( name, "GL_SRC_ALPHA" ) ) - { - return GLS_SRCBLEND_SRC_ALPHA; - } - else if ( !Q_stricmp( name, "GL_ONE_MINUS_SRC_ALPHA" ) ) - { - return GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA; - } - else if ( !Q_stricmp( name, "GL_DST_ALPHA" ) ) - { - return GLS_SRCBLEND_DST_ALPHA; - } - else if ( !Q_stricmp( name, "GL_ONE_MINUS_DST_ALPHA" ) ) - { - return GLS_SRCBLEND_ONE_MINUS_DST_ALPHA; - } - else if ( !Q_stricmp( name, "GL_SRC_ALPHA_SATURATE" ) ) - { - return GLS_SRCBLEND_ALPHA_SATURATE; - } - - ri.Printf( PRINT_WARNING, "WARNING: unknown blend mode '%s' in shader '%s', substituting GL_ONE\n", name, shader.name ); - return GLS_SRCBLEND_ONE; -} - -/* -=============== -NameToDstBlendMode -=============== -*/ -static int NameToDstBlendMode( const char *name ) -{ - if ( !Q_stricmp( name, "GL_ONE" ) ) - { - return GLS_DSTBLEND_ONE; - } - else if ( !Q_stricmp( name, "GL_ZERO" ) ) - { - return GLS_DSTBLEND_ZERO; - } - else if ( !Q_stricmp( name, "GL_SRC_ALPHA" ) ) - { - return GLS_DSTBLEND_SRC_ALPHA; - } - else if ( !Q_stricmp( name, "GL_ONE_MINUS_SRC_ALPHA" ) ) - { - return GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; - } - else if ( !Q_stricmp( name, "GL_DST_ALPHA" ) ) - { - return GLS_DSTBLEND_DST_ALPHA; - } - else if ( !Q_stricmp( name, "GL_ONE_MINUS_DST_ALPHA" ) ) - { - return GLS_DSTBLEND_ONE_MINUS_DST_ALPHA; - } - else if ( !Q_stricmp( name, "GL_SRC_COLOR" ) ) - { - return GLS_DSTBLEND_SRC_COLOR; - } - else if ( !Q_stricmp( name, "GL_ONE_MINUS_SRC_COLOR" ) ) - { - return GLS_DSTBLEND_ONE_MINUS_SRC_COLOR; - } - - ri.Printf( PRINT_WARNING, "WARNING: unknown blend mode '%s' in shader '%s', substituting GL_ONE\n", name, shader.name ); - return GLS_DSTBLEND_ONE; -} - -/* -=============== -NameToGenFunc -=============== -*/ -static genFunc_t NameToGenFunc( const char *funcname ) -{ - if ( !Q_stricmp( funcname, "sin" ) ) - { - return GF_SIN; - } - else if ( !Q_stricmp( funcname, "square" ) ) - { - return GF_SQUARE; - } - else if ( !Q_stricmp( funcname, "triangle" ) ) - { - return GF_TRIANGLE; - } - else if ( !Q_stricmp( funcname, "sawtooth" ) ) - { - return GF_SAWTOOTH; - } - else if ( !Q_stricmp( funcname, "inversesawtooth" ) ) - { - return GF_INVERSE_SAWTOOTH; - } - else if ( !Q_stricmp( funcname, "noise" ) ) - { - return GF_NOISE; - } - - ri.Printf( PRINT_WARNING, "WARNING: invalid genfunc name '%s' in shader '%s'\n", funcname, shader.name ); - return GF_SIN; -} - - -/* -=================== -ParseWaveForm -=================== -*/ -static void ParseWaveForm( char **text, waveForm_t *wave ) -{ - char *token; - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); - return; - } - wave->func = NameToGenFunc( token ); - - // BASE, AMP, PHASE, FREQ - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); - return; - } - wave->base = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); - return; - } - wave->amplitude = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); - return; - } - wave->phase = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); - return; - } - wave->frequency = atof( token ); -} - - -/* -=================== -ParseTexMod -=================== -*/ -static void ParseTexMod( char *_text, shaderStage_t *stage ) -{ - const char *token; - char **text = &_text; - texModInfo_t *tmi; - - if ( stage->bundle[0].numTexMods == TR_MAX_TEXMODS ) { - ri.Error( ERR_DROP, "ERROR: too many tcMod stages in shader '%s'", shader.name ); - return; - } - - tmi = &stage->bundle[0].texMods[stage->bundle[0].numTexMods]; - stage->bundle[0].numTexMods++; - - token = COM_ParseExt( text, qfalse ); - - // - // turb - // - if ( !Q_stricmp( token, "turb" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb parms in shader '%s'\n", shader.name ); - return; - } - tmi->wave.base = atof( token ); - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name ); - return; - } - tmi->wave.amplitude = atof( token ); - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name ); - return; - } - tmi->wave.phase = atof( token ); - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name ); - return; - } - tmi->wave.frequency = atof( token ); - - tmi->type = TMOD_TURBULENT; - } - // - // scale - // - else if ( !Q_stricmp( token, "scale" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing scale parms in shader '%s'\n", shader.name ); - return; - } - tmi->scale[0] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing scale parms in shader '%s'\n", shader.name ); - return; - } - tmi->scale[1] = atof( token ); - tmi->type = TMOD_SCALE; - } - // - // scroll - // - else if ( !Q_stricmp( token, "scroll" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing scale scroll parms in shader '%s'\n", shader.name ); - return; - } - tmi->scroll[0] = atof( token ); - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing scale scroll parms in shader '%s'\n", shader.name ); - return; - } - tmi->scroll[1] = atof( token ); - tmi->type = TMOD_SCROLL; - } - // - // stretch - // - else if ( !Q_stricmp( token, "stretch" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); - return; - } - tmi->wave.func = NameToGenFunc( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); - return; - } - tmi->wave.base = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); - return; - } - tmi->wave.amplitude = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); - return; - } - tmi->wave.phase = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); - return; - } - tmi->wave.frequency = atof( token ); - - tmi->type = TMOD_STRETCH; - } - // - // transform - // - else if ( !Q_stricmp( token, "transform" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); - return; - } - tmi->matrix[0][0] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); - return; - } - tmi->matrix[0][1] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); - return; - } - tmi->matrix[1][0] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); - return; - } - tmi->matrix[1][1] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); - return; - } - tmi->translate[0] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); - return; - } - tmi->translate[1] = atof( token ); - - tmi->type = TMOD_TRANSFORM; - } - // - // rotate - // - else if ( !Q_stricmp( token, "rotate" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing tcMod rotate parms in shader '%s'\n", shader.name ); - return; - } - tmi->rotateSpeed = atof( token ); - tmi->type = TMOD_ROTATE; - } - // - // entityTranslate - // - else if ( !Q_stricmp( token, "entityTranslate" ) ) - { - tmi->type = TMOD_ENTITY_TRANSLATE; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown tcMod '%s' in shader '%s'\n", token, shader.name ); - } -} - - -/* -=================== -ParseStage -=================== -*/ -static qboolean ParseStage( shaderStage_t *stage, char **text ) -{ - char *token; - int depthMaskBits = GLS_DEPTHMASK_TRUE, blendSrcBits = 0, blendDstBits = 0, atestBits = 0, depthFuncBits = 0; - qboolean depthMaskExplicit = qfalse; - - stage->active = qtrue; - - while ( 1 ) - { - token = COM_ParseExt( text, qtrue ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: no matching '}' found\n" ); - return qfalse; - } - - if ( token[0] == '}' ) - { - break; - } - // - // map - // - else if ( !Q_stricmp( token, "map" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'map' keyword in shader '%s'\n", shader.name ); - return qfalse; - } - - if ( !Q_stricmp( token, "$whiteimage" ) ) - { - stage->bundle[0].image[0] = tr.whiteImage; - continue; - } - else if ( !Q_stricmp( token, "$lightmap" ) ) - { - stage->bundle[0].isLightmap = qtrue; - if ( shader.lightmapIndex < 0 ) { - stage->bundle[0].image[0] = tr.whiteImage; - } else { - stage->bundle[0].image[0] = tr.lightmaps[shader.lightmapIndex]; - } - continue; - } - else if ( !Q_stricmp( token, "$deluxemap" ) ) - { - if (!tr.worldDeluxeMapping) - { - ri.Printf( PRINT_WARNING, "WARNING: shader '%s' wants a deluxe map in a map compiled without them\n", shader.name ); - return qfalse; - } - - stage->bundle[0].isLightmap = qtrue; - if ( shader.lightmapIndex < 0 ) { - stage->bundle[0].image[0] = tr.whiteImage; - } else { - stage->bundle[0].image[0] = tr.deluxemaps[shader.lightmapIndex]; - } - continue; - } - else - { - imgType_t type = IMGTYPE_COLORALPHA; - imgFlags_t flags = IMGFLAG_NONE; - - if (!shader.noMipMaps) - flags |= IMGFLAG_MIPMAP; - - if (!shader.noPicMip) - flags |= IMGFLAG_PICMIP; - - if (stage->type == ST_NORMALMAP || stage->type == ST_NORMALPARALLAXMAP) - { - type = IMGTYPE_NORMAL; - flags |= IMGFLAG_NOLIGHTSCALE; - - if (stage->type == ST_NORMALPARALLAXMAP) - type = IMGTYPE_NORMALHEIGHT; - } - else - { - if (r_genNormalMaps->integer) - flags |= IMGFLAG_GENNORMALMAP; - - if (r_srgb->integer) - flags |= IMGFLAG_SRGB; - } - - stage->bundle[0].image[0] = R_FindImageFile( token, type, flags ); - - if ( !stage->bundle[0].image[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name ); - return qfalse; - } - } - } - // - // clampmap - // - else if ( !Q_stricmp( token, "clampmap" ) ) - { - imgType_t type = IMGTYPE_COLORALPHA; - imgFlags_t flags = IMGFLAG_CLAMPTOEDGE | IMGFLAG_SRGB; - - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'clampmap' keyword in shader '%s'\n", shader.name ); - return qfalse; - } - - if (!shader.noMipMaps) - flags |= IMGFLAG_MIPMAP; - - if (!shader.noPicMip) - flags |= IMGFLAG_PICMIP; - - if (stage->type == ST_NORMALMAP || stage->type == ST_NORMALPARALLAXMAP) - { - type = IMGTYPE_NORMAL; - flags |= IMGFLAG_NOLIGHTSCALE; - - if (stage->type == ST_NORMALPARALLAXMAP) - type = IMGTYPE_NORMALHEIGHT; - } - else - { - if (r_genNormalMaps->integer) - flags |= IMGFLAG_GENNORMALMAP; - - if (r_srgb->integer) - flags |= IMGFLAG_SRGB; - } - - - stage->bundle[0].image[0] = R_FindImageFile( token, type, flags ); - if ( !stage->bundle[0].image[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name ); - return qfalse; - } - } - // - // animMap .... - // - else if ( !Q_stricmp( token, "animMap" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'animMmap' keyword in shader '%s'\n", shader.name ); - return qfalse; - } - stage->bundle[0].imageAnimationSpeed = atof( token ); - - // parse up to MAX_IMAGE_ANIMATIONS animations - while ( 1 ) { - int num; - - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) { - break; - } - num = stage->bundle[0].numImageAnimations; - if ( num < MAX_IMAGE_ANIMATIONS ) { - imgFlags_t flags = IMGFLAG_SRGB; - - if (!shader.noMipMaps) - flags |= IMGFLAG_MIPMAP; - - if (!shader.noPicMip) - flags |= IMGFLAG_PICMIP; - - stage->bundle[0].image[num] = R_FindImageFile( token, IMGTYPE_COLORALPHA, flags ); - if ( !stage->bundle[0].image[num] ) - { - ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name ); - return qfalse; - } - stage->bundle[0].numImageAnimations++; - } - } - } - else if ( !Q_stricmp( token, "videoMap" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'videoMmap' keyword in shader '%s'\n", shader.name ); - return qfalse; - } - stage->bundle[0].videoMapHandle = ri.CIN_PlayCinematic( token, 0, 0, 256, 256, (CIN_loop | CIN_silent | CIN_shader)); - if (stage->bundle[0].videoMapHandle != -1) { - stage->bundle[0].isVideoMap = qtrue; - stage->bundle[0].image[0] = tr.scratchImage[stage->bundle[0].videoMapHandle]; - } - } - // - // alphafunc - // - else if ( !Q_stricmp( token, "alphaFunc" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'alphaFunc' keyword in shader '%s'\n", shader.name ); - return qfalse; - } - - atestBits = NameToAFunc( token ); - } - // - // depthFunc - // - else if ( !Q_stricmp( token, "depthfunc" ) ) - { - token = COM_ParseExt( text, qfalse ); - - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'depthfunc' keyword in shader '%s'\n", shader.name ); - return qfalse; - } - - if ( !Q_stricmp( token, "lequal" ) ) - { - depthFuncBits = 0; - } - else if ( !Q_stricmp( token, "equal" ) ) - { - depthFuncBits = GLS_DEPTHFUNC_EQUAL; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown depthfunc '%s' in shader '%s'\n", token, shader.name ); - continue; - } - } - // - // detail - // - else if ( !Q_stricmp( token, "detail" ) ) - { - stage->isDetail = qtrue; - } - // - // blendfunc - // or blendfunc - // - else if ( !Q_stricmp( token, "blendfunc" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parm for blendFunc in shader '%s'\n", shader.name ); - continue; - } - // check for "simple" blends first - if ( !Q_stricmp( token, "add" ) ) { - blendSrcBits = GLS_SRCBLEND_ONE; - blendDstBits = GLS_DSTBLEND_ONE; - } else if ( !Q_stricmp( token, "filter" ) ) { - blendSrcBits = GLS_SRCBLEND_DST_COLOR; - blendDstBits = GLS_DSTBLEND_ZERO; - } else if ( !Q_stricmp( token, "blend" ) ) { - blendSrcBits = GLS_SRCBLEND_SRC_ALPHA; - blendDstBits = GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; - } else { - // complex double blends - blendSrcBits = NameToSrcBlendMode( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parm for blendFunc in shader '%s'\n", shader.name ); - continue; - } - blendDstBits = NameToDstBlendMode( token ); - } - - // clear depth mask for blended surfaces - if ( !depthMaskExplicit ) - { - depthMaskBits = 0; - } - } - // - // stage - // - else if(!Q_stricmp(token, "stage")) - { - token = COM_ParseExt(text, qfalse); - if(token[0] == 0) - { - ri.Printf(PRINT_WARNING, "WARNING: missing parameters for stage in shader '%s'\n", shader.name); - continue; - } - - if(!Q_stricmp(token, "diffuseMap")) - { - stage->type = ST_DIFFUSEMAP; - } - else if(!Q_stricmp(token, "normalMap") || !Q_stricmp(token, "bumpMap")) - { - stage->type = ST_NORMALMAP; - } - else if(!Q_stricmp(token, "normalParallaxMap") || !Q_stricmp(token, "bumpParallaxMap")) - { - if (r_parallaxMapping->integer) - stage->type = ST_NORMALPARALLAXMAP; - else - stage->type = ST_NORMALMAP; - } - else if(!Q_stricmp(token, "specularMap")) - { - stage->type = ST_SPECULARMAP; - stage->materialInfo[0] = 0.04f; - stage->materialInfo[1] = 256.0f; - } - else - { - ri.Printf(PRINT_WARNING, "WARNING: unknown stage parameter '%s' in shader '%s'\n", token, shader.name); - continue; - } - } - // - // specularReflectance - // - else if (!Q_stricmp(token, "specularreflectance")) - { - token = COM_ParseExt(text, qfalse); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for specular reflectance in shader '%s'\n", shader.name ); - continue; - } - stage->materialInfo[0] = atof( token ); - } - // - // specularExponent - // - else if (!Q_stricmp(token, "specularexponent")) - { - token = COM_ParseExt(text, qfalse); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for specular exponent in shader '%s'\n", shader.name ); - continue; - } - stage->materialInfo[1] = atof( token ); - } - // - // rgbGen - // - else if ( !Q_stricmp( token, "rgbGen" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameters for rgbGen in shader '%s'\n", shader.name ); - continue; - } - - if ( !Q_stricmp( token, "wave" ) ) - { - ParseWaveForm( text, &stage->rgbWave ); - stage->rgbGen = CGEN_WAVEFORM; - } - else if ( !Q_stricmp( token, "const" ) ) - { - vec3_t color; - - ParseVector( text, 3, color ); - stage->constantColor[0] = 255 * color[0]; - stage->constantColor[1] = 255 * color[1]; - stage->constantColor[2] = 255 * color[2]; - - stage->rgbGen = CGEN_CONST; - } - else if ( !Q_stricmp( token, "identity" ) ) - { - stage->rgbGen = CGEN_IDENTITY; - } - else if ( !Q_stricmp( token, "identityLighting" ) ) - { - stage->rgbGen = CGEN_IDENTITY_LIGHTING; - } - else if ( !Q_stricmp( token, "entity" ) ) - { - stage->rgbGen = CGEN_ENTITY; - } - else if ( !Q_stricmp( token, "oneMinusEntity" ) ) - { - stage->rgbGen = CGEN_ONE_MINUS_ENTITY; - } - else if ( !Q_stricmp( token, "vertex" ) ) - { - stage->rgbGen = CGEN_VERTEX; - if ( stage->alphaGen == 0 ) { - stage->alphaGen = AGEN_VERTEX; - } - } - else if ( !Q_stricmp( token, "exactVertex" ) ) - { - stage->rgbGen = CGEN_EXACT_VERTEX; - } - else if ( !Q_stricmp( token, "vertexLit" ) ) - { - stage->rgbGen = CGEN_VERTEX_LIT; - if ( stage->alphaGen == 0 ) { - stage->alphaGen = AGEN_VERTEX; - } - } - else if ( !Q_stricmp( token, "exactVertexLit" ) ) - { - stage->rgbGen = CGEN_EXACT_VERTEX_LIT; - } - else if ( !Q_stricmp( token, "lightingDiffuse" ) ) - { - stage->rgbGen = CGEN_LIGHTING_DIFFUSE; - } - else if ( !Q_stricmp( token, "oneMinusVertex" ) ) - { - stage->rgbGen = CGEN_ONE_MINUS_VERTEX; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown rgbGen parameter '%s' in shader '%s'\n", token, shader.name ); - continue; - } - } - // - // alphaGen - // - else if ( !Q_stricmp( token, "alphaGen" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameters for alphaGen in shader '%s'\n", shader.name ); - continue; - } - - if ( !Q_stricmp( token, "wave" ) ) - { - ParseWaveForm( text, &stage->alphaWave ); - stage->alphaGen = AGEN_WAVEFORM; - } - else if ( !Q_stricmp( token, "const" ) ) - { - token = COM_ParseExt( text, qfalse ); - stage->constantColor[3] = 255 * atof( token ); - stage->alphaGen = AGEN_CONST; - } - else if ( !Q_stricmp( token, "identity" ) ) - { - stage->alphaGen = AGEN_IDENTITY; - } - else if ( !Q_stricmp( token, "entity" ) ) - { - stage->alphaGen = AGEN_ENTITY; - } - else if ( !Q_stricmp( token, "oneMinusEntity" ) ) - { - stage->alphaGen = AGEN_ONE_MINUS_ENTITY; - } - else if ( !Q_stricmp( token, "vertex" ) ) - { - stage->alphaGen = AGEN_VERTEX; - } - else if ( !Q_stricmp( token, "lightingSpecular" ) ) - { - stage->alphaGen = AGEN_LIGHTING_SPECULAR; - } - else if ( !Q_stricmp( token, "oneMinusVertex" ) ) - { - stage->alphaGen = AGEN_ONE_MINUS_VERTEX; - } - else if ( !Q_stricmp( token, "portal" ) ) - { - stage->alphaGen = AGEN_PORTAL; - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - shader.portalRange = 256; - ri.Printf( PRINT_WARNING, "WARNING: missing range parameter for alphaGen portal in shader '%s', defaulting to 256\n", shader.name ); - } - else - { - shader.portalRange = atof( token ); - } - } - else if ( !Q_stricmp( token, "fresnel" ) ) - { - stage->alphaGen = AGEN_FRESNEL; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown alphaGen parameter '%s' in shader '%s'\n", token, shader.name ); - continue; - } - } - // - // tcGen - // - else if ( !Q_stricmp(token, "texgen") || !Q_stricmp( token, "tcGen" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing texgen parm in shader '%s'\n", shader.name ); - continue; - } - - if ( !Q_stricmp( token, "environment" ) ) - { - stage->bundle[0].tcGen = TCGEN_ENVIRONMENT_MAPPED; - } - else if ( !Q_stricmp( token, "lightmap" ) ) - { - stage->bundle[0].tcGen = TCGEN_LIGHTMAP; - } - else if ( !Q_stricmp( token, "texture" ) || !Q_stricmp( token, "base" ) ) - { - stage->bundle[0].tcGen = TCGEN_TEXTURE; - } - else if ( !Q_stricmp( token, "vector" ) ) - { - ParseVector( text, 3, stage->bundle[0].tcGenVectors[0] ); - ParseVector( text, 3, stage->bundle[0].tcGenVectors[1] ); - - stage->bundle[0].tcGen = TCGEN_VECTOR; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown texgen parm in shader '%s'\n", shader.name ); - } - } - // - // tcMod <...> - // - else if ( !Q_stricmp( token, "tcMod" ) ) - { - char buffer[1024] = ""; - - while ( 1 ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - break; - strcat( buffer, token ); - strcat( buffer, " " ); - } - - ParseTexMod( buffer, stage ); - - continue; - } - // - // depthmask - // - else if ( !Q_stricmp( token, "depthwrite" ) ) - { - depthMaskBits = GLS_DEPTHMASK_TRUE; - depthMaskExplicit = qtrue; - - continue; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown parameter '%s' in shader '%s'\n", token, shader.name ); - return qfalse; - } - } - - // - // if cgen isn't explicitly specified, use either identity or identitylighting - // - if ( stage->rgbGen == CGEN_BAD ) { - if ( blendSrcBits == 0 || - blendSrcBits == GLS_SRCBLEND_ONE || - blendSrcBits == GLS_SRCBLEND_SRC_ALPHA ) { - stage->rgbGen = CGEN_IDENTITY_LIGHTING; - } else { - stage->rgbGen = CGEN_IDENTITY; - } - } - - - // - // implicitly assume that a GL_ONE GL_ZERO blend mask disables blending - // - if ( ( blendSrcBits == GLS_SRCBLEND_ONE ) && - ( blendDstBits == GLS_DSTBLEND_ZERO ) ) - { - blendDstBits = blendSrcBits = 0; - depthMaskBits = GLS_DEPTHMASK_TRUE; - } - - // decide which agens we can skip - if ( stage->alphaGen == AGEN_IDENTITY ) { - if ( stage->rgbGen == CGEN_IDENTITY - || stage->rgbGen == CGEN_LIGHTING_DIFFUSE ) { - stage->alphaGen = AGEN_SKIP; - } - } - - // - // compute state bits - // - stage->stateBits = depthMaskBits | - blendSrcBits | blendDstBits | - atestBits | - depthFuncBits; - - return qtrue; -} - -/* -=============== -ParseDeform - -deformVertexes wave -deformVertexes normal -deformVertexes move -deformVertexes bulge -deformVertexes projectionShadow -deformVertexes autoSprite -deformVertexes autoSprite2 -deformVertexes text[0-7] -=============== -*/ -static void ParseDeform( char **text ) { - char *token; - deformStage_t *ds; - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deform parm in shader '%s'\n", shader.name ); - return; - } - - if ( shader.numDeforms == MAX_SHADER_DEFORMS ) { - ri.Printf( PRINT_WARNING, "WARNING: MAX_SHADER_DEFORMS in '%s'\n", shader.name ); - return; - } - - ds = &shader.deforms[ shader.numDeforms ]; - shader.numDeforms++; - - if ( !Q_stricmp( token, "projectionShadow" ) ) { - ds->deformation = DEFORM_PROJECTION_SHADOW; - return; - } - - if ( !Q_stricmp( token, "autosprite" ) ) { - ds->deformation = DEFORM_AUTOSPRITE; - return; - } - - if ( !Q_stricmp( token, "autosprite2" ) ) { - ds->deformation = DEFORM_AUTOSPRITE2; - return; - } - - if ( !Q_stricmpn( token, "text", 4 ) ) { - int n; - - n = token[4] - '0'; - if ( n < 0 || n > 7 ) { - n = 0; - } - ds->deformation = DEFORM_TEXT0 + n; - return; - } - - if ( !Q_stricmp( token, "bulge" ) ) { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name ); - return; - } - ds->bulgeWidth = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name ); - return; - } - ds->bulgeHeight = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name ); - return; - } - ds->bulgeSpeed = atof( token ); - - ds->deformation = DEFORM_BULGE; - return; - } - - if ( !Q_stricmp( token, "wave" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); - return; - } - - if ( atof( token ) != 0 ) - { - ds->deformationSpread = 1.0f / atof( token ); - } - else - { - ds->deformationSpread = 100.0f; - ri.Printf( PRINT_WARNING, "WARNING: illegal div value of 0 in deformVertexes command for shader '%s'\n", shader.name ); - } - - ParseWaveForm( text, &ds->deformationWave ); - ds->deformation = DEFORM_WAVE; - return; - } - - if ( !Q_stricmp( token, "normal" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); - return; - } - ds->deformationWave.amplitude = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); - return; - } - ds->deformationWave.frequency = atof( token ); - - ds->deformation = DEFORM_NORMALS; - return; - } - - if ( !Q_stricmp( token, "move" ) ) { - int i; - - for ( i = 0 ; i < 3 ; i++ ) { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); - return; - } - ds->moveVector[i] = atof( token ); - } - - ParseWaveForm( text, &ds->deformationWave ); - ds->deformation = DEFORM_MOVE; - return; - } - - ri.Printf( PRINT_WARNING, "WARNING: unknown deformVertexes subtype '%s' found in shader '%s'\n", token, shader.name ); -} - - -/* -=============== -ParseSkyParms - -skyParms -=============== -*/ -static void ParseSkyParms( char **text ) { - char *token; - static char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; - char pathname[MAX_QPATH]; - int i; - - // outerbox - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) { - ri.Printf( PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name ); - return; - } - if ( strcmp( token, "-" ) ) { - for (i=0 ; i<6 ; i++) { - Com_sprintf( pathname, sizeof(pathname), "%s_%s.tga" - , token, suf[i] ); - shader.sky.outerbox[i] = R_FindImageFile( ( char * ) pathname, IMGTYPE_COLORALPHA, IMGFLAG_SRGB | IMGFLAG_MIPMAP | IMGFLAG_PICMIP | IMGFLAG_CLAMPTOEDGE ); - - if ( !shader.sky.outerbox[i] ) { - shader.sky.outerbox[i] = tr.defaultImage; - } - } - } - - // cloudheight - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) { - ri.Printf( PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name ); - return; - } - shader.sky.cloudHeight = atof( token ); - if ( !shader.sky.cloudHeight ) { - shader.sky.cloudHeight = 512; - } - R_InitSkyTexCoords( shader.sky.cloudHeight ); - - - // innerbox - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) { - ri.Printf( PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name ); - return; - } - if ( strcmp( token, "-" ) ) { - for (i=0 ; i<6 ; i++) { - Com_sprintf( pathname, sizeof(pathname), "%s_%s.tga" - , token, suf[i] ); - shader.sky.innerbox[i] = R_FindImageFile( ( char * ) pathname, IMGTYPE_COLORALPHA, IMGFLAG_SRGB | IMGFLAG_MIPMAP | IMGFLAG_PICMIP ); - if ( !shader.sky.innerbox[i] ) { - shader.sky.innerbox[i] = tr.defaultImage; - } - } - } - - shader.isSky = qtrue; -} - - -/* -================= -ParseSort -================= -*/ -void ParseSort( char **text ) { - char *token; - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) { - ri.Printf( PRINT_WARNING, "WARNING: missing sort parameter in shader '%s'\n", shader.name ); - return; - } - - if ( !Q_stricmp( token, "portal" ) ) { - shader.sort = SS_PORTAL; - } else if ( !Q_stricmp( token, "sky" ) ) { - shader.sort = SS_ENVIRONMENT; - } else if ( !Q_stricmp( token, "opaque" ) ) { - shader.sort = SS_OPAQUE; - }else if ( !Q_stricmp( token, "decal" ) ) { - shader.sort = SS_DECAL; - } else if ( !Q_stricmp( token, "seeThrough" ) ) { - shader.sort = SS_SEE_THROUGH; - } else if ( !Q_stricmp( token, "banner" ) ) { - shader.sort = SS_BANNER; - } else if ( !Q_stricmp( token, "additive" ) ) { - shader.sort = SS_BLEND1; - } else if ( !Q_stricmp( token, "nearest" ) ) { - shader.sort = SS_NEAREST; - } else if ( !Q_stricmp( token, "underwater" ) ) { - shader.sort = SS_UNDERWATER; - } else { - shader.sort = atof( token ); - } -} - - - -// this table is also present in q3map - -typedef struct { - char *name; - int clearSolid, surfaceFlags, contents; -} infoParm_t; - -infoParm_t infoParms[] = { - // server relevant contents - {"water", 1, 0, CONTENTS_WATER }, - {"slime", 1, 0, CONTENTS_SLIME }, // mildly damaging - {"lava", 1, 0, CONTENTS_LAVA }, // very damaging - {"playerclip", 1, 0, CONTENTS_PLAYERCLIP }, - {"monsterclip", 1, 0, CONTENTS_MONSTERCLIP }, - {"nodrop", 1, 0, CONTENTS_NODROP }, // don't drop items or leave bodies (death fog, lava, etc) - {"nonsolid", 1, SURF_NONSOLID, 0}, // clears the solid flag - - // utility relevant attributes - {"origin", 1, 0, CONTENTS_ORIGIN }, // center of rotating brushes - {"trans", 0, 0, CONTENTS_TRANSLUCENT }, // don't eat contained surfaces - {"detail", 0, 0, CONTENTS_DETAIL }, // don't include in structural bsp - {"structural", 0, 0, CONTENTS_STRUCTURAL }, // force into structural bsp even if trnas - {"areaportal", 1, 0, CONTENTS_AREAPORTAL }, // divides areas - {"clusterportal", 1,0, CONTENTS_CLUSTERPORTAL }, // for bots - {"donotenter", 1, 0, CONTENTS_DONOTENTER }, // for bots - - {"fog", 1, 0, CONTENTS_FOG}, // carves surfaces entering - {"sky", 0, SURF_SKY, 0 }, // emit light from an environment map - {"lightfilter", 0, SURF_LIGHTFILTER, 0 }, // filter light going through it - {"alphashadow", 0, SURF_ALPHASHADOW, 0 }, // test light on a per-pixel basis - {"hint", 0, SURF_HINT, 0 }, // use as a primary splitter - - // server attributes - {"slick", 0, SURF_SLICK, 0 }, - {"noimpact", 0, SURF_NOIMPACT, 0 }, // don't make impact explosions or marks - {"nomarks", 0, SURF_NOMARKS, 0 }, // don't make impact marks, but still explode - {"ladder", 0, SURF_LADDER, 0 }, - {"nodamage", 0, SURF_NODAMAGE, 0 }, - {"metalsteps", 0, SURF_METALSTEPS,0 }, - {"flesh", 0, SURF_FLESH, 0 }, - {"nosteps", 0, SURF_NOSTEPS, 0 }, - - // drawsurf attributes - {"nodraw", 0, SURF_NODRAW, 0 }, // don't generate a drawsurface (or a lightmap) - {"pointlight", 0, SURF_POINTLIGHT, 0 }, // sample lighting at vertexes - {"nolightmap", 0, SURF_NOLIGHTMAP,0 }, // don't generate a lightmap - {"nodlight", 0, SURF_NODLIGHT, 0 }, // don't ever add dynamic lights - {"dust", 0, SURF_DUST, 0} // leave a dust trail when walking on this surface -}; - - -/* -=============== -ParseSurfaceParm - -surfaceparm -=============== -*/ -static void ParseSurfaceParm( char **text ) { - char *token; - int numInfoParms = ARRAY_LEN( infoParms ); - int i; - - token = COM_ParseExt( text, qfalse ); - for ( i = 0 ; i < numInfoParms ; i++ ) { - if ( !Q_stricmp( token, infoParms[i].name ) ) { - shader.surfaceFlags |= infoParms[i].surfaceFlags; - shader.contentFlags |= infoParms[i].contents; -#if 0 - if ( infoParms[i].clearSolid ) { - si->contents &= ~CONTENTS_SOLID; - } -#endif - break; - } - } -} - -/* -================= -ParseShader - -The current text pointer is at the explicit text definition of the -shader. Parse it into the global shader variable. Later functions -will optimize it. -================= -*/ -static qboolean ParseShader( char **text ) -{ - char *token; - int s; - - s = 0; - - token = COM_ParseExt( text, qtrue ); - if ( token[0] != '{' ) - { - ri.Printf( PRINT_WARNING, "WARNING: expecting '{', found '%s' instead in shader '%s'\n", token, shader.name ); - return qfalse; - } - - while ( 1 ) - { - token = COM_ParseExt( text, qtrue ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: no concluding '}' in shader %s\n", shader.name ); - return qfalse; - } - - // end of shader definition - if ( token[0] == '}' ) - { - break; - } - // stage definition - else if ( token[0] == '{' ) - { - if ( s >= MAX_SHADER_STAGES ) { - ri.Printf( PRINT_WARNING, "WARNING: too many stages in shader %s\n", shader.name ); - return qfalse; - } - - if ( !ParseStage( &stages[s], text ) ) - { - return qfalse; - } - stages[s].active = qtrue; - s++; - - continue; - } - // skip stuff that only the QuakeEdRadient needs - else if ( !Q_stricmpn( token, "qer", 3 ) ) { - SkipRestOfLine( text ); - continue; - } - // sun parms - else if ( !Q_stricmp( token, "q3map_sun" ) || !Q_stricmp( token, "q3map_sunExt" ) || !Q_stricmp( token, "q3gl2_sun" ) ) { - float a, b; - qboolean isGL2Sun = qfalse; - - if (!Q_stricmp( token, "q3gl2_sun" ) && r_sunShadows->integer ) - { - isGL2Sun = qtrue; - tr.sunShadows = qtrue; - } - - token = COM_ParseExt( text, qfalse ); - tr.sunLight[0] = atof( token ); - token = COM_ParseExt( text, qfalse ); - tr.sunLight[1] = atof( token ); - token = COM_ParseExt( text, qfalse ); - tr.sunLight[2] = atof( token ); - - VectorNormalize( tr.sunLight ); - - token = COM_ParseExt( text, qfalse ); - a = atof( token ); - VectorScale( tr.sunLight, a, tr.sunLight); - - VectorSet( tr.sunAmbient, 0.0f, 0.0f, 0.0f); - - token = COM_ParseExt( text, qfalse ); - a = atof( token ); - a = a / 180 * M_PI; - - token = COM_ParseExt( text, qfalse ); - b = atof( token ); - b = b / 180 * M_PI; - - tr.sunDirection[0] = cos( a ) * cos( b ); - tr.sunDirection[1] = sin( a ) * cos( b ); - tr.sunDirection[2] = sin( b ); - - if (isGL2Sun) - { - token = COM_ParseExt( text, qfalse ); - tr.mapLightScale = atof(token); - - token = COM_ParseExt( text, qfalse ); - VectorScale( tr.sunLight, atof(token), tr.sunAmbient ); - } - - SkipRestOfLine( text ); - continue; - } - // tonemap parms - else if ( !Q_stricmp( token, "q3gl2_tonemap" ) ) { - token = COM_ParseExt( text, qfalse ); - tr.toneMinAvgMaxLevel[0] = atof( token ); - token = COM_ParseExt( text, qfalse ); - tr.toneMinAvgMaxLevel[1] = atof( token ); - token = COM_ParseExt( text, qfalse ); - tr.toneMinAvgMaxLevel[2] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - tr.autoExposureMinMax[0] = atof( token ); - token = COM_ParseExt( text, qfalse ); - tr.autoExposureMinMax[1] = atof( token ); - - SkipRestOfLine( text ); - continue; - } - else if ( !Q_stricmp( token, "deformVertexes" ) ) { - ParseDeform( text ); - continue; - } - else if ( !Q_stricmp( token, "tesssize" ) ) { - SkipRestOfLine( text ); - continue; - } - else if ( !Q_stricmp( token, "clampTime" ) ) { - token = COM_ParseExt( text, qfalse ); - if (token[0]) { - shader.clampTime = atof(token); - } - } - // skip stuff that only the q3map needs - else if ( !Q_stricmpn( token, "q3map", 5 ) ) { - SkipRestOfLine( text ); - continue; - } - // skip stuff that only q3map or the server needs - else if ( !Q_stricmp( token, "surfaceParm" ) ) { - ParseSurfaceParm( text ); - continue; - } - // no mip maps - else if ( !Q_stricmp( token, "nomipmaps" ) ) - { - shader.noMipMaps = qtrue; - shader.noPicMip = qtrue; - continue; - } - // no picmip adjustment - else if ( !Q_stricmp( token, "nopicmip" ) ) - { - shader.noPicMip = qtrue; - continue; - } - // polygonOffset - else if ( !Q_stricmp( token, "polygonOffset" ) ) - { - shader.polygonOffset = qtrue; - continue; - } - // entityMergable, allowing sprite surfaces from multiple entities - // to be merged into one batch. This is a savings for smoke - // puffs and blood, but can't be used for anything where the - // shader calcs (not the surface function) reference the entity color or scroll - else if ( !Q_stricmp( token, "entityMergable" ) ) - { - shader.entityMergable = qtrue; - continue; - } - // fogParms - else if ( !Q_stricmp( token, "fogParms" ) ) - { - if ( !ParseVector( text, 3, shader.fogParms.color ) ) { - return qfalse; - } - - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parm for 'fogParms' keyword in shader '%s'\n", shader.name ); - continue; - } - shader.fogParms.depthForOpaque = atof( token ); - - // skip any old gradient directions - SkipRestOfLine( text ); - continue; - } - // portal - else if ( !Q_stricmp(token, "portal") ) - { - shader.sort = SS_PORTAL; - shader.isPortal = qtrue; - continue; - } - // skyparms - else if ( !Q_stricmp( token, "skyparms" ) ) - { - ParseSkyParms( text ); - continue; - } - // light determines flaring in q3map, not needed here - else if ( !Q_stricmp(token, "light") ) - { - token = COM_ParseExt( text, qfalse ); - continue; - } - // cull - else if ( !Q_stricmp( token, "cull") ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing cull parms in shader '%s'\n", shader.name ); - continue; - } - - if ( !Q_stricmp( token, "none" ) || !Q_stricmp( token, "twosided" ) || !Q_stricmp( token, "disable" ) ) - { - shader.cullType = CT_TWO_SIDED; - } - else if ( !Q_stricmp( token, "back" ) || !Q_stricmp( token, "backside" ) || !Q_stricmp( token, "backsided" ) ) - { - shader.cullType = CT_BACK_SIDED; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: invalid cull parm '%s' in shader '%s'\n", token, shader.name ); - } - continue; - } - // sort - else if ( !Q_stricmp( token, "sort" ) ) - { - ParseSort( text ); - continue; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown general shader parameter '%s' in '%s'\n", token, shader.name ); - return qfalse; - } - } - - // - // ignore shaders that don't have any stages, unless it is a sky or fog - // - if ( s == 0 && !shader.isSky && !(shader.contentFlags & CONTENTS_FOG ) ) { - return qfalse; - } - - shader.explicitlyDefined = qtrue; - - return qtrue; -} - -/* -======================================================================================== - -SHADER OPTIMIZATION AND FOGGING - -======================================================================================== -*/ - -/* -=================== -ComputeStageIteratorFunc - -See if we can use on of the simple fastpath stage functions, -otherwise set to the generic stage function -=================== -*/ -static void ComputeStageIteratorFunc( void ) -{ - shader.optimalStageIteratorFunc = RB_StageIteratorGeneric; - - // - // see if this should go into the sky path - // - if ( shader.isSky ) - { - shader.optimalStageIteratorFunc = RB_StageIteratorSky; - return; - } -} - -/* -=================== -ComputeVertexAttribs - -Check which vertex attributes we only need, so we -don't need to submit/copy all of them. -=================== -*/ -static void ComputeVertexAttribs(void) -{ - int i, stage; - - // dlights always need ATTR_NORMAL - shader.vertexAttribs = ATTR_POSITION | ATTR_NORMAL; - - // portals always need normals, for SurfIsOffscreen() - if (shader.isPortal) - { - shader.vertexAttribs |= ATTR_NORMAL; - } - - if (shader.defaultShader) - { - shader.vertexAttribs |= ATTR_TEXCOORD; - return; - } - - if(shader.numDeforms) - { - for ( i = 0; i < shader.numDeforms; i++) - { - deformStage_t *ds = &shader.deforms[i]; - - switch (ds->deformation) - { - case DEFORM_BULGE: - shader.vertexAttribs |= ATTR_NORMAL | ATTR_TEXCOORD; - break; - - case DEFORM_AUTOSPRITE: - shader.vertexAttribs |= ATTR_NORMAL | ATTR_COLOR; - break; - - case DEFORM_WAVE: - case DEFORM_NORMALS: - case DEFORM_TEXT0: - case DEFORM_TEXT1: - case DEFORM_TEXT2: - case DEFORM_TEXT3: - case DEFORM_TEXT4: - case DEFORM_TEXT5: - case DEFORM_TEXT6: - case DEFORM_TEXT7: - shader.vertexAttribs |= ATTR_NORMAL; - break; - - default: - case DEFORM_NONE: - case DEFORM_MOVE: - case DEFORM_PROJECTION_SHADOW: - case DEFORM_AUTOSPRITE2: - break; - } - } - } - - for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) - { - shaderStage_t *pStage = &stages[stage]; - - if ( !pStage->active ) - { - break; - } - - if (pStage->glslShaderGroup == tr.lightallShader) - { - shader.vertexAttribs |= ATTR_NORMAL; - -#ifdef USE_VERT_TANGENT_SPACE - if (pStage->glslShaderIndex & LIGHTDEF_USE_NORMALMAP) - { - shader.vertexAttribs |= ATTR_BITANGENT | ATTR_TANGENT; - } -#endif - - switch (pStage->glslShaderIndex & LIGHTDEF_LIGHTTYPE_MASK) - { - case LIGHTDEF_USE_LIGHTMAP: - case LIGHTDEF_USE_LIGHT_VERTEX: - shader.vertexAttribs |= ATTR_LIGHTDIRECTION; - break; - default: - break; - } - } - - for (i = 0; i < NUM_TEXTURE_BUNDLES; i++) - { - if ( pStage->bundle[i].image[0] == 0 ) - { - continue; - } - - switch(pStage->bundle[i].tcGen) - { - case TCGEN_TEXTURE: - shader.vertexAttribs |= ATTR_TEXCOORD; - break; - case TCGEN_LIGHTMAP: - shader.vertexAttribs |= ATTR_LIGHTCOORD; - break; - case TCGEN_ENVIRONMENT_MAPPED: - shader.vertexAttribs |= ATTR_NORMAL; - break; - - default: - break; - } - } - - switch(pStage->rgbGen) - { - case CGEN_EXACT_VERTEX: - case CGEN_VERTEX: - case CGEN_EXACT_VERTEX_LIT: - case CGEN_VERTEX_LIT: - case CGEN_ONE_MINUS_VERTEX: - shader.vertexAttribs |= ATTR_COLOR; - break; - - case CGEN_LIGHTING_DIFFUSE: - shader.vertexAttribs |= ATTR_NORMAL; - break; - - default: - break; - } - - switch(pStage->alphaGen) - { - case AGEN_LIGHTING_SPECULAR: - case AGEN_FRESNEL: - shader.vertexAttribs |= ATTR_NORMAL; - break; - - case AGEN_VERTEX: - case AGEN_ONE_MINUS_VERTEX: - shader.vertexAttribs |= ATTR_COLOR; - break; - - default: - break; - } - } -} - -typedef struct { - int blendA; - int blendB; - - int multitextureEnv; - int multitextureBlend; -} collapse_t; - -static collapse_t collapse[] = { - { 0, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, - GL_MODULATE, 0 }, - - { 0, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, - GL_MODULATE, 0 }, - - { GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, - GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, - - { GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, - GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, - - { GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, - GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, - - { GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, - GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, - - { 0, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE, - GL_ADD, 0 }, - - { GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE, - GL_ADD, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE }, -#if 0 - { 0, GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_SRCBLEND_SRC_ALPHA, - GL_DECAL, 0 }, -#endif - { -1 } -}; - -/* -================ -CollapseMultitexture - -Attempt to combine two stages into a single multitexture stage -FIXME: I think modulated add + modulated add collapses incorrectly -================= -*/ -static qboolean CollapseMultitexture( void ) { - int abits, bbits; - int i; - textureBundle_t tmpBundle; - - if ( !qglActiveTextureARB ) { - return qfalse; - } - - // make sure both stages are active - if ( !stages[0].active || !stages[1].active ) { - return qfalse; - } - - // on voodoo2, don't combine different tmus - if ( glConfig.driverType == GLDRV_VOODOO ) { - if ( stages[0].bundle[0].image[0]->TMU == - stages[1].bundle[0].image[0]->TMU ) { - return qfalse; - } - } - - abits = stages[0].stateBits; - bbits = stages[1].stateBits; - - // make sure that both stages have identical state other than blend modes - if ( ( abits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) != - ( bbits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) ) { - return qfalse; - } - - abits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); - bbits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); - - // search for a valid multitexture blend function - for ( i = 0; collapse[i].blendA != -1 ; i++ ) { - if ( abits == collapse[i].blendA - && bbits == collapse[i].blendB ) { - break; - } - } - - // nothing found - if ( collapse[i].blendA == -1 ) { - return qfalse; - } - - // GL_ADD is a separate extension - if ( collapse[i].multitextureEnv == GL_ADD && !glConfig.textureEnvAddAvailable ) { - return qfalse; - } - - // make sure waveforms have identical parameters - if ( ( stages[0].rgbGen != stages[1].rgbGen ) || - ( stages[0].alphaGen != stages[1].alphaGen ) ) { - return qfalse; - } - - // an add collapse can only have identity colors - if ( collapse[i].multitextureEnv == GL_ADD && stages[0].rgbGen != CGEN_IDENTITY ) { - return qfalse; - } - - if ( stages[0].rgbGen == CGEN_WAVEFORM ) - { - if ( memcmp( &stages[0].rgbWave, - &stages[1].rgbWave, - sizeof( stages[0].rgbWave ) ) ) - { - return qfalse; - } - } - if ( stages[0].alphaGen == AGEN_WAVEFORM ) - { - if ( memcmp( &stages[0].alphaWave, - &stages[1].alphaWave, - sizeof( stages[0].alphaWave ) ) ) - { - return qfalse; - } - } - - - // make sure that lightmaps are in bundle 1 for 3dfx - if ( stages[0].bundle[0].isLightmap ) - { - tmpBundle = stages[0].bundle[0]; - stages[0].bundle[0] = stages[1].bundle[0]; - stages[0].bundle[1] = tmpBundle; - } - else - { - stages[0].bundle[1] = stages[1].bundle[0]; - } - - // set the new blend state bits - shader.multitextureEnv = collapse[i].multitextureEnv; - stages[0].stateBits &= ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); - stages[0].stateBits |= collapse[i].multitextureBlend; - - // - // move down subsequent shaders - // - memmove( &stages[1], &stages[2], sizeof( stages[0] ) * ( MAX_SHADER_STAGES - 2 ) ); - Com_Memset( &stages[MAX_SHADER_STAGES-1], 0, sizeof( stages[0] ) ); - - return qtrue; -} - -static void CollapseStagesToLightall(shaderStage_t *diffuse, - shaderStage_t *normal, shaderStage_t *specular, shaderStage_t *lightmap, - qboolean useLightVector, qboolean useLightVertex, qboolean parallax, qboolean environment) -{ - int defs = 0; - - //ri.Printf(PRINT_ALL, "shader %s has diffuse %s", shader.name, diffuse->bundle[0].image[0]->imgName); - - // reuse diffuse, mark others inactive - diffuse->type = ST_GLSL; - - if (lightmap) - { - //ri.Printf(PRINT_ALL, ", lightmap"); - diffuse->bundle[TB_LIGHTMAP] = lightmap->bundle[0]; - defs |= LIGHTDEF_USE_LIGHTMAP; - } - else if (useLightVector) - { - defs |= LIGHTDEF_USE_LIGHT_VECTOR; - } - else if (useLightVertex) - { - defs |= LIGHTDEF_USE_LIGHT_VERTEX; - } - - if (r_deluxeMapping->integer && tr.worldDeluxeMapping && lightmap) - { - //ri.Printf(PRINT_ALL, ", deluxemap"); - diffuse->bundle[TB_DELUXEMAP] = lightmap->bundle[0]; - diffuse->bundle[TB_DELUXEMAP].image[0] = tr.deluxemaps[shader.lightmapIndex]; - defs |= LIGHTDEF_USE_DELUXEMAP; - } - - if (r_normalMapping->integer) - { - image_t *diffuseImg; - if (normal) - { - //ri.Printf(PRINT_ALL, ", normalmap %s", normal->bundle[0].image[0]->imgName); - diffuse->bundle[TB_NORMALMAP] = normal->bundle[0]; - defs |= LIGHTDEF_USE_NORMALMAP; - if (parallax && r_parallaxMapping->integer) - defs |= LIGHTDEF_USE_PARALLAXMAP; - } - else if ((lightmap || useLightVector || useLightVertex) && (diffuseImg = diffuse->bundle[TB_DIFFUSEMAP].image[0])) - { - char normalName[MAX_QPATH]; - image_t *normalImg; - imgFlags_t normalFlags = (diffuseImg->flags & ~(IMGFLAG_GENNORMALMAP | IMGFLAG_SRGB)) | IMGFLAG_NOLIGHTSCALE; - - COM_StripExtension(diffuseImg->imgName, normalName, MAX_QPATH); - Q_strcat(normalName, MAX_QPATH, "_n"); - - normalImg = R_FindImageFile(normalName, IMGTYPE_NORMAL, normalFlags); - - if (normalImg) - { - diffuse->bundle[TB_NORMALMAP] = diffuse->bundle[0]; - diffuse->bundle[TB_NORMALMAP].image[0] = normalImg; - - defs |= LIGHTDEF_USE_NORMALMAP; - if (parallax && r_parallaxMapping->integer) - defs |= LIGHTDEF_USE_PARALLAXMAP; - } - } - } - - if (r_specularMapping->integer) - { - if (specular) - { - //ri.Printf(PRINT_ALL, ", specularmap %s", specular->bundle[0].image[0]->imgName); - diffuse->bundle[TB_SPECULARMAP] = specular->bundle[0]; - diffuse->materialInfo[0] = specular->materialInfo[0]; - diffuse->materialInfo[1] = specular->materialInfo[1]; - defs |= LIGHTDEF_USE_SPECULARMAP; - } - } - - if (environment || diffuse->bundle[0].numTexMods) - { - defs |= LIGHTDEF_USE_TCGEN_AND_TCMOD; - } - - //ri.Printf(PRINT_ALL, ".\n"); - - diffuse->glslShaderGroup = tr.lightallShader; - diffuse->glslShaderIndex = defs; -} - - -static qboolean CollapseStagesToGLSL(void) -{ - int i, j, numStages; - qboolean skip = qfalse; - - // skip shaders with deforms - if (shader.numDeforms != 0) - { - skip = qtrue; - } - - if (!skip) - { - // if 2+ stages and first stage is lightmap, switch them - // this makes it easier for the later bits to process - if (stages[0].active && stages[0].bundle[0].isLightmap && stages[1].active) - { - int blendBits = stages[1].stateBits & ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); - - if (blendBits == (GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO) - || blendBits == (GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR)) - { - int stateBits0 = stages[0].stateBits; - int stateBits1 = stages[1].stateBits; - shaderStage_t swapStage; - - swapStage = stages[0]; - stages[0] = stages[1]; - stages[1] = swapStage; - - stages[0].stateBits = stateBits0; - stages[1].stateBits = stateBits1; - } - } - } - - if (!skip) - { - // scan for shaders that aren't supported - for (i = 0; i < MAX_SHADER_STAGES; i++) - { - shaderStage_t *pStage = &stages[i]; - - if (!pStage->active) - continue; - - if (pStage->adjustColorsForFog) - { - skip = qtrue; - break; - } - - if (pStage->bundle[0].isLightmap) - { - int blendBits = pStage->stateBits & ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); - - if (blendBits != (GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO) - && blendBits != (GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR)) - { - skip = qtrue; - break; - } - } - - switch(pStage->bundle[0].tcGen) - { - case TCGEN_TEXTURE: - case TCGEN_LIGHTMAP: - case TCGEN_ENVIRONMENT_MAPPED: - break; - default: - skip = qtrue; - break; - } - - switch(pStage->alphaGen) - { - case AGEN_LIGHTING_SPECULAR: - case AGEN_PORTAL: - case AGEN_FRESNEL: - skip = qtrue; - break; - default: - break; - } - } - } - - if (!skip) - { - for (i = 0; i < MAX_SHADER_STAGES; i++) - { - shaderStage_t *pStage = &stages[i]; - shaderStage_t *diffuse, *normal, *specular, *lightmap; - qboolean parallax, environment, diffuselit, vertexlit; - - if (!pStage->active) - continue; - - // skip normal and specular maps - if (pStage->type != ST_COLORMAP) - continue; - - // skip lightmaps - if (pStage->bundle[0].isLightmap) - continue; - - diffuse = pStage; - normal = NULL; - parallax = qfalse; - specular = NULL; - lightmap = NULL; - - // we have a diffuse map, find matching normal, specular, and lightmap - for (j = i + 1; j < MAX_SHADER_STAGES; j++) - { - shaderStage_t *pStage2 = &stages[j]; - - if (!pStage2->active) - continue; - - switch(pStage2->type) - { - case ST_NORMALMAP: - if (!normal) - { - normal = pStage2; - } - break; - - case ST_NORMALPARALLAXMAP: - if (!normal) - { - normal = pStage2; - parallax = qtrue; - } - break; - - case ST_SPECULARMAP: - if (!specular) - { - specular = pStage2; - } - break; - - case ST_COLORMAP: - if (pStage2->bundle[0].isLightmap) - { - lightmap = pStage2; - } - break; - - default: - break; - } - } - - environment = qfalse; - if (diffuse->bundle[0].tcGen == TCGEN_ENVIRONMENT_MAPPED) - { - environment = qtrue; - } - - diffuselit = qfalse; - if (diffuse->rgbGen == CGEN_LIGHTING_DIFFUSE) - { - diffuselit = qtrue; - } - - vertexlit = qfalse; - if (diffuse->rgbGen == CGEN_VERTEX_LIT || diffuse->rgbGen == CGEN_EXACT_VERTEX_LIT) - { - vertexlit = qtrue; - } - - CollapseStagesToLightall(diffuse, normal, specular, lightmap, diffuselit, vertexlit, parallax, environment); - } - - // deactivate lightmap stages - for (i = 0; i < MAX_SHADER_STAGES; i++) - { - shaderStage_t *pStage = &stages[i]; - - if (!pStage->active) - continue; - - if (pStage->bundle[0].isLightmap) - { - pStage->active = qfalse; - } - } - } - - // deactivate normal and specular stages - for (i = 0; i < MAX_SHADER_STAGES; i++) - { - shaderStage_t *pStage = &stages[i]; - - if (!pStage->active) - continue; - - if (pStage->type == ST_NORMALMAP) - { - pStage->active = qfalse; - } - - if (pStage->type == ST_NORMALPARALLAXMAP) - { - pStage->active = qfalse; - } - - if (pStage->type == ST_SPECULARMAP) - { - pStage->active = qfalse; - } - } - - // remove inactive stages - numStages = 0; - for (i = 0; i < MAX_SHADER_STAGES; i++) - { - if (!stages[i].active) - continue; - - if (i == numStages) - { - numStages++; - continue; - } - - stages[numStages] = stages[i]; - stages[i].active = qfalse; - numStages++; - } - - if (numStages == i && i >= 2 && CollapseMultitexture()) - numStages--; - - return numStages; -} - -/* -============= - -FixRenderCommandList -https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=493 -Arnout: this is a nasty issue. Shaders can be registered after drawsurfaces are generated -but before the frame is rendered. This will, for the duration of one frame, cause drawsurfaces -to be rendered with bad shaders. To fix this, need to go through all render commands and fix -sortedIndex. -============== -*/ -static void FixRenderCommandList( int newShader ) { - renderCommandList_t *cmdList = &backEndData->commands; - - if( cmdList ) { - const void *curCmd = cmdList->cmds; - - while ( 1 ) { - curCmd = PADP(curCmd, sizeof(void *)); - - switch ( *(const int *)curCmd ) { - case RC_SET_COLOR: - { - const setColorCommand_t *sc_cmd = (const setColorCommand_t *)curCmd; - curCmd = (const void *)(sc_cmd + 1); - break; - } - case RC_STRETCH_PIC: - { - const stretchPicCommand_t *sp_cmd = (const stretchPicCommand_t *)curCmd; - curCmd = (const void *)(sp_cmd + 1); - break; - } - case RC_DRAW_SURFS: - { - int i; - drawSurf_t *drawSurf; - shader_t *shader; - int fogNum; - int entityNum; - int dlightMap; - int pshadowMap; - int sortedIndex; - const drawSurfsCommand_t *ds_cmd = (const drawSurfsCommand_t *)curCmd; - - for( i = 0, drawSurf = ds_cmd->drawSurfs; i < ds_cmd->numDrawSurfs; i++, drawSurf++ ) { - R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlightMap, &pshadowMap ); - sortedIndex = (( drawSurf->sort >> QSORT_SHADERNUM_SHIFT ) & (MAX_SHADERS-1)); - if( sortedIndex >= newShader ) { - sortedIndex++; - drawSurf->sort = (sortedIndex << QSORT_SHADERNUM_SHIFT) | entityNum | ( fogNum << QSORT_FOGNUM_SHIFT ) | ( (int)pshadowMap << QSORT_PSHADOW_SHIFT) | (int)dlightMap; - } - } - curCmd = (const void *)(ds_cmd + 1); - break; - } - case RC_DRAW_BUFFER: - { - const drawBufferCommand_t *db_cmd = (const drawBufferCommand_t *)curCmd; - curCmd = (const void *)(db_cmd + 1); - break; - } - case RC_SWAP_BUFFERS: - { - const swapBuffersCommand_t *sb_cmd = (const swapBuffersCommand_t *)curCmd; - curCmd = (const void *)(sb_cmd + 1); - break; - } - case RC_END_OF_LIST: - default: - return; - } - } - } -} - -/* -============== -SortNewShader - -Positions the most recently created shader in the tr.sortedShaders[] -array so that the shader->sort key is sorted reletive to the other -shaders. - -Sets shader->sortedIndex -============== -*/ -static void SortNewShader( void ) { - int i; - float sort; - shader_t *newShader; - - newShader = tr.shaders[ tr.numShaders - 1 ]; - sort = newShader->sort; - - for ( i = tr.numShaders - 2 ; i >= 0 ; i-- ) { - if ( tr.sortedShaders[ i ]->sort <= sort ) { - break; - } - tr.sortedShaders[i+1] = tr.sortedShaders[i]; - tr.sortedShaders[i+1]->sortedIndex++; - } - - // Arnout: fix rendercommandlist - // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=493 - FixRenderCommandList( i+1 ); - - newShader->sortedIndex = i+1; - tr.sortedShaders[i+1] = newShader; -} - - -/* -==================== -GeneratePermanentShader -==================== -*/ -static shader_t *GeneratePermanentShader( void ) { - shader_t *newShader; - int i, b; - int size, hash; - - if ( tr.numShaders == MAX_SHADERS ) { - ri.Printf( PRINT_WARNING, "WARNING: GeneratePermanentShader - MAX_SHADERS hit\n"); - return tr.defaultShader; - } - - newShader = ri.Hunk_Alloc( sizeof( shader_t ), h_low ); - - *newShader = shader; - - if ( shader.sort <= SS_OPAQUE ) { - newShader->fogPass = FP_EQUAL; - } else if ( shader.contentFlags & CONTENTS_FOG ) { - newShader->fogPass = FP_LE; - } - - tr.shaders[ tr.numShaders ] = newShader; - newShader->index = tr.numShaders; - - tr.sortedShaders[ tr.numShaders ] = newShader; - newShader->sortedIndex = tr.numShaders; - - tr.numShaders++; - - for ( i = 0 ; i < newShader->numUnfoggedPasses ; i++ ) { - if ( !stages[i].active ) { - break; - } - newShader->stages[i] = ri.Hunk_Alloc( sizeof( stages[i] ), h_low ); - *newShader->stages[i] = stages[i]; - - for ( b = 0 ; b < NUM_TEXTURE_BUNDLES ; b++ ) { - size = newShader->stages[i]->bundle[b].numTexMods * sizeof( texModInfo_t ); - newShader->stages[i]->bundle[b].texMods = ri.Hunk_Alloc( size, h_low ); - Com_Memcpy( newShader->stages[i]->bundle[b].texMods, stages[i].bundle[b].texMods, size ); - } - } - - SortNewShader(); - - hash = generateHashValue(newShader->name, FILE_HASH_SIZE); - newShader->next = hashTable[hash]; - hashTable[hash] = newShader; - - return newShader; -} - -/* -================= -VertexLightingCollapse - -If vertex lighting is enabled, only render a single -pass, trying to guess which is the correct one to best aproximate -what it is supposed to look like. -================= -*/ -static void VertexLightingCollapse( void ) { - int stage; - shaderStage_t *bestStage; - int bestImageRank; - int rank; - - // if we aren't opaque, just use the first pass - if ( shader.sort == SS_OPAQUE ) { - - // pick the best texture for the single pass - bestStage = &stages[0]; - bestImageRank = -999999; - - for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) { - shaderStage_t *pStage = &stages[stage]; - - if ( !pStage->active ) { - break; - } - rank = 0; - - if ( pStage->bundle[0].isLightmap ) { - rank -= 100; - } - if ( pStage->bundle[0].tcGen != TCGEN_TEXTURE ) { - rank -= 5; - } - if ( pStage->bundle[0].numTexMods ) { - rank -= 5; - } - if ( pStage->rgbGen != CGEN_IDENTITY && pStage->rgbGen != CGEN_IDENTITY_LIGHTING ) { - rank -= 3; - } - - if ( rank > bestImageRank ) { - bestImageRank = rank; - bestStage = pStage; - } - } - - stages[0].bundle[0] = bestStage->bundle[0]; - stages[0].stateBits &= ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); - stages[0].stateBits |= GLS_DEPTHMASK_TRUE; - if ( shader.lightmapIndex == LIGHTMAP_NONE ) { - stages[0].rgbGen = CGEN_LIGHTING_DIFFUSE; - } else { - stages[0].rgbGen = CGEN_EXACT_VERTEX; - } - stages[0].alphaGen = AGEN_SKIP; - } else { - // don't use a lightmap (tesla coils) - if ( stages[0].bundle[0].isLightmap ) { - stages[0] = stages[1]; - } - - // if we were in a cross-fade cgen, hack it to normal - if ( stages[0].rgbGen == CGEN_ONE_MINUS_ENTITY || stages[1].rgbGen == CGEN_ONE_MINUS_ENTITY ) { - stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; - } - if ( ( stages[0].rgbGen == CGEN_WAVEFORM && stages[0].rgbWave.func == GF_SAWTOOTH ) - && ( stages[1].rgbGen == CGEN_WAVEFORM && stages[1].rgbWave.func == GF_INVERSE_SAWTOOTH ) ) { - stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; - } - if ( ( stages[0].rgbGen == CGEN_WAVEFORM && stages[0].rgbWave.func == GF_INVERSE_SAWTOOTH ) - && ( stages[1].rgbGen == CGEN_WAVEFORM && stages[1].rgbWave.func == GF_SAWTOOTH ) ) { - stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; - } - } - - for ( stage = 1; stage < MAX_SHADER_STAGES; stage++ ) { - shaderStage_t *pStage = &stages[stage]; - - if ( !pStage->active ) { - break; - } - - Com_Memset( pStage, 0, sizeof( *pStage ) ); - } -} - -/* -========================= -FinishShader - -Returns a freshly allocated shader with all the needed info -from the current global working shader -========================= -*/ -static shader_t *FinishShader( void ) { - int stage; - qboolean hasLightmapStage; - qboolean vertexLightmap; - - hasLightmapStage = qfalse; - vertexLightmap = qfalse; - - // - // set sky stuff appropriate - // - if ( shader.isSky ) { - shader.sort = SS_ENVIRONMENT; - } - - // - // set polygon offset - // - if ( shader.polygonOffset && !shader.sort ) { - shader.sort = SS_DECAL; - } - - // - // set appropriate stage information - // - for ( stage = 0; stage < MAX_SHADER_STAGES; ) { - shaderStage_t *pStage = &stages[stage]; - - if ( !pStage->active ) { - break; - } - - // check for a missing texture - if ( !pStage->bundle[0].image[0] ) { - ri.Printf( PRINT_WARNING, "Shader %s has a stage with no image\n", shader.name ); - pStage->active = qfalse; - stage++; - continue; - } - - // - // ditch this stage if it's detail and detail textures are disabled - // - if ( pStage->isDetail && !r_detailTextures->integer ) - { - int index; - - for(index = stage + 1; index < MAX_SHADER_STAGES; index++) - { - if(!stages[index].active) - break; - } - - if(index < MAX_SHADER_STAGES) - memmove(pStage, pStage + 1, sizeof(*pStage) * (index - stage)); - else - { - if(stage + 1 < MAX_SHADER_STAGES) - memmove(pStage, pStage + 1, sizeof(*pStage) * (index - stage - 1)); - - Com_Memset(&stages[index - 1], 0, sizeof(*stages)); - } - - continue; - } - - // - // default texture coordinate generation - // - if ( pStage->bundle[0].isLightmap ) { - if ( pStage->bundle[0].tcGen == TCGEN_BAD ) { - pStage->bundle[0].tcGen = TCGEN_LIGHTMAP; - } - hasLightmapStage = qtrue; - } else { - if ( pStage->bundle[0].tcGen == TCGEN_BAD ) { - pStage->bundle[0].tcGen = TCGEN_TEXTURE; - } - } - - - // not a true lightmap but we want to leave existing - // behaviour in place and not print out a warning - //if (pStage->rgbGen == CGEN_VERTEX) { - // vertexLightmap = qtrue; - //} - - - - // - // determine sort order and fog color adjustment - // - if ( ( pStage->stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) && - ( stages[0].stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) ) { - int blendSrcBits = pStage->stateBits & GLS_SRCBLEND_BITS; - int blendDstBits = pStage->stateBits & GLS_DSTBLEND_BITS; - - // fog color adjustment only works for blend modes that have a contribution - // that aproaches 0 as the modulate values aproach 0 -- - // GL_ONE, GL_ONE - // GL_ZERO, GL_ONE_MINUS_SRC_COLOR - // GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA - - // modulate, additive - if ( ( ( blendSrcBits == GLS_SRCBLEND_ONE ) && ( blendDstBits == GLS_DSTBLEND_ONE ) ) || - ( ( blendSrcBits == GLS_SRCBLEND_ZERO ) && ( blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_COLOR ) ) ) { - pStage->adjustColorsForFog = ACFF_MODULATE_RGB; - } - // strict blend - else if ( ( blendSrcBits == GLS_SRCBLEND_SRC_ALPHA ) && ( blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ) ) - { - pStage->adjustColorsForFog = ACFF_MODULATE_ALPHA; - } - // premultiplied alpha - else if ( ( blendSrcBits == GLS_SRCBLEND_ONE ) && ( blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ) ) - { - pStage->adjustColorsForFog = ACFF_MODULATE_RGBA; - } else { - // we can't adjust this one correctly, so it won't be exactly correct in fog - } - - // don't screw with sort order if this is a portal or environment - if ( !shader.sort ) { - // see through item, like a grill or grate - if ( pStage->stateBits & GLS_DEPTHMASK_TRUE ) { - shader.sort = SS_SEE_THROUGH; - } else { - shader.sort = SS_BLEND0; - } - } - } - - stage++; - } - - // there are times when you will need to manually apply a sort to - // opaque alpha tested shaders that have later blend passes - if ( !shader.sort ) { - shader.sort = SS_OPAQUE; - } - - // - // if we are in r_vertexLight mode, never use a lightmap texture - // - if ( stage > 1 && ( (r_vertexLight->integer && !r_uiFullScreen->integer) || glConfig.hardwareType == GLHW_PERMEDIA2 ) ) { - VertexLightingCollapse(); - stage = 1; - hasLightmapStage = qfalse; - } - - // - // look for multitexture potential - // - stage = CollapseStagesToGLSL(); - - if ( shader.lightmapIndex >= 0 && !hasLightmapStage ) { - if (vertexLightmap) { - ri.Printf( PRINT_DEVELOPER, "WARNING: shader '%s' has VERTEX forced lightmap!\n", shader.name ); - } else { - ri.Printf( PRINT_DEVELOPER, "WARNING: shader '%s' has lightmap but no lightmap stage!\n", shader.name ); - // Don't set this, it will just add duplicate shaders to the hash - //shader.lightmapIndex = LIGHTMAP_NONE; - } - } - - - // - // compute number of passes - // - shader.numUnfoggedPasses = stage; - - // fogonly shaders don't have any normal passes - if (stage == 0 && !shader.isSky) - shader.sort = SS_FOG; - - // determine which stage iterator function is appropriate - ComputeStageIteratorFunc(); - - // determine which vertex attributes this shader needs - ComputeVertexAttribs(); - - return GeneratePermanentShader(); -} - -//======================================================================================== - -/* -==================== -FindShaderInShaderText - -Scans the combined text description of all the shader files for -the given shader name. - -return NULL if not found - -If found, it will return a valid shader -===================== -*/ -static char *FindShaderInShaderText( const char *shadername ) { - - char *token, *p; - - int i, hash; - - hash = generateHashValue(shadername, MAX_SHADERTEXT_HASH); - - if(shaderTextHashTable[hash]) - { - for (i = 0; shaderTextHashTable[hash][i]; i++) - { - p = shaderTextHashTable[hash][i]; - token = COM_ParseExt(&p, qtrue); - - if(!Q_stricmp(token, shadername)) - return p; - } - } - - p = s_shaderText; - - if ( !p ) { - return NULL; - } - - // look for label - while ( 1 ) { - token = COM_ParseExt( &p, qtrue ); - if ( token[0] == 0 ) { - break; - } - - if ( !Q_stricmp( token, shadername ) ) { - return p; - } - else { - // skip the definition - SkipBracedSection( &p ); - } - } - - return NULL; -} - - -/* -================== -R_FindShaderByName - -Will always return a valid shader, but it might be the -default shader if the real one can't be found. -================== -*/ -shader_t *R_FindShaderByName( const char *name ) { - char strippedName[MAX_QPATH]; - int hash; - shader_t *sh; - - if ( (name==NULL) || (name[0] == 0) ) { - return tr.defaultShader; - } - - COM_StripExtension(name, strippedName, sizeof(strippedName)); - - hash = generateHashValue(strippedName, FILE_HASH_SIZE); - - // - // see if the shader is already loaded - // - for (sh=hashTable[hash]; sh; sh=sh->next) { - // NOTE: if there was no shader or image available with the name strippedName - // then a default shader is created with lightmapIndex == LIGHTMAP_NONE, so we - // have to check all default shaders otherwise for every call to R_FindShader - // with that same strippedName a new default shader is created. - if (Q_stricmp(sh->name, strippedName) == 0) { - // match found - return sh; - } - } - - return tr.defaultShader; -} - - -/* -=============== -R_FindShader - -Will always return a valid shader, but it might be the -default shader if the real one can't be found. - -In the interest of not requiring an explicit shader text entry to -be defined for every single image used in the game, three default -shader behaviors can be auto-created for any image: - -If lightmapIndex == LIGHTMAP_NONE, then the image will have -dynamic diffuse lighting applied to it, as apropriate for most -entity skin surfaces. - -If lightmapIndex == LIGHTMAP_2D, then the image will be used -for 2D rendering unless an explicit shader is found - -If lightmapIndex == LIGHTMAP_BY_VERTEX, then the image will use -the vertex rgba modulate values, as apropriate for misc_model -pre-lit surfaces. - -Other lightmapIndex values will have a lightmap stage created -and src*dest blending applied with the texture, as apropriate for -most world construction surfaces. - -=============== -*/ -shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImage ) { - char strippedName[MAX_QPATH]; - int i, hash; - char *shaderText; - image_t *image; - shader_t *sh; - - if ( name[0] == 0 ) { - return tr.defaultShader; - } - - // use (fullbright) vertex lighting if the bsp file doesn't have - // lightmaps - if ( lightmapIndex >= 0 && lightmapIndex >= tr.numLightmaps ) { - lightmapIndex = LIGHTMAP_BY_VERTEX; - } else if ( lightmapIndex < LIGHTMAP_2D ) { - // negative lightmap indexes cause stray pointers (think tr.lightmaps[lightmapIndex]) - ri.Printf( PRINT_WARNING, "WARNING: shader '%s' has invalid lightmap index of %d\n", name, lightmapIndex ); - lightmapIndex = LIGHTMAP_BY_VERTEX; - } - - COM_StripExtension(name, strippedName, sizeof(strippedName)); - - hash = generateHashValue(strippedName, FILE_HASH_SIZE); - - // - // see if the shader is already loaded - // - for (sh = hashTable[hash]; sh; sh = sh->next) { - // NOTE: if there was no shader or image available with the name strippedName - // then a default shader is created with lightmapIndex == LIGHTMAP_NONE, so we - // have to check all default shaders otherwise for every call to R_FindShader - // with that same strippedName a new default shader is created. - if ( (sh->lightmapIndex == lightmapIndex || sh->defaultShader) && - !Q_stricmp(sh->name, strippedName)) { - // match found - return sh; - } - } - - // clear the global shader - Com_Memset( &shader, 0, sizeof( shader ) ); - Com_Memset( &stages, 0, sizeof( stages ) ); - Q_strncpyz(shader.name, strippedName, sizeof(shader.name)); - shader.lightmapIndex = lightmapIndex; - for ( i = 0 ; i < MAX_SHADER_STAGES ; i++ ) { - stages[i].bundle[0].texMods = texMods[i]; - } - - // - // attempt to define shader from an explicit parameter file - // - shaderText = FindShaderInShaderText( strippedName ); - if ( shaderText ) { - // enable this when building a pak file to get a global list - // of all explicit shaders - if ( r_printShaders->integer ) { - ri.Printf( PRINT_ALL, "*SHADER* %s\n", name ); - } - - if ( !ParseShader( &shaderText ) ) { - // had errors, so use default shader - shader.defaultShader = qtrue; - } - sh = FinishShader(); - return sh; - } - - - // - // if not defined in the in-memory shader descriptions, - // look for a single supported image file - // - { - imgFlags_t flags; - - flags = IMGFLAG_NONE; - - if (r_srgb->integer) - flags |= IMGFLAG_SRGB; - - if (mipRawImage) - { - flags |= IMGFLAG_MIPMAP | IMGFLAG_PICMIP; - - if (r_genNormalMaps->integer) - flags |= IMGFLAG_GENNORMALMAP; - } - else - { - flags |= IMGFLAG_CLAMPTOEDGE; - } - - image = R_FindImageFile( name, IMGTYPE_COLORALPHA, flags ); - if ( !image ) { - ri.Printf( PRINT_DEVELOPER, "Couldn't find image file for shader %s\n", name ); - shader.defaultShader = qtrue; - return FinishShader(); - } - } - - // - // create the default shading commands - // - if ( shader.lightmapIndex == LIGHTMAP_NONE ) { - // dynamic colors at vertexes - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_LIGHTING_DIFFUSE; - stages[0].stateBits = GLS_DEFAULT; - } else if ( shader.lightmapIndex == LIGHTMAP_BY_VERTEX ) { - // explicit colors at vertexes - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_EXACT_VERTEX; - stages[0].alphaGen = AGEN_SKIP; - stages[0].stateBits = GLS_DEFAULT; - } else if ( shader.lightmapIndex == LIGHTMAP_2D ) { - // GUI elements - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_VERTEX; - stages[0].alphaGen = AGEN_VERTEX; - stages[0].stateBits = GLS_DEPTHTEST_DISABLE | - GLS_SRCBLEND_SRC_ALPHA | - GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; - } else if ( shader.lightmapIndex == LIGHTMAP_WHITEIMAGE ) { - // fullbright level - stages[0].bundle[0].image[0] = tr.whiteImage; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; - stages[0].stateBits = GLS_DEFAULT; - - stages[1].bundle[0].image[0] = image; - stages[1].active = qtrue; - stages[1].rgbGen = CGEN_IDENTITY; - stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; - } else { - // two pass lightmap - stages[0].bundle[0].image[0] = tr.lightmaps[shader.lightmapIndex]; - stages[0].bundle[0].isLightmap = qtrue; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_IDENTITY; // lightmaps are scaled on creation - // for identitylight - stages[0].stateBits = GLS_DEFAULT; - - stages[1].bundle[0].image[0] = image; - stages[1].active = qtrue; - stages[1].rgbGen = CGEN_IDENTITY; - stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; - } - - return FinishShader(); -} - - -qhandle_t RE_RegisterShaderFromImage(const char *name, int lightmapIndex, image_t *image, qboolean mipRawImage) { - int i, hash; - shader_t *sh; - - hash = generateHashValue(name, FILE_HASH_SIZE); - - // probably not necessary since this function - // only gets called from tr_font.c with lightmapIndex == LIGHTMAP_2D - // but better safe than sorry. - if ( lightmapIndex >= tr.numLightmaps ) { - lightmapIndex = LIGHTMAP_WHITEIMAGE; - } - - // - // see if the shader is already loaded - // - for (sh=hashTable[hash]; sh; sh=sh->next) { - // NOTE: if there was no shader or image available with the name strippedName - // then a default shader is created with lightmapIndex == LIGHTMAP_NONE, so we - // have to check all default shaders otherwise for every call to R_FindShader - // with that same strippedName a new default shader is created. - if ( (sh->lightmapIndex == lightmapIndex || sh->defaultShader) && - // index by name - !Q_stricmp(sh->name, name)) { - // match found - return sh->index; - } - } - - // clear the global shader - Com_Memset( &shader, 0, sizeof( shader ) ); - Com_Memset( &stages, 0, sizeof( stages ) ); - Q_strncpyz(shader.name, name, sizeof(shader.name)); - shader.lightmapIndex = lightmapIndex; - for ( i = 0 ; i < MAX_SHADER_STAGES ; i++ ) { - stages[i].bundle[0].texMods = texMods[i]; - } - - // - // create the default shading commands - // - if ( shader.lightmapIndex == LIGHTMAP_NONE ) { - // dynamic colors at vertexes - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_LIGHTING_DIFFUSE; - stages[0].stateBits = GLS_DEFAULT; - } else if ( shader.lightmapIndex == LIGHTMAP_BY_VERTEX ) { - // explicit colors at vertexes - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_EXACT_VERTEX; - stages[0].alphaGen = AGEN_SKIP; - stages[0].stateBits = GLS_DEFAULT; - } else if ( shader.lightmapIndex == LIGHTMAP_2D ) { - // GUI elements - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_VERTEX; - stages[0].alphaGen = AGEN_VERTEX; - stages[0].stateBits = GLS_DEPTHTEST_DISABLE | - GLS_SRCBLEND_SRC_ALPHA | - GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; - } else if ( shader.lightmapIndex == LIGHTMAP_WHITEIMAGE ) { - // fullbright level - stages[0].bundle[0].image[0] = tr.whiteImage; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; - stages[0].stateBits = GLS_DEFAULT; - - stages[1].bundle[0].image[0] = image; - stages[1].active = qtrue; - stages[1].rgbGen = CGEN_IDENTITY; - stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; - } else { - // two pass lightmap - stages[0].bundle[0].image[0] = tr.lightmaps[shader.lightmapIndex]; - stages[0].bundle[0].isLightmap = qtrue; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_IDENTITY; // lightmaps are scaled on creation - // for identitylight - stages[0].stateBits = GLS_DEFAULT; - - stages[1].bundle[0].image[0] = image; - stages[1].active = qtrue; - stages[1].rgbGen = CGEN_IDENTITY; - stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; - } - - sh = FinishShader(); - return sh->index; -} - - -/* -==================== -RE_RegisterShader - -This is the exported shader entry point for the rest of the system -It will always return an index that will be valid. - -This should really only be used for explicit shaders, because there is no -way to ask for different implicit lighting modes (vertex, lightmap, etc) -==================== -*/ -qhandle_t RE_RegisterShaderLightMap( const char *name, int lightmapIndex ) { - shader_t *sh; - - if ( strlen( name ) >= MAX_QPATH ) { - ri.Printf( PRINT_ALL, "Shader name exceeds MAX_QPATH\n" ); - return 0; - } - - sh = R_FindShader( name, lightmapIndex, qtrue ); - - // we want to return 0 if the shader failed to - // load for some reason, but R_FindShader should - // still keep a name allocated for it, so if - // something calls RE_RegisterShader again with - // the same name, we don't try looking for it again - if ( sh->defaultShader ) { - return 0; - } - - return sh->index; -} - - -/* -==================== -RE_RegisterShader - -This is the exported shader entry point for the rest of the system -It will always return an index that will be valid. - -This should really only be used for explicit shaders, because there is no -way to ask for different implicit lighting modes (vertex, lightmap, etc) -==================== -*/ -qhandle_t RE_RegisterShader( const char *name ) { - shader_t *sh; - - if ( strlen( name ) >= MAX_QPATH ) { - ri.Printf( PRINT_ALL, "Shader name exceeds MAX_QPATH\n" ); - return 0; - } - - sh = R_FindShader( name, LIGHTMAP_2D, qtrue ); - - // we want to return 0 if the shader failed to - // load for some reason, but R_FindShader should - // still keep a name allocated for it, so if - // something calls RE_RegisterShader again with - // the same name, we don't try looking for it again - if ( sh->defaultShader ) { - return 0; - } - - return sh->index; -} - - -/* -==================== -RE_RegisterShaderNoMip - -For menu graphics that should never be picmiped -==================== -*/ -qhandle_t RE_RegisterShaderNoMip( const char *name ) { - shader_t *sh; - - if ( strlen( name ) >= MAX_QPATH ) { - ri.Printf( PRINT_ALL, "Shader name exceeds MAX_QPATH\n" ); - return 0; - } - - sh = R_FindShader( name, LIGHTMAP_2D, qfalse ); - - // we want to return 0 if the shader failed to - // load for some reason, but R_FindShader should - // still keep a name allocated for it, so if - // something calls RE_RegisterShader again with - // the same name, we don't try looking for it again - if ( sh->defaultShader ) { - return 0; - } - - return sh->index; -} - -/* -==================== -R_GetShaderByHandle - -When a handle is passed in by another module, this range checks -it and returns a valid (possibly default) shader_t to be used internally. -==================== -*/ -shader_t *R_GetShaderByHandle( qhandle_t hShader ) { - if ( hShader < 0 ) { - ri.Printf( PRINT_WARNING, "R_GetShaderByHandle: out of range hShader '%d'\n", hShader ); - return tr.defaultShader; - } - if ( hShader >= tr.numShaders ) { - ri.Printf( PRINT_WARNING, "R_GetShaderByHandle: out of range hShader '%d'\n", hShader ); - return tr.defaultShader; - } - return tr.shaders[hShader]; -} - -/* -=============== -R_ShaderList_f - -Dump information on all valid shaders to the console -A second parameter will cause it to print in sorted order -=============== -*/ -void R_ShaderList_f (void) { - int i; - int count; - shader_t *shader; - - ri.Printf (PRINT_ALL, "-----------------------\n"); - - count = 0; - for ( i = 0 ; i < tr.numShaders ; i++ ) { - if ( ri.Cmd_Argc() > 1 ) { - shader = tr.sortedShaders[i]; - } else { - shader = tr.shaders[i]; - } - - ri.Printf( PRINT_ALL, "%i ", shader->numUnfoggedPasses ); - - if (shader->lightmapIndex >= 0 ) { - ri.Printf (PRINT_ALL, "L "); - } else { - ri.Printf (PRINT_ALL, " "); - } - if ( shader->multitextureEnv == GL_ADD ) { - ri.Printf( PRINT_ALL, "MT(a) " ); - } else if ( shader->multitextureEnv == GL_MODULATE ) { - ri.Printf( PRINT_ALL, "MT(m) " ); - } else if ( shader->multitextureEnv == GL_DECAL ) { - ri.Printf( PRINT_ALL, "MT(d) " ); - } else { - ri.Printf( PRINT_ALL, " " ); - } - if ( shader->explicitlyDefined ) { - ri.Printf( PRINT_ALL, "E " ); - } else { - ri.Printf( PRINT_ALL, " " ); - } - - if ( shader->optimalStageIteratorFunc == RB_StageIteratorGeneric ) { - ri.Printf( PRINT_ALL, "gen " ); - } else if ( shader->optimalStageIteratorFunc == RB_StageIteratorSky ) { - ri.Printf( PRINT_ALL, "sky " ); - } else { - ri.Printf( PRINT_ALL, " " ); - } - - if ( shader->defaultShader ) { - ri.Printf (PRINT_ALL, ": %s (DEFAULTED)\n", shader->name); - } else { - ri.Printf (PRINT_ALL, ": %s\n", shader->name); - } - count++; - } - ri.Printf (PRINT_ALL, "%i total shaders\n", count); - ri.Printf (PRINT_ALL, "------------------\n"); -} - -/* -==================== -ScanAndLoadShaderFiles - -Finds and loads all .shader files, combining them into -a single large text block that can be scanned for shader names -===================== -*/ -#define MAX_SHADER_FILES 4096 -static void ScanAndLoadShaderFiles( void ) -{ - char **shaderFiles; - char *buffers[MAX_SHADER_FILES]; - char *p; - int numShaderFiles; - int i; - char *oldp, *token, *hashMem, *textEnd; - int shaderTextHashTableSizes[MAX_SHADERTEXT_HASH], hash, size; - - long sum = 0, summand; - // scan for shader files - shaderFiles = ri.FS_ListFiles( "scripts", ".shader", &numShaderFiles ); - - if ( !shaderFiles || !numShaderFiles ) - { - ri.Printf( PRINT_WARNING, "WARNING: no shader files found\n" ); - return; - } - - if ( numShaderFiles > MAX_SHADER_FILES ) { - numShaderFiles = MAX_SHADER_FILES; - } - - // load and parse shader files - for ( i = 0; i < numShaderFiles; i++ ) - { - char filename[MAX_QPATH]; - - // look for a .mtr file first - { - char *ext; - Com_sprintf( filename, sizeof( filename ), "scripts/%s", shaderFiles[i] ); - if ( (ext = strrchr(filename, '.')) ) - { - strcpy(ext, ".mtr"); - } - - if ( ri.FS_ReadFile( filename, NULL ) <= 0 ) - { - Com_sprintf( filename, sizeof( filename ), "scripts/%s", shaderFiles[i] ); - } - } - - ri.Printf( PRINT_DEVELOPER, "...loading '%s'\n", filename ); - summand = ri.FS_ReadFile( filename, (void **)&buffers[i] ); - - if ( !buffers[i] ) - ri.Error( ERR_DROP, "Couldn't load %s", filename ); - - // Do a simple check on the shader structure in that file to make sure one bad shader file cannot fuck up all other shaders. - p = buffers[i]; - while(1) - { - token = COM_ParseExt(&p, qtrue); - - if(!*token) - break; - - oldp = p; - - token = COM_ParseExt(&p, qtrue); - if(token[0] != '{' && token[1] != '\0') - { - ri.Printf(PRINT_WARNING, "WARNING: Bad shader file %s has incorrect syntax.\n", filename); - ri.FS_FreeFile(buffers[i]); - buffers[i] = NULL; - break; - } - - SkipBracedSection(&oldp); - p = oldp; - } - - - if (buffers[i]) - sum += summand; - } - - // build single large buffer - s_shaderText = ri.Hunk_Alloc( sum + numShaderFiles*2, h_low ); - s_shaderText[ 0 ] = '\0'; - textEnd = s_shaderText; - - // free in reverse order, so the temp files are all dumped - for ( i = numShaderFiles - 1; i >= 0 ; i-- ) - { - if ( !buffers[i] ) - continue; - - strcat( textEnd, buffers[i] ); - strcat( textEnd, "\n" ); - textEnd += strlen( textEnd ); - ri.FS_FreeFile( buffers[i] ); - } - - COM_Compress( s_shaderText ); - - // free up memory - ri.FS_FreeFileList( shaderFiles ); - - Com_Memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes)); - size = 0; - - p = s_shaderText; - // look for shader names - while ( 1 ) { - token = COM_ParseExt( &p, qtrue ); - if ( token[0] == 0 ) { - break; - } - - hash = generateHashValue(token, MAX_SHADERTEXT_HASH); - shaderTextHashTableSizes[hash]++; - size++; - SkipBracedSection(&p); - } - - size += MAX_SHADERTEXT_HASH; - - hashMem = ri.Hunk_Alloc( size * sizeof(char *), h_low ); - - for (i = 0; i < MAX_SHADERTEXT_HASH; i++) { - shaderTextHashTable[i] = (char **) hashMem; - hashMem = ((char *) hashMem) + ((shaderTextHashTableSizes[i] + 1) * sizeof(char *)); - } - - Com_Memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes)); - - p = s_shaderText; - // look for shader names - while ( 1 ) { - oldp = p; - token = COM_ParseExt( &p, qtrue ); - if ( token[0] == 0 ) { - break; - } - - hash = generateHashValue(token, MAX_SHADERTEXT_HASH); - shaderTextHashTable[hash][shaderTextHashTableSizes[hash]++] = oldp; - - SkipBracedSection(&p); - } - - return; - -} - - -/* -==================== -CreateInternalShaders -==================== -*/ -static void CreateInternalShaders( void ) { - tr.numShaders = 0; - - // init the default shader - Com_Memset( &shader, 0, sizeof( shader ) ); - Com_Memset( &stages, 0, sizeof( stages ) ); - - Q_strncpyz( shader.name, "", sizeof( shader.name ) ); - - shader.lightmapIndex = LIGHTMAP_NONE; - stages[0].bundle[0].image[0] = tr.defaultImage; - stages[0].active = qtrue; - stages[0].stateBits = GLS_DEFAULT; - tr.defaultShader = FinishShader(); - - // shadow shader is just a marker - Q_strncpyz( shader.name, "", sizeof( shader.name ) ); - shader.sort = SS_STENCIL_SHADOW; - tr.shadowShader = FinishShader(); -} - -static void CreateExternalShaders( void ) { - tr.projectionShadowShader = R_FindShader( "projectionShadow", LIGHTMAP_NONE, qtrue ); - tr.flareShader = R_FindShader( "flareShader", LIGHTMAP_NONE, qtrue ); - - // Hack to make fogging work correctly on flares. Fog colors are calculated - // in tr_flare.c already. - if(!tr.flareShader->defaultShader) - { - int index; - - for(index = 0; index < tr.flareShader->numUnfoggedPasses; index++) - { - tr.flareShader->stages[index]->adjustColorsForFog = ACFF_NONE; - tr.flareShader->stages[index]->stateBits |= GLS_DEPTHTEST_DISABLE; - } - } - - tr.sunShader = R_FindShader( "sun", LIGHTMAP_NONE, qtrue ); - - tr.sunFlareShader = R_FindShader( "gfx/2d/sunflare", LIGHTMAP_NONE, qtrue); - - // HACK: if sunflare is missing, make one using the flare image or dlight image - if (tr.sunFlareShader->defaultShader) - { - image_t *image; - - if (!tr.flareShader->defaultShader && tr.flareShader->stages[0] && tr.flareShader->stages[0]->bundle[0].image[0]) - image = tr.flareShader->stages[0]->bundle[0].image[0]; - else - image = tr.dlightImage; - - Com_Memset( &shader, 0, sizeof( shader ) ); - Com_Memset( &stages, 0, sizeof( stages ) ); - - Q_strncpyz( shader.name, "gfx/2d/sunflare", sizeof( shader.name ) ); - - shader.lightmapIndex = LIGHTMAP_NONE; - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].stateBits = GLS_DEFAULT; - tr.sunFlareShader = FinishShader(); - } - -} - -/* -================== -R_InitShaders -================== -*/ -void R_InitShaders( void ) { - ri.Printf( PRINT_ALL, "Initializing Shaders\n" ); - - Com_Memset(hashTable, 0, sizeof(hashTable)); - - CreateInternalShaders(); - - ScanAndLoadShaderFiles(); - - CreateExternalShaders(); -} diff --git a/src/rend2/tr_shadows.c b/src/rend2/tr_shadows.c deleted file mode 100644 index f412b00d..00000000 --- a/src/rend2/tr_shadows.c +++ /dev/null @@ -1,343 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#include "tr_local.h" - - -/* - - for a projection shadow: - - point[x] += light vector * ( z - shadow plane ) - point[y] += - point[z] = shadow plane - - 1 0 light[x] / light[z] - -*/ - -typedef struct { - int i2; - int facing; -} edgeDef_t; - -#define MAX_EDGE_DEFS 32 - -static edgeDef_t edgeDefs[SHADER_MAX_VERTEXES][MAX_EDGE_DEFS]; -static int numEdgeDefs[SHADER_MAX_VERTEXES]; -static int facing[SHADER_MAX_INDEXES/3]; - -void R_AddEdgeDef( int i1, int i2, int facing ) { - int c; - - c = numEdgeDefs[ i1 ]; - if ( c == MAX_EDGE_DEFS ) { - return; // overflow - } - edgeDefs[ i1 ][ c ].i2 = i2; - edgeDefs[ i1 ][ c ].facing = facing; - - numEdgeDefs[ i1 ]++; -} - -void R_RenderShadowEdges( void ) { - int i; - -#if 0 - int numTris; - - // dumb way -- render every triangle's edges - numTris = tess.numIndexes / 3; - - for ( i = 0 ; i < numTris ; i++ ) { - int i1, i2, i3; - - if ( !facing[i] ) { - continue; - } - - i1 = tess.indexes[ i*3 + 0 ]; - i2 = tess.indexes[ i*3 + 1 ]; - i3 = tess.indexes[ i*3 + 2 ]; - - qglBegin( GL_TRIANGLE_STRIP ); - qglVertex3fv( tess.xyz[ i1 ] ); - qglVertex3fv( tess.xyz[ i1 + tess.numVertexes ] ); - qglVertex3fv( tess.xyz[ i2 ] ); - qglVertex3fv( tess.xyz[ i2 + tess.numVertexes ] ); - qglVertex3fv( tess.xyz[ i3 ] ); - qglVertex3fv( tess.xyz[ i3 + tess.numVertexes ] ); - qglVertex3fv( tess.xyz[ i1 ] ); - qglVertex3fv( tess.xyz[ i1 + tess.numVertexes ] ); - qglEnd(); - } -#else - int c, c2; - int j, k; - int i2; - int c_edges, c_rejected; - int hit[2]; - - // an edge is NOT a silhouette edge if its face doesn't face the light, - // or if it has a reverse paired edge that also faces the light. - // A well behaved polyhedron would have exactly two faces for each edge, - // but lots of models have dangling edges or overfanned edges - c_edges = 0; - c_rejected = 0; - - for ( i = 0 ; i < tess.numVertexes ; i++ ) { - c = numEdgeDefs[ i ]; - for ( j = 0 ; j < c ; j++ ) { - if ( !edgeDefs[ i ][ j ].facing ) { - continue; - } - - hit[0] = 0; - hit[1] = 0; - - i2 = edgeDefs[ i ][ j ].i2; - c2 = numEdgeDefs[ i2 ]; - for ( k = 0 ; k < c2 ; k++ ) { - if ( edgeDefs[ i2 ][ k ].i2 == i ) { - hit[ edgeDefs[ i2 ][ k ].facing ]++; - } - } - - // if it doesn't share the edge with another front facing - // triangle, it is a sil edge - if ( hit[ 1 ] == 0 ) { - qglBegin( GL_TRIANGLE_STRIP ); - qglVertex3fv( tess.xyz[ i ] ); - qglVertex3fv( tess.xyz[ i + tess.numVertexes ] ); - qglVertex3fv( tess.xyz[ i2 ] ); - qglVertex3fv( tess.xyz[ i2 + tess.numVertexes ] ); - qglEnd(); - c_edges++; - } else { - c_rejected++; - } - } - } -#endif -} - -/* -================= -RB_ShadowTessEnd - -triangleFromEdge[ v1 ][ v2 ] - - - set triangle from edge( v1, v2, tri ) - if ( facing[ triangleFromEdge[ v1 ][ v2 ] ] && !facing[ triangleFromEdge[ v2 ][ v1 ] ) { - } -================= -*/ -void RB_ShadowTessEnd( void ) { - int i; - int numTris; - vec3_t lightDir; - GLboolean rgba[4]; - - // we can only do this if we have enough space in the vertex buffers - if ( tess.numVertexes >= SHADER_MAX_VERTEXES / 2 ) { - return; - } - - if ( glConfig.stencilBits < 4 ) { - return; - } - - VectorCopy( backEnd.currentEntity->lightDir, lightDir ); - - // project vertexes away from light direction - for ( i = 0 ; i < tess.numVertexes ; i++ ) { - VectorMA( tess.xyz[i], -512, lightDir, tess.xyz[i+tess.numVertexes] ); - } - - // decide which triangles face the light - Com_Memset( numEdgeDefs, 0, 4 * tess.numVertexes ); - - numTris = tess.numIndexes / 3; - for ( i = 0 ; i < numTris ; i++ ) { - int i1, i2, i3; - vec3_t d1, d2, normal; - float *v1, *v2, *v3; - float d; - - i1 = tess.indexes[ i*3 + 0 ]; - i2 = tess.indexes[ i*3 + 1 ]; - i3 = tess.indexes[ i*3 + 2 ]; - - v1 = tess.xyz[ i1 ]; - v2 = tess.xyz[ i2 ]; - v3 = tess.xyz[ i3 ]; - - VectorSubtract( v2, v1, d1 ); - VectorSubtract( v3, v1, d2 ); - CrossProduct( d1, d2, normal ); - - d = DotProduct( normal, lightDir ); - if ( d > 0 ) { - facing[ i ] = 1; - } else { - facing[ i ] = 0; - } - - // create the edges - R_AddEdgeDef( i1, i2, facing[ i ] ); - R_AddEdgeDef( i2, i3, facing[ i ] ); - R_AddEdgeDef( i3, i1, facing[ i ] ); - } - - // draw the silhouette edges - - GL_Bind( tr.whiteImage ); - qglEnable( GL_CULL_FACE ); - GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO ); - qglColor3f( 0.2f, 0.2f, 0.2f ); - - // don't write to the color buffer - qglGetBooleanv(GL_COLOR_WRITEMASK, rgba); - qglColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); - - qglEnable( GL_STENCIL_TEST ); - qglStencilFunc( GL_ALWAYS, 1, 255 ); - - // mirrors have the culling order reversed - if ( backEnd.viewParms.isMirror ) { - qglCullFace( GL_FRONT ); - qglStencilOp( GL_KEEP, GL_KEEP, GL_INCR ); - - R_RenderShadowEdges(); - - qglCullFace( GL_BACK ); - qglStencilOp( GL_KEEP, GL_KEEP, GL_DECR ); - - R_RenderShadowEdges(); - } else { - qglCullFace( GL_BACK ); - qglStencilOp( GL_KEEP, GL_KEEP, GL_INCR ); - - R_RenderShadowEdges(); - - qglCullFace( GL_FRONT ); - qglStencilOp( GL_KEEP, GL_KEEP, GL_DECR ); - - R_RenderShadowEdges(); - } - - - // reenable writing to the color buffer - qglColorMask(rgba[0], rgba[1], rgba[2], rgba[3]); -} - - -/* -================= -RB_ShadowFinish - -Darken everything that is is a shadow volume. -We have to delay this until everything has been shadowed, -because otherwise shadows from different body parts would -overlap and double darken. -================= -*/ -void RB_ShadowFinish( void ) { - if ( r_shadows->integer != 2 ) { - return; - } - if ( glConfig.stencilBits < 4 ) { - return; - } - qglEnable( GL_STENCIL_TEST ); - qglStencilFunc( GL_NOTEQUAL, 0, 255 ); - - qglDisable (GL_CLIP_PLANE0); - qglDisable (GL_CULL_FACE); - - GL_Bind( tr.whiteImage ); - - qglLoadIdentity (); - - qglColor3f( 0.6f, 0.6f, 0.6f ); - GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO ); - -// qglColor3f( 1, 0, 0 ); -// GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO ); - - qglBegin( GL_QUADS ); - qglVertex3f( -100, 100, -10 ); - qglVertex3f( 100, 100, -10 ); - qglVertex3f( 100, -100, -10 ); - qglVertex3f( -100, -100, -10 ); - qglEnd (); - - qglColor4f(1,1,1,1); - qglDisable( GL_STENCIL_TEST ); -} - - -/* -================= -RB_ProjectionShadowDeform - -================= -*/ -void RB_ProjectionShadowDeform( void ) { - float *xyz; - int i; - float h; - vec3_t ground; - vec3_t light; - float groundDist; - float d; - vec3_t lightDir; - - xyz = ( float * ) tess.xyz; - - ground[0] = backEnd.or.axis[0][2]; - ground[1] = backEnd.or.axis[1][2]; - ground[2] = backEnd.or.axis[2][2]; - - groundDist = backEnd.or.origin[2] - backEnd.currentEntity->e.shadowPlane; - - VectorCopy( backEnd.currentEntity->lightDir, lightDir ); - d = DotProduct( lightDir, ground ); - // don't let the shadows get too long or go negative - if ( d < 0.5 ) { - VectorMA( lightDir, (0.5 - d), ground, lightDir ); - d = DotProduct( lightDir, ground ); - } - d = 1.0 / d; - - light[0] = lightDir[0] * d; - light[1] = lightDir[1] * d; - light[2] = lightDir[2] * d; - - for ( i = 0; i < tess.numVertexes; i++, xyz += 4 ) { - h = DotProduct( xyz, ground ) + groundDist; - - xyz[0] -= light[0] * h; - xyz[1] -= light[1] * h; - xyz[2] -= light[2] * h; - } -} diff --git a/src/rend2/tr_sky.c b/src/rend2/tr_sky.c deleted file mode 100644 index f3c24a45..00000000 --- a/src/rend2/tr_sky.c +++ /dev/null @@ -1,916 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_sky.c -#include "tr_local.h" - -#define SKY_SUBDIVISIONS 8 -#define HALF_SKY_SUBDIVISIONS (SKY_SUBDIVISIONS/2) - -static float s_cloudTexCoords[6][SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1][2]; -static float s_cloudTexP[6][SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1]; - -/* -=================================================================================== - -POLYGON TO BOX SIDE PROJECTION - -=================================================================================== -*/ - -static vec3_t sky_clip[6] = -{ - {1,1,0}, - {1,-1,0}, - {0,-1,1}, - {0,1,1}, - {1,0,1}, - {-1,0,1} -}; - -static float sky_mins[2][6], sky_maxs[2][6]; -static float sky_min, sky_max; - -/* -================ -AddSkyPolygon -================ -*/ -static void AddSkyPolygon (int nump, vec3_t vecs) -{ - int i,j; - vec3_t v, av; - float s, t, dv; - int axis; - float *vp; - // s = [0]/[2], t = [1]/[2] - static int vec_to_st[6][3] = - { - {-2,3,1}, - {2,3,-1}, - - {1,3,2}, - {-1,3,-2}, - - {-2,-1,3}, - {-2,1,-3} - - // {-1,2,3}, - // {1,2,-3} - }; - - // decide which face it maps to - VectorCopy (vec3_origin, v); - for (i=0, vp=vecs ; i av[1] && av[0] > av[2]) - { - if (v[0] < 0) - axis = 1; - else - axis = 0; - } - else if (av[1] > av[2] && av[1] > av[0]) - { - if (v[1] < 0) - axis = 3; - else - axis = 2; - } - else - { - if (v[2] < 0) - axis = 5; - else - axis = 4; - } - - // project new texture coords - for (i=0 ; i 0) - dv = vecs[j - 1]; - else - dv = -vecs[-j - 1]; - if (dv < 0.001) - continue; // don't divide by zero - j = vec_to_st[axis][0]; - if (j < 0) - s = -vecs[-j -1] / dv; - else - s = vecs[j-1] / dv; - j = vec_to_st[axis][1]; - if (j < 0) - t = -vecs[-j -1] / dv; - else - t = vecs[j-1] / dv; - - if (s < sky_mins[0][axis]) - sky_mins[0][axis] = s; - if (t < sky_mins[1][axis]) - sky_mins[1][axis] = t; - if (s > sky_maxs[0][axis]) - sky_maxs[0][axis] = s; - if (t > sky_maxs[1][axis]) - sky_maxs[1][axis] = t; - } -} - -#define ON_EPSILON 0.1f // point on plane side epsilon -#define MAX_CLIP_VERTS 64 -/* -================ -ClipSkyPolygon -================ -*/ -static void ClipSkyPolygon (int nump, vec3_t vecs, int stage) -{ - float *norm; - float *v; - qboolean front, back; - float d, e; - float dists[MAX_CLIP_VERTS]; - int sides[MAX_CLIP_VERTS]; - vec3_t newv[2][MAX_CLIP_VERTS]; - int newc[2]; - int i, j; - - if (nump > MAX_CLIP_VERTS-2) - ri.Error (ERR_DROP, "ClipSkyPolygon: MAX_CLIP_VERTS"); - if (stage == 6) - { // fully clipped, so draw it - AddSkyPolygon (nump, vecs); - return; - } - - front = back = qfalse; - norm = sky_clip[stage]; - for (i=0, v = vecs ; i ON_EPSILON) - { - front = qtrue; - sides[i] = SIDE_FRONT; - } - else if (d < -ON_EPSILON) - { - back = qtrue; - sides[i] = SIDE_BACK; - } - else - sides[i] = SIDE_ON; - dists[i] = d; - } - - if (!front || !back) - { // not clipped - ClipSkyPolygon (nump, vecs, stage+1); - return; - } - - // clip it - sides[i] = sides[0]; - dists[i] = dists[0]; - VectorCopy (vecs, (vecs+(i*3)) ); - newc[0] = newc[1] = 0; - - for (i=0, v = vecs ; inumIndexes; i += 3 ) - { - for (j = 0 ; j < 3 ; j++) - { - VectorSubtract( input->xyz[input->indexes[i+j]], - backEnd.viewParms.or.origin, - p[j] ); - } - ClipSkyPolygon( 3, p[0], 0 ); - } -} - -/* -=================================================================================== - -CLOUD VERTEX GENERATION - -=================================================================================== -*/ - -/* -** MakeSkyVec -** -** Parms: s, t range from -1 to 1 -*/ -static void MakeSkyVec( float s, float t, int axis, float outSt[2], vec3_t outXYZ ) -{ - // 1 = s, 2 = t, 3 = 2048 - static int st_to_vec[6][3] = - { - {3,-1,2}, - {-3,1,2}, - - {1,3,2}, - {-1,-3,2}, - - {-2,-1,3}, // 0 degrees yaw, look straight up - {2,-1,-3} // look straight down - }; - - vec3_t b; - int j, k; - float boxSize; - - boxSize = backEnd.viewParms.zFar / 1.75; // div sqrt(3) - b[0] = s*boxSize; - b[1] = t*boxSize; - b[2] = boxSize; - - for (j=0 ; j<3 ; j++) - { - k = st_to_vec[axis][j]; - if (k < 0) - { - outXYZ[j] = -b[-k - 1]; - } - else - { - outXYZ[j] = b[k - 1]; - } - } - - // avoid bilerp seam - s = (s+1)*0.5; - t = (t+1)*0.5; - if (s < sky_min) - { - s = sky_min; - } - else if (s > sky_max) - { - s = sky_max; - } - - if (t < sky_min) - { - t = sky_min; - } - else if (t > sky_max) - { - t = sky_max; - } - - t = 1.0 - t; - - - if ( outSt ) - { - outSt[0] = s; - outSt[1] = t; - } -} - -static int sky_texorder[6] = {0,2,1,3,4,5}; -static vec3_t s_skyPoints[SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1]; -static float s_skyTexCoords[SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1][2]; - -static void DrawSkySide( struct image_s *image, const int mins[2], const int maxs[2] ) -{ - int s, t; - int firstVertex = tess.numVertexes; - //int firstIndex = tess.numIndexes; - int minIndex = tess.minIndex; - int maxIndex = tess.maxIndex; - vec4_t color; - - //tess.numVertexes = 0; - //tess.numIndexes = 0; - tess.firstIndex = tess.numIndexes; - - GL_Bind( image ); - GL_Cull( CT_TWO_SIDED ); - - for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t <= maxs[1]+HALF_SKY_SUBDIVISIONS; t++ ) - { - for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ ) - { - tess.xyz[tess.numVertexes][0] = s_skyPoints[t][s][0]; - tess.xyz[tess.numVertexes][1] = s_skyPoints[t][s][1]; - tess.xyz[tess.numVertexes][2] = s_skyPoints[t][s][2]; - tess.xyz[tess.numVertexes][3] = 1.0; - - tess.texCoords[tess.numVertexes][0][0] = s_skyTexCoords[t][s][0]; - tess.texCoords[tess.numVertexes][0][1] = s_skyTexCoords[t][s][1]; - - tess.numVertexes++; - - if(tess.numVertexes >= SHADER_MAX_VERTEXES) - { - ri.Error(ERR_DROP, "SHADER_MAX_VERTEXES hit in DrawSkySideVBO()"); - } - } - } - - for ( t = 0; t < maxs[1] - mins[1]; t++ ) - { - for ( s = 0; s < maxs[0] - mins[0]; s++ ) - { - if (tess.numIndexes + 6 >= SHADER_MAX_INDEXES) - { - ri.Error(ERR_DROP, "SHADER_MAX_INDEXES hit in DrawSkySideVBO()"); - } - - tess.indexes[tess.numIndexes++] = s + t * (maxs[0] - mins[0] + 1) + firstVertex; - tess.indexes[tess.numIndexes++] = s + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex; - tess.indexes[tess.numIndexes++] = (s + 1) + t * (maxs[0] - mins[0] + 1) + firstVertex; - - tess.indexes[tess.numIndexes++] = (s + 1) + t * (maxs[0] - mins[0] + 1) + firstVertex; - tess.indexes[tess.numIndexes++] = s + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex; - tess.indexes[tess.numIndexes++] = (s + 1) + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex; - } - } - - tess.minIndex = firstVertex; - tess.maxIndex = tess.numVertexes; - - // FIXME: A lot of this can probably be removed for speed, and refactored into a more convenient function - RB_UpdateVBOs(ATTR_POSITION | ATTR_TEXCOORD); -/* - { - shaderProgram_t *sp = &tr.textureColorShader; - - GLSL_VertexAttribsState(ATTR_POSITION | ATTR_TEXCOORD); - GLSL_BindProgram(sp); - - GLSL_SetUniformMatrix16(sp, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); - - color[0] = - color[1] = - color[2] = tr.identityLight; - color[3] = 1.0f; - GLSL_SetUniformVec4(sp, TEXTURECOLOR_UNIFORM_COLOR, color); - } -*/ - { - shaderProgram_t *sp = &tr.lightallShader[0]; - vec4_t vector; - - GLSL_VertexAttribsState(ATTR_POSITION | ATTR_TEXCOORD); - GLSL_BindProgram(sp); - - GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); - - color[0] = - color[1] = - color[2] = tr.identityLight; - color[3] = 1.0f; - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_BASECOLOR, color); - - color[0] = - color[1] = - color[2] = - color[3] = 0.0f; - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_VERTCOLOR, color); - - VectorSet4(vector, 1.0, 0.0, 0.0, 1.0); - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_DIFFUSETEXMATRIX, vector); - - VectorSet4(vector, 0.0, 0.0, 0.0, 0.0); - GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_DIFFUSETEXOFFTURB, vector); - } - - R_DrawElementsVBO(tess.numIndexes - tess.firstIndex, tess.firstIndex, tess.minIndex, tess.maxIndex); - - //qglDrawElements(GL_TRIANGLES, tess.numIndexes - tess.firstIndex, GL_INDEX_TYPE, BUFFER_OFFSET(tess.firstIndex * sizeof(GL_INDEX_TYPE))); - - //R_BindNullVBO(); - //R_BindNullIBO(); - - tess.numIndexes = tess.firstIndex; - tess.numVertexes = firstVertex; - tess.firstIndex = 0; - tess.minIndex = minIndex; - tess.maxIndex = maxIndex; -} - -static void DrawSkyBox( shader_t *shader ) -{ - int i; - - sky_min = 0; - sky_max = 1; - - Com_Memset( s_skyTexCoords, 0, sizeof( s_skyTexCoords ) ); - - for (i=0 ; i<6 ; i++) - { - int sky_mins_subd[2], sky_maxs_subd[2]; - int s, t; - - sky_mins[0][i] = floor( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; - sky_mins[1][i] = floor( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; - sky_maxs[0][i] = ceil( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; - sky_maxs[1][i] = ceil( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; - - if ( ( sky_mins[0][i] >= sky_maxs[0][i] ) || - ( sky_mins[1][i] >= sky_maxs[1][i] ) ) - { - continue; - } - - sky_mins_subd[0] = sky_mins[0][i] * HALF_SKY_SUBDIVISIONS; - sky_mins_subd[1] = sky_mins[1][i] * HALF_SKY_SUBDIVISIONS; - sky_maxs_subd[0] = sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS; - sky_maxs_subd[1] = sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS; - - if ( sky_mins_subd[0] < -HALF_SKY_SUBDIVISIONS ) - sky_mins_subd[0] = -HALF_SKY_SUBDIVISIONS; - else if ( sky_mins_subd[0] > HALF_SKY_SUBDIVISIONS ) - sky_mins_subd[0] = HALF_SKY_SUBDIVISIONS; - if ( sky_mins_subd[1] < -HALF_SKY_SUBDIVISIONS ) - sky_mins_subd[1] = -HALF_SKY_SUBDIVISIONS; - else if ( sky_mins_subd[1] > HALF_SKY_SUBDIVISIONS ) - sky_mins_subd[1] = HALF_SKY_SUBDIVISIONS; - - if ( sky_maxs_subd[0] < -HALF_SKY_SUBDIVISIONS ) - sky_maxs_subd[0] = -HALF_SKY_SUBDIVISIONS; - else if ( sky_maxs_subd[0] > HALF_SKY_SUBDIVISIONS ) - sky_maxs_subd[0] = HALF_SKY_SUBDIVISIONS; - if ( sky_maxs_subd[1] < -HALF_SKY_SUBDIVISIONS ) - sky_maxs_subd[1] = -HALF_SKY_SUBDIVISIONS; - else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS ) - sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS; - - // - // iterate through the subdivisions - // - for ( t = sky_mins_subd[1]+HALF_SKY_SUBDIVISIONS; t <= sky_maxs_subd[1]+HALF_SKY_SUBDIVISIONS; t++ ) - { - for ( s = sky_mins_subd[0]+HALF_SKY_SUBDIVISIONS; s <= sky_maxs_subd[0]+HALF_SKY_SUBDIVISIONS; s++ ) - { - MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, - ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, - i, - s_skyTexCoords[t][s], - s_skyPoints[t][s] ); - } - } - - DrawSkySide( shader->sky.outerbox[sky_texorder[i]], - sky_mins_subd, - sky_maxs_subd ); - } - -} - -static void FillCloudySkySide( const int mins[2], const int maxs[2], qboolean addIndexes ) -{ - int s, t; - int vertexStart = tess.numVertexes; - int tHeight, sWidth; - - tHeight = maxs[1] - mins[1] + 1; - sWidth = maxs[0] - mins[0] + 1; - - for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t <= maxs[1]+HALF_SKY_SUBDIVISIONS; t++ ) - { - for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ ) - { - VectorAdd( s_skyPoints[t][s], backEnd.viewParms.or.origin, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = s_skyTexCoords[t][s][0]; - tess.texCoords[tess.numVertexes][0][1] = s_skyTexCoords[t][s][1]; - - tess.numVertexes++; - - if ( tess.numVertexes >= SHADER_MAX_VERTEXES ) - { - ri.Error( ERR_DROP, "SHADER_MAX_VERTEXES hit in FillCloudySkySide()" ); - } - } - } - - // only add indexes for one pass, otherwise it would draw multiple times for each pass - if ( addIndexes ) { - for ( t = 0; t < tHeight-1; t++ ) - { - for ( s = 0; s < sWidth-1; s++ ) - { - tess.indexes[tess.numIndexes] = vertexStart + s + t * ( sWidth ); - tess.numIndexes++; - tess.indexes[tess.numIndexes] = vertexStart + s + ( t + 1 ) * ( sWidth ); - tess.numIndexes++; - tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * ( sWidth ); - tess.numIndexes++; - - tess.indexes[tess.numIndexes] = vertexStart + s + ( t + 1 ) * ( sWidth ); - tess.numIndexes++; - tess.indexes[tess.numIndexes] = vertexStart + s + 1 + ( t + 1 ) * ( sWidth ); - tess.numIndexes++; - tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * ( sWidth ); - tess.numIndexes++; - } - } - } -} - -static void FillCloudBox( const shader_t *shader, int stage ) -{ - int i; - - for ( i =0; i < 6; i++ ) - { - int sky_mins_subd[2], sky_maxs_subd[2]; - int s, t; - float MIN_T; - - if ( 1 ) // FIXME? shader->sky.fullClouds ) - { - MIN_T = -HALF_SKY_SUBDIVISIONS; - - // still don't want to draw the bottom, even if fullClouds - if ( i == 5 ) - continue; - } - else - { - switch( i ) - { - case 0: - case 1: - case 2: - case 3: - MIN_T = -1; - break; - case 5: - // don't draw clouds beneath you - continue; - case 4: // top - default: - MIN_T = -HALF_SKY_SUBDIVISIONS; - break; - } - } - - sky_mins[0][i] = floor( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; - sky_mins[1][i] = floor( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; - sky_maxs[0][i] = ceil( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; - sky_maxs[1][i] = ceil( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; - - if ( ( sky_mins[0][i] >= sky_maxs[0][i] ) || - ( sky_mins[1][i] >= sky_maxs[1][i] ) ) - { - continue; - } - - sky_mins_subd[0] = ri.ftol(sky_mins[0][i] * HALF_SKY_SUBDIVISIONS); - sky_mins_subd[1] = ri.ftol(sky_mins[1][i] * HALF_SKY_SUBDIVISIONS); - sky_maxs_subd[0] = ri.ftol(sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS); - sky_maxs_subd[1] = ri.ftol(sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS); - - if ( sky_mins_subd[0] < -HALF_SKY_SUBDIVISIONS ) - sky_mins_subd[0] = -HALF_SKY_SUBDIVISIONS; - else if ( sky_mins_subd[0] > HALF_SKY_SUBDIVISIONS ) - sky_mins_subd[0] = HALF_SKY_SUBDIVISIONS; - if ( sky_mins_subd[1] < MIN_T ) - sky_mins_subd[1] = MIN_T; - else if ( sky_mins_subd[1] > HALF_SKY_SUBDIVISIONS ) - sky_mins_subd[1] = HALF_SKY_SUBDIVISIONS; - - if ( sky_maxs_subd[0] < -HALF_SKY_SUBDIVISIONS ) - sky_maxs_subd[0] = -HALF_SKY_SUBDIVISIONS; - else if ( sky_maxs_subd[0] > HALF_SKY_SUBDIVISIONS ) - sky_maxs_subd[0] = HALF_SKY_SUBDIVISIONS; - if ( sky_maxs_subd[1] < MIN_T ) - sky_maxs_subd[1] = MIN_T; - else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS ) - sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS; - - // - // iterate through the subdivisions - // - for ( t = sky_mins_subd[1]+HALF_SKY_SUBDIVISIONS; t <= sky_maxs_subd[1]+HALF_SKY_SUBDIVISIONS; t++ ) - { - for ( s = sky_mins_subd[0]+HALF_SKY_SUBDIVISIONS; s <= sky_maxs_subd[0]+HALF_SKY_SUBDIVISIONS; s++ ) - { - MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, - ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, - i, - NULL, - s_skyPoints[t][s] ); - - s_skyTexCoords[t][s][0] = s_cloudTexCoords[i][t][s][0]; - s_skyTexCoords[t][s][1] = s_cloudTexCoords[i][t][s][1]; - } - } - - // only add indexes for first stage - FillCloudySkySide( sky_mins_subd, sky_maxs_subd, ( stage == 0 ) ); - } -} - -/* -** R_BuildCloudData -*/ -void R_BuildCloudData( shaderCommands_t *input ) -{ - int i; - shader_t *shader; - - shader = input->shader; - - assert( shader->isSky ); - - sky_min = 1.0 / 256.0f; // FIXME: not correct? - sky_max = 255.0 / 256.0f; - - // set up for drawing - tess.numIndexes = 0; - tess.numVertexes = 0; - tess.firstIndex = 0; - - if ( shader->sky.cloudHeight ) - { - for ( i = 0; i < MAX_SHADER_STAGES; i++ ) - { - if ( !tess.xstages[i] ) { - break; - } - FillCloudBox( shader, i ); - } - } -} - -/* -** R_InitSkyTexCoords -** Called when a sky shader is parsed -*/ -#define SQR( a ) ((a)*(a)) -void R_InitSkyTexCoords( float heightCloud ) -{ - int i, s, t; - float radiusWorld = 4096; - float p; - float sRad, tRad; - vec3_t skyVec; - vec3_t v; - - // init zfar so MakeSkyVec works even though - // a world hasn't been bounded - backEnd.viewParms.zFar = 1024; - - for ( i = 0; i < 6; i++ ) - { - for ( t = 0; t <= SKY_SUBDIVISIONS; t++ ) - { - for ( s = 0; s <= SKY_SUBDIVISIONS; s++ ) - { - // compute vector from view origin to sky side integral point - MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, - ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, - i, - NULL, - skyVec ); - - // compute parametric value 'p' that intersects with cloud layer - p = ( 1.0f / ( 2 * DotProduct( skyVec, skyVec ) ) ) * - ( -2 * skyVec[2] * radiusWorld + - 2 * sqrt( SQR( skyVec[2] ) * SQR( radiusWorld ) + - 2 * SQR( skyVec[0] ) * radiusWorld * heightCloud + - SQR( skyVec[0] ) * SQR( heightCloud ) + - 2 * SQR( skyVec[1] ) * radiusWorld * heightCloud + - SQR( skyVec[1] ) * SQR( heightCloud ) + - 2 * SQR( skyVec[2] ) * radiusWorld * heightCloud + - SQR( skyVec[2] ) * SQR( heightCloud ) ) ); - - s_cloudTexP[i][t][s] = p; - - // compute intersection point based on p - VectorScale( skyVec, p, v ); - v[2] += radiusWorld; - - // compute vector from world origin to intersection point 'v' - VectorNormalize( v ); - - sRad = Q_acos( v[0] ); - tRad = Q_acos( v[1] ); - - s_cloudTexCoords[i][t][s][0] = sRad; - s_cloudTexCoords[i][t][s][1] = tRad; - } - } - } -} - -//====================================================================================== - -/* -** RB_DrawSun -*/ -void RB_DrawSun( float scale, shader_t *shader ) { - float size; - float dist; - vec3_t origin, vec1, vec2; - - if ( !backEnd.skyRenderedThisView ) { - return; - } - - //qglLoadMatrixf( backEnd.viewParms.world.modelMatrix ); - //qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]); - { - // FIXME: this could be a lot cleaner - matrix_t translation, modelview; - - Matrix16Translation( backEnd.viewParms.or.origin, translation ); - Matrix16Multiply( backEnd.viewParms.world.modelMatrix, translation, modelview ); - GL_SetModelviewMatrix( modelview ); - } - - dist = backEnd.viewParms.zFar / 1.75; // div sqrt(3) - size = dist * scale; - - VectorScale( tr.sunDirection, dist, origin ); - PerpendicularVector( vec1, tr.sunDirection ); - CrossProduct( tr.sunDirection, vec1, vec2 ); - - VectorScale( vec1, size, vec1 ); - VectorScale( vec2, size, vec2 ); - - // farthest depth range - qglDepthRange( 1.0, 1.0 ); - - RB_BeginSurface( shader, 0 ); - - { - vec4_t color; - color[0] = color[1] = color[2] = color[3] = 1; - RB_AddQuadStamp(origin, vec1, vec2, color); - } - - RB_EndSurface(); - - // back to normal depth range - qglDepthRange( 0.0, 1.0 ); -} - - - - -/* -================ -RB_StageIteratorSky - -All of the visible sky triangles are in tess - -Other things could be stuck in here, like birds in the sky, etc -================ -*/ -void RB_StageIteratorSky( void ) { - if ( r_fastsky->integer ) { - return; - } - - // go through all the polygons and project them onto - // the sky box to see which blocks on each side need - // to be drawn - RB_ClipSkyPolygons( &tess ); - - // r_showsky will let all the sky blocks be drawn in - // front of everything to allow developers to see how - // much sky is getting sucked in - if ( r_showsky->integer ) { - qglDepthRange( 0.0, 0.0 ); - } else { - qglDepthRange( 1.0, 1.0 ); - } - - // draw the outer skybox - if ( tess.shader->sky.outerbox[0] && tess.shader->sky.outerbox[0] != tr.defaultImage ) { - matrix_t oldmodelview; - - GL_State( 0 ); - //qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]); - - { - // FIXME: this could be a lot cleaner - matrix_t trans, product; - - Matrix16Copy( glState.modelview, oldmodelview ); - Matrix16Translation( backEnd.viewParms.or.origin, trans ); - Matrix16Multiply( glState.modelview, trans, product ); - GL_SetModelviewMatrix( product ); - - } - - DrawSkyBox( tess.shader ); - - GL_SetModelviewMatrix( oldmodelview ); - } - - // generate the vertexes for all the clouds, which will be drawn - // by the generic shader routine - R_BuildCloudData( &tess ); - - RB_StageIteratorGeneric(); - - // draw the inner skybox - - - // back to normal depth range - qglDepthRange( 0.0, 1.0 ); - - // note that sky was drawn so we will draw a sun later - backEnd.skyRenderedThisView = qtrue; -} - - - - - diff --git a/src/rend2/tr_subs.c b/src/rend2/tr_subs.c deleted file mode 100644 index 6f490128..00000000 --- a/src/rend2/tr_subs.c +++ /dev/null @@ -1,48 +0,0 @@ -/* -=========================================================================== -Copyright (C) 2010 James Canete (use.less01@gmail.com) - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_subs.c - common function replacements for modular renderer - -#include "tr_local.h" - -void QDECL Com_Printf( const char *msg, ... ) -{ - va_list argptr; - char text[1024]; - - va_start(argptr, msg); - Q_vsnprintf(text, sizeof(text), msg, argptr); - va_end(argptr); - - ri.Printf(PRINT_ALL, "%s", text); -} - -void QDECL Com_Error( int level, const char *error, ... ) -{ - va_list argptr; - char text[1024]; - - va_start(argptr, error); - Q_vsnprintf(text, sizeof(text), error, argptr); - va_end(argptr); - - ri.Error(level, "%s", text); -} diff --git a/src/rend2/tr_surface.c b/src/rend2/tr_surface.c deleted file mode 100644 index ad7f5a5f..00000000 --- a/src/rend2/tr_surface.c +++ /dev/null @@ -1,1698 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_surf.c -#include "tr_local.h" -#if idppc_altivec && !defined(MACOS_X) -#include -#endif - -/* - - THIS ENTIRE FILE IS BACK END - -backEnd.currentEntity will be valid. - -Tess_Begin has already been called for the surface's shader. - -The modelview matrix will be set. - -It is safe to actually issue drawing commands here if you don't want to -use the shader system. -*/ - - -//============================================================================ - - -/* -============== -RB_CheckOverflow -============== -*/ -void RB_CheckOverflow( int verts, int indexes ) { - if (tess.numVertexes + verts < SHADER_MAX_VERTEXES - && tess.numIndexes + indexes < SHADER_MAX_INDEXES) { - return; - } - - RB_EndSurface(); - - if ( verts >= SHADER_MAX_VERTEXES ) { - ri.Error(ERR_DROP, "RB_CheckOverflow: verts > MAX (%d > %d)", verts, SHADER_MAX_VERTEXES ); - } - if ( indexes >= SHADER_MAX_INDEXES ) { - ri.Error(ERR_DROP, "RB_CheckOverflow: indices > MAX (%d > %d)", indexes, SHADER_MAX_INDEXES ); - } - - RB_BeginSurface(tess.shader, tess.fogNum ); -} - -void RB_CheckVBOandIBO(VBO_t *vbo, IBO_t *ibo) -{ - if (!(vbo == glState.currentVBO && ibo == glState.currentIBO) || tess.multiDrawPrimitives >= MAX_MULTIDRAW_PRIMITIVES) - { - RB_EndSurface(); - RB_BeginSurface(tess.shader, tess.fogNum); - - R_BindVBO(vbo); - R_BindIBO(ibo); - } - - if (vbo != tess.vbo && ibo != tess.ibo) - tess.useInternalVBO = qfalse; -} - - -/* -============== -RB_AddQuadStampExt -============== -*/ -void RB_AddQuadStampExt( vec3_t origin, vec3_t left, vec3_t up, float color[4], float s1, float t1, float s2, float t2 ) { - vec3_t normal; - int ndx; - - RB_CHECKOVERFLOW( 4, 6 ); - - ndx = tess.numVertexes; - - // triangle indexes for a simple quad - tess.indexes[ tess.numIndexes ] = ndx; - tess.indexes[ tess.numIndexes + 1 ] = ndx + 1; - tess.indexes[ tess.numIndexes + 2 ] = ndx + 3; - - tess.indexes[ tess.numIndexes + 3 ] = ndx + 3; - tess.indexes[ tess.numIndexes + 4 ] = ndx + 1; - tess.indexes[ tess.numIndexes + 5 ] = ndx + 2; - - tess.xyz[ndx][0] = origin[0] + left[0] + up[0]; - tess.xyz[ndx][1] = origin[1] + left[1] + up[1]; - tess.xyz[ndx][2] = origin[2] + left[2] + up[2]; - - tess.xyz[ndx+1][0] = origin[0] - left[0] + up[0]; - tess.xyz[ndx+1][1] = origin[1] - left[1] + up[1]; - tess.xyz[ndx+1][2] = origin[2] - left[2] + up[2]; - - tess.xyz[ndx+2][0] = origin[0] - left[0] - up[0]; - tess.xyz[ndx+2][1] = origin[1] - left[1] - up[1]; - tess.xyz[ndx+2][2] = origin[2] - left[2] - up[2]; - - tess.xyz[ndx+3][0] = origin[0] + left[0] - up[0]; - tess.xyz[ndx+3][1] = origin[1] + left[1] - up[1]; - tess.xyz[ndx+3][2] = origin[2] + left[2] - up[2]; - - - // constant normal all the way around - VectorSubtract( vec3_origin, backEnd.viewParms.or.axis[0], normal ); - - tess.normal[ndx][0] = tess.normal[ndx+1][0] = tess.normal[ndx+2][0] = tess.normal[ndx+3][0] = normal[0]; - tess.normal[ndx][1] = tess.normal[ndx+1][1] = tess.normal[ndx+2][1] = tess.normal[ndx+3][1] = normal[1]; - tess.normal[ndx][2] = tess.normal[ndx+1][2] = tess.normal[ndx+2][2] = tess.normal[ndx+3][2] = normal[2]; - - // standard square texture coordinates - tess.texCoords[ndx][0][0] = tess.texCoords[ndx][1][0] = s1; - tess.texCoords[ndx][0][1] = tess.texCoords[ndx][1][1] = t1; - - tess.texCoords[ndx+1][0][0] = tess.texCoords[ndx+1][1][0] = s2; - tess.texCoords[ndx+1][0][1] = tess.texCoords[ndx+1][1][1] = t1; - - tess.texCoords[ndx+2][0][0] = tess.texCoords[ndx+2][1][0] = s2; - tess.texCoords[ndx+2][0][1] = tess.texCoords[ndx+2][1][1] = t2; - - tess.texCoords[ndx+3][0][0] = tess.texCoords[ndx+3][1][0] = s1; - tess.texCoords[ndx+3][0][1] = tess.texCoords[ndx+3][1][1] = t2; - - // constant color all the way around - // should this be identity and let the shader specify from entity? - tess.vertexColors[ndx][0] = color[0]; - tess.vertexColors[ndx][1] = color[1]; - tess.vertexColors[ndx][2] = color[2]; - tess.vertexColors[ndx][3] = color[3]; - - tess.vertexColors[ndx+1][0] = color[0]; - tess.vertexColors[ndx+1][1] = color[1]; - tess.vertexColors[ndx+1][2] = color[2]; - tess.vertexColors[ndx+1][3] = color[3]; - - tess.vertexColors[ndx+2][0] = color[0]; - tess.vertexColors[ndx+2][1] = color[1]; - tess.vertexColors[ndx+2][2] = color[2]; - tess.vertexColors[ndx+2][3] = color[3]; - - tess.vertexColors[ndx+3][0] = color[0]; - tess.vertexColors[ndx+3][1] = color[1]; - tess.vertexColors[ndx+3][2] = color[2]; - tess.vertexColors[ndx+3][3] = color[3]; - - tess.numVertexes += 4; - tess.numIndexes += 6; -} - -/* -============== -RB_AddQuadStamp -============== -*/ -void RB_AddQuadStamp( vec3_t origin, vec3_t left, vec3_t up, float color[4] ) { - RB_AddQuadStampExt( origin, left, up, color, 0, 0, 1, 1 ); -} - - -/* -============== -RB_InstantQuad - -based on Tess_InstantQuad from xreal -============== -*/ -void RB_InstantQuad2(vec4_t quadVerts[4], vec2_t texCoords[4]) -{ - GLimp_LogComment("--- RB_InstantQuad2 ---\n"); - - tess.numVertexes = 0; - tess.numIndexes = 0; - tess.firstIndex = 0; - - VectorCopy4(quadVerts[0], tess.xyz[tess.numVertexes]); - VectorCopy2(texCoords[0], tess.texCoords[tess.numVertexes][0]); - tess.numVertexes++; - - VectorCopy4(quadVerts[1], tess.xyz[tess.numVertexes]); - VectorCopy2(texCoords[1], tess.texCoords[tess.numVertexes][0]); - tess.numVertexes++; - - VectorCopy4(quadVerts[2], tess.xyz[tess.numVertexes]); - VectorCopy2(texCoords[2], tess.texCoords[tess.numVertexes][0]); - tess.numVertexes++; - - VectorCopy4(quadVerts[3], tess.xyz[tess.numVertexes]); - VectorCopy2(texCoords[3], tess.texCoords[tess.numVertexes][0]); - tess.numVertexes++; - - tess.indexes[tess.numIndexes++] = 0; - tess.indexes[tess.numIndexes++] = 1; - tess.indexes[tess.numIndexes++] = 2; - tess.indexes[tess.numIndexes++] = 0; - tess.indexes[tess.numIndexes++] = 2; - tess.indexes[tess.numIndexes++] = 3; - tess.minIndex = 0; - tess.maxIndex = 3; - - RB_UpdateVBOs(ATTR_POSITION | ATTR_TEXCOORD); - - GLSL_VertexAttribsState(ATTR_POSITION | ATTR_TEXCOORD); - - R_DrawElementsVBO(tess.numIndexes, tess.firstIndex, tess.minIndex, tess.maxIndex); - - tess.numIndexes = 0; - tess.numVertexes = 0; - tess.firstIndex = 0; - tess.minIndex = 0; - tess.maxIndex = 0; -} - - -void RB_InstantQuad(vec4_t quadVerts[4]) -{ - vec4_t color; - vec2_t texCoords[4]; - vec2_t invTexRes; - - VectorSet4(color, 1, 1, 1, 1); - - texCoords[0][0] = 0; - texCoords[0][1] = 0; - - texCoords[1][0] = 1; - texCoords[1][1] = 0; - - texCoords[2][0] = 1; - texCoords[2][1] = 1; - - texCoords[3][0] = 0; - texCoords[3][1] = 1; - - invTexRes[0] = 1.0f / 256.0f; - invTexRes[1] = 1.0f / 256.0f; - - GLSL_BindProgram(&tr.textureColorShader); - - GLSL_SetUniformMatrix16(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); - GLSL_SetUniformVec4(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_COLOR, color); - GLSL_SetUniformVec2(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_INVTEXRES, invTexRes); - GLSL_SetUniformVec2(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_AUTOEXPOSUREMINMAX, tr.refdef.autoExposureMinMax); - GLSL_SetUniformVec3(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_TONEMINAVGMAXLINEAR, tr.refdef.toneMinAvgMaxLinear); - - RB_InstantQuad2(quadVerts, texCoords); //, color, &tr.textureColorShader, invTexRes); -} - - -/* -============== -RB_SurfaceSprite -============== -*/ -static void RB_SurfaceSprite( void ) { - vec3_t left, up; - float radius; - float colors[4]; - trRefEntity_t *ent = backEnd.currentEntity; - - // calculate the xyz locations for the four corners - radius = ent->e.radius; - if ( ent->e.rotation == 0 ) { - VectorScale( backEnd.viewParms.or.axis[1], radius, left ); - VectorScale( backEnd.viewParms.or.axis[2], radius, up ); - } else { - float s, c; - float ang; - - ang = M_PI * ent->e.rotation / 180; - s = sin( ang ); - c = cos( ang ); - - VectorScale( backEnd.viewParms.or.axis[1], c * radius, left ); - VectorMA( left, -s * radius, backEnd.viewParms.or.axis[2], left ); - - VectorScale( backEnd.viewParms.or.axis[2], c * radius, up ); - VectorMA( up, s * radius, backEnd.viewParms.or.axis[1], up ); - } - if ( backEnd.viewParms.isMirror ) { - VectorSubtract( vec3_origin, left, left ); - } - - VectorScale4(ent->e.shaderRGBA, 1.0f / 255.0f, colors); - - RB_AddQuadStamp( ent->e.origin, left, up, colors ); -} - - -/* -============= -RB_SurfacePolychain -============= -*/ -static void RB_SurfacePolychain( srfPoly_t *p ) { - int i; - int numv; - - RB_CHECKOVERFLOW( p->numVerts, 3*(p->numVerts - 2) ); - - // fan triangles into the tess array - numv = tess.numVertexes; - for ( i = 0; i < p->numVerts; i++ ) { - VectorCopy( p->verts[i].xyz, tess.xyz[numv] ); - tess.texCoords[numv][0][0] = p->verts[i].st[0]; - tess.texCoords[numv][0][1] = p->verts[i].st[1]; - tess.vertexColors[numv][0] = p->verts[ i ].modulate[0] / 255.0f; - tess.vertexColors[numv][1] = p->verts[ i ].modulate[1] / 255.0f; - tess.vertexColors[numv][2] = p->verts[ i ].modulate[2] / 255.0f; - tess.vertexColors[numv][3] = p->verts[ i ].modulate[3] / 255.0f; - - numv++; - } - - // generate fan indexes into the tess array - for ( i = 0; i < p->numVerts-2; i++ ) { - tess.indexes[tess.numIndexes + 0] = tess.numVertexes; - tess.indexes[tess.numIndexes + 1] = tess.numVertexes + i + 1; - tess.indexes[tess.numIndexes + 2] = tess.numVertexes + i + 2; - tess.numIndexes += 3; - } - - tess.numVertexes = numv; -} - -static void RB_SurfaceHelper( int numVerts, srfVert_t *verts, int numTriangles, srfTriangle_t *triangles, int dlightBits, int pshadowBits) -{ - int i; - srfTriangle_t *tri; - srfVert_t *dv; - float *xyz, *normal, *texCoords, *lightCoords, *lightdir; -#ifdef USE_VERT_TANGENT_SPACE - float *tangent, *bitangent; -#endif - glIndex_t *index; - float *color; - - RB_CheckVBOandIBO(tess.vbo, tess.ibo); - - RB_CHECKOVERFLOW( numVerts, numTriangles * 3 ); - - tri = triangles; - index = &tess.indexes[ tess.numIndexes ]; - for ( i = 0 ; i < numTriangles ; i++, tri++ ) { - *index++ = tess.numVertexes + tri->indexes[0]; - *index++ = tess.numVertexes + tri->indexes[1]; - *index++ = tess.numVertexes + tri->indexes[2]; - } - tess.numIndexes += numTriangles * 3; - - if ( tess.shader->vertexAttribs & ATTR_POSITION ) - { - dv = verts; - xyz = tess.xyz[ tess.numVertexes ]; - for ( i = 0 ; i < numVerts ; i++, dv++, xyz+=4 ) - VectorCopy(dv->xyz, xyz); - } - - if ( tess.shader->vertexAttribs & ATTR_NORMAL ) - { - dv = verts; - normal = tess.normal[ tess.numVertexes ]; - for ( i = 0 ; i < numVerts ; i++, dv++, normal+=4 ) - VectorCopy(dv->normal, normal); - } - -#ifdef USE_VERT_TANGENT_SPACE - if ( tess.shader->vertexAttribs & ATTR_TANGENT ) - { - dv = verts; - tangent = tess.tangent[ tess.numVertexes ]; - for ( i = 0 ; i < numVerts ; i++, dv++, tangent+=4 ) - VectorCopy(dv->tangent, tangent); - } - - if ( tess.shader->vertexAttribs & ATTR_BITANGENT ) - { - dv = verts; - bitangent = tess.bitangent[ tess.numVertexes ]; - for ( i = 0 ; i < numVerts ; i++, dv++, bitangent+=4 ) - VectorCopy(dv->bitangent, bitangent); - } -#endif - - if ( tess.shader->vertexAttribs & ATTR_TEXCOORD ) - { - dv = verts; - texCoords = tess.texCoords[ tess.numVertexes ][0]; - for ( i = 0 ; i < numVerts ; i++, dv++, texCoords+=4 ) - VectorCopy2(dv->st, texCoords); - } - - if ( tess.shader->vertexAttribs & ATTR_LIGHTCOORD ) - { - dv = verts; - lightCoords = tess.texCoords[ tess.numVertexes ][1]; - for ( i = 0 ; i < numVerts ; i++, dv++, lightCoords+=4 ) - VectorCopy2(dv->lightmap, lightCoords); - } - - if ( tess.shader->vertexAttribs & ATTR_COLOR ) - { - dv = verts; - color = tess.vertexColors[ tess.numVertexes ]; - for ( i = 0 ; i < numVerts ; i++, dv++, color+=4 ) - VectorCopy4(dv->vertexColors, color); - } - - if ( tess.shader->vertexAttribs & ATTR_LIGHTDIRECTION ) - { - dv = verts; - lightdir = tess.lightdir[ tess.numVertexes ]; - for ( i = 0 ; i < numVerts ; i++, dv++, lightdir+=4 ) - VectorCopy(dv->lightdir, lightdir); - } - -#if 0 // nothing even uses vertex dlightbits - for ( i = 0 ; i < numVerts ; i++ ) { - tess.vertexDlightBits[ tess.numVertexes + i ] = dlightBits; - } -#endif - - tess.dlightBits |= dlightBits; - tess.pshadowBits |= pshadowBits; - - tess.numVertexes += numVerts; -} - -static qboolean RB_SurfaceHelperVBO(VBO_t *vbo, IBO_t *ibo, int numVerts, int numIndexes, int firstIndex, int minIndex, int maxIndex, int dlightBits, int pshadowBits, qboolean shaderCheck) -{ - int i, mergeForward, mergeBack; - GLvoid *firstIndexOffset, *lastIndexOffset; - - if (!vbo || !ibo) - { - return qfalse; - } - - if (shaderCheck && !(!ShaderRequiresCPUDeforms(tess.shader) && !tess.shader->isSky && !tess.shader->isPortal)) - { - return qfalse; - } - - RB_CheckVBOandIBO(vbo, ibo); - - tess.dlightBits |= dlightBits; - tess.pshadowBits |= pshadowBits; - - // merge this into any existing multidraw primitives - mergeForward = -1; - mergeBack = -1; - firstIndexOffset = BUFFER_OFFSET(firstIndex * sizeof(GL_INDEX_TYPE)); - lastIndexOffset = BUFFER_OFFSET((firstIndex + numIndexes) * sizeof(GL_INDEX_TYPE)); - - if (r_mergeMultidraws->integer) - { - i = 0; - - if (r_mergeMultidraws->integer == 1) - { - // lazy merge, only check the last primitive - if (tess.multiDrawPrimitives) - { - i = tess.multiDrawPrimitives - 1; - } - } - - for (; i < tess.multiDrawPrimitives; i++) - { - if (tess.multiDrawLastIndex[i] == firstIndexOffset) - { - mergeBack = i; - } - - if (lastIndexOffset == tess.multiDrawFirstIndex[i]) - { - mergeForward = i; - } - } - } - - if (mergeBack != -1 && mergeForward == -1) - { - tess.multiDrawNumIndexes[mergeBack] += numIndexes; - tess.multiDrawLastIndex[mergeBack] = tess.multiDrawFirstIndex[mergeBack] + tess.multiDrawNumIndexes[mergeBack]; - tess.multiDrawMinIndex[mergeBack] = MIN(tess.multiDrawMinIndex[mergeBack], minIndex); - tess.multiDrawMaxIndex[mergeBack] = MAX(tess.multiDrawMaxIndex[mergeBack], maxIndex); - backEnd.pc.c_multidrawsMerged++; - } - else if (mergeBack == -1 && mergeForward != -1) - { - tess.multiDrawNumIndexes[mergeForward] += numIndexes; - tess.multiDrawFirstIndex[mergeForward] = firstIndexOffset; - tess.multiDrawLastIndex[mergeForward] = tess.multiDrawFirstIndex[mergeForward] + tess.multiDrawNumIndexes[mergeForward]; - tess.multiDrawMinIndex[mergeForward] = MIN(tess.multiDrawMinIndex[mergeForward], minIndex); - tess.multiDrawMaxIndex[mergeForward] = MAX(tess.multiDrawMaxIndex[mergeForward], maxIndex); - backEnd.pc.c_multidrawsMerged++; - } - else if (mergeBack != -1 && mergeForward != -1) - { - tess.multiDrawNumIndexes[mergeBack] += numIndexes + tess.multiDrawNumIndexes[mergeForward]; - tess.multiDrawLastIndex[mergeBack] = tess.multiDrawFirstIndex[mergeBack] + tess.multiDrawNumIndexes[mergeBack]; - tess.multiDrawMinIndex[mergeBack] = MIN(tess.multiDrawMinIndex[mergeBack], MIN(tess.multiDrawMinIndex[mergeForward], minIndex)); - tess.multiDrawMaxIndex[mergeBack] = MAX(tess.multiDrawMaxIndex[mergeBack], MAX(tess.multiDrawMaxIndex[mergeForward], maxIndex)); - tess.multiDrawPrimitives--; - - if (mergeForward != tess.multiDrawPrimitives) - { - tess.multiDrawNumIndexes[mergeForward] = tess.multiDrawNumIndexes[tess.multiDrawPrimitives]; - tess.multiDrawFirstIndex[mergeForward] = tess.multiDrawFirstIndex[tess.multiDrawPrimitives]; - } - backEnd.pc.c_multidrawsMerged += 2; - } - else if (mergeBack == -1 && mergeForward == -1) - { - tess.multiDrawNumIndexes[tess.multiDrawPrimitives] = numIndexes; - tess.multiDrawFirstIndex[tess.multiDrawPrimitives] = firstIndexOffset; - tess.multiDrawLastIndex[tess.multiDrawPrimitives] = lastIndexOffset; - tess.multiDrawMinIndex[tess.multiDrawPrimitives] = minIndex; - tess.multiDrawMaxIndex[tess.multiDrawPrimitives] = maxIndex; - tess.multiDrawPrimitives++; - } - - backEnd.pc.c_multidraws++; - - tess.numIndexes += numIndexes; - tess.numVertexes += numVerts; - - return qtrue; -} - -/* -============= -RB_SurfaceTriangles -============= -*/ -static void RB_SurfaceTriangles( srfTriangles_t *srf ) { - if( RB_SurfaceHelperVBO (srf->vbo, srf->ibo, srf->numVerts, srf->numTriangles * 3, - srf->firstIndex, srf->minIndex, srf->maxIndex, srf->dlightBits, srf->pshadowBits, qtrue ) ) - { - return; - } - - RB_SurfaceHelper(srf->numVerts, srf->verts, srf->numTriangles, - srf->triangles, srf->dlightBits, srf->pshadowBits); -} - - - -/* -============== -RB_SurfaceBeam -============== -*/ -static void RB_SurfaceBeam( void ) -{ -#define NUM_BEAM_SEGS 6 - refEntity_t *e; - int i; - vec3_t perpvec; - vec3_t direction, normalized_direction; - vec3_t start_points[NUM_BEAM_SEGS], end_points[NUM_BEAM_SEGS]; - vec3_t oldorigin, origin; - - e = &backEnd.currentEntity->e; - - oldorigin[0] = e->oldorigin[0]; - oldorigin[1] = e->oldorigin[1]; - oldorigin[2] = e->oldorigin[2]; - - origin[0] = e->origin[0]; - origin[1] = e->origin[1]; - origin[2] = e->origin[2]; - - normalized_direction[0] = direction[0] = oldorigin[0] - origin[0]; - normalized_direction[1] = direction[1] = oldorigin[1] - origin[1]; - normalized_direction[2] = direction[2] = oldorigin[2] - origin[2]; - - if ( VectorNormalize( normalized_direction ) == 0 ) - return; - - PerpendicularVector( perpvec, normalized_direction ); - - VectorScale( perpvec, 4, perpvec ); - - for ( i = 0; i < NUM_BEAM_SEGS ; i++ ) - { - RotatePointAroundVector( start_points[i], normalized_direction, perpvec, (360.0/NUM_BEAM_SEGS)*i ); -// VectorAdd( start_points[i], origin, start_points[i] ); - VectorAdd( start_points[i], direction, end_points[i] ); - } - - GL_Bind( tr.whiteImage ); - - GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); - - // FIXME: Quake3 doesn't use this, so I never tested it - tess.numVertexes = 0; - tess.numIndexes = 0; - tess.firstIndex = 0; - tess.minIndex = 0; - tess.maxIndex = 0; - - for ( i = 0; i <= NUM_BEAM_SEGS; i++ ) { - VectorCopy(start_points[ i % NUM_BEAM_SEGS ], tess.xyz[tess.numVertexes++]); - VectorCopy(end_points [ i % NUM_BEAM_SEGS ], tess.xyz[tess.numVertexes++]); - } - - for ( i = 0; i < NUM_BEAM_SEGS; i++ ) { - tess.indexes[tess.numIndexes++] = i * 2; - tess.indexes[tess.numIndexes++] = (i + 1) * 2; - tess.indexes[tess.numIndexes++] = 1 + i * 2; - - tess.indexes[tess.numIndexes++] = 1 + i * 2; - tess.indexes[tess.numIndexes++] = (i + 1) * 2; - tess.indexes[tess.numIndexes++] = 1 + (i + 1) * 2; - } - - tess.minIndex = 0; - tess.maxIndex = tess.numVertexes; - - // FIXME: A lot of this can probably be removed for speed, and refactored into a more convenient function - RB_UpdateVBOs(ATTR_POSITION); - - { - shaderProgram_t *sp = &tr.textureColorShader; - vec4_t color; - - GLSL_VertexAttribsState(ATTR_POSITION); - GLSL_BindProgram(sp); - - GLSL_SetUniformMatrix16(sp, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); - - color[0] = 1.0f; - color[1] = 0.0f; - color[2] = 0.0f; - color[3] = 1.0f; - GLSL_SetUniformVec4(sp, TEXTURECOLOR_UNIFORM_COLOR, color); - } - - R_DrawElementsVBO(tess.numIndexes, tess.firstIndex, tess.minIndex, tess.maxIndex); - - tess.numIndexes = 0; - tess.numVertexes = 0; - tess.firstIndex = 0; - tess.minIndex = 0; - tess.maxIndex = 0; -} - -//================================================================================ - -static void DoRailCore( const vec3_t start, const vec3_t end, const vec3_t up, float len, float spanWidth ) -{ - float spanWidth2; - int vbase; - float t = len / 256.0f; - - vbase = tess.numVertexes; - - spanWidth2 = -spanWidth; - - // FIXME: use quad stamp? - VectorMA( start, spanWidth, up, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 0; - tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0] * 0.25 / 255.0f; - tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1] * 0.25 / 255.0f; - tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2] * 0.25 / 255.0f; - tess.numVertexes++; - - VectorMA( start, spanWidth2, up, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 1; - tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0] / 255.0f; - tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1] / 255.0f; - tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2] / 255.0f; - tess.numVertexes++; - - VectorMA( end, spanWidth, up, tess.xyz[tess.numVertexes] ); - - tess.texCoords[tess.numVertexes][0][0] = t; - tess.texCoords[tess.numVertexes][0][1] = 0; - tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0] / 255.0f; - tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1] / 255.0f; - tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2] / 255.0f; - tess.numVertexes++; - - VectorMA( end, spanWidth2, up, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = t; - tess.texCoords[tess.numVertexes][0][1] = 1; - tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0] / 255.0f; - tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1] / 255.0f; - tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2] / 255.0f; - tess.numVertexes++; - - tess.indexes[tess.numIndexes++] = vbase; - tess.indexes[tess.numIndexes++] = vbase + 1; - tess.indexes[tess.numIndexes++] = vbase + 2; - - tess.indexes[tess.numIndexes++] = vbase + 2; - tess.indexes[tess.numIndexes++] = vbase + 1; - tess.indexes[tess.numIndexes++] = vbase + 3; -} - -static void DoRailDiscs( int numSegs, const vec3_t start, const vec3_t dir, const vec3_t right, const vec3_t up ) -{ - int i; - vec3_t pos[4]; - vec3_t v; - int spanWidth = r_railWidth->integer; - float c, s; - float scale; - - if ( numSegs > 1 ) - numSegs--; - if ( !numSegs ) - return; - - scale = 0.25; - - for ( i = 0; i < 4; i++ ) - { - c = cos( DEG2RAD( 45 + i * 90 ) ); - s = sin( DEG2RAD( 45 + i * 90 ) ); - v[0] = ( right[0] * c + up[0] * s ) * scale * spanWidth; - v[1] = ( right[1] * c + up[1] * s ) * scale * spanWidth; - v[2] = ( right[2] * c + up[2] * s ) * scale * spanWidth; - VectorAdd( start, v, pos[i] ); - - if ( numSegs > 1 ) - { - // offset by 1 segment if we're doing a long distance shot - VectorAdd( pos[i], dir, pos[i] ); - } - } - - for ( i = 0; i < numSegs; i++ ) - { - int j; - - RB_CHECKOVERFLOW( 4, 6 ); - - for ( j = 0; j < 4; j++ ) - { - VectorCopy( pos[j], tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = ( j < 2 ); - tess.texCoords[tess.numVertexes][0][1] = ( j && j != 3 ); - tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0] / 255.0f; - tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1] / 255.0f; - tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2] / 255.0f; - tess.numVertexes++; - - VectorAdd( pos[j], dir, pos[j] ); - } - - tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 0; - tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 1; - tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 3; - tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 3; - tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 1; - tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 2; - } -} - -/* -** RB_SurfaceRailRinges -*/ -static void RB_SurfaceRailRings( void ) { - refEntity_t *e; - int numSegs; - int len; - vec3_t vec; - vec3_t right, up; - vec3_t start, end; - - e = &backEnd.currentEntity->e; - - VectorCopy( e->oldorigin, start ); - VectorCopy( e->origin, end ); - - // compute variables - VectorSubtract( end, start, vec ); - len = VectorNormalize( vec ); - MakeNormalVectors( vec, right, up ); - numSegs = ( len ) / r_railSegmentLength->value; - if ( numSegs <= 0 ) { - numSegs = 1; - } - - VectorScale( vec, r_railSegmentLength->value, vec ); - - DoRailDiscs( numSegs, start, vec, right, up ); -} - -/* -** RB_SurfaceRailCore -*/ -static void RB_SurfaceRailCore( void ) { - refEntity_t *e; - int len; - vec3_t right; - vec3_t vec; - vec3_t start, end; - vec3_t v1, v2; - - e = &backEnd.currentEntity->e; - - VectorCopy( e->oldorigin, start ); - VectorCopy( e->origin, end ); - - VectorSubtract( end, start, vec ); - len = VectorNormalize( vec ); - - // compute side vector - VectorSubtract( start, backEnd.viewParms.or.origin, v1 ); - VectorNormalize( v1 ); - VectorSubtract( end, backEnd.viewParms.or.origin, v2 ); - VectorNormalize( v2 ); - CrossProduct( v1, v2, right ); - VectorNormalize( right ); - - DoRailCore( start, end, right, len, r_railCoreWidth->integer ); -} - -/* -** RB_SurfaceLightningBolt -*/ -static void RB_SurfaceLightningBolt( void ) { - refEntity_t *e; - int len; - vec3_t right; - vec3_t vec; - vec3_t start, end; - vec3_t v1, v2; - int i; - - e = &backEnd.currentEntity->e; - - VectorCopy( e->oldorigin, end ); - VectorCopy( e->origin, start ); - - // compute variables - VectorSubtract( end, start, vec ); - len = VectorNormalize( vec ); - - // compute side vector - VectorSubtract( start, backEnd.viewParms.or.origin, v1 ); - VectorNormalize( v1 ); - VectorSubtract( end, backEnd.viewParms.or.origin, v2 ); - VectorNormalize( v2 ); - CrossProduct( v1, v2, right ); - VectorNormalize( right ); - - for ( i = 0 ; i < 4 ; i++ ) { - vec3_t temp; - - DoRailCore( start, end, right, len, 8 ); - RotatePointAroundVector( temp, vec, right, 45 ); - VectorCopy( temp, right ); - } -} - -/* -** VectorArrayNormalize -* -* The inputs to this routing seem to always be close to length = 1.0 (about 0.6 to 2.0) -* This means that we don't have to worry about zero length or enormously long vectors. -*/ -static void VectorArrayNormalize(vec4_t *normals, unsigned int count) -{ -// assert(count); - -#if idppc - { - register float half = 0.5; - register float one = 1.0; - float *components = (float *)normals; - - // Vanilla PPC code, but since PPC has a reciprocal square root estimate instruction, - // runs *much* faster than calling sqrt(). We'll use a single Newton-Raphson - // refinement step to get a little more precision. This seems to yeild results - // that are correct to 3 decimal places and usually correct to at least 4 (sometimes 5). - // (That is, for the given input range of about 0.6 to 2.0). - do { - float x, y, z; - float B, y0, y1; - - x = components[0]; - y = components[1]; - z = components[2]; - components += 4; - B = x*x + y*y + z*z; - -#ifdef __GNUC__ - asm("frsqrte %0,%1" : "=f" (y0) : "f" (B)); -#else - y0 = __frsqrte(B); -#endif - y1 = y0 + half*y0*(one - B*y0*y0); - - x = x * y1; - y = y * y1; - components[-4] = x; - z = z * y1; - components[-3] = y; - components[-2] = z; - } while(count--); - } -#else // No assembly version for this architecture, or C_ONLY defined - // given the input, it's safe to call VectorNormalizeFast - while (count--) { - VectorNormalizeFast(normals[0]); - normals++; - } -#endif - -} - - - -/* -** LerpMeshVertexes -*/ -#if idppc_altivec -static void LerpMeshVertexes_altivec(md3Surface_t *surf, float backlerp) -{ - short *oldXyz, *newXyz, *oldNormals, *newNormals; - float *outXyz, *outNormal; - float oldXyzScale QALIGN(16); - float newXyzScale QALIGN(16); - float oldNormalScale QALIGN(16); - float newNormalScale QALIGN(16); - int vertNum; - unsigned lat, lng; - int numVerts; - - outXyz = tess.xyz[tess.numVertexes]; - outNormal = tess.normal[tess.numVertexes]; - - newXyz = (short *)((byte *)surf + surf->ofsXyzNormals) - + (backEnd.currentEntity->e.frame * surf->numVerts * 4); - newNormals = newXyz + 3; - - newXyzScale = MD3_XYZ_SCALE * (1.0 - backlerp); - newNormalScale = 1.0 - backlerp; - - numVerts = surf->numVerts; - - if ( backlerp == 0 ) { - vector signed short newNormalsVec0; - vector signed short newNormalsVec1; - vector signed int newNormalsIntVec; - vector float newNormalsFloatVec; - vector float newXyzScaleVec; - vector unsigned char newNormalsLoadPermute; - vector unsigned char newNormalsStorePermute; - vector float zero; - - newNormalsStorePermute = vec_lvsl(0,(float *)&newXyzScaleVec); - newXyzScaleVec = *(vector float *)&newXyzScale; - newXyzScaleVec = vec_perm(newXyzScaleVec,newXyzScaleVec,newNormalsStorePermute); - newXyzScaleVec = vec_splat(newXyzScaleVec,0); - newNormalsLoadPermute = vec_lvsl(0,newXyz); - newNormalsStorePermute = vec_lvsr(0,outXyz); - zero = (vector float)vec_splat_s8(0); - // - // just copy the vertexes - // - for (vertNum=0 ; vertNum < numVerts ; vertNum++, - newXyz += 4, newNormals += 4, - outXyz += 4, outNormal += 4) - { - newNormalsLoadPermute = vec_lvsl(0,newXyz); - newNormalsStorePermute = vec_lvsr(0,outXyz); - newNormalsVec0 = vec_ld(0,newXyz); - newNormalsVec1 = vec_ld(16,newXyz); - newNormalsVec0 = vec_perm(newNormalsVec0,newNormalsVec1,newNormalsLoadPermute); - newNormalsIntVec = vec_unpackh(newNormalsVec0); - newNormalsFloatVec = vec_ctf(newNormalsIntVec,0); - newNormalsFloatVec = vec_madd(newNormalsFloatVec,newXyzScaleVec,zero); - newNormalsFloatVec = vec_perm(newNormalsFloatVec,newNormalsFloatVec,newNormalsStorePermute); - //outXyz[0] = newXyz[0] * newXyzScale; - //outXyz[1] = newXyz[1] * newXyzScale; - //outXyz[2] = newXyz[2] * newXyzScale; - - lat = ( newNormals[0] >> 8 ) & 0xff; - lng = ( newNormals[0] & 0xff ); - lat *= (FUNCTABLE_SIZE/256); - lng *= (FUNCTABLE_SIZE/256); - - // decode X as cos( lat ) * sin( long ) - // decode Y as sin( lat ) * sin( long ) - // decode Z as cos( long ) - - outNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; - outNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; - outNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; - - vec_ste(newNormalsFloatVec,0,outXyz); - vec_ste(newNormalsFloatVec,4,outXyz); - vec_ste(newNormalsFloatVec,8,outXyz); - } - } else { - // - // interpolate and copy the vertex and normal - // - oldXyz = (short *)((byte *)surf + surf->ofsXyzNormals) - + (backEnd.currentEntity->e.oldframe * surf->numVerts * 4); - oldNormals = oldXyz + 3; - - oldXyzScale = MD3_XYZ_SCALE * backlerp; - oldNormalScale = backlerp; - - for (vertNum=0 ; vertNum < numVerts ; vertNum++, - oldXyz += 4, newXyz += 4, oldNormals += 4, newNormals += 4, - outXyz += 4, outNormal += 4) - { - vec3_t uncompressedOldNormal, uncompressedNewNormal; - - // interpolate the xyz - outXyz[0] = oldXyz[0] * oldXyzScale + newXyz[0] * newXyzScale; - outXyz[1] = oldXyz[1] * oldXyzScale + newXyz[1] * newXyzScale; - outXyz[2] = oldXyz[2] * oldXyzScale + newXyz[2] * newXyzScale; - - // FIXME: interpolate lat/long instead? - lat = ( newNormals[0] >> 8 ) & 0xff; - lng = ( newNormals[0] & 0xff ); - lat *= 4; - lng *= 4; - uncompressedNewNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; - uncompressedNewNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; - uncompressedNewNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; - - lat = ( oldNormals[0] >> 8 ) & 0xff; - lng = ( oldNormals[0] & 0xff ); - lat *= 4; - lng *= 4; - - uncompressedOldNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; - uncompressedOldNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; - uncompressedOldNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; - - outNormal[0] = uncompressedOldNormal[0] * oldNormalScale + uncompressedNewNormal[0] * newNormalScale; - outNormal[1] = uncompressedOldNormal[1] * oldNormalScale + uncompressedNewNormal[1] * newNormalScale; - outNormal[2] = uncompressedOldNormal[2] * oldNormalScale + uncompressedNewNormal[2] * newNormalScale; - -// VectorNormalize (outNormal); - } - VectorArrayNormalize((vec4_t *)tess.normal[tess.numVertexes], numVerts); - } -} -#endif - -static void LerpMeshVertexes_scalar(mdvSurface_t *surf, float backlerp) -{ -#if 0 - short *oldXyz, *newXyz, *oldNormals, *newNormals; - float *outXyz, *outNormal; - float oldXyzScale, newXyzScale; - float oldNormalScale, newNormalScale; - int vertNum; - unsigned lat, lng; - int numVerts; - - outXyz = tess.xyz[tess.numVertexes]; - outNormal = tess.normal[tess.numVertexes]; - - newXyz = (short *)((byte *)surf + surf->ofsXyzNormals) - + (backEnd.currentEntity->e.frame * surf->numVerts * 4); - newNormals = newXyz + 3; - - newXyzScale = MD3_XYZ_SCALE * (1.0 - backlerp); - newNormalScale = 1.0 - backlerp; - - numVerts = surf->numVerts; - - if ( backlerp == 0 ) { - // - // just copy the vertexes - // - for (vertNum=0 ; vertNum < numVerts ; vertNum++, - newXyz += 4, newNormals += 4, - outXyz += 4, outNormal += 4) - { - - outXyz[0] = newXyz[0] * newXyzScale; - outXyz[1] = newXyz[1] * newXyzScale; - outXyz[2] = newXyz[2] * newXyzScale; - - lat = ( newNormals[0] >> 8 ) & 0xff; - lng = ( newNormals[0] & 0xff ); - lat *= (FUNCTABLE_SIZE/256); - lng *= (FUNCTABLE_SIZE/256); - - // decode X as cos( lat ) * sin( long ) - // decode Y as sin( lat ) * sin( long ) - // decode Z as cos( long ) - - outNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; - outNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; - outNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; - } - } else { - // - // interpolate and copy the vertex and normal - // - oldXyz = (short *)((byte *)surf + surf->ofsXyzNormals) - + (backEnd.currentEntity->e.oldframe * surf->numVerts * 4); - oldNormals = oldXyz + 3; - - oldXyzScale = MD3_XYZ_SCALE * backlerp; - oldNormalScale = backlerp; - - for (vertNum=0 ; vertNum < numVerts ; vertNum++, - oldXyz += 4, newXyz += 4, oldNormals += 4, newNormals += 4, - outXyz += 4, outNormal += 4) - { - vec3_t uncompressedOldNormal, uncompressedNewNormal; - - // interpolate the xyz - outXyz[0] = oldXyz[0] * oldXyzScale + newXyz[0] * newXyzScale; - outXyz[1] = oldXyz[1] * oldXyzScale + newXyz[1] * newXyzScale; - outXyz[2] = oldXyz[2] * oldXyzScale + newXyz[2] * newXyzScale; - - // FIXME: interpolate lat/long instead? - lat = ( newNormals[0] >> 8 ) & 0xff; - lng = ( newNormals[0] & 0xff ); - lat *= 4; - lng *= 4; - uncompressedNewNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; - uncompressedNewNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; - uncompressedNewNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; - - lat = ( oldNormals[0] >> 8 ) & 0xff; - lng = ( oldNormals[0] & 0xff ); - lat *= 4; - lng *= 4; - - uncompressedOldNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; - uncompressedOldNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; - uncompressedOldNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; - - outNormal[0] = uncompressedOldNormal[0] * oldNormalScale + uncompressedNewNormal[0] * newNormalScale; - outNormal[1] = uncompressedOldNormal[1] * oldNormalScale + uncompressedNewNormal[1] * newNormalScale; - outNormal[2] = uncompressedOldNormal[2] * oldNormalScale + uncompressedNewNormal[2] * newNormalScale; - -// VectorNormalize (outNormal); - } - VectorArrayNormalize((vec4_t *)tess.normal[tess.numVertexes], numVerts); - } -#endif - float *outXyz, *outNormal; - mdvVertex_t *newVerts; - int vertNum; - - newVerts = surf->verts + backEnd.currentEntity->e.frame * surf->numVerts; - - outXyz = tess.xyz[tess.numVertexes]; - outNormal = tess.normal[tess.numVertexes]; - - if (backlerp == 0) - { - // - // just copy the vertexes - // - - for (vertNum=0 ; vertNum < surf->numVerts ; vertNum++) - { - VectorCopy(newVerts->xyz, outXyz); - VectorCopy(newVerts->normal, outNormal); - newVerts++; - outXyz += 4; - outNormal += 4; - } - } - else - { - // - // interpolate and copy the vertex and normal - // - - mdvVertex_t *oldVerts; - - oldVerts = surf->verts + backEnd.currentEntity->e.oldframe * surf->numVerts; - - for (vertNum=0 ; vertNum < surf->numVerts ; vertNum++) - { - VectorLerp(backlerp, newVerts->xyz, oldVerts->xyz, outXyz); - VectorLerp(backlerp, newVerts->normal, oldVerts->normal, outNormal); - //VectorNormalize(outNormal); - newVerts++; - oldVerts++; - outXyz += 4; - outNormal += 4; - } - VectorArrayNormalize((vec4_t *)tess.normal[tess.numVertexes], surf->numVerts); - } - -} - -static void LerpMeshVertexes(mdvSurface_t *surf, float backlerp) -{ -#if 0 -#if idppc_altivec - if (com_altivec->integer) { - // must be in a seperate function or G3 systems will crash. - LerpMeshVertexes_altivec( surf, backlerp ); - return; - } -#endif // idppc_altivec -#endif - LerpMeshVertexes_scalar( surf, backlerp ); -} - - -/* -============= -RB_SurfaceMesh -============= -*/ -static void RB_SurfaceMesh(mdvSurface_t *surface) { - int j; - float backlerp; - srfTriangle_t *triangles; - mdvSt_t *texCoords; - int indexes; - int Bob, Doug; - int numVerts; - - if ( backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame ) { - backlerp = 0; - } else { - backlerp = backEnd.currentEntity->e.backlerp; - } - - RB_CHECKOVERFLOW( surface->numVerts, surface->numTriangles*3 ); - - LerpMeshVertexes (surface, backlerp); - - triangles = surface->triangles; - indexes = surface->numTriangles * 3; - Bob = tess.numIndexes; - Doug = tess.numVertexes; - for (j = 0 ; j < surface->numTriangles ; j++) { - tess.indexes[Bob + j*3 + 0] = Doug + triangles[j].indexes[0]; - tess.indexes[Bob + j*3 + 1] = Doug + triangles[j].indexes[1]; - tess.indexes[Bob + j*3 + 2] = Doug + triangles[j].indexes[2]; - } - tess.numIndexes += indexes; - - texCoords = surface->st; - - numVerts = surface->numVerts; - for ( j = 0; j < numVerts; j++ ) { - tess.texCoords[Doug + j][0][0] = texCoords[j].st[0]; - tess.texCoords[Doug + j][0][1] = texCoords[j].st[1]; - // FIXME: fill in lightmapST for completeness? - } - - tess.numVertexes += surface->numVerts; - -} - - -/* -============== -RB_SurfaceFace -============== -*/ -static void RB_SurfaceFace( srfSurfaceFace_t *srf ) { - if( RB_SurfaceHelperVBO (srf->vbo, srf->ibo, srf->numVerts, srf->numTriangles * 3, - srf->firstIndex, srf->minIndex, srf->maxIndex, srf->dlightBits, srf->pshadowBits, qtrue ) ) - { - return; - } - - RB_SurfaceHelper(srf->numVerts, srf->verts, srf->numTriangles, - srf->triangles, srf->dlightBits, srf->pshadowBits); -} - - -static float LodErrorForVolume( vec3_t local, float radius ) { - vec3_t world; - float d; - - // never let it go negative - if ( r_lodCurveError->value < 0 ) { - return 0; - } - - world[0] = local[0] * backEnd.or.axis[0][0] + local[1] * backEnd.or.axis[1][0] + - local[2] * backEnd.or.axis[2][0] + backEnd.or.origin[0]; - world[1] = local[0] * backEnd.or.axis[0][1] + local[1] * backEnd.or.axis[1][1] + - local[2] * backEnd.or.axis[2][1] + backEnd.or.origin[1]; - world[2] = local[0] * backEnd.or.axis[0][2] + local[1] * backEnd.or.axis[1][2] + - local[2] * backEnd.or.axis[2][2] + backEnd.or.origin[2]; - - VectorSubtract( world, backEnd.viewParms.or.origin, world ); - d = DotProduct( world, backEnd.viewParms.or.axis[0] ); - - if ( d < 0 ) { - d = -d; - } - d -= radius; - if ( d < 1 ) { - d = 1; - } - - return r_lodCurveError->value / d; -} - -/* -============= -RB_SurfaceGrid - -Just copy the grid of points and triangulate -============= -*/ -static void RB_SurfaceGrid( srfGridMesh_t *srf ) { - int i, j; - float *xyz; - float *texCoords, *lightCoords; - float *normal; -#ifdef USE_VERT_TANGENT_SPACE - float *tangent, *bitangent; -#endif - float *color, *lightdir; - srfVert_t *dv; - int rows, irows, vrows; - int used; - int widthTable[MAX_GRID_SIZE]; - int heightTable[MAX_GRID_SIZE]; - float lodError; - int lodWidth, lodHeight; - int numVertexes; - int dlightBits; - int pshadowBits; - //int *vDlightBits; - - if( RB_SurfaceHelperVBO (srf->vbo, srf->ibo, srf->numVerts, srf->numTriangles * 3, - srf->firstIndex, srf->minIndex, srf->maxIndex, srf->dlightBits, srf->pshadowBits, qtrue ) ) - { - return; - } - - dlightBits = srf->dlightBits; - tess.dlightBits |= dlightBits; - - pshadowBits = srf->pshadowBits; - tess.pshadowBits |= pshadowBits; - - // determine the allowable discrepance - lodError = LodErrorForVolume( srf->lodOrigin, srf->lodRadius ); - - // determine which rows and columns of the subdivision - // we are actually going to use - widthTable[0] = 0; - lodWidth = 1; - for ( i = 1 ; i < srf->width-1 ; i++ ) { - if ( srf->widthLodError[i] <= lodError ) { - widthTable[lodWidth] = i; - lodWidth++; - } - } - widthTable[lodWidth] = srf->width-1; - lodWidth++; - - heightTable[0] = 0; - lodHeight = 1; - for ( i = 1 ; i < srf->height-1 ; i++ ) { - if ( srf->heightLodError[i] <= lodError ) { - heightTable[lodHeight] = i; - lodHeight++; - } - } - heightTable[lodHeight] = srf->height-1; - lodHeight++; - - - // very large grids may have more points or indexes than can be fit - // in the tess structure, so we may have to issue it in multiple passes - - used = 0; - while ( used < lodHeight - 1 ) { - // see how many rows of both verts and indexes we can add without overflowing - do { - vrows = ( SHADER_MAX_VERTEXES - tess.numVertexes ) / lodWidth; - irows = ( SHADER_MAX_INDEXES - tess.numIndexes ) / ( lodWidth * 6 ); - - // if we don't have enough space for at least one strip, flush the buffer - if ( vrows < 2 || irows < 1 ) { - RB_EndSurface(); - RB_BeginSurface(tess.shader, tess.fogNum ); - } else { - break; - } - } while ( 1 ); - - rows = irows; - if ( vrows < irows + 1 ) { - rows = vrows - 1; - } - if ( used + rows > lodHeight ) { - rows = lodHeight - used; - } - - numVertexes = tess.numVertexes; - - xyz = tess.xyz[numVertexes]; - normal = tess.normal[numVertexes]; -#ifdef USE_VERT_TANGENT_SPACE - tangent = tess.tangent[numVertexes]; - bitangent = tess.bitangent[numVertexes]; -#endif - texCoords = tess.texCoords[numVertexes][0]; - lightCoords = tess.texCoords[numVertexes][1]; - color = tess.vertexColors[numVertexes]; - lightdir = tess.lightdir[numVertexes]; - //vDlightBits = &tess.vertexDlightBits[numVertexes]; - - for ( i = 0 ; i < rows ; i++ ) { - for ( j = 0 ; j < lodWidth ; j++ ) { - dv = srf->verts + heightTable[ used + i ] * srf->width - + widthTable[ j ]; - - if ( tess.shader->vertexAttribs & ATTR_POSITION ) - { - VectorCopy(dv->xyz, xyz); - xyz += 4; - } - - if ( tess.shader->vertexAttribs & ATTR_NORMAL ) - { - VectorCopy(dv->normal, normal); - normal += 4; - } - -#ifdef USE_VERT_TANGENT_SPACE - if ( tess.shader->vertexAttribs & ATTR_TANGENT ) - { - VectorCopy(dv->tangent, tangent); - tangent += 4; - } - - if ( tess.shader->vertexAttribs & ATTR_BITANGENT ) - { - VectorCopy(dv->bitangent, bitangent); - bitangent += 4; - } -#endif - if ( tess.shader->vertexAttribs & ATTR_TEXCOORD ) - { - VectorCopy2(dv->st, texCoords); - texCoords += 4; - } - - if ( tess.shader->vertexAttribs & ATTR_LIGHTCOORD ) - { - VectorCopy2(dv->lightmap, lightCoords); - lightCoords += 4; - } - - if ( tess.shader->vertexAttribs & ATTR_COLOR ) - { - VectorCopy4(dv->vertexColors, color); - color += 4; - } - - if ( tess.shader->vertexAttribs & ATTR_LIGHTDIRECTION ) - { - VectorCopy(dv->lightdir, lightdir); - lightdir += 4; - } - - //*vDlightBits++ = dlightBits; - } - } - - - // add the indexes - { - int numIndexes; - int w, h; - - h = rows - 1; - w = lodWidth - 1; - numIndexes = tess.numIndexes; - for (i = 0 ; i < h ; i++) { - for (j = 0 ; j < w ; j++) { - int v1, v2, v3, v4; - - // vertex order to be reckognized as tristrips - v1 = numVertexes + i*lodWidth + j + 1; - v2 = v1 - 1; - v3 = v2 + lodWidth; - v4 = v3 + 1; - - tess.indexes[numIndexes] = v2; - tess.indexes[numIndexes+1] = v3; - tess.indexes[numIndexes+2] = v1; - - tess.indexes[numIndexes+3] = v1; - tess.indexes[numIndexes+4] = v3; - tess.indexes[numIndexes+5] = v4; - numIndexes += 6; - } - } - - tess.numIndexes = numIndexes; - } - - tess.numVertexes += rows * lodWidth; - - used += rows - 1; - } -} - - -/* -=========================================================================== - -NULL MODEL - -=========================================================================== -*/ - -/* -=================== -RB_SurfaceAxis - -Draws x/y/z lines from the origin for orientation debugging -=================== -*/ -static void RB_SurfaceAxis( void ) { - // FIXME: implement this -#if 0 - GL_Bind( tr.whiteImage ); - qglLineWidth( 3 ); - qglBegin( GL_LINES ); - qglColor3f( 1,0,0 ); - qglVertex3f( 0,0,0 ); - qglVertex3f( 16,0,0 ); - qglColor3f( 0,1,0 ); - qglVertex3f( 0,0,0 ); - qglVertex3f( 0,16,0 ); - qglColor3f( 0,0,1 ); - qglVertex3f( 0,0,0 ); - qglVertex3f( 0,0,16 ); - qglEnd(); - qglLineWidth( 1 ); -#endif -} - -//=========================================================================== - -/* -==================== -RB_SurfaceEntity - -Entities that have a single procedurally generated surface -==================== -*/ -static void RB_SurfaceEntity( surfaceType_t *surfType ) { - switch( backEnd.currentEntity->e.reType ) { - case RT_SPRITE: - RB_SurfaceSprite(); - break; - case RT_BEAM: - RB_SurfaceBeam(); - break; - case RT_RAIL_CORE: - RB_SurfaceRailCore(); - break; - case RT_RAIL_RINGS: - RB_SurfaceRailRings(); - break; - case RT_LIGHTNING: - RB_SurfaceLightningBolt(); - break; - default: - RB_SurfaceAxis(); - break; - } - return; -} - -static void RB_SurfaceBad( surfaceType_t *surfType ) { - ri.Printf( PRINT_ALL, "Bad surface tesselated.\n" ); -} - -static void RB_SurfaceFlare(srfFlare_t *surf) -{ - if (r_flares->integer) - RB_AddFlare(surf, tess.fogNum, surf->origin, surf->color, surf->normal); -} - -static void RB_SurfaceVBOMesh(srfVBOMesh_t * srf) -{ - RB_SurfaceHelperVBO (srf->vbo, srf->ibo, srf->numVerts, srf->numIndexes, srf->firstIndex, - srf->minIndex, srf->maxIndex, srf->dlightBits, srf->pshadowBits, qfalse ); -} - -void RB_SurfaceVBOMDVMesh(srfVBOMDVMesh_t * surface) -{ - //mdvModel_t *mdvModel; - //mdvSurface_t *mdvSurface; - refEntity_t *refEnt; - - GLimp_LogComment("--- RB_SurfaceVBOMDVMesh ---\n"); - - if(!surface->vbo || !surface->ibo) - return; - - //RB_CheckVBOandIBO(surface->vbo, surface->ibo); - RB_EndSurface(); - RB_BeginSurface(tess.shader, tess.fogNum); - - R_BindVBO(surface->vbo); - R_BindIBO(surface->ibo); - - tess.useInternalVBO = qfalse; - - tess.numIndexes += surface->numIndexes; - tess.numVertexes += surface->numVerts; - tess.minIndex = surface->minIndex; - tess.maxIndex = surface->maxIndex; - - //mdvModel = surface->mdvModel; - //mdvSurface = surface->mdvSurface; - - refEnt = &backEnd.currentEntity->e; - - if(refEnt->oldframe == refEnt->frame) - { - glState.vertexAttribsInterpolation = 0; - } - else - { - glState.vertexAttribsInterpolation = refEnt->backlerp; - } - - glState.vertexAttribsOldFrame = refEnt->oldframe; - glState.vertexAttribsNewFrame = refEnt->frame; - - RB_EndSurface(); - - // So we don't lerp surfaces that shouldn't be lerped - glState.vertexAttribsInterpolation = 0; -} - -static void RB_SurfaceDisplayList( srfDisplayList_t *surf ) { - // all apropriate state must be set in RB_BeginSurface - // this isn't implemented yet... - qglCallList( surf->listNum ); -} - -static void RB_SurfaceSkip( void *surf ) { -} - - -void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])( void *) = { - (void(*)(void*))RB_SurfaceBad, // SF_BAD, - (void(*)(void*))RB_SurfaceSkip, // SF_SKIP, - (void(*)(void*))RB_SurfaceFace, // SF_FACE, - (void(*)(void*))RB_SurfaceGrid, // SF_GRID, - (void(*)(void*))RB_SurfaceTriangles, // SF_TRIANGLES, - (void(*)(void*))RB_SurfacePolychain, // SF_POLY, - (void(*)(void*))RB_SurfaceMesh, // SF_MDV, - (void(*)(void*))RB_SurfaceAnim, // SF_MD4, -#ifdef RAVENMD4 - (void(*)(void*))RB_MDRSurfaceAnim, // SF_MDR, -#endif - (void(*)(void*))RB_IQMSurfaceAnim, // SF_IQM, - (void(*)(void*))RB_SurfaceFlare, // SF_FLARE, - (void(*)(void*))RB_SurfaceEntity, // SF_ENTITY - (void(*)(void*))RB_SurfaceDisplayList, // SF_DISPLAY_LIST - (void(*)(void*))RB_SurfaceVBOMesh, // SF_VBO_MESH, - (void(*)(void*))RB_SurfaceVBOMDVMesh, // SF_VBO_MDVMESH -}; diff --git a/src/rend2/tr_vbo.c b/src/rend2/tr_vbo.c deleted file mode 100644 index 483df2e3..00000000 --- a/src/rend2/tr_vbo.c +++ /dev/null @@ -1,928 +0,0 @@ -/* -=========================================================================== -Copyright (C) 2007-2009 Robert Beckebans - -This file is part of XreaL source code. - -XreaL source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -XreaL source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with XreaL source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_vbo.c -#include "tr_local.h" - -/* -============ -R_CreateVBO -============ -*/ -VBO_t *R_CreateVBO(const char *name, byte * vertexes, int vertexesSize, vboUsage_t usage) -{ - VBO_t *vbo; - int glUsage; - - switch (usage) - { - case VBO_USAGE_STATIC: - glUsage = GL_STATIC_DRAW_ARB; - break; - - case VBO_USAGE_DYNAMIC: - glUsage = GL_DYNAMIC_DRAW_ARB; - break; - - default: - Com_Error(ERR_FATAL, "bad vboUsage_t given: %i", usage); - return NULL; - } - - if(strlen(name) >= MAX_QPATH) - { - ri.Error(ERR_DROP, "R_CreateVBO: \"%s\" is too long", name); - } - - if ( tr.numVBOs == MAX_VBOS ) { - ri.Error( ERR_DROP, "R_CreateVBO: MAX_VBOS hit"); - } - - R_IssuePendingRenderCommands(); - - vbo = tr.vbos[tr.numVBOs] = ri.Hunk_Alloc(sizeof(*vbo), h_low); - tr.numVBOs++; - - memset(vbo, 0, sizeof(*vbo)); - - Q_strncpyz(vbo->name, name, sizeof(vbo->name)); - - vbo->vertexesSize = vertexesSize; - - qglGenBuffersARB(1, &vbo->vertexesVBO); - - qglBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo->vertexesVBO); - qglBufferDataARB(GL_ARRAY_BUFFER_ARB, vertexesSize, vertexes, glUsage); - - qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); - - glState.currentVBO = NULL; - - GL_CheckErrors(); - - return vbo; -} - -/* -============ -R_CreateVBO2 -============ -*/ -VBO_t *R_CreateVBO2(const char *name, int numVertexes, srfVert_t * verts, unsigned int stateBits, vboUsage_t usage) -{ - VBO_t *vbo; - int i; - - byte *data; - int dataSize; - int dataOfs; - - int glUsage; - - switch (usage) - { - case VBO_USAGE_STATIC: - glUsage = GL_STATIC_DRAW_ARB; - break; - - case VBO_USAGE_DYNAMIC: - glUsage = GL_DYNAMIC_DRAW_ARB; - break; - - default: - Com_Error(ERR_FATAL, "bad vboUsage_t given: %i", usage); - return NULL; - } - - if(!numVertexes) - return NULL; - - if(strlen(name) >= MAX_QPATH) - { - ri.Error(ERR_DROP, "R_CreateVBO2: \"%s\" is too long", name); - } - - if ( tr.numVBOs == MAX_VBOS ) { - ri.Error( ERR_DROP, "R_CreateVBO2: MAX_VBOS hit"); - } - - R_IssuePendingRenderCommands(); - - vbo = tr.vbos[tr.numVBOs] = ri.Hunk_Alloc(sizeof(*vbo), h_low); - tr.numVBOs++; - - memset(vbo, 0, sizeof(*vbo)); - - Q_strncpyz(vbo->name, name, sizeof(vbo->name)); - - if (usage == VBO_USAGE_STATIC) - { - // since these vertex attributes are never altered, interleave them - vbo->ofs_xyz = 0; - dataSize = sizeof(verts[0].xyz); - - if(stateBits & ATTR_NORMAL) - { - vbo->ofs_normal = dataSize; - dataSize += sizeof(verts[0].normal); - } - -#ifdef USE_VERT_TANGENT_SPACE - if(stateBits & ATTR_TANGENT) - { - vbo->ofs_tangent = dataSize; - dataSize += sizeof(verts[0].tangent); - } - - if(stateBits & ATTR_BITANGENT) - { - vbo->ofs_bitangent = dataSize; - dataSize += sizeof(verts[0].bitangent); - } -#endif - - if(stateBits & ATTR_TEXCOORD) - { - vbo->ofs_st = dataSize; - dataSize += sizeof(verts[0].st); - } - - if(stateBits & ATTR_LIGHTCOORD) - { - vbo->ofs_lightmap = dataSize; - dataSize += sizeof(verts[0].lightmap); - } - - if(stateBits & ATTR_COLOR) - { - vbo->ofs_vertexcolor = dataSize; - dataSize += sizeof(verts[0].vertexColors); - } - - if(stateBits & ATTR_LIGHTDIRECTION) - { - vbo->ofs_lightdir = dataSize; - dataSize += sizeof(verts[0].lightdir); - } - - vbo->stride_xyz = dataSize; - vbo->stride_normal = dataSize; -#ifdef USE_VERT_TANGENT_SPACE - vbo->stride_tangent = dataSize; - vbo->stride_bitangent = dataSize; -#endif - vbo->stride_st = dataSize; - vbo->stride_lightmap = dataSize; - vbo->stride_vertexcolor = dataSize; - vbo->stride_lightdir = dataSize; - - // create VBO - dataSize *= numVertexes; - data = ri.Hunk_AllocateTempMemory(dataSize); - dataOfs = 0; - - //ri.Printf(PRINT_ALL, "CreateVBO: %d, %d %d %d %d %d, %d %d %d %d %d\n", dataSize, vbo->ofs_xyz, vbo->ofs_normal, vbo->ofs_st, vbo->ofs_lightmap, vbo->ofs_vertexcolor, - //vbo->stride_xyz, vbo->stride_normal, vbo->stride_st, vbo->stride_lightmap, vbo->stride_vertexcolor); - - for (i = 0; i < numVertexes; i++) - { - // xyz - memcpy(data + dataOfs, &verts[i].xyz, sizeof(verts[i].xyz)); - dataOfs += sizeof(verts[i].xyz); - - // normal - if(stateBits & ATTR_NORMAL) - { - memcpy(data + dataOfs, &verts[i].normal, sizeof(verts[i].normal)); - dataOfs += sizeof(verts[i].normal); - } - -#ifdef USE_VERT_TANGENT_SPACE - // tangent - if(stateBits & ATTR_TANGENT) - { - memcpy(data + dataOfs, &verts[i].tangent, sizeof(verts[i].tangent)); - dataOfs += sizeof(verts[i].tangent); - } - - // bitangent - if(stateBits & ATTR_BITANGENT) - { - memcpy(data + dataOfs, &verts[i].bitangent, sizeof(verts[i].bitangent)); - dataOfs += sizeof(verts[i].bitangent); - } -#endif - - // vertex texcoords - if(stateBits & ATTR_TEXCOORD) - { - memcpy(data + dataOfs, &verts[i].st, sizeof(verts[i].st)); - dataOfs += sizeof(verts[i].st); - } - - // feed vertex lightmap texcoords - if(stateBits & ATTR_LIGHTCOORD) - { - memcpy(data + dataOfs, &verts[i].lightmap, sizeof(verts[i].lightmap)); - dataOfs += sizeof(verts[i].lightmap); - } - - // feed vertex colors - if(stateBits & ATTR_COLOR) - { - memcpy(data + dataOfs, &verts[i].vertexColors, sizeof(verts[i].vertexColors)); - dataOfs += sizeof(verts[i].vertexColors); - } - - // feed vertex light directions - if(stateBits & ATTR_LIGHTDIRECTION) - { - memcpy(data + dataOfs, &verts[i].lightdir, sizeof(verts[i].lightdir)); - dataOfs += sizeof(verts[i].lightdir); - } - } - } - else - { - // since these vertex attributes may be changed, put them in flat arrays - dataSize = sizeof(verts[0].xyz); - - if(stateBits & ATTR_NORMAL) - { - dataSize += sizeof(verts[0].normal); - } - -#ifdef USE_VERT_TANGENT_SPACE - if(stateBits & ATTR_TANGENT) - { - dataSize += sizeof(verts[0].tangent); - } - - if(stateBits & ATTR_BITANGENT) - { - dataSize += sizeof(verts[0].bitangent); - } -#endif - - if(stateBits & ATTR_TEXCOORD) - { - dataSize += sizeof(verts[0].st); - } - - if(stateBits & ATTR_LIGHTCOORD) - { - dataSize += sizeof(verts[0].lightmap); - } - - if(stateBits & ATTR_COLOR) - { - dataSize += sizeof(verts[0].vertexColors); - } - - if(stateBits & ATTR_LIGHTDIRECTION) - { - dataSize += sizeof(verts[0].lightdir); - } - - // create VBO - dataSize *= numVertexes; - data = ri.Hunk_AllocateTempMemory(dataSize); - dataOfs = 0; - - vbo->ofs_xyz = 0; - vbo->ofs_normal = 0; -#ifdef USE_VERT_TANGENT_SPACE - vbo->ofs_tangent = 0; - vbo->ofs_bitangent = 0; -#endif - vbo->ofs_st = 0; - vbo->ofs_lightmap = 0; - vbo->ofs_vertexcolor = 0; - vbo->ofs_lightdir = 0; - - vbo->stride_xyz = sizeof(verts[0].xyz); - vbo->stride_normal = sizeof(verts[0].normal); -#ifdef USE_VERT_TANGENT_SPACE - vbo->stride_tangent = sizeof(verts[0].tangent); - vbo->stride_bitangent = sizeof(verts[0].bitangent); -#endif - vbo->stride_vertexcolor = sizeof(verts[0].vertexColors); - vbo->stride_st = sizeof(verts[0].st); - vbo->stride_lightmap = sizeof(verts[0].lightmap); - vbo->stride_lightdir = sizeof(verts[0].lightdir); - - //ri.Printf(PRINT_ALL, "2CreateVBO: %d, %d %d %d %d %d, %d %d %d %d %d\n", dataSize, vbo->ofs_xyz, vbo->ofs_normal, vbo->ofs_st, vbo->ofs_lightmap, vbo->ofs_vertexcolor, - //vbo->stride_xyz, vbo->stride_normal, vbo->stride_st, vbo->stride_lightmap, vbo->stride_vertexcolor); - - // xyz - for (i = 0; i < numVertexes; i++) - { - memcpy(data + dataOfs, &verts[i].xyz, sizeof(verts[i].xyz)); - dataOfs += sizeof(verts[i].xyz); - } - - // normal - if(stateBits & ATTR_NORMAL) - { - vbo->ofs_normal = dataOfs; - for (i = 0; i < numVertexes; i++) - { - memcpy(data + dataOfs, &verts[i].normal, sizeof(verts[i].normal)); - dataOfs += sizeof(verts[i].normal); - } - } - -#ifdef USE_VERT_TANGENT_SPACE - // tangent - if(stateBits & ATTR_TANGENT) - { - vbo->ofs_tangent = dataOfs; - for (i = 0; i < numVertexes; i++) - { - memcpy(data + dataOfs, &verts[i].tangent, sizeof(verts[i].tangent)); - dataOfs += sizeof(verts[i].tangent); - } - } - - // bitangent - if(stateBits & ATTR_BITANGENT) - { - vbo->ofs_bitangent = dataOfs; - for (i = 0; i < numVertexes; i++) - { - memcpy(data + dataOfs, &verts[i].bitangent, sizeof(verts[i].bitangent)); - dataOfs += sizeof(verts[i].bitangent); - } - } -#endif - - // vertex texcoords - if(stateBits & ATTR_TEXCOORD) - { - vbo->ofs_st = dataOfs; - for (i = 0; i < numVertexes; i++) - { - memcpy(data + dataOfs, &verts[i].st, sizeof(verts[i].st)); - dataOfs += sizeof(verts[i].st); - } - } - - // feed vertex lightmap texcoords - if(stateBits & ATTR_LIGHTCOORD) - { - vbo->ofs_lightmap = dataOfs; - for (i = 0; i < numVertexes; i++) - { - memcpy(data + dataOfs, &verts[i].lightmap, sizeof(verts[i].lightmap)); - dataOfs += sizeof(verts[i].lightmap); - } - } - - // feed vertex colors - if(stateBits & ATTR_COLOR) - { - vbo->ofs_vertexcolor = dataOfs; - for (i = 0; i < numVertexes; i++) - { - memcpy(data + dataOfs, &verts[i].vertexColors, sizeof(verts[i].vertexColors)); - dataOfs += sizeof(verts[i].vertexColors); - } - } - - // feed vertex lightdirs - if(stateBits & ATTR_LIGHTDIRECTION) - { - vbo->ofs_lightdir = dataOfs; - for (i = 0; i < numVertexes; i++) - { - memcpy(data + dataOfs, &verts[i].lightdir, sizeof(verts[i].lightdir)); - dataOfs += sizeof(verts[i].lightdir); - } - } - } - - - vbo->vertexesSize = dataSize; - - qglGenBuffersARB(1, &vbo->vertexesVBO); - - qglBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo->vertexesVBO); - qglBufferDataARB(GL_ARRAY_BUFFER_ARB, dataSize, data, glUsage); - - qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); - - glState.currentVBO = NULL; - - GL_CheckErrors(); - - ri.Hunk_FreeTempMemory(data); - - return vbo; -} - - -/* -============ -R_CreateIBO -============ -*/ -IBO_t *R_CreateIBO(const char *name, byte * indexes, int indexesSize, vboUsage_t usage) -{ - IBO_t *ibo; - int glUsage; - - switch (usage) - { - case VBO_USAGE_STATIC: - glUsage = GL_STATIC_DRAW_ARB; - break; - - case VBO_USAGE_DYNAMIC: - glUsage = GL_DYNAMIC_DRAW_ARB; - break; - - default: - Com_Error(ERR_FATAL, "bad vboUsage_t given: %i", usage); - return NULL; - } - - if(strlen(name) >= MAX_QPATH) - { - ri.Error(ERR_DROP, "R_CreateIBO: \"%s\" is too long", name); - } - - if ( tr.numIBOs == MAX_IBOS ) { - ri.Error( ERR_DROP, "R_CreateIBO: MAX_IBOS hit"); - } - - R_IssuePendingRenderCommands(); - - ibo = tr.ibos[tr.numIBOs] = ri.Hunk_Alloc(sizeof(*ibo), h_low); - tr.numIBOs++; - - Q_strncpyz(ibo->name, name, sizeof(ibo->name)); - - ibo->indexesSize = indexesSize; - - qglGenBuffersARB(1, &ibo->indexesVBO); - - qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ibo->indexesVBO); - qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indexesSize, indexes, glUsage); - - qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); - - glState.currentIBO = NULL; - - GL_CheckErrors(); - - return ibo; -} - -/* -============ -R_CreateIBO2 -============ -*/ -IBO_t *R_CreateIBO2(const char *name, int numTriangles, srfTriangle_t * triangles, vboUsage_t usage) -{ - IBO_t *ibo; - int i, j; - - byte *indexes; - int indexesSize; - int indexesOfs; - - srfTriangle_t *tri; - glIndex_t index; - int glUsage; - - switch (usage) - { - case VBO_USAGE_STATIC: - glUsage = GL_STATIC_DRAW_ARB; - break; - - case VBO_USAGE_DYNAMIC: - glUsage = GL_DYNAMIC_DRAW_ARB; - break; - - default: - Com_Error(ERR_FATAL, "bad vboUsage_t given: %i", usage); - return NULL; - } - - if(!numTriangles) - return NULL; - - if(strlen(name) >= MAX_QPATH) - { - ri.Error(ERR_DROP, "R_CreateIBO2: \"%s\" is too long", name); - } - - if ( tr.numIBOs == MAX_IBOS ) { - ri.Error( ERR_DROP, "R_CreateIBO2: MAX_IBOS hit"); - } - - R_IssuePendingRenderCommands(); - - ibo = tr.ibos[tr.numIBOs] = ri.Hunk_Alloc(sizeof(*ibo), h_low); - tr.numIBOs++; - - Q_strncpyz(ibo->name, name, sizeof(ibo->name)); - - indexesSize = numTriangles * 3 * sizeof(int); - indexes = ri.Hunk_AllocateTempMemory(indexesSize); - indexesOfs = 0; - - for(i = 0, tri = triangles; i < numTriangles; i++, tri++) - { - for(j = 0; j < 3; j++) - { - index = tri->indexes[j]; - memcpy(indexes + indexesOfs, &index, sizeof(glIndex_t)); - indexesOfs += sizeof(glIndex_t); - } - } - - ibo->indexesSize = indexesSize; - - qglGenBuffersARB(1, &ibo->indexesVBO); - - qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ibo->indexesVBO); - qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indexesSize, indexes, glUsage); - - qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); - - glState.currentIBO = NULL; - - GL_CheckErrors(); - - ri.Hunk_FreeTempMemory(indexes); - - return ibo; -} - -/* -============ -R_BindVBO -============ -*/ -void R_BindVBO(VBO_t * vbo) -{ - if(!vbo) - { - //R_BindNullVBO(); - ri.Error(ERR_DROP, "R_BindNullVBO: NULL vbo"); - return; - } - - if(r_logFile->integer) - { - // don't just call LogComment, or we will get a call to va() every frame! - GLimp_LogComment(va("--- R_BindVBO( %s ) ---\n", vbo->name)); - } - - if(glState.currentVBO != vbo) - { - glState.currentVBO = vbo; - glState.vertexAttribPointersSet = 0; - - glState.vertexAttribsInterpolation = 0; - glState.vertexAttribsOldFrame = 0; - glState.vertexAttribsNewFrame = 0; - - qglBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo->vertexesVBO); - - backEnd.pc.c_vboVertexBuffers++; - } -} - -/* -============ -R_BindNullVBO -============ -*/ -void R_BindNullVBO(void) -{ - GLimp_LogComment("--- R_BindNullVBO ---\n"); - - if(glState.currentVBO) - { - qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); - glState.currentVBO = NULL; - } - - GL_CheckErrors(); -} - -/* -============ -R_BindIBO -============ -*/ -void R_BindIBO(IBO_t * ibo) -{ - if(!ibo) - { - //R_BindNullIBO(); - ri.Error(ERR_DROP, "R_BindIBO: NULL ibo"); - return; - } - - if(r_logFile->integer) - { - // don't just call LogComment, or we will get a call to va() every frame! - GLimp_LogComment(va("--- R_BindIBO( %s ) ---\n", ibo->name)); - } - - if(glState.currentIBO != ibo) - { - qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ibo->indexesVBO); - - glState.currentIBO = ibo; - - backEnd.pc.c_vboIndexBuffers++; - } -} - -/* -============ -R_BindNullIBO -============ -*/ -void R_BindNullIBO(void) -{ - GLimp_LogComment("--- R_BindNullIBO ---\n"); - - if(glState.currentIBO) - { - qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); - glState.currentIBO = NULL; - glState.vertexAttribPointersSet = 0; - } -} - -/* -============ -R_InitVBOs -============ -*/ -void R_InitVBOs(void) -{ - int dataSize; - int offset; - - ri.Printf(PRINT_ALL, "------- R_InitVBOs -------\n"); - - tr.numVBOs = 0; - tr.numIBOs = 0; - - dataSize = sizeof(tess.xyz[0]); - dataSize += sizeof(tess.normal[0]); -#ifdef USE_VERT_TANGENT_SPACE - dataSize += sizeof(tess.tangent[0]); - dataSize += sizeof(tess.bitangent[0]); -#endif - dataSize += sizeof(tess.vertexColors[0]); - dataSize += sizeof(tess.texCoords[0][0]) * 2; - dataSize += sizeof(tess.lightdir[0]); - dataSize *= SHADER_MAX_VERTEXES; - - tess.vbo = R_CreateVBO("tessVertexArray_VBO", NULL, dataSize, VBO_USAGE_DYNAMIC); - - offset = 0; - - tess.vbo->ofs_xyz = offset; offset += sizeof(tess.xyz[0]) * SHADER_MAX_VERTEXES; - tess.vbo->ofs_normal = offset; offset += sizeof(tess.normal[0]) * SHADER_MAX_VERTEXES; -#ifdef USE_VERT_TANGENT_SPACE - tess.vbo->ofs_tangent = offset; offset += sizeof(tess.tangent[0]) * SHADER_MAX_VERTEXES; - tess.vbo->ofs_bitangent = offset; offset += sizeof(tess.bitangent[0]) * SHADER_MAX_VERTEXES; -#endif - // these next two are actually interleaved - tess.vbo->ofs_st = offset; - tess.vbo->ofs_lightmap = offset + sizeof(tess.texCoords[0][0]); - offset += sizeof(tess.texCoords[0][0]) * 2 * SHADER_MAX_VERTEXES; - - tess.vbo->ofs_vertexcolor = offset; offset += sizeof(tess.vertexColors[0]) * SHADER_MAX_VERTEXES; - tess.vbo->ofs_lightdir = offset; - - tess.vbo->stride_xyz = sizeof(tess.xyz[0]); - tess.vbo->stride_normal = sizeof(tess.normal[0]); -#ifdef USE_VERT_TANGENT_SPACE - tess.vbo->stride_tangent = sizeof(tess.tangent[0]); - tess.vbo->stride_bitangent = sizeof(tess.bitangent[0]); -#endif - tess.vbo->stride_vertexcolor = sizeof(tess.vertexColors[0]); - tess.vbo->stride_st = sizeof(tess.texCoords[0][0]) * 2; - tess.vbo->stride_lightmap = sizeof(tess.texCoords[0][0]) * 2; - tess.vbo->stride_lightdir = sizeof(tess.lightdir[0]); - - dataSize = sizeof(tess.indexes[0]) * SHADER_MAX_INDEXES; - - tess.ibo = R_CreateIBO("tessVertexArray_IBO", NULL, dataSize, VBO_USAGE_DYNAMIC); - - R_BindNullVBO(); - R_BindNullIBO(); - - GL_CheckErrors(); -} - -/* -============ -R_ShutdownVBOs -============ -*/ -void R_ShutdownVBOs(void) -{ - int i; - VBO_t *vbo; - IBO_t *ibo; - - ri.Printf(PRINT_ALL, "------- R_ShutdownVBOs -------\n"); - - R_BindNullVBO(); - R_BindNullIBO(); - - - for(i = 0; i < tr.numVBOs; i++) - { - vbo = tr.vbos[i]; - - if(vbo->vertexesVBO) - { - qglDeleteBuffersARB(1, &vbo->vertexesVBO); - } - - //ri.Free(vbo); - } - - for(i = 0; i < tr.numIBOs; i++) - { - ibo = tr.ibos[i]; - - if(ibo->indexesVBO) - { - qglDeleteBuffersARB(1, &ibo->indexesVBO); - } - - //ri.Free(ibo); - } - - tr.numVBOs = 0; - tr.numIBOs = 0; -} - -/* -============ -R_VBOList_f -============ -*/ -void R_VBOList_f(void) -{ - int i; - VBO_t *vbo; - IBO_t *ibo; - int vertexesSize = 0; - int indexesSize = 0; - - ri.Printf(PRINT_ALL, " size name\n"); - ri.Printf(PRINT_ALL, "----------------------------------------------------------\n"); - - for(i = 0; i < tr.numVBOs; i++) - { - vbo = tr.vbos[i]; - - ri.Printf(PRINT_ALL, "%d.%02d MB %s\n", vbo->vertexesSize / (1024 * 1024), - (vbo->vertexesSize % (1024 * 1024)) * 100 / (1024 * 1024), vbo->name); - - vertexesSize += vbo->vertexesSize; - } - - for(i = 0; i < tr.numIBOs; i++) - { - ibo = tr.ibos[i]; - - ri.Printf(PRINT_ALL, "%d.%02d MB %s\n", ibo->indexesSize / (1024 * 1024), - (ibo->indexesSize % (1024 * 1024)) * 100 / (1024 * 1024), ibo->name); - - indexesSize += ibo->indexesSize; - } - - ri.Printf(PRINT_ALL, " %i total VBOs\n", tr.numVBOs); - ri.Printf(PRINT_ALL, " %d.%02d MB total vertices memory\n", vertexesSize / (1024 * 1024), - (vertexesSize % (1024 * 1024)) * 100 / (1024 * 1024)); - - ri.Printf(PRINT_ALL, " %i total IBOs\n", tr.numIBOs); - ri.Printf(PRINT_ALL, " %d.%02d MB total triangle indices memory\n", indexesSize / (1024 * 1024), - (indexesSize % (1024 * 1024)) * 100 / (1024 * 1024)); -} - - -/* -============== -RB_UpdateVBOs - -Adapted from Tess_UpdateVBOs from xreal - -Tr3B: update the default VBO to replace the client side vertex arrays -============== -*/ -void RB_UpdateVBOs(unsigned int attribBits) -{ - GLimp_LogComment("--- RB_UpdateVBOs ---\n"); - - backEnd.pc.c_dynamicVboDraws++; - - // update the default VBO - if(tess.numVertexes > 0 && tess.numVertexes <= SHADER_MAX_VERTEXES) - { - R_BindVBO(tess.vbo); - - if(attribBits & ATTR_BITS) - { - if(attribBits & ATTR_POSITION) - { - //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_xyz, tess.numVertexes * sizeof(tess.xyz[0])); - qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_xyz, tess.numVertexes * sizeof(tess.xyz[0]), tess.xyz); - } - - if(attribBits & ATTR_TEXCOORD || attribBits & ATTR_LIGHTCOORD) - { - // these are interleaved, so we update both if either need it - //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_st, tess.numVertexes * sizeof(tess.texCoords[0][0]) * 2); - qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_st, tess.numVertexes * sizeof(tess.texCoords[0][0]) * 2, tess.texCoords); - } - - if(attribBits & ATTR_NORMAL) - { - //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_normal, tess.numVertexes * sizeof(tess.normal[0])); - qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_normal, tess.numVertexes * sizeof(tess.normal[0]), tess.normal); - } - -#ifdef USE_VERT_TANGENT_SPACE - if(attribBits & ATTR_TANGENT) - { - //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_tangent, tess.numVertexes * sizeof(tess.tangent[0])); - qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_tangent, tess.numVertexes * sizeof(tess.tangent[0]), tess.tangent); - } - - if(attribBits & ATTR_BITANGENT) - { - //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_bitangent, tess.numVertexes * sizeof(tess.bitangent[0])); - qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_bitangent, tess.numVertexes * sizeof(tess.bitangent[0]), tess.bitangent); - } -#endif - - if(attribBits & ATTR_COLOR) - { - //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_vertexcolor, tess.numVertexes * sizeof(tess.vertexColors[0])); - qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_vertexcolor, tess.numVertexes * sizeof(tess.vertexColors[0]), tess.vertexColors); - } - - if(attribBits & ATTR_LIGHTDIRECTION) - { - //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_lightdir, tess.numVertexes * sizeof(tess.lightdir[0])); - qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_lightdir, tess.numVertexes * sizeof(tess.lightdir[0]), tess.lightdir); - } - } - else - { - qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_xyz, tess.numVertexes * sizeof(tess.xyz[0]), tess.xyz); - qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_st, tess.numVertexes * sizeof(tess.texCoords[0][0]) * 2, tess.texCoords); - qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_normal, tess.numVertexes * sizeof(tess.normal[0]), tess.normal); -#ifdef USE_VERT_TANGENT_SPACE - qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_tangent, tess.numVertexes * sizeof(tess.tangent[0]), tess.tangent); - qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_bitangent, tess.numVertexes * sizeof(tess.bitangent[0]), tess.bitangent); -#endif - qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_vertexcolor, tess.numVertexes * sizeof(tess.vertexColors[0]), tess.vertexColors); - qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_lightdir, tess.numVertexes * sizeof(tess.lightdir[0]), tess.lightdir); - } - - } - - // update the default IBO - if(tess.numIndexes > 0 && tess.numIndexes <= SHADER_MAX_INDEXES) - { - R_BindIBO(tess.ibo); - - qglBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, tess.numIndexes * sizeof(tess.indexes[0]), tess.indexes); - } -} diff --git a/src/rend2/tr_world.c b/src/rend2/tr_world.c deleted file mode 100644 index 950ee6a4..00000000 --- a/src/rend2/tr_world.c +++ /dev/null @@ -1,850 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#include "tr_local.h" - - - -/* -================ -R_CullSurface - -Tries to cull surfaces before they are lighted or -added to the sorting list. -================ -*/ -static qboolean R_CullSurface( msurface_t *surf ) { - if ( r_nocull->integer || surf->cullinfo.type == CULLINFO_NONE) { - return qfalse; - } - - if (surf->cullinfo.type & CULLINFO_PLANE) - { - // Only true for SF_FACE, so treat like its own function - float d; - cullType_t ct; - - if ( !r_facePlaneCull->integer ) { - return qfalse; - } - - ct = surf->shader->cullType; - - if (ct == CT_TWO_SIDED) - { - return qfalse; - } - - // don't cull for depth shadow - /* - if ( tr.viewParms.flags & VPF_DEPTHSHADOW ) - { - return qfalse; - } - */ - - // shadowmaps draw back surfaces - if ( tr.viewParms.flags & (VPF_SHADOWMAP | VPF_DEPTHSHADOW) ) - { - if (ct == CT_FRONT_SIDED) - { - ct = CT_BACK_SIDED; - } - else - { - ct = CT_FRONT_SIDED; - } - } - - // do proper cull for orthographic projection - if (tr.viewParms.flags & VPF_ORTHOGRAPHIC) { - d = DotProduct(tr.viewParms.or.axis[0], surf->cullinfo.plane.normal); - if ( ct == CT_FRONT_SIDED ) { - if (d > 0) - return qtrue; - } else { - if (d < 0) - return qtrue; - } - return qfalse; - } - - d = DotProduct (tr.or.viewOrigin, surf->cullinfo.plane.normal); - - // don't cull exactly on the plane, because there are levels of rounding - // through the BSP, ICD, and hardware that may cause pixel gaps if an - // epsilon isn't allowed here - if ( ct == CT_FRONT_SIDED ) { - if ( d < surf->cullinfo.plane.dist - 8 ) { - return qtrue; - } - } else { - if ( d > surf->cullinfo.plane.dist + 8 ) { - return qtrue; - } - } - - return qfalse; - } - - if (surf->cullinfo.type & CULLINFO_SPHERE) - { - int sphereCull; - - if ( tr.currentEntityNum != REFENTITYNUM_WORLD ) { - sphereCull = R_CullLocalPointAndRadius( surf->cullinfo.localOrigin, surf->cullinfo.radius ); - } else { - sphereCull = R_CullPointAndRadius( surf->cullinfo.localOrigin, surf->cullinfo.radius ); - } - - if ( sphereCull == CULL_OUT ) - { - return qtrue; - } - } - - if (surf->cullinfo.type & CULLINFO_BOX) - { - int boxCull; - - if ( tr.currentEntityNum != REFENTITYNUM_WORLD ) { - boxCull = R_CullLocalBox( surf->cullinfo.bounds ); - } else { - boxCull = R_CullBox( surf->cullinfo.bounds ); - } - - if ( boxCull == CULL_OUT ) - { - return qtrue; - } - } - - return qfalse; -} - - -/* -==================== -R_DlightSurface - -The given surface is going to be drawn, and it touches a leaf -that is touched by one or more dlights, so try to throw out -more dlights if possible. -==================== -*/ -static int R_DlightSurface( msurface_t *surf, int dlightBits ) { - float d; - int i; - dlight_t *dl; - - if ( surf->cullinfo.type & CULLINFO_PLANE ) - { - int i; - for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { - if ( ! ( dlightBits & ( 1 << i ) ) ) { - continue; - } - dl = &tr.refdef.dlights[i]; - d = DotProduct( dl->origin, surf->cullinfo.plane.normal ) - surf->cullinfo.plane.dist; - if ( d < -dl->radius || d > dl->radius ) { - // dlight doesn't reach the plane - dlightBits &= ~( 1 << i ); - } - } - } - - if ( surf->cullinfo.type & CULLINFO_BOX ) - { - for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { - if ( ! ( dlightBits & ( 1 << i ) ) ) { - continue; - } - dl = &tr.refdef.dlights[i]; - if ( dl->origin[0] - dl->radius > surf->cullinfo.bounds[1][0] - || dl->origin[0] + dl->radius < surf->cullinfo.bounds[0][0] - || dl->origin[1] - dl->radius > surf->cullinfo.bounds[1][1] - || dl->origin[1] + dl->radius < surf->cullinfo.bounds[0][1] - || dl->origin[2] - dl->radius > surf->cullinfo.bounds[1][2] - || dl->origin[2] + dl->radius < surf->cullinfo.bounds[0][2] ) { - // dlight doesn't reach the bounds - dlightBits &= ~( 1 << i ); - } - } - } - - if ( surf->cullinfo.type & CULLINFO_SPHERE ) - { - for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { - if ( ! ( dlightBits & ( 1 << i ) ) ) { - continue; - } - dl = &tr.refdef.dlights[i]; - if (!SpheresIntersect(dl->origin, dl->radius, surf->cullinfo.localOrigin, surf->cullinfo.radius)) - { - // dlight doesn't reach the bounds - dlightBits &= ~( 1 << i ); - } - } - } - - if ( *surf->data == SF_FACE ) { - ((srfSurfaceFace_t *)surf->data)->dlightBits = dlightBits; - } else if ( *surf->data == SF_GRID ) { - ((srfGridMesh_t *)surf->data)->dlightBits = dlightBits; - } else if ( *surf->data == SF_TRIANGLES ) { - ((srfTriangles_t *)surf->data)->dlightBits = dlightBits; - } else if ( *surf->data == SF_VBO_MESH ) { - ((srfVBOMesh_t *)surf->data)->dlightBits = dlightBits; - } else { - dlightBits = 0; - } - - if ( dlightBits ) { - tr.pc.c_dlightSurfaces++; - } - - return dlightBits; -} - -/* -==================== -R_PshadowSurface - -Just like R_DlightSurface, cull any we can -==================== -*/ -static int R_PshadowSurface( msurface_t *surf, int pshadowBits ) { - float d; - int i; - pshadow_t *ps; - - if ( surf->cullinfo.type & CULLINFO_PLANE ) - { - int i; - for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) { - if ( ! ( pshadowBits & ( 1 << i ) ) ) { - continue; - } - ps = &tr.refdef.pshadows[i]; - d = DotProduct( ps->lightOrigin, surf->cullinfo.plane.normal ) - surf->cullinfo.plane.dist; - if ( d < -ps->lightRadius || d > ps->lightRadius ) { - // pshadow doesn't reach the plane - pshadowBits &= ~( 1 << i ); - } - } - } - - if ( surf->cullinfo.type & CULLINFO_BOX ) - { - for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) { - if ( ! ( pshadowBits & ( 1 << i ) ) ) { - continue; - } - ps = &tr.refdef.pshadows[i]; - if ( ps->lightOrigin[0] - ps->lightRadius > surf->cullinfo.bounds[1][0] - || ps->lightOrigin[0] + ps->lightRadius < surf->cullinfo.bounds[0][0] - || ps->lightOrigin[1] - ps->lightRadius > surf->cullinfo.bounds[1][1] - || ps->lightOrigin[1] + ps->lightRadius < surf->cullinfo.bounds[0][1] - || ps->lightOrigin[2] - ps->lightRadius > surf->cullinfo.bounds[1][2] - || ps->lightOrigin[2] + ps->lightRadius < surf->cullinfo.bounds[0][2] - || BoxOnPlaneSide(surf->cullinfo.bounds[0], surf->cullinfo.bounds[1], &ps->cullPlane) == 2 ) { - // pshadow doesn't reach the bounds - pshadowBits &= ~( 1 << i ); - } - } - } - - if ( surf->cullinfo.type & CULLINFO_SPHERE ) - { - for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) { - if ( ! ( pshadowBits & ( 1 << i ) ) ) { - continue; - } - ps = &tr.refdef.pshadows[i]; - if (!SpheresIntersect(ps->viewOrigin, ps->viewRadius, surf->cullinfo.localOrigin, surf->cullinfo.radius) - || DotProduct( surf->cullinfo.localOrigin, ps->cullPlane.normal ) - ps->cullPlane.dist < -surf->cullinfo.radius) - { - // pshadow doesn't reach the bounds - pshadowBits &= ~( 1 << i ); - } - } - } - - if ( *surf->data == SF_FACE ) { - ((srfSurfaceFace_t *)surf->data)->pshadowBits = pshadowBits; - } else if ( *surf->data == SF_GRID ) { - ((srfGridMesh_t *)surf->data)->pshadowBits = pshadowBits; - } else if ( *surf->data == SF_TRIANGLES ) { - ((srfTriangles_t *)surf->data)->pshadowBits = pshadowBits; - } else if ( *surf->data == SF_VBO_MESH ) { - ((srfVBOMesh_t *)surf->data)->pshadowBits = pshadowBits; - } else { - pshadowBits = 0; - } - - if ( pshadowBits ) { - //tr.pc.c_dlightSurfaces++; - } - - return pshadowBits; -} - - -/* -====================== -R_AddWorldSurface -====================== -*/ -static void R_AddWorldSurface( msurface_t *surf, int dlightBits, int pshadowBits ) { - // FIXME: bmodel fog? - - // try to cull before dlighting or adding - if ( R_CullSurface( surf ) ) { - return; - } - - // check for dlighting - if ( dlightBits ) { - dlightBits = R_DlightSurface( surf, dlightBits ); - dlightBits = ( dlightBits != 0 ); - } - - // check for pshadows - /*if ( pshadowBits ) */{ - pshadowBits = R_PshadowSurface( surf, pshadowBits); - pshadowBits = ( pshadowBits != 0 ); - } - - R_AddDrawSurf( surf->data, surf->shader, surf->fogIndex, dlightBits, pshadowBits ); -} - -/* -============================================================= - - BRUSH MODELS - -============================================================= -*/ - -/* -================= -R_AddBrushModelSurfaces -================= -*/ -void R_AddBrushModelSurfaces ( trRefEntity_t *ent ) { - bmodel_t *bmodel; - int clip; - model_t *pModel; - int i; - - pModel = R_GetModelByHandle( ent->e.hModel ); - - bmodel = pModel->bmodel; - - clip = R_CullLocalBox( bmodel->bounds ); - if ( clip == CULL_OUT ) { - return; - } - - R_SetupEntityLighting( &tr.refdef, ent ); - R_DlightBmodel( bmodel ); - - for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) { - int surf = bmodel->firstSurface + i; - - if (tr.world->surfacesViewCount[surf] != tr.viewCount) - { - tr.world->surfacesViewCount[surf] = tr.viewCount; - R_AddWorldSurface( tr.world->surfaces + surf, tr.currentEntity->needDlights, 0 ); - } - } -} - - -/* -============================================================= - - WORLD MODEL - -============================================================= -*/ - - -/* -================ -R_RecursiveWorldNode -================ -*/ -static void R_RecursiveWorldNode( mnode_t *node, int planeBits, int dlightBits, int pshadowBits ) { - - do { - int newDlights[2]; - unsigned int newPShadows[2]; - - // if the node wasn't marked as potentially visible, exit - // pvs is skipped for depth shadows - if (!(tr.viewParms.flags & VPF_DEPTHSHADOW) && node->visCounts[tr.visIndex] != tr.visCounts[tr.visIndex]) { - return; - } - - // if the bounding volume is outside the frustum, nothing - // inside can be visible OPTIMIZE: don't do this all the way to leafs? - - if ( !r_nocull->integer ) { - int r; - - if ( planeBits & 1 ) { - r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[0]); - if (r == 2) { - return; // culled - } - if ( r == 1 ) { - planeBits &= ~1; // all descendants will also be in front - } - } - - if ( planeBits & 2 ) { - r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[1]); - if (r == 2) { - return; // culled - } - if ( r == 1 ) { - planeBits &= ~2; // all descendants will also be in front - } - } - - if ( planeBits & 4 ) { - r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[2]); - if (r == 2) { - return; // culled - } - if ( r == 1 ) { - planeBits &= ~4; // all descendants will also be in front - } - } - - if ( planeBits & 8 ) { - r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[3]); - if (r == 2) { - return; // culled - } - if ( r == 1 ) { - planeBits &= ~8; // all descendants will also be in front - } - } - - if ( planeBits & 16 ) { - r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[4]); - if (r == 2) { - return; // culled - } - if ( r == 1 ) { - planeBits &= ~16; // all descendants will also be in front - } - } - } - - if ( node->contents != -1 ) { - break; - } - - // node is just a decision point, so go down both sides - // since we don't care about sort orders, just go positive to negative - - // determine which dlights are needed - newDlights[0] = 0; - newDlights[1] = 0; - if ( dlightBits ) { - int i; - - for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { - dlight_t *dl; - float dist; - - if ( dlightBits & ( 1 << i ) ) { - dl = &tr.refdef.dlights[i]; - dist = DotProduct( dl->origin, node->plane->normal ) - node->plane->dist; - - if ( dist > -dl->radius ) { - newDlights[0] |= ( 1 << i ); - } - if ( dist < dl->radius ) { - newDlights[1] |= ( 1 << i ); - } - } - } - } - - newPShadows[0] = 0; - newPShadows[1] = 0; - if ( pshadowBits ) { - int i; - - for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) { - pshadow_t *shadow; - float dist; - - if ( pshadowBits & ( 1 << i ) ) { - shadow = &tr.refdef.pshadows[i]; - dist = DotProduct( shadow->lightOrigin, node->plane->normal ) - node->plane->dist; - - if ( dist > -shadow->lightRadius ) { - newPShadows[0] |= ( 1 << i ); - } - if ( dist < shadow->lightRadius ) { - newPShadows[1] |= ( 1 << i ); - } - } - } - } - - // recurse down the children, front side first - R_RecursiveWorldNode (node->children[0], planeBits, newDlights[0], newPShadows[0] ); - - // tail recurse - node = node->children[1]; - dlightBits = newDlights[1]; - pshadowBits = newPShadows[1]; - } while ( 1 ); - - { - // leaf node, so add mark surfaces - int c; - int surf, *view; - - tr.pc.c_leafs++; - - // add to z buffer bounds - if ( node->mins[0] < tr.viewParms.visBounds[0][0] ) { - tr.viewParms.visBounds[0][0] = node->mins[0]; - } - if ( node->mins[1] < tr.viewParms.visBounds[0][1] ) { - tr.viewParms.visBounds[0][1] = node->mins[1]; - } - if ( node->mins[2] < tr.viewParms.visBounds[0][2] ) { - tr.viewParms.visBounds[0][2] = node->mins[2]; - } - - if ( node->maxs[0] > tr.viewParms.visBounds[1][0] ) { - tr.viewParms.visBounds[1][0] = node->maxs[0]; - } - if ( node->maxs[1] > tr.viewParms.visBounds[1][1] ) { - tr.viewParms.visBounds[1][1] = node->maxs[1]; - } - if ( node->maxs[2] > tr.viewParms.visBounds[1][2] ) { - tr.viewParms.visBounds[1][2] = node->maxs[2]; - } - - // add merged and unmerged surfaces - if (tr.world->viewSurfaces) - view = tr.world->viewSurfaces + node->firstmarksurface; - else - view = tr.world->marksurfaces + node->firstmarksurface; - - c = node->nummarksurfaces; - while (c--) { - // just mark it as visible, so we don't jump out of the cache derefencing the surface - surf = *view; - if (surf < 0) - { - if (tr.world->mergedSurfacesViewCount[-surf - 1] != tr.viewCount) - { - tr.world->mergedSurfacesViewCount[-surf - 1] = tr.viewCount; - tr.world->mergedSurfacesDlightBits[-surf - 1] = dlightBits; - tr.world->mergedSurfacesPshadowBits[-surf - 1] = pshadowBits; - } - else - { - tr.world->mergedSurfacesDlightBits[-surf - 1] |= dlightBits; - tr.world->mergedSurfacesPshadowBits[-surf - 1] |= pshadowBits; - } - } - else - { - if (tr.world->surfacesViewCount[surf] != tr.viewCount) - { - tr.world->surfacesViewCount[surf] = tr.viewCount; - tr.world->surfacesDlightBits[surf] = dlightBits; - tr.world->surfacesPshadowBits[surf] = pshadowBits; - } - else - { - tr.world->surfacesDlightBits[surf] |= dlightBits; - tr.world->surfacesPshadowBits[surf] |= pshadowBits; - } - } - view++; - } - } - -} - - -/* -=============== -R_PointInLeaf -=============== -*/ -static mnode_t *R_PointInLeaf( const vec3_t p ) { - mnode_t *node; - float d; - cplane_t *plane; - - if ( !tr.world ) { - ri.Error (ERR_DROP, "R_PointInLeaf: bad model"); - } - - node = tr.world->nodes; - while( 1 ) { - if (node->contents != -1) { - break; - } - plane = node->plane; - d = DotProduct (p,plane->normal) - plane->dist; - if (d > 0) { - node = node->children[0]; - } else { - node = node->children[1]; - } - } - - return node; -} - -/* -============== -R_ClusterPVS -============== -*/ -static const byte *R_ClusterPVS (int cluster) { - if (!tr.world || !tr.world->vis || cluster < 0 || cluster >= tr.world->numClusters ) { - return tr.world->novis; - } - - return tr.world->vis + cluster * tr.world->clusterBytes; -} - -/* -================= -R_inPVS -================= -*/ -qboolean R_inPVS( const vec3_t p1, const vec3_t p2 ) { - mnode_t *leaf; - byte *vis; - - leaf = R_PointInLeaf( p1 ); - vis = ri.CM_ClusterPVS( leaf->cluster ); // why not R_ClusterPVS ?? - leaf = R_PointInLeaf( p2 ); - - if ( !(vis[leaf->cluster>>3] & (1<<(leaf->cluster&7))) ) { - return qfalse; - } - return qtrue; -} - -/* -=============== -R_MarkLeaves - -Mark the leaves and nodes that are in the PVS for the current -cluster -=============== -*/ -static void R_MarkLeaves (void) { - const byte *vis; - mnode_t *leaf, *parent; - int i; - int cluster; - - // lockpvs lets designers walk around to determine the - // extent of the current pvs - if ( r_lockpvs->integer ) { - return; - } - - // current viewcluster - leaf = R_PointInLeaf( tr.viewParms.pvsOrigin ); - cluster = leaf->cluster; - - // if the cluster is the same and the area visibility matrix - // hasn't changed, we don't need to mark everything again - - for(i = 0; i < MAX_VISCOUNTS; i++) - { - if(tr.visClusters[i] == cluster) - { - //tr.visIndex = i; - break; - } - } - - // if r_showcluster was just turned on, remark everything - if(i != MAX_VISCOUNTS && !tr.refdef.areamaskModified && !r_showcluster->modified)// && !r_dynamicBspOcclusionCulling->modified) - { - if(tr.visClusters[i] != tr.visClusters[tr.visIndex] && r_showcluster->integer) - { - ri.Printf(PRINT_ALL, "found cluster:%i area:%i index:%i\n", cluster, leaf->area, i); - } - tr.visIndex = i; - return; - } - - // if the areamask was modified, invalidate all visclusters - // this caused doors to open into undrawn areas - if (tr.refdef.areamaskModified) - { - memset(tr.visClusters, -2, sizeof(tr.visClusters)); - } - - tr.visIndex = (tr.visIndex + 1) % MAX_VISCOUNTS; - tr.visCounts[tr.visIndex]++; - tr.visClusters[tr.visIndex] = cluster; - - if ( r_showcluster->modified || r_showcluster->integer ) { - r_showcluster->modified = qfalse; - if ( r_showcluster->integer ) { - ri.Printf( PRINT_ALL, "cluster:%i area:%i\n", cluster, leaf->area ); - } - } - - // set all nodes to visible if there is no vis - // this caused some levels to simply not render - if (r_novis->integer || !tr.world->vis || tr.visClusters[tr.visIndex] == -1) { - for (i=0 ; inumnodes ; i++) { - if (tr.world->nodes[i].contents != CONTENTS_SOLID) { - tr.world->nodes[i].visCounts[tr.visIndex] = tr.visCounts[tr.visIndex]; - } - } - return; - } - - vis = R_ClusterPVS(tr.visClusters[tr.visIndex]); - - for (i=0,leaf=tr.world->nodes ; inumnodes ; i++, leaf++) { - cluster = leaf->cluster; - if ( cluster < 0 || cluster >= tr.world->numClusters ) { - continue; - } - - // check general pvs - if ( !(vis[cluster>>3] & (1<<(cluster&7))) ) { - continue; - } - - // check for door connection - if ( (tr.refdef.areamask[leaf->area>>3] & (1<<(leaf->area&7)) ) ) { - continue; // not visible - } - - parent = leaf; - do { - if(parent->visCounts[tr.visIndex] == tr.visCounts[tr.visIndex]) - break; - parent->visCounts[tr.visIndex] = tr.visCounts[tr.visIndex]; - parent = parent->parent; - } while (parent); - } -} - - -/* -============= -R_AddWorldSurfaces -============= -*/ -void R_AddWorldSurfaces (void) { - int planeBits, dlightBits, pshadowBits; - - if ( !r_drawworld->integer ) { - return; - } - - if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { - return; - } - - tr.currentEntityNum = REFENTITYNUM_WORLD; - tr.shiftedEntityNum = tr.currentEntityNum << QSORT_REFENTITYNUM_SHIFT; - - // determine which leaves are in the PVS / areamask - if (!(tr.viewParms.flags & VPF_DEPTHSHADOW)) - R_MarkLeaves (); - - // clear out the visible min/max - ClearBounds( tr.viewParms.visBounds[0], tr.viewParms.visBounds[1] ); - - // perform frustum culling and flag all the potentially visible surfaces - if ( tr.refdef.num_dlights > 32 ) { - tr.refdef.num_dlights = 32 ; - } - - if ( tr.refdef.num_pshadows > 32 ) { - tr.refdef.num_pshadows = 32 ; - } - - planeBits = (tr.viewParms.flags & VPF_FARPLANEFRUSTUM) ? 31 : 15; - - if ( tr.viewParms.flags & VPF_DEPTHSHADOW ) - { - dlightBits = 0; - pshadowBits = 0; - } - else if ( !(tr.viewParms.flags & VPF_SHADOWMAP) ) - { - dlightBits = ( 1 << tr.refdef.num_dlights ) - 1; - pshadowBits = ( 1 << tr.refdef.num_pshadows ) - 1; - } - else - { - dlightBits = ( 1 << tr.refdef.num_dlights ) - 1; - pshadowBits = 0; - } - - R_RecursiveWorldNode( tr.world->nodes, planeBits, dlightBits, pshadowBits); - - // now add all the potentially visible surfaces - // also mask invisible dlights for next frame - { - int i; - - tr.refdef.dlightMask = 0; - - for (i = 0; i < tr.world->numWorldSurfaces; i++) - { - if (tr.world->surfacesViewCount[i] != tr.viewCount) - continue; - - R_AddWorldSurface( tr.world->surfaces + i, tr.world->surfacesDlightBits[i], tr.world->surfacesPshadowBits[i] ); - tr.refdef.dlightMask |= tr.world->surfacesDlightBits[i]; - } - for (i = 0; i < tr.world->numMergedSurfaces; i++) - { - if (tr.world->mergedSurfacesViewCount[i] != tr.viewCount) - continue; - - R_AddWorldSurface( tr.world->mergedSurfaces + i, tr.world->mergedSurfacesDlightBits[i], tr.world->mergedSurfacesPshadowBits[i] ); - tr.refdef.dlightMask |= tr.world->mergedSurfacesDlightBits[i]; - } - - tr.refdef.dlightMask = ~tr.refdef.dlightMask; - } -} diff --git a/src/renderer/iqm.h b/src/renderer/iqm.h deleted file mode 100644 index ab2247ac..00000000 --- a/src/renderer/iqm.h +++ /dev/null @@ -1,129 +0,0 @@ -/* -=========================================================================== -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#ifndef __IQM_H__ -#define __IQM_H__ - -#define IQM_MAGIC "INTERQUAKEMODEL" -#define IQM_VERSION 2 - -#define IQM_MAX_JOINTS 128 - -typedef struct iqmheader -{ - char magic[16]; - unsigned int version; - unsigned int filesize; - unsigned int flags; - unsigned int num_text, ofs_text; - unsigned int num_meshes, ofs_meshes; - unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays; - unsigned int num_triangles, ofs_triangles, ofs_adjacency; - unsigned int num_joints, ofs_joints; - unsigned int num_poses, ofs_poses; - unsigned int num_anims, ofs_anims; - unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds; - unsigned int num_comment, ofs_comment; - unsigned int num_extensions, ofs_extensions; -} iqmHeader_t; - -typedef struct iqmmesh -{ - unsigned int name; - unsigned int material; - unsigned int first_vertex, num_vertexes; - unsigned int first_triangle, num_triangles; -} iqmMesh_t; - -enum -{ - IQM_POSITION = 0, - IQM_TEXCOORD = 1, - IQM_NORMAL = 2, - IQM_TANGENT = 3, - IQM_BLENDINDEXES = 4, - IQM_BLENDWEIGHTS = 5, - IQM_COLOR = 6, - IQM_CUSTOM = 0x10 -}; - -enum -{ - IQM_BYTE = 0, - IQM_UBYTE = 1, - IQM_SHORT = 2, - IQM_USHORT = 3, - IQM_INT = 4, - IQM_UINT = 5, - IQM_HALF = 6, - IQM_FLOAT = 7, - IQM_DOUBLE = 8, -}; - -typedef struct iqmtriangle -{ - unsigned int vertex[3]; -} iqmTriangle_t; - -typedef struct iqmjoint -{ - unsigned int name; - int parent; - float translate[3], rotate[4], scale[3]; -} iqmJoint_t; - -typedef struct iqmpose -{ - int parent; - unsigned int mask; - float channeloffset[10]; - float channelscale[10]; -} iqmPose_t; - -typedef struct iqmanim -{ - unsigned int name; - unsigned int first_frame, num_frames; - float framerate; - unsigned int flags; -} iqmAnim_t; - -enum -{ - IQM_LOOP = 1<<0 -}; - -typedef struct iqmvertexarray -{ - unsigned int type; - unsigned int flags; - unsigned int format; - unsigned int size; - unsigned int offset; -} iqmVertexArray_t; - -typedef struct iqmbounds -{ - float bbmin[3], bbmax[3]; - float xyradius, radius; -} iqmBounds_t; - -#endif - diff --git a/src/renderer/qgl.h b/src/renderer/qgl.h deleted file mode 100644 index 2dbe27db..00000000 --- a/src/renderer/qgl.h +++ /dev/null @@ -1,381 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -/* -** QGL.H -*/ - -#ifndef __QGL_H__ -#define __QGL_H__ - -#ifdef USE_LOCAL_HEADERS -# include "SDL_opengl.h" -#else -# include -#endif - -extern void (APIENTRYP qglActiveTextureARB) (GLenum texture); -extern void (APIENTRYP qglClientActiveTextureARB) (GLenum texture); -extern void (APIENTRYP qglMultiTexCoord2fARB) (GLenum target, GLfloat s, GLfloat t); - -extern void (APIENTRYP qglLockArraysEXT) (GLint first, GLsizei count); -extern void (APIENTRYP qglUnlockArraysEXT) (void); - - -//=========================================================================== - -#define qglAccum glAccum -#define qglAlphaFunc glAlphaFunc -#define qglAreTexturesResident glAreTexturesResident -#define qglArrayElement glArrayElement -#define qglBegin glBegin -#define qglBindTexture glBindTexture -#define qglBitmap glBitmap -#define qglBlendFunc glBlendFunc -#define qglCallList glCallList -#define qglCallLists glCallLists -#define qglClear glClear -#define qglClearAccum glClearAccum -#define qglClearColor glClearColor -#define qglClearDepth glClearDepth -#define qglClearIndex glClearIndex -#define qglClearStencil glClearStencil -#define qglClipPlane glClipPlane -#define qglColor3b glColor3b -#define qglColor3bv glColor3bv -#define qglColor3d glColor3d -#define qglColor3dv glColor3dv -#define qglColor3f glColor3f -#define qglColor3fv glColor3fv -#define qglColor3i glColor3i -#define qglColor3iv glColor3iv -#define qglColor3s glColor3s -#define qglColor3sv glColor3sv -#define qglColor3ub glColor3ub -#define qglColor3ubv glColor3ubv -#define qglColor3ui glColor3ui -#define qglColor3uiv glColor3uiv -#define qglColor3us glColor3us -#define qglColor3usv glColor3usv -#define qglColor4b glColor4b -#define qglColor4bv glColor4bv -#define qglColor4d glColor4d -#define qglColor4dv glColor4dv -#define qglColor4f glColor4f -#define qglColor4fv glColor4fv -#define qglColor4i glColor4i -#define qglColor4iv glColor4iv -#define qglColor4s glColor4s -#define qglColor4sv glColor4sv -#define qglColor4ub glColor4ub -#define qglColor4ubv glColor4ubv -#define qglColor4ui glColor4ui -#define qglColor4uiv glColor4uiv -#define qglColor4us glColor4us -#define qglColor4usv glColor4usv -#define qglColorMask glColorMask -#define qglColorMaterial glColorMaterial -#define qglColorPointer glColorPointer -#define qglCopyPixels glCopyPixels -#define qglCopyTexImage1D glCopyTexImage1D -#define qglCopyTexImage2D glCopyTexImage2D -#define qglCopyTexSubImage1D glCopyTexSubImage1D -#define qglCopyTexSubImage2D glCopyTexSubImage2D -#define qglCullFace glCullFace -#define qglDeleteLists glDeleteLists -#define qglDeleteTextures glDeleteTextures -#define qglDepthFunc glDepthFunc -#define qglDepthMask glDepthMask -#define qglDepthRange glDepthRange -#define qglDisable glDisable -#define qglDisableClientState glDisableClientState -#define qglDrawArrays glDrawArrays -#define qglDrawBuffer glDrawBuffer -#define qglDrawElements glDrawElements -#define qglDrawPixels glDrawPixels -#define qglEdgeFlag glEdgeFlag -#define qglEdgeFlagPointer glEdgeFlagPointer -#define qglEdgeFlagv glEdgeFlagv -#define qglEnable glEnable -#define qglEnableClientState glEnableClientState -#define qglEnd glEnd -#define qglEndList glEndList -#define qglEvalCoord1d glEvalCoord1d -#define qglEvalCoord1dv glEvalCoord1dv -#define qglEvalCoord1f glEvalCoord1f -#define qglEvalCoord1fv glEvalCoord1fv -#define qglEvalCoord2d glEvalCoord2d -#define qglEvalCoord2dv glEvalCoord2dv -#define qglEvalCoord2f glEvalCoord2f -#define qglEvalCoord2fv glEvalCoord2fv -#define qglEvalMesh1 glEvalMesh1 -#define qglEvalMesh2 glEvalMesh2 -#define qglEvalPoint1 glEvalPoint1 -#define qglEvalPoint2 glEvalPoint2 -#define qglFeedbackBuffer glFeedbackBuffer -#define qglFinish glFinish -#define qglFlush glFlush -#define qglFogf glFogf -#define qglFogfv glFogfv -#define qglFogi glFogi -#define qglFogiv glFogiv -#define qglFrontFace glFrontFace -#define qglFrustum glFrustum -#define qglGenLists glGenLists -#define qglGenTextures glGenTextures -#define qglGetBooleanv glGetBooleanv -#define qglGetClipPlane glGetClipPlane -#define qglGetDoublev glGetDoublev -#define qglGetError glGetError -#define qglGetFloatv glGetFloatv -#define qglGetIntegerv glGetIntegerv -#define qglGetLightfv glGetLightfv -#define qglGetLightiv glGetLightiv -#define qglGetMapdv glGetMapdv -#define qglGetMapfv glGetMapfv -#define qglGetMapiv glGetMapiv -#define qglGetMaterialfv glGetMaterialfv -#define qglGetMaterialiv glGetMaterialiv -#define qglGetPixelMapfv glGetPixelMapfv -#define qglGetPixelMapuiv glGetPixelMapuiv -#define qglGetPixelMapusv glGetPixelMapusv -#define qglGetPointerv glGetPointerv -#define qglGetPolygonStipple glGetPolygonStipple -#define qglGetString glGetString -#define qglGetTexGendv glGetTexGendv -#define qglGetTexGenfv glGetTexGenfv -#define qglGetTexGeniv glGetTexGeniv -#define qglGetTexImage glGetTexImage -#define qglGetTexLevelParameterfv glGetTexLevelParameterfv -#define qglGetTexLevelParameteriv glGetTexLevelParameteriv -#define qglGetTexParameterfv glGetTexParameterfv -#define qglGetTexParameteriv glGetTexParameteriv -#define qglHint glHint -#define qglIndexMask glIndexMask -#define qglIndexPointer glIndexPointer -#define qglIndexd glIndexd -#define qglIndexdv glIndexdv -#define qglIndexf glIndexf -#define qglIndexfv glIndexfv -#define qglIndexi glIndexi -#define qglIndexiv glIndexiv -#define qglIndexs glIndexs -#define qglIndexsv glIndexsv -#define qglIndexub glIndexub -#define qglIndexubv glIndexubv -#define qglInitNames glInitNames -#define qglInterleavedArrays glInterleavedArrays -#define qglIsEnabled glIsEnabled -#define qglIsList glIsList -#define qglIsTexture glIsTexture -#define qglLightModelf glLightModelf -#define qglLightModelfv glLightModelfv -#define qglLightModeli glLightModeli -#define qglLightModeliv glLightModeliv -#define qglLightf glLightf -#define qglLightfv glLightfv -#define qglLighti glLighti -#define qglLightiv glLightiv -#define qglLineStipple glLineStipple -#define qglLineWidth glLineWidth -#define qglListBase glListBase -#define qglLoadIdentity glLoadIdentity -#define qglLoadMatrixd glLoadMatrixd -#define qglLoadMatrixf glLoadMatrixf -#define qglLoadName glLoadName -#define qglLogicOp glLogicOp -#define qglMap1d glMap1d -#define qglMap1f glMap1f -#define qglMap2d glMap2d -#define qglMap2f glMap2f -#define qglMapGrid1d glMapGrid1d -#define qglMapGrid1f glMapGrid1f -#define qglMapGrid2d glMapGrid2d -#define qglMapGrid2f glMapGrid2f -#define qglMaterialf glMaterialf -#define qglMaterialfv glMaterialfv -#define qglMateriali glMateriali -#define qglMaterialiv glMaterialiv -#define qglMatrixMode glMatrixMode -#define qglMultMatrixd glMultMatrixd -#define qglMultMatrixf glMultMatrixf -#define qglNewList glNewList -#define qglNormal3b glNormal3b -#define qglNormal3bv glNormal3bv -#define qglNormal3d glNormal3d -#define qglNormal3dv glNormal3dv -#define qglNormal3f glNormal3f -#define qglNormal3fv glNormal3fv -#define qglNormal3i glNormal3i -#define qglNormal3iv glNormal3iv -#define qglNormal3s glNormal3s -#define qglNormal3sv glNormal3sv -#define qglNormalPointer glNormalPointer -#define qglOrtho glOrtho -#define qglPassThrough glPassThrough -#define qglPixelMapfv glPixelMapfv -#define qglPixelMapuiv glPixelMapuiv -#define qglPixelMapusv glPixelMapusv -#define qglPixelStoref glPixelStoref -#define qglPixelStorei glPixelStorei -#define qglPixelTransferf glPixelTransferf -#define qglPixelTransferi glPixelTransferi -#define qglPixelZoom glPixelZoom -#define qglPointSize glPointSize -#define qglPolygonMode glPolygonMode -#define qglPolygonOffset glPolygonOffset -#define qglPolygonStipple glPolygonStipple -#define qglPopAttrib glPopAttrib -#define qglPopClientAttrib glPopClientAttrib -#define qglPopMatrix glPopMatrix -#define qglPopName glPopName -#define qglPrioritizeTextures glPrioritizeTextures -#define qglPushAttrib glPushAttrib -#define qglPushClientAttrib glPushClientAttrib -#define qglPushMatrix glPushMatrix -#define qglPushName glPushName -#define qglRasterPos2d glRasterPos2d -#define qglRasterPos2dv glRasterPos2dv -#define qglRasterPos2f glRasterPos2f -#define qglRasterPos2fv glRasterPos2fv -#define qglRasterPos2i glRasterPos2i -#define qglRasterPos2iv glRasterPos2iv -#define qglRasterPos2s glRasterPos2s -#define qglRasterPos2sv glRasterPos2sv -#define qglRasterPos3d glRasterPos3d -#define qglRasterPos3dv glRasterPos3dv -#define qglRasterPos3f glRasterPos3f -#define qglRasterPos3fv glRasterPos3fv -#define qglRasterPos3i glRasterPos3i -#define qglRasterPos3iv glRasterPos3iv -#define qglRasterPos3s glRasterPos3s -#define qglRasterPos3sv glRasterPos3sv -#define qglRasterPos4d glRasterPos4d -#define qglRasterPos4dv glRasterPos4dv -#define qglRasterPos4f glRasterPos4f -#define qglRasterPos4fv glRasterPos4fv -#define qglRasterPos4i glRasterPos4i -#define qglRasterPos4iv glRasterPos4iv -#define qglRasterPos4s glRasterPos4s -#define qglRasterPos4sv glRasterPos4sv -#define qglReadBuffer glReadBuffer -#define qglReadPixels glReadPixels -#define qglRectd glRectd -#define qglRectdv glRectdv -#define qglRectf glRectf -#define qglRectfv glRectfv -#define qglRecti glRecti -#define qglRectiv glRectiv -#define qglRects glRects -#define qglRectsv glRectsv -#define qglRenderMode glRenderMode -#define qglRotated glRotated -#define qglRotatef glRotatef -#define qglScaled glScaled -#define qglScalef glScalef -#define qglScissor glScissor -#define qglSelectBuffer glSelectBuffer -#define qglShadeModel glShadeModel -#define qglStencilFunc glStencilFunc -#define qglStencilMask glStencilMask -#define qglStencilOp glStencilOp -#define qglTexCoord1d glTexCoord1d -#define qglTexCoord1dv glTexCoord1dv -#define qglTexCoord1f glTexCoord1f -#define qglTexCoord1fv glTexCoord1fv -#define qglTexCoord1i glTexCoord1i -#define qglTexCoord1iv glTexCoord1iv -#define qglTexCoord1s glTexCoord1s -#define qglTexCoord1sv glTexCoord1sv -#define qglTexCoord2d glTexCoord2d -#define qglTexCoord2dv glTexCoord2dv -#define qglTexCoord2f glTexCoord2f -#define qglTexCoord2fv glTexCoord2fv -#define qglTexCoord2i glTexCoord2i -#define qglTexCoord2iv glTexCoord2iv -#define qglTexCoord2s glTexCoord2s -#define qglTexCoord2sv glTexCoord2sv -#define qglTexCoord3d glTexCoord3d -#define qglTexCoord3dv glTexCoord3dv -#define qglTexCoord3f glTexCoord3f -#define qglTexCoord3fv glTexCoord3fv -#define qglTexCoord3i glTexCoord3i -#define qglTexCoord3iv glTexCoord3iv -#define qglTexCoord3s glTexCoord3s -#define qglTexCoord3sv glTexCoord3sv -#define qglTexCoord4d glTexCoord4d -#define qglTexCoord4dv glTexCoord4dv -#define qglTexCoord4f glTexCoord4f -#define qglTexCoord4fv glTexCoord4fv -#define qglTexCoord4i glTexCoord4i -#define qglTexCoord4iv glTexCoord4iv -#define qglTexCoord4s glTexCoord4s -#define qglTexCoord4sv glTexCoord4sv -#define qglTexCoordPointer glTexCoordPointer -#define qglTexEnvf glTexEnvf -#define qglTexEnvfv glTexEnvfv -#define qglTexEnvi glTexEnvi -#define qglTexEnviv glTexEnviv -#define qglTexGend glTexGend -#define qglTexGendv glTexGendv -#define qglTexGenf glTexGenf -#define qglTexGenfv glTexGenfv -#define qglTexGeni glTexGeni -#define qglTexGeniv glTexGeniv -#define qglTexImage1D glTexImage1D -#define qglTexImage2D glTexImage2D -#define qglTexParameterf glTexParameterf -#define qglTexParameterfv glTexParameterfv -#define qglTexParameteri glTexParameteri -#define qglTexParameteriv glTexParameteriv -#define qglTexSubImage1D glTexSubImage1D -#define qglTexSubImage2D glTexSubImage2D -#define qglTranslated glTranslated -#define qglTranslatef glTranslatef -#define qglVertex2d glVertex2d -#define qglVertex2dv glVertex2dv -#define qglVertex2f glVertex2f -#define qglVertex2fv glVertex2fv -#define qglVertex2i glVertex2i -#define qglVertex2iv glVertex2iv -#define qglVertex2s glVertex2s -#define qglVertex2sv glVertex2sv -#define qglVertex3d glVertex3d -#define qglVertex3dv glVertex3dv -#define qglVertex3f glVertex3f -#define qglVertex3fv glVertex3fv -#define qglVertex3i glVertex3i -#define qglVertex3iv glVertex3iv -#define qglVertex3s glVertex3s -#define qglVertex3sv glVertex3sv -#define qglVertex4d glVertex4d -#define qglVertex4dv glVertex4dv -#define qglVertex4f glVertex4f -#define qglVertex4fv glVertex4fv -#define qglVertex4i glVertex4i -#define qglVertex4iv glVertex4iv -#define qglVertex4s glVertex4s -#define qglVertex4sv glVertex4sv -#define qglVertexPointer glVertexPointer -#define qglViewport glViewport - -#endif diff --git a/src/renderer/tr_animation.c b/src/renderer/tr_animation.c deleted file mode 100644 index a9bcd507..00000000 --- a/src/renderer/tr_animation.c +++ /dev/null @@ -1,659 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "tr_local.h" - -/* - -All bones should be an identity orientation to display the mesh exactly -as it is specified. - -For all other frames, the bones represent the transformation from the -orientation of the bone in the base frame to the orientation in this -frame. - -*/ - -/* -============== -R_AddAnimSurfaces -============== -*/ -void R_AddAnimSurfaces( trRefEntity_t *ent ) { - md4Header_t *header; - md4Surface_t *surface; - md4LOD_t *lod; - shader_t *shader; - int i; - - header = (md4Header_t *) tr.currentModel->modelData; - lod = (md4LOD_t *)( (byte *)header + header->ofsLODs ); - - surface = (md4Surface_t *)( (byte *)lod + lod->ofsSurfaces ); - for ( i = 0 ; i < lod->numSurfaces ; i++ ) { - shader = R_GetShaderByHandle( surface->shaderIndex ); - R_AddDrawSurf( (void *)surface, shader, 0 /*fogNum*/, qfalse ); - surface = (md4Surface_t *)( (byte *)surface + surface->ofsEnd ); - } -} - -/* -============== -RB_SurfaceAnim -============== -*/ -void RB_SurfaceAnim( md4Surface_t *surface ) { - int i, j, k; - float frontlerp, backlerp; - int *triangles; - int indexes; - int baseIndex, baseVertex; - int numVerts; - md4Vertex_t *v; - md4Bone_t bones[MD4_MAX_BONES]; - md4Bone_t *bonePtr, *bone; - md4Header_t *header; - md4Frame_t *frame; - md4Frame_t *oldFrame; - int frameSize; - - - if ( backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame ) { - backlerp = 0; - frontlerp = 1; - } else { - backlerp = backEnd.currentEntity->e.backlerp; - frontlerp = 1.0f - backlerp; - } - header = (md4Header_t *)((byte *)surface + surface->ofsHeader); - - frameSize = (size_t)( &((md4Frame_t *)0)->bones[ header->numBones ] ); - - frame = (md4Frame_t *)((byte *)header + header->ofsFrames + - backEnd.currentEntity->e.frame * frameSize ); - oldFrame = (md4Frame_t *)((byte *)header + header->ofsFrames + - backEnd.currentEntity->e.oldframe * frameSize ); - - RB_CheckOverflow( surface->numVerts, surface->numTriangles * 3 ); - - triangles = (int *) ((byte *)surface + surface->ofsTriangles); - indexes = surface->numTriangles * 3; - baseIndex = tess.numIndexes; - baseVertex = tess.numVertexes; - for (j = 0 ; j < indexes ; j++) { - tess.indexes[baseIndex + j] = baseIndex + triangles[j]; - } - tess.numIndexes += indexes; - - // - // lerp all the needed bones - // - if ( !backlerp ) { - // no lerping needed - bonePtr = frame->bones; - } else { - bonePtr = bones; - for ( i = 0 ; i < header->numBones*12 ; i++ ) { - ((float *)bonePtr)[i] = frontlerp * ((float *)frame->bones)[i] - + backlerp * ((float *)oldFrame->bones)[i]; - } - } - - // - // deform the vertexes by the lerped bones - // - numVerts = surface->numVerts; - // FIXME - // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left - // in for reference. - //v = (md4Vertex_t *) ((byte *)surface + surface->ofsVerts + 12); - v = (md4Vertex_t *) ((byte *)surface + surface->ofsVerts); - for ( j = 0; j < numVerts; j++ ) { - vec3_t tempVert, tempNormal; - md4Weight_t *w; - - VectorClear( tempVert ); - VectorClear( tempNormal ); - w = v->weights; - for ( k = 0 ; k < v->numWeights ; k++, w++ ) { - bone = bonePtr + w->boneIndex; - - tempVert[0] += w->boneWeight * ( DotProduct( bone->matrix[0], w->offset ) + bone->matrix[0][3] ); - tempVert[1] += w->boneWeight * ( DotProduct( bone->matrix[1], w->offset ) + bone->matrix[1][3] ); - tempVert[2] += w->boneWeight * ( DotProduct( bone->matrix[2], w->offset ) + bone->matrix[2][3] ); - - tempNormal[0] += w->boneWeight * DotProduct( bone->matrix[0], v->normal ); - tempNormal[1] += w->boneWeight * DotProduct( bone->matrix[1], v->normal ); - tempNormal[2] += w->boneWeight * DotProduct( bone->matrix[2], v->normal ); - } - - tess.xyz[baseVertex + j][0] = tempVert[0]; - tess.xyz[baseVertex + j][1] = tempVert[1]; - tess.xyz[baseVertex + j][2] = tempVert[2]; - - tess.normal[baseVertex + j][0] = tempNormal[0]; - tess.normal[baseVertex + j][1] = tempNormal[1]; - tess.normal[baseVertex + j][2] = tempNormal[2]; - - tess.texCoords[baseVertex + j][0][0] = v->texCoords[0]; - tess.texCoords[baseVertex + j][0][1] = v->texCoords[1]; - - // FIXME - // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left - // in for reference. - //v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights] + 12 ); - v = (md4Vertex_t *)&v->weights[v->numWeights]; - } - - tess.numVertexes += surface->numVerts; -} - - -#ifdef RAVENMD4 - -// copied and adapted from tr_mesh.c - -/* -============= -R_MDRCullModel -============= -*/ - -static int R_MDRCullModel( mdrHeader_t *header, trRefEntity_t *ent ) { - vec3_t bounds[2]; - mdrFrame_t *oldFrame, *newFrame; - int i, frameSize; - - frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] ); - - // compute frame pointers - newFrame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.frame); - oldFrame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.oldframe); - - // cull bounding sphere ONLY if this is not an upscaled entity - if ( !ent->e.nonNormalizedAxes ) - { - if ( ent->e.frame == ent->e.oldframe ) - { - switch ( R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ) ) - { - // Ummm... yeah yeah I know we don't really have an md3 here.. but we pretend - // we do. After all, the purpose of md4s are not that different, are they? - - case CULL_OUT: - tr.pc.c_sphere_cull_md3_out++; - return CULL_OUT; - - case CULL_IN: - tr.pc.c_sphere_cull_md3_in++; - return CULL_IN; - - case CULL_CLIP: - tr.pc.c_sphere_cull_md3_clip++; - break; - } - } - else - { - int sphereCull, sphereCullB; - - sphereCull = R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ); - if ( newFrame == oldFrame ) { - sphereCullB = sphereCull; - } else { - sphereCullB = R_CullLocalPointAndRadius( oldFrame->localOrigin, oldFrame->radius ); - } - - if ( sphereCull == sphereCullB ) - { - if ( sphereCull == CULL_OUT ) - { - tr.pc.c_sphere_cull_md3_out++; - return CULL_OUT; - } - else if ( sphereCull == CULL_IN ) - { - tr.pc.c_sphere_cull_md3_in++; - return CULL_IN; - } - else - { - tr.pc.c_sphere_cull_md3_clip++; - } - } - } - } - - // calculate a bounding box in the current coordinate system - for (i = 0 ; i < 3 ; i++) { - bounds[0][i] = oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i]; - bounds[1][i] = oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i]; - } - - switch ( R_CullLocalBox( bounds ) ) - { - case CULL_IN: - tr.pc.c_box_cull_md3_in++; - return CULL_IN; - case CULL_CLIP: - tr.pc.c_box_cull_md3_clip++; - return CULL_CLIP; - case CULL_OUT: - default: - tr.pc.c_box_cull_md3_out++; - return CULL_OUT; - } -} - -/* -================= -R_MDRComputeFogNum - -================= -*/ - -int R_MDRComputeFogNum( mdrHeader_t *header, trRefEntity_t *ent ) { - int i, j; - fog_t *fog; - mdrFrame_t *mdrFrame; - vec3_t localOrigin; - int frameSize; - - if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { - return 0; - } - - frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] ); - - // FIXME: non-normalized axis issues - mdrFrame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.frame); - VectorAdd( ent->e.origin, mdrFrame->localOrigin, localOrigin ); - for ( i = 1 ; i < tr.world->numfogs ; i++ ) { - fog = &tr.world->fogs[i]; - for ( j = 0 ; j < 3 ; j++ ) { - if ( localOrigin[j] - mdrFrame->radius >= fog->bounds[1][j] ) { - break; - } - if ( localOrigin[j] + mdrFrame->radius <= fog->bounds[0][j] ) { - break; - } - } - if ( j == 3 ) { - return i; - } - } - - return 0; -} - - -/* -============== -R_MDRAddAnimSurfaces -============== -*/ - -// much stuff in there is just copied from R_AddMd3Surfaces in tr_mesh.c - -void R_MDRAddAnimSurfaces( trRefEntity_t *ent ) { - mdrHeader_t *header; - mdrSurface_t *surface; - mdrLOD_t *lod; - shader_t *shader; - skin_t *skin; - int i, j; - int lodnum = 0; - int fogNum = 0; - int cull; - qboolean personalModel; - - header = (mdrHeader_t *) tr.currentModel->modelData; - - personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal; - - if ( ent->e.renderfx & RF_WRAP_FRAMES ) - { - ent->e.frame %= header->numFrames; - ent->e.oldframe %= header->numFrames; - } - - // - // Validate the frames so there is no chance of a crash. - // This will write directly into the entity structure, so - // when the surfaces are rendered, they don't need to be - // range checked again. - // - if ((ent->e.frame >= header->numFrames) - || (ent->e.frame < 0) - || (ent->e.oldframe >= header->numFrames) - || (ent->e.oldframe < 0) ) - { - ri.Printf( PRINT_DEVELOPER, "R_MDRAddAnimSurfaces: no such frame %d to %d for '%s'\n", - ent->e.oldframe, ent->e.frame, tr.currentModel->name ); - ent->e.frame = 0; - ent->e.oldframe = 0; - } - - // - // cull the entire model if merged bounding box of both frames - // is outside the view frustum. - // - cull = R_MDRCullModel (header, ent); - if ( cull == CULL_OUT ) { - return; - } - - // figure out the current LOD of the model we're rendering, and set the lod pointer respectively. - lodnum = R_ComputeLOD(ent); - // check whether this model has as that many LODs at all. If not, try the closest thing we got. - if(header->numLODs <= 0) - return; - if(header->numLODs <= lodnum) - lodnum = header->numLODs - 1; - - lod = (mdrLOD_t *)( (byte *)header + header->ofsLODs); - for(i = 0; i < lodnum; i++) - { - lod = (mdrLOD_t *) ((byte *) lod + lod->ofsEnd); - } - - // set up lighting - if ( !personalModel || r_shadows->integer > 1 ) - { - R_SetupEntityLighting( &tr.refdef, ent ); - } - - // fogNum? - fogNum = R_MDRComputeFogNum( header, ent ); - - surface = (mdrSurface_t *)( (byte *)lod + lod->ofsSurfaces ); - - for ( i = 0 ; i < lod->numSurfaces ; i++ ) - { - - if(ent->e.customShader) - shader = R_GetShaderByHandle(ent->e.customShader); - else if(ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins) - { - skin = R_GetSkinByHandle(ent->e.customSkin); - shader = tr.defaultShader; - - for(j = 0; j < skin->numSurfaces; j++) - { - if (!strcmp(skin->surfaces[j]->name, surface->name)) - { - shader = skin->surfaces[j]->shader; - break; - } - } - } - else if(surface->shaderIndex > 0) - shader = R_GetShaderByHandle( surface->shaderIndex ); - else - shader = tr.defaultShader; - - // we will add shadows even if the main object isn't visible in the view - - // stencil shadows can't do personal models unless I polyhedron clip - if ( !personalModel - && r_shadows->integer == 2 - && fogNum == 0 - && !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) - && shader->sort == SS_OPAQUE ) - { - R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, qfalse ); - } - - // projection shadows work fine with personal models - if ( r_shadows->integer == 3 - && fogNum == 0 - && (ent->e.renderfx & RF_SHADOW_PLANE ) - && shader->sort == SS_OPAQUE ) - { - R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, qfalse ); - } - - if (!personalModel) - R_AddDrawSurf( (void *)surface, shader, fogNum, qfalse ); - - surface = (mdrSurface_t *)( (byte *)surface + surface->ofsEnd ); - } -} - -/* -============== -RB_MDRSurfaceAnim -============== -*/ -void RB_MDRSurfaceAnim( md4Surface_t *surface ) -{ - int i, j, k; - float frontlerp, backlerp; - int *triangles; - int indexes; - int baseIndex, baseVertex; - int numVerts; - mdrVertex_t *v; - mdrHeader_t *header; - mdrFrame_t *frame; - mdrFrame_t *oldFrame; - mdrBone_t bones[MD4_MAX_BONES], *bonePtr, *bone; - - int frameSize; - - // don't lerp if lerping off, or this is the only frame, or the last frame... - // - if (backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame) - { - backlerp = 0; // if backlerp is 0, lerping is off and frontlerp is never used - frontlerp = 1; - } - else - { - backlerp = backEnd.currentEntity->e.backlerp; - frontlerp = 1.0f - backlerp; - } - - header = (mdrHeader_t *)((byte *)surface + surface->ofsHeader); - - frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] ); - - frame = (mdrFrame_t *)((byte *)header + header->ofsFrames + - backEnd.currentEntity->e.frame * frameSize ); - oldFrame = (mdrFrame_t *)((byte *)header + header->ofsFrames + - backEnd.currentEntity->e.oldframe * frameSize ); - - RB_CheckOverflow( surface->numVerts, surface->numTriangles ); - - triangles = (int *) ((byte *)surface + surface->ofsTriangles); - indexes = surface->numTriangles * 3; - baseIndex = tess.numIndexes; - baseVertex = tess.numVertexes; - - // Set up all triangles. - for (j = 0 ; j < indexes ; j++) - { - tess.indexes[baseIndex + j] = baseVertex + triangles[j]; - } - tess.numIndexes += indexes; - - // - // lerp all the needed bones - // - if ( !backlerp ) - { - // no lerping needed - bonePtr = frame->bones; - } - else - { - bonePtr = bones; - - for ( i = 0 ; i < header->numBones*12 ; i++ ) - { - ((float *)bonePtr)[i] = frontlerp * ((float *)frame->bones)[i] + backlerp * ((float *)oldFrame->bones)[i]; - } - } - - // - // deform the vertexes by the lerped bones - // - numVerts = surface->numVerts; - v = (mdrVertex_t *) ((byte *)surface + surface->ofsVerts); - for ( j = 0; j < numVerts; j++ ) - { - vec3_t tempVert, tempNormal; - mdrWeight_t *w; - - VectorClear( tempVert ); - VectorClear( tempNormal ); - w = v->weights; - for ( k = 0 ; k < v->numWeights ; k++, w++ ) - { - bone = bonePtr + w->boneIndex; - - tempVert[0] += w->boneWeight * ( DotProduct( bone->matrix[0], w->offset ) + bone->matrix[0][3] ); - tempVert[1] += w->boneWeight * ( DotProduct( bone->matrix[1], w->offset ) + bone->matrix[1][3] ); - tempVert[2] += w->boneWeight * ( DotProduct( bone->matrix[2], w->offset ) + bone->matrix[2][3] ); - - tempNormal[0] += w->boneWeight * DotProduct( bone->matrix[0], v->normal ); - tempNormal[1] += w->boneWeight * DotProduct( bone->matrix[1], v->normal ); - tempNormal[2] += w->boneWeight * DotProduct( bone->matrix[2], v->normal ); - } - - tess.xyz[baseVertex + j][0] = tempVert[0]; - tess.xyz[baseVertex + j][1] = tempVert[1]; - tess.xyz[baseVertex + j][2] = tempVert[2]; - - tess.normal[baseVertex + j][0] = tempNormal[0]; - tess.normal[baseVertex + j][1] = tempNormal[1]; - tess.normal[baseVertex + j][2] = tempNormal[2]; - - tess.texCoords[baseVertex + j][0][0] = v->texCoords[0]; - tess.texCoords[baseVertex + j][0][1] = v->texCoords[1]; - - v = (mdrVertex_t *)&v->weights[v->numWeights]; - } - - tess.numVertexes += surface->numVerts; -} - - -#define MC_MASK_X ((1<<(MC_BITS_X))-1) -#define MC_MASK_Y ((1<<(MC_BITS_Y))-1) -#define MC_MASK_Z ((1<<(MC_BITS_Z))-1) -#define MC_MASK_VECT ((1<<(MC_BITS_VECT))-1) - -#define MC_SCALE_VECT (1.0f/(float)((1<<(MC_BITS_VECT-1))-2)) - -#define MC_POS_X (0) -#define MC_SHIFT_X (0) - -#define MC_POS_Y ((((MC_BITS_X))/8)) -#define MC_SHIFT_Y ((((MC_BITS_X)%8))) - -#define MC_POS_Z ((((MC_BITS_X+MC_BITS_Y))/8)) -#define MC_SHIFT_Z ((((MC_BITS_X+MC_BITS_Y)%8))) - -#define MC_POS_V11 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z))/8)) -#define MC_SHIFT_V11 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z)%8))) - -#define MC_POS_V12 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT))/8)) -#define MC_SHIFT_V12 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT)%8))) - -#define MC_POS_V13 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*2))/8)) -#define MC_SHIFT_V13 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*2)%8))) - -#define MC_POS_V21 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*3))/8)) -#define MC_SHIFT_V21 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*3)%8))) - -#define MC_POS_V22 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*4))/8)) -#define MC_SHIFT_V22 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*4)%8))) - -#define MC_POS_V23 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*5))/8)) -#define MC_SHIFT_V23 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*5)%8))) - -#define MC_POS_V31 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*6))/8)) -#define MC_SHIFT_V31 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*6)%8))) - -#define MC_POS_V32 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*7))/8)) -#define MC_SHIFT_V32 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*7)%8))) - -#define MC_POS_V33 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*8))/8)) -#define MC_SHIFT_V33 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*8)%8))) - -void MC_UnCompress(float mat[3][4],const unsigned char * comp) -{ - int val; - - val=(int)((unsigned short *)(comp))[0]; - val-=1<<(MC_BITS_X-1); - mat[0][3]=((float)(val))*MC_SCALE_X; - - val=(int)((unsigned short *)(comp))[1]; - val-=1<<(MC_BITS_Y-1); - mat[1][3]=((float)(val))*MC_SCALE_Y; - - val=(int)((unsigned short *)(comp))[2]; - val-=1<<(MC_BITS_Z-1); - mat[2][3]=((float)(val))*MC_SCALE_Z; - - val=(int)((unsigned short *)(comp))[3]; - val-=1<<(MC_BITS_VECT-1); - mat[0][0]=((float)(val))*MC_SCALE_VECT; - - val=(int)((unsigned short *)(comp))[4]; - val-=1<<(MC_BITS_VECT-1); - mat[0][1]=((float)(val))*MC_SCALE_VECT; - - val=(int)((unsigned short *)(comp))[5]; - val-=1<<(MC_BITS_VECT-1); - mat[0][2]=((float)(val))*MC_SCALE_VECT; - - - val=(int)((unsigned short *)(comp))[6]; - val-=1<<(MC_BITS_VECT-1); - mat[1][0]=((float)(val))*MC_SCALE_VECT; - - val=(int)((unsigned short *)(comp))[7]; - val-=1<<(MC_BITS_VECT-1); - mat[1][1]=((float)(val))*MC_SCALE_VECT; - - val=(int)((unsigned short *)(comp))[8]; - val-=1<<(MC_BITS_VECT-1); - mat[1][2]=((float)(val))*MC_SCALE_VECT; - - - val=(int)((unsigned short *)(comp))[9]; - val-=1<<(MC_BITS_VECT-1); - mat[2][0]=((float)(val))*MC_SCALE_VECT; - - val=(int)((unsigned short *)(comp))[10]; - val-=1<<(MC_BITS_VECT-1); - mat[2][1]=((float)(val))*MC_SCALE_VECT; - - val=(int)((unsigned short *)(comp))[11]; - val-=1<<(MC_BITS_VECT-1); - mat[2][2]=((float)(val))*MC_SCALE_VECT; -} -#endif diff --git a/src/renderer/tr_backend.c b/src/renderer/tr_backend.c deleted file mode 100644 index 61495083..00000000 --- a/src/renderer/tr_backend.c +++ /dev/null @@ -1,1162 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#include "tr_local.h" - -backEndData_t *backEndData; -backEndState_t backEnd; - - -static float s_flipMatrix[16] = { - // convert from our coordinate system (looking down X) - // to OpenGL's coordinate system (looking down -Z) - 0, 0, -1, 0, - -1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 0, 1 -}; - - -/* -** GL_Bind -*/ -void GL_Bind( image_t *image ) { - int texnum; - - if ( !image ) { - ri.Printf( PRINT_WARNING, "GL_Bind: NULL image\n" ); - texnum = tr.defaultImage->texnum; - } else { - texnum = image->texnum; - } - - if ( r_nobind->integer && tr.dlightImage ) { // performance evaluation option - texnum = tr.dlightImage->texnum; - } - - if ( glState.currenttextures[glState.currenttmu] != texnum ) { - image->frameUsed = tr.frameCount; - glState.currenttextures[glState.currenttmu] = texnum; - qglBindTexture (GL_TEXTURE_2D, texnum); - } -} - -/* -** GL_SelectTexture -*/ -void GL_SelectTexture( int unit ) -{ - if ( glState.currenttmu == unit ) - { - return; - } - - if ( unit == 0 ) - { - qglActiveTextureARB( GL_TEXTURE0_ARB ); - GLimp_LogComment( "glActiveTextureARB( GL_TEXTURE0_ARB )\n" ); - qglClientActiveTextureARB( GL_TEXTURE0_ARB ); - GLimp_LogComment( "glClientActiveTextureARB( GL_TEXTURE0_ARB )\n" ); - } - else if ( unit == 1 ) - { - qglActiveTextureARB( GL_TEXTURE1_ARB ); - GLimp_LogComment( "glActiveTextureARB( GL_TEXTURE1_ARB )\n" ); - qglClientActiveTextureARB( GL_TEXTURE1_ARB ); - GLimp_LogComment( "glClientActiveTextureARB( GL_TEXTURE1_ARB )\n" ); - } else { - ri.Error( ERR_DROP, "GL_SelectTexture: unit = %i", unit ); - } - - glState.currenttmu = unit; -} - - -/* -** GL_BindMultitexture -*/ -void GL_BindMultitexture( image_t *image0, GLuint env0, image_t *image1, GLuint env1 ) { - int texnum0, texnum1; - - texnum0 = image0->texnum; - texnum1 = image1->texnum; - - if ( r_nobind->integer && tr.dlightImage ) { // performance evaluation option - texnum0 = texnum1 = tr.dlightImage->texnum; - } - - if ( glState.currenttextures[1] != texnum1 ) { - GL_SelectTexture( 1 ); - image1->frameUsed = tr.frameCount; - glState.currenttextures[1] = texnum1; - qglBindTexture( GL_TEXTURE_2D, texnum1 ); - } - if ( glState.currenttextures[0] != texnum0 ) { - GL_SelectTexture( 0 ); - image0->frameUsed = tr.frameCount; - glState.currenttextures[0] = texnum0; - qglBindTexture( GL_TEXTURE_2D, texnum0 ); - } -} - - -/* -** GL_Cull -*/ -void GL_Cull( int cullType ) { - if ( glState.faceCulling == cullType ) { - return; - } - - glState.faceCulling = cullType; - - if ( cullType == CT_TWO_SIDED ) - { - qglDisable( GL_CULL_FACE ); - } - else - { - qboolean cullFront; - qglEnable( GL_CULL_FACE ); - - cullFront = (cullType == CT_FRONT_SIDED); - if ( backEnd.viewParms.isMirror ) - { - cullFront = !cullFront; - } - - qglCullFace( cullFront ? GL_FRONT : GL_BACK ); - } -} - -/* -** GL_TexEnv -*/ -void GL_TexEnv( int env ) -{ - if ( env == glState.texEnv[glState.currenttmu] ) - { - return; - } - - glState.texEnv[glState.currenttmu] = env; - - - switch ( env ) - { - case GL_MODULATE: - qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); - break; - case GL_REPLACE: - qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); - break; - case GL_DECAL: - qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL ); - break; - case GL_ADD: - qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD ); - break; - default: - ri.Error( ERR_DROP, "GL_TexEnv: invalid env '%d' passed", env ); - break; - } -} - -/* -** GL_State -** -** This routine is responsible for setting the most commonly changed state -** in Q3. -*/ -void GL_State( unsigned long stateBits ) -{ - unsigned long diff = stateBits ^ glState.glStateBits; - - if ( !diff ) - { - return; - } - - // - // check depthFunc bits - // - if ( diff & GLS_DEPTHFUNC_EQUAL ) - { - if ( stateBits & GLS_DEPTHFUNC_EQUAL ) - { - qglDepthFunc( GL_EQUAL ); - } - else - { - qglDepthFunc( GL_LEQUAL ); - } - } - - // - // check blend bits - // - if ( diff & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) - { - GLenum srcFactor, dstFactor; - - if ( stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) - { - switch ( stateBits & GLS_SRCBLEND_BITS ) - { - case GLS_SRCBLEND_ZERO: - srcFactor = GL_ZERO; - break; - case GLS_SRCBLEND_ONE: - srcFactor = GL_ONE; - break; - case GLS_SRCBLEND_DST_COLOR: - srcFactor = GL_DST_COLOR; - break; - case GLS_SRCBLEND_ONE_MINUS_DST_COLOR: - srcFactor = GL_ONE_MINUS_DST_COLOR; - break; - case GLS_SRCBLEND_SRC_ALPHA: - srcFactor = GL_SRC_ALPHA; - break; - case GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA: - srcFactor = GL_ONE_MINUS_SRC_ALPHA; - break; - case GLS_SRCBLEND_DST_ALPHA: - srcFactor = GL_DST_ALPHA; - break; - case GLS_SRCBLEND_ONE_MINUS_DST_ALPHA: - srcFactor = GL_ONE_MINUS_DST_ALPHA; - break; - case GLS_SRCBLEND_ALPHA_SATURATE: - srcFactor = GL_SRC_ALPHA_SATURATE; - break; - default: - srcFactor = GL_ONE; // to get warning to shut up - ri.Error( ERR_DROP, "GL_State: invalid src blend state bits" ); - break; - } - - switch ( stateBits & GLS_DSTBLEND_BITS ) - { - case GLS_DSTBLEND_ZERO: - dstFactor = GL_ZERO; - break; - case GLS_DSTBLEND_ONE: - dstFactor = GL_ONE; - break; - case GLS_DSTBLEND_SRC_COLOR: - dstFactor = GL_SRC_COLOR; - break; - case GLS_DSTBLEND_ONE_MINUS_SRC_COLOR: - dstFactor = GL_ONE_MINUS_SRC_COLOR; - break; - case GLS_DSTBLEND_SRC_ALPHA: - dstFactor = GL_SRC_ALPHA; - break; - case GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA: - dstFactor = GL_ONE_MINUS_SRC_ALPHA; - break; - case GLS_DSTBLEND_DST_ALPHA: - dstFactor = GL_DST_ALPHA; - break; - case GLS_DSTBLEND_ONE_MINUS_DST_ALPHA: - dstFactor = GL_ONE_MINUS_DST_ALPHA; - break; - default: - dstFactor = GL_ONE; // to get warning to shut up - ri.Error( ERR_DROP, "GL_State: invalid dst blend state bits" ); - break; - } - - qglEnable( GL_BLEND ); - qglBlendFunc( srcFactor, dstFactor ); - } - else - { - qglDisable( GL_BLEND ); - } - } - - // - // check depthmask - // - if ( diff & GLS_DEPTHMASK_TRUE ) - { - if ( stateBits & GLS_DEPTHMASK_TRUE ) - { - qglDepthMask( GL_TRUE ); - } - else - { - qglDepthMask( GL_FALSE ); - } - } - - // - // fill/line mode - // - if ( diff & GLS_POLYMODE_LINE ) - { - if ( stateBits & GLS_POLYMODE_LINE ) - { - qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); - } - else - { - qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); - } - } - - // - // depthtest - // - if ( diff & GLS_DEPTHTEST_DISABLE ) - { - if ( stateBits & GLS_DEPTHTEST_DISABLE ) - { - qglDisable( GL_DEPTH_TEST ); - } - else - { - qglEnable( GL_DEPTH_TEST ); - } - } - - // - // alpha test - // - if ( diff & GLS_ATEST_BITS ) - { - switch ( stateBits & GLS_ATEST_BITS ) - { - case 0: - qglDisable( GL_ALPHA_TEST ); - break; - case GLS_ATEST_GT_0: - qglEnable( GL_ALPHA_TEST ); - qglAlphaFunc( GL_GREATER, 0.0f ); - break; - case GLS_ATEST_LT_80: - qglEnable( GL_ALPHA_TEST ); - qglAlphaFunc( GL_LESS, 0.5f ); - break; - case GLS_ATEST_GE_80: - qglEnable( GL_ALPHA_TEST ); - qglAlphaFunc( GL_GEQUAL, 0.5f ); - break; - default: - assert( 0 ); - break; - } - } - - glState.glStateBits = stateBits; -} - - - -/* -================ -RB_Hyperspace - -A player has predicted a teleport, but hasn't arrived yet -================ -*/ -static void RB_Hyperspace( void ) { - float c; - - if ( !backEnd.isHyperspace ) { - // do initialization shit - } - - c = ( backEnd.refdef.time & 255 ) / 255.0f; - qglClearColor( c, c, c, 1 ); - qglClear( GL_COLOR_BUFFER_BIT ); - - backEnd.isHyperspace = qtrue; -} - - -static void SetViewportAndScissor( void ) { - qglMatrixMode(GL_PROJECTION); - qglLoadMatrixf( backEnd.viewParms.projectionMatrix ); - qglMatrixMode(GL_MODELVIEW); - - // set the window clipping - qglViewport( backEnd.viewParms.viewportX, backEnd.viewParms.viewportY, - backEnd.viewParms.viewportWidth, backEnd.viewParms.viewportHeight ); - qglScissor( backEnd.viewParms.viewportX, backEnd.viewParms.viewportY, - backEnd.viewParms.viewportWidth, backEnd.viewParms.viewportHeight ); -} - -/* -================= -RB_BeginDrawingView - -Any mirrored or portaled views have already been drawn, so prepare -to actually render the visible surfaces for this view -================= -*/ -void RB_BeginDrawingView (void) { - int clearBits = 0; - - // sync with gl if needed - if ( r_finish->integer == 1 && !glState.finishCalled ) { - qglFinish (); - glState.finishCalled = qtrue; - } - if ( r_finish->integer == 0 ) { - glState.finishCalled = qtrue; - } - - // we will need to change the projection matrix before drawing - // 2D images again - backEnd.projection2D = qfalse; - - // - // set the modelview matrix for the viewer - // - SetViewportAndScissor(); - - // ensures that depth writes are enabled for the depth clear - GL_State( GLS_DEFAULT ); - // clear relevant buffers - clearBits = GL_DEPTH_BUFFER_BIT; - - if ( r_measureOverdraw->integer || r_shadows->integer == 2 ) - { - clearBits |= GL_STENCIL_BUFFER_BIT; - } - if ( r_fastsky->integer && !( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) ) - { - clearBits |= GL_COLOR_BUFFER_BIT; // FIXME: only if sky shaders have been used -#ifdef _DEBUG - qglClearColor( 0.8f, 0.7f, 0.4f, 1.0f ); // FIXME: get color of sky -#else - qglClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); // FIXME: get color of sky -#endif - } - qglClear( clearBits ); - - if ( ( backEnd.refdef.rdflags & RDF_HYPERSPACE ) ) - { - RB_Hyperspace(); - return; - } - else - { - backEnd.isHyperspace = qfalse; - } - - glState.faceCulling = -1; // force face culling to set next time - - // we will only draw a sun if there was sky rendered in this view - backEnd.skyRenderedThisView = qfalse; - - // clip to the plane of the portal - if ( backEnd.viewParms.isPortal ) { - float plane[4]; - double plane2[4]; - - plane[0] = backEnd.viewParms.portalPlane.normal[0]; - plane[1] = backEnd.viewParms.portalPlane.normal[1]; - plane[2] = backEnd.viewParms.portalPlane.normal[2]; - plane[3] = backEnd.viewParms.portalPlane.dist; - - plane2[0] = DotProduct (backEnd.viewParms.or.axis[0], plane); - plane2[1] = DotProduct (backEnd.viewParms.or.axis[1], plane); - plane2[2] = DotProduct (backEnd.viewParms.or.axis[2], plane); - plane2[3] = DotProduct (plane, backEnd.viewParms.or.origin) - plane[3]; - - qglLoadMatrixf( s_flipMatrix ); - qglClipPlane (GL_CLIP_PLANE0, plane2); - qglEnable (GL_CLIP_PLANE0); - } else { - qglDisable (GL_CLIP_PLANE0); - } -} - - -#define MAC_EVENT_PUMP_MSEC 5 - -/* -================== -RB_RenderDrawSurfList -================== -*/ -void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { - shader_t *shader, *oldShader; - int fogNum, oldFogNum; - int entityNum, oldEntityNum; - int dlighted, oldDlighted; - qboolean depthRange, oldDepthRange, isCrosshair, wasCrosshair; - int i; - drawSurf_t *drawSurf; - int oldSort; - float originalTime; - - // save original time for entity shader offsets - originalTime = backEnd.refdef.floatTime; - - // clear the z buffer, set the modelview, etc - RB_BeginDrawingView (); - - // draw everything - oldEntityNum = -1; - backEnd.currentEntity = &tr.worldEntity; - oldShader = NULL; - oldFogNum = -1; - oldDepthRange = qfalse; - wasCrosshair = qfalse; - oldDlighted = qfalse; - oldSort = -1; - depthRange = qfalse; - - backEnd.pc.c_surfaces += numDrawSurfs; - - for (i = 0, drawSurf = drawSurfs ; i < numDrawSurfs ; i++, drawSurf++) { - if ( drawSurf->sort == oldSort ) { - // fast path, same as previous sort - rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface ); - continue; - } - oldSort = drawSurf->sort; - R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted ); - - // - // change the tess parameters if needed - // a "entityMergable" shader is a shader that can have surfaces from seperate - // entities merged into a single batch, like smoke and blood puff sprites - if (shader != oldShader || fogNum != oldFogNum || dlighted != oldDlighted - || ( entityNum != oldEntityNum && !shader->entityMergable ) ) { - if (oldShader != NULL) { - RB_EndSurface(); - } - RB_BeginSurface( shader, fogNum ); - oldShader = shader; - oldFogNum = fogNum; - oldDlighted = dlighted; - } - - // - // change the modelview matrix if needed - // - if ( entityNum != oldEntityNum ) { - depthRange = isCrosshair = qfalse; - - if ( entityNum != REFENTITYNUM_WORLD ) { - backEnd.currentEntity = &backEnd.refdef.entities[entityNum]; - backEnd.refdef.floatTime = originalTime - backEnd.currentEntity->e.shaderTime; - // we have to reset the shaderTime as well otherwise image animations start - // from the wrong frame - tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; - - // set up the transformation matrix - R_RotateForEntity( backEnd.currentEntity, &backEnd.viewParms, &backEnd.or ); - - // set up the dynamic lighting if needed - if ( backEnd.currentEntity->needDlights ) { - R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.or ); - } - - if(backEnd.currentEntity->e.renderfx & RF_DEPTHHACK) - { - // hack the depth range to prevent view model from poking into walls - depthRange = qtrue; - - if(backEnd.currentEntity->e.renderfx & RF_CROSSHAIR) - isCrosshair = qtrue; - } - } else { - backEnd.currentEntity = &tr.worldEntity; - backEnd.refdef.floatTime = originalTime; - backEnd.or = backEnd.viewParms.world; - // we have to reset the shaderTime as well otherwise image animations on - // the world (like water) continue with the wrong frame - tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; - R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.or ); - } - - qglLoadMatrixf( backEnd.or.modelMatrix ); - - // - // change depthrange. Also change projection matrix so first person weapon does not look like coming - // out of the screen. - // - if (oldDepthRange != depthRange || wasCrosshair != isCrosshair) - { - if (depthRange) - { - if(backEnd.viewParms.stereoFrame != STEREO_CENTER) - { - if(isCrosshair) - { - if(oldDepthRange) - { - // was not a crosshair but now is, change back proj matrix - qglMatrixMode(GL_PROJECTION); - qglLoadMatrixf(backEnd.viewParms.projectionMatrix); - qglMatrixMode(GL_MODELVIEW); - } - } - else - { - viewParms_t temp = backEnd.viewParms; - - R_SetupProjection(&temp, r_znear->value, qfalse); - - qglMatrixMode(GL_PROJECTION); - qglLoadMatrixf(temp.projectionMatrix); - qglMatrixMode(GL_MODELVIEW); - } - } - - if(!oldDepthRange) - qglDepthRange (0, 0.3); - } - else - { - if(!wasCrosshair && backEnd.viewParms.stereoFrame != STEREO_CENTER) - { - qglMatrixMode(GL_PROJECTION); - qglLoadMatrixf(backEnd.viewParms.projectionMatrix); - qglMatrixMode(GL_MODELVIEW); - } - - qglDepthRange (0, 1); - } - - oldDepthRange = depthRange; - wasCrosshair = isCrosshair; - } - - oldEntityNum = entityNum; - } - - // add the triangles for this surface - rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface ); - } - - backEnd.refdef.floatTime = originalTime; - - // draw the contents of the last shader batch - if (oldShader != NULL) { - RB_EndSurface(); - } - - // go back to the world modelview matrix - qglLoadMatrixf( backEnd.viewParms.world.modelMatrix ); - if ( depthRange ) { - qglDepthRange (0, 1); - } - -#if 0 - RB_DrawSun(); -#endif - // darken down any stencil shadows - RB_ShadowFinish(); - - // add light flares on lights that aren't obscured - RB_RenderFlares(); -} - - -/* -============================================================================ - -RENDER BACK END FUNCTIONS - -============================================================================ -*/ - -/* -================ -RB_SetGL2D - -================ -*/ -void RB_SetGL2D (void) { - backEnd.projection2D = qtrue; - - // set 2D virtual screen size - qglViewport( 0, 0, glConfig.vidWidth, glConfig.vidHeight ); - qglScissor( 0, 0, glConfig.vidWidth, glConfig.vidHeight ); - qglMatrixMode(GL_PROJECTION); - qglLoadIdentity (); - qglOrtho (0, glConfig.vidWidth, glConfig.vidHeight, 0, 0, 1); - qglMatrixMode(GL_MODELVIEW); - qglLoadIdentity (); - - GL_State( GLS_DEPTHTEST_DISABLE | - GLS_SRCBLEND_SRC_ALPHA | - GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); - - qglDisable( GL_CULL_FACE ); - qglDisable( GL_CLIP_PLANE0 ); - - // set time for 2D shaders - backEnd.refdef.time = ri.Milliseconds(); - backEnd.refdef.floatTime = backEnd.refdef.time * 0.001f; -} - - -/* -============= -RE_StretchRaw - -FIXME: not exactly backend -Stretches a raw 32 bit power of 2 bitmap image over the given screen rectangle. -Used for cinematics. -============= -*/ -void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty) { - int i, j; - int start, end; - - if ( !tr.registered ) { - return; - } - R_IssuePendingRenderCommands(); - - // we definately want to sync every frame for the cinematics - qglFinish(); - - start = 0; - if ( r_speeds->integer ) { - start = ri.Milliseconds(); - } - - // make sure rows and cols are powers of 2 - for ( i = 0 ; ( 1 << i ) < cols ; i++ ) { - } - for ( j = 0 ; ( 1 << j ) < rows ; j++ ) { - } - if ( ( 1 << i ) != cols || ( 1 << j ) != rows) { - ri.Error (ERR_DROP, "Draw_StretchRaw: size not a power of 2: %i by %i", cols, rows); - } - - GL_Bind( tr.scratchImage[client] ); - - // if the scratchImage isn't in the format we want, specify it as a new texture - if ( cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height ) { - tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols; - tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows; - qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - } else { - if (dirty) { - // otherwise, just subimage upload it so that drivers can tell we are going to be changing - // it and don't try and do a texture compression - qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data ); - } - } - - if ( r_speeds->integer ) { - end = ri.Milliseconds(); - ri.Printf( PRINT_ALL, "qglTexSubImage2D %i, %i: %i msec\n", cols, rows, end - start ); - } - - RB_SetGL2D(); - - qglColor3f( tr.identityLight, tr.identityLight, tr.identityLight ); - - qglBegin (GL_QUADS); - qglTexCoord2f ( 0.5f / cols, 0.5f / rows ); - qglVertex2f (x, y); - qglTexCoord2f ( ( cols - 0.5f ) / cols , 0.5f / rows ); - qglVertex2f (x+w, y); - qglTexCoord2f ( ( cols - 0.5f ) / cols, ( rows - 0.5f ) / rows ); - qglVertex2f (x+w, y+h); - qglTexCoord2f ( 0.5f / cols, ( rows - 0.5f ) / rows ); - qglVertex2f (x, y+h); - qglEnd (); -} - -void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty) { - - GL_Bind( tr.scratchImage[client] ); - - // if the scratchImage isn't in the format we want, specify it as a new texture - if ( cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height ) { - tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols; - tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows; - qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); - } else { - if (dirty) { - // otherwise, just subimage upload it so that drivers can tell we are going to be changing - // it and don't try and do a texture compression - qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data ); - } - } -} - - -/* -============= -RB_SetColor - -============= -*/ -const void *RB_SetColor( const void *data ) { - const setColorCommand_t *cmd; - - cmd = (const setColorCommand_t *)data; - - backEnd.color2D[0] = cmd->color[0] * 255; - backEnd.color2D[1] = cmd->color[1] * 255; - backEnd.color2D[2] = cmd->color[2] * 255; - backEnd.color2D[3] = cmd->color[3] * 255; - - return (const void *)(cmd + 1); -} - -/* -============= -RB_StretchPic -============= -*/ -const void *RB_StretchPic ( const void *data ) { - const stretchPicCommand_t *cmd; - shader_t *shader; - int numVerts, numIndexes; - - cmd = (const stretchPicCommand_t *)data; - - if ( !backEnd.projection2D ) { - RB_SetGL2D(); - } - - shader = cmd->shader; - if ( shader != tess.shader ) { - if ( tess.numIndexes ) { - RB_EndSurface(); - } - backEnd.currentEntity = &backEnd.entity2D; - RB_BeginSurface( shader, 0 ); - } - - RB_CHECKOVERFLOW( 4, 6 ); - numVerts = tess.numVertexes; - numIndexes = tess.numIndexes; - - tess.numVertexes += 4; - tess.numIndexes += 6; - - tess.indexes[ numIndexes ] = numVerts + 3; - tess.indexes[ numIndexes + 1 ] = numVerts + 0; - tess.indexes[ numIndexes + 2 ] = numVerts + 2; - tess.indexes[ numIndexes + 3 ] = numVerts + 2; - tess.indexes[ numIndexes + 4 ] = numVerts + 0; - tess.indexes[ numIndexes + 5 ] = numVerts + 1; - - *(int *)tess.vertexColors[ numVerts ] = - *(int *)tess.vertexColors[ numVerts + 1 ] = - *(int *)tess.vertexColors[ numVerts + 2 ] = - *(int *)tess.vertexColors[ numVerts + 3 ] = *(int *)backEnd.color2D; - - tess.xyz[ numVerts ][0] = cmd->x; - tess.xyz[ numVerts ][1] = cmd->y; - tess.xyz[ numVerts ][2] = 0; - - tess.texCoords[ numVerts ][0][0] = cmd->s1; - tess.texCoords[ numVerts ][0][1] = cmd->t1; - - tess.xyz[ numVerts + 1 ][0] = cmd->x + cmd->w; - tess.xyz[ numVerts + 1 ][1] = cmd->y; - tess.xyz[ numVerts + 1 ][2] = 0; - - tess.texCoords[ numVerts + 1 ][0][0] = cmd->s2; - tess.texCoords[ numVerts + 1 ][0][1] = cmd->t1; - - tess.xyz[ numVerts + 2 ][0] = cmd->x + cmd->w; - tess.xyz[ numVerts + 2 ][1] = cmd->y + cmd->h; - tess.xyz[ numVerts + 2 ][2] = 0; - - tess.texCoords[ numVerts + 2 ][0][0] = cmd->s2; - tess.texCoords[ numVerts + 2 ][0][1] = cmd->t2; - - tess.xyz[ numVerts + 3 ][0] = cmd->x; - tess.xyz[ numVerts + 3 ][1] = cmd->y + cmd->h; - tess.xyz[ numVerts + 3 ][2] = 0; - - tess.texCoords[ numVerts + 3 ][0][0] = cmd->s1; - tess.texCoords[ numVerts + 3 ][0][1] = cmd->t2; - - return (const void *)(cmd + 1); -} - - -/* -============= -RB_DrawSurfs - -============= -*/ -const void *RB_DrawSurfs( const void *data ) { - const drawSurfsCommand_t *cmd; - - // finish any 2D drawing if needed - if ( tess.numIndexes ) { - RB_EndSurface(); - } - - cmd = (const drawSurfsCommand_t *)data; - - backEnd.refdef = cmd->refdef; - backEnd.viewParms = cmd->viewParms; - - RB_RenderDrawSurfList( cmd->drawSurfs, cmd->numDrawSurfs ); - - return (const void *)(cmd + 1); -} - - -/* -============= -RB_DrawBuffer - -============= -*/ -const void *RB_DrawBuffer( const void *data ) { - const drawBufferCommand_t *cmd; - - cmd = (const drawBufferCommand_t *)data; - - qglDrawBuffer( cmd->buffer ); - - // clear screen for debugging - if ( r_clear->integer ) { - qglClearColor( 1, 0, 0.5, 1 ); - qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); - } - - return (const void *)(cmd + 1); -} - -/* -=============== -RB_ShowImages - -Draw all the images to the screen, on top of whatever -was there. This is used to test for texture thrashing. - -Also called by RE_EndRegistration -=============== -*/ -void RB_ShowImages( void ) { - int i; - image_t *image; - float x, y, w, h; - int start, end; - - if ( !backEnd.projection2D ) { - RB_SetGL2D(); - } - - qglClear( GL_COLOR_BUFFER_BIT ); - - qglFinish(); - - start = ri.Milliseconds(); - - for ( i=0 ; iinteger == 2 ) { - w *= image->uploadWidth / 512.0f; - h *= image->uploadHeight / 512.0f; - } - - GL_Bind( image ); - qglBegin (GL_QUADS); - qglTexCoord2f( 0, 0 ); - qglVertex2f( x, y ); - qglTexCoord2f( 1, 0 ); - qglVertex2f( x + w, y ); - qglTexCoord2f( 1, 1 ); - qglVertex2f( x + w, y + h ); - qglTexCoord2f( 0, 1 ); - qglVertex2f( x, y + h ); - qglEnd(); - } - - qglFinish(); - - end = ri.Milliseconds(); - ri.Printf( PRINT_ALL, "%i msec to draw all images\n", end - start ); - -} - -/* -============= -RB_ColorMask - -============= -*/ -const void *RB_ColorMask(const void *data) -{ - const colorMaskCommand_t *cmd = data; - - qglColorMask(cmd->rgba[0], cmd->rgba[1], cmd->rgba[2], cmd->rgba[3]); - - return (const void *)(cmd + 1); -} - -/* -============= -RB_ClearDepth - -============= -*/ -const void *RB_ClearDepth(const void *data) -{ - const clearDepthCommand_t *cmd = data; - - if(tess.numIndexes) - RB_EndSurface(); - - // texture swapping test - if (r_showImages->integer) - RB_ShowImages(); - - qglClear(GL_DEPTH_BUFFER_BIT); - - return (const void *)(cmd + 1); -} - -/* -============= -RB_SwapBuffers - -============= -*/ -const void *RB_SwapBuffers( const void *data ) { - const swapBuffersCommand_t *cmd; - - // finish any 2D drawing if needed - if ( tess.numIndexes ) { - RB_EndSurface(); - } - - // texture swapping test - if ( r_showImages->integer ) { - RB_ShowImages(); - } - - cmd = (const swapBuffersCommand_t *)data; - - // we measure overdraw by reading back the stencil buffer and - // counting up the number of increments that have happened - if ( r_measureOverdraw->integer ) { - int i; - long sum = 0; - unsigned char *stencilReadback; - - stencilReadback = ri.Hunk_AllocateTempMemory( glConfig.vidWidth * glConfig.vidHeight ); - qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencilReadback ); - - for ( i = 0; i < glConfig.vidWidth * glConfig.vidHeight; i++ ) { - sum += stencilReadback[i]; - } - - backEnd.pc.c_overDraw += sum; - ri.Hunk_FreeTempMemory( stencilReadback ); - } - - - if ( !glState.finishCalled ) { - qglFinish(); - } - - GLimp_LogComment( "***************** RB_SwapBuffers *****************\n\n\n" ); - - GLimp_EndFrame(); - - backEnd.projection2D = qfalse; - - return (const void *)(cmd + 1); -} - -/* -==================== -RB_ExecuteRenderCommands -==================== -*/ -void RB_ExecuteRenderCommands( const void *data ) { - int t1, t2; - - t1 = ri.Milliseconds (); - - while ( 1 ) { - data = PADP(data, sizeof(void *)); - - switch ( *(const int *)data ) { - case RC_SET_COLOR: - data = RB_SetColor( data ); - break; - case RC_STRETCH_PIC: - data = RB_StretchPic( data ); - break; - case RC_DRAW_SURFS: - data = RB_DrawSurfs( data ); - break; - case RC_DRAW_BUFFER: - data = RB_DrawBuffer( data ); - break; - case RC_SWAP_BUFFERS: - data = RB_SwapBuffers( data ); - break; - case RC_SCREENSHOT: - data = RB_TakeScreenshotCmd( data ); - break; - case RC_VIDEOFRAME: - data = RB_TakeVideoFrameCmd( data ); - break; - case RC_COLORMASK: - data = RB_ColorMask(data); - break; - case RC_CLEARDEPTH: - data = RB_ClearDepth(data); - break; - case RC_END_OF_LIST: - default: - // stop rendering - t2 = ri.Milliseconds (); - backEnd.pc.msec = t2 - t1; - return; - } - } - -} diff --git a/src/renderer/tr_bsp.c b/src/renderer/tr_bsp.c deleted file mode 100644 index 74596a53..00000000 --- a/src/renderer/tr_bsp.c +++ /dev/null @@ -1,1870 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_map.c - -#include "tr_local.h" - -/* - -Loads and prepares a map file for scene rendering. - -A single entry point: - -void RE_LoadWorldMap( const char *name ); - -*/ - -static world_t s_worldData; -static byte *fileBase; - -int c_subdivisions; -int c_gridVerts; - -//=============================================================================== - -static void HSVtoRGB( float h, float s, float v, float rgb[3] ) -{ - int i; - float f; - float p, q, t; - - h *= 5; - - i = floor( h ); - f = h - i; - - p = v * ( 1 - s ); - q = v * ( 1 - s * f ); - t = v * ( 1 - s * ( 1 - f ) ); - - switch ( i ) - { - case 0: - rgb[0] = v; - rgb[1] = t; - rgb[2] = p; - break; - case 1: - rgb[0] = q; - rgb[1] = v; - rgb[2] = p; - break; - case 2: - rgb[0] = p; - rgb[1] = v; - rgb[2] = t; - break; - case 3: - rgb[0] = p; - rgb[1] = q; - rgb[2] = v; - break; - case 4: - rgb[0] = t; - rgb[1] = p; - rgb[2] = v; - break; - case 5: - rgb[0] = v; - rgb[1] = p; - rgb[2] = q; - break; - } -} - -/* -=============== -R_ColorShiftLightingBytes - -=============== -*/ -static void R_ColorShiftLightingBytes( byte in[4], byte out[4] ) { - int shift, r, g, b; - - // shift the color data based on overbright range - shift = r_mapOverBrightBits->integer - tr.overbrightBits; - - // shift the data based on overbright range - r = in[0] << shift; - g = in[1] << shift; - b = in[2] << shift; - - // normalize by color instead of saturating to white - if ( ( r | g | b ) > 255 ) { - int max; - - max = r > g ? r : g; - max = max > b ? max : b; - r = r * 255 / max; - g = g * 255 / max; - b = b * 255 / max; - } - - out[0] = r; - out[1] = g; - out[2] = b; - out[3] = in[3]; -} - -/* -=============== -R_LoadLightmaps - -=============== -*/ -#define LIGHTMAP_SIZE 128 -static void R_LoadLightmaps( lump_t *l ) { - byte *buf, *buf_p; - int len; - byte image[LIGHTMAP_SIZE*LIGHTMAP_SIZE*4]; - int i, j; - float maxIntensity = 0; - double sumIntensity = 0; - - len = l->filelen; - if ( !len ) { - return; - } - buf = fileBase + l->fileofs; - - // we are about to upload textures - R_IssuePendingRenderCommands(); - - // create all the lightmaps - tr.numLightmaps = len / (LIGHTMAP_SIZE * LIGHTMAP_SIZE * 3); - if ( tr.numLightmaps == 1 ) { - //FIXME: HACK: maps with only one lightmap turn up fullbright for some reason. - //this avoids this, but isn't the correct solution. - tr.numLightmaps++; - } - - // if we are in r_vertexLight mode, we don't need the lightmaps at all - if ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) { - return; - } - - tr.lightmaps = ri.Hunk_Alloc( tr.numLightmaps * sizeof(image_t *), h_low ); - for ( i = 0 ; i < tr.numLightmaps ; i++ ) { - // expand the 24 bit on-disk to 32 bit - buf_p = buf + i * LIGHTMAP_SIZE*LIGHTMAP_SIZE * 3; - - if ( r_lightmap->integer == 2 ) - { // color code by intensity as development tool (FIXME: check range) - for ( j = 0; j < LIGHTMAP_SIZE * LIGHTMAP_SIZE; j++ ) - { - float r = buf_p[j*3+0]; - float g = buf_p[j*3+1]; - float b = buf_p[j*3+2]; - float intensity; - float out[3] = {0.0, 0.0, 0.0}; - - intensity = 0.33f * r + 0.685f * g + 0.063f * b; - - if ( intensity > 255 ) - intensity = 1.0f; - else - intensity /= 255.0f; - - if ( intensity > maxIntensity ) - maxIntensity = intensity; - - HSVtoRGB( intensity, 1.00, 0.50, out ); - - image[j*4+0] = out[0] * 255; - image[j*4+1] = out[1] * 255; - image[j*4+2] = out[2] * 255; - image[j*4+3] = 255; - - sumIntensity += intensity; - } - } else { - for ( j = 0 ; j < LIGHTMAP_SIZE * LIGHTMAP_SIZE; j++ ) { - R_ColorShiftLightingBytes( &buf_p[j*3], &image[j*4] ); - image[j*4+3] = 255; - } - } - tr.lightmaps[i] = R_CreateImage( va("*lightmap%d",i), image, - LIGHTMAP_SIZE, LIGHTMAP_SIZE, qfalse, qfalse, GL_CLAMP_TO_EDGE ); - } - - if ( r_lightmap->integer == 2 ) { - ri.Printf( PRINT_ALL, "Brightest lightmap value: %d\n", ( int ) ( maxIntensity * 255 ) ); - } -} - - -/* -================= -RE_SetWorldVisData - -This is called by the clipmodel subsystem so we can share the 1.8 megs of -space in big maps... -================= -*/ -void RE_SetWorldVisData( const byte *vis ) { - tr.externalVisData = vis; -} - - -/* -================= -R_LoadVisibility -================= -*/ -static void R_LoadVisibility( lump_t *l ) { - int len; - byte *buf; - - len = ( s_worldData.numClusters + 63 ) & ~63; - s_worldData.novis = ri.Hunk_Alloc( len, h_low ); - Com_Memset( s_worldData.novis, 0xff, len ); - - len = l->filelen; - if ( !len ) { - return; - } - buf = fileBase + l->fileofs; - - s_worldData.numClusters = LittleLong( ((int *)buf)[0] ); - s_worldData.clusterBytes = LittleLong( ((int *)buf)[1] ); - - // CM_Load should have given us the vis data to share, so - // we don't need to allocate another copy - if ( tr.externalVisData ) { - s_worldData.vis = tr.externalVisData; - } else { - byte *dest; - - dest = ri.Hunk_Alloc( len - 8, h_low ); - Com_Memcpy( dest, buf + 8, len - 8 ); - s_worldData.vis = dest; - } -} - -//=============================================================================== - - -/* -=============== -ShaderForShaderNum -=============== -*/ -static shader_t *ShaderForShaderNum( int shaderNum, int lightmapNum ) { - shader_t *shader; - dshader_t *dsh; - - int _shaderNum = LittleLong( shaderNum ); - if ( _shaderNum < 0 || _shaderNum >= s_worldData.numShaders ) { - ri.Error( ERR_DROP, "ShaderForShaderNum: bad num %i", _shaderNum ); - } - dsh = &s_worldData.shaders[ _shaderNum ]; - - if ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) { - lightmapNum = LIGHTMAP_BY_VERTEX; - } - - if ( r_fullbright->integer ) { - lightmapNum = LIGHTMAP_WHITEIMAGE; - } - - shader = R_FindShader( dsh->shader, lightmapNum, qtrue ); - - // if the shader had errors, just use default shader - if ( shader->defaultShader ) { - return tr.defaultShader; - } - - return shader; -} - -/* -=============== -ParseFace -=============== -*/ -static void ParseFace( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int *indexes ) { - int i, j; - srfSurfaceFace_t *cv; - int numPoints, numIndexes; - int lightmapNum; - int sfaceSize, ofsIndexes; - - lightmapNum = LittleLong( ds->lightmapNum ); - - // get fog volume - surf->fogIndex = LittleLong( ds->fogNum ) + 1; - - // get shader value - surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapNum ); - if ( r_singleShader->integer && !surf->shader->isSky ) { - surf->shader = tr.defaultShader; - } - - numPoints = LittleLong( ds->numVerts ); - if (numPoints > MAX_FACE_POINTS) { - ri.Printf( PRINT_WARNING, "WARNING: MAX_FACE_POINTS exceeded: %i\n", numPoints); - numPoints = MAX_FACE_POINTS; - surf->shader = tr.defaultShader; - } - - numIndexes = LittleLong( ds->numIndexes ); - - // create the srfSurfaceFace_t - sfaceSize = ( size_t ) &((srfSurfaceFace_t *)0)->points[numPoints]; - ofsIndexes = sfaceSize; - sfaceSize += sizeof( int ) * numIndexes; - - cv = ri.Hunk_Alloc( sfaceSize, h_low ); - cv->surfaceType = SF_FACE; - cv->numPoints = numPoints; - cv->numIndices = numIndexes; - cv->ofsIndices = ofsIndexes; - - verts += LittleLong( ds->firstVert ); - for ( i = 0 ; i < numPoints ; i++ ) { - for ( j = 0 ; j < 3 ; j++ ) { - cv->points[i][j] = LittleFloat( verts[i].xyz[j] ); - } - for ( j = 0 ; j < 2 ; j++ ) { - cv->points[i][3+j] = LittleFloat( verts[i].st[j] ); - cv->points[i][5+j] = LittleFloat( verts[i].lightmap[j] ); - } - R_ColorShiftLightingBytes( verts[i].color, (byte *)&cv->points[i][7] ); - } - - indexes += LittleLong( ds->firstIndex ); - for ( i = 0 ; i < numIndexes ; i++ ) { - ((int *)((byte *)cv + cv->ofsIndices ))[i] = LittleLong( indexes[ i ] ); - } - - // take the plane information from the lightmap vector - for ( i = 0 ; i < 3 ; i++ ) { - cv->plane.normal[i] = LittleFloat( ds->lightmapVecs[2][i] ); - } - cv->plane.dist = DotProduct( cv->points[0], cv->plane.normal ); - SetPlaneSignbits( &cv->plane ); - cv->plane.type = PlaneTypeForNormal( cv->plane.normal ); - - surf->data = (surfaceType_t *)cv; -} - - -/* -=============== -ParseMesh -=============== -*/ -static void ParseMesh ( dsurface_t *ds, drawVert_t *verts, msurface_t *surf ) { - srfGridMesh_t *grid; - int i, j; - int width, height, numPoints; - drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE]; - int lightmapNum; - vec3_t bounds[2]; - vec3_t tmpVec; - static surfaceType_t skipData = SF_SKIP; - - lightmapNum = LittleLong( ds->lightmapNum ); - - // get fog volume - surf->fogIndex = LittleLong( ds->fogNum ) + 1; - - // get shader value - surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapNum ); - if ( r_singleShader->integer && !surf->shader->isSky ) { - surf->shader = tr.defaultShader; - } - - // we may have a nodraw surface, because they might still need to - // be around for movement clipping - if ( s_worldData.shaders[ LittleLong( ds->shaderNum ) ].surfaceFlags & SURF_NODRAW ) { - surf->data = &skipData; - return; - } - - width = LittleLong( ds->patchWidth ); - height = LittleLong( ds->patchHeight ); - - verts += LittleLong( ds->firstVert ); - numPoints = width * height; - for ( i = 0 ; i < numPoints ; i++ ) { - for ( j = 0 ; j < 3 ; j++ ) { - points[i].xyz[j] = LittleFloat( verts[i].xyz[j] ); - points[i].normal[j] = LittleFloat( verts[i].normal[j] ); - } - for ( j = 0 ; j < 2 ; j++ ) { - points[i].st[j] = LittleFloat( verts[i].st[j] ); - points[i].lightmap[j] = LittleFloat( verts[i].lightmap[j] ); - } - R_ColorShiftLightingBytes( verts[i].color, points[i].color ); - } - - // pre-tesseleate - grid = R_SubdividePatchToGrid( width, height, points ); - surf->data = (surfaceType_t *)grid; - - // copy the level of detail origin, which is the center - // of the group of all curves that must subdivide the same - // to avoid cracking - for ( i = 0 ; i < 3 ; i++ ) { - bounds[0][i] = LittleFloat( ds->lightmapVecs[0][i] ); - bounds[1][i] = LittleFloat( ds->lightmapVecs[1][i] ); - } - VectorAdd( bounds[0], bounds[1], bounds[1] ); - VectorScale( bounds[1], 0.5f, grid->lodOrigin ); - VectorSubtract( bounds[0], grid->lodOrigin, tmpVec ); - grid->lodRadius = VectorLength( tmpVec ); -} - -/* -=============== -ParseTriSurf -=============== -*/ -static void ParseTriSurf( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int *indexes ) { - srfTriangles_t *tri; - int i, j; - int numVerts, numIndexes; - - // get fog volume - surf->fogIndex = LittleLong( ds->fogNum ) + 1; - - // get shader - surf->shader = ShaderForShaderNum( ds->shaderNum, LIGHTMAP_BY_VERTEX ); - if ( r_singleShader->integer && !surf->shader->isSky ) { - surf->shader = tr.defaultShader; - } - - numVerts = LittleLong( ds->numVerts ); - numIndexes = LittleLong( ds->numIndexes ); - - tri = ri.Hunk_Alloc( sizeof( *tri ) + numVerts * sizeof( tri->verts[0] ) - + numIndexes * sizeof( tri->indexes[0] ), h_low ); - tri->surfaceType = SF_TRIANGLES; - tri->numVerts = numVerts; - tri->numIndexes = numIndexes; - tri->verts = (drawVert_t *)(tri + 1); - tri->indexes = (int *)(tri->verts + tri->numVerts ); - - surf->data = (surfaceType_t *)tri; - - // copy vertexes - ClearBounds( tri->bounds[0], tri->bounds[1] ); - verts += LittleLong( ds->firstVert ); - for ( i = 0 ; i < numVerts ; i++ ) { - for ( j = 0 ; j < 3 ; j++ ) { - tri->verts[i].xyz[j] = LittleFloat( verts[i].xyz[j] ); - tri->verts[i].normal[j] = LittleFloat( verts[i].normal[j] ); - } - AddPointToBounds( tri->verts[i].xyz, tri->bounds[0], tri->bounds[1] ); - for ( j = 0 ; j < 2 ; j++ ) { - tri->verts[i].st[j] = LittleFloat( verts[i].st[j] ); - tri->verts[i].lightmap[j] = LittleFloat( verts[i].lightmap[j] ); - } - - R_ColorShiftLightingBytes( verts[i].color, tri->verts[i].color ); - } - - // copy indexes - indexes += LittleLong( ds->firstIndex ); - for ( i = 0 ; i < numIndexes ; i++ ) { - tri->indexes[i] = LittleLong( indexes[i] ); - if ( tri->indexes[i] < 0 || tri->indexes[i] >= numVerts ) { - ri.Error( ERR_DROP, "Bad index in triangle surface" ); - } - } -} - -/* -=============== -ParseFlare -=============== -*/ -static void ParseFlare( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int *indexes ) { - srfFlare_t *flare; - int i; - - // get fog volume - surf->fogIndex = LittleLong( ds->fogNum ) + 1; - - // get shader - surf->shader = ShaderForShaderNum( ds->shaderNum, LIGHTMAP_BY_VERTEX ); - if ( r_singleShader->integer && !surf->shader->isSky ) { - surf->shader = tr.defaultShader; - } - - flare = ri.Hunk_Alloc( sizeof( *flare ), h_low ); - flare->surfaceType = SF_FLARE; - - surf->data = (surfaceType_t *)flare; - - for ( i = 0 ; i < 3 ; i++ ) { - flare->origin[i] = LittleFloat( ds->lightmapOrigin[i] ); - flare->color[i] = LittleFloat( ds->lightmapVecs[0][i] ); - flare->normal[i] = LittleFloat( ds->lightmapVecs[2][i] ); - } -} - - -/* -================= -R_MergedWidthPoints - -returns true if there are grid points merged on a width edge -================= -*/ -int R_MergedWidthPoints(srfGridMesh_t *grid, int offset) { - int i, j; - - for (i = 1; i < grid->width-1; i++) { - for (j = i + 1; j < grid->width-1; j++) { - if ( fabs(grid->verts[i + offset].xyz[0] - grid->verts[j + offset].xyz[0]) > .1) continue; - if ( fabs(grid->verts[i + offset].xyz[1] - grid->verts[j + offset].xyz[1]) > .1) continue; - if ( fabs(grid->verts[i + offset].xyz[2] - grid->verts[j + offset].xyz[2]) > .1) continue; - return qtrue; - } - } - return qfalse; -} - -/* -================= -R_MergedHeightPoints - -returns true if there are grid points merged on a height edge -================= -*/ -int R_MergedHeightPoints(srfGridMesh_t *grid, int offset) { - int i, j; - - for (i = 1; i < grid->height-1; i++) { - for (j = i + 1; j < grid->height-1; j++) { - if ( fabs(grid->verts[grid->width * i + offset].xyz[0] - grid->verts[grid->width * j + offset].xyz[0]) > .1) continue; - if ( fabs(grid->verts[grid->width * i + offset].xyz[1] - grid->verts[grid->width * j + offset].xyz[1]) > .1) continue; - if ( fabs(grid->verts[grid->width * i + offset].xyz[2] - grid->verts[grid->width * j + offset].xyz[2]) > .1) continue; - return qtrue; - } - } - return qfalse; -} - -/* -================= -R_FixSharedVertexLodError_r - -NOTE: never sync LoD through grid edges with merged points! - -FIXME: write generalized version that also avoids cracks between a patch and one that meets half way? -================= -*/ -void R_FixSharedVertexLodError_r( int start, srfGridMesh_t *grid1 ) { - int j, k, l, m, n, offset1, offset2, touch; - srfGridMesh_t *grid2; - - for ( j = start; j < s_worldData.numsurfaces; j++ ) { - // - grid2 = (srfGridMesh_t *) s_worldData.surfaces[j].data; - // if this surface is not a grid - if ( grid2->surfaceType != SF_GRID ) continue; - // if the LOD errors are already fixed for this patch - if ( grid2->lodFixed == 2 ) continue; - // grids in the same LOD group should have the exact same lod radius - if ( grid1->lodRadius != grid2->lodRadius ) continue; - // grids in the same LOD group should have the exact same lod origin - if ( grid1->lodOrigin[0] != grid2->lodOrigin[0] ) continue; - if ( grid1->lodOrigin[1] != grid2->lodOrigin[1] ) continue; - if ( grid1->lodOrigin[2] != grid2->lodOrigin[2] ) continue; - // - touch = qfalse; - for (n = 0; n < 2; n++) { - // - if (n) offset1 = (grid1->height-1) * grid1->width; - else offset1 = 0; - if (R_MergedWidthPoints(grid1, offset1)) continue; - for (k = 1; k < grid1->width-1; k++) { - for (m = 0; m < 2; m++) { - - if (m) offset2 = (grid2->height-1) * grid2->width; - else offset2 = 0; - if (R_MergedWidthPoints(grid2, offset2)) continue; - for ( l = 1; l < grid2->width-1; l++) { - // - if ( fabs(grid1->verts[k + offset1].xyz[0] - grid2->verts[l + offset2].xyz[0]) > .1) continue; - if ( fabs(grid1->verts[k + offset1].xyz[1] - grid2->verts[l + offset2].xyz[1]) > .1) continue; - if ( fabs(grid1->verts[k + offset1].xyz[2] - grid2->verts[l + offset2].xyz[2]) > .1) continue; - // ok the points are equal and should have the same lod error - grid2->widthLodError[l] = grid1->widthLodError[k]; - touch = qtrue; - } - } - for (m = 0; m < 2; m++) { - - if (m) offset2 = grid2->width-1; - else offset2 = 0; - if (R_MergedHeightPoints(grid2, offset2)) continue; - for ( l = 1; l < grid2->height-1; l++) { - // - if ( fabs(grid1->verts[k + offset1].xyz[0] - grid2->verts[grid2->width * l + offset2].xyz[0]) > .1) continue; - if ( fabs(grid1->verts[k + offset1].xyz[1] - grid2->verts[grid2->width * l + offset2].xyz[1]) > .1) continue; - if ( fabs(grid1->verts[k + offset1].xyz[2] - grid2->verts[grid2->width * l + offset2].xyz[2]) > .1) continue; - // ok the points are equal and should have the same lod error - grid2->heightLodError[l] = grid1->widthLodError[k]; - touch = qtrue; - } - } - } - } - for (n = 0; n < 2; n++) { - // - if (n) offset1 = grid1->width-1; - else offset1 = 0; - if (R_MergedHeightPoints(grid1, offset1)) continue; - for (k = 1; k < grid1->height-1; k++) { - for (m = 0; m < 2; m++) { - - if (m) offset2 = (grid2->height-1) * grid2->width; - else offset2 = 0; - if (R_MergedWidthPoints(grid2, offset2)) continue; - for ( l = 1; l < grid2->width-1; l++) { - // - if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[0] - grid2->verts[l + offset2].xyz[0]) > .1) continue; - if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[1] - grid2->verts[l + offset2].xyz[1]) > .1) continue; - if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[2] - grid2->verts[l + offset2].xyz[2]) > .1) continue; - // ok the points are equal and should have the same lod error - grid2->widthLodError[l] = grid1->heightLodError[k]; - touch = qtrue; - } - } - for (m = 0; m < 2; m++) { - - if (m) offset2 = grid2->width-1; - else offset2 = 0; - if (R_MergedHeightPoints(grid2, offset2)) continue; - for ( l = 1; l < grid2->height-1; l++) { - // - if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[0] - grid2->verts[grid2->width * l + offset2].xyz[0]) > .1) continue; - if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[1] - grid2->verts[grid2->width * l + offset2].xyz[1]) > .1) continue; - if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[2] - grid2->verts[grid2->width * l + offset2].xyz[2]) > .1) continue; - // ok the points are equal and should have the same lod error - grid2->heightLodError[l] = grid1->heightLodError[k]; - touch = qtrue; - } - } - } - } - if (touch) { - grid2->lodFixed = 2; - R_FixSharedVertexLodError_r ( start, grid2 ); - //NOTE: this would be correct but makes things really slow - //grid2->lodFixed = 1; - } - } -} - -/* -================= -R_FixSharedVertexLodError - -This function assumes that all patches in one group are nicely stitched together for the highest LoD. -If this is not the case this function will still do its job but won't fix the highest LoD cracks. -================= -*/ -void R_FixSharedVertexLodError( void ) { - int i; - srfGridMesh_t *grid1; - - for ( i = 0; i < s_worldData.numsurfaces; i++ ) { - // - grid1 = (srfGridMesh_t *) s_worldData.surfaces[i].data; - // if this surface is not a grid - if ( grid1->surfaceType != SF_GRID ) - continue; - // - if ( grid1->lodFixed ) - continue; - // - grid1->lodFixed = 2; - // recursively fix other patches in the same LOD group - R_FixSharedVertexLodError_r( i + 1, grid1); - } -} - - -/* -=============== -R_StitchPatches -=============== -*/ -int R_StitchPatches( int grid1num, int grid2num ) { - float *v1, *v2; - srfGridMesh_t *grid1, *grid2; - int k, l, m, n, offset1, offset2, row, column; - - grid1 = (srfGridMesh_t *) s_worldData.surfaces[grid1num].data; - grid2 = (srfGridMesh_t *) s_worldData.surfaces[grid2num].data; - for (n = 0; n < 2; n++) { - // - if (n) offset1 = (grid1->height-1) * grid1->width; - else offset1 = 0; - if (R_MergedWidthPoints(grid1, offset1)) - continue; - for (k = 0; k < grid1->width-2; k += 2) { - - for (m = 0; m < 2; m++) { - - if ( grid2->width >= MAX_GRID_SIZE ) - break; - if (m) offset2 = (grid2->height-1) * grid2->width; - else offset2 = 0; - for ( l = 0; l < grid2->width-1; l++) { - // - v1 = grid1->verts[k + offset1].xyz; - v2 = grid2->verts[l + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - - v1 = grid1->verts[k + 2 + offset1].xyz; - v2 = grid2->verts[l + 1 + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - // - v1 = grid2->verts[l + offset2].xyz; - v2 = grid2->verts[l + 1 + offset2].xyz; - if ( fabs(v1[0] - v2[0]) < .01 && - fabs(v1[1] - v2[1]) < .01 && - fabs(v1[2] - v2[2]) < .01) - continue; - // - //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); - // insert column into grid2 right after after column l - if (m) row = grid2->height-1; - else row = 0; - grid2 = R_GridInsertColumn( grid2, l+1, row, - grid1->verts[k + 1 + offset1].xyz, grid1->widthLodError[k+1]); - grid2->lodStitched = qfalse; - s_worldData.surfaces[grid2num].data = (void *) grid2; - return qtrue; - } - } - for (m = 0; m < 2; m++) { - - if (grid2->height >= MAX_GRID_SIZE) - break; - if (m) offset2 = grid2->width-1; - else offset2 = 0; - for ( l = 0; l < grid2->height-1; l++) { - // - v1 = grid1->verts[k + offset1].xyz; - v2 = grid2->verts[grid2->width * l + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - - v1 = grid1->verts[k + 2 + offset1].xyz; - v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - // - v1 = grid2->verts[grid2->width * l + offset2].xyz; - v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) < .01 && - fabs(v1[1] - v2[1]) < .01 && - fabs(v1[2] - v2[2]) < .01) - continue; - // - //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); - // insert row into grid2 right after after row l - if (m) column = grid2->width-1; - else column = 0; - grid2 = R_GridInsertRow( grid2, l+1, column, - grid1->verts[k + 1 + offset1].xyz, grid1->widthLodError[k+1]); - grid2->lodStitched = qfalse; - s_worldData.surfaces[grid2num].data = (void *) grid2; - return qtrue; - } - } - } - } - for (n = 0; n < 2; n++) { - // - if (n) offset1 = grid1->width-1; - else offset1 = 0; - if (R_MergedHeightPoints(grid1, offset1)) - continue; - for (k = 0; k < grid1->height-2; k += 2) { - for (m = 0; m < 2; m++) { - - if ( grid2->width >= MAX_GRID_SIZE ) - break; - if (m) offset2 = (grid2->height-1) * grid2->width; - else offset2 = 0; - for ( l = 0; l < grid2->width-1; l++) { - // - v1 = grid1->verts[grid1->width * k + offset1].xyz; - v2 = grid2->verts[l + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - - v1 = grid1->verts[grid1->width * (k + 2) + offset1].xyz; - v2 = grid2->verts[l + 1 + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - // - v1 = grid2->verts[l + offset2].xyz; - v2 = grid2->verts[(l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) < .01 && - fabs(v1[1] - v2[1]) < .01 && - fabs(v1[2] - v2[2]) < .01) - continue; - // - //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); - // insert column into grid2 right after after column l - if (m) row = grid2->height-1; - else row = 0; - grid2 = R_GridInsertColumn( grid2, l+1, row, - grid1->verts[grid1->width * (k + 1) + offset1].xyz, grid1->heightLodError[k+1]); - grid2->lodStitched = qfalse; - s_worldData.surfaces[grid2num].data = (void *) grid2; - return qtrue; - } - } - for (m = 0; m < 2; m++) { - - if (grid2->height >= MAX_GRID_SIZE) - break; - if (m) offset2 = grid2->width-1; - else offset2 = 0; - for ( l = 0; l < grid2->height-1; l++) { - // - v1 = grid1->verts[grid1->width * k + offset1].xyz; - v2 = grid2->verts[grid2->width * l + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - - v1 = grid1->verts[grid1->width * (k + 2) + offset1].xyz; - v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - // - v1 = grid2->verts[grid2->width * l + offset2].xyz; - v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) < .01 && - fabs(v1[1] - v2[1]) < .01 && - fabs(v1[2] - v2[2]) < .01) - continue; - // - //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); - // insert row into grid2 right after after row l - if (m) column = grid2->width-1; - else column = 0; - grid2 = R_GridInsertRow( grid2, l+1, column, - grid1->verts[grid1->width * (k + 1) + offset1].xyz, grid1->heightLodError[k+1]); - grid2->lodStitched = qfalse; - s_worldData.surfaces[grid2num].data = (void *) grid2; - return qtrue; - } - } - } - } - for (n = 0; n < 2; n++) { - // - if (n) offset1 = (grid1->height-1) * grid1->width; - else offset1 = 0; - if (R_MergedWidthPoints(grid1, offset1)) - continue; - for (k = grid1->width-1; k > 1; k -= 2) { - - for (m = 0; m < 2; m++) { - - if ( grid2->width >= MAX_GRID_SIZE ) - break; - if (m) offset2 = (grid2->height-1) * grid2->width; - else offset2 = 0; - for ( l = 0; l < grid2->width-1; l++) { - // - v1 = grid1->verts[k + offset1].xyz; - v2 = grid2->verts[l + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - - v1 = grid1->verts[k - 2 + offset1].xyz; - v2 = grid2->verts[l + 1 + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - // - v1 = grid2->verts[l + offset2].xyz; - v2 = grid2->verts[(l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) < .01 && - fabs(v1[1] - v2[1]) < .01 && - fabs(v1[2] - v2[2]) < .01) - continue; - // - //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); - // insert column into grid2 right after after column l - if (m) row = grid2->height-1; - else row = 0; - grid2 = R_GridInsertColumn( grid2, l+1, row, - grid1->verts[k - 1 + offset1].xyz, grid1->widthLodError[k+1]); - grid2->lodStitched = qfalse; - s_worldData.surfaces[grid2num].data = (void *) grid2; - return qtrue; - } - } - for (m = 0; m < 2; m++) { - - if (grid2->height >= MAX_GRID_SIZE) - break; - if (m) offset2 = grid2->width-1; - else offset2 = 0; - for ( l = 0; l < grid2->height-1; l++) { - // - v1 = grid1->verts[k + offset1].xyz; - v2 = grid2->verts[grid2->width * l + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - - v1 = grid1->verts[k - 2 + offset1].xyz; - v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - // - v1 = grid2->verts[grid2->width * l + offset2].xyz; - v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) < .01 && - fabs(v1[1] - v2[1]) < .01 && - fabs(v1[2] - v2[2]) < .01) - continue; - // - //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); - // insert row into grid2 right after after row l - if (m) column = grid2->width-1; - else column = 0; - grid2 = R_GridInsertRow( grid2, l+1, column, - grid1->verts[k - 1 + offset1].xyz, grid1->widthLodError[k+1]); - if (!grid2) - break; - grid2->lodStitched = qfalse; - s_worldData.surfaces[grid2num].data = (void *) grid2; - return qtrue; - } - } - } - } - for (n = 0; n < 2; n++) { - // - if (n) offset1 = grid1->width-1; - else offset1 = 0; - if (R_MergedHeightPoints(grid1, offset1)) - continue; - for (k = grid1->height-1; k > 1; k -= 2) { - for (m = 0; m < 2; m++) { - - if ( grid2->width >= MAX_GRID_SIZE ) - break; - if (m) offset2 = (grid2->height-1) * grid2->width; - else offset2 = 0; - for ( l = 0; l < grid2->width-1; l++) { - // - v1 = grid1->verts[grid1->width * k + offset1].xyz; - v2 = grid2->verts[l + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - - v1 = grid1->verts[grid1->width * (k - 2) + offset1].xyz; - v2 = grid2->verts[l + 1 + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - // - v1 = grid2->verts[l + offset2].xyz; - v2 = grid2->verts[(l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) < .01 && - fabs(v1[1] - v2[1]) < .01 && - fabs(v1[2] - v2[2]) < .01) - continue; - // - //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); - // insert column into grid2 right after after column l - if (m) row = grid2->height-1; - else row = 0; - grid2 = R_GridInsertColumn( grid2, l+1, row, - grid1->verts[grid1->width * (k - 1) + offset1].xyz, grid1->heightLodError[k+1]); - grid2->lodStitched = qfalse; - s_worldData.surfaces[grid2num].data = (void *) grid2; - return qtrue; - } - } - for (m = 0; m < 2; m++) { - - if (grid2->height >= MAX_GRID_SIZE) - break; - if (m) offset2 = grid2->width-1; - else offset2 = 0; - for ( l = 0; l < grid2->height-1; l++) { - // - v1 = grid1->verts[grid1->width * k + offset1].xyz; - v2 = grid2->verts[grid2->width * l + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - - v1 = grid1->verts[grid1->width * (k - 2) + offset1].xyz; - v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) > .1) - continue; - if ( fabs(v1[1] - v2[1]) > .1) - continue; - if ( fabs(v1[2] - v2[2]) > .1) - continue; - // - v1 = grid2->verts[grid2->width * l + offset2].xyz; - v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; - if ( fabs(v1[0] - v2[0]) < .01 && - fabs(v1[1] - v2[1]) < .01 && - fabs(v1[2] - v2[2]) < .01) - continue; - // - //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); - // insert row into grid2 right after after row l - if (m) column = grid2->width-1; - else column = 0; - grid2 = R_GridInsertRow( grid2, l+1, column, - grid1->verts[grid1->width * (k - 1) + offset1].xyz, grid1->heightLodError[k+1]); - grid2->lodStitched = qfalse; - s_worldData.surfaces[grid2num].data = (void *) grid2; - return qtrue; - } - } - } - } - return qfalse; -} - -/* -=============== -R_TryStitchPatch - -This function will try to stitch patches in the same LoD group together for the highest LoD. - -Only single missing vertice cracks will be fixed. - -Vertices will be joined at the patch side a crack is first found, at the other side -of the patch (on the same row or column) the vertices will not be joined and cracks -might still appear at that side. -=============== -*/ -int R_TryStitchingPatch( int grid1num ) { - int j, numstitches; - srfGridMesh_t *grid1, *grid2; - - numstitches = 0; - grid1 = (srfGridMesh_t *) s_worldData.surfaces[grid1num].data; - for ( j = 0; j < s_worldData.numsurfaces; j++ ) { - // - grid2 = (srfGridMesh_t *) s_worldData.surfaces[j].data; - // if this surface is not a grid - if ( grid2->surfaceType != SF_GRID ) continue; - // grids in the same LOD group should have the exact same lod radius - if ( grid1->lodRadius != grid2->lodRadius ) continue; - // grids in the same LOD group should have the exact same lod origin - if ( grid1->lodOrigin[0] != grid2->lodOrigin[0] ) continue; - if ( grid1->lodOrigin[1] != grid2->lodOrigin[1] ) continue; - if ( grid1->lodOrigin[2] != grid2->lodOrigin[2] ) continue; - // - while (R_StitchPatches(grid1num, j)) - { - numstitches++; - } - } - return numstitches; -} - -/* -=============== -R_StitchAllPatches -=============== -*/ -void R_StitchAllPatches( void ) { - int i, stitched, numstitches; - srfGridMesh_t *grid1; - - numstitches = 0; - do - { - stitched = qfalse; - for ( i = 0; i < s_worldData.numsurfaces; i++ ) { - // - grid1 = (srfGridMesh_t *) s_worldData.surfaces[i].data; - // if this surface is not a grid - if ( grid1->surfaceType != SF_GRID ) - continue; - // - if ( grid1->lodStitched ) - continue; - // - grid1->lodStitched = qtrue; - stitched = qtrue; - // - numstitches += R_TryStitchingPatch( i ); - } - } - while (stitched); - ri.Printf( PRINT_ALL, "stitched %d LoD cracks\n", numstitches ); -} - -/* -=============== -R_MovePatchSurfacesToHunk -=============== -*/ -void R_MovePatchSurfacesToHunk(void) { - int i, size; - srfGridMesh_t *grid, *hunkgrid; - - for ( i = 0; i < s_worldData.numsurfaces; i++ ) { - // - grid = (srfGridMesh_t *) s_worldData.surfaces[i].data; - // if this surface is not a grid - if ( grid->surfaceType != SF_GRID ) - continue; - // - size = (grid->width * grid->height - 1) * sizeof( drawVert_t ) + sizeof( *grid ); - hunkgrid = ri.Hunk_Alloc( size, h_low ); - Com_Memcpy(hunkgrid, grid, size); - - hunkgrid->widthLodError = ri.Hunk_Alloc( grid->width * 4, h_low ); - Com_Memcpy( hunkgrid->widthLodError, grid->widthLodError, grid->width * 4 ); - - hunkgrid->heightLodError = ri.Hunk_Alloc( grid->height * 4, h_low ); - Com_Memcpy( hunkgrid->heightLodError, grid->heightLodError, grid->height * 4 ); - - R_FreeSurfaceGridMesh( grid ); - - s_worldData.surfaces[i].data = (void *) hunkgrid; - } -} - -/* -=============== -R_LoadSurfaces -=============== -*/ -static void R_LoadSurfaces( lump_t *surfs, lump_t *verts, lump_t *indexLump ) { - dsurface_t *in; - msurface_t *out; - drawVert_t *dv; - int *indexes; - int count; - int numFaces, numMeshes, numTriSurfs, numFlares; - int i; - - numFaces = 0; - numMeshes = 0; - numTriSurfs = 0; - numFlares = 0; - - in = (void *)(fileBase + surfs->fileofs); - if (surfs->filelen % sizeof(*in)) - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - count = surfs->filelen / sizeof(*in); - - dv = (void *)(fileBase + verts->fileofs); - if (verts->filelen % sizeof(*dv)) - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - - indexes = (void *)(fileBase + indexLump->fileofs); - if ( indexLump->filelen % sizeof(*indexes)) - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - - out = ri.Hunk_Alloc ( count * sizeof(*out), h_low ); - - s_worldData.surfaces = out; - s_worldData.numsurfaces = count; - - for ( i = 0 ; i < count ; i++, in++, out++ ) { - switch ( LittleLong( in->surfaceType ) ) { - case MST_PATCH: - ParseMesh ( in, dv, out ); - numMeshes++; - break; - case MST_TRIANGLE_SOUP: - ParseTriSurf( in, dv, out, indexes ); - numTriSurfs++; - break; - case MST_PLANAR: - ParseFace( in, dv, out, indexes ); - numFaces++; - break; - case MST_FLARE: - ParseFlare( in, dv, out, indexes ); - numFlares++; - break; - default: - ri.Error( ERR_DROP, "Bad surfaceType" ); - } - } - -#ifdef PATCH_STITCHING - R_StitchAllPatches(); -#endif - - R_FixSharedVertexLodError(); - -#ifdef PATCH_STITCHING - R_MovePatchSurfacesToHunk(); -#endif - - ri.Printf( PRINT_ALL, "...loaded %d faces, %i meshes, %i trisurfs, %i flares\n", - numFaces, numMeshes, numTriSurfs, numFlares ); -} - - - -/* -================= -R_LoadSubmodels -================= -*/ -static void R_LoadSubmodels( lump_t *l ) { - dmodel_t *in; - bmodel_t *out; - int i, j, count; - - in = (void *)(fileBase + l->fileofs); - if (l->filelen % sizeof(*in)) - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - count = l->filelen / sizeof(*in); - - s_worldData.bmodels = out = ri.Hunk_Alloc( count * sizeof(*out), h_low ); - - for ( i=0 ; itype = MOD_BRUSH; - model->bmodel = out; - Com_sprintf( model->name, sizeof( model->name ), "*%d", i ); - - for (j=0 ; j<3 ; j++) { - out->bounds[0][j] = LittleFloat (in->mins[j]); - out->bounds[1][j] = LittleFloat (in->maxs[j]); - } - - out->firstSurface = s_worldData.surfaces + LittleLong( in->firstSurface ); - out->numSurfaces = LittleLong( in->numSurfaces ); - } -} - - - -//================================================================== - -/* -================= -R_SetParent -================= -*/ -static void R_SetParent (mnode_t *node, mnode_t *parent) -{ - node->parent = parent; - if (node->contents != -1) - return; - R_SetParent (node->children[0], node); - R_SetParent (node->children[1], node); -} - -/* -================= -R_LoadNodesAndLeafs -================= -*/ -static void R_LoadNodesAndLeafs (lump_t *nodeLump, lump_t *leafLump) { - int i, j, p; - dnode_t *in; - dleaf_t *inLeaf; - mnode_t *out; - int numNodes, numLeafs; - - in = (void *)(fileBase + nodeLump->fileofs); - if (nodeLump->filelen % sizeof(dnode_t) || - leafLump->filelen % sizeof(dleaf_t) ) { - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - } - numNodes = nodeLump->filelen / sizeof(dnode_t); - numLeafs = leafLump->filelen / sizeof(dleaf_t); - - out = ri.Hunk_Alloc ( (numNodes + numLeafs) * sizeof(*out), h_low); - - s_worldData.nodes = out; - s_worldData.numnodes = numNodes + numLeafs; - s_worldData.numDecisionNodes = numNodes; - - // load nodes - for ( i=0 ; imins[j] = LittleLong (in->mins[j]); - out->maxs[j] = LittleLong (in->maxs[j]); - } - - p = LittleLong(in->planeNum); - out->plane = s_worldData.planes + p; - - out->contents = CONTENTS_NODE; // differentiate from leafs - - for (j=0 ; j<2 ; j++) - { - p = LittleLong (in->children[j]); - if (p >= 0) - out->children[j] = s_worldData.nodes + p; - else - out->children[j] = s_worldData.nodes + numNodes + (-1 - p); - } - } - - // load leafs - inLeaf = (void *)(fileBase + leafLump->fileofs); - for ( i=0 ; imins[j] = LittleLong (inLeaf->mins[j]); - out->maxs[j] = LittleLong (inLeaf->maxs[j]); - } - - out->cluster = LittleLong(inLeaf->cluster); - out->area = LittleLong(inLeaf->area); - - if ( out->cluster >= s_worldData.numClusters ) { - s_worldData.numClusters = out->cluster + 1; - } - - out->firstmarksurface = s_worldData.marksurfaces + - LittleLong(inLeaf->firstLeafSurface); - out->nummarksurfaces = LittleLong(inLeaf->numLeafSurfaces); - } - - // chain decendants - R_SetParent (s_worldData.nodes, NULL); -} - -//============================================================================= - -/* -================= -R_LoadShaders -================= -*/ -static void R_LoadShaders( lump_t *l ) { - int i, count; - dshader_t *in, *out; - - in = (void *)(fileBase + l->fileofs); - if (l->filelen % sizeof(*in)) - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - count = l->filelen / sizeof(*in); - out = ri.Hunk_Alloc ( count*sizeof(*out), h_low ); - - s_worldData.shaders = out; - s_worldData.numShaders = count; - - Com_Memcpy( out, in, count*sizeof(*out) ); - - for ( i=0 ; ifileofs); - if (l->filelen % sizeof(*in)) - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - count = l->filelen / sizeof(*in); - out = ri.Hunk_Alloc ( count*sizeof(*out), h_low); - - s_worldData.marksurfaces = out; - s_worldData.nummarksurfaces = count; - - for ( i=0 ; ifileofs); - if (l->filelen % sizeof(*in)) - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - count = l->filelen / sizeof(*in); - out = ri.Hunk_Alloc ( count*2*sizeof(*out), h_low); - - s_worldData.planes = out; - s_worldData.numplanes = count; - - for ( i=0 ; inormal[j] = LittleFloat (in->normal[j]); - if (out->normal[j] < 0) { - bits |= 1<dist = LittleFloat (in->dist); - out->type = PlaneTypeForNormal( out->normal ); - out->signbits = bits; - } -} - -/* -================= -R_LoadFogs - -================= -*/ -static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) { - int i; - fog_t *out; - dfog_t *fogs; - dbrush_t *brushes, *brush; - dbrushside_t *sides; - int count, brushesCount, sidesCount; - int sideNum; - int planeNum; - shader_t *shader; - float d; - int firstSide; - - fogs = (void *)(fileBase + l->fileofs); - if (l->filelen % sizeof(*fogs)) { - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - } - count = l->filelen / sizeof(*fogs); - - // create fog strucutres for them - s_worldData.numfogs = count + 1; - s_worldData.fogs = ri.Hunk_Alloc ( s_worldData.numfogs*sizeof(*out), h_low); - out = s_worldData.fogs + 1; - - if ( !count ) { - return; - } - - brushes = (void *)(fileBase + brushesLump->fileofs); - if (brushesLump->filelen % sizeof(*brushes)) { - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - } - brushesCount = brushesLump->filelen / sizeof(*brushes); - - sides = (void *)(fileBase + sidesLump->fileofs); - if (sidesLump->filelen % sizeof(*sides)) { - ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); - } - sidesCount = sidesLump->filelen / sizeof(*sides); - - for ( i=0 ; ioriginalBrushNumber = LittleLong( fogs->brushNum ); - - if ( (unsigned)out->originalBrushNumber >= brushesCount ) { - ri.Error( ERR_DROP, "fog brushNumber out of range" ); - } - brush = brushes + out->originalBrushNumber; - - firstSide = LittleLong( brush->firstSide ); - - if ( (unsigned)firstSide > sidesCount - 6 ) { - ri.Error( ERR_DROP, "fog brush sideNumber out of range" ); - } - - // brushes are always sorted with the axial sides first - sideNum = firstSide + 0; - planeNum = LittleLong( sides[ sideNum ].planeNum ); - out->bounds[0][0] = -s_worldData.planes[ planeNum ].dist; - - sideNum = firstSide + 1; - planeNum = LittleLong( sides[ sideNum ].planeNum ); - out->bounds[1][0] = s_worldData.planes[ planeNum ].dist; - - sideNum = firstSide + 2; - planeNum = LittleLong( sides[ sideNum ].planeNum ); - out->bounds[0][1] = -s_worldData.planes[ planeNum ].dist; - - sideNum = firstSide + 3; - planeNum = LittleLong( sides[ sideNum ].planeNum ); - out->bounds[1][1] = s_worldData.planes[ planeNum ].dist; - - sideNum = firstSide + 4; - planeNum = LittleLong( sides[ sideNum ].planeNum ); - out->bounds[0][2] = -s_worldData.planes[ planeNum ].dist; - - sideNum = firstSide + 5; - planeNum = LittleLong( sides[ sideNum ].planeNum ); - out->bounds[1][2] = s_worldData.planes[ planeNum ].dist; - - // get information from the shader for fog parameters - shader = R_FindShader( fogs->shader, LIGHTMAP_NONE, qtrue ); - - out->parms = shader->fogParms; - - out->colorInt = ColorBytes4 ( shader->fogParms.color[0] * tr.identityLight, - shader->fogParms.color[1] * tr.identityLight, - shader->fogParms.color[2] * tr.identityLight, 1.0 ); - - d = shader->fogParms.depthForOpaque < 1 ? 1 : shader->fogParms.depthForOpaque; - out->tcScale = 1.0f / ( d * 8 ); - - // set the gradient vector - sideNum = LittleLong( fogs->visibleSide ); - - if ( sideNum == -1 ) { - out->hasSurface = qfalse; - } else { - out->hasSurface = qtrue; - planeNum = LittleLong( sides[ firstSide + sideNum ].planeNum ); - VectorSubtract( vec3_origin, s_worldData.planes[ planeNum ].normal, out->surface ); - out->surface[3] = -s_worldData.planes[ planeNum ].dist; - } - - out++; - } - -} - - -/* -================ -R_LoadLightGrid - -================ -*/ -void R_LoadLightGrid( lump_t *l ) { - int i; - vec3_t maxs; - int numGridPoints; - world_t *w; - float *wMins, *wMaxs; - - w = &s_worldData; - - w->lightGridInverseSize[0] = 1.0f / w->lightGridSize[0]; - w->lightGridInverseSize[1] = 1.0f / w->lightGridSize[1]; - w->lightGridInverseSize[2] = 1.0f / w->lightGridSize[2]; - - wMins = w->bmodels[0].bounds[0]; - wMaxs = w->bmodels[0].bounds[1]; - - for ( i = 0 ; i < 3 ; i++ ) { - w->lightGridOrigin[i] = w->lightGridSize[i] * ceil( wMins[i] / w->lightGridSize[i] ); - maxs[i] = w->lightGridSize[i] * floor( wMaxs[i] / w->lightGridSize[i] ); - w->lightGridBounds[i] = (maxs[i] - w->lightGridOrigin[i])/w->lightGridSize[i] + 1; - } - - numGridPoints = w->lightGridBounds[0] * w->lightGridBounds[1] * w->lightGridBounds[2]; - - if ( l->filelen != numGridPoints * 8 ) { - ri.Printf( PRINT_WARNING, "WARNING: light grid mismatch\n" ); - w->lightGridData = NULL; - return; - } - - w->lightGridData = ri.Hunk_Alloc( l->filelen, h_low ); - Com_Memcpy( w->lightGridData, (void *)(fileBase + l->fileofs), l->filelen ); - - // deal with overbright bits - for ( i = 0 ; i < numGridPoints ; i++ ) { - R_ColorShiftLightingBytes( &w->lightGridData[i*8], &w->lightGridData[i*8] ); - R_ColorShiftLightingBytes( &w->lightGridData[i*8+3], &w->lightGridData[i*8+3] ); - } -} - -/* -================ -R_LoadEntities -================ -*/ -void R_LoadEntities( lump_t *l ) { - char *p, *token, *s; - char keyname[MAX_TOKEN_CHARS]; - char value[MAX_TOKEN_CHARS]; - world_t *w; - - w = &s_worldData; - w->lightGridSize[0] = 64; - w->lightGridSize[1] = 64; - w->lightGridSize[2] = 128; - - p = (char *)(fileBase + l->fileofs); - - // store for reference by the cgame - w->entityString = ri.Hunk_Alloc( l->filelen + 1, h_low ); - strcpy( w->entityString, p ); - w->entityParsePoint = w->entityString; - - token = COM_ParseExt( &p, qtrue ); - if (!*token || *token != '{') { - return; - } - - // only parse the world spawn - while ( 1 ) { - // parse key - token = COM_ParseExt( &p, qtrue ); - - if ( !*token || *token == '}' ) { - break; - } - Q_strncpyz(keyname, token, sizeof(keyname)); - - // parse value - token = COM_ParseExt( &p, qtrue ); - - if ( !*token || *token == '}' ) { - break; - } - Q_strncpyz(value, token, sizeof(value)); - - // check for remapping of shaders for vertex lighting - s = "vertexremapshader"; - if (!Q_strncmp(keyname, s, strlen(s)) ) { - s = strchr(value, ';'); - if (!s) { - ri.Printf( PRINT_WARNING, "WARNING: no semi colon in vertexshaderremap '%s'\n", value ); - break; - } - *s++ = 0; - if (r_vertexLight->integer) { - R_RemapShader(value, s, "0"); - } - continue; - } - // check for remapping of shaders - s = "remapshader"; - if (!Q_strncmp(keyname, s, strlen(s)) ) { - s = strchr(value, ';'); - if (!s) { - ri.Printf( PRINT_WARNING, "WARNING: no semi colon in shaderremap '%s'\n", value ); - break; - } - *s++ = 0; - R_RemapShader(value, s, "0"); - continue; - } - // check for a different grid size - if (!Q_stricmp(keyname, "gridsize")) { - sscanf(value, "%f %f %f", &w->lightGridSize[0], &w->lightGridSize[1], &w->lightGridSize[2] ); - continue; - } - } -} - -/* -================= -R_GetEntityToken -================= -*/ -qboolean R_GetEntityToken( char *buffer, int size ) { - const char *s; - - s = COM_Parse( &s_worldData.entityParsePoint ); - Q_strncpyz( buffer, s, size ); - if ( !s_worldData.entityParsePoint || !s[0] ) { - s_worldData.entityParsePoint = s_worldData.entityString; - return qfalse; - } else { - return qtrue; - } -} - -/* -================= -RE_LoadWorldMap - -Called directly from cgame -================= -*/ -void RE_LoadWorldMap( const char *name ) { - int i; - dheader_t *header; - union { - byte *b; - void *v; - } buffer; - byte *startMarker; - - if ( tr.worldMapLoaded ) { - ri.Error( ERR_DROP, "ERROR: attempted to redundantly load world map" ); - } - - // set default sun direction to be used if it isn't - // overridden by a shader - tr.sunDirection[0] = 0.45f; - tr.sunDirection[1] = 0.3f; - tr.sunDirection[2] = 0.9f; - - VectorNormalize( tr.sunDirection ); - - tr.worldMapLoaded = qtrue; - - // load it - ri.FS_ReadFile( name, &buffer.v ); - if ( !buffer.b ) { - ri.Error (ERR_DROP, "RE_LoadWorldMap: %s not found", name); - } - - // clear tr.world so if the level fails to load, the next - // try will not look at the partially loaded version - tr.world = NULL; - - Com_Memset( &s_worldData, 0, sizeof( s_worldData ) ); - Q_strncpyz( s_worldData.name, name, sizeof( s_worldData.name ) ); - - Q_strncpyz( s_worldData.baseName, COM_SkipPath( s_worldData.name ), sizeof( s_worldData.name ) ); - COM_StripExtension(s_worldData.baseName, s_worldData.baseName, sizeof(s_worldData.baseName)); - - startMarker = ri.Hunk_Alloc(0, h_low); - c_gridVerts = 0; - - header = (dheader_t *)buffer.b; - fileBase = (byte *)header; - - i = LittleLong (header->version); - if ( i != BSP_VERSION ) { - ri.Error (ERR_DROP, "RE_LoadWorldMap: %s has wrong version number (%i should be %i)", - name, i, BSP_VERSION); - } - - // swap all the lumps - for (i=0 ; ilumps[LUMP_SHADERS] ); - R_LoadLightmaps( &header->lumps[LUMP_LIGHTMAPS] ); - R_LoadPlanes (&header->lumps[LUMP_PLANES]); - R_LoadFogs( &header->lumps[LUMP_FOGS], &header->lumps[LUMP_BRUSHES], &header->lumps[LUMP_BRUSHSIDES] ); - R_LoadSurfaces( &header->lumps[LUMP_SURFACES], &header->lumps[LUMP_DRAWVERTS], &header->lumps[LUMP_DRAWINDEXES] ); - R_LoadMarksurfaces (&header->lumps[LUMP_LEAFSURFACES]); - R_LoadNodesAndLeafs (&header->lumps[LUMP_NODES], &header->lumps[LUMP_LEAFS]); - R_LoadSubmodels (&header->lumps[LUMP_MODELS]); - R_LoadVisibility( &header->lumps[LUMP_VISIBILITY] ); - R_LoadEntities( &header->lumps[LUMP_ENTITIES] ); - R_LoadLightGrid( &header->lumps[LUMP_LIGHTGRID] ); - - s_worldData.dataSize = (byte *)ri.Hunk_Alloc(0, h_low) - startMarker; - - // only set tr.world now that we know the entire level has loaded properly - tr.world = &s_worldData; - - ri.FS_FreeFile( buffer.v ); -} - diff --git a/src/renderer/tr_cmds.c b/src/renderer/tr_cmds.c deleted file mode 100644 index e4a2dcfe..00000000 --- a/src/renderer/tr_cmds.c +++ /dev/null @@ -1,591 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#include "tr_local.h" - -volatile renderCommandList_t *renderCommandList; - -/* -===================== -R_PerformanceCounters -===================== -*/ -void R_PerformanceCounters( void ) { - if ( !r_speeds->integer ) { - // clear the counters even if we aren't printing - Com_Memset( &tr.pc, 0, sizeof( tr.pc ) ); - Com_Memset( &backEnd.pc, 0, sizeof( backEnd.pc ) ); - return; - } - - if (r_speeds->integer == 1) { - ri.Printf (PRINT_ALL, "%i/%i shaders/surfs %i leafs %i verts %i/%i tris %.2f mtex %.2f dc\n", - backEnd.pc.c_shaders, backEnd.pc.c_surfaces, tr.pc.c_leafs, backEnd.pc.c_vertexes, - backEnd.pc.c_indexes/3, backEnd.pc.c_totalIndexes/3, - R_SumOfUsedImages()/(1000000.0f), backEnd.pc.c_overDraw / (float)(glConfig.vidWidth * glConfig.vidHeight) ); - } else if (r_speeds->integer == 2) { - ri.Printf (PRINT_ALL, "(patch) %i sin %i sclip %i sout %i bin %i bclip %i bout\n", - tr.pc.c_sphere_cull_patch_in, tr.pc.c_sphere_cull_patch_clip, tr.pc.c_sphere_cull_patch_out, - tr.pc.c_box_cull_patch_in, tr.pc.c_box_cull_patch_clip, tr.pc.c_box_cull_patch_out ); - ri.Printf (PRINT_ALL, "(md3) %i sin %i sclip %i sout %i bin %i bclip %i bout\n", - tr.pc.c_sphere_cull_md3_in, tr.pc.c_sphere_cull_md3_clip, tr.pc.c_sphere_cull_md3_out, - tr.pc.c_box_cull_md3_in, tr.pc.c_box_cull_md3_clip, tr.pc.c_box_cull_md3_out ); - } else if (r_speeds->integer == 3) { - ri.Printf (PRINT_ALL, "viewcluster: %i\n", tr.viewCluster ); - } else if (r_speeds->integer == 4) { - if ( backEnd.pc.c_dlightVertexes ) { - ri.Printf (PRINT_ALL, "dlight srf:%i culled:%i verts:%i tris:%i\n", - tr.pc.c_dlightSurfaces, tr.pc.c_dlightSurfacesCulled, - backEnd.pc.c_dlightVertexes, backEnd.pc.c_dlightIndexes / 3 ); - } - } - else if (r_speeds->integer == 5 ) - { - ri.Printf( PRINT_ALL, "zFar: %.0f\n", tr.viewParms.zFar ); - } - else if (r_speeds->integer == 6 ) - { - ri.Printf( PRINT_ALL, "flare adds:%i tests:%i renders:%i\n", - backEnd.pc.c_flareAdds, backEnd.pc.c_flareTests, backEnd.pc.c_flareRenders ); - } - - Com_Memset( &tr.pc, 0, sizeof( tr.pc ) ); - Com_Memset( &backEnd.pc, 0, sizeof( backEnd.pc ) ); -} - - -/* -==================== -R_IssueRenderCommands -==================== -*/ -void R_IssueRenderCommands( qboolean runPerformanceCounters ) { - renderCommandList_t *cmdList; - - cmdList = &backEndData->commands; - assert(cmdList); - // add an end-of-list command - *(int *)(cmdList->cmds + cmdList->used) = RC_END_OF_LIST; - - // clear it out, in case this is a sync and not a buffer flip - cmdList->used = 0; - - if ( runPerformanceCounters ) { - R_PerformanceCounters(); - } - - // actually start the commands going - if ( !r_skipBackEnd->integer ) { - // let it start on the new batch - RB_ExecuteRenderCommands( cmdList->cmds ); - } -} - - -/* -==================== -R_IssuePendingRenderCommands - -Issue any pending commands and wait for them to complete. -==================== -*/ -void R_IssuePendingRenderCommands( void ) { - if ( !tr.registered ) { - return; - } - R_IssueRenderCommands( qfalse ); -} - -/* -============ -R_GetCommandBuffer - -make sure there is enough command space -============ -*/ -void *R_GetCommandBuffer( int bytes ) { - renderCommandList_t *cmdList; - - cmdList = &backEndData->commands; - bytes = PAD(bytes, sizeof(void *)); - - // always leave room for the end of list command - if ( cmdList->used + bytes + 4 > MAX_RENDER_COMMANDS ) { - if ( bytes > MAX_RENDER_COMMANDS - 4 ) { - ri.Error( ERR_FATAL, "R_GetCommandBuffer: bad size %i", bytes ); - } - // if we run out of room, just start dropping commands - return NULL; - } - - cmdList->used += bytes; - - return cmdList->cmds + cmdList->used - bytes; -} - - -/* -============= -R_AddDrawSurfCmd - -============= -*/ -void R_AddDrawSurfCmd( drawSurf_t *drawSurfs, int numDrawSurfs ) { - drawSurfsCommand_t *cmd; - - cmd = R_GetCommandBuffer( sizeof( *cmd ) ); - if ( !cmd ) { - return; - } - cmd->commandId = RC_DRAW_SURFS; - - cmd->drawSurfs = drawSurfs; - cmd->numDrawSurfs = numDrawSurfs; - - cmd->refdef = tr.refdef; - cmd->viewParms = tr.viewParms; -} - - -/* -============= -RE_SetColor - -Passing NULL will set the color to white -============= -*/ -void RE_SetColor( const float *rgba ) { - setColorCommand_t *cmd; - - if ( !tr.registered ) { - return; - } - cmd = R_GetCommandBuffer( sizeof( *cmd ) ); - if ( !cmd ) { - return; - } - cmd->commandId = RC_SET_COLOR; - if ( !rgba ) { - static float colorWhite[4] = { 1, 1, 1, 1 }; - - rgba = colorWhite; - } - - cmd->color[0] = rgba[0]; - cmd->color[1] = rgba[1]; - cmd->color[2] = rgba[2]; - cmd->color[3] = rgba[3]; -} - -/* -============= -R_ClipRegion -============= -*/ -static qboolean R_ClipRegion ( float *x, float *y, float *w, float *h, - float *s1, float *t1, float *s2, float *t2 ) { - float left, top, right, bottom; - float _s1, _t1, _s2, _t2; - float clipLeft, clipTop, clipRight, clipBottom; - - if (tr.clipRegion[2] <= tr.clipRegion[0] || - tr.clipRegion[3] <= tr.clipRegion[1] ) { - return qfalse; - } - - left = *x; - top = *y; - right = *x + *w; - bottom = *y + *h; - - _s1 = *s1; - _t1 = *t1; - _s2 = *s2; - _t2 = *t2; - - clipLeft = tr.clipRegion[0]; - clipTop = tr.clipRegion[1]; - clipRight = tr.clipRegion[2]; - clipBottom = tr.clipRegion[3]; - - // Completely clipped away - if ( right <= clipLeft || left >= clipRight || - bottom <= clipTop || top >= clipBottom ) { - return qtrue; - } - - // Clip left edge - if ( left < clipLeft ) { - float f = ( clipLeft - left ) / ( right - left ); - *s1 = ( f * ( _s2 - _s1 ) ) + _s1; - *x = clipLeft; - *w -= ( clipLeft - left ); - } - - // Clip right edge - if ( right > clipRight ) { - float f = ( clipRight - right ) / ( left - right ); - *s2 = ( f * ( _s1 - _s2 ) ) + _s2; - *w = clipRight - *x; - } - - // Clip top edge - if ( top < clipTop ) { - float f = ( clipTop - top ) / ( bottom - top ); - *t1 = ( f * ( _t2 - _t1 ) ) + _t1; - *y = clipTop; - *h -= ( clipTop - top ); - } - - // Clip bottom edge - if ( bottom > clipBottom ) { - float f = ( clipBottom - bottom ) / ( top - bottom ); - *t2 = ( f * ( _t1 - _t2 ) ) + _t2; - *h = clipBottom - *y; - } - - return qfalse; -} - -/* -============= -RE_SetClipRegion -============= -*/ -void RE_SetClipRegion( const float *region ) { - if ( region == NULL ) { - Com_Memset( tr.clipRegion, 0, sizeof( vec4_t ) ); - } else { - Vector4Copy( region, tr.clipRegion ); - } -} - -/* -============= -RE_StretchPic -============= -*/ -void RE_StretchPic ( float x, float y, float w, float h, - float s1, float t1, float s2, float t2, qhandle_t hShader ) { - stretchPicCommand_t *cmd; - - if (!tr.registered) { - return; - } - if (R_ClipRegion(&x, &y, &w, &h, &s1, &t1, &s2, &t2)) { - return; - } - cmd = R_GetCommandBuffer( sizeof( *cmd ) ); - if ( !cmd ) { - return; - } - cmd->commandId = RC_STRETCH_PIC; - cmd->shader = R_GetShaderByHandle( hShader ); - cmd->x = x; - cmd->y = y; - cmd->w = w; - cmd->h = h; - cmd->s1 = s1; - cmd->t1 = t1; - cmd->s2 = s2; - cmd->t2 = t2; -} - -#define MODE_RED_CYAN 1 -#define MODE_RED_BLUE 2 -#define MODE_RED_GREEN 3 -#define MODE_GREEN_MAGENTA 4 -#define MODE_MAX MODE_GREEN_MAGENTA - -void R_SetColorMode(GLboolean *rgba, stereoFrame_t stereoFrame, int colormode) -{ - rgba[0] = rgba[1] = rgba[2] = rgba[3] = GL_TRUE; - - if(colormode > MODE_MAX) - { - if(stereoFrame == STEREO_LEFT) - stereoFrame = STEREO_RIGHT; - else if(stereoFrame == STEREO_RIGHT) - stereoFrame = STEREO_LEFT; - - colormode -= MODE_MAX; - } - - if(colormode == MODE_GREEN_MAGENTA) - { - if(stereoFrame == STEREO_LEFT) - rgba[0] = rgba[2] = GL_FALSE; - else if(stereoFrame == STEREO_RIGHT) - rgba[1] = GL_FALSE; - } - else - { - if(stereoFrame == STEREO_LEFT) - rgba[1] = rgba[2] = GL_FALSE; - else if(stereoFrame == STEREO_RIGHT) - { - rgba[0] = GL_FALSE; - - if(colormode == MODE_RED_BLUE) - rgba[1] = GL_FALSE; - else if(colormode == MODE_RED_GREEN) - rgba[2] = GL_FALSE; - } - } -} - - -/* -==================== -RE_BeginFrame - -If running in stereo, RE_BeginFrame will be called twice -for each RE_EndFrame -==================== -*/ -void RE_BeginFrame( stereoFrame_t stereoFrame ) { - drawBufferCommand_t *cmd = NULL; - colorMaskCommand_t *colcmd = NULL; - - if ( !tr.registered ) { - return; - } - glState.finishCalled = qfalse; - - tr.frameCount++; - tr.frameSceneNum = 0; - - // - // do overdraw measurement - // - if ( r_measureOverdraw->integer ) - { - if ( glConfig.stencilBits < 4 ) - { - ri.Printf( PRINT_ALL, "Warning: not enough stencil bits to measure overdraw: %d\n", glConfig.stencilBits ); - ri.Cvar_Set( "r_measureOverdraw", "0" ); - r_measureOverdraw->modified = qfalse; - } - else if ( r_shadows->integer == 2 ) - { - ri.Printf( PRINT_ALL, "Warning: stencil shadows and overdraw measurement are mutually exclusive\n" ); - ri.Cvar_Set( "r_measureOverdraw", "0" ); - r_measureOverdraw->modified = qfalse; - } - else - { - R_IssuePendingRenderCommands(); - qglEnable( GL_STENCIL_TEST ); - qglStencilMask( ~0U ); - qglClearStencil( 0U ); - qglStencilFunc( GL_ALWAYS, 0U, ~0U ); - qglStencilOp( GL_KEEP, GL_INCR, GL_INCR ); - } - r_measureOverdraw->modified = qfalse; - } - else - { - // this is only reached if it was on and is now off - if ( r_measureOverdraw->modified ) { - R_IssuePendingRenderCommands(); - qglDisable( GL_STENCIL_TEST ); - } - r_measureOverdraw->modified = qfalse; - } - - // - // texturemode stuff - // - if ( r_textureMode->modified ) { - R_IssuePendingRenderCommands(); - GL_TextureMode( r_textureMode->string ); - r_textureMode->modified = qfalse; - } - - // - // gamma stuff - // - if ( r_gamma->modified ) { - r_gamma->modified = qfalse; - - R_IssuePendingRenderCommands(); - R_SetColorMappings(); - } - - // check for errors - if ( !r_ignoreGLErrors->integer ) - { - int err; - - R_IssuePendingRenderCommands(); - if ((err = qglGetError()) != GL_NO_ERROR) - ri.Error(ERR_FATAL, "RE_BeginFrame() - glGetError() failed (0x%x)!", err); - } - - if (glConfig.stereoEnabled) { - if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) ) - return; - - cmd->commandId = RC_DRAW_BUFFER; - - if ( stereoFrame == STEREO_LEFT ) { - cmd->buffer = (int)GL_BACK_LEFT; - } else if ( stereoFrame == STEREO_RIGHT ) { - cmd->buffer = (int)GL_BACK_RIGHT; - } else { - ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame ); - } - } - else - { - if(r_anaglyphMode->integer) - { - if(r_anaglyphMode->modified) - { - // clear both, front and backbuffer. - qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - qglClearColor(0.0f, 0.0f, 0.0f, 1.0f); - - qglDrawBuffer(GL_FRONT); - qglClear(GL_COLOR_BUFFER_BIT); - qglDrawBuffer(GL_BACK); - qglClear(GL_COLOR_BUFFER_BIT); - - r_anaglyphMode->modified = qfalse; - } - - if(stereoFrame == STEREO_LEFT) - { - if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) ) - return; - - if( !(colcmd = R_GetCommandBuffer(sizeof(*colcmd))) ) - return; - } - else if(stereoFrame == STEREO_RIGHT) - { - clearDepthCommand_t *cldcmd; - - if( !(cldcmd = R_GetCommandBuffer(sizeof(*cldcmd))) ) - return; - - cldcmd->commandId = RC_CLEARDEPTH; - - if( !(colcmd = R_GetCommandBuffer(sizeof(*colcmd))) ) - return; - } - else - ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame ); - - R_SetColorMode(colcmd->rgba, stereoFrame, r_anaglyphMode->integer); - colcmd->commandId = RC_COLORMASK; - } - else - { - if(stereoFrame != STEREO_CENTER) - ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is disabled, but stereoFrame was %i", stereoFrame ); - - if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) ) - return; - } - - if(cmd) - { - cmd->commandId = RC_DRAW_BUFFER; - - if(r_anaglyphMode->modified) - { - qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - r_anaglyphMode->modified = qfalse; - } - - if (!Q_stricmp(r_drawBuffer->string, "GL_FRONT")) - cmd->buffer = (int)GL_FRONT; - else - cmd->buffer = (int)GL_BACK; - } - } - - tr.refdef.stereoFrame = stereoFrame; -} - - -/* -============= -RE_EndFrame - -Returns the number of msec spent in the back end -============= -*/ -void RE_EndFrame( int *frontEndMsec, int *backEndMsec ) { - swapBuffersCommand_t *cmd; - - if ( !tr.registered ) { - return; - } - cmd = R_GetCommandBuffer( sizeof( *cmd ) ); - if ( !cmd ) { - return; - } - cmd->commandId = RC_SWAP_BUFFERS; - - R_IssueRenderCommands( qtrue ); - - R_InitNextFrame(); - - if ( frontEndMsec ) { - *frontEndMsec = tr.frontEndMsec; - } - tr.frontEndMsec = 0; - if ( backEndMsec ) { - *backEndMsec = backEnd.pc.msec; - } - backEnd.pc.msec = 0; -} - -/* -============= -RE_TakeVideoFrame -============= -*/ -void RE_TakeVideoFrame( int width, int height, - byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg ) -{ - videoFrameCommand_t *cmd; - - if( !tr.registered ) { - return; - } - - cmd = R_GetCommandBuffer( sizeof( *cmd ) ); - if( !cmd ) { - return; - } - - cmd->commandId = RC_VIDEOFRAME; - - cmd->width = width; - cmd->height = height; - cmd->captureBuffer = captureBuffer; - cmd->encodeBuffer = encodeBuffer; - cmd->motionJpeg = motionJpeg; -} diff --git a/src/renderer/tr_curve.c b/src/renderer/tr_curve.c deleted file mode 100644 index 684329b1..00000000 --- a/src/renderer/tr_curve.c +++ /dev/null @@ -1,626 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "tr_local.h" - -/* - -This file does all of the processing necessary to turn a raw grid of points -read from the map file into a srfGridMesh_t ready for rendering. - -The level of detail solution is direction independent, based only on subdivided -distance from the true curve. - -Only a single entry point: - -srfGridMesh_t *R_SubdividePatchToGrid( int width, int height, - drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) { - -*/ - - -/* -============ -LerpDrawVert -============ -*/ -static void LerpDrawVert( drawVert_t *a, drawVert_t *b, drawVert_t *out ) { - out->xyz[0] = 0.5f * (a->xyz[0] + b->xyz[0]); - out->xyz[1] = 0.5f * (a->xyz[1] + b->xyz[1]); - out->xyz[2] = 0.5f * (a->xyz[2] + b->xyz[2]); - - out->st[0] = 0.5f * (a->st[0] + b->st[0]); - out->st[1] = 0.5f * (a->st[1] + b->st[1]); - - out->lightmap[0] = 0.5f * (a->lightmap[0] + b->lightmap[0]); - out->lightmap[1] = 0.5f * (a->lightmap[1] + b->lightmap[1]); - - out->color[0] = (a->color[0] + b->color[0]) >> 1; - out->color[1] = (a->color[1] + b->color[1]) >> 1; - out->color[2] = (a->color[2] + b->color[2]) >> 1; - out->color[3] = (a->color[3] + b->color[3]) >> 1; -} - -/* -============ -Transpose -============ -*/ -static void Transpose( int width, int height, drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { - int i, j; - drawVert_t temp; - - if ( width > height ) { - for ( i = 0 ; i < height ; i++ ) { - for ( j = i + 1 ; j < width ; j++ ) { - if ( j < height ) { - // swap the value - temp = ctrl[j][i]; - ctrl[j][i] = ctrl[i][j]; - ctrl[i][j] = temp; - } else { - // just copy - ctrl[j][i] = ctrl[i][j]; - } - } - } - } else { - for ( i = 0 ; i < width ; i++ ) { - for ( j = i + 1 ; j < height ; j++ ) { - if ( j < width ) { - // swap the value - temp = ctrl[i][j]; - ctrl[i][j] = ctrl[j][i]; - ctrl[j][i] = temp; - } else { - // just copy - ctrl[i][j] = ctrl[j][i]; - } - } - } - } - -} - - -/* -================= -MakeMeshNormals - -Handles all the complicated wrapping and degenerate cases -================= -*/ -static void MakeMeshNormals( int width, int height, drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { - int i, j, k, dist; - vec3_t normal; - vec3_t sum; - int count = 0; - vec3_t base; - vec3_t delta; - int x, y; - drawVert_t *dv; - vec3_t around[8], temp; - qboolean good[8]; - qboolean wrapWidth, wrapHeight; - float len; -static int neighbors[8][2] = { - {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1} - }; - - wrapWidth = qfalse; - for ( i = 0 ; i < height ; i++ ) { - VectorSubtract( ctrl[i][0].xyz, ctrl[i][width-1].xyz, delta ); - len = VectorLengthSquared( delta ); - if ( len > 1.0 ) { - break; - } - } - if ( i == height ) { - wrapWidth = qtrue; - } - - wrapHeight = qfalse; - for ( i = 0 ; i < width ; i++ ) { - VectorSubtract( ctrl[0][i].xyz, ctrl[height-1][i].xyz, delta ); - len = VectorLengthSquared( delta ); - if ( len > 1.0 ) { - break; - } - } - if ( i == width) { - wrapHeight = qtrue; - } - - - for ( i = 0 ; i < width ; i++ ) { - for ( j = 0 ; j < height ; j++ ) { - count = 0; - dv = &ctrl[j][i]; - VectorCopy( dv->xyz, base ); - for ( k = 0 ; k < 8 ; k++ ) { - VectorClear( around[k] ); - good[k] = qfalse; - - for ( dist = 1 ; dist <= 3 ; dist++ ) { - x = i + neighbors[k][0] * dist; - y = j + neighbors[k][1] * dist; - if ( wrapWidth ) { - if ( x < 0 ) { - x = width - 1 + x; - } else if ( x >= width ) { - x = 1 + x - width; - } - } - if ( wrapHeight ) { - if ( y < 0 ) { - y = height - 1 + y; - } else if ( y >= height ) { - y = 1 + y - height; - } - } - - if ( x < 0 || x >= width || y < 0 || y >= height ) { - break; // edge of patch - } - VectorSubtract( ctrl[y][x].xyz, base, temp ); - if ( VectorNormalize2( temp, temp ) == 0 ) { - continue; // degenerate edge, get more dist - } else { - good[k] = qtrue; - VectorCopy( temp, around[k] ); - break; // good edge - } - } - } - - VectorClear( sum ); - for ( k = 0 ; k < 8 ; k++ ) { - if ( !good[k] || !good[(k+1)&7] ) { - continue; // didn't get two points - } - CrossProduct( around[(k+1)&7], around[k], normal ); - if ( VectorNormalize2( normal, normal ) == 0 ) { - continue; - } - VectorAdd( normal, sum, sum ); - count++; - } - //if ( count == 0 ) { - // printf("bad normal\n"); - //} - VectorNormalize2( sum, dv->normal ); - } - } -} - - -/* -============ -InvertCtrl -============ -*/ -static void InvertCtrl( int width, int height, drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { - int i, j; - drawVert_t temp; - - for ( i = 0 ; i < height ; i++ ) { - for ( j = 0 ; j < width/2 ; j++ ) { - temp = ctrl[i][j]; - ctrl[i][j] = ctrl[i][width-1-j]; - ctrl[i][width-1-j] = temp; - } - } -} - - -/* -================= -InvertErrorTable -================= -*/ -static void InvertErrorTable( float errorTable[2][MAX_GRID_SIZE], int width, int height ) { - int i; - float copy[2][MAX_GRID_SIZE]; - - Com_Memcpy( copy, errorTable, sizeof( copy ) ); - - for ( i = 0 ; i < width ; i++ ) { - errorTable[1][i] = copy[0][i]; //[width-1-i]; - } - - for ( i = 0 ; i < height ; i++ ) { - errorTable[0][i] = copy[1][height-1-i]; - } - -} - -/* -================== -PutPointsOnCurve -================== -*/ -static void PutPointsOnCurve( drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], - int width, int height ) { - int i, j; - drawVert_t prev, next; - - for ( i = 0 ; i < width ; i++ ) { - for ( j = 1 ; j < height ; j += 2 ) { - LerpDrawVert( &ctrl[j][i], &ctrl[j+1][i], &prev ); - LerpDrawVert( &ctrl[j][i], &ctrl[j-1][i], &next ); - LerpDrawVert( &prev, &next, &ctrl[j][i] ); - } - } - - - for ( j = 0 ; j < height ; j++ ) { - for ( i = 1 ; i < width ; i += 2 ) { - LerpDrawVert( &ctrl[j][i], &ctrl[j][i+1], &prev ); - LerpDrawVert( &ctrl[j][i], &ctrl[j][i-1], &next ); - LerpDrawVert( &prev, &next, &ctrl[j][i] ); - } - } -} - -/* -================= -R_CreateSurfaceGridMesh -================= -*/ -srfGridMesh_t *R_CreateSurfaceGridMesh(int width, int height, - drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], float errorTable[2][MAX_GRID_SIZE] ) { - int i, j, size; - drawVert_t *vert; - vec3_t tmpVec; - srfGridMesh_t *grid; - - // copy the results out to a grid - size = (width * height - 1) * sizeof( drawVert_t ) + sizeof( *grid ); - -#ifdef PATCH_STITCHING - grid = /*ri.Hunk_Alloc*/ ri.Malloc( size ); - Com_Memset(grid, 0, size); - - grid->widthLodError = /*ri.Hunk_Alloc*/ ri.Malloc( width * 4 ); - Com_Memcpy( grid->widthLodError, errorTable[0], width * 4 ); - - grid->heightLodError = /*ri.Hunk_Alloc*/ ri.Malloc( height * 4 ); - Com_Memcpy( grid->heightLodError, errorTable[1], height * 4 ); -#else - grid = ri.Hunk_Alloc( size ); - Com_Memset(grid, 0, size); - - grid->widthLodError = ri.Hunk_Alloc( width * 4 ); - Com_Memcpy( grid->widthLodError, errorTable[0], width * 4 ); - - grid->heightLodError = ri.Hunk_Alloc( height * 4 ); - Com_Memcpy( grid->heightLodError, errorTable[1], height * 4 ); -#endif - - grid->width = width; - grid->height = height; - grid->surfaceType = SF_GRID; - ClearBounds( grid->meshBounds[0], grid->meshBounds[1] ); - for ( i = 0 ; i < width ; i++ ) { - for ( j = 0 ; j < height ; j++ ) { - vert = &grid->verts[j*width+i]; - *vert = ctrl[j][i]; - AddPointToBounds( vert->xyz, grid->meshBounds[0], grid->meshBounds[1] ); - } - } - - // compute local origin and bounds - VectorAdd( grid->meshBounds[0], grid->meshBounds[1], grid->localOrigin ); - VectorScale( grid->localOrigin, 0.5f, grid->localOrigin ); - VectorSubtract( grid->meshBounds[0], grid->localOrigin, tmpVec ); - grid->meshRadius = VectorLength( tmpVec ); - - VectorCopy( grid->localOrigin, grid->lodOrigin ); - grid->lodRadius = grid->meshRadius; - // - return grid; -} - -/* -================= -R_FreeSurfaceGridMesh -================= -*/ -void R_FreeSurfaceGridMesh( srfGridMesh_t *grid ) { - ri.Free(grid->widthLodError); - ri.Free(grid->heightLodError); - ri.Free(grid); -} - -/* -================= -R_SubdividePatchToGrid -================= -*/ -srfGridMesh_t *R_SubdividePatchToGrid( int width, int height, - drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) { - int i, j, k, l; - drawVert_t_cleared( prev ); - drawVert_t_cleared( next ); - drawVert_t_cleared( mid ); - float len, maxLen; - int dir; - int t; - drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; - float errorTable[2][MAX_GRID_SIZE]; - - for ( i = 0 ; i < width ; i++ ) { - for ( j = 0 ; j < height ; j++ ) { - ctrl[j][i] = points[j*width+i]; - } - } - - for ( dir = 0 ; dir < 2 ; dir++ ) { - - for ( j = 0 ; j < MAX_GRID_SIZE ; j++ ) { - errorTable[dir][j] = 0; - } - - // horizontal subdivisions - for ( j = 0 ; j + 2 < width ; j += 2 ) { - // check subdivided midpoints against control points - - // FIXME: also check midpoints of adjacent patches against the control points - // this would basically stitch all patches in the same LOD group together. - - maxLen = 0; - for ( i = 0 ; i < height ; i++ ) { - vec3_t midxyz; - vec3_t midxyz2; - vec3_t dir; - vec3_t projected; - float d; - - // calculate the point on the curve - for ( l = 0 ; l < 3 ; l++ ) { - midxyz[l] = (ctrl[i][j].xyz[l] + ctrl[i][j+1].xyz[l] * 2 - + ctrl[i][j+2].xyz[l] ) * 0.25f; - } - - // see how far off the line it is - // using dist-from-line will not account for internal - // texture warping, but it gives a lot less polygons than - // dist-from-midpoint - VectorSubtract( midxyz, ctrl[i][j].xyz, midxyz ); - VectorSubtract( ctrl[i][j+2].xyz, ctrl[i][j].xyz, dir ); - VectorNormalize( dir ); - - d = DotProduct( midxyz, dir ); - VectorScale( dir, d, projected ); - VectorSubtract( midxyz, projected, midxyz2); - len = VectorLengthSquared( midxyz2 ); // we will do the sqrt later - if ( len > maxLen ) { - maxLen = len; - } - } - - maxLen = sqrt(maxLen); - - // if all the points are on the lines, remove the entire columns - if ( maxLen < 0.1f ) { - errorTable[dir][j+1] = 999; - continue; - } - - // see if we want to insert subdivided columns - if ( width + 2 > MAX_GRID_SIZE ) { - errorTable[dir][j+1] = 1.0f/maxLen; - continue; // can't subdivide any more - } - - if ( maxLen <= r_subdivisions->value ) { - errorTable[dir][j+1] = 1.0f/maxLen; - continue; // didn't need subdivision - } - - errorTable[dir][j+2] = 1.0f/maxLen; - - // insert two columns and replace the peak - width += 2; - for ( i = 0 ; i < height ; i++ ) { - LerpDrawVert( &ctrl[i][j], &ctrl[i][j+1], &prev ); - LerpDrawVert( &ctrl[i][j+1], &ctrl[i][j+2], &next ); - LerpDrawVert( &prev, &next, &mid ); - - for ( k = width - 1 ; k > j + 3 ; k-- ) { - ctrl[i][k] = ctrl[i][k-2]; - } - ctrl[i][j + 1] = prev; - ctrl[i][j + 2] = mid; - ctrl[i][j + 3] = next; - } - - // back up and recheck this set again, it may need more subdivision - j -= 2; - - } - - Transpose( width, height, ctrl ); - t = width; - width = height; - height = t; - } - - - // put all the aproximating points on the curve - PutPointsOnCurve( ctrl, width, height ); - - // cull out any rows or columns that are colinear - for ( i = 1 ; i < width-1 ; i++ ) { - if ( errorTable[0][i] != 999 ) { - continue; - } - for ( j = i+1 ; j < width ; j++ ) { - for ( k = 0 ; k < height ; k++ ) { - ctrl[k][j-1] = ctrl[k][j]; - } - errorTable[0][j-1] = errorTable[0][j]; - } - width--; - } - - for ( i = 1 ; i < height-1 ; i++ ) { - if ( errorTable[1][i] != 999 ) { - continue; - } - for ( j = i+1 ; j < height ; j++ ) { - for ( k = 0 ; k < width ; k++ ) { - ctrl[j-1][k] = ctrl[j][k]; - } - errorTable[1][j-1] = errorTable[1][j]; - } - height--; - } - -#if 1 - // flip for longest tristrips as an optimization - // the results should be visually identical with or - // without this step - if ( height > width ) { - Transpose( width, height, ctrl ); - InvertErrorTable( errorTable, width, height ); - t = width; - width = height; - height = t; - InvertCtrl( width, height, ctrl ); - } -#endif - - // calculate normals - MakeMeshNormals( width, height, ctrl ); - - return R_CreateSurfaceGridMesh( width, height, ctrl, errorTable ); -} - -/* -=============== -R_GridInsertColumn -=============== -*/ -srfGridMesh_t *R_GridInsertColumn( srfGridMesh_t *grid, int column, int row, vec3_t point, float loderror ) { - int i, j; - int width, height, oldwidth; - drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; - float errorTable[2][MAX_GRID_SIZE]; - float lodRadius; - vec3_t lodOrigin; - - oldwidth = 0; - width = grid->width + 1; - if (width > MAX_GRID_SIZE) - return NULL; - height = grid->height; - for (i = 0; i < width; i++) { - if (i == column) { - //insert new column - for (j = 0; j < grid->height; j++) { - LerpDrawVert( &grid->verts[j * grid->width + i-1], &grid->verts[j * grid->width + i], &ctrl[j][i] ); - if (j == row) - VectorCopy(point, ctrl[j][i].xyz); - } - errorTable[0][i] = loderror; - continue; - } - errorTable[0][i] = grid->widthLodError[oldwidth]; - for (j = 0; j < grid->height; j++) { - ctrl[j][i] = grid->verts[j * grid->width + oldwidth]; - } - oldwidth++; - } - for (j = 0; j < grid->height; j++) { - errorTable[1][j] = grid->heightLodError[j]; - } - // put all the aproximating points on the curve - //PutPointsOnCurve( ctrl, width, height ); - // calculate normals - MakeMeshNormals( width, height, ctrl ); - - VectorCopy(grid->lodOrigin, lodOrigin); - lodRadius = grid->lodRadius; - // free the old grid - R_FreeSurfaceGridMesh(grid); - // create a new grid - grid = R_CreateSurfaceGridMesh( width, height, ctrl, errorTable ); - grid->lodRadius = lodRadius; - VectorCopy(lodOrigin, grid->lodOrigin); - return grid; -} - -/* -=============== -R_GridInsertRow -=============== -*/ -srfGridMesh_t *R_GridInsertRow( srfGridMesh_t *grid, int row, int column, vec3_t point, float loderror ) { - int i, j; - int width, height, oldheight; - drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; - float errorTable[2][MAX_GRID_SIZE]; - float lodRadius; - vec3_t lodOrigin; - - oldheight = 0; - width = grid->width; - height = grid->height + 1; - if (height > MAX_GRID_SIZE) - return NULL; - for (i = 0; i < height; i++) { - if (i == row) { - //insert new row - for (j = 0; j < grid->width; j++) { - LerpDrawVert( &grid->verts[(i-1) * grid->width + j], &grid->verts[i * grid->width + j], &ctrl[i][j] ); - if (j == column) - VectorCopy(point, ctrl[i][j].xyz); - } - errorTable[1][i] = loderror; - continue; - } - errorTable[1][i] = grid->heightLodError[oldheight]; - for (j = 0; j < grid->width; j++) { - ctrl[i][j] = grid->verts[oldheight * grid->width + j]; - } - oldheight++; - } - for (j = 0; j < grid->width; j++) { - errorTable[0][j] = grid->widthLodError[j]; - } - // put all the aproximating points on the curve - //PutPointsOnCurve( ctrl, width, height ); - // calculate normals - MakeMeshNormals( width, height, ctrl ); - - VectorCopy(grid->lodOrigin, lodOrigin); - lodRadius = grid->lodRadius; - // free the old grid - R_FreeSurfaceGridMesh(grid); - // create a new grid - grid = R_CreateSurfaceGridMesh( width, height, ctrl, errorTable ); - grid->lodRadius = lodRadius; - VectorCopy(lodOrigin, grid->lodOrigin); - return grid; -} diff --git a/src/renderer/tr_flares.c b/src/renderer/tr_flares.c deleted file mode 100644 index 9397fbac..00000000 --- a/src/renderer/tr_flares.c +++ /dev/null @@ -1,529 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_flares.c - -#include "tr_local.h" - -/* -============================================================================= - -LIGHT FLARES - -A light flare is an effect that takes place inside the eye when bright light -sources are visible. The size of the flare relative to the screen is nearly -constant, irrespective of distance, but the intensity should be proportional to the -projected area of the light source. - -A surface that has been flagged as having a light flare will calculate the depth -buffer value that its midpoint should have when the surface is added. - -After all opaque surfaces have been rendered, the depth buffer is read back for -each flare in view. If the point has not been obscured by a closer surface, the -flare should be drawn. - -Surfaces that have a repeated texture should never be flagged as flaring, because -there will only be a single flare added at the midpoint of the polygon. - -To prevent abrupt popping, the intensity of the flare is interpolated up and -down as it changes visibility. This involves scene to scene state, unlike almost -all other aspects of the renderer, and is complicated by the fact that a single -frame may have multiple scenes. - -RB_RenderFlares() will be called once per view (twice in a mirrored scene, potentially -up to five or more times in a frame with 3D status bar icons). - -============================================================================= -*/ - - -// flare states maintain visibility over multiple frames for fading -// layers: view, mirror, menu -typedef struct flare_s { - struct flare_s *next; // for active chain - - int addedFrame; - - qboolean inPortal; // true if in a portal view of the scene - int frameSceneNum; - void *surface; - int fogNum; - - int fadeTime; - - qboolean visible; // state of last test - float drawIntensity; // may be non 0 even if !visible due to fading - - int windowX, windowY; - float eyeZ; - - vec3_t origin; - vec3_t color; -} flare_t; - -#define MAX_FLARES 256 - -flare_t r_flareStructs[MAX_FLARES]; -flare_t *r_activeFlares, *r_inactiveFlares; - -int flareCoeff; - -/* -================== -R_ClearFlares -================== -*/ -void R_ClearFlares( void ) { - int i; - - Com_Memset( r_flareStructs, 0, sizeof( r_flareStructs ) ); - r_activeFlares = NULL; - r_inactiveFlares = NULL; - - for ( i = 0 ; i < MAX_FLARES ; i++ ) { - r_flareStructs[i].next = r_inactiveFlares; - r_inactiveFlares = &r_flareStructs[i]; - } -} - - -/* -================== -RB_AddFlare - -This is called at surface tesselation time -================== -*/ -void RB_AddFlare( void *surface, int fogNum, vec3_t point, vec3_t color, vec3_t normal ) { - int i; - flare_t *f; - vec3_t local; - float d = 1; - vec4_t eye, clip, normalized, window; - - backEnd.pc.c_flareAdds++; - - if(normal && (normal[0] || normal[1] || normal[2])) - { - VectorSubtract( backEnd.viewParms.or.origin, point, local ); - VectorNormalizeFast(local); - d = DotProduct(local, normal); - - // If the viewer is behind the flare don't add it. - if(d < 0) - return; - } - - // if the point is off the screen, don't bother adding it - // calculate screen coordinates and depth - R_TransformModelToClip( point, backEnd.or.modelMatrix, - backEnd.viewParms.projectionMatrix, eye, clip ); - - // check to see if the point is completely off screen - for ( i = 0 ; i < 3 ; i++ ) { - if ( clip[i] >= clip[3] || clip[i] <= -clip[3] ) { - return; - } - } - - R_TransformClipToWindow( clip, &backEnd.viewParms, normalized, window ); - - if ( window[0] < 0 || window[0] >= backEnd.viewParms.viewportWidth - || window[1] < 0 || window[1] >= backEnd.viewParms.viewportHeight ) { - return; // shouldn't happen, since we check the clip[] above, except for FP rounding - } - - // see if a flare with a matching surface, scene, and view exists - for ( f = r_activeFlares ; f ; f = f->next ) { - if ( f->surface == surface && f->frameSceneNum == backEnd.viewParms.frameSceneNum - && f->inPortal == backEnd.viewParms.isPortal ) { - break; - } - } - - // allocate a new one - if (!f ) { - if ( !r_inactiveFlares ) { - // the list is completely full - return; - } - f = r_inactiveFlares; - r_inactiveFlares = r_inactiveFlares->next; - f->next = r_activeFlares; - r_activeFlares = f; - - f->surface = surface; - f->frameSceneNum = backEnd.viewParms.frameSceneNum; - f->inPortal = backEnd.viewParms.isPortal; - f->addedFrame = -1; - } - - if ( f->addedFrame != backEnd.viewParms.frameCount - 1 ) { - f->visible = qfalse; - f->fadeTime = backEnd.refdef.time - 2000; - } - - f->addedFrame = backEnd.viewParms.frameCount; - f->fogNum = fogNum; - - VectorCopy(point, f->origin); - VectorCopy( color, f->color ); - - // fade the intensity of the flare down as the - // light surface turns away from the viewer - VectorScale( f->color, d, f->color ); - - // save info needed to test - f->windowX = backEnd.viewParms.viewportX + window[0]; - f->windowY = backEnd.viewParms.viewportY + window[1]; - - f->eyeZ = eye[2]; -} - -/* -================== -RB_AddDlightFlares -================== -*/ -void RB_AddDlightFlares( void ) { - dlight_t *l; - int i, j, k; - fog_t *fog = NULL; - - if ( !r_flares->integer ) { - return; - } - - l = backEnd.refdef.dlights; - - if(tr.world) - fog = tr.world->fogs; - - for (i=0 ; inumfogs ; j++ ) { - fog = &tr.world->fogs[j]; - for ( k = 0 ; k < 3 ; k++ ) { - if ( l->origin[k] < fog->bounds[0][k] || l->origin[k] > fog->bounds[1][k] ) { - break; - } - } - if ( k == 3 ) { - break; - } - } - if ( j == tr.world->numfogs ) { - j = 0; - } - } - else - j = 0; - - RB_AddFlare( (void *)l, j, l->origin, l->color, NULL ); - } -} - -/* -=============================================================================== - -FLARE BACK END - -=============================================================================== -*/ - -/* -================== -RB_TestFlare -================== -*/ -void RB_TestFlare( flare_t *f ) { - float depth; - qboolean visible; - float fade; - float screenZ; - - backEnd.pc.c_flareTests++; - - // doing a readpixels is as good as doing a glFinish(), so - // don't bother with another sync - glState.finishCalled = qfalse; - - // read back the z buffer contents - qglReadPixels( f->windowX, f->windowY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth ); - - screenZ = backEnd.viewParms.projectionMatrix[14] / - ( ( 2*depth - 1 ) * backEnd.viewParms.projectionMatrix[11] - backEnd.viewParms.projectionMatrix[10] ); - - visible = ( -f->eyeZ - -screenZ ) < 24; - - if ( visible ) { - if ( !f->visible ) { - f->visible = qtrue; - f->fadeTime = backEnd.refdef.time - 1; - } - fade = ( ( backEnd.refdef.time - f->fadeTime ) /1000.0f ) * r_flareFade->value; - } else { - if ( f->visible ) { - f->visible = qfalse; - f->fadeTime = backEnd.refdef.time - 1; - } - fade = 1.0f - ( ( backEnd.refdef.time - f->fadeTime ) / 1000.0f ) * r_flareFade->value; - } - - if ( fade < 0 ) { - fade = 0; - } - if ( fade > 1 ) { - fade = 1; - } - - f->drawIntensity = fade; -} - - -/* -================== -RB_RenderFlare -================== -*/ -void RB_RenderFlare( flare_t *f ) { - float size; - vec3_t color; - int iColor[3]; - float distance, intensity, factor; - byte fogFactors[3] = {255, 255, 255}; - - backEnd.pc.c_flareRenders++; - - // We don't want too big values anyways when dividing by distance. - if(f->eyeZ > -1.0f) - distance = 1.0f; - else - distance = -f->eyeZ; - - // calculate the flare size.. - size = backEnd.viewParms.viewportWidth * ( r_flareSize->value/640.0f + 8 / distance ); - -/* - * This is an alternative to intensity scaling. It changes the size of the flare on screen instead - * with growing distance. See in the description at the top why this is not the way to go. - // size will change ~ 1/r. - size = backEnd.viewParms.viewportWidth * (r_flareSize->value / (distance * -2.0f)); -*/ - -/* - * As flare sizes stay nearly constant with increasing distance we must decrease the intensity - * to achieve a reasonable visual result. The intensity is ~ (size^2 / distance^2) which can be - * got by considering the ratio of - * (flaresurface on screen) : (Surface of sphere defined by flare origin and distance from flare) - * An important requirement is: - * intensity <= 1 for all distances. - * - * The formula used here to compute the intensity is as follows: - * intensity = flareCoeff * size^2 / (distance + size*sqrt(flareCoeff))^2 - * As you can see, the intensity will have a max. of 1 when the distance is 0. - * The coefficient flareCoeff will determine the falloff speed with increasing distance. - */ - - factor = distance + size * sqrt(flareCoeff); - - intensity = flareCoeff * size * size / (factor * factor); - - VectorScale(f->color, f->drawIntensity * intensity, color); - -// Calculations for fogging - if(tr.world && f->fogNum < tr.world->numfogs) - { - tess.numVertexes = 1; - VectorCopy(f->origin, tess.xyz[0]); - tess.fogNum = f->fogNum; - - RB_CalcModulateColorsByFog(fogFactors); - - // We don't need to render the flare if colors are 0 anyways. - if(!(fogFactors[0] || fogFactors[1] || fogFactors[2])) - return; - } - - iColor[0] = color[0] * fogFactors[0]; - iColor[1] = color[1] * fogFactors[1]; - iColor[2] = color[2] * fogFactors[2]; - - RB_BeginSurface( tr.flareShader, f->fogNum ); - - // FIXME: use quadstamp? - tess.xyz[tess.numVertexes][0] = f->windowX - size; - tess.xyz[tess.numVertexes][1] = f->windowY - size; - tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 0; - tess.vertexColors[tess.numVertexes][0] = iColor[0]; - tess.vertexColors[tess.numVertexes][1] = iColor[1]; - tess.vertexColors[tess.numVertexes][2] = iColor[2]; - tess.vertexColors[tess.numVertexes][3] = 255; - tess.numVertexes++; - - tess.xyz[tess.numVertexes][0] = f->windowX - size; - tess.xyz[tess.numVertexes][1] = f->windowY + size; - tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 1; - tess.vertexColors[tess.numVertexes][0] = iColor[0]; - tess.vertexColors[tess.numVertexes][1] = iColor[1]; - tess.vertexColors[tess.numVertexes][2] = iColor[2]; - tess.vertexColors[tess.numVertexes][3] = 255; - tess.numVertexes++; - - tess.xyz[tess.numVertexes][0] = f->windowX + size; - tess.xyz[tess.numVertexes][1] = f->windowY + size; - tess.texCoords[tess.numVertexes][0][0] = 1; - tess.texCoords[tess.numVertexes][0][1] = 1; - tess.vertexColors[tess.numVertexes][0] = iColor[0]; - tess.vertexColors[tess.numVertexes][1] = iColor[1]; - tess.vertexColors[tess.numVertexes][2] = iColor[2]; - tess.vertexColors[tess.numVertexes][3] = 255; - tess.numVertexes++; - - tess.xyz[tess.numVertexes][0] = f->windowX + size; - tess.xyz[tess.numVertexes][1] = f->windowY - size; - tess.texCoords[tess.numVertexes][0][0] = 1; - tess.texCoords[tess.numVertexes][0][1] = 0; - tess.vertexColors[tess.numVertexes][0] = iColor[0]; - tess.vertexColors[tess.numVertexes][1] = iColor[1]; - tess.vertexColors[tess.numVertexes][2] = iColor[2]; - tess.vertexColors[tess.numVertexes][3] = 255; - tess.numVertexes++; - - tess.indexes[tess.numIndexes++] = 0; - tess.indexes[tess.numIndexes++] = 1; - tess.indexes[tess.numIndexes++] = 2; - tess.indexes[tess.numIndexes++] = 0; - tess.indexes[tess.numIndexes++] = 2; - tess.indexes[tess.numIndexes++] = 3; - - RB_EndSurface(); -} - -/* -================== -RB_RenderFlares - -Because flares are simulating an occular effect, they should be drawn after -everything (all views) in the entire frame has been drawn. - -Because of the way portals use the depth buffer to mark off areas, the -needed information would be lost after each view, so we are forced to draw -flares after each view. - -The resulting artifact is that flares in mirrors or portals don't dim properly -when occluded by something in the main view, and portal flares that should -extend past the portal edge will be overwritten. -================== -*/ -void RB_RenderFlares (void) { - flare_t *f; - flare_t **prev; - qboolean draw; - - if ( !r_flares->integer ) { - return; - } - - if(r_flareCoeff->modified) - { - if(r_flareCoeff->value == 0.0f) - flareCoeff = atof(FLARE_STDCOEFF); - else - flareCoeff = r_flareCoeff->value; - - r_flareCoeff->modified = qfalse; - } - - // Reset currentEntity to world so that any previously referenced entities - // don't have influence on the rendering of these flares (i.e. RF_ renderer flags). - backEnd.currentEntity = &tr.worldEntity; - backEnd.or = backEnd.viewParms.world; - -// RB_AddDlightFlares(); - - // perform z buffer readback on each flare in this view - draw = qfalse; - prev = &r_activeFlares; - while ( ( f = *prev ) != NULL ) { - // throw out any flares that weren't added last frame - if ( f->addedFrame < backEnd.viewParms.frameCount - 1 ) { - *prev = f->next; - f->next = r_inactiveFlares; - r_inactiveFlares = f; - continue; - } - - // don't draw any here that aren't from this scene / portal - f->drawIntensity = 0; - if ( f->frameSceneNum == backEnd.viewParms.frameSceneNum - && f->inPortal == backEnd.viewParms.isPortal ) { - RB_TestFlare( f ); - if ( f->drawIntensity ) { - draw = qtrue; - } else { - // this flare has completely faded out, so remove it from the chain - *prev = f->next; - f->next = r_inactiveFlares; - r_inactiveFlares = f; - continue; - } - } - - prev = &f->next; - } - - if ( !draw ) { - return; // none visible - } - - if ( backEnd.viewParms.isPortal ) { - qglDisable (GL_CLIP_PLANE0); - } - - qglPushMatrix(); - qglLoadIdentity(); - qglMatrixMode( GL_PROJECTION ); - qglPushMatrix(); - qglLoadIdentity(); - qglOrtho( backEnd.viewParms.viewportX, backEnd.viewParms.viewportX + backEnd.viewParms.viewportWidth, - backEnd.viewParms.viewportY, backEnd.viewParms.viewportY + backEnd.viewParms.viewportHeight, - -99999, 99999 ); - - for ( f = r_activeFlares ; f ; f = f->next ) { - if ( f->frameSceneNum == backEnd.viewParms.frameSceneNum - && f->inPortal == backEnd.viewParms.isPortal - && f->drawIntensity ) { - RB_RenderFlare( f ); - } - } - - qglPopMatrix(); - qglMatrixMode( GL_MODELVIEW ); - qglPopMatrix(); -} - diff --git a/src/renderer/tr_font.c b/src/renderer/tr_font.c deleted file mode 100644 index 432d1215..00000000 --- a/src/renderer/tr_font.c +++ /dev/null @@ -1,555 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_font.c -// -// -// The font system uses FreeType 2.x to render TrueType fonts for use within the game. -// As of this writing ( Nov, 2000 ) Team Arena uses these fonts for all of the ui and -// about 90% of the cgame presentation. A few areas of the CGAME were left uses the old -// fonts since the code is shared with standard Q3A. -// -// If you include this font rendering code in a commercial product you MUST include the -// following somewhere with your product, see www.freetype.org for specifics or changes. -// The Freetype code also uses some hinting techniques that MIGHT infringe on patents -// held by apple so be aware of that also. -// -// As of Q3A 1.25+ and Team Arena, we are shipping the game with the font rendering code -// disabled. This removes any potential patent issues and it keeps us from having to -// distribute an actual TrueTrype font which is 1. expensive to do and 2. seems to require -// an act of god to accomplish. -// -// What we did was pre-render the fonts using FreeType ( which is why we leave the FreeType -// credit in the credits ) and then saved off the glyph data and then hand touched up the -// font bitmaps so they scale a bit better in GL. -// -// There are limitations in the way fonts are saved and reloaded in that it is based on -// point size and not name. So if you pre-render Helvetica in 18 point and Impact in 18 point -// you will end up with a single 18 point data file and image set. Typically you will want to -// choose 3 sizes to best approximate the scaling you will be doing in the ui scripting system -// -// In the UI Scripting code, a scale of 1.0 is equal to a 48 point font. In Team Arena, we -// use three or four scales, most of them exactly equaling the specific rendered size. We -// rendered three sizes in Team Arena, 12, 16, and 20. -// -// To generate new font data you need to go through the following steps. -// 1. delete the fontImage_x_xx.tga files and fontImage_xx.dat files from the fonts path. -// 2. in a ui script, specificy a font, smallFont, and bigFont keyword with font name and -// point size. the original TrueType fonts must exist in fonts at this point. -// 3. run the game, you should see things normally. -// 4. Exit the game and there will be three dat files and at least three tga files. The -// tga's are in 256x256 pages so if it takes three images to render a 24 point font you -// will end up with fontImage_0_24.tga through fontImage_2_24.tga -// 5. In future runs of the game, the system looks for these images and data files when a s -// specific point sized font is rendered and loads them for use. -// 6. Because of the original beta nature of the FreeType code you will probably want to hand -// touch the font bitmaps. -// -// Currently a define in the project turns on or off the FreeType code which is currently -// defined out. To pre-render new fonts you need enable the define ( BUILD_FREETYPE ) and -// uncheck the exclude from build check box in the FreeType2 area of the Renderer project. - - -#include "tr_local.h" -#include "../qcommon/qcommon.h" - -#ifdef BUILD_FREETYPE -#include -#include FT_ERRORS_H -#include FT_SYSTEM_H -#include FT_IMAGE_H -#include FT_FREETYPE_H -#include FT_OUTLINE_H - -#define _FLOOR(x) ((x) & -64) -#define _CEIL(x) (((x)+63) & -64) -#define _TRUNC(x) ((x) >> 6) - -FT_Library ftLibrary = NULL; -#endif - -#define MAX_FONTS 6 -static int registeredFontCount = 0; -static fontInfo_t registeredFont[MAX_FONTS]; - -#ifdef BUILD_FREETYPE -void R_GetGlyphInfo(FT_GlyphSlot glyph, int *left, int *right, int *width, int *top, int *bottom, int *height, int *pitch) { - *left = _FLOOR( glyph->metrics.horiBearingX ); - *right = _CEIL( glyph->metrics.horiBearingX + glyph->metrics.width ); - *width = _TRUNC(*right - *left); - - *top = _CEIL( glyph->metrics.horiBearingY ); - *bottom = _FLOOR( glyph->metrics.horiBearingY - glyph->metrics.height ); - *height = _TRUNC( *top - *bottom ); - *pitch = ( qtrue ? (*width+3) & -4 : (*width+7) >> 3 ); -} - - -FT_Bitmap *R_RenderGlyph(FT_GlyphSlot glyph, glyphInfo_t* glyphOut) { - FT_Bitmap *bit2; - int left, right, width, top, bottom, height, pitch, size; - - R_GetGlyphInfo(glyph, &left, &right, &width, &top, &bottom, &height, &pitch); - - if ( glyph->format == ft_glyph_format_outline ) { - size = pitch*height; - - bit2 = ri.Malloc(sizeof(FT_Bitmap)); - - bit2->width = width; - bit2->rows = height; - bit2->pitch = pitch; - bit2->pixel_mode = ft_pixel_mode_grays; - //bit2->pixel_mode = ft_pixel_mode_mono; - bit2->buffer = ri.Malloc(pitch*height); - bit2->num_grays = 256; - - Com_Memset( bit2->buffer, 0, size ); - - FT_Outline_Translate( &glyph->outline, -left, -bottom ); - - FT_Outline_Get_Bitmap( ftLibrary, &glyph->outline, bit2 ); - - glyphOut->height = height; - glyphOut->pitch = pitch; - glyphOut->top = (glyph->metrics.horiBearingY >> 6) + 1; - glyphOut->bottom = bottom; - - return bit2; - } else { - ri.Printf(PRINT_ALL, "Non-outline fonts are not supported\n"); - } - return NULL; -} - -void WriteTGA (char *filename, byte *data, int width, int height) { - byte *buffer; - int i, c; - int row; - unsigned char *flip; - unsigned char *src, *dst; - - buffer = ri.Malloc(width*height*4 + 18); - Com_Memset (buffer, 0, 18); - buffer[2] = 2; // uncompressed type - buffer[12] = width&255; - buffer[13] = width>>8; - buffer[14] = height&255; - buffer[15] = height>>8; - buffer[16] = 32; // pixel size - - // swap rgb to bgr - c = 18 + width * height * 4; - for (i=18 ; iglyph, &glyph); - if (bitmap) { - glyph.xSkip = (face->glyph->metrics.horiAdvance >> 6) + 1; - } else { - return &glyph; - } - - if (glyph.height > *maxHeight) { - *maxHeight = glyph.height; - } - - if (calcHeight) { - ri.Free(bitmap->buffer); - ri.Free(bitmap); - return &glyph; - } - -/* - // need to convert to power of 2 sizes so we do not get - // any scaling from the gl upload - for (scaled_width = 1 ; scaled_width < glyph.pitch ; scaled_width<<=1) - ; - for (scaled_height = 1 ; scaled_height < glyph.height ; scaled_height<<=1) - ; -*/ - - scaled_width = glyph.pitch; - scaled_height = glyph.height; - - // we need to make sure we fit - if (*xOut + scaled_width + 1 >= 255) { - *xOut = 0; - *yOut += *maxHeight + 1; - } - - if (*yOut + *maxHeight + 1 >= 255) { - *yOut = -1; - *xOut = -1; - ri.Free(bitmap->buffer); - ri.Free(bitmap); - return &glyph; - } - - - src = bitmap->buffer; - dst = imageOut + (*yOut * 256) + *xOut; - - if (bitmap->pixel_mode == ft_pixel_mode_mono) { - for (i = 0; i < glyph.height; i++) { - int j; - unsigned char *_src = src; - unsigned char *_dst = dst; - unsigned char mask = 0x80; - unsigned char val = *_src; - for (j = 0; j < glyph.pitch; j++) { - if (mask == 0x80) { - val = *_src++; - } - if (val & mask) { - *_dst = 0xff; - } - mask >>= 1; - - if ( mask == 0 ) { - mask = 0x80; - } - _dst++; - } - - src += glyph.pitch; - dst += 256; - } - } else { - for (i = 0; i < glyph.height; i++) { - Com_Memcpy(dst, src, glyph.pitch); - src += glyph.pitch; - dst += 256; - } - } - - // we now have an 8 bit per pixel grey scale bitmap - // that is width wide and pf->ftSize->metrics.y_ppem tall - - glyph.imageHeight = scaled_height; - glyph.imageWidth = scaled_width; - glyph.s = (float)*xOut / 256; - glyph.t = (float)*yOut / 256; - glyph.s2 = glyph.s + (float)scaled_width / 256; - glyph.t2 = glyph.t + (float)scaled_height / 256; - - *xOut += scaled_width + 1; - - ri.Free(bitmap->buffer); - ri.Free(bitmap); - } - - return &glyph; -} -#endif - -static int fdOffset; -static byte *fdFile; - -int readInt( void ) { - int i = fdFile[fdOffset]+(fdFile[fdOffset+1]<<8)+(fdFile[fdOffset+2]<<16)+(fdFile[fdOffset+3]<<24); - fdOffset += 4; - return i; -} - -typedef union { - byte fred[4]; - float ffred; -} poor; - -float readFloat( void ) { - poor me; -#if defined Q3_BIG_ENDIAN - me.fred[0] = fdFile[fdOffset+3]; - me.fred[1] = fdFile[fdOffset+2]; - me.fred[2] = fdFile[fdOffset+1]; - me.fred[3] = fdFile[fdOffset+0]; -#elif defined Q3_LITTLE_ENDIAN - me.fred[0] = fdFile[fdOffset+0]; - me.fred[1] = fdFile[fdOffset+1]; - me.fred[2] = fdFile[fdOffset+2]; - me.fred[3] = fdFile[fdOffset+3]; -#endif - fdOffset += 4; - return me.ffred; -} - -void RE_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font) { -#ifdef BUILD_FREETYPE - FT_Face face; - int j, k, xOut, yOut, lastStart, imageNumber; - int scaledSize, newSize, maxHeight, left; - unsigned char *out, *imageBuff; - glyphInfo_t *glyph; - image_t *image; - qhandle_t h; - float max; - float dpi = 72; - float glyphScale; -#endif - void *faceData; - int i, len; - char name[1024]; - - if (!fontName) { - ri.Printf(PRINT_ALL, "RE_RegisterFont: called with empty name\n"); - return; - } - - if (pointSize <= 0) { - pointSize = 12; - } - - R_IssuePendingRenderCommands(); - - if (registeredFontCount >= MAX_FONTS) { - ri.Printf(PRINT_WARNING, "RE_RegisterFont: Too many fonts registered already.\n"); - return; - } - - Com_sprintf(name, sizeof(name), "fonts/fontImage_%i.dat",pointSize); - for (i = 0; i < registeredFontCount; i++) { - if (Q_stricmp(name, registeredFont[i].name) == 0) { - Com_Memcpy(font, ®isteredFont[i], sizeof(fontInfo_t)); - return; - } - } - - len = ri.FS_ReadFile(name, NULL); - if (len == sizeof(fontInfo_t)) { - ri.FS_ReadFile(name, &faceData); - fdOffset = 0; - fdFile = faceData; - for(i=0; iglyphs[i].height = readInt(); - font->glyphs[i].top = readInt(); - font->glyphs[i].bottom = readInt(); - font->glyphs[i].pitch = readInt(); - font->glyphs[i].xSkip = readInt(); - font->glyphs[i].imageWidth = readInt(); - font->glyphs[i].imageHeight = readInt(); - font->glyphs[i].s = readFloat(); - font->glyphs[i].t = readFloat(); - font->glyphs[i].s2 = readFloat(); - font->glyphs[i].t2 = readFloat(); - font->glyphs[i].glyph = readInt(); - Q_strncpyz(font->glyphs[i].shaderName, (const char *)&fdFile[fdOffset], sizeof(font->glyphs[i].shaderName)); - fdOffset += sizeof(font->glyphs[i].shaderName); - } - font->glyphScale = readFloat(); - Com_Memcpy(font->name, &fdFile[fdOffset], MAX_QPATH); - -// Com_Memcpy(font, faceData, sizeof(fontInfo_t)); - Q_strncpyz(font->name, name, sizeof(font->name)); - for (i = GLYPH_START; i < GLYPH_END; i++) { - font->glyphs[i].glyph = RE_RegisterShaderNoMip(font->glyphs[i].shaderName); - } - Com_Memcpy(®isteredFont[registeredFontCount++], font, sizeof(fontInfo_t)); - return; - } - -#ifndef BUILD_FREETYPE - ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType code not available\n"); -#else - if (ftLibrary == NULL) { - ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType not initialized.\n"); - return; - } - - len = ri.FS_ReadFile(fontName, &faceData); - if (len <= 0) { - ri.Printf(PRINT_WARNING, "RE_RegisterFont: Unable to read font file '%s'\n", fontName); - return; - } - - // allocate on the stack first in case we fail - if (FT_New_Memory_Face( ftLibrary, faceData, len, 0, &face )) { - ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType, unable to allocate new face.\n"); - return; - } - - - if (FT_Set_Char_Size( face, pointSize << 6, pointSize << 6, dpi, dpi)) { - ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType, unable to set face char size.\n"); - return; - } - - //*font = ®isteredFonts[registeredFontCount++]; - - // make a 256x256 image buffer, once it is full, register it, clean it and keep going - // until all glyphs are rendered - - out = ri.Malloc(1024*1024); - if (out == NULL) { - ri.Printf(PRINT_WARNING, "RE_RegisterFont: ri.Malloc failure during output image creation.\n"); - return; - } - Com_Memset(out, 0, 1024*1024); - - maxHeight = 0; - - for (i = GLYPH_START; i < GLYPH_END; i++) { - RE_ConstructGlyphInfo(out, &xOut, &yOut, &maxHeight, face, (unsigned char)i, qtrue); - } - - xOut = 0; - yOut = 0; - i = GLYPH_START; - lastStart = i; - imageNumber = 0; - - while ( i <= GLYPH_END ) { - - glyph = RE_ConstructGlyphInfo(out, &xOut, &yOut, &maxHeight, face, (unsigned char)i, qfalse); - - if (xOut == -1 || yOut == -1 || i == GLYPH_END) { - // ran out of room - // we need to create an image from the bitmap, set all the handles in the glyphs to this point - // - - scaledSize = 256*256; - newSize = scaledSize * 4; - imageBuff = ri.Malloc(newSize); - left = 0; - max = 0; - for ( k = 0; k < (scaledSize) ; k++ ) { - if (max < out[k]) { - max = out[k]; - } - } - - if (max > 0) { - max = 255/max; - } - - for ( k = 0; k < (scaledSize) ; k++ ) { - imageBuff[left++] = 255; - imageBuff[left++] = 255; - imageBuff[left++] = 255; - - imageBuff[left++] = ((float)out[k] * max); - } - - Com_sprintf (name, sizeof(name), "fonts/fontImage_%i_%i.tga", imageNumber++, pointSize); - if (r_saveFontData->integer) { - WriteTGA(name, imageBuff, 256, 256); - } - - //Com_sprintf (name, sizeof(name), "fonts/fontImage_%i_%i", imageNumber++, pointSize); - image = R_CreateImage(name, imageBuff, 256, 256, qfalse, qfalse, GL_CLAMP_TO_EDGE); - h = RE_RegisterShaderFromImage(name, LIGHTMAP_2D, image, qfalse); - for (j = lastStart; j < i; j++) { - font->glyphs[j].glyph = h; - Q_strncpyz(font->glyphs[j].shaderName, name, sizeof(font->glyphs[j].shaderName)); - } - lastStart = i; - Com_Memset(out, 0, 1024*1024); - xOut = 0; - yOut = 0; - ri.Free(imageBuff); - i++; - } else { - Com_Memcpy(&font->glyphs[i], glyph, sizeof(glyphInfo_t)); - i++; - } - } - - // change the scale to be relative to 1 based on 72 dpi ( so dpi of 144 means a scale of .5 ) - glyphScale = 72.0f / dpi; - - // we also need to adjust the scale based on point size relative to 48 points as the ui scaling is based on a 48 point font - glyphScale *= 48.0f / pointSize; - - registeredFont[registeredFontCount].glyphScale = glyphScale; - font->glyphScale = glyphScale; - Com_Memcpy(®isteredFont[registeredFontCount++], font, sizeof(fontInfo_t)); - - if (r_saveFontData->integer) { - ri.FS_WriteFile(va("fonts/fontImage_%i.dat", pointSize), font, sizeof(fontInfo_t)); - } - - ri.Free(out); - - ri.FS_FreeFile(faceData); -#endif -} - - - -void R_InitFreeType(void) { -#ifdef BUILD_FREETYPE - if (FT_Init_FreeType( &ftLibrary )) { - ri.Printf(PRINT_WARNING, "R_InitFreeType: Unable to initialize FreeType.\n"); - } -#endif - registeredFontCount = 0; -} - - -void R_DoneFreeType(void) { -#ifdef BUILD_FREETYPE - if (ftLibrary) { - FT_Done_FreeType( ftLibrary ); - ftLibrary = NULL; - } -#endif - registeredFontCount = 0; -} - diff --git a/src/renderer/tr_image.c b/src/renderer/tr_image.c deleted file mode 100644 index 3338a25b..00000000 --- a/src/renderer/tr_image.c +++ /dev/null @@ -1,1608 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_image.c -#include "tr_local.h" - -static byte s_intensitytable[256]; -static unsigned char s_gammatable[256]; - -int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; -int gl_filter_max = GL_LINEAR; - -#define FILE_HASH_SIZE 1024 -static image_t* hashTable[FILE_HASH_SIZE]; - -/* -** R_GammaCorrect -*/ -void R_GammaCorrect( byte *buffer, int bufSize ) { - int i; - - for ( i = 0; i < bufSize; i++ ) { - buffer[i] = s_gammatable[buffer[i]]; - } -} - -typedef struct { - char *name; - int minimize, maximize; -} textureMode_t; - -textureMode_t modes[] = { - {"GL_NEAREST", GL_NEAREST, GL_NEAREST}, - {"GL_LINEAR", GL_LINEAR, GL_LINEAR}, - {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, - {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, - {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, - {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} -}; - -/* -================ -return a hash value for the filename -================ -*/ -static long generateHashValue( const char *fname ) { - int i; - long hash; - char letter; - - hash = 0; - i = 0; - while (fname[i] != '\0') { - letter = tolower(fname[i]); - if (letter =='.') break; // don't include extension - if (letter =='\\') letter = '/'; // damn path names - hash+=(long)(letter)*(i+119); - i++; - } - hash &= (FILE_HASH_SIZE-1); - return hash; -} - -/* -=============== -GL_TextureMode -=============== -*/ -void GL_TextureMode( const char *string ) { - int i; - image_t *glt; - - for ( i=0 ; i< 6 ; i++ ) { - if ( !Q_stricmp( modes[i].name, string ) ) { - break; - } - } - - // hack to prevent trilinear from being set on voodoo, - // because their driver freaks... - if ( i == 5 && glConfig.hardwareType == GLHW_3DFX_2D3D ) { - ri.Printf( PRINT_ALL, "Refusing to set trilinear on a voodoo.\n" ); - i = 3; - } - - - if ( i == 6 ) { - ri.Printf (PRINT_ALL, "bad filter name\n"); - return; - } - - gl_filter_min = modes[i].minimize; - gl_filter_max = modes[i].maximize; - - // change all the existing mipmap texture objects - for ( i = 0 ; i < tr.numImages ; i++ ) { - glt = tr.images[ i ]; - if ( glt->mipmap ) { - GL_Bind (glt); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); - } - } -} - -/* -=============== -R_SumOfUsedImages -=============== -*/ -int R_SumOfUsedImages( void ) { - int total; - int i; - - total = 0; - for ( i = 0; i < tr.numImages; i++ ) { - if ( tr.images[i]->frameUsed == tr.frameCount ) { - total += tr.images[i]->uploadWidth * tr.images[i]->uploadHeight; - } - } - - return total; -} - -/* -=============== -R_ImageList_f -=============== -*/ -void R_ImageList_f( void ) { - int i; - image_t *image; - int texels; - const char *yesno[] = { - "no ", "yes" - }; - - ri.Printf (PRINT_ALL, "\n -w-- -h-- -mm- -TMU- -if-- wrap --name-------\n"); - texels = 0; - - for ( i = 0 ; i < tr.numImages ; i++ ) { - image = tr.images[ i ]; - - texels += image->uploadWidth*image->uploadHeight; - ri.Printf (PRINT_ALL, "%4i: %4i %4i %s %d ", - i, image->uploadWidth, image->uploadHeight, yesno[image->mipmap], image->TMU ); - switch ( image->internalFormat ) { - case 1: - ri.Printf( PRINT_ALL, "I " ); - break; - case 2: - ri.Printf( PRINT_ALL, "IA " ); - break; - case 3: - ri.Printf( PRINT_ALL, "RGB " ); - break; - case 4: - ri.Printf( PRINT_ALL, "RGBA " ); - break; - case GL_RGBA8: - ri.Printf( PRINT_ALL, "RGBA8" ); - break; - case GL_RGB8: - ri.Printf( PRINT_ALL, "RGB8" ); - break; - case GL_RGB4_S3TC: - case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: - ri.Printf( PRINT_ALL, "S3TC " ); - break; - case GL_RGBA4: - ri.Printf( PRINT_ALL, "RGBA4" ); - break; - case GL_RGB5: - ri.Printf( PRINT_ALL, "RGB5 " ); - break; - default: - ri.Printf( PRINT_ALL, "???? " ); - } - - switch ( image->wrapClampMode ) { - case GL_REPEAT: - ri.Printf( PRINT_ALL, "rept " ); - break; - case GL_CLAMP_TO_EDGE: - ri.Printf( PRINT_ALL, "clmp " ); - break; - default: - ri.Printf( PRINT_ALL, "%4i ", image->wrapClampMode ); - break; - } - - ri.Printf( PRINT_ALL, " %s\n", image->imgName ); - } - ri.Printf (PRINT_ALL, " ---------\n"); - ri.Printf (PRINT_ALL, " %i total texels (not including mipmaps)\n", texels); - ri.Printf (PRINT_ALL, " %i total images\n\n", tr.numImages ); -} - -//======================================================================= - -/* -================ -ResampleTexture - -Used to resample images in a more general than quartering fashion. - -This will only be filtered properly if the resampled size -is greater than half the original size. - -If a larger shrinking is needed, use the mipmap function -before or after. -================ -*/ -static void ResampleTexture( unsigned *in, int inwidth, int inheight, unsigned *out, - int outwidth, int outheight ) { - int i, j; - unsigned *inrow, *inrow2; - unsigned frac, fracstep; - unsigned p1[2048], p2[2048]; - byte *pix1, *pix2, *pix3, *pix4; - - if (outwidth>2048) - ri.Error(ERR_DROP, "ResampleTexture: max width"); - - fracstep = inwidth*0x10000/outwidth; - - frac = fracstep>>2; - for ( i=0 ; i>16); - frac += fracstep; - } - frac = 3*(fracstep>>2); - for ( i=0 ; i>16); - frac += fracstep; - } - - for (i=0 ; i> 1; - for (j=0 ; j>2; - ((byte *)(out+j))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2; - ((byte *)(out+j))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2; - ((byte *)(out+j))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2; - } - } -} - -/* -================ -R_LightScaleTexture - -Scale up the pixel values in a texture to increase the -lighting range -================ -*/ -void R_LightScaleTexture (unsigned *in, int inwidth, int inheight, qboolean only_gamma ) -{ - if ( only_gamma ) - { - if ( !glConfig.deviceSupportsGamma ) - { - int i, c; - byte *p; - - p = (byte *)in; - - c = inwidth*inheight; - for (i=0 ; i> 1; - outHeight = inHeight >> 1; - temp = ri.Hunk_AllocateTempMemory( outWidth * outHeight * 4 ); - - inWidthMask = inWidth - 1; - inHeightMask = inHeight - 1; - - for ( i = 0 ; i < outHeight ; i++ ) { - for ( j = 0 ; j < outWidth ; j++ ) { - outpix = (byte *) ( temp + i * outWidth + j ); - for ( k = 0 ; k < 4 ; k++ ) { - total = - 1 * ((byte *)&in[ ((i*2-1)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask) ])[k] + - 2 * ((byte *)&in[ ((i*2-1)&inHeightMask)*inWidth + ((j*2)&inWidthMask) ])[k] + - 2 * ((byte *)&in[ ((i*2-1)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask) ])[k] + - 1 * ((byte *)&in[ ((i*2-1)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask) ])[k] + - - 2 * ((byte *)&in[ ((i*2)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask) ])[k] + - 4 * ((byte *)&in[ ((i*2)&inHeightMask)*inWidth + ((j*2)&inWidthMask) ])[k] + - 4 * ((byte *)&in[ ((i*2)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask) ])[k] + - 2 * ((byte *)&in[ ((i*2)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask) ])[k] + - - 2 * ((byte *)&in[ ((i*2+1)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask) ])[k] + - 4 * ((byte *)&in[ ((i*2+1)&inHeightMask)*inWidth + ((j*2)&inWidthMask) ])[k] + - 4 * ((byte *)&in[ ((i*2+1)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask) ])[k] + - 2 * ((byte *)&in[ ((i*2+1)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask) ])[k] + - - 1 * ((byte *)&in[ ((i*2+2)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask) ])[k] + - 2 * ((byte *)&in[ ((i*2+2)&inHeightMask)*inWidth + ((j*2)&inWidthMask) ])[k] + - 2 * ((byte *)&in[ ((i*2+2)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask) ])[k] + - 1 * ((byte *)&in[ ((i*2+2)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask) ])[k]; - outpix[k] = total / 36; - } - } - } - - Com_Memcpy( in, temp, outWidth * outHeight * 4 ); - ri.Hunk_FreeTempMemory( temp ); -} - -/* -================ -R_MipMap - -Operates in place, quartering the size of the texture -================ -*/ -static void R_MipMap (byte *in, int width, int height) { - int i, j; - byte *out; - int row; - - if ( !r_simpleMipMaps->integer ) { - R_MipMap2( (unsigned *)in, width, height ); - return; - } - - if ( width == 1 && height == 1 ) { - return; - } - - row = width * 4; - out = in; - width >>= 1; - height >>= 1; - - if ( width == 0 || height == 0 ) { - width += height; // get largest - for (i=0 ; i>1; - out[1] = ( in[1] + in[5] )>>1; - out[2] = ( in[2] + in[6] )>>1; - out[3] = ( in[3] + in[7] )>>1; - } - return; - } - - for (i=0 ; i>2; - out[1] = (in[1] + in[5] + in[row+1] + in[row+5])>>2; - out[2] = (in[2] + in[6] + in[row+2] + in[row+6])>>2; - out[3] = (in[3] + in[7] + in[row+3] + in[row+7])>>2; - } - } -} - - -/* -================== -R_BlendOverTexture - -Apply a color blend over a set of pixels -================== -*/ -static void R_BlendOverTexture( byte *data, int pixelCount, byte blend[4] ) { - int i; - int inverseAlpha; - int premult[3]; - - inverseAlpha = 255 - blend[3]; - premult[0] = blend[0] * blend[3]; - premult[1] = blend[1] * blend[3]; - premult[2] = blend[2] * blend[3]; - - for ( i = 0 ; i < pixelCount ; i++, data+=4 ) { - data[0] = ( data[0] * inverseAlpha + premult[0] ) >> 9; - data[1] = ( data[1] * inverseAlpha + premult[1] ) >> 9; - data[2] = ( data[2] * inverseAlpha + premult[2] ) >> 9; - } -} - -byte mipBlendColors[16][4] = { - {0,0,0,0}, - {255,0,0,128}, - {0,255,0,128}, - {0,0,255,128}, - {255,0,0,128}, - {0,255,0,128}, - {0,0,255,128}, - {255,0,0,128}, - {0,255,0,128}, - {0,0,255,128}, - {255,0,0,128}, - {0,255,0,128}, - {0,0,255,128}, - {255,0,0,128}, - {0,255,0,128}, - {0,0,255,128}, -}; - - -/* -=============== -Upload32 - -=============== -*/ -extern qboolean charSet; -static void Upload32( unsigned *data, - int width, int height, - qboolean mipmap, - qboolean picmip, - qboolean lightMap, - int *format, - int *pUploadWidth, int *pUploadHeight ) -{ - int samples; - unsigned *scaledBuffer = NULL; - unsigned *resampledBuffer = NULL; - int scaled_width, scaled_height; - int i, c; - byte *scan; - GLenum internalFormat = GL_RGB; - float rMax = 0, gMax = 0, bMax = 0; - - // - // convert to exact power of 2 sizes - // - for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1) - ; - for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1) - ; - if ( r_roundImagesDown->integer && scaled_width > width ) - scaled_width >>= 1; - if ( r_roundImagesDown->integer && scaled_height > height ) - scaled_height >>= 1; - - if ( scaled_width != width || scaled_height != height ) { - resampledBuffer = ri.Hunk_AllocateTempMemory( scaled_width * scaled_height * 4 ); - ResampleTexture (data, width, height, resampledBuffer, scaled_width, scaled_height); - data = resampledBuffer; - width = scaled_width; - height = scaled_height; - } - - // - // perform optional picmip operation - // - if ( picmip ) { - scaled_width >>= r_picmip->integer; - scaled_height >>= r_picmip->integer; - } - - // - // clamp to minimum size - // - if (scaled_width < 1) { - scaled_width = 1; - } - if (scaled_height < 1) { - scaled_height = 1; - } - - // - // clamp to the current upper OpenGL limit - // scale both axis down equally so we don't have to - // deal with a half mip resampling - // - while ( scaled_width > glConfig.maxTextureSize - || scaled_height > glConfig.maxTextureSize ) { - scaled_width >>= 1; - scaled_height >>= 1; - } - - scaledBuffer = ri.Hunk_AllocateTempMemory( sizeof( unsigned ) * scaled_width * scaled_height ); - - // - // scan the texture for each channel's max values - // and verify if the alpha channel is being used or not - // - c = width*height; - scan = ((byte *)data); - samples = 3; - - if( r_greyscale->integer ) - { - for ( i = 0; i < c; i++ ) - { - byte luma = LUMA(scan[i*4], scan[i*4 + 1], scan[i*4 + 2]); - scan[i*4] = luma; - scan[i*4 + 1] = luma; - scan[i*4 + 2] = luma; - } - } - else if( r_greyscale->value ) - { - for ( i = 0; i < c; i++ ) - { - float luma = LUMA(scan[i*4], scan[i*4 + 1], scan[i*4 + 2]); - scan[i*4] = LERP(scan[i*4], luma, r_greyscale->value); - scan[i*4 + 1] = LERP(scan[i*4 + 1], luma, r_greyscale->value); - scan[i*4 + 2] = LERP(scan[i*4 + 2], luma, r_greyscale->value); - } - } - - if(lightMap) - { - if(r_greyscale->integer) - internalFormat = GL_LUMINANCE; - else - internalFormat = GL_RGB; - } - else - { - for ( i = 0; i < c; i++ ) - { - if ( scan[i*4+0] > rMax ) - { - rMax = scan[i*4+0]; - } - if ( scan[i*4+1] > gMax ) - { - gMax = scan[i*4+1]; - } - if ( scan[i*4+2] > bMax ) - { - bMax = scan[i*4+2]; - } - if ( scan[i*4 + 3] != 255 ) - { - samples = 4; - break; - } - } - // select proper internal format - if ( samples == 3 ) - { - if(r_greyscale->integer) - { - if(r_texturebits->integer == 16) - internalFormat = GL_LUMINANCE8; - else if(r_texturebits->integer == 32) - internalFormat = GL_LUMINANCE16; - else - internalFormat = GL_LUMINANCE; - } - else - { - if ( glConfig.textureCompression == TC_S3TC_ARB ) - { - internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - } - else if ( glConfig.textureCompression == TC_S3TC ) - { - internalFormat = GL_RGB4_S3TC; - } - else if ( r_texturebits->integer == 16 ) - { - internalFormat = GL_RGB5; - } - else if ( r_texturebits->integer == 32 ) - { - internalFormat = GL_RGB8; - } - else - { - internalFormat = GL_RGB; - } - } - } - else if ( samples == 4 ) - { - if(r_greyscale->integer) - { - if(r_texturebits->integer == 16) - internalFormat = GL_LUMINANCE8_ALPHA8; - else if(r_texturebits->integer == 32) - internalFormat = GL_LUMINANCE16_ALPHA16; - else - internalFormat = GL_LUMINANCE_ALPHA; - } - else - { - if ( r_texturebits->integer == 16 ) - { - internalFormat = GL_RGBA4; - } - else if ( r_texturebits->integer == 32 ) - { - internalFormat = GL_RGBA8; - } - else - { - internalFormat = GL_RGBA; - } - } - } - } - - // copy or resample data as appropriate for first MIP level - if ( ( scaled_width == width ) && - ( scaled_height == height ) ) { - if (!mipmap) - { - qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); - *pUploadWidth = scaled_width; - *pUploadHeight = scaled_height; - *format = internalFormat; - - goto done; - } - Com_Memcpy (scaledBuffer, data, width*height*4); - } - else - { - // use the normal mip-mapping function to go down from here - while ( width > scaled_width || height > scaled_height ) { - R_MipMap( (byte *)data, width, height ); - width >>= 1; - height >>= 1; - if ( width < 1 ) { - width = 1; - } - if ( height < 1 ) { - height = 1; - } - } - Com_Memcpy( scaledBuffer, data, width * height * 4 ); - } - - R_LightScaleTexture (scaledBuffer, scaled_width, scaled_height, !mipmap ); - - *pUploadWidth = scaled_width; - *pUploadHeight = scaled_height; - *format = internalFormat; - - qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer ); - - if (mipmap) - { - int miplevel; - - miplevel = 0; - while (scaled_width > 1 || scaled_height > 1) - { - R_MipMap( (byte *)scaledBuffer, scaled_width, scaled_height ); - scaled_width >>= 1; - scaled_height >>= 1; - if (scaled_width < 1) - scaled_width = 1; - if (scaled_height < 1) - scaled_height = 1; - miplevel++; - - if ( r_colorMipLevels->integer ) { - R_BlendOverTexture( (byte *)scaledBuffer, scaled_width * scaled_height, mipBlendColors[miplevel] ); - } - - qglTexImage2D (GL_TEXTURE_2D, miplevel, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer ); - } - } -done: - - if (mipmap) - { - if ( glConfig.textureFilterAnisotropic ) - qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, - (GLint)Com_Clamp( 1, glConfig.maxAnisotropy, r_ext_max_anisotropy->integer ) ); - - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); - } - else - { - if ( glConfig.textureFilterAnisotropic ) - qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 ); - - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - } - - GL_CheckErrors(); - - if ( scaledBuffer != 0 ) - ri.Hunk_FreeTempMemory( scaledBuffer ); - if ( resampledBuffer != 0 ) - ri.Hunk_FreeTempMemory( resampledBuffer ); -} - - -/* -================ -R_CreateImage - -This is the only way any image_t are created -================ -*/ -image_t *R_CreateImage( const char *name, const byte *pic, int width, int height, - qboolean mipmap, qboolean allowPicmip, int glWrapClampMode ) { - image_t *image; - qboolean isLightmap = qfalse; - long hash; - - if (strlen(name) >= MAX_QPATH ) { - ri.Error (ERR_DROP, "R_CreateImage: \"%s\" is too long", name); - } - if ( !strncmp( name, "*lightmap", 9 ) ) { - isLightmap = qtrue; - } - - if ( tr.numImages == MAX_DRAWIMAGES ) { - ri.Error( ERR_DROP, "R_CreateImage: MAX_DRAWIMAGES hit"); - } - - image = tr.images[tr.numImages] = ri.Hunk_Alloc( sizeof( image_t ), h_low ); - image->texnum = 1024 + tr.numImages; - tr.numImages++; - - image->mipmap = mipmap; - image->allowPicmip = allowPicmip; - - strcpy (image->imgName, name); - - image->width = width; - image->height = height; - image->wrapClampMode = glWrapClampMode; - - // lightmaps are always allocated on TMU 1 - if ( qglActiveTextureARB && isLightmap ) { - image->TMU = 1; - } else { - image->TMU = 0; - } - - if ( qglActiveTextureARB ) { - GL_SelectTexture( image->TMU ); - } - - GL_Bind(image); - - Upload32( (unsigned *)pic, image->width, image->height, - image->mipmap, - allowPicmip, - isLightmap, - &image->internalFormat, - &image->uploadWidth, - &image->uploadHeight ); - - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrapClampMode ); - qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrapClampMode ); - - qglBindTexture( GL_TEXTURE_2D, 0 ); - - if ( image->TMU == 1 ) { - GL_SelectTexture( 0 ); - } - - hash = generateHashValue(name); - image->next = hashTable[hash]; - hashTable[hash] = image; - - return image; -} - -//=================================================================== - -typedef struct -{ - char *ext; - void (*ImageLoader)( const char *, unsigned char **, int *, int * ); -} imageExtToLoaderMap_t; - -// Note that the ordering indicates the order of preference used -// when there are multiple images of different formats available -static imageExtToLoaderMap_t imageLoaders[ ] = -{ - { "tga", R_LoadTGA }, - { "jpg", R_LoadJPG }, - { "jpeg", R_LoadJPG }, - { "png", R_LoadPNG }, - { "pcx", R_LoadPCX }, - { "bmp", R_LoadBMP } -}; - -static int numImageLoaders = ARRAY_LEN( imageLoaders ); - -/* -================= -R_LoadImage - -Loads any of the supported image types into a cannonical -32 bit format. -================= -*/ -void R_LoadImage( const char *name, byte **pic, int *width, int *height ) -{ - qboolean orgNameFailed = qfalse; - int orgLoader = -1; - int i; - char localName[ MAX_QPATH ]; - const char *ext; - char *altName; - - *pic = NULL; - *width = 0; - *height = 0; - - Q_strncpyz( localName, name, MAX_QPATH ); - - ext = COM_GetExtension( localName ); - - if( *ext ) - { - // Look for the correct loader and use it - for( i = 0; i < numImageLoaders; i++ ) - { - if( !Q_stricmp( ext, imageLoaders[ i ].ext ) ) - { - // Load - imageLoaders[ i ].ImageLoader( localName, pic, width, height ); - break; - } - } - - // A loader was found - if( i < numImageLoaders ) - { - if( *pic == NULL ) - { - // Loader failed, most likely because the file isn't there; - // try again without the extension - orgNameFailed = qtrue; - orgLoader = i; - COM_StripExtension( name, localName, MAX_QPATH ); - } - else - { - // Something loaded - return; - } - } - } - - // Try and find a suitable match using all - // the image formats supported - for( i = 0; i < numImageLoaders; i++ ) - { - if (i == orgLoader) - continue; - - altName = va( "%s.%s", localName, imageLoaders[ i ].ext ); - - // Load - imageLoaders[ i ].ImageLoader( altName, pic, width, height ); - - if( *pic ) - { - if( orgNameFailed ) - { - ri.Printf( PRINT_DEVELOPER, "WARNING: %s not present, using %s instead\n", - name, altName ); - } - - break; - } - } -} - - -/* -=============== -R_FindImageFile - -Finds or loads the given image. -Returns NULL if it fails, not a default image. -============== -*/ -image_t *R_FindImageFile( const char *name, qboolean mipmap, qboolean allowPicmip, int glWrapClampMode ) { - image_t *image; - int width, height; - byte *pic; - long hash; - - if (!name) { - return NULL; - } - - hash = generateHashValue(name); - - // - // see if the image is already loaded - // - for (image=hashTable[hash]; image; image=image->next) { - if ( !strcmp( name, image->imgName ) ) { - // the white image can be used with any set of parms, but other mismatches are errors - if ( strcmp( name, "*white" ) ) { - if ( image->mipmap != mipmap ) { - ri.Printf( PRINT_DEVELOPER, "WARNING: reused image %s with mixed mipmap parm\n", name ); - } - if ( image->allowPicmip != allowPicmip ) { - ri.Printf( PRINT_DEVELOPER, "WARNING: reused image %s with mixed allowPicmip parm\n", name ); - } - if ( image->wrapClampMode != glWrapClampMode ) { - ri.Printf( PRINT_ALL, "WARNING: reused image %s with mixed glWrapClampMode parm\n", name ); - } - } - return image; - } - } - - // - // load the pic from disk - // - R_LoadImage( name, &pic, &width, &height ); - if ( pic == NULL ) { - return NULL; - } - - image = R_CreateImage( ( char * ) name, pic, width, height, mipmap, allowPicmip, glWrapClampMode ); - ri.Free( pic ); - return image; -} - - -/* -================ -R_CreateDlightImage -================ -*/ -#define DLIGHT_SIZE 16 -static void R_CreateDlightImage( void ) { - int x,y; - byte data[DLIGHT_SIZE][DLIGHT_SIZE][4]; - int b; - - // make a centered inverse-square falloff blob for dynamic lighting - for (x=0 ; x 255) { - b = 255; - } else if ( b < 75 ) { - b = 0; - } - data[y][x][0] = - data[y][x][1] = - data[y][x][2] = b; - data[y][x][3] = 255; - } - } - tr.dlightImage = R_CreateImage("*dlight", (byte *)data, DLIGHT_SIZE, DLIGHT_SIZE, qfalse, qfalse, GL_CLAMP_TO_EDGE ); -} - - -/* -================= -R_InitFogTable -================= -*/ -void R_InitFogTable( void ) { - int i; - float d; - float exp; - - exp = 0.5; - - for ( i = 0 ; i < FOG_TABLE_SIZE ; i++ ) { - d = pow ( (float)i/(FOG_TABLE_SIZE-1), exp ); - - tr.fogTable[i] = d; - } -} - -/* -================ -R_FogFactor - -Returns a 0.0 to 1.0 fog density value -This is called for each texel of the fog texture on startup -and for each vertex of transparent shaders in fog dynamically -================ -*/ -float R_FogFactor( float s, float t ) { - float d; - - s -= 1.0/512; - if ( s < 0 ) { - return 0; - } - if ( t < 1.0/32 ) { - return 0; - } - if ( t < 31.0/32 ) { - s *= (t - 1.0f/32.0f) / (30.0f/32.0f); - } - - // we need to leave a lot of clamp range - s *= 8; - - if ( s > 1.0 ) { - s = 1.0; - } - - d = tr.fogTable[ (int)(s * (FOG_TABLE_SIZE-1)) ]; - - return d; -} - -/* -================ -R_CreateFogImage -================ -*/ -#define FOG_S 256 -#define FOG_T 32 -static void R_CreateFogImage( void ) { - int x,y; - byte *data; - float d; - float borderColor[4]; - - data = ri.Hunk_AllocateTempMemory( FOG_S * FOG_T * 4 ); - - // S is distance, T is depth - for (x=0 ; xinteger; - if ( !glConfig.deviceSupportsGamma ) { - tr.overbrightBits = 0; // need hardware gamma for overbright - } - - // never overbright in windowed mode - if ( !glConfig.isFullscreen ) - { - tr.overbrightBits = 0; - } - - // allow 2 overbright bits in 24 bit, but only 1 in 16 bit - if ( glConfig.colorBits > 16 ) { - if ( tr.overbrightBits > 2 ) { - tr.overbrightBits = 2; - } - } else { - if ( tr.overbrightBits > 1 ) { - tr.overbrightBits = 1; - } - } - if ( tr.overbrightBits < 0 ) { - tr.overbrightBits = 0; - } - - tr.identityLight = 1.0f / ( 1 << tr.overbrightBits ); - tr.identityLightByte = 255 * tr.identityLight; - - - if ( r_intensity->value <= 1 ) { - ri.Cvar_Set( "r_intensity", "1" ); - } - - if ( r_gamma->value < 0.5f ) { - ri.Cvar_Set( "r_gamma", "0.5" ); - } else if ( r_gamma->value > 3.0f ) { - ri.Cvar_Set( "r_gamma", "3.0" ); - } - - g = r_gamma->value; - - shift = tr.overbrightBits; - - for ( i = 0; i < 256; i++ ) { - if ( g == 1 ) { - inf = i; - } else { - inf = 255 * pow ( i/255.0f, 1.0f / g ) + 0.5f; - } - inf <<= shift; - if (inf < 0) { - inf = 0; - } - if (inf > 255) { - inf = 255; - } - s_gammatable[i] = inf; - } - - for (i=0 ; i<256 ; i++) { - j = i * r_intensity->value; - if (j > 255) { - j = 255; - } - s_intensitytable[i] = j; - } - - if ( glConfig.deviceSupportsGamma ) - { - GLimp_SetGamma( s_gammatable, s_gammatable, s_gammatable ); - } -} - -/* -=============== -R_InitImages -=============== -*/ -void R_InitImages( void ) { - Com_Memset(hashTable, 0, sizeof(hashTable)); - // build brightness translation tables - R_SetColorMappings(); - - // create default texture and white texture - R_CreateBuiltinImages(); -} - -/* -=============== -R_DeleteTextures -=============== -*/ -void R_DeleteTextures( void ) { - int i; - - for ( i=0; itexnum ); - } - Com_Memset( tr.images, 0, sizeof( tr.images ) ); - - tr.numImages = 0; - - Com_Memset( glState.currenttextures, 0, sizeof( glState.currenttextures ) ); - if ( qglActiveTextureARB ) { - GL_SelectTexture( 1 ); - qglBindTexture( GL_TEXTURE_2D, 0 ); - GL_SelectTexture( 0 ); - qglBindTexture( GL_TEXTURE_2D, 0 ); - } else { - qglBindTexture( GL_TEXTURE_2D, 0 ); - } -} - -/* -============================================================================ - -SKINS - -============================================================================ -*/ - -/* -================== -CommaParse - -This is unfortunate, but the skin files aren't -compatable with our normal parsing rules. -================== -*/ -static char *CommaParse( char **data_p ) { - int c = 0, len; - char *data; - static char com_token[MAX_TOKEN_CHARS]; - - data = *data_p; - len = 0; - com_token[0] = 0; - - // make sure incoming data is valid - if ( !data ) { - *data_p = NULL; - return com_token; - } - - while ( 1 ) { - // skip whitespace - while( (c = *data) <= ' ') { - if( !c ) { - break; - } - data++; - } - - - c = *data; - - // skip double slash comments - if ( c == '/' && data[1] == '/' ) - { - while (*data && *data != '\n') - data++; - } - // skip /* */ comments - else if ( c=='/' && data[1] == '*' ) - { - while ( *data && ( *data != '*' || data[1] != '/' ) ) - { - data++; - } - if ( *data ) - { - data += 2; - } - } - else - { - break; - } - } - - if ( c == 0 ) { - return ""; - } - - // handle quoted strings - if (c == '\"') - { - data++; - while (1) - { - c = *data++; - if (c=='\"' || !c) - { - com_token[len] = 0; - *data_p = ( char * ) data; - return com_token; - } - if (len < MAX_TOKEN_CHARS) - { - com_token[len] = c; - len++; - } - } - } - - // parse a regular word - do - { - if (len < MAX_TOKEN_CHARS) - { - com_token[len] = c; - len++; - } - data++; - c = *data; - } while (c>32 && c != ',' ); - - if (len == MAX_TOKEN_CHARS) - { -// ri.Printf (PRINT_DEVELOPER, "Token exceeded %i chars, discarded.\n", MAX_TOKEN_CHARS); - len = 0; - } - com_token[len] = 0; - - *data_p = ( char * ) data; - return com_token; -} - - -/* -=============== -RE_RegisterSkin - -=============== -*/ -qhandle_t RE_RegisterSkin( const char *name ) { - qhandle_t hSkin; - skin_t *skin; - skinSurface_t *surf; - union { - char *c; - void *v; - } text; - char *text_p; - char *token; - char surfName[MAX_QPATH]; - - if ( !name || !name[0] ) { - ri.Printf( PRINT_DEVELOPER, "Empty name passed to RE_RegisterSkin\n" ); - return 0; - } - - if ( strlen( name ) >= MAX_QPATH ) { - ri.Printf( PRINT_DEVELOPER, "Skin name exceeds MAX_QPATH\n" ); - return 0; - } - - - // see if the skin is already loaded - for ( hSkin = 1; hSkin < tr.numSkins ; hSkin++ ) { - skin = tr.skins[hSkin]; - if ( !Q_stricmp( skin->name, name ) ) { - if( skin->numSurfaces == 0 ) { - return 0; // default skin - } - return hSkin; - } - } - - // allocate a new skin - if ( tr.numSkins == MAX_SKINS ) { - ri.Printf( PRINT_WARNING, "WARNING: RE_RegisterSkin( '%s' ) MAX_SKINS hit\n", name ); - return 0; - } - tr.numSkins++; - skin = ri.Hunk_Alloc( sizeof( skin_t ), h_low ); - tr.skins[hSkin] = skin; - Q_strncpyz( skin->name, name, sizeof( skin->name ) ); - skin->numSurfaces = 0; - - R_IssuePendingRenderCommands(); - - // If not a .skin file, load as a single shader - if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) { - skin->numSurfaces = 1; - skin->surfaces[0] = ri.Hunk_Alloc( sizeof(skin->surfaces[0]), h_low ); - skin->surfaces[0]->shader = R_FindShader( name, LIGHTMAP_NONE, qtrue ); - return hSkin; - } - - // load and parse the skin file - ri.FS_ReadFile( name, &text.v ); - if ( !text.c ) { - return 0; - } - - text_p = text.c; - while ( text_p && *text_p ) { - // get surface name - token = CommaParse( &text_p ); - Q_strncpyz( surfName, token, sizeof( surfName ) ); - - if ( !token[0] ) { - break; - } - // lowercase the surface name so skin compares are faster - Q_strlwr( surfName ); - - if ( *text_p == ',' ) { - text_p++; - } - - if ( strstr( token, "tag_" ) ) { - continue; - } - - // parse the shader name - token = CommaParse( &text_p ); - - surf = skin->surfaces[ skin->numSurfaces ] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); - Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); - surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue ); - skin->numSurfaces++; - } - - ri.FS_FreeFile( text.v ); - - - // never let a skin have 0 shaders - if ( skin->numSurfaces == 0 ) { - return 0; // use default skin - } - - return hSkin; -} - - -/* -=============== -R_InitSkins -=============== -*/ -void R_InitSkins( void ) { - skin_t *skin; - - tr.numSkins = 1; - - // make the default skin have all default shaders - skin = tr.skins[0] = ri.Hunk_Alloc( sizeof( skin_t ), h_low ); - Q_strncpyz( skin->name, "", sizeof( skin->name ) ); - skin->numSurfaces = 1; - skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces ), h_low ); - skin->surfaces[0]->shader = tr.defaultShader; -} - -/* -=============== -R_GetSkinByHandle -=============== -*/ -skin_t *R_GetSkinByHandle( qhandle_t hSkin ) { - if ( hSkin < 1 || hSkin >= tr.numSkins ) { - return tr.skins[0]; - } - return tr.skins[ hSkin ]; -} - -/* -=============== -R_SkinList_f -=============== -*/ -void R_SkinList_f( void ) { - int i, j; - skin_t *skin; - - ri.Printf (PRINT_ALL, "------------------\n"); - - for ( i = 0 ; i < tr.numSkins ; i++ ) { - skin = tr.skins[i]; - - ri.Printf( PRINT_ALL, "%3i:%s\n", i, skin->name ); - for ( j = 0 ; j < skin->numSurfaces ; j++ ) { - ri.Printf( PRINT_ALL, " %s = %s\n", - skin->surfaces[j]->name, skin->surfaces[j]->shader->name ); - } - } - ri.Printf (PRINT_ALL, "------------------\n"); -} - diff --git a/src/renderer/tr_image_bmp.c b/src/renderer/tr_image_bmp.c deleted file mode 100644 index eed62c58..00000000 --- a/src/renderer/tr_image_bmp.c +++ /dev/null @@ -1,240 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "tr_local.h" - -typedef struct -{ - char id[2]; - unsigned fileSize; - unsigned reserved0; - unsigned bitmapDataOffset; - unsigned bitmapHeaderSize; - unsigned width; - unsigned height; - unsigned short planes; - unsigned short bitsPerPixel; - unsigned compression; - unsigned bitmapDataSize; - unsigned hRes; - unsigned vRes; - unsigned colors; - unsigned importantColors; - unsigned char palette[256][4]; -} BMPHeader_t; - -void R_LoadBMP( const char *name, byte **pic, int *width, int *height ) -{ - int columns, rows; - unsigned numPixels; - byte *pixbuf; - int row, column; - byte *buf_p; - byte *end; - union { - byte *b; - void *v; - } buffer; - int length; - BMPHeader_t bmpHeader; - byte *bmpRGBA; - - *pic = NULL; - - if(width) - *width = 0; - - if(height) - *height = 0; - - // - // load the file - // - length = ri.FS_ReadFile( ( char * ) name, &buffer.v); - if (!buffer.b || length < 0) { - return; - } - - if (length < 54) - { - ri.Error( ERR_DROP, "LoadBMP: header too short (%s)", name ); - } - - buf_p = buffer.b; - end = buffer.b + length; - - bmpHeader.id[0] = *buf_p++; - bmpHeader.id[1] = *buf_p++; - bmpHeader.fileSize = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.reserved0 = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.bitmapDataOffset = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.bitmapHeaderSize = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.width = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.height = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.planes = LittleShort( * ( short * ) buf_p ); - buf_p += 2; - bmpHeader.bitsPerPixel = LittleShort( * ( short * ) buf_p ); - buf_p += 2; - bmpHeader.compression = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.bitmapDataSize = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.hRes = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.vRes = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.colors = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - bmpHeader.importantColors = LittleLong( * ( int * ) buf_p ); - buf_p += 4; - - if ( bmpHeader.bitsPerPixel == 8 ) - { - if (buf_p + sizeof(bmpHeader.palette) > end) - ri.Error( ERR_DROP, "LoadBMP: header too short (%s)", name ); - - Com_Memcpy( bmpHeader.palette, buf_p, sizeof( bmpHeader.palette ) ); - buf_p += sizeof(bmpHeader.palette); - } - - if (buffer.b + bmpHeader.bitmapDataOffset > end) - { - ri.Error( ERR_DROP, "LoadBMP: invalid offset value in header (%s)", name ); - } - - buf_p = buffer.b + bmpHeader.bitmapDataOffset; - - if ( bmpHeader.id[0] != 'B' && bmpHeader.id[1] != 'M' ) - { - ri.Error( ERR_DROP, "LoadBMP: only Windows-style BMP files supported (%s)", name ); - } - if ( bmpHeader.fileSize != length ) - { - ri.Error( ERR_DROP, "LoadBMP: header size does not match file size (%u vs. %u) (%s)", bmpHeader.fileSize, length, name ); - } - if ( bmpHeader.compression != 0 ) - { - ri.Error( ERR_DROP, "LoadBMP: only uncompressed BMP files supported (%s)", name ); - } - if ( bmpHeader.bitsPerPixel < 8 ) - { - ri.Error( ERR_DROP, "LoadBMP: monochrome and 4-bit BMP files not supported (%s)", name ); - } - - switch ( bmpHeader.bitsPerPixel ) - { - case 8: - case 16: - case 24: - case 32: - break; - default: - ri.Error( ERR_DROP, "LoadBMP: illegal pixel_size '%hu' in file '%s'", bmpHeader.bitsPerPixel, name ); - break; - } - - columns = bmpHeader.width; - rows = bmpHeader.height; - if ( rows < 0 ) - rows = -rows; - numPixels = columns * rows; - - if(columns <= 0 || !rows || numPixels > 0x1FFFFFFF // 4*1FFFFFFF == 0x7FFFFFFC < 0x7FFFFFFF - || ((numPixels * 4) / columns) / 4 != rows) - { - ri.Error (ERR_DROP, "LoadBMP: %s has an invalid image size", name); - } - if(buf_p + numPixels*bmpHeader.bitsPerPixel/8 > end) - { - ri.Error (ERR_DROP, "LoadBMP: file truncated (%s)", name); - } - - if ( width ) - *width = columns; - if ( height ) - *height = rows; - - bmpRGBA = ri.Malloc( numPixels * 4 ); - *pic = bmpRGBA; - - - for ( row = rows-1; row >= 0; row-- ) - { - pixbuf = bmpRGBA + row*columns*4; - - for ( column = 0; column < columns; column++ ) - { - unsigned char red, green, blue, alpha; - int palIndex; - unsigned short shortPixel; - - switch ( bmpHeader.bitsPerPixel ) - { - case 8: - palIndex = *buf_p++; - *pixbuf++ = bmpHeader.palette[palIndex][2]; - *pixbuf++ = bmpHeader.palette[palIndex][1]; - *pixbuf++ = bmpHeader.palette[palIndex][0]; - *pixbuf++ = 0xff; - break; - case 16: - shortPixel = * ( unsigned short * ) pixbuf; - pixbuf += 2; - *pixbuf++ = ( shortPixel & ( 31 << 10 ) ) >> 7; - *pixbuf++ = ( shortPixel & ( 31 << 5 ) ) >> 2; - *pixbuf++ = ( shortPixel & ( 31 ) ) << 3; - *pixbuf++ = 0xff; - break; - - case 24: - blue = *buf_p++; - green = *buf_p++; - red = *buf_p++; - *pixbuf++ = red; - *pixbuf++ = green; - *pixbuf++ = blue; - *pixbuf++ = 255; - break; - case 32: - blue = *buf_p++; - green = *buf_p++; - red = *buf_p++; - alpha = *buf_p++; - *pixbuf++ = red; - *pixbuf++ = green; - *pixbuf++ = blue; - *pixbuf++ = alpha; - break; - } - } - } - - ri.FS_FreeFile( buffer.v ); - -} diff --git a/src/renderer/tr_image_jpg.c b/src/renderer/tr_image_jpg.c deleted file mode 100644 index 7d4a793a..00000000 --- a/src/renderer/tr_image_jpg.c +++ /dev/null @@ -1,438 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "tr_local.h" - -/* - * Include file for users of JPEG library. - * You will need to have included system headers that define at least - * the typedefs FILE and size_t before you can include jpeglib.h. - * (stdio.h is sufficient on ANSI-conforming systems.) - * You may also wish to include "jerror.h". - */ - -#ifdef USE_INTERNAL_JPEG -# define JPEG_INTERNALS -#endif - -#include - -#ifndef USE_INTERNAL_JPEG -# if JPEG_LIB_VERSION < 80 -# error Need system libjpeg >= 80 -# endif -#endif - -static void R_JPGErrorExit(j_common_ptr cinfo) -{ - char buffer[JMSG_LENGTH_MAX]; - - (*cinfo->err->format_message) (cinfo, buffer); - - /* Let the memory manager delete any temp files before we die */ - jpeg_destroy(cinfo); - - ri.Error(ERR_FATAL, "%s", buffer); -} - -static void R_JPGOutputMessage(j_common_ptr cinfo) -{ - char buffer[JMSG_LENGTH_MAX]; - - /* Create the message */ - (*cinfo->err->format_message) (cinfo, buffer); - - /* Send it to stderr, adding a newline */ - ri.Printf(PRINT_ALL, "%s\n", buffer); -} - -void R_LoadJPG(const char *filename, unsigned char **pic, int *width, int *height) -{ - /* This struct contains the JPEG decompression parameters and pointers to - * working space (which is allocated as needed by the JPEG library). - */ - struct jpeg_decompress_struct cinfo = {NULL}; - /* We use our private extension JPEG error handler. - * Note that this struct must live as long as the main JPEG parameter - * struct, to avoid dangling-pointer problems. - */ - /* This struct represents a JPEG error handler. It is declared separately - * because applications often want to supply a specialized error handler - * (see the second half of this file for an example). But here we just - * take the easy way out and use the standard error handler, which will - * print a message on stderr and call exit() if compression fails. - * Note that this struct must live as long as the main JPEG parameter - * struct, to avoid dangling-pointer problems. - */ - struct jpeg_error_mgr jerr; - /* More stuff */ - JSAMPARRAY buffer; /* Output row buffer */ - unsigned int row_stride; /* physical row width in output buffer */ - unsigned int pixelcount, memcount; - unsigned int sindex, dindex; - byte *out; - int len; - union { - byte *b; - void *v; - } fbuffer; - byte *buf; - - /* In this example we want to open the input file before doing anything else, - * so that the setjmp() error recovery below can assume the file is open. - * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that - * requires it in order to read binary files. - */ - - len = ri.FS_ReadFile ( ( char * ) filename, &fbuffer.v); - if (!fbuffer.b || len < 0) { - return; - } - - /* Step 1: allocate and initialize JPEG decompression object */ - - /* We have to set up the error handler first, in case the initialization - * step fails. (Unlikely, but it could happen if you are out of memory.) - * This routine fills in the contents of struct jerr, and returns jerr's - * address which we place into the link field in cinfo. - */ - cinfo.err = jpeg_std_error(&jerr); - cinfo.err->error_exit = R_JPGErrorExit; - cinfo.err->output_message = R_JPGOutputMessage; - - /* Now we can initialize the JPEG decompression object. */ - jpeg_create_decompress(&cinfo); - - /* Step 2: specify data source (eg, a file) */ - - jpeg_mem_src(&cinfo, fbuffer.b, len); - - /* Step 3: read file parameters with jpeg_read_header() */ - - (void) jpeg_read_header(&cinfo, TRUE); - /* We can ignore the return value from jpeg_read_header since - * (a) suspension is not possible with the stdio data source, and - * (b) we passed TRUE to reject a tables-only JPEG file as an error. - * See libjpeg.doc for more info. - */ - - /* Step 4: set parameters for decompression */ - - /* - * Make sure it always converts images to RGB color space. This will - * automatically convert 8-bit greyscale images to RGB as well. - */ - cinfo.out_color_space = JCS_RGB; - - /* Step 5: Start decompressor */ - - (void) jpeg_start_decompress(&cinfo); - /* We can ignore the return value since suspension is not possible - * with the stdio data source. - */ - - /* We may need to do some setup of our own at this point before reading - * the data. After jpeg_start_decompress() we have the correct scaled - * output image dimensions available, as well as the output colormap - * if we asked for color quantization. - * In this example, we need to make an output work buffer of the right size. - */ - /* JSAMPLEs per row in output buffer */ - - pixelcount = cinfo.output_width * cinfo.output_height; - - if(!cinfo.output_width || !cinfo.output_height - || ((pixelcount * 4) / cinfo.output_width) / 4 != cinfo.output_height - || pixelcount > 0x1FFFFFFF || cinfo.output_components != 3 - ) - { - // Free the memory to make sure we don't leak memory - ri.FS_FreeFile (fbuffer.v); - jpeg_destroy_decompress(&cinfo); - - ri.Error(ERR_DROP, "LoadJPG: %s has an invalid image format: %dx%d*4=%d, components: %d", filename, - cinfo.output_width, cinfo.output_height, pixelcount * 4, cinfo.output_components); - } - - memcount = pixelcount * 4; - row_stride = cinfo.output_width * cinfo.output_components; - - out = ri.Malloc(memcount); - - *width = cinfo.output_width; - *height = cinfo.output_height; - - /* Step 6: while (scan lines remain to be read) */ - /* jpeg_read_scanlines(...); */ - - /* Here we use the library's state variable cinfo.output_scanline as the - * loop counter, so that we don't have to keep track ourselves. - */ - while (cinfo.output_scanline < cinfo.output_height) { - /* jpeg_read_scanlines expects an array of pointers to scanlines. - * Here the array is only one element long, but you could ask for - * more than one scanline at a time if that's more convenient. - */ - buf = ((out+(row_stride*cinfo.output_scanline))); - buffer = &buf; - (void) jpeg_read_scanlines(&cinfo, buffer, 1); - } - - buf = out; - - // Expand from RGB to RGBA - sindex = pixelcount * cinfo.output_components; - dindex = memcount; - - do - { - buf[--dindex] = 255; - buf[--dindex] = buf[--sindex]; - buf[--dindex] = buf[--sindex]; - buf[--dindex] = buf[--sindex]; - } while(sindex); - - *pic = out; - - /* Step 7: Finish decompression */ - - jpeg_finish_decompress(&cinfo); - /* We can ignore the return value since suspension is not possible - * with the stdio data source. - */ - - /* Step 8: Release JPEG decompression object */ - - /* This is an important step since it will release a good deal of memory. */ - jpeg_destroy_decompress(&cinfo); - - /* After finish_decompress, we can close the input file. - * Here we postpone it until after no more JPEG errors are possible, - * so as to simplify the setjmp error logic above. (Actually, I don't - * think that jpeg_destroy can do an error exit, but why assume anything...) - */ - ri.FS_FreeFile (fbuffer.v); - - /* At this point you may want to check to see whether any corrupt-data - * warnings occurred (test whether jerr.pub.num_warnings is nonzero). - */ - - /* And we're done! */ -} - - -/* Expanded data destination object for stdio output */ - -typedef struct { - struct jpeg_destination_mgr pub; /* public fields */ - - byte* outfile; /* target stream */ - int size; -} my_destination_mgr; - -typedef my_destination_mgr * my_dest_ptr; - - -/* - * Initialize destination --- called by jpeg_start_compress - * before any data is actually written. - */ - -static void -init_destination (j_compress_ptr cinfo) -{ - my_dest_ptr dest = (my_dest_ptr) cinfo->dest; - - dest->pub.next_output_byte = dest->outfile; - dest->pub.free_in_buffer = dest->size; -} - - -/* - * Empty the output buffer --- called whenever buffer fills up. - * - * In typical applications, this should write the entire output buffer - * (ignoring the current state of next_output_byte & free_in_buffer), - * reset the pointer & count to the start of the buffer, and return TRUE - * indicating that the buffer has been dumped. - * - * In applications that need to be able to suspend compression due to output - * overrun, a FALSE return indicates that the buffer cannot be emptied now. - * In this situation, the compressor will return to its caller (possibly with - * an indication that it has not accepted all the supplied scanlines). The - * application should resume compression after it has made more room in the - * output buffer. Note that there are substantial restrictions on the use of - * suspension --- see the documentation. - * - * When suspending, the compressor will back up to a convenient restart point - * (typically the start of the current MCU). next_output_byte & free_in_buffer - * indicate where the restart point will be if the current call returns FALSE. - * Data beyond this point will be regenerated after resumption, so do not - * write it out when emptying the buffer externally. - */ - -static boolean -empty_output_buffer (j_compress_ptr cinfo) -{ - my_dest_ptr dest = (my_dest_ptr) cinfo->dest; - - jpeg_destroy_compress(cinfo); - - // Make crash fatal or we would probably leak memory. - ri.Error(ERR_FATAL, "Output buffer for encoded JPEG image has insufficient size of %d bytes", - dest->size); - - return FALSE; -} - -/* - * Terminate destination --- called by jpeg_finish_compress - * after all data has been written. Usually needs to flush buffer. - * - * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding - * application must deal with any cleanup that should happen even - * for error exit. - */ - -static void term_destination(j_compress_ptr cinfo) -{ -} - - -/* - * Prepare for output to a stdio stream. - * The caller must have already opened the stream, and is responsible - * for closing it after finishing compression. - */ - -static void -jpegDest (j_compress_ptr cinfo, byte* outfile, int size) -{ - my_dest_ptr dest; - - /* The destination object is made permanent so that multiple JPEG images - * can be written to the same file without re-executing jpeg_stdio_dest. - * This makes it dangerous to use this manager and a different destination - * manager serially with the same JPEG object, because their private object - * sizes may be different. Caveat programmer. - */ - if (cinfo->dest == NULL) { /* first time for this JPEG object? */ - cinfo->dest = (struct jpeg_destination_mgr *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, - sizeof(my_destination_mgr)); - } - - dest = (my_dest_ptr) cinfo->dest; - dest->pub.init_destination = init_destination; - dest->pub.empty_output_buffer = empty_output_buffer; - dest->pub.term_destination = term_destination; - dest->outfile = outfile; - dest->size = size; -} - -/* -================= -SaveJPGToBuffer - -Encodes JPEG from image in image_buffer and writes to buffer. -Expects RGB input data -================= -*/ -size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality, - int image_width, int image_height, byte *image_buffer, int padding) -{ - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; - JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ - my_dest_ptr dest; - int row_stride; /* physical row width in image buffer */ - size_t outcount; - - /* Step 1: allocate and initialize JPEG compression object */ - cinfo.err = jpeg_std_error(&jerr); - cinfo.err->error_exit = R_JPGErrorExit; - cinfo.err->output_message = R_JPGOutputMessage; - - /* Now we can initialize the JPEG compression object. */ - jpeg_create_compress(&cinfo); - - /* Step 2: specify data destination (eg, a file) */ - /* Note: steps 2 and 3 can be done in either order. */ - jpegDest(&cinfo, buffer, bufSize); - - /* Step 3: set parameters for compression */ - cinfo.image_width = image_width; /* image width and height, in pixels */ - cinfo.image_height = image_height; - cinfo.input_components = 3; /* # of color components per pixel */ - cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ - - jpeg_set_defaults(&cinfo); - jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); - /* If quality is set high, disable chroma subsampling */ - if (quality >= 85) { - cinfo.comp_info[0].h_samp_factor = 1; - cinfo.comp_info[0].v_samp_factor = 1; - } - - /* Step 4: Start compressor */ - jpeg_start_compress(&cinfo, TRUE); - - /* Step 5: while (scan lines remain to be written) */ - /* jpeg_write_scanlines(...); */ - row_stride = image_width * cinfo.input_components + padding; /* JSAMPLEs per row in image_buffer */ - - while (cinfo.next_scanline < cinfo.image_height) { - /* jpeg_write_scanlines expects an array of pointers to scanlines. - * Here the array is only one element long, but you could pass - * more than one scanline at a time if that's more convenient. - */ - row_pointer[0] = &image_buffer[((cinfo.image_height-1)*row_stride)-cinfo.next_scanline * row_stride]; - (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); - } - - /* Step 6: Finish compression */ - jpeg_finish_compress(&cinfo); - - dest = (my_dest_ptr) cinfo.dest; - outcount = dest->size - dest->pub.free_in_buffer; - - /* Step 7: release JPEG compression object */ - jpeg_destroy_compress(&cinfo); - - /* And we're done! */ - return outcount; -} - -void RE_SaveJPG(char * filename, int quality, int image_width, int image_height, byte *image_buffer, int padding) -{ - byte *out; - size_t bufSize; - - bufSize = image_width * image_height * 3; - out = ri.Hunk_AllocateTempMemory(bufSize); - - bufSize = RE_SaveJPGToBuffer(out, bufSize, quality, image_width, image_height, image_buffer, padding); - ri.FS_WriteFile(filename, out, bufSize); - - ri.Hunk_FreeTempMemory(out); -} diff --git a/src/renderer/tr_image_pcx.c b/src/renderer/tr_image_pcx.c deleted file mode 100644 index 8837d5b7..00000000 --- a/src/renderer/tr_image_pcx.c +++ /dev/null @@ -1,176 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. - 2008 Ludwig Nussel -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "tr_local.h" - -/* -======================================================================== - -PCX files are used for 8 bit images - -======================================================================== -*/ - -typedef struct { - char manufacturer; - char version; - char encoding; - char bits_per_pixel; - unsigned short xmin,ymin,xmax,ymax; - unsigned short hres,vres; - unsigned char palette[48]; - char reserved; - char color_planes; - unsigned short bytes_per_line; - unsigned short palette_type; - unsigned short hscreensize, vscreensize; - char filler[54]; - unsigned char data[]; -} pcx_t; - -void R_LoadPCX ( const char *filename, byte **pic, int *width, int *height) -{ - union { - byte *b; - void *v; - } raw; - byte *end; - pcx_t *pcx; - int len; - unsigned char dataByte = 0, runLength = 0; - byte *out, *pix; - unsigned short w, h; - byte *pic8; - byte *palette; - int i; - unsigned size = 0; - - if (width) - *width = 0; - if (height) - *height = 0; - *pic = NULL; - - // - // load the file - // - len = ri.FS_ReadFile( ( char * ) filename, &raw.v); - if (!raw.b || len < 0) { - return; - } - - if((unsigned)len < sizeof(pcx_t)) - { - ri.Printf (PRINT_ALL, "PCX truncated: %s\n", filename); - ri.FS_FreeFile (raw.v); - return; - } - - // - // parse the PCX file - // - pcx = (pcx_t *)raw.b; - end = raw.b+len; - - w = LittleShort(pcx->xmax)+1; - h = LittleShort(pcx->ymax)+1; - size = w*h; - - if (pcx->manufacturer != 0x0a - || pcx->version != 5 - || pcx->encoding != 1 - || pcx->color_planes != 1 - || pcx->bits_per_pixel != 8 - || w >= 1024 - || h >= 1024) - { - ri.Printf (PRINT_ALL, "Bad or unsupported pcx file %s (%dx%d@%d)\n", filename, w, h, pcx->bits_per_pixel); - return; - } - - pix = pic8 = ri.Malloc ( size ); - - raw.b = pcx->data; - // FIXME: should use bytes_per_line but original q3 didn't do that either - while(pix < pic8+size) - { - if(runLength > 0) { - *pix++ = dataByte; - --runLength; - continue; - } - - if(raw.b+1 > end) - break; - dataByte = *raw.b++; - - if((dataByte & 0xC0) == 0xC0) - { - if(raw.b+1 > end) - break; - runLength = dataByte & 0x3F; - dataByte = *raw.b++; - } - else - runLength = 1; - } - - if(pix < pic8+size) - { - ri.Printf (PRINT_ALL, "PCX file truncated: %s\n", filename); - ri.FS_FreeFile (pcx); - ri.Free (pic8); - } - - if (raw.b-(byte*)pcx >= end - (byte*)769 || end[-769] != 0x0c) - { - ri.Printf (PRINT_ALL, "PCX missing palette: %s\n", filename); - ri.FS_FreeFile (pcx); - ri.Free (pic8); - return; - } - - palette = end-768; - - pix = out = ri.Malloc(4 * size ); - for (i = 0 ; i < size ; i++) - { - unsigned char p = pic8[i]; - pix[0] = palette[p*3]; - pix[1] = palette[p*3 + 1]; - pix[2] = palette[p*3 + 2]; - pix[3] = 255; - pix += 4; - } - - if (width) - *width = w; - if (height) - *height = h; - - *pic = out; - - ri.FS_FreeFile (pcx); - ri.Free (pic8); -} diff --git a/src/renderer/tr_image_png.c b/src/renderer/tr_image_png.c deleted file mode 100644 index b30c7fea..00000000 --- a/src/renderer/tr_image_png.c +++ /dev/null @@ -1,2486 +0,0 @@ -/* -=========================================================================== -ioquake3 png decoder -Copyright (C) 2007,2008 Joerg Dietrich - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -=========================================================================== -*/ - -#include "tr_local.h" - -#include "../qcommon/puff.h" - -// we could limit the png size to a lower value here -#ifndef INT_MAX -#define INT_MAX 0x1fffffff -#endif - -/* -================= -PNG LOADING -================= -*/ - -/* - * Quake 3 image format : RGBA - */ - -#define Q3IMAGE_BYTESPERPIXEL (4) - -/* - * PNG specifications - */ - -/* - * The first 8 Bytes of every PNG-File are a fixed signature - * to identify the file as a PNG. - */ - -#define PNG_Signature "\x89\x50\x4E\x47\xD\xA\x1A\xA" -#define PNG_Signature_Size (8) - -/* - * After the signature diverse chunks follow. - * A chunk consists of a header and if Length - * is bigger than 0 a body and a CRC of the body follow. - */ - -struct PNG_ChunkHeader -{ - uint32_t Length; - uint32_t Type; -}; - -#define PNG_ChunkHeader_Size (8) - -typedef uint32_t PNG_ChunkCRC; - -#define PNG_ChunkCRC_Size (4) - -/* - * We use the following ChunkTypes. - * All others are ignored. - */ - -#define MAKE_CHUNKTYPE(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | ((d))) - -#define PNG_ChunkType_IHDR MAKE_CHUNKTYPE('I', 'H', 'D', 'R') -#define PNG_ChunkType_PLTE MAKE_CHUNKTYPE('P', 'L', 'T', 'E') -#define PNG_ChunkType_IDAT MAKE_CHUNKTYPE('I', 'D', 'A', 'T') -#define PNG_ChunkType_IEND MAKE_CHUNKTYPE('I', 'E', 'N', 'D') -#define PNG_ChunkType_tRNS MAKE_CHUNKTYPE('t', 'R', 'N', 'S') - -/* - * Per specification the first chunk after the signature SHALL be IHDR. - */ - -struct PNG_Chunk_IHDR -{ - uint32_t Width; - uint32_t Height; - uint8_t BitDepth; - uint8_t ColourType; - uint8_t CompressionMethod; - uint8_t FilterMethod; - uint8_t InterlaceMethod; -}; - -#define PNG_Chunk_IHDR_Size (13) - -/* - * ColourTypes - */ - -#define PNG_ColourType_Grey (0) -#define PNG_ColourType_True (2) -#define PNG_ColourType_Indexed (3) -#define PNG_ColourType_GreyAlpha (4) -#define PNG_ColourType_TrueAlpha (6) - -/* - * number of colour components - * - * Grey : 1 grey - * True : 1 R, 1 G, 1 B - * Indexed : 1 index - * GreyAlpha : 1 grey, 1 alpha - * TrueAlpha : 1 R, 1 G, 1 B, 1 alpha - */ - -#define PNG_NumColourComponents_Grey (1) -#define PNG_NumColourComponents_True (3) -#define PNG_NumColourComponents_Indexed (1) -#define PNG_NumColourComponents_GreyAlpha (2) -#define PNG_NumColourComponents_TrueAlpha (4) - -/* - * For the different ColourTypes - * different BitDepths are specified. - */ - -#define PNG_BitDepth_1 ( 1) -#define PNG_BitDepth_2 ( 2) -#define PNG_BitDepth_4 ( 4) -#define PNG_BitDepth_8 ( 8) -#define PNG_BitDepth_16 (16) - -/* - * Only one valid CompressionMethod is standardized. - */ - -#define PNG_CompressionMethod_0 (0) - -/* - * Only one valid FilterMethod is currently standardized. - */ - -#define PNG_FilterMethod_0 (0) - -/* - * This FilterMethod defines 5 FilterTypes - */ - -#define PNG_FilterType_None (0) -#define PNG_FilterType_Sub (1) -#define PNG_FilterType_Up (2) -#define PNG_FilterType_Average (3) -#define PNG_FilterType_Paeth (4) - -/* - * Two InterlaceMethods are standardized : - * 0 - NonInterlaced - * 1 - Interlaced - */ - -#define PNG_InterlaceMethod_NonInterlaced (0) -#define PNG_InterlaceMethod_Interlaced (1) - -/* - * The Adam7 interlace method uses 7 passes. - */ - -#define PNG_Adam7_NumPasses (7) - -/* - * The compressed data starts with a header ... - */ - -struct PNG_ZlibHeader -{ - uint8_t CompressionMethod; - uint8_t Flags; -}; - -#define PNG_ZlibHeader_Size (2) - -/* - * ... and is followed by a check value - */ - -#define PNG_ZlibCheckValue_Size (4) - -/* - * Some support functions for buffered files follow. - */ - -/* - * buffered file representation - */ - -struct BufferedFile -{ - byte *Buffer; - int Length; - byte *Ptr; - int BytesLeft; -}; - -/* - * Read a file into a buffer. - */ - -static struct BufferedFile *ReadBufferedFile(const char *name) -{ - struct BufferedFile *BF; - union { - byte *b; - void *v; - } buffer; - - /* - * input verification - */ - - if(!name) - { - return(NULL); - } - - /* - * Allocate control struct. - */ - - BF = ri.Malloc(sizeof(struct BufferedFile)); - if(!BF) - { - return(NULL); - } - - /* - * Initialize the structs components. - */ - - BF->Length = 0; - BF->Buffer = NULL; - BF->Ptr = NULL; - BF->BytesLeft = 0; - - /* - * Read the file. - */ - - BF->Length = ri.FS_ReadFile((char *) name, &buffer.v); - BF->Buffer = buffer.b; - - /* - * Did we get it? Is it big enough? - */ - - if(!(BF->Buffer && (BF->Length > 0))) - { - ri.Free(BF); - - return(NULL); - } - - /* - * Set the pointers and counters. - */ - - BF->Ptr = BF->Buffer; - BF->BytesLeft = BF->Length; - - return(BF); -} - -/* - * Close a buffered file. - */ - -static void CloseBufferedFile(struct BufferedFile *BF) -{ - if(BF) - { - if(BF->Buffer) - { - ri.FS_FreeFile(BF->Buffer); - } - - ri.Free(BF); - } -} - -/* - * Get a pointer to the requested bytes. - */ - -static void *BufferedFileRead(struct BufferedFile *BF, unsigned Length) -{ - void *RetVal; - - /* - * input verification - */ - - if(!(BF && Length)) - { - return(NULL); - } - - /* - * not enough bytes left - */ - - if(Length > BF->BytesLeft) - { - return(NULL); - } - - /* - * the pointer to the requested data - */ - - RetVal = BF->Ptr; - - /* - * Raise the pointer and counter. - */ - - BF->Ptr += Length; - BF->BytesLeft -= Length; - - return(RetVal); -} - -/* - * Rewind the buffer. - */ - -static qboolean BufferedFileRewind(struct BufferedFile *BF, unsigned Offset) -{ - unsigned BytesRead; - - /* - * input verification - */ - - if(!BF) - { - return(qfalse); - } - - /* - * special trick to rewind to the beginning of the buffer - */ - - if(Offset == (unsigned)-1) - { - BF->Ptr = BF->Buffer; - BF->BytesLeft = BF->Length; - - return(qtrue); - } - - /* - * How many bytes do we have already read? - */ - - BytesRead = BF->Ptr - BF->Buffer; - - /* - * We can only rewind to the beginning of the BufferedFile. - */ - - if(Offset > BytesRead) - { - return(qfalse); - } - - /* - * lower the pointer and counter. - */ - - BF->Ptr -= Offset; - BF->BytesLeft += Offset; - - return(qtrue); -} - -/* - * Skip some bytes. - */ - -static qboolean BufferedFileSkip(struct BufferedFile *BF, unsigned Offset) -{ - /* - * input verification - */ - - if(!BF) - { - return(qfalse); - } - - /* - * We can only skip to the end of the BufferedFile. - */ - - if(Offset > BF->BytesLeft) - { - return(qfalse); - } - - /* - * lower the pointer and counter. - */ - - BF->Ptr += Offset; - BF->BytesLeft -= Offset; - - return(qtrue); -} - -/* - * Find a chunk - */ - -static qboolean FindChunk(struct BufferedFile *BF, uint32_t ChunkType) -{ - struct PNG_ChunkHeader *CH; - - uint32_t Length; - uint32_t Type; - - /* - * input verification - */ - - if(!BF) - { - return(qfalse); - } - - /* - * cycle trough the chunks - */ - - while(qtrue) - { - /* - * Read the chunk-header. - */ - - CH = BufferedFileRead(BF, PNG_ChunkHeader_Size); - if(!CH) - { - return(qfalse); - } - - /* - * Do not swap the original types - * they might be needed later. - */ - - Length = BigLong(CH->Length); - Type = BigLong(CH->Type); - - /* - * We found it! - */ - - if(Type == ChunkType) - { - /* - * Rewind to the start of the chunk. - */ - - BufferedFileRewind(BF, PNG_ChunkHeader_Size); - - break; - } - else - { - /* - * Skip the rest of the chunk. - */ - - if(Length) - { - if(!BufferedFileSkip(BF, Length + PNG_ChunkCRC_Size)) - { - return(qfalse); - } - } - } - } - - return(qtrue); -} - -/* - * Decompress all IDATs - */ - -static uint32_t DecompressIDATs(struct BufferedFile *BF, uint8_t **Buffer) -{ - uint8_t *DecompressedData; - uint32_t DecompressedDataLength; - - uint8_t *CompressedData; - uint8_t *CompressedDataPtr; - uint32_t CompressedDataLength; - - struct PNG_ChunkHeader *CH; - - uint32_t Length; - uint32_t Type; - - int BytesToRewind; - - int32_t puffResult; - uint8_t *puffDest; - uint32_t puffDestLen; - uint8_t *puffSrc; - uint32_t puffSrcLen; - - /* - * input verification - */ - - if(!(BF && Buffer)) - { - return(-1); - } - - /* - * some zeroing - */ - - DecompressedData = NULL; - DecompressedDataLength = 0; - *Buffer = DecompressedData; - - CompressedData = NULL; - CompressedDataLength = 0; - - BytesToRewind = 0; - - /* - * Find the first IDAT chunk. - */ - - if(!FindChunk(BF, PNG_ChunkType_IDAT)) - { - return(-1); - } - - /* - * Count the size of the uncompressed data - */ - - while(qtrue) - { - /* - * Read chunk header - */ - - CH = BufferedFileRead(BF, PNG_ChunkHeader_Size); - if(!CH) - { - /* - * Rewind to the start of this adventure - * and return unsuccessfull - */ - - BufferedFileRewind(BF, BytesToRewind); - - return(-1); - } - - /* - * Length and Type of chunk - */ - - Length = BigLong(CH->Length); - Type = BigLong(CH->Type); - - /* - * We have reached the end of the IDAT chunks - */ - - if(!(Type == PNG_ChunkType_IDAT)) - { - BufferedFileRewind(BF, PNG_ChunkHeader_Size); - - break; - } - - /* - * Add chunk header to count. - */ - - BytesToRewind += PNG_ChunkHeader_Size; - - /* - * Skip to next chunk - */ - - if(Length) - { - if(!BufferedFileSkip(BF, Length + PNG_ChunkCRC_Size)) - { - BufferedFileRewind(BF, BytesToRewind); - - return(-1); - } - - BytesToRewind += Length + PNG_ChunkCRC_Size; - CompressedDataLength += Length; - } - } - - BufferedFileRewind(BF, BytesToRewind); - - CompressedData = ri.Malloc(CompressedDataLength); - if(!CompressedData) - { - return(-1); - } - - CompressedDataPtr = CompressedData; - - /* - * Collect the compressed Data - */ - - while(qtrue) - { - /* - * Read chunk header - */ - - CH = BufferedFileRead(BF, PNG_ChunkHeader_Size); - if(!CH) - { - ri.Free(CompressedData); - - return(-1); - } - - /* - * Length and Type of chunk - */ - - Length = BigLong(CH->Length); - Type = BigLong(CH->Type); - - /* - * We have reached the end of the IDAT chunks - */ - - if(!(Type == PNG_ChunkType_IDAT)) - { - BufferedFileRewind(BF, PNG_ChunkHeader_Size); - - break; - } - - /* - * Copy the Data - */ - - if(Length) - { - uint8_t *OrigCompressedData; - - OrigCompressedData = BufferedFileRead(BF, Length); - if(!OrigCompressedData) - { - ri.Free(CompressedData); - - return(-1); - } - - if(!BufferedFileSkip(BF, PNG_ChunkCRC_Size)) - { - ri.Free(CompressedData); - - return(-1); - } - - memcpy(CompressedDataPtr, OrigCompressedData, Length); - CompressedDataPtr += Length; - } - } - - /* - * Let puff() calculate the decompressed data length. - */ - - puffDest = NULL; - puffDestLen = 0; - - /* - * The zlib header and checkvalue don't belong to the compressed data. - */ - - puffSrc = CompressedData + PNG_ZlibHeader_Size; - puffSrcLen = CompressedDataLength - PNG_ZlibHeader_Size - PNG_ZlibCheckValue_Size; - - /* - * first puff() to calculate the size of the uncompressed data - */ - - puffResult = puff(puffDest, &puffDestLen, puffSrc, &puffSrcLen); - if(!((puffResult == 0) && (puffDestLen > 0))) - { - ri.Free(CompressedData); - - return(-1); - } - - /* - * Allocate the buffer for the uncompressed data. - */ - - DecompressedData = ri.Malloc(puffDestLen); - if(!DecompressedData) - { - ri.Free(CompressedData); - - return(-1); - } - - /* - * Set the input again in case something was changed by the last puff() . - */ - - puffDest = DecompressedData; - puffSrc = CompressedData + PNG_ZlibHeader_Size; - puffSrcLen = CompressedDataLength - PNG_ZlibHeader_Size - PNG_ZlibCheckValue_Size; - - /* - * decompression puff() - */ - - puffResult = puff(puffDest, &puffDestLen, puffSrc, &puffSrcLen); - - /* - * The compressed data is not needed anymore. - */ - - ri.Free(CompressedData); - - /* - * Check if the last puff() was successfull. - */ - - if(!((puffResult == 0) && (puffDestLen > 0))) - { - ri.Free(DecompressedData); - - return(-1); - } - - /* - * Set the output of this function. - */ - - DecompressedDataLength = puffDestLen; - *Buffer = DecompressedData; - - return(DecompressedDataLength); -} - -/* - * the Paeth predictor - */ - -static uint8_t PredictPaeth(uint8_t a, uint8_t b, uint8_t c) -{ - /* - * a == Left - * b == Up - * c == UpLeft - */ - - uint8_t Pr; - int p; - int pa, pb, pc; - - p = ((int) a) + ((int) b) - ((int) c); - pa = abs(p - ((int) a)); - pb = abs(p - ((int) b)); - pc = abs(p - ((int) c)); - - if((pa <= pb) && (pa <= pc)) - { - Pr = a; - } - else if(pb <= pc) - { - Pr = b; - } - else - { - Pr = c; - } - - return(Pr); - -} - -/* - * Reverse the filters. - */ - -static qboolean UnfilterImage(uint8_t *DecompressedData, - uint32_t ImageHeight, - uint32_t BytesPerScanline, - uint32_t BytesPerPixel) -{ - uint8_t *DecompPtr; - uint8_t FilterType; - uint8_t *PixelLeft, *PixelUp, *PixelUpLeft; - uint32_t w, h, p; - - /* - * some zeros for the filters - */ - - uint8_t Zeros[8] = {0, 0, 0, 0, 0, 0, 0, 0}; - - /* - * input verification - */ - - if(!(DecompressedData && BytesPerPixel)) - { - return(qfalse); - } - - /* - * ImageHeight and BytesPerScanline can be zero in small interlaced images. - */ - - if((!ImageHeight) || (!BytesPerScanline)) - { - return(qtrue); - } - - /* - * Set the pointer to the start of the decompressed Data. - */ - - DecompPtr = DecompressedData; - - /* - * Un-filtering is done in place. - */ - - /* - * Go trough all scanlines. - */ - - for(h = 0; h < ImageHeight; h++) - { - /* - * Every scanline starts with a FilterType byte. - */ - - FilterType = *DecompPtr; - DecompPtr++; - - /* - * Left pixel of the first byte in a scanline is zero. - */ - - PixelLeft = Zeros; - - /* - * Set PixelUp to previous line only if we are on the second line or above. - * - * Plus one byte for the FilterType - */ - - if(h > 0) - { - PixelUp = DecompPtr - (BytesPerScanline + 1); - } - else - { - PixelUp = Zeros; - } - - /* - * The pixel left to the first pixel of the previous scanline is zero too. - */ - - PixelUpLeft = Zeros; - - /* - * Cycle trough all pixels of the scanline. - */ - - for(w = 0; w < (BytesPerScanline / BytesPerPixel); w++) - { - /* - * Cycle trough the bytes of the pixel. - */ - - for(p = 0; p < BytesPerPixel; p++) - { - switch(FilterType) - { - case PNG_FilterType_None : - { - /* - * The byte is unfiltered. - */ - - break; - } - - case PNG_FilterType_Sub : - { - DecompPtr[p] += PixelLeft[p]; - - break; - } - - case PNG_FilterType_Up : - { - DecompPtr[p] += PixelUp[p]; - - break; - } - - case PNG_FilterType_Average : - { - DecompPtr[p] += ((uint8_t) ((((uint16_t) PixelLeft[p]) + ((uint16_t) PixelUp[p])) / 2)); - - break; - } - - case PNG_FilterType_Paeth : - { - DecompPtr[p] += PredictPaeth(PixelLeft[p], PixelUp[p], PixelUpLeft[p]); - - break; - } - - default : - { - return(qfalse); - } - } - } - - PixelLeft = DecompPtr; - - /* - * We only have an upleft pixel if we are on the second line or above. - */ - - if(h > 0) - { - PixelUpLeft = DecompPtr - (BytesPerScanline + 1); - } - - /* - * Skip to the next pixel. - */ - - DecompPtr += BytesPerPixel; - - /* - * We only have a previous line if we are on the second line and above. - */ - - if(h > 0) - { - PixelUp = DecompPtr - (BytesPerScanline + 1); - } - } - } - - return(qtrue); -} - -/* - * Convert a raw input pixel to Quake 3 RGA format. - */ - -static qboolean ConvertPixel(struct PNG_Chunk_IHDR *IHDR, - byte *OutPtr, - uint8_t *DecompPtr, - qboolean HasTransparentColour, - uint8_t *TransparentColour, - uint8_t *OutPal) -{ - /* - * input verification - */ - - if(!(IHDR && OutPtr && DecompPtr && TransparentColour && OutPal)) - { - return(qfalse); - } - - switch(IHDR->ColourType) - { - case PNG_ColourType_Grey : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_1 : - case PNG_BitDepth_2 : - case PNG_BitDepth_4 : - { - uint8_t Step; - uint8_t GreyValue; - - Step = 0xFF / ((1 << IHDR->BitDepth) - 1); - - GreyValue = DecompPtr[0] * Step; - - OutPtr[0] = GreyValue; - OutPtr[1] = GreyValue; - OutPtr[2] = GreyValue; - OutPtr[3] = 0xFF; - - /* - * Grey supports full transparency for one specified colour - */ - - if(HasTransparentColour) - { - if(TransparentColour[1] == DecompPtr[0]) - { - OutPtr[3] = 0x00; - } - } - - - break; - } - - case PNG_BitDepth_8 : - case PNG_BitDepth_16 : - { - OutPtr[0] = DecompPtr[0]; - OutPtr[1] = DecompPtr[0]; - OutPtr[2] = DecompPtr[0]; - OutPtr[3] = 0xFF; - - /* - * Grey supports full transparency for one specified colour - */ - - if(HasTransparentColour) - { - if(IHDR->BitDepth == PNG_BitDepth_8) - { - if(TransparentColour[1] == DecompPtr[0]) - { - OutPtr[3] = 0x00; - } - } - else - { - if((TransparentColour[0] == DecompPtr[0]) && (TransparentColour[1] == DecompPtr[1])) - { - OutPtr[3] = 0x00; - } - } - } - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_True : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_8 : - { - OutPtr[0] = DecompPtr[0]; - OutPtr[1] = DecompPtr[1]; - OutPtr[2] = DecompPtr[2]; - OutPtr[3] = 0xFF; - - /* - * True supports full transparency for one specified colour - */ - - if(HasTransparentColour) - { - if((TransparentColour[1] == DecompPtr[0]) && - (TransparentColour[3] == DecompPtr[1]) && - (TransparentColour[5] == DecompPtr[2])) - { - OutPtr[3] = 0x00; - } - } - - break; - } - - case PNG_BitDepth_16 : - { - /* - * We use only the upper byte. - */ - - OutPtr[0] = DecompPtr[0]; - OutPtr[1] = DecompPtr[2]; - OutPtr[2] = DecompPtr[4]; - OutPtr[3] = 0xFF; - - /* - * True supports full transparency for one specified colour - */ - - if(HasTransparentColour) - { - if((TransparentColour[0] == DecompPtr[0]) && (TransparentColour[1] == DecompPtr[1]) && - (TransparentColour[2] == DecompPtr[2]) && (TransparentColour[3] == DecompPtr[3]) && - (TransparentColour[4] == DecompPtr[4]) && (TransparentColour[5] == DecompPtr[5])) - { - OutPtr[3] = 0x00; - } - } - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_Indexed : - { - OutPtr[0] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 0]; - OutPtr[1] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 1]; - OutPtr[2] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 2]; - OutPtr[3] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 3]; - - break; - } - - case PNG_ColourType_GreyAlpha : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_8 : - { - OutPtr[0] = DecompPtr[0]; - OutPtr[1] = DecompPtr[0]; - OutPtr[2] = DecompPtr[0]; - OutPtr[3] = DecompPtr[1]; - - break; - } - - case PNG_BitDepth_16 : - { - /* - * We use only the upper byte. - */ - - OutPtr[0] = DecompPtr[0]; - OutPtr[1] = DecompPtr[0]; - OutPtr[2] = DecompPtr[0]; - OutPtr[3] = DecompPtr[2]; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_TrueAlpha : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_8 : - { - OutPtr[0] = DecompPtr[0]; - OutPtr[1] = DecompPtr[1]; - OutPtr[2] = DecompPtr[2]; - OutPtr[3] = DecompPtr[3]; - - break; - } - - case PNG_BitDepth_16 : - { - /* - * We use only the upper byte. - */ - - OutPtr[0] = DecompPtr[0]; - OutPtr[1] = DecompPtr[2]; - OutPtr[2] = DecompPtr[4]; - OutPtr[3] = DecompPtr[6]; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - default : - { - return(qfalse); - } - } - - return(qtrue); -} - - -/* - * Decode a non-interlaced image. - */ - -static qboolean DecodeImageNonInterlaced(struct PNG_Chunk_IHDR *IHDR, - byte *OutBuffer, - uint8_t *DecompressedData, - uint32_t DecompressedDataLength, - qboolean HasTransparentColour, - uint8_t *TransparentColour, - uint8_t *OutPal) -{ - uint32_t IHDR_Width; - uint32_t IHDR_Height; - uint32_t BytesPerScanline, BytesPerPixel, PixelsPerByte; - uint32_t w, h, p; - byte *OutPtr; - uint8_t *DecompPtr; - - /* - * input verification - */ - - if(!(IHDR && OutBuffer && DecompressedData && DecompressedDataLength && TransparentColour && OutPal)) - { - return(qfalse); - } - - /* - * byte swapping - */ - - IHDR_Width = BigLong(IHDR->Width); - IHDR_Height = BigLong(IHDR->Height); - - /* - * information for un-filtering - */ - - switch(IHDR->ColourType) - { - case PNG_ColourType_Grey : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_1 : - case PNG_BitDepth_2 : - case PNG_BitDepth_4 : - { - BytesPerPixel = 1; - PixelsPerByte = 8 / IHDR->BitDepth; - - break; - } - - case PNG_BitDepth_8 : - case PNG_BitDepth_16 : - { - BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_Grey; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_True : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_8 : - case PNG_BitDepth_16 : - { - BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_True; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_Indexed : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_1 : - case PNG_BitDepth_2 : - case PNG_BitDepth_4 : - { - BytesPerPixel = 1; - PixelsPerByte = 8 / IHDR->BitDepth; - - break; - } - - case PNG_BitDepth_8 : - { - BytesPerPixel = PNG_NumColourComponents_Indexed; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_GreyAlpha : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_8 : - case PNG_BitDepth_16 : - { - BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_GreyAlpha; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_TrueAlpha : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_8 : - case PNG_BitDepth_16 : - { - BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_TrueAlpha; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - default : - { - return(qfalse); - } - } - - /* - * Calculate the size of one scanline - */ - - BytesPerScanline = (IHDR_Width * BytesPerPixel + (PixelsPerByte - 1)) / PixelsPerByte; - - /* - * Check if we have enough data for the whole image. - */ - - if(!(DecompressedDataLength == ((BytesPerScanline + 1) * IHDR_Height))) - { - return(qfalse); - } - - /* - * Unfilter the image. - */ - - if(!UnfilterImage(DecompressedData, IHDR_Height, BytesPerScanline, BytesPerPixel)) - { - return(qfalse); - } - - /* - * Set the working pointers to the beginning of the buffers. - */ - - OutPtr = OutBuffer; - DecompPtr = DecompressedData; - - /* - * Create the output image. - */ - - for(h = 0; h < IHDR_Height; h++) - { - /* - * Count the pixels on the scanline for those multipixel bytes - */ - - uint32_t CurrPixel; - - /* - * skip FilterType - */ - - DecompPtr++; - - /* - * Reset the pixel count. - */ - - CurrPixel = 0; - - for(w = 0; w < (BytesPerScanline / BytesPerPixel); w++) - { - if(PixelsPerByte > 1) - { - uint8_t Mask; - uint32_t Shift; - uint8_t SinglePixel; - - for(p = 0; p < PixelsPerByte; p++) - { - if(CurrPixel < IHDR_Width) - { - Mask = (1 << IHDR->BitDepth) - 1; - Shift = (PixelsPerByte - 1 - p) * IHDR->BitDepth; - - SinglePixel = ((DecompPtr[0] & (Mask << Shift)) >> Shift); - - if(!ConvertPixel(IHDR, OutPtr, &SinglePixel, HasTransparentColour, TransparentColour, OutPal)) - { - return(qfalse); - } - - OutPtr += Q3IMAGE_BYTESPERPIXEL; - CurrPixel++; - } - } - - } - else - { - if(!ConvertPixel(IHDR, OutPtr, DecompPtr, HasTransparentColour, TransparentColour, OutPal)) - { - return(qfalse); - } - - - OutPtr += Q3IMAGE_BYTESPERPIXEL; - } - - DecompPtr += BytesPerPixel; - } - } - - return(qtrue); -} - -/* - * Decode an interlaced image. - */ - -static qboolean DecodeImageInterlaced(struct PNG_Chunk_IHDR *IHDR, - byte *OutBuffer, - uint8_t *DecompressedData, - uint32_t DecompressedDataLength, - qboolean HasTransparentColour, - uint8_t *TransparentColour, - uint8_t *OutPal) -{ - uint32_t IHDR_Width; - uint32_t IHDR_Height; - uint32_t BytesPerScanline[PNG_Adam7_NumPasses], BytesPerPixel, PixelsPerByte; - uint32_t PassWidth[PNG_Adam7_NumPasses], PassHeight[PNG_Adam7_NumPasses]; - uint32_t WSkip[PNG_Adam7_NumPasses], WOffset[PNG_Adam7_NumPasses], HSkip[PNG_Adam7_NumPasses], HOffset[PNG_Adam7_NumPasses]; - uint32_t w, h, p, a; - byte *OutPtr; - uint8_t *DecompPtr; - uint32_t TargetLength; - - /* - * input verification - */ - - if(!(IHDR && OutBuffer && DecompressedData && DecompressedDataLength && TransparentColour && OutPal)) - { - return(qfalse); - } - - /* - * byte swapping - */ - - IHDR_Width = BigLong(IHDR->Width); - IHDR_Height = BigLong(IHDR->Height); - - /* - * Skip and Offset for the passes. - */ - - WSkip[0] = 8; - WOffset[0] = 0; - HSkip[0] = 8; - HOffset[0] = 0; - - WSkip[1] = 8; - WOffset[1] = 4; - HSkip[1] = 8; - HOffset[1] = 0; - - WSkip[2] = 4; - WOffset[2] = 0; - HSkip[2] = 8; - HOffset[2] = 4; - - WSkip[3] = 4; - WOffset[3] = 2; - HSkip[3] = 4; - HOffset[3] = 0; - - WSkip[4] = 2; - WOffset[4] = 0; - HSkip[4] = 4; - HOffset[4] = 2; - - WSkip[5] = 2; - WOffset[5] = 1; - HSkip[5] = 2; - HOffset[5] = 0; - - WSkip[6] = 1; - WOffset[6] = 0; - HSkip[6] = 2; - HOffset[6] = 1; - - /* - * Calculate the sizes of the passes. - */ - - PassWidth[0] = (IHDR_Width + 7) / 8; - PassHeight[0] = (IHDR_Height + 7) / 8; - - PassWidth[1] = (IHDR_Width + 3) / 8; - PassHeight[1] = (IHDR_Height + 7) / 8; - - PassWidth[2] = (IHDR_Width + 3) / 4; - PassHeight[2] = (IHDR_Height + 3) / 8; - - PassWidth[3] = (IHDR_Width + 1) / 4; - PassHeight[3] = (IHDR_Height + 3) / 4; - - PassWidth[4] = (IHDR_Width + 1) / 2; - PassHeight[4] = (IHDR_Height + 1) / 4; - - PassWidth[5] = (IHDR_Width + 0) / 2; - PassHeight[5] = (IHDR_Height + 1) / 2; - - PassWidth[6] = (IHDR_Width + 0) / 1; - PassHeight[6] = (IHDR_Height + 0) / 2; - - /* - * information for un-filtering - */ - - switch(IHDR->ColourType) - { - case PNG_ColourType_Grey : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_1 : - case PNG_BitDepth_2 : - case PNG_BitDepth_4 : - { - BytesPerPixel = 1; - PixelsPerByte = 8 / IHDR->BitDepth; - - break; - } - - case PNG_BitDepth_8 : - case PNG_BitDepth_16 : - { - BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_Grey; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_True : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_8 : - case PNG_BitDepth_16 : - { - BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_True; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_Indexed : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_1 : - case PNG_BitDepth_2 : - case PNG_BitDepth_4 : - { - BytesPerPixel = 1; - PixelsPerByte = 8 / IHDR->BitDepth; - - break; - } - - case PNG_BitDepth_8 : - { - BytesPerPixel = PNG_NumColourComponents_Indexed; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_GreyAlpha : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_8 : - case PNG_BitDepth_16 : - { - BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_GreyAlpha; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - case PNG_ColourType_TrueAlpha : - { - switch(IHDR->BitDepth) - { - case PNG_BitDepth_8 : - case PNG_BitDepth_16 : - { - BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_TrueAlpha; - PixelsPerByte = 1; - - break; - } - - default : - { - return(qfalse); - } - } - - break; - } - - default : - { - return(qfalse); - } - } - - /* - * Calculate the size of the scanlines per pass - */ - - for(a = 0; a < PNG_Adam7_NumPasses; a++) - { - BytesPerScanline[a] = (PassWidth[a] * BytesPerPixel + (PixelsPerByte - 1)) / PixelsPerByte; - } - - /* - * Calculate the size of all passes - */ - - TargetLength = 0; - - for(a = 0; a < PNG_Adam7_NumPasses; a++) - { - TargetLength += ((BytesPerScanline[a] + (BytesPerScanline[a] ? 1 : 0)) * PassHeight[a]); - } - - /* - * Check if we have enough data for the whole image. - */ - - if(!(DecompressedDataLength == TargetLength)) - { - return(qfalse); - } - - /* - * Unfilter the image. - */ - - DecompPtr = DecompressedData; - - for(a = 0; a < PNG_Adam7_NumPasses; a++) - { - if(!UnfilterImage(DecompPtr, PassHeight[a], BytesPerScanline[a], BytesPerPixel)) - { - return(qfalse); - } - - DecompPtr += ((BytesPerScanline[a] + (BytesPerScanline[a] ? 1 : 0)) * PassHeight[a]); - } - - /* - * Set the working pointers to the beginning of the buffers. - */ - - DecompPtr = DecompressedData; - - /* - * Create the output image. - */ - - for(a = 0; a < PNG_Adam7_NumPasses; a++) - { - for(h = 0; h < PassHeight[a]; h++) - { - /* - * Count the pixels on the scanline for those multipixel bytes - */ - - uint32_t CurrPixel; - - /* - * skip FilterType - * but only when the pass has a width bigger than zero - */ - - if(BytesPerScanline[a]) - { - DecompPtr++; - } - - /* - * Reset the pixel count. - */ - - CurrPixel = 0; - - for(w = 0; w < (BytesPerScanline[a] / BytesPerPixel); w++) - { - if(PixelsPerByte > 1) - { - uint8_t Mask; - uint32_t Shift; - uint8_t SinglePixel; - - for(p = 0; p < PixelsPerByte; p++) - { - if(CurrPixel < PassWidth[a]) - { - Mask = (1 << IHDR->BitDepth) - 1; - Shift = (PixelsPerByte - 1 - p) * IHDR->BitDepth; - - SinglePixel = ((DecompPtr[0] & (Mask << Shift)) >> Shift); - - OutPtr = OutBuffer + (((((h * HSkip[a]) + HOffset[a]) * IHDR_Width) + ((CurrPixel * WSkip[a]) + WOffset[a])) * Q3IMAGE_BYTESPERPIXEL); - - if(!ConvertPixel(IHDR, OutPtr, &SinglePixel, HasTransparentColour, TransparentColour, OutPal)) - { - return(qfalse); - } - - CurrPixel++; - } - } - - } - else - { - OutPtr = OutBuffer + (((((h * HSkip[a]) + HOffset[a]) * IHDR_Width) + ((w * WSkip[a]) + WOffset[a])) * Q3IMAGE_BYTESPERPIXEL); - - if(!ConvertPixel(IHDR, OutPtr, DecompPtr, HasTransparentColour, TransparentColour, OutPal)) - { - return(qfalse); - } - } - - DecompPtr += BytesPerPixel; - } - } - } - - return(qtrue); -} - -/* - * The PNG loader - */ - -void R_LoadPNG(const char *name, byte **pic, int *width, int *height) -{ - struct BufferedFile *ThePNG; - byte *OutBuffer; - uint8_t *Signature; - struct PNG_ChunkHeader *CH; - uint32_t ChunkHeaderLength; - uint32_t ChunkHeaderType; - struct PNG_Chunk_IHDR *IHDR; - uint32_t IHDR_Width; - uint32_t IHDR_Height; - PNG_ChunkCRC *CRC; - uint8_t *InPal; - uint8_t *DecompressedData; - uint32_t DecompressedDataLength; - uint32_t i; - - /* - * palette with 256 RGBA entries - */ - - uint8_t OutPal[1024]; - - /* - * transparent colour from the tRNS chunk - */ - - qboolean HasTransparentColour = qfalse; - uint8_t TransparentColour[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - - /* - * input verification - */ - - if(!(name && pic)) - { - return; - } - - /* - * Zero out return values. - */ - - *pic = NULL; - - if(width) - { - *width = 0; - } - - if(height) - { - *height = 0; - } - - /* - * Read the file. - */ - - ThePNG = ReadBufferedFile(name); - if(!ThePNG) - { - return; - } - - /* - * Read the siganture of the file. - */ - - Signature = BufferedFileRead(ThePNG, PNG_Signature_Size); - if(!Signature) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Is it a PNG? - */ - - if(memcmp(Signature, PNG_Signature, PNG_Signature_Size)) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Read the first chunk-header. - */ - - CH = BufferedFileRead(ThePNG, PNG_ChunkHeader_Size); - if(!CH) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * PNG multi-byte types are in Big Endian - */ - - ChunkHeaderLength = BigLong(CH->Length); - ChunkHeaderType = BigLong(CH->Type); - - /* - * Check if the first chunk is an IHDR. - */ - - if(!((ChunkHeaderType == PNG_ChunkType_IHDR) && (ChunkHeaderLength == PNG_Chunk_IHDR_Size))) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Read the IHDR. - */ - - IHDR = BufferedFileRead(ThePNG, PNG_Chunk_IHDR_Size); - if(!IHDR) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Read the CRC for IHDR - */ - - CRC = BufferedFileRead(ThePNG, PNG_ChunkCRC_Size); - if(!CRC) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Here we could check the CRC if we wanted to. - */ - - /* - * multi-byte type swapping - */ - - IHDR_Width = BigLong(IHDR->Width); - IHDR_Height = BigLong(IHDR->Height); - - /* - * Check if Width and Height are valid. - */ - - if(!((IHDR_Width > 0) && (IHDR_Height > 0)) - || IHDR_Width > INT_MAX / Q3IMAGE_BYTESPERPIXEL / IHDR_Height) - { - CloseBufferedFile(ThePNG); - - ri.Printf( PRINT_WARNING, "%s: invalid image size\n", name ); - - return; - } - - /* - * Do we need to check if the dimensions of the image are valid for Quake3? - */ - - /* - * Check if CompressionMethod and FilterMethod are valid. - */ - - if(!((IHDR->CompressionMethod == PNG_CompressionMethod_0) && (IHDR->FilterMethod == PNG_FilterMethod_0))) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Check if InterlaceMethod is valid. - */ - - if(!((IHDR->InterlaceMethod == PNG_InterlaceMethod_NonInterlaced) || (IHDR->InterlaceMethod == PNG_InterlaceMethod_Interlaced))) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Read palette for an indexed image. - */ - - if(IHDR->ColourType == PNG_ColourType_Indexed) - { - /* - * We need the palette first. - */ - - if(!FindChunk(ThePNG, PNG_ChunkType_PLTE)) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Read the chunk-header. - */ - - CH = BufferedFileRead(ThePNG, PNG_ChunkHeader_Size); - if(!CH) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * PNG multi-byte types are in Big Endian - */ - - ChunkHeaderLength = BigLong(CH->Length); - ChunkHeaderType = BigLong(CH->Type); - - /* - * Check if the chunk is a PLTE. - */ - - if(!(ChunkHeaderType == PNG_ChunkType_PLTE)) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Check if Length is divisible by 3 - */ - - if(ChunkHeaderLength % 3) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Read the raw palette data - */ - - InPal = BufferedFileRead(ThePNG, ChunkHeaderLength); - if(!InPal) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Read the CRC for the palette - */ - - CRC = BufferedFileRead(ThePNG, PNG_ChunkCRC_Size); - if(!CRC) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Set some default values. - */ - - for(i = 0; i < 256; i++) - { - OutPal[i * Q3IMAGE_BYTESPERPIXEL + 0] = 0x00; - OutPal[i * Q3IMAGE_BYTESPERPIXEL + 1] = 0x00; - OutPal[i * Q3IMAGE_BYTESPERPIXEL + 2] = 0x00; - OutPal[i * Q3IMAGE_BYTESPERPIXEL + 3] = 0xFF; - } - - /* - * Convert to the Quake3 RGBA-format. - */ - - for(i = 0; i < (ChunkHeaderLength / 3); i++) - { - OutPal[i * Q3IMAGE_BYTESPERPIXEL + 0] = InPal[i*3+0]; - OutPal[i * Q3IMAGE_BYTESPERPIXEL + 1] = InPal[i*3+1]; - OutPal[i * Q3IMAGE_BYTESPERPIXEL + 2] = InPal[i*3+2]; - OutPal[i * Q3IMAGE_BYTESPERPIXEL + 3] = 0xFF; - } - } - - /* - * transparency information is sometimes stored in a tRNS chunk - */ - - /* - * Let's see if there is a tRNS chunk - */ - - if(FindChunk(ThePNG, PNG_ChunkType_tRNS)) - { - uint8_t *Trans; - - /* - * Read the chunk-header. - */ - - CH = BufferedFileRead(ThePNG, PNG_ChunkHeader_Size); - if(!CH) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * PNG multi-byte types are in Big Endian - */ - - ChunkHeaderLength = BigLong(CH->Length); - ChunkHeaderType = BigLong(CH->Type); - - /* - * Check if the chunk is a tRNS. - */ - - if(!(ChunkHeaderType == PNG_ChunkType_tRNS)) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Read the transparency information. - */ - - Trans = BufferedFileRead(ThePNG, ChunkHeaderLength); - if(!Trans) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Read the CRC. - */ - - CRC = BufferedFileRead(ThePNG, PNG_ChunkCRC_Size); - if(!CRC) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Only for Grey, True and Indexed ColourType should tRNS exist. - */ - - switch(IHDR->ColourType) - { - case PNG_ColourType_Grey : - { - if(!ChunkHeaderLength == 2) - { - CloseBufferedFile(ThePNG); - - return; - } - - HasTransparentColour = qtrue; - - /* - * Grey can have one colour which is completely transparent. - * This colour is always stored in 16 bits. - */ - - TransparentColour[0] = Trans[0]; - TransparentColour[1] = Trans[1]; - - break; - } - - case PNG_ColourType_True : - { - if(!ChunkHeaderLength == 6) - { - CloseBufferedFile(ThePNG); - - return; - } - - HasTransparentColour = qtrue; - - /* - * True can have one colour which is completely transparent. - * This colour is always stored in 16 bits. - */ - - TransparentColour[0] = Trans[0]; - TransparentColour[1] = Trans[1]; - TransparentColour[2] = Trans[2]; - TransparentColour[3] = Trans[3]; - TransparentColour[4] = Trans[4]; - TransparentColour[5] = Trans[5]; - - break; - } - - case PNG_ColourType_Indexed : - { - /* - * Maximum of 256 one byte transparency entries. - */ - - if(ChunkHeaderLength > 256) - { - CloseBufferedFile(ThePNG); - - return; - } - - HasTransparentColour = qtrue; - - /* - * alpha values for palette entries - */ - - for(i = 0; i < ChunkHeaderLength; i++) - { - OutPal[i * Q3IMAGE_BYTESPERPIXEL + 3] = Trans[i]; - } - - break; - } - - /* - * All other ColourTypes should not have tRNS chunks - */ - - default : - { - CloseBufferedFile(ThePNG); - - return; - } - } - } - - /* - * Rewind to the start of the file. - */ - - if(!BufferedFileRewind(ThePNG, -1)) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Skip the signature - */ - - if(!BufferedFileSkip(ThePNG, PNG_Signature_Size)) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Decompress all IDAT chunks - */ - - DecompressedDataLength = DecompressIDATs(ThePNG, &DecompressedData); - if(!(DecompressedDataLength && DecompressedData)) - { - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Allocate output buffer. - */ - - OutBuffer = ri.Malloc(IHDR_Width * IHDR_Height * Q3IMAGE_BYTESPERPIXEL); - if(!OutBuffer) - { - ri.Free(DecompressedData); - CloseBufferedFile(ThePNG); - - return; - } - - /* - * Interlaced and Non-interlaced images need to be handled differently. - */ - - switch(IHDR->InterlaceMethod) - { - case PNG_InterlaceMethod_NonInterlaced : - { - if(!DecodeImageNonInterlaced(IHDR, OutBuffer, DecompressedData, DecompressedDataLength, HasTransparentColour, TransparentColour, OutPal)) - { - ri.Free(OutBuffer); - ri.Free(DecompressedData); - CloseBufferedFile(ThePNG); - - return; - } - - break; - } - - case PNG_InterlaceMethod_Interlaced : - { - if(!DecodeImageInterlaced(IHDR, OutBuffer, DecompressedData, DecompressedDataLength, HasTransparentColour, TransparentColour, OutPal)) - { - ri.Free(OutBuffer); - ri.Free(DecompressedData); - CloseBufferedFile(ThePNG); - - return; - } - - break; - } - - default : - { - ri.Free(OutBuffer); - ri.Free(DecompressedData); - CloseBufferedFile(ThePNG); - - return; - } - } - - /* - * update the pointer to the image data - */ - - *pic = OutBuffer; - - /* - * Fill width and height. - */ - - if(width) - { - *width = IHDR_Width; - } - - if(height) - { - *height = IHDR_Height; - } - - /* - * DecompressedData is not needed anymore. - */ - - ri.Free(DecompressedData); - - /* - * We have all data, so close the file. - */ - - CloseBufferedFile(ThePNG); -} diff --git a/src/renderer/tr_image_tga.c b/src/renderer/tr_image_tga.c deleted file mode 100644 index b707c0ed..00000000 --- a/src/renderer/tr_image_tga.c +++ /dev/null @@ -1,321 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "tr_local.h" - -/* -======================================================================== - -TGA files are used for 24/32 bit images - -======================================================================== -*/ - -typedef struct _TargaHeader { - unsigned char id_length, colormap_type, image_type; - unsigned short colormap_index, colormap_length; - unsigned char colormap_size; - unsigned short x_origin, y_origin, width, height; - unsigned char pixel_size, attributes; -} TargaHeader; - -void R_LoadTGA ( const char *name, byte **pic, int *width, int *height) -{ - unsigned columns, rows, numPixels; - byte *pixbuf; - int row, column; - byte *buf_p; - byte *end; - union { - byte *b; - void *v; - } buffer; - TargaHeader targa_header; - byte *targa_rgba; - int length; - - *pic = NULL; - - if(width) - *width = 0; - if(height) - *height = 0; - - // - // load the file - // - length = ri.FS_ReadFile ( ( char * ) name, &buffer.v); - if (!buffer.b || length < 0) { - return; - } - - if(length < 18) - { - ri.Error( ERR_DROP, "LoadTGA: header too short (%s)", name ); - } - - buf_p = buffer.b; - end = buffer.b + length; - - targa_header.id_length = buf_p[0]; - targa_header.colormap_type = buf_p[1]; - targa_header.image_type = buf_p[2]; - - memcpy(&targa_header.colormap_index, &buf_p[3], 2); - memcpy(&targa_header.colormap_length, &buf_p[5], 2); - targa_header.colormap_size = buf_p[7]; - memcpy(&targa_header.x_origin, &buf_p[8], 2); - memcpy(&targa_header.y_origin, &buf_p[10], 2); - memcpy(&targa_header.width, &buf_p[12], 2); - memcpy(&targa_header.height, &buf_p[14], 2); - targa_header.pixel_size = buf_p[16]; - targa_header.attributes = buf_p[17]; - - targa_header.colormap_index = LittleShort(targa_header.colormap_index); - targa_header.colormap_length = LittleShort(targa_header.colormap_length); - targa_header.x_origin = LittleShort(targa_header.x_origin); - targa_header.y_origin = LittleShort(targa_header.y_origin); - targa_header.width = LittleShort(targa_header.width); - targa_header.height = LittleShort(targa_header.height); - - buf_p += 18; - - if (targa_header.image_type!=2 - && targa_header.image_type!=10 - && targa_header.image_type != 3 ) - { - ri.Error (ERR_DROP, "LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported"); - } - - if ( targa_header.colormap_type != 0 ) - { - ri.Error( ERR_DROP, "LoadTGA: colormaps not supported" ); - } - - if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 ) - { - ri.Error (ERR_DROP, "LoadTGA: Only 32 or 24 bit images supported (no colormaps)"); - } - - columns = targa_header.width; - rows = targa_header.height; - numPixels = columns * rows * 4; - - if(!columns || !rows || numPixels > 0x7FFFFFFF || numPixels / columns / 4 != rows) - { - ri.Error (ERR_DROP, "LoadTGA: %s has an invalid image size", name); - } - - - targa_rgba = ri.Malloc (numPixels); - - if (targa_header.id_length != 0) - { - if (buf_p + targa_header.id_length > end) - ri.Error( ERR_DROP, "LoadTGA: header too short (%s)", name ); - - buf_p += targa_header.id_length; // skip TARGA image comment - } - - if ( targa_header.image_type==2 || targa_header.image_type == 3 ) - { - if(buf_p + columns*rows*targa_header.pixel_size/8 > end) - { - ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name); - } - - // Uncompressed RGB or gray scale image - for(row=rows-1; row>=0; row--) - { - pixbuf = targa_rgba + row*columns*4; - for(column=0; column=0; row--) { - pixbuf = targa_rgba + row*columns*4; - for(column=0; column end) - ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name); - packetHeader= *buf_p++; - packetSize = 1 + (packetHeader & 0x7f); - if (packetHeader & 0x80) { // run-length packet - if(buf_p + targa_header.pixel_size/8 > end) - ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name); - switch (targa_header.pixel_size) { - case 24: - blue = *buf_p++; - green = *buf_p++; - red = *buf_p++; - alphabyte = 255; - break; - case 32: - blue = *buf_p++; - green = *buf_p++; - red = *buf_p++; - alphabyte = *buf_p++; - break; - default: - ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'", targa_header.pixel_size, name ); - break; - } - - for(j=0;j0) - row--; - else - goto breakOut; - pixbuf = targa_rgba + row*columns*4; - } - } - } - else { // non run-length packet - - if(buf_p + targa_header.pixel_size/8*packetSize > end) - ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name); - for(j=0;j0) - row--; - else - goto breakOut; - pixbuf = targa_rgba + row*columns*4; - } - } - } - } - breakOut:; - } - } - -#if 0 - // TTimo: this is the chunk of code to ensure a behavior that meets TGA specs - // bit 5 set => top-down - if (targa_header.attributes & 0x20) { - unsigned char *flip = (unsigned char*)malloc (columns*4); - unsigned char *src, *dst; - - for (row = 0; row < rows/2; row++) { - src = targa_rgba + row * 4 * columns; - dst = targa_rgba + (rows - row - 1) * 4 * columns; - - memcpy (flip, src, columns*4); - memcpy (src, dst, columns*4); - memcpy (dst, flip, columns*4); - } - free (flip); - } -#endif - // instead we just print a warning - if (targa_header.attributes & 0x20) { - ri.Printf( PRINT_WARNING, "WARNING: '%s' TGA file header declares top-down image, ignoring\n", name); - } - - if (width) - *width = columns; - if (height) - *height = rows; - - *pic = targa_rgba; - - ri.FS_FreeFile (buffer.v); -} diff --git a/src/renderer/tr_init.c b/src/renderer/tr_init.c deleted file mode 100644 index 14b91927..00000000 --- a/src/renderer/tr_init.c +++ /dev/null @@ -1,1375 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_init.c -- functions that are not called every frame - -#include "tr_local.h" - -glconfig_t glConfig; -glstate_t glState; - -static void GfxInfo_f( void ); - -#ifdef USE_RENDERER_DLOPEN -cvar_t *com_altivec; -#endif - -cvar_t *r_flareSize; -cvar_t *r_flareFade; -cvar_t *r_flareCoeff; - -cvar_t *r_railWidth; -cvar_t *r_railCoreWidth; -cvar_t *r_railSegmentLength; - -cvar_t *r_ignoreFastPath; - -cvar_t *r_verbose; -cvar_t *r_ignore; - -cvar_t *r_detailTextures; - -cvar_t *r_znear; -cvar_t *r_zproj; -cvar_t *r_stereoSeparation; - -cvar_t *r_skipBackEnd; - -cvar_t *r_stereoEnabled; -cvar_t *r_anaglyphMode; - -cvar_t *r_greyscale; - -cvar_t *r_ignorehwgamma; -cvar_t *r_measureOverdraw; - -cvar_t *r_inGameVideo; -cvar_t *r_fastsky; -cvar_t *r_drawSun; -cvar_t *r_dynamiclight; -cvar_t *r_dlightBacks; - -cvar_t *r_lodbias; -cvar_t *r_lodscale; - -cvar_t *r_norefresh; -cvar_t *r_drawentities; -cvar_t *r_drawworld; -cvar_t *r_speeds; -cvar_t *r_fullbright; -cvar_t *r_novis; -cvar_t *r_nocull; -cvar_t *r_facePlaneCull; -cvar_t *r_showcluster; -cvar_t *r_nocurves; - -cvar_t *r_allowExtensions; - -cvar_t *r_ext_compressed_textures; -cvar_t *r_ext_multitexture; -cvar_t *r_ext_compiled_vertex_array; -cvar_t *r_ext_texture_env_add; -cvar_t *r_ext_texture_filter_anisotropic; -cvar_t *r_ext_max_anisotropy; - -cvar_t *r_ignoreGLErrors; -cvar_t *r_logFile; - -cvar_t *r_stencilbits; -cvar_t *r_depthbits; -cvar_t *r_colorbits; -cvar_t *r_primitives; -cvar_t *r_texturebits; -cvar_t *r_ext_multisample; - -cvar_t *r_drawBuffer; -cvar_t *r_lightmap; -cvar_t *r_vertexLight; -cvar_t *r_uiFullScreen; -cvar_t *r_shadows; -cvar_t *r_flares; -cvar_t *r_nobind; -cvar_t *r_singleShader; -cvar_t *r_roundImagesDown; -cvar_t *r_colorMipLevels; -cvar_t *r_picmip; -cvar_t *r_showtris; -cvar_t *r_showsky; -cvar_t *r_shownormals; -cvar_t *r_finish; -cvar_t *r_clear; -cvar_t *r_swapInterval; -cvar_t *r_textureMode; -cvar_t *r_offsetFactor; -cvar_t *r_offsetUnits; -cvar_t *r_gamma; -cvar_t *r_intensity; -cvar_t *r_lockpvs; -cvar_t *r_noportals; -cvar_t *r_portalOnly; - -cvar_t *r_subdivisions; -cvar_t *r_lodCurveError; - -cvar_t *r_fullscreen; -cvar_t *r_noborder; - -cvar_t *r_width; -cvar_t *r_height; -cvar_t *r_pixelAspect; - -cvar_t *r_overBrightBits; -cvar_t *r_mapOverBrightBits; - -cvar_t *r_debugSurface; -cvar_t *r_simpleMipMaps; - -cvar_t *r_showImages; - -cvar_t *r_ambientScale; -cvar_t *r_directedScale; -cvar_t *r_debugLight; -cvar_t *r_debugSort; -cvar_t *r_printShaders; -cvar_t *r_saveFontData; - -cvar_t *r_marksOnTriangleMeshes; - -cvar_t *r_aviMotionJpegQuality; -cvar_t *r_screenshotJpegQuality; - -cvar_t *r_maxpolys; -int max_polys; -cvar_t *r_maxpolyverts; -int max_polyverts; - -#define GENERIC_HW_R_PICMIP_DEFAULT "0" -#define GENERIC_HW_R_TEXTUREMODE_DEFAULT "GL_LINEAR_MIPMAP_LINEAR" - -/* -================== -GL_ResolveHardwareType - -Chipset specific configuration -================== -*/ -void GL_ResolveHardwareType( void ) -{ - char buf[ 1024 ]; - cvar_t *lastValidRenderer = ri.Cvar_Get( - "r_lastValidRenderer", "(uninitialized)", CVAR_ARCHIVE ); - - Q_strncpyz( buf, glConfig.renderer_string, sizeof( buf ) ); - Q_strlwr( buf ); - - // NOTE: if changing cvars, do it within this block. This allows them - // to be overridden when testing driver fixes, etc. but only sets - // them to their default state when the hardware is first installed/run. - if( Q_stricmp( lastValidRenderer->string, glConfig.renderer_string ) ) - { - glConfig.hardwareType = GLHW_GENERIC; - - ri.Cvar_Set( "r_textureMode", GENERIC_HW_R_TEXTUREMODE_DEFAULT ); - - // VOODOO GRAPHICS w/ 2MB - if ( strstr( buf, "voodoo graphics/1 tmu/2 mb" ) ) - { - ri.Cvar_Set( "r_picmip", "2" ); - ri.Cvar_Get( "r_picmip", "1", CVAR_ARCHIVE | CVAR_LATCH ); - } - else - { - ri.Cvar_Set( "r_picmip", GENERIC_HW_R_PICMIP_DEFAULT ); - - if ( strstr( buf, "rage 128" ) || strstr( buf, "rage128" ) ) - { - ri.Cvar_Set( "r_finish", "0" ); - } - // Savage3D and Savage4 should always have trilinear enabled - else if ( strstr( buf, "savage3d" ) || strstr( buf, "s3 savage4" ) ) - { - ri.Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" ); - } - } - } - - // - // this is where hardware specific workarounds that should be - // detected/initialized every startup should go. - // - if ( strstr( buf, "banshee" ) || strstr( buf, "voodoo3" ) ) - { - glConfig.hardwareType = GLHW_3DFX_2D3D; - } - // VOODOO GRAPHICS w/ 2MB - else if ( strstr( buf, "voodoo graphics/1 tmu/2 mb" ) ) - { - } - else if ( strstr( buf, "glzicd" ) ) - { - } - else if ( strstr( buf, "rage pro" ) || - strstr( buf, "Rage Pro" ) || - strstr( buf, "ragepro" ) ) - { - glConfig.hardwareType = GLHW_RAGEPRO; - } - else if ( strstr( buf, "rage 128" ) ) - { - } - else if ( strstr( buf, "permedia2" ) ) - { - glConfig.hardwareType = GLHW_PERMEDIA2; - } - else if ( strstr( buf, "riva 128" ) ) - { - glConfig.hardwareType = GLHW_RIVA128; - } - else if ( strstr( buf, "riva tnt " ) ) - { - } -} - -/* -** InitOpenGL -** -** This function is responsible for initializing a valid OpenGL subsystem. This -** is done by calling GLimp_Init (which gives us a working OGL subsystem) then -** setting variables, checking GL constants, and reporting the gfx system config -** to the user. -*/ -static void InitOpenGL( void ) -{ - char renderer_buffer[1024]; - - // - // initialize OS specific portions of the renderer - // - // GLimp_Init directly or indirectly references the following cvars: - // - r_fullscreen - // - r_(width|height|pixelAspect) - // - r_(color|depth|stencil)bits - // - r_ignorehwgamma - // - r_gamma - // - - if ( glConfig.vidWidth == 0 ) - { - GLint temp; - - GLimp_Init(); - - strcpy( renderer_buffer, glConfig.renderer_string ); - Q_strlwr( renderer_buffer ); - - // OpenGL driver constants - qglGetIntegerv( GL_MAX_TEXTURE_SIZE, &temp ); - glConfig.maxTextureSize = temp; - - // stubbed or broken drivers may have reported 0... - if ( glConfig.maxTextureSize <= 0 ) - { - glConfig.maxTextureSize = 0; - } - } - - // set default state - GL_SetDefaultState(); -} - -/* -================== -GL_CheckErrors -================== -*/ -void GL_CheckErrors( void ) { - int err; - char s[64]; - - err = qglGetError(); - if ( err == GL_NO_ERROR ) { - return; - } - if ( r_ignoreGLErrors->integer ) { - return; - } - switch( err ) { - case GL_INVALID_ENUM: - strcpy( s, "GL_INVALID_ENUM" ); - break; - case GL_INVALID_VALUE: - strcpy( s, "GL_INVALID_VALUE" ); - break; - case GL_INVALID_OPERATION: - strcpy( s, "GL_INVALID_OPERATION" ); - break; - case GL_STACK_OVERFLOW: - strcpy( s, "GL_STACK_OVERFLOW" ); - break; - case GL_STACK_UNDERFLOW: - strcpy( s, "GL_STACK_UNDERFLOW" ); - break; - case GL_OUT_OF_MEMORY: - strcpy( s, "GL_OUT_OF_MEMORY" ); - break; - default: - Com_sprintf( s, sizeof(s), "%i", err); - break; - } - - ri.Error( ERR_FATAL, "GL_CheckErrors: %s", s ); -} - - -/* -============================================================================== - - SCREEN SHOTS - -NOTE TTimo -some thoughts about the screenshots system: -screenshots get written in fs_homepath + fs_gamedir -vanilla q3 .. baseq3/screenshots/ *.tga -team arena .. missionpack/screenshots/ *.tga - -two commands: "screenshot" and "screenshotJPEG" -we use statics to store a count and start writing the first screenshot/screenshot????.tga (.jpg) available -(with FS_FileExists / FS_FOpenFileWrite calls) -FIXME: the statics don't get a reinit between fs_game changes - -============================================================================== -*/ - -/* -================== -RB_ReadPixels - -Reads an image but takes care of alignment issues for reading RGB images. - -Reads a minimum offset for where the RGB data starts in the image from -integer stored at pointer offset. When the function has returned the actual -offset was written back to address offset. This address will always have an -alignment of packAlign to ensure efficient copying. - -Stores the length of padding after a line of pixels to address padlen - -Return value must be freed with ri.Hunk_FreeTempMemory() -================== -*/ - -byte *RB_ReadPixels(int x, int y, int width, int height, size_t *offset, int *padlen) -{ - byte *buffer, *bufstart; - int padwidth, linelen; - GLint packAlign; - - qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); - - linelen = width * 3; - padwidth = PAD(linelen, packAlign); - - // Allocate a few more bytes so that we can choose an alignment we like - buffer = ri.Hunk_AllocateTempMemory(padwidth * height + *offset + packAlign - 1); - - bufstart = PADP((intptr_t) buffer + *offset, packAlign); - qglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, bufstart); - - *offset = bufstart - buffer; - *padlen = padwidth - linelen; - - return buffer; -} - -/* -================== -RB_TakeScreenshot -================== -*/ -void RB_TakeScreenshot(int x, int y, int width, int height, char *fileName) -{ - byte *allbuf, *buffer; - byte *srcptr, *destptr; - byte *endline, *endmem; - byte temp; - - int linelen, padlen; - size_t offset = 18, memcount; - - allbuf = RB_ReadPixels(x, y, width, height, &offset, &padlen); - buffer = allbuf + offset - 18; - - Com_Memset (buffer, 0, 18); - buffer[2] = 2; // uncompressed type - buffer[12] = width & 255; - buffer[13] = width >> 8; - buffer[14] = height & 255; - buffer[15] = height >> 8; - buffer[16] = 24; // pixel size - - // swap rgb to bgr and remove padding from line endings - linelen = width * 3; - - srcptr = destptr = allbuf + offset; - endmem = srcptr + (linelen + padlen) * height; - - while(srcptr < endmem) - { - endline = srcptr + linelen; - - while(srcptr < endline) - { - temp = srcptr[0]; - *destptr++ = srcptr[2]; - *destptr++ = srcptr[1]; - *destptr++ = temp; - - srcptr += 3; - } - - // Skip the pad - srcptr += padlen; - } - - memcount = linelen * height; - - // gamma correct - if(glConfig.deviceSupportsGamma) - R_GammaCorrect(allbuf + offset, memcount); - - ri.FS_WriteFile(fileName, buffer, memcount + 18); - - ri.Hunk_FreeTempMemory(allbuf); -} - -/* -================== -RB_TakeScreenshotJPEG -================== -*/ - -void RB_TakeScreenshotJPEG(int x, int y, int width, int height, char *fileName) -{ - byte *buffer; - size_t offset = 0, memcount; - int padlen; - - buffer = RB_ReadPixels(x, y, width, height, &offset, &padlen); - memcount = (width * 3 + padlen) * height; - - // gamma correct - if(glConfig.deviceSupportsGamma) - R_GammaCorrect(buffer + offset, memcount); - - RE_SaveJPG(fileName, r_screenshotJpegQuality->integer, width, height, buffer + offset, padlen); - ri.Hunk_FreeTempMemory(buffer); -} - -/* -================== -RB_TakeScreenshotCmd -================== -*/ -const void *RB_TakeScreenshotCmd( const void *data ) { - const screenshotCommand_t *cmd; - - cmd = (const screenshotCommand_t *)data; - - if (cmd->jpeg) - RB_TakeScreenshotJPEG( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName); - else - RB_TakeScreenshot( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName); - - return (const void *)(cmd + 1); -} - -/* -================== -R_TakeScreenshot -================== -*/ -void R_TakeScreenshot( int x, int y, int width, int height, char *name, qboolean jpeg ) { - static char fileName[MAX_OSPATH]; // bad things if two screenshots per frame? - screenshotCommand_t *cmd; - - cmd = R_GetCommandBuffer( sizeof( *cmd ) ); - if ( !cmd ) { - return; - } - cmd->commandId = RC_SCREENSHOT; - - cmd->x = x; - cmd->y = y; - cmd->width = width; - cmd->height = height; - Q_strncpyz( fileName, name, sizeof(fileName) ); - cmd->fileName = fileName; - cmd->jpeg = jpeg; -} - -/* -================== -R_ScreenshotFilename -================== -*/ -void R_ScreenshotFilename( int lastNumber, char *fileName ) { - int a,b,c,d; - - if ( lastNumber < 0 || lastNumber > 9999 ) { - Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot9999.tga" ); - return; - } - - a = lastNumber / 1000; - lastNumber -= a*1000; - b = lastNumber / 100; - lastNumber -= b*100; - c = lastNumber / 10; - lastNumber -= c*10; - d = lastNumber; - - Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot%i%i%i%i.tga" - , a, b, c, d ); -} - -/* -================== -R_ScreenshotFilename -================== -*/ -void R_ScreenshotFilenameJPEG( int lastNumber, char *fileName ) { - int a,b,c,d; - - if ( lastNumber < 0 || lastNumber > 9999 ) { - Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot9999.jpg" ); - return; - } - - a = lastNumber / 1000; - lastNumber -= a*1000; - b = lastNumber / 100; - lastNumber -= b*100; - c = lastNumber / 10; - lastNumber -= c*10; - d = lastNumber; - - Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot%i%i%i%i.jpg" - , a, b, c, d ); -} - -/* -==================== -R_LevelShot - -levelshots are specialized 128*128 thumbnails for -the menu system, sampled down from full screen distorted images -==================== -*/ -void R_LevelShot( void ) { - char checkname[MAX_OSPATH]; - byte *buffer; - byte *source, *allsource; - byte *src, *dst; - size_t offset = 0; - int padlen; - int x, y; - int r, g, b; - float xScale, yScale; - int xx, yy; - - Com_sprintf(checkname, sizeof(checkname), "levelshots/%s.tga", tr.world->baseName); - - allsource = RB_ReadPixels(0, 0, glConfig.vidWidth, glConfig.vidHeight, &offset, &padlen); - source = allsource + offset; - - buffer = ri.Hunk_AllocateTempMemory(128 * 128*3 + 18); - Com_Memset (buffer, 0, 18); - buffer[2] = 2; // uncompressed type - buffer[12] = 128; - buffer[14] = 128; - buffer[16] = 24; // pixel size - - // resample from source - xScale = glConfig.vidWidth / 512.0f; - yScale = glConfig.vidHeight / 384.0f; - for ( y = 0 ; y < 128 ; y++ ) { - for ( x = 0 ; x < 128 ; x++ ) { - r = g = b = 0; - for ( yy = 0 ; yy < 3 ; yy++ ) { - for ( xx = 0 ; xx < 4 ; xx++ ) { - src = source + (3 * glConfig.vidWidth + padlen) * (int)((y*3 + yy) * yScale) + - 3 * (int) ((x*4 + xx) * xScale); - r += src[0]; - g += src[1]; - b += src[2]; - } - } - dst = buffer + 18 + 3 * ( y * 128 + x ); - dst[0] = b / 12; - dst[1] = g / 12; - dst[2] = r / 12; - } - } - - // gamma correct - if ( glConfig.deviceSupportsGamma ) { - R_GammaCorrect( buffer + 18, 128 * 128 * 3 ); - } - - ri.FS_WriteFile( checkname, buffer, 128 * 128*3 + 18 ); - - ri.Hunk_FreeTempMemory(buffer); - ri.Hunk_FreeTempMemory(allsource); - - ri.Printf( PRINT_ALL, "Wrote %s\n", checkname ); -} - -/* -================== -R_ScreenShot_f - -screenshot -screenshot [silent] -screenshot [levelshot] -screenshot [filename] - -Doesn't print the pacifier message if there is a second arg -================== -*/ -void R_ScreenShot_f (void) { - char checkname[MAX_OSPATH]; - static int lastNumber = -1; - qboolean silent; - - if ( !strcmp( ri.Cmd_Argv(1), "levelshot" ) ) { - R_LevelShot(); - return; - } - - if ( !strcmp( ri.Cmd_Argv(1), "silent" ) ) { - silent = qtrue; - } else { - silent = qfalse; - } - - if ( ri.Cmd_Argc() == 2 && !silent ) { - // explicit filename - Com_sprintf( checkname, MAX_OSPATH, "screenshots/%s.tga", ri.Cmd_Argv( 1 ) ); - } else { - // scan for a free filename - - // if we have saved a previous screenshot, don't scan - // again, because recording demo avis can involve - // thousands of shots - if ( lastNumber == -1 ) { - lastNumber = 0; - } - // scan for a free number - for ( ; lastNumber <= 9999 ; lastNumber++ ) { - R_ScreenshotFilename( lastNumber, checkname ); - - if (!ri.FS_FileExists( checkname )) - { - break; // file doesn't exist - } - } - - if ( lastNumber >= 9999 ) { - ri.Printf (PRINT_ALL, "ScreenShot: Couldn't create a file\n"); - return; - } - - lastNumber++; - } - - R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, qfalse ); - - if ( !silent ) { - ri.Printf (PRINT_ALL, "Wrote %s\n", checkname); - } -} - -void R_ScreenShotJPEG_f (void) { - char checkname[MAX_OSPATH]; - static int lastNumber = -1; - qboolean silent; - - if ( !strcmp( ri.Cmd_Argv(1), "levelshot" ) ) { - R_LevelShot(); - return; - } - - if ( !strcmp( ri.Cmd_Argv(1), "silent" ) ) { - silent = qtrue; - } else { - silent = qfalse; - } - - if ( ri.Cmd_Argc() == 2 && !silent ) { - // explicit filename - Com_sprintf( checkname, MAX_OSPATH, "screenshots/%s.jpg", ri.Cmd_Argv( 1 ) ); - } else { - // scan for a free filename - - // if we have saved a previous screenshot, don't scan - // again, because recording demo avis can involve - // thousands of shots - if ( lastNumber == -1 ) { - lastNumber = 0; - } - // scan for a free number - for ( ; lastNumber <= 9999 ; lastNumber++ ) { - R_ScreenshotFilenameJPEG( lastNumber, checkname ); - - if (!ri.FS_FileExists( checkname )) - { - break; // file doesn't exist - } - } - - if ( lastNumber == 10000 ) { - ri.Printf (PRINT_ALL, "ScreenShot: Couldn't create a file\n"); - return; - } - - lastNumber++; - } - - R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, qtrue ); - - if ( !silent ) { - ri.Printf (PRINT_ALL, "Wrote %s\n", checkname); - } -} - -//============================================================================ - -/* -================== -RB_TakeVideoFrameCmd -================== -*/ -const void *RB_TakeVideoFrameCmd( const void *data ) -{ - const videoFrameCommand_t *cmd; - byte *cBuf; - size_t memcount, linelen; - int padwidth, avipadwidth, padlen, avipadlen; - GLint packAlign; - - cmd = (const videoFrameCommand_t *)data; - - qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); - - linelen = cmd->width * 3; - - // Alignment stuff for glReadPixels - padwidth = PAD(linelen, packAlign); - padlen = padwidth - linelen; - // AVI line padding - avipadwidth = PAD(linelen, AVI_LINE_PADDING); - avipadlen = avipadwidth - linelen; - - cBuf = PADP(cmd->captureBuffer, packAlign); - - qglReadPixels(0, 0, cmd->width, cmd->height, GL_RGB, - GL_UNSIGNED_BYTE, cBuf); - - memcount = padwidth * cmd->height; - - // gamma correct - if(glConfig.deviceSupportsGamma) - R_GammaCorrect(cBuf, memcount); - - if(cmd->motionJpeg) - { - memcount = RE_SaveJPGToBuffer(cmd->encodeBuffer, linelen * cmd->height, - r_aviMotionJpegQuality->integer, - cmd->width, cmd->height, cBuf, padlen); - ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, memcount); - } - else - { - byte *lineend, *memend; - byte *srcptr, *destptr; - - srcptr = cBuf; - destptr = cmd->encodeBuffer; - memend = srcptr + memcount; - - // swap R and B and remove line paddings - while(srcptr < memend) - { - lineend = srcptr + linelen; - while(srcptr < lineend) - { - *destptr++ = srcptr[2]; - *destptr++ = srcptr[1]; - *destptr++ = srcptr[0]; - srcptr += 3; - } - - Com_Memset(destptr, '\0', avipadlen); - destptr += avipadlen; - - srcptr += padlen; - } - - ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, avipadwidth * cmd->height); - } - - return (const void *)(cmd + 1); -} - -//============================================================================ - -/* -** GL_SetDefaultState -*/ -void GL_SetDefaultState( void ) -{ - qglClearDepth( 1.0f ); - - qglCullFace(GL_FRONT); - - qglColor4f (1,1,1,1); - - // initialize downstream texture unit if we're running - // in a multitexture environment - if ( qglActiveTextureARB ) { - GL_SelectTexture( 1 ); - GL_TextureMode( r_textureMode->string ); - GL_TexEnv( GL_MODULATE ); - qglDisable( GL_TEXTURE_2D ); - GL_SelectTexture( 0 ); - } - - qglEnable(GL_TEXTURE_2D); - GL_TextureMode( r_textureMode->string ); - GL_TexEnv( GL_MODULATE ); - - qglShadeModel( GL_SMOOTH ); - qglDepthFunc( GL_LEQUAL ); - - // the vertex array is always enabled, but the color and texture - // arrays are enabled and disabled around the compiled vertex array call - qglEnableClientState (GL_VERTEX_ARRAY); - - // - // make sure our GL state vector is set correctly - // - glState.glStateBits = GLS_DEPTHTEST_DISABLE | GLS_DEPTHMASK_TRUE; - - qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); - qglDepthMask( GL_TRUE ); - qglDisable( GL_DEPTH_TEST ); - qglEnable( GL_SCISSOR_TEST ); - qglDisable( GL_CULL_FACE ); - qglDisable( GL_BLEND ); -} - -/* -================ -R_PrintLongString - -Workaround for ri.Printf's 1024 characters buffer limit. -================ -*/ -void R_PrintLongString(const char *string) { - char buffer[1024]; - const char *p; - int size = strlen(string); - - p = string; - while(size > 0) - { - Q_strncpyz(buffer, p, sizeof (buffer) ); - ri.Printf( PRINT_ALL, "%s", buffer ); - p += 1023; - size -= 1023; - } -} - -/* -================ -GfxInfo_f -================ -*/ -void GfxInfo_f( void ) -{ - const char *enablestrings[] = - { - "disabled", - "enabled" - }; - const char *fsstrings[] = - { - "windowed", - "fullscreen" - }; - - ri.Printf( PRINT_ALL, "\nGL_VENDOR: %s\n", glConfig.vendor_string ); - ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", glConfig.renderer_string ); - ri.Printf( PRINT_ALL, "GL_VERSION: %s\n", glConfig.version_string ); - ri.Printf( PRINT_ALL, "GL_EXTENSIONS: " ); - R_PrintLongString( glConfig.extensions_string ); - ri.Printf( PRINT_ALL, "\n" ); - ri.Printf( PRINT_ALL, "GL_MAX_TEXTURE_SIZE: %d\n", glConfig.maxTextureSize ); - ri.Printf( PRINT_ALL, "GL_MAX_TEXTURE_UNITS_ARB: %d\n", glConfig.numTextureUnits ); - ri.Printf( PRINT_ALL, "\nPIXELFORMAT: color(%d-bits) Z(%d-bit) stencil(%d-bits)\n", glConfig.colorBits, glConfig.depthBits, glConfig.stencilBits ); - ri.Printf( PRINT_ALL, "MODE: %d x %d %s hz:", glConfig.vidWidth, glConfig.vidHeight, fsstrings[r_fullscreen->integer == 1] ); - if ( glConfig.displayFrequency ) - { - ri.Printf( PRINT_ALL, "%d\n", glConfig.displayFrequency ); - } - else - { - ri.Printf( PRINT_ALL, "N/A\n" ); - } - if ( glConfig.deviceSupportsGamma ) - { - ri.Printf( PRINT_ALL, "GAMMA: hardware w/ %d overbright bits\n", tr.overbrightBits ); - } - else - { - ri.Printf( PRINT_ALL, "GAMMA: software w/ %d overbright bits\n", tr.overbrightBits ); - } - - // rendering primitives - { - int primitives; - - // default is to use triangles if compiled vertex arrays are present - ri.Printf( PRINT_ALL, "rendering primitives: " ); - primitives = r_primitives->integer; - if ( primitives == 0 ) { - if ( qglLockArraysEXT ) { - primitives = 2; - } else { - primitives = 1; - } - } - if ( primitives == -1 ) { - ri.Printf( PRINT_ALL, "none\n" ); - } else if ( primitives == 2 ) { - ri.Printf( PRINT_ALL, "single glDrawElements\n" ); - } else if ( primitives == 1 ) { - ri.Printf( PRINT_ALL, "multiple glArrayElement\n" ); - } else if ( primitives == 3 ) { - ri.Printf( PRINT_ALL, "multiple glColor4ubv + glTexCoord2fv + glVertex3fv\n" ); - } - } - - ri.Printf( PRINT_ALL, "texturemode: %s\n", r_textureMode->string ); - ri.Printf( PRINT_ALL, "picmip: %d\n", r_picmip->integer ); - ri.Printf( PRINT_ALL, "texture bits: %d\n", r_texturebits->integer ); - ri.Printf( PRINT_ALL, "multitexture: %s\n", enablestrings[qglActiveTextureARB != 0] ); - ri.Printf( PRINT_ALL, "compiled vertex arrays: %s\n", enablestrings[qglLockArraysEXT != 0 ] ); - ri.Printf( PRINT_ALL, "texenv add: %s\n", enablestrings[glConfig.textureEnvAddAvailable != 0] ); - ri.Printf( PRINT_ALL, "compressed textures: %s\n", enablestrings[glConfig.textureCompression!=TC_NONE] ); - if ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) - { - ri.Printf( PRINT_ALL, "HACK: using vertex lightmap approximation\n" ); - } - if ( glConfig.hardwareType == GLHW_RAGEPRO ) - { - ri.Printf( PRINT_ALL, "HACK: ragePro approximations\n" ); - } - if ( glConfig.hardwareType == GLHW_RIVA128 ) - { - ri.Printf( PRINT_ALL, "HACK: riva128 approximations\n" ); - } - if ( r_finish->integer ) { - ri.Printf( PRINT_ALL, "Forcing glFinish\n" ); - } -} - -/* -=============== -R_Register -=============== -*/ -void R_Register( void ) -{ - #ifdef USE_RENDERER_DLOPEN - com_altivec = ri.Cvar_Get("com_altivec", "1", CVAR_ARCHIVE); - #endif - - // - // latched and archived variables - // - r_allowExtensions = ri.Cvar_Get( "r_allowExtensions", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_ext_compressed_textures = ri.Cvar_Get( "r_ext_compressed_textures", "0", CVAR_ARCHIVE | CVAR_LATCH ); - r_ext_multitexture = ri.Cvar_Get( "r_ext_multitexture", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_ext_compiled_vertex_array = ri.Cvar_Get( "r_ext_compiled_vertex_array", "1", CVAR_ARCHIVE | CVAR_LATCH); - r_ext_texture_env_add = ri.Cvar_Get( "r_ext_texture_env_add", "1", CVAR_ARCHIVE | CVAR_LATCH); - - r_picmip = ri.Cvar_Get ("r_picmip", GENERIC_HW_R_PICMIP_DEFAULT, - CVAR_ARCHIVE | CVAR_LATCH ); - r_ext_texture_filter_anisotropic = ri.Cvar_Get( "r_ext_texture_filter_anisotropic", - "0", CVAR_ARCHIVE | CVAR_LATCH ); - r_ext_max_anisotropy = ri.Cvar_Get( "r_ext_max_anisotropy", "2", CVAR_ARCHIVE | CVAR_LATCH ); - - r_roundImagesDown = ri.Cvar_Get ("r_roundImagesDown", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_colorMipLevels = ri.Cvar_Get ("r_colorMipLevels", "0", CVAR_LATCH ); - ri.Cvar_CheckRange( r_picmip, 0, 16, qtrue ); - r_detailTextures = ri.Cvar_Get( "r_detailtextures", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_texturebits = ri.Cvar_Get( "r_texturebits", "0", CVAR_ARCHIVE | CVAR_LATCH ); - r_colorbits = ri.Cvar_Get( "r_colorbits", "0", CVAR_ARCHIVE | CVAR_LATCH ); - r_stencilbits = ri.Cvar_Get( "r_stencilbits", "8", CVAR_ARCHIVE | CVAR_LATCH ); - r_depthbits = ri.Cvar_Get( "r_depthbits", "0", CVAR_ARCHIVE | CVAR_LATCH ); - r_ext_multisample = ri.Cvar_Get( "r_ext_multisample", "0", CVAR_ARCHIVE | CVAR_LATCH ); - ri.Cvar_CheckRange( r_ext_multisample, 0, 4, qtrue ); - r_overBrightBits = ri.Cvar_Get ("r_overBrightBits", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_ignorehwgamma = ri.Cvar_Get( "r_ignorehwgamma", "0", CVAR_ARCHIVE | CVAR_LATCH); - r_fullscreen = ri.Cvar_Get( "r_fullscreen", "1", CVAR_ARCHIVE ); - r_noborder = ri.Cvar_Get("r_noborder", "0", CVAR_ARCHIVE); - r_width = ri.Cvar_Get( "r_width", "640", CVAR_ARCHIVE | CVAR_LATCH ); - r_height = ri.Cvar_Get( "r_height", "480", CVAR_ARCHIVE | CVAR_LATCH ); - r_pixelAspect = ri.Cvar_Get( "r_pixelAspect", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_simpleMipMaps = ri.Cvar_Get( "r_simpleMipMaps", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_vertexLight = ri.Cvar_Get( "r_vertexLight", "0", CVAR_ARCHIVE | CVAR_LATCH ); - r_uiFullScreen = ri.Cvar_Get( "r_uifullscreen", "0", 0); - r_subdivisions = ri.Cvar_Get ("r_subdivisions", "4", CVAR_ARCHIVE | CVAR_LATCH); - r_stereoEnabled = ri.Cvar_Get( "r_stereoEnabled", "0", CVAR_ARCHIVE | CVAR_LATCH); - r_ignoreFastPath = ri.Cvar_Get( "r_ignoreFastPath", "1", CVAR_ARCHIVE | CVAR_LATCH ); - r_greyscale = ri.Cvar_Get("r_greyscale", "0", CVAR_ARCHIVE | CVAR_LATCH); - ri.Cvar_CheckRange(r_greyscale, 0, 1, qfalse); - - // - // temporary latched variables that can only change over a restart - // - r_fullbright = ri.Cvar_Get ("r_fullbright", "0", CVAR_LATCH|CVAR_CHEAT ); - r_mapOverBrightBits = ri.Cvar_Get ("r_mapOverBrightBits", "2", CVAR_LATCH ); - r_intensity = ri.Cvar_Get ("r_intensity", "1", CVAR_LATCH ); - r_singleShader = ri.Cvar_Get ("r_singleShader", "0", CVAR_CHEAT | CVAR_LATCH ); - - // - // archived variables that can change at any time - // - r_lodCurveError = ri.Cvar_Get( "r_lodCurveError", "250", CVAR_ARCHIVE|CVAR_CHEAT ); - r_lodbias = ri.Cvar_Get( "r_lodbias", "0", CVAR_ARCHIVE ); - r_flares = ri.Cvar_Get ("r_flares", "0", CVAR_ARCHIVE ); - r_znear = ri.Cvar_Get( "r_znear", "1", CVAR_CHEAT ); - ri.Cvar_CheckRange( r_znear, 0.001f, 200, qfalse ); - r_zproj = ri.Cvar_Get( "r_zproj", "64", CVAR_ARCHIVE ); - r_stereoSeparation = ri.Cvar_Get( "r_stereoSeparation", "64", CVAR_ARCHIVE ); - r_ignoreGLErrors = ri.Cvar_Get( "r_ignoreGLErrors", "1", CVAR_ARCHIVE ); - r_fastsky = ri.Cvar_Get( "r_fastsky", "0", CVAR_ARCHIVE ); - r_inGameVideo = ri.Cvar_Get( "r_inGameVideo", "1", CVAR_ARCHIVE ); - r_drawSun = ri.Cvar_Get( "r_drawSun", "0", CVAR_ARCHIVE ); - r_dynamiclight = ri.Cvar_Get( "r_dynamiclight", "1", CVAR_ARCHIVE ); - r_dlightBacks = ri.Cvar_Get( "r_dlightBacks", "1", CVAR_ARCHIVE ); - r_finish = ri.Cvar_Get ("r_finish", "0", CVAR_ARCHIVE); - r_textureMode = ri.Cvar_Get( "r_textureMode", - GENERIC_HW_R_TEXTUREMODE_DEFAULT, CVAR_ARCHIVE ); - r_swapInterval = ri.Cvar_Get( "r_swapInterval", "0", - CVAR_ARCHIVE | CVAR_LATCH ); - r_gamma = ri.Cvar_Get( "r_gamma", "1", CVAR_ARCHIVE ); - r_facePlaneCull = ri.Cvar_Get ("r_facePlaneCull", "1", CVAR_ARCHIVE ); - - r_railWidth = ri.Cvar_Get( "r_railWidth", "16", CVAR_ARCHIVE ); - r_railCoreWidth = ri.Cvar_Get( "r_railCoreWidth", "6", CVAR_ARCHIVE ); - r_railSegmentLength = ri.Cvar_Get( "r_railSegmentLength", "32", CVAR_ARCHIVE ); - - r_primitives = ri.Cvar_Get( "r_primitives", "0", CVAR_ARCHIVE ); - - r_ambientScale = ri.Cvar_Get( "r_ambientScale", "0.6", CVAR_CHEAT ); - r_directedScale = ri.Cvar_Get( "r_directedScale", "1", CVAR_CHEAT ); - - r_anaglyphMode = ri.Cvar_Get("r_anaglyphMode", "0", CVAR_ARCHIVE); - - // - // temporary variables that can change at any time - // - r_showImages = ri.Cvar_Get( "r_showImages", "0", CVAR_TEMP ); - - r_debugLight = ri.Cvar_Get( "r_debuglight", "0", CVAR_TEMP ); - r_debugSort = ri.Cvar_Get( "r_debugSort", "0", CVAR_CHEAT ); - r_printShaders = ri.Cvar_Get( "r_printShaders", "0", 0 ); - r_saveFontData = ri.Cvar_Get( "r_saveFontData", "0", 0 ); - - r_nocurves = ri.Cvar_Get ("r_nocurves", "0", CVAR_CHEAT ); - r_drawworld = ri.Cvar_Get ("r_drawworld", "1", CVAR_CHEAT ); - r_lightmap = ri.Cvar_Get ("r_lightmap", "0", CVAR_CHEAT ); - r_portalOnly = ri.Cvar_Get ("r_portalOnly", "0", CVAR_CHEAT ); - - r_flareSize = ri.Cvar_Get ("r_flareSize", "40", CVAR_CHEAT); - r_flareFade = ri.Cvar_Get ("r_flareFade", "7", CVAR_CHEAT); - r_flareCoeff = ri.Cvar_Get ("r_flareCoeff", FLARE_STDCOEFF, CVAR_CHEAT); - - r_skipBackEnd = ri.Cvar_Get ("r_skipBackEnd", "0", CVAR_CHEAT); - - r_measureOverdraw = ri.Cvar_Get( "r_measureOverdraw", "0", CVAR_CHEAT ); - r_lodscale = ri.Cvar_Get( "r_lodscale", "5", CVAR_CHEAT ); - r_norefresh = ri.Cvar_Get ("r_norefresh", "0", CVAR_CHEAT); - r_drawentities = ri.Cvar_Get ("r_drawentities", "1", CVAR_CHEAT ); - r_ignore = ri.Cvar_Get( "r_ignore", "1", CVAR_CHEAT ); - r_nocull = ri.Cvar_Get ("r_nocull", "0", CVAR_CHEAT); - r_novis = ri.Cvar_Get ("r_novis", "0", CVAR_CHEAT); - r_showcluster = ri.Cvar_Get ("r_showcluster", "0", CVAR_CHEAT); - r_speeds = ri.Cvar_Get ("r_speeds", "0", CVAR_CHEAT); - r_verbose = ri.Cvar_Get( "r_verbose", "0", CVAR_CHEAT ); - r_logFile = ri.Cvar_Get( "r_logFile", "0", CVAR_CHEAT ); - r_debugSurface = ri.Cvar_Get ("r_debugSurface", "0", CVAR_CHEAT); - r_nobind = ri.Cvar_Get ("r_nobind", "0", CVAR_CHEAT); - r_showtris = ri.Cvar_Get ("r_showtris", "0", CVAR_CHEAT); - r_showsky = ri.Cvar_Get ("r_showsky", "0", CVAR_CHEAT); - r_shownormals = ri.Cvar_Get ("r_shownormals", "0", CVAR_CHEAT); - r_clear = ri.Cvar_Get ("r_clear", "0", CVAR_CHEAT); - r_offsetFactor = ri.Cvar_Get( "r_offsetfactor", "-1", CVAR_CHEAT ); - r_offsetUnits = ri.Cvar_Get( "r_offsetunits", "-2", CVAR_CHEAT ); - r_drawBuffer = ri.Cvar_Get( "r_drawBuffer", "GL_BACK", CVAR_CHEAT ); - r_lockpvs = ri.Cvar_Get ("r_lockpvs", "0", CVAR_CHEAT); - r_noportals = ri.Cvar_Get ("r_noportals", "0", CVAR_CHEAT); - r_shadows = ri.Cvar_Get( "cg_shadows", "1", 0 ); - - r_marksOnTriangleMeshes = ri.Cvar_Get("r_marksOnTriangleMeshes", "0", CVAR_ARCHIVE); - - r_aviMotionJpegQuality = ri.Cvar_Get("r_aviMotionJpegQuality", "90", CVAR_ARCHIVE); - r_screenshotJpegQuality = ri.Cvar_Get("r_screenshotJpegQuality", "90", CVAR_ARCHIVE); - - r_maxpolys = ri.Cvar_Get( "r_maxpolys", va("%d", MAX_POLYS), 0); - r_maxpolyverts = ri.Cvar_Get( "r_maxpolyverts", va("%d", MAX_POLYVERTS), 0); - - // make sure all the commands added here are also - // removed in R_Shutdown - ri.Cmd_AddCommand( "imagelist", R_ImageList_f ); - ri.Cmd_AddCommand( "shaderlist", R_ShaderList_f ); - ri.Cmd_AddCommand( "skinlist", R_SkinList_f ); - ri.Cmd_AddCommand( "modellist", R_Modellist_f ); - ri.Cmd_AddCommand( "screenshot", R_ScreenShot_f ); - ri.Cmd_AddCommand( "screenshotJPEG", R_ScreenShotJPEG_f ); - ri.Cmd_AddCommand( "gfxinfo", GfxInfo_f ); - ri.Cmd_AddCommand( "minimize", GLimp_Minimize ); -} - -/* -=============== -R_Init -=============== -*/ -void R_Init( void ) { - int err; - int i; - byte *ptr; - - ri.Printf( PRINT_ALL, "----- R_Init -----\n" ); - - // clear all our internal state - Com_Memset( &tr, 0, sizeof( tr ) ); - Com_Memset( &backEnd, 0, sizeof( backEnd ) ); - Com_Memset( &tess, 0, sizeof( tess ) ); - -// Swap_Init(); - - if ( (intptr_t)tess.xyz & 15 ) { - ri.Printf( PRINT_WARNING, "tess.xyz not 16 byte aligned\n" ); - } - Com_Memset( tess.constantColor255, 255, sizeof( tess.constantColor255 ) ); - - // - // init function tables - // - for ( i = 0; i < FUNCTABLE_SIZE; i++ ) - { - tr.sinTable[i] = sin( DEG2RAD( i * 360.0f / ( ( float ) ( FUNCTABLE_SIZE - 1 ) ) ) ); - tr.squareTable[i] = ( i < FUNCTABLE_SIZE/2 ) ? 1.0f : -1.0f; - tr.sawToothTable[i] = (float)i / FUNCTABLE_SIZE; - tr.inverseSawToothTable[i] = 1.0f - tr.sawToothTable[i]; - - if ( i < FUNCTABLE_SIZE / 2 ) - { - if ( i < FUNCTABLE_SIZE / 4 ) - { - tr.triangleTable[i] = ( float ) i / ( FUNCTABLE_SIZE / 4 ); - } - else - { - tr.triangleTable[i] = 1.0f - tr.triangleTable[i-FUNCTABLE_SIZE / 4]; - } - } - else - { - tr.triangleTable[i] = -tr.triangleTable[i-FUNCTABLE_SIZE/2]; - } - } - - R_InitFogTable(); - - R_NoiseInit(); - - R_Register(); - - max_polys = r_maxpolys->integer; - if (max_polys < MAX_POLYS) - max_polys = MAX_POLYS; - - max_polyverts = r_maxpolyverts->integer; - if (max_polyverts < MAX_POLYVERTS) - max_polyverts = MAX_POLYVERTS; - - ptr = ri.Hunk_Alloc( sizeof( *backEndData ) + sizeof(srfPoly_t) * max_polys + sizeof(polyVert_t) * max_polyverts, h_low); - backEndData = (backEndData_t *) ptr; - backEndData->polys = (srfPoly_t *) ((char *) ptr + sizeof( *backEndData )); - backEndData->polyVerts = (polyVert_t *) ((char *) ptr + sizeof( *backEndData ) + sizeof(srfPoly_t) * max_polys); - R_InitNextFrame(); - - InitOpenGL(); - - R_InitImages(); - - R_InitShaders(); - - R_InitSkins(); - - R_ModelInit(); - - R_InitFreeType(); - - - err = qglGetError(); - if ( err != GL_NO_ERROR ) - ri.Printf (PRINT_ALL, "glGetError() = 0x%x\n", err); - - // print info - GfxInfo_f(); - ri.Printf( PRINT_ALL, "----- finished R_Init -----\n" ); -} - -/* -=============== -RE_Shutdown -=============== -*/ -void RE_Shutdown( qboolean destroyWindow ) { - - ri.Printf( PRINT_ALL, "RE_Shutdown( %i )\n", destroyWindow ); - - ri.Cmd_RemoveCommand ("modellist"); - ri.Cmd_RemoveCommand ("screenshotJPEG"); - ri.Cmd_RemoveCommand ("screenshot"); - ri.Cmd_RemoveCommand ("imagelist"); - ri.Cmd_RemoveCommand ("shaderlist"); - ri.Cmd_RemoveCommand ("skinlist"); - ri.Cmd_RemoveCommand ("gfxinfo"); - ri.Cmd_RemoveCommand("minimize"); - ri.Cmd_RemoveCommand( "shaderstate" ); - - - if ( tr.registered ) { - R_IssuePendingRenderCommands(); - R_DeleteTextures(); - } - - R_DoneFreeType(); - - // shut down platform specific OpenGL stuff - if ( destroyWindow ) { - GLimp_Shutdown(); - } - - tr.registered = qfalse; -} - - -/* -============= -RE_EndRegistration - -Touch all images to make sure they are resident -============= -*/ -void RE_EndRegistration( void ) { - R_IssuePendingRenderCommands(); - if (!ri.Sys_LowPhysicalMemory()) { - RB_ShowImages(); - } -} - - -/* -@@@@@@@@@@@@@@@@@@@@@ -GetRefAPI - -@@@@@@@@@@@@@@@@@@@@@ -*/ -#ifdef USE_RENDERER_DLOPEN -Q_EXPORT refexport_t* QDECL GetRefAPI ( int apiVersion, refimport_t *rimp ) { -#else -refexport_t *GetRefAPI ( int apiVersion, refimport_t *rimp ) { -#endif - - static refexport_t re; - - ri = *rimp; - - Com_Memset( &re, 0, sizeof( re ) ); - - if ( apiVersion != REF_API_VERSION ) { - ri.Printf(PRINT_ALL, "Mismatched REF_API_VERSION: expected %i, got %i\n", - REF_API_VERSION, apiVersion ); - return NULL; - } - - // the RE_ functions are Renderer Entry points - - re.Shutdown = RE_Shutdown; - - re.BeginRegistration = RE_BeginRegistration; - re.RegisterModel = RE_RegisterModel; - re.RegisterSkin = RE_RegisterSkin; - re.RegisterShader = RE_RegisterShader; - re.RegisterShaderNoMip = RE_RegisterShaderNoMip; - re.LoadWorld = RE_LoadWorldMap; - re.SetWorldVisData = RE_SetWorldVisData; - re.EndRegistration = RE_EndRegistration; - - re.BeginFrame = RE_BeginFrame; - re.EndFrame = RE_EndFrame; - - re.MarkFragments = R_MarkFragments; - re.LerpTag = R_LerpTag; - re.ModelBounds = R_ModelBounds; - - re.ClearScene = RE_ClearScene; - re.AddRefEntityToScene = RE_AddRefEntityToScene; - re.AddPolyToScene = RE_AddPolyToScene; - re.LightForPoint = R_LightForPoint; - re.AddLightToScene = RE_AddLightToScene; - re.AddAdditiveLightToScene = RE_AddAdditiveLightToScene; - re.RenderScene = RE_RenderScene; - - re.SetColor = RE_SetColor; - re.SetClipRegion = RE_SetClipRegion; - re.DrawStretchPic = RE_StretchPic; - re.DrawStretchRaw = RE_StretchRaw; - re.UploadCinematic = RE_UploadCinematic; - - re.RegisterFont = RE_RegisterFont; - re.RemapShader = R_RemapShader; - re.GetEntityToken = R_GetEntityToken; - re.inPVS = R_inPVS; - - re.TakeVideoFrame = RE_TakeVideoFrame; - - return &re; -} diff --git a/src/renderer/tr_light.c b/src/renderer/tr_light.c deleted file mode 100644 index 36c1c480..00000000 --- a/src/renderer/tr_light.c +++ /dev/null @@ -1,395 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_light.c - -#include "tr_local.h" - -#define DLIGHT_AT_RADIUS 16 -// at the edge of a dlight's influence, this amount of light will be added - -#define DLIGHT_MINIMUM_RADIUS 16 -// never calculate a range less than this to prevent huge light numbers - - -/* -=============== -R_TransformDlights - -Transforms the origins of an array of dlights. -Used by both the front end (for DlightBmodel) and -the back end (before doing the lighting calculation) -=============== -*/ -void R_TransformDlights( int count, dlight_t *dl, orientationr_t *or) { - int i; - vec3_t temp; - - for ( i = 0 ; i < count ; i++, dl++ ) { - VectorSubtract( dl->origin, or->origin, temp ); - dl->transformed[0] = DotProduct( temp, or->axis[0] ); - dl->transformed[1] = DotProduct( temp, or->axis[1] ); - dl->transformed[2] = DotProduct( temp, or->axis[2] ); - } -} - -/* -============= -R_DlightBmodel - -Determine which dynamic lights may effect this bmodel -============= -*/ -void R_DlightBmodel( bmodel_t *bmodel ) { - int i, j; - dlight_t *dl; - int mask; - msurface_t *surf; - - // transform all the lights - R_TransformDlights( tr.refdef.num_dlights, tr.refdef.dlights, &tr.or ); - - mask = 0; - for ( i=0 ; itransformed[j] - bmodel->bounds[1][j] > dl->radius ) { - break; - } - if ( bmodel->bounds[0][j] - dl->transformed[j] > dl->radius ) { - break; - } - } - if ( j < 3 ) { - continue; - } - - // we need to check this light - mask |= 1 << i; - } - - tr.currentEntity->needDlights = (mask != 0); - - // set the dlight bits in all the surfaces - for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) { - surf = bmodel->firstSurface + i; - - if ( *surf->data == SF_FACE ) { - ((srfSurfaceFace_t *)surf->data)->dlightBits = mask; - } else if ( *surf->data == SF_GRID ) { - ((srfGridMesh_t *)surf->data)->dlightBits = mask; - } else if ( *surf->data == SF_TRIANGLES ) { - ((srfTriangles_t *)surf->data)->dlightBits = mask; - } - } -} - - -/* -============================================================================= - -LIGHT SAMPLING - -============================================================================= -*/ - -extern cvar_t *r_ambientScale; -extern cvar_t *r_directedScale; -extern cvar_t *r_debugLight; - -/* -================= -R_SetupEntityLightingGrid - -================= -*/ -static void R_SetupEntityLightingGrid( trRefEntity_t *ent ) { - vec3_t lightOrigin; - int pos[3]; - int i, j; - byte *gridData; - float frac[3]; - int gridStep[3]; - vec3_t direction; - float totalFactor; - - if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) { - // seperate lightOrigins are needed so an object that is - // sinking into the ground can still be lit, and so - // multi-part models can be lit identically - VectorCopy( ent->e.lightingOrigin, lightOrigin ); - } else { - VectorCopy( ent->e.origin, lightOrigin ); - } - - VectorSubtract( lightOrigin, tr.world->lightGridOrigin, lightOrigin ); - for ( i = 0 ; i < 3 ; i++ ) { - float v; - - v = lightOrigin[i]*tr.world->lightGridInverseSize[i]; - pos[i] = floor( v ); - frac[i] = v - pos[i]; - if ( pos[i] < 0 ) { - pos[i] = 0; - } else if ( pos[i] >= tr.world->lightGridBounds[i] - 1 ) { - pos[i] = tr.world->lightGridBounds[i] - 1; - } - } - - VectorClear( ent->ambientLight ); - VectorClear( ent->directedLight ); - VectorClear( direction ); - - assert( tr.world->lightGridData ); // NULL with -nolight maps - - // trilerp the light value - gridStep[0] = 8; - gridStep[1] = 8 * tr.world->lightGridBounds[0]; - gridStep[2] = 8 * tr.world->lightGridBounds[0] * tr.world->lightGridBounds[1]; - gridData = tr.world->lightGridData + pos[0] * gridStep[0] - + pos[1] * gridStep[1] + pos[2] * gridStep[2]; - - totalFactor = 0; - for ( i = 0 ; i < 8 ; i++ ) { - float factor; - byte *data; - int lat, lng; - vec3_t normal; - #if idppc - float d0, d1, d2, d3, d4, d5; - #endif - factor = 1.0; - data = gridData; - for ( j = 0 ; j < 3 ; j++ ) { - if ( i & (1<ambientLight[0] += factor * d0; - ent->ambientLight[1] += factor * d1; - ent->ambientLight[2] += factor * d2; - - ent->directedLight[0] += factor * d3; - ent->directedLight[1] += factor * d4; - ent->directedLight[2] += factor * d5; - #else - ent->ambientLight[0] += factor * data[0]; - ent->ambientLight[1] += factor * data[1]; - ent->ambientLight[2] += factor * data[2]; - - ent->directedLight[0] += factor * data[3]; - ent->directedLight[1] += factor * data[4]; - ent->directedLight[2] += factor * data[5]; - #endif - lat = data[7]; - lng = data[6]; - lat *= (FUNCTABLE_SIZE/256); - lng *= (FUNCTABLE_SIZE/256); - - // decode X as cos( lat ) * sin( long ) - // decode Y as sin( lat ) * sin( long ) - // decode Z as cos( long ) - - normal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; - normal[1] = tr.sinTable[lat] * tr.sinTable[lng]; - normal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; - - VectorMA( direction, factor, normal, direction ); - } - - if ( totalFactor > 0 && totalFactor < 0.99 ) { - totalFactor = 1.0f / totalFactor; - VectorScale( ent->ambientLight, totalFactor, ent->ambientLight ); - VectorScale( ent->directedLight, totalFactor, ent->directedLight ); - } - - VectorScale( ent->ambientLight, r_ambientScale->value, ent->ambientLight ); - VectorScale( ent->directedLight, r_directedScale->value, ent->directedLight ); - - VectorNormalize2( direction, ent->lightDir ); -} - - -/* -=============== -LogLight -=============== -*/ -static void LogLight( trRefEntity_t *ent ) { - int max1, max2; - - if ( !(ent->e.renderfx & RF_FIRST_PERSON ) ) { - return; - } - - max1 = ent->ambientLight[0]; - if ( ent->ambientLight[1] > max1 ) { - max1 = ent->ambientLight[1]; - } else if ( ent->ambientLight[2] > max1 ) { - max1 = ent->ambientLight[2]; - } - - max2 = ent->directedLight[0]; - if ( ent->directedLight[1] > max2 ) { - max2 = ent->directedLight[1]; - } else if ( ent->directedLight[2] > max2 ) { - max2 = ent->directedLight[2]; - } - - ri.Printf( PRINT_ALL, "amb:%i dir:%i\n", max1, max2 ); -} - -/* -================= -R_SetupEntityLighting - -Calculates all the lighting values that will be used -by the Calc_* functions -================= -*/ -void R_SetupEntityLighting( const trRefdef_t *refdef, trRefEntity_t *ent ) { - int i; - dlight_t *dl; - float power; - vec3_t dir; - float d; - vec3_t lightDir; - vec3_t lightOrigin; - - // lighting calculations - if ( ent->lightingCalculated ) { - return; - } - ent->lightingCalculated = qtrue; - - // - // trace a sample point down to find ambient light - // - if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) { - // seperate lightOrigins are needed so an object that is - // sinking into the ground can still be lit, and so - // multi-part models can be lit identically - VectorCopy( ent->e.lightingOrigin, lightOrigin ); - } else { - VectorCopy( ent->e.origin, lightOrigin ); - } - - // if NOWORLDMODEL, only use dynamic lights (menu system, etc) - if ( !(refdef->rdflags & RDF_NOWORLDMODEL ) - && tr.world->lightGridData ) { - R_SetupEntityLightingGrid( ent ); - } else { - ent->ambientLight[0] = ent->ambientLight[1] = - ent->ambientLight[2] = tr.identityLight * 150; - ent->directedLight[0] = ent->directedLight[1] = - ent->directedLight[2] = tr.identityLight * 150; - VectorCopy( tr.sunDirection, ent->lightDir ); - } - - // bonus items and view weapons have a fixed minimum add - if ( 1 /* ent->e.renderfx & RF_MINLIGHT */ ) { - // give everything a minimum light add - ent->ambientLight[0] += tr.identityLight * 32; - ent->ambientLight[1] += tr.identityLight * 32; - ent->ambientLight[2] += tr.identityLight * 32; - } - - // - // modify the light by dynamic lights - // - d = VectorLength( ent->directedLight ); - VectorScale( ent->lightDir, d, lightDir ); - - for ( i = 0 ; i < refdef->num_dlights ; i++ ) { - dl = &refdef->dlights[i]; - VectorSubtract( dl->origin, lightOrigin, dir ); - d = VectorNormalize( dir ); - - power = DLIGHT_AT_RADIUS * ( dl->radius * dl->radius ); - if ( d < DLIGHT_MINIMUM_RADIUS ) { - d = DLIGHT_MINIMUM_RADIUS; - } - d = power / ( d * d ); - - VectorMA( ent->directedLight, d, dl->color, ent->directedLight ); - VectorMA( lightDir, d, dir, lightDir ); - } - - // clamp ambient - for ( i = 0 ; i < 3 ; i++ ) { - if ( ent->ambientLight[i] > tr.identityLightByte ) { - ent->ambientLight[i] = tr.identityLightByte; - } - } - - if ( r_debugLight->integer ) { - LogLight( ent ); - } - - // save out the byte packet version - ((byte *)&ent->ambientLightInt)[0] = ri.ftol(ent->ambientLight[0]); - ((byte *)&ent->ambientLightInt)[1] = ri.ftol(ent->ambientLight[1]); - ((byte *)&ent->ambientLightInt)[2] = ri.ftol(ent->ambientLight[2]); - ((byte *)&ent->ambientLightInt)[3] = 0xff; - - // transform the direction to local space - VectorNormalize( lightDir ); - ent->lightDir[0] = DotProduct( lightDir, ent->e.axis[0] ); - ent->lightDir[1] = DotProduct( lightDir, ent->e.axis[1] ); - ent->lightDir[2] = DotProduct( lightDir, ent->e.axis[2] ); -} - -/* -================= -R_LightForPoint -================= -*/ -int R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ) -{ - trRefEntity_t ent; - - if ( tr.world->lightGridData == NULL ) - return qfalse; - - Com_Memset(&ent, 0, sizeof(ent)); - VectorCopy( point, ent.e.origin ); - R_SetupEntityLightingGrid( &ent ); - VectorCopy(ent.ambientLight, ambientLight); - VectorCopy(ent.directedLight, directedLight); - VectorCopy(ent.lightDir, lightDir); - - return qtrue; -} diff --git a/src/renderer/tr_local.h b/src/renderer/tr_local.h deleted file mode 100644 index fc80645b..00000000 --- a/src/renderer/tr_local.h +++ /dev/null @@ -1,1728 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - - -#ifndef TR_LOCAL_H -#define TR_LOCAL_H - -#include "../qcommon/q_shared.h" -#include "../qcommon/qfiles.h" -#include "../qcommon/qcommon.h" -#include "tr_public.h" -#include "qgl.h" -#include "iqm.h" - -#define GL_INDEX_TYPE GL_UNSIGNED_INT -typedef unsigned int glIndex_t; - -// 14 bits -// can't be increased without changing bit packing for drawsurfs -// see QSORT_SHADERNUM_SHIFT -#define SHADERNUM_BITS 14 -#define MAX_SHADERS (1<or.origin in local coordinates - float modelMatrix[16]; -} orientationr_t; - -typedef struct image_s { - char imgName[MAX_QPATH]; // game path, including extension - int width, height; // source image - int uploadWidth, uploadHeight; // after power of two and picmip but not including clamp to MAX_TEXTURE_SIZE - GLuint texnum; // gl texture binding - - int frameUsed; // for texture usage in frame statistics - - int internalFormat; - int TMU; // only needed for voodoo2 - - qboolean mipmap; - qboolean allowPicmip; - int wrapClampMode; // GL_CLAMP_TO_EDGE or GL_REPEAT - - struct image_s* next; -} image_t; - -//=============================================================================== - -typedef enum { - SS_BAD, - SS_PORTAL, // mirrors, portals, viewscreens - SS_ENVIRONMENT, // sky box - SS_OPAQUE, // opaque - - SS_DECAL, // scorch marks, etc. - SS_SEE_THROUGH, // ladders, grates, grills that may have small blended edges - // in addition to alpha test - SS_BANNER, - - SS_FOG, - - SS_UNDERWATER, // for items that should be drawn in front of the water plane - - SS_BLEND0, // regular transparency and filters - SS_BLEND1, // generally only used for additive type effects - SS_BLEND2, - SS_BLEND3, - - SS_BLEND6, - SS_STENCIL_SHADOW, - SS_ALMOST_NEAREST, // gun smoke puffs - - SS_NEAREST // blood blobs -} shaderSort_t; - - -#define MAX_SHADER_STAGES 8 - -typedef enum { - GF_NONE, - - GF_SIN, - GF_SQUARE, - GF_TRIANGLE, - GF_SAWTOOTH, - GF_INVERSE_SAWTOOTH, - - GF_NOISE - -} genFunc_t; - - -typedef enum { - DEFORM_NONE, - DEFORM_WAVE, - DEFORM_NORMALS, - DEFORM_BULGE, - DEFORM_MOVE, - DEFORM_PROJECTION_SHADOW, - DEFORM_AUTOSPRITE, - DEFORM_AUTOSPRITE2, - DEFORM_TEXT0, - DEFORM_TEXT1, - DEFORM_TEXT2, - DEFORM_TEXT3, - DEFORM_TEXT4, - DEFORM_TEXT5, - DEFORM_TEXT6, - DEFORM_TEXT7 -} deform_t; - -typedef enum { - AGEN_IDENTITY, - AGEN_SKIP, - AGEN_ENTITY, - AGEN_ONE_MINUS_ENTITY, - AGEN_VERTEX, - AGEN_ONE_MINUS_VERTEX, - AGEN_LIGHTING_SPECULAR, - AGEN_WAVEFORM, - AGEN_PORTAL, - AGEN_CONST -} alphaGen_t; - -typedef enum { - CGEN_BAD, - CGEN_IDENTITY_LIGHTING, // tr.identityLight - CGEN_IDENTITY, // always (1,1,1,1) - CGEN_ENTITY, // grabbed from entity's modulate field - CGEN_ONE_MINUS_ENTITY, // grabbed from 1 - entity.modulate - CGEN_EXACT_VERTEX, // tess.vertexColors - CGEN_VERTEX, // tess.vertexColors * tr.identityLight - CGEN_ONE_MINUS_VERTEX, - CGEN_WAVEFORM, // programmatically generated - CGEN_LIGHTING_DIFFUSE, - CGEN_FOG, // standard fog - CGEN_CONST // fixed color -} colorGen_t; - -typedef enum { - TCGEN_BAD, - TCGEN_IDENTITY, // clear to 0,0 - TCGEN_LIGHTMAP, - TCGEN_TEXTURE, - TCGEN_ENVIRONMENT_MAPPED, - TCGEN_FOG, - TCGEN_VECTOR // S and T from world coordinates -} texCoordGen_t; - -typedef enum { - ACFF_NONE, - ACFF_MODULATE_RGB, - ACFF_MODULATE_RGBA, - ACFF_MODULATE_ALPHA -} acff_t; - -typedef struct { - genFunc_t func; - - float base; - float amplitude; - float phase; - float frequency; -} waveForm_t; - -#define TR_MAX_TEXMODS 4 - -typedef enum { - TMOD_NONE, - TMOD_TRANSFORM, - TMOD_TURBULENT, - TMOD_SCROLL, - TMOD_SCALE, - TMOD_STRETCH, - TMOD_ROTATE, - TMOD_ENTITY_TRANSLATE -} texMod_t; - -#define MAX_SHADER_DEFORMS 3 -typedef struct { - deform_t deformation; // vertex coordinate modification type - - vec3_t moveVector; - waveForm_t deformationWave; - float deformationSpread; - - float bulgeWidth; - float bulgeHeight; - float bulgeSpeed; -} deformStage_t; - - -typedef struct { - texMod_t type; - - // used for TMOD_TURBULENT and TMOD_STRETCH - waveForm_t wave; - - // used for TMOD_TRANSFORM - float matrix[2][2]; // s' = s * m[0][0] + t * m[1][0] + trans[0] - float translate[2]; // t' = s * m[0][1] + t * m[0][1] + trans[1] - - // used for TMOD_SCALE - float scale[2]; // s *= scale[0] - // t *= scale[1] - - // used for TMOD_SCROLL - float scroll[2]; // s' = s + scroll[0] * time - // t' = t + scroll[1] * time - - // + = clockwise - // - = counterclockwise - float rotateSpeed; - -} texModInfo_t; - - -#define MAX_IMAGE_ANIMATIONS 8 - -typedef struct { - image_t *image[MAX_IMAGE_ANIMATIONS]; - int numImageAnimations; - float imageAnimationSpeed; - - texCoordGen_t tcGen; - vec3_t tcGenVectors[2]; - - int numTexMods; - texModInfo_t *texMods; - - int videoMapHandle; - qboolean isLightmap; - qboolean vertexLightmap; - qboolean isVideoMap; -} textureBundle_t; - -#define NUM_TEXTURE_BUNDLES 2 - -typedef struct { - qboolean active; - - textureBundle_t bundle[NUM_TEXTURE_BUNDLES]; - - waveForm_t rgbWave; - colorGen_t rgbGen; - - waveForm_t alphaWave; - alphaGen_t alphaGen; - - byte constantColor[4]; // for CGEN_CONST and AGEN_CONST - - unsigned stateBits; // GLS_xxxx mask - - acff_t adjustColorsForFog; - - qboolean isDetail; -} shaderStage_t; - -struct shaderCommands_s; - -// any change in the LIGHTMAP_* defines here MUST be reflected in -// R_FindShader() in tr_bsp.c -#define LIGHTMAP_2D -4 // shader is for 2D rendering -#define LIGHTMAP_BY_VERTEX -3 // pre-lit triangle models -#define LIGHTMAP_WHITEIMAGE -2 -#define LIGHTMAP_NONE -1 - -typedef enum { - CT_FRONT_SIDED, - CT_BACK_SIDED, - CT_TWO_SIDED -} cullType_t; - -typedef enum { - FP_NONE, // surface is translucent and will just be adjusted properly - FP_EQUAL, // surface is opaque but possibly alpha tested - FP_LE // surface is trnaslucent, but still needs a fog pass (fog surface) -} fogPass_t; - -typedef struct { - float cloudHeight; - image_t *outerbox[6], *innerbox[6]; -} skyParms_t; - -typedef struct { - vec3_t color; - float depthForOpaque; -} fogParms_t; - - -typedef struct shader_s { - char name[MAX_QPATH]; // game path, including extension - int lightmapIndex; // for a shader to match, both name and lightmapIndex must match - - int index; // this shader == tr.shaders[index] - int sortedIndex; // this shader == tr.sortedShaders[sortedIndex] - - float sort; // lower numbered shaders draw before higher numbered - - qboolean defaultShader; // we want to return index 0 if the shader failed to - // load for some reason, but R_FindShader should - // still keep a name allocated for it, so if - // something calls RE_RegisterShader again with - // the same name, we don't try looking for it again - - qboolean explicitlyDefined; // found in a .shader file - - int surfaceFlags; // if explicitlyDefined, this will have SURF_* flags - int contentFlags; - - qboolean entityMergable; // merge across entites optimizable (smoke, blood) - - qboolean isSky; - skyParms_t sky; - fogParms_t fogParms; - - float portalRange; // distance to fog out at - - int multitextureEnv; // 0, GL_MODULATE, GL_ADD (FIXME: put in stage) - - cullType_t cullType; // CT_FRONT_SIDED, CT_BACK_SIDED, or CT_TWO_SIDED - qboolean polygonOffset; // set for decals and other items that must be offset - qboolean noMipMaps; // for console fonts, 2D elements, etc. - qboolean noPicMip; // for images that must always be full resolution - - fogPass_t fogPass; // draw a blended pass, possibly with depth test equals - - qboolean needsNormal; // not all shaders will need all data to be gathered - qboolean needsST1; - qboolean needsST2; - qboolean needsColor; - - int numDeforms; - deformStage_t deforms[MAX_SHADER_DEFORMS]; - - int numUnfoggedPasses; - shaderStage_t *stages[MAX_SHADER_STAGES]; - - void (*optimalStageIteratorFunc)( void ); - - float clampTime; // time this shader is clamped to - float timeOffset; // current time offset for this shader - - int numStates; // if non-zero this is a state shader - struct shader_s *currentShader; // current state if this is a state shader - struct shader_s *parentShader; // current state if this is a state shader - int currentState; // current state index for cycle purposes - long expireTime; // time in milliseconds this expires - - struct shader_s *remappedShader; // current shader this one is remapped too - - int shaderStates[MAX_STATES_PER_SHADER]; // index to valid shader states - - struct shader_s *next; -} shader_t; - -typedef struct shaderState_s { - char shaderName[MAX_QPATH]; // name of shader this state belongs to - char name[MAX_STATE_NAME]; // name of this state - char stateShader[MAX_QPATH]; // shader this name invokes - int cycleTime; // time this cycle lasts, <= 0 is forever - shader_t *shader; -} shaderState_t; - - -// trRefdef_t holds everything that comes in refdef_t, -// as well as the locally generated scene information -typedef struct { - int x, y, width, height; - float fov_x, fov_y; - vec3_t vieworg; - vec3_t viewaxis[3]; // transformation matrix - - stereoFrame_t stereoFrame; - - int time; // time in milliseconds for shader effects and other time dependent rendering issues - int rdflags; // RDF_NOWORLDMODEL, etc - - // 1 bits will prevent the associated area from rendering at all - byte areamask[MAX_MAP_AREA_BYTES]; - qboolean areamaskModified; // qtrue if areamask changed since last scene - - float floatTime; // tr.refdef.time / 1000.0 - - // text messages for deform text shaders - char text[MAX_RENDER_STRINGS][MAX_RENDER_STRING_LENGTH]; - - int num_entities; - trRefEntity_t *entities; - - int num_dlights; - struct dlight_s *dlights; - - int numPolys; - struct srfPoly_s *polys; - - int numDrawSurfs; - struct drawSurf_s *drawSurfs; - - -} trRefdef_t; - - -//================================================================================= - -// skins allow models to be retextured without modifying the model file -typedef struct { - char name[MAX_QPATH]; - shader_t *shader; -} skinSurface_t; - -typedef struct skin_s { - char name[MAX_QPATH]; // game path, including extension - int numSurfaces; - skinSurface_t *surfaces[MD3_MAX_SURFACES]; -} skin_t; - - -typedef struct { - int originalBrushNumber; - vec3_t bounds[2]; - - unsigned colorInt; // in packed byte format - float tcScale; // texture coordinate vector scales - fogParms_t parms; - - // for clipping distance in fog when outside - qboolean hasSurface; - float surface[4]; -} fog_t; - -typedef struct { - orientationr_t or; - orientationr_t world; - vec3_t pvsOrigin; // may be different than or.origin for portals - qboolean isPortal; // true if this view is through a portal - qboolean isMirror; // the portal is a mirror, invert the face culling - int frameSceneNum; // copied from tr.frameSceneNum - int frameCount; // copied from tr.frameCount - cplane_t portalPlane; // clip anything behind this if mirroring - int viewportX, viewportY, viewportWidth, viewportHeight; - float fovX, fovY; - float projectionMatrix[16]; - cplane_t frustum[4]; - vec3_t visBounds[2]; - float zFar; - stereoFrame_t stereoFrame; -} viewParms_t; - - -/* -============================================================================== - -SURFACES - -============================================================================== -*/ - -// any changes in surfaceType must be mirrored in rb_surfaceTable[] -typedef enum { - SF_BAD, - SF_SKIP, // ignore - SF_FACE, - SF_GRID, - SF_TRIANGLES, - SF_POLY, - SF_MD3, - SF_MD4, -#ifdef RAVENMD4 - SF_MDR, -#endif - SF_IQM, - SF_FLARE, - SF_ENTITY, // beams, rails, lightning, etc that can be determined by entity - SF_DISPLAY_LIST, - - SF_NUM_SURFACE_TYPES, - SF_MAX = 0x7fffffff // ensures that sizeof( surfaceType_t ) == sizeof( int ) -} surfaceType_t; - -typedef struct drawSurf_s { - unsigned sort; // bit combination for fast compares - surfaceType_t *surface; // any of surface*_t -} drawSurf_t; - -#define MAX_FACE_POINTS 64 - -#define MAX_PATCH_SIZE 32 // max dimensions of a patch mesh in map file -#define MAX_GRID_SIZE 65 // max dimensions of a grid mesh in memory - -// when cgame directly specifies a polygon, it becomes a srfPoly_t -// as soon as it is called -typedef struct srfPoly_s { - surfaceType_t surfaceType; - qhandle_t hShader; - int fogIndex; - int numVerts; - polyVert_t *verts; -} srfPoly_t; - -typedef struct srfDisplayList_s { - surfaceType_t surfaceType; - int listNum; -} srfDisplayList_t; - - -typedef struct srfFlare_s { - surfaceType_t surfaceType; - vec3_t origin; - vec3_t normal; - vec3_t color; -} srfFlare_t; - -typedef struct srfGridMesh_s { - surfaceType_t surfaceType; - - // dynamic lighting information - int dlightBits; - - // culling information - vec3_t meshBounds[2]; - vec3_t localOrigin; - float meshRadius; - - // lod information, which may be different - // than the culling information to allow for - // groups of curves that LOD as a unit - vec3_t lodOrigin; - float lodRadius; - int lodFixed; - int lodStitched; - - // vertexes - int width, height; - float *widthLodError; - float *heightLodError; - drawVert_t verts[1]; // variable sized -} srfGridMesh_t; - - - -#define VERTEXSIZE 8 -typedef struct { - surfaceType_t surfaceType; - cplane_t plane; - - // dynamic lighting information - int dlightBits; - - // triangle definitions (no normals at points) - int numPoints; - int numIndices; - int ofsIndices; - float points[1][VERTEXSIZE]; // variable sized - // there is a variable length list of indices here also -} srfSurfaceFace_t; - - -// misc_models in maps are turned into direct geometry by q3map -typedef struct { - surfaceType_t surfaceType; - - // dynamic lighting information - int dlightBits; - - // culling information (FIXME: use this!) - vec3_t bounds[2]; - vec3_t localOrigin; - float radius; - - // triangle definitions - int numIndexes; - int *indexes; - - int numVerts; - drawVert_t *verts; -} srfTriangles_t; - -// inter-quake-model -typedef struct { - int num_vertexes; - int num_triangles; - int num_frames; - int num_surfaces; - int num_joints; - struct srfIQModel_s *surfaces; - - float *positions; - float *texcoords; - float *normals; - float *tangents; - byte *blendIndexes; - byte *blendWeights; - byte *colors; - int *triangles; - - int *jointParents; - float *poseMats; - float *bounds; - char *names; -} iqmData_t; - -// inter-quake-model surface -typedef struct srfIQModel_s { - surfaceType_t surfaceType; - char name[MAX_QPATH]; - shader_t *shader; - iqmData_t *data; - int first_vertex, num_vertexes; - int first_triangle, num_triangles; -} srfIQModel_t; - - -extern void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])(void *); - -/* -============================================================================== - -BRUSH MODELS - -============================================================================== -*/ - - -// -// in memory representation -// - -#define SIDE_FRONT 0 -#define SIDE_BACK 1 -#define SIDE_ON 2 - -typedef struct msurface_s { - int viewCount; // if == tr.viewCount, already added - struct shader_s *shader; - int fogIndex; - - surfaceType_t *data; // any of srf*_t -} msurface_t; - - - -#define CONTENTS_NODE -1 -typedef struct mnode_s { - // common with leaf and node - int contents; // -1 for nodes, to differentiate from leafs - int visframe; // node needs to be traversed if current - vec3_t mins, maxs; // for bounding box culling - struct mnode_s *parent; - - // node specific - cplane_t *plane; - struct mnode_s *children[2]; - - // leaf specific - int cluster; - int area; - - msurface_t **firstmarksurface; - int nummarksurfaces; -} mnode_t; - -typedef struct { - vec3_t bounds[2]; // for culling - msurface_t *firstSurface; - int numSurfaces; -} bmodel_t; - -typedef struct { - char name[MAX_QPATH]; // ie: maps/tim_dm2.bsp - char baseName[MAX_QPATH]; // ie: tim_dm2 - - int dataSize; - - int numShaders; - dshader_t *shaders; - - bmodel_t *bmodels; - - int numplanes; - cplane_t *planes; - - int numnodes; // includes leafs - int numDecisionNodes; - mnode_t *nodes; - - int numsurfaces; - msurface_t *surfaces; - - int nummarksurfaces; - msurface_t **marksurfaces; - - int numfogs; - fog_t *fogs; - - vec3_t lightGridOrigin; - vec3_t lightGridSize; - vec3_t lightGridInverseSize; - int lightGridBounds[3]; - byte *lightGridData; - - - int numClusters; - int clusterBytes; - const byte *vis; // may be passed in by CM_LoadMap to save space - - byte *novis; // clusterBytes of 0xff - - char *entityString; - char *entityParsePoint; -} world_t; - -//====================================================================== - -typedef enum { - MOD_BAD, - MOD_BRUSH, - MOD_MESH, - MOD_MD4, -#ifdef RAVENMD4 - MOD_MDR, -#endif - MOD_IQM -} modtype_t; - -typedef struct model_s { - char name[MAX_QPATH]; - modtype_t type; - int index; // model = tr.models[model->index] - - int dataSize; // just for listing purposes - bmodel_t *bmodel; // only if type == MOD_BRUSH - md3Header_t *md3[MD3_MAX_LODS]; // only if type == MOD_MESH - void *modelData; // only if type == (MOD_MD4 | MOD_MDR | MOD_IQM) - - int numLods; -} model_t; - - -#define MAX_MOD_KNOWN 1024 - -void R_ModelInit (void); -model_t *R_GetModelByHandle( qhandle_t hModel ); -int R_LerpTag( orientation_t *tag, qhandle_t handle, int startFrame, int endFrame, - float frac, const char *tagName ); -void R_ModelBounds( qhandle_t handle, vec3_t mins, vec3_t maxs ); - -void R_Modellist_f (void); - -//==================================================== -extern refimport_t ri; - -#define MAX_DRAWIMAGES 2048 -#define MAX_SKINS 1024 - - -#define MAX_DRAWSURFS 0x10000 -#define DRAWSURF_MASK (MAX_DRAWSURFS-1) - -/* - -the drawsurf sort data is packed into a single 32 bit value so it can be -compared quickly during the qsorting process - -the bits are allocated as follows: - -0 - 1 : dlightmap index -//2 : used to be clipped flag REMOVED - 03.21.00 rad -2 - 6 : fog index -11 - 20 : entity index -21 - 31 : sorted shader index - - TTimo - 1.32 -0-1 : dlightmap index -2-6 : fog index -7-16 : entity index -17-30 : sorted shader index -*/ -#define QSORT_FOGNUM_SHIFT 2 -#define QSORT_REFENTITYNUM_SHIFT 7 -#define QSORT_SHADERNUM_SHIFT (QSORT_REFENTITYNUM_SHIFT+REFENTITYNUM_BITS) -#if (QSORT_SHADERNUM_SHIFT+SHADERNUM_BITS) > 32 - #error "Need to update sorting, too many bits." -#endif - -extern int gl_filter_min, gl_filter_max; - -/* -** performanceCounters_t -*/ -typedef struct { - int c_sphere_cull_patch_in, c_sphere_cull_patch_clip, c_sphere_cull_patch_out; - int c_box_cull_patch_in, c_box_cull_patch_clip, c_box_cull_patch_out; - int c_sphere_cull_md3_in, c_sphere_cull_md3_clip, c_sphere_cull_md3_out; - int c_box_cull_md3_in, c_box_cull_md3_clip, c_box_cull_md3_out; - - int c_leafs; - int c_dlightSurfaces; - int c_dlightSurfacesCulled; -} frontEndCounters_t; - -#define FOG_TABLE_SIZE 256 -#define FUNCTABLE_SIZE 1024 -#define FUNCTABLE_SIZE2 10 -#define FUNCTABLE_MASK (FUNCTABLE_SIZE-1) - - -// the renderer front end should never modify glstate_t -typedef struct { - int currenttextures[2]; - int currenttmu; - qboolean finishCalled; - int texEnv[2]; - int faceCulling; - unsigned long glStateBits; -} glstate_t; - - -typedef struct { - int c_surfaces, c_shaders, c_vertexes, c_indexes, c_totalIndexes; - float c_overDraw; - - int c_dlightVertexes; - int c_dlightIndexes; - - int c_flareAdds; - int c_flareTests; - int c_flareRenders; - - int msec; // total msec for backend run -} backEndCounters_t; - -// all state modified by the back end is seperated -// from the front end state -typedef struct { - trRefdef_t refdef; - viewParms_t viewParms; - orientationr_t or; - backEndCounters_t pc; - qboolean isHyperspace; - trRefEntity_t *currentEntity; - qboolean skyRenderedThisView; // flag for drawing sun - - qboolean projection2D; // if qtrue, drawstretchpic doesn't need to change modes - byte color2D[4]; - qboolean vertexes2D; // shader needs to be finished - trRefEntity_t entity2D; // currentEntity will point at this when doing 2D rendering -} backEndState_t; - -/* -** trGlobals_t -** -** Most renderer globals are defined here. -** backend functions should never modify any of these fields, -** but may read fields that aren't dynamically modified -** by the frontend. -*/ -typedef struct { - qboolean registered; // cleared at shutdown, set at beginRegistration - - int visCount; // incremented every time a new vis cluster is entered - int frameCount; // incremented every frame - int sceneCount; // incremented every scene - int viewCount; // incremented every view (twice a scene if portaled) - // and every R_MarkFragments call - - int frameSceneNum; // zeroed at RE_BeginFrame - - qboolean worldMapLoaded; - world_t *world; - - const byte *externalVisData; // from RE_SetWorldVisData, shared with CM_Load - - image_t *defaultImage; - image_t *scratchImage[32]; - image_t *fogImage; - image_t *dlightImage; // inverse-quare highlight for projective adding - image_t *flareImage; - image_t *whiteImage; // full of 0xff - image_t *identityLightImage; // full of tr.identityLightByte - - shader_t *defaultShader; - shader_t *shadowShader; - shader_t *projectionShadowShader; - - shader_t *flareShader; - shader_t *sunShader; - - int numLightmaps; - image_t **lightmaps; - - trRefEntity_t *currentEntity; - trRefEntity_t worldEntity; // point currentEntity at this when rendering world - int currentEntityNum; - int shiftedEntityNum; // currentEntityNum << QSORT_REFENTITYNUM_SHIFT - model_t *currentModel; - - viewParms_t viewParms; - - float identityLight; // 1.0 / ( 1 << overbrightBits ) - int identityLightByte; // identityLight * 255 - int overbrightBits; // r_overbrightBits->integer, but set to 0 if no hw gamma - - orientationr_t or; // for current entity - - trRefdef_t refdef; - - int viewCluster; - - vec3_t sunLight; // from the sky shader for this level - vec3_t sunDirection; - - frontEndCounters_t pc; - int frontEndMsec; // not in pc due to clearing issue - - vec4_t clipRegion; // 2D clipping region - - // - // put large tables at the end, so most elements will be - // within the +/32K indexed range on risc processors - // - model_t *models[MAX_MOD_KNOWN]; - int numModels; - - int numImages; - image_t *images[MAX_DRAWIMAGES]; - - // shader indexes from other modules will be looked up in tr.shaders[] - // shader indexes from drawsurfs will be looked up in sortedShaders[] - // lower indexed sortedShaders must be rendered first (opaque surfaces before translucent) - int numShaders; - shader_t *shaders[MAX_SHADERS]; - shader_t *sortedShaders[MAX_SHADERS]; - - int numSkins; - skin_t *skins[MAX_SKINS]; - - float sinTable[FUNCTABLE_SIZE]; - float squareTable[FUNCTABLE_SIZE]; - float triangleTable[FUNCTABLE_SIZE]; - float sawToothTable[FUNCTABLE_SIZE]; - float inverseSawToothTable[FUNCTABLE_SIZE]; - float fogTable[FOG_TABLE_SIZE]; -} trGlobals_t; - -extern backEndState_t backEnd; -extern trGlobals_t tr; -extern glconfig_t glConfig; // outside of TR since it shouldn't be cleared during ref re-init -extern glstate_t glState; // outside of TR since it shouldn't be cleared during ref re-init - - -// -// cvars -// -extern cvar_t *r_flareSize; -extern cvar_t *r_flareFade; -// coefficient for the flare intensity falloff function. -#define FLARE_STDCOEFF "150" -extern cvar_t *r_flareCoeff; - -extern cvar_t *r_railWidth; -extern cvar_t *r_railCoreWidth; -extern cvar_t *r_railSegmentLength; - -extern cvar_t *r_ignore; // used for debugging anything -extern cvar_t *r_verbose; // used for verbose debug spew -extern cvar_t *r_ignoreFastPath; // allows us to ignore our Tess fast paths - -extern cvar_t *r_znear; // near Z clip plane -extern cvar_t *r_zproj; // z distance of projection plane -extern cvar_t *r_stereoSeparation; // separation of cameras for stereo rendering - -extern cvar_t *r_stencilbits; // number of desired stencil bits -extern cvar_t *r_depthbits; // number of desired depth bits -extern cvar_t *r_colorbits; // number of desired color bits, only relevant for fullscreen -extern cvar_t *r_texturebits; // number of desired texture bits -extern cvar_t *r_ext_multisample; - // 0 = use framebuffer depth - // 16 = use 16-bit textures - // 32 = use 32-bit textures - // all else = error - -extern cvar_t *r_measureOverdraw; // enables stencil buffer overdraw measurement - -extern cvar_t *r_lodbias; // push/pull LOD transitions -extern cvar_t *r_lodscale; - -extern cvar_t *r_primitives; // "0" = based on compiled vertex array existance - // "1" = glDrawElemet tristrips - // "2" = glDrawElements triangles - // "-1" = no drawing - -extern cvar_t *r_inGameVideo; // controls whether in game video should be draw -extern cvar_t *r_fastsky; // controls whether sky should be cleared or drawn -extern cvar_t *r_drawSun; // controls drawing of sun quad -extern cvar_t *r_dynamiclight; // dynamic lights enabled/disabled -extern cvar_t *r_dlightBacks; // dlight non-facing surfaces for continuity - -extern cvar_t *r_norefresh; // bypasses the ref rendering -extern cvar_t *r_drawentities; // disable/enable entity rendering -extern cvar_t *r_drawworld; // disable/enable world rendering -extern cvar_t *r_speeds; // various levels of information display -extern cvar_t *r_detailTextures; // enables/disables detail texturing stages -extern cvar_t *r_novis; // disable/enable usage of PVS -extern cvar_t *r_nocull; -extern cvar_t *r_facePlaneCull; // enables culling of planar surfaces with back side test -extern cvar_t *r_nocurves; -extern cvar_t *r_showcluster; - -extern cvar_t *r_width; -extern cvar_t *r_height; -extern cvar_t *r_pixelAspect; - -extern cvar_t *r_fullscreen; -extern cvar_t *r_noborder; -extern cvar_t *r_gamma; -extern cvar_t *r_ignorehwgamma; // overrides hardware gamma capabilities - -extern cvar_t *r_allowExtensions; // global enable/disable of OpenGL extensions -extern cvar_t *r_ext_compressed_textures; // these control use of specific extensions -extern cvar_t *r_ext_multitexture; -extern cvar_t *r_ext_compiled_vertex_array; -extern cvar_t *r_ext_texture_env_add; - -extern cvar_t *r_ext_texture_filter_anisotropic; -extern cvar_t *r_ext_max_anisotropy; - -extern cvar_t *r_nobind; // turns off binding to appropriate textures -extern cvar_t *r_singleShader; // make most world faces use default shader -extern cvar_t *r_roundImagesDown; -extern cvar_t *r_colorMipLevels; // development aid to see texture mip usage -extern cvar_t *r_picmip; // controls picmip values -extern cvar_t *r_finish; -extern cvar_t *r_drawBuffer; -extern cvar_t *r_swapInterval; -extern cvar_t *r_textureMode; -extern cvar_t *r_offsetFactor; -extern cvar_t *r_offsetUnits; - -extern cvar_t *r_fullbright; // avoid lightmap pass -extern cvar_t *r_lightmap; // render lightmaps only -extern cvar_t *r_vertexLight; // vertex lighting mode for better performance -extern cvar_t *r_uiFullScreen; // ui is running fullscreen - -extern cvar_t *r_logFile; // number of frames to emit GL logs -extern cvar_t *r_showtris; // enables wireframe rendering of the world -extern cvar_t *r_showsky; // forces sky in front of all surfaces -extern cvar_t *r_shownormals; // draws wireframe normals -extern cvar_t *r_clear; // force screen clear every frame - -extern cvar_t *r_shadows; // controls shadows: 0 = none, 1 = blur, 2 = stencil, 3 = black planar projection -extern cvar_t *r_flares; // light flares - -extern cvar_t *r_intensity; - -extern cvar_t *r_lockpvs; -extern cvar_t *r_noportals; -extern cvar_t *r_portalOnly; - -extern cvar_t *r_subdivisions; -extern cvar_t *r_lodCurveError; -extern cvar_t *r_skipBackEnd; - -extern cvar_t *r_stereoEnabled; -extern cvar_t *r_anaglyphMode; - -extern cvar_t *r_greyscale; - -extern cvar_t *r_ignoreGLErrors; - -extern cvar_t *r_overBrightBits; -extern cvar_t *r_mapOverBrightBits; - -extern cvar_t *r_debugSurface; -extern cvar_t *r_simpleMipMaps; - -extern cvar_t *r_showImages; -extern cvar_t *r_debugSort; - -extern cvar_t *r_printShaders; -extern cvar_t *r_saveFontData; - -extern cvar_t *r_marksOnTriangleMeshes; - -//==================================================================== - -float R_NoiseGet4f( float x, float y, float z, float t ); -void R_NoiseInit( void ); - -void R_SwapBuffers( int ); - -void R_RenderView( viewParms_t *parms ); - -void R_AddMD3Surfaces( trRefEntity_t *e ); -void R_AddNullModelSurfaces( trRefEntity_t *e ); -void R_AddBeamSurfaces( trRefEntity_t *e ); -void R_AddRailSurfaces( trRefEntity_t *e, qboolean isUnderwater ); -void R_AddLightningBoltSurfaces( trRefEntity_t *e ); - -void R_AddPolygonSurfaces( void ); - -void R_DecomposeSort( unsigned sort, int *entityNum, shader_t **shader, - int *fogNum, int *dlightMap ); - -void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int fogIndex, int dlightMap ); - - -#define CULL_IN 0 // completely unclipped -#define CULL_CLIP 1 // clipped by one or more planes -#define CULL_OUT 2 // completely outside the clipping planes -void R_LocalNormalToWorld (vec3_t local, vec3_t world); -void R_LocalPointToWorld (vec3_t local, vec3_t world); -int R_CullLocalBox (vec3_t bounds[2]); -int R_CullPointAndRadius( vec3_t origin, float radius ); -int R_CullLocalPointAndRadius( vec3_t origin, float radius ); - -void R_SetupProjection(viewParms_t *dest, float zProj, qboolean computeFrustum); -void R_RotateForEntity( const trRefEntity_t *ent, const viewParms_t *viewParms, orientationr_t *or ); - -/* -** GL wrapper/helper functions -*/ -void GL_Bind( image_t *image ); -void GL_SetDefaultState (void); -void GL_SelectTexture( int unit ); -void GL_TextureMode( const char *string ); -void GL_CheckErrors( void ); -void GL_State( unsigned long stateVector ); -void GL_TexEnv( int env ); -void GL_Cull( int cullType ); - -#define GLS_SRCBLEND_ZERO 0x00000001 -#define GLS_SRCBLEND_ONE 0x00000002 -#define GLS_SRCBLEND_DST_COLOR 0x00000003 -#define GLS_SRCBLEND_ONE_MINUS_DST_COLOR 0x00000004 -#define GLS_SRCBLEND_SRC_ALPHA 0x00000005 -#define GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA 0x00000006 -#define GLS_SRCBLEND_DST_ALPHA 0x00000007 -#define GLS_SRCBLEND_ONE_MINUS_DST_ALPHA 0x00000008 -#define GLS_SRCBLEND_ALPHA_SATURATE 0x00000009 -#define GLS_SRCBLEND_BITS 0x0000000f - -#define GLS_DSTBLEND_ZERO 0x00000010 -#define GLS_DSTBLEND_ONE 0x00000020 -#define GLS_DSTBLEND_SRC_COLOR 0x00000030 -#define GLS_DSTBLEND_ONE_MINUS_SRC_COLOR 0x00000040 -#define GLS_DSTBLEND_SRC_ALPHA 0x00000050 -#define GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA 0x00000060 -#define GLS_DSTBLEND_DST_ALPHA 0x00000070 -#define GLS_DSTBLEND_ONE_MINUS_DST_ALPHA 0x00000080 -#define GLS_DSTBLEND_BITS 0x000000f0 - -#define GLS_DEPTHMASK_TRUE 0x00000100 - -#define GLS_POLYMODE_LINE 0x00001000 - -#define GLS_DEPTHTEST_DISABLE 0x00010000 -#define GLS_DEPTHFUNC_EQUAL 0x00020000 - -#define GLS_ATEST_GT_0 0x10000000 -#define GLS_ATEST_LT_80 0x20000000 -#define GLS_ATEST_GE_80 0x40000000 -#define GLS_ATEST_BITS 0x70000000 - -#define GLS_DEFAULT GLS_DEPTHMASK_TRUE - -void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty); -void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty); - -void RE_BeginFrame( stereoFrame_t stereoFrame ); -void RE_BeginRegistration( glconfig_t *glconfig ); -void RE_LoadWorldMap( const char *mapname ); -void RE_SetWorldVisData( const byte *vis ); -qhandle_t RE_RegisterModel( const char *name ); -qhandle_t RE_RegisterSkin( const char *name ); -void RE_Shutdown( qboolean destroyWindow ); - -qboolean R_GetEntityToken( char *buffer, int size ); - -model_t *R_AllocModel( void ); - -void R_Init( void ); -image_t *R_FindImageFile( const char *name, qboolean mipmap, qboolean allowPicmip, int glWrapClampMode ); - -image_t *R_CreateImage( const char *name, const byte *pic, int width, int height, qboolean mipmap, - qboolean allowPicmip, int wrapClampMode ); - -void R_SetColorMappings( void ); -void R_GammaCorrect( byte *buffer, int bufSize ); - -void R_ImageList_f( void ); -void R_SkinList_f( void ); -// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=516 -const void *RB_TakeScreenshotCmd( const void *data ); -void R_ScreenShot_f( void ); - -void R_InitFogTable( void ); -float R_FogFactor( float s, float t ); -void R_InitImages( void ); -void R_DeleteTextures( void ); -int R_SumOfUsedImages( void ); -void R_InitSkins( void ); -skin_t *R_GetSkinByHandle( qhandle_t hSkin ); - -int R_ComputeLOD( trRefEntity_t *ent ); - -const void *RB_TakeVideoFrameCmd( const void *data ); - -// -// tr_shader.c -// -qhandle_t RE_RegisterShaderLightMap( const char *name, int lightmapIndex ); -qhandle_t RE_RegisterShader( const char *name ); -qhandle_t RE_RegisterShaderNoMip( const char *name ); -qhandle_t RE_RegisterShaderFromImage(const char *name, int lightmapIndex, image_t *image, qboolean mipRawImage); - -shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImage ); -shader_t *R_GetShaderByHandle( qhandle_t hShader ); -shader_t *R_GetShaderByState( int index, long *cycleTime ); -shader_t *R_FindShaderByName( const char *name ); -void R_InitShaders( void ); -void R_ShaderList_f( void ); -void R_RemapShader(const char *oldShader, const char *newShader, const char *timeOffset); - -/* -==================================================================== - -IMPLEMENTATION SPECIFIC FUNCTIONS - -==================================================================== -*/ - -void GLimp_Init( void ); -void GLimp_Shutdown( void ); -void GLimp_EndFrame( void ); - -void GLimp_LogComment( char *comment ); -void GLimp_Minimize(void); - -// NOTE TTimo linux works with float gamma value, not the gamma table -// the params won't be used, getting the r_gamma cvar directly -void GLimp_SetGamma( unsigned char red[256], - unsigned char green[256], - unsigned char blue[256] ); - -void GL_ResolveHardwareType( void ); - -/* -==================================================================== - -TESSELATOR/SHADER DECLARATIONS - -==================================================================== -*/ -typedef byte color4ub_t[4]; - -typedef struct stageVars -{ - color4ub_t colors[SHADER_MAX_VERTEXES]; - vec2_t texcoords[NUM_TEXTURE_BUNDLES][SHADER_MAX_VERTEXES]; -} stageVars_t; - - -typedef struct shaderCommands_s -{ - glIndex_t indexes[SHADER_MAX_INDEXES] QALIGN(16); - vec4_t xyz[SHADER_MAX_VERTEXES] QALIGN(16); - vec4_t normal[SHADER_MAX_VERTEXES] QALIGN(16); - vec2_t texCoords[SHADER_MAX_VERTEXES][2] QALIGN(16); - color4ub_t vertexColors[SHADER_MAX_VERTEXES] QALIGN(16); - int vertexDlightBits[SHADER_MAX_VERTEXES] QALIGN(16); - - stageVars_t svars QALIGN(16); - - color4ub_t constantColor255[SHADER_MAX_VERTEXES] QALIGN(16); - - shader_t *shader; - float shaderTime; - int fogNum; - - int dlightBits; // or together of all vertexDlightBits - - int numIndexes; - int numVertexes; - - // info extracted from current shader - int numPasses; - void (*currentStageIteratorFunc)( void ); - shaderStage_t **xstages; -} shaderCommands_t; - -extern shaderCommands_t tess; - -void RB_BeginSurface(shader_t *shader, int fogNum ); -void RB_EndSurface(void); -void RB_CheckOverflow( int verts, int indexes ); -#define RB_CHECKOVERFLOW(v,i) if (tess.numVertexes + (v) >= SHADER_MAX_VERTEXES || tess.numIndexes + (i) >= SHADER_MAX_INDEXES ) {RB_CheckOverflow(v,i);} - -void RB_StageIteratorGeneric( void ); -void RB_StageIteratorSky( void ); -void RB_StageIteratorVertexLitTexture( void ); -void RB_StageIteratorLightmappedMultitexture( void ); - -void RB_AddQuadStamp( vec3_t origin, vec3_t left, vec3_t up, byte *color ); -void RB_AddQuadStampExt( vec3_t origin, vec3_t left, vec3_t up, byte *color, float s1, float t1, float s2, float t2 ); - -void RB_ShowImages( void ); - - -/* -============================================================ - -WORLD MAP - -============================================================ -*/ - -void R_AddBrushModelSurfaces( trRefEntity_t *e ); -void R_AddWorldSurfaces( void ); -qboolean R_inPVS( const vec3_t p1, const vec3_t p2 ); - - -/* -============================================================ - -FLARES - -============================================================ -*/ - -void R_ClearFlares( void ); - -void RB_AddFlare( void *surface, int fogNum, vec3_t point, vec3_t color, vec3_t normal ); -void RB_AddDlightFlares( void ); -void RB_RenderFlares (void); - -/* -============================================================ - -LIGHTS - -============================================================ -*/ - -void R_DlightBmodel( bmodel_t *bmodel ); -void R_SetupEntityLighting( const trRefdef_t *refdef, trRefEntity_t *ent ); -void R_TransformDlights( int count, dlight_t *dl, orientationr_t *or ); -int R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ); - - -/* -============================================================ - -SHADOWS - -============================================================ -*/ - -void RB_ShadowTessEnd( void ); -void RB_ShadowFinish( void ); -void RB_ProjectionShadowDeform( void ); - -/* -============================================================ - -SKIES - -============================================================ -*/ - -void R_BuildCloudData( shaderCommands_t *shader ); -void R_InitSkyTexCoords( float cloudLayerHeight ); -void R_DrawSkyBox( shaderCommands_t *shader ); -void RB_DrawSun( void ); -void RB_ClipSkyPolygons( shaderCommands_t *shader ); - -/* -============================================================ - -CURVE TESSELATION - -============================================================ -*/ - -#define PATCH_STITCHING - -srfGridMesh_t *R_SubdividePatchToGrid( int width, int height, - drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ); -srfGridMesh_t *R_GridInsertColumn( srfGridMesh_t *grid, int column, int row, vec3_t point, float loderror ); -srfGridMesh_t *R_GridInsertRow( srfGridMesh_t *grid, int row, int column, vec3_t point, float loderror ); -void R_FreeSurfaceGridMesh( srfGridMesh_t *grid ); - -/* -============================================================ - -MARKERS, POLYGON PROJECTION ON WORLD POLYGONS - -============================================================ -*/ - -int R_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection, - int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ); - - -/* -============================================================ - -SCENE GENERATION - -============================================================ -*/ - -void R_InitNextFrame( void ); - -void RE_ClearScene( void ); -void RE_AddRefEntityToScene( const refEntity_t *ent ); -void RE_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts, int num ); -void RE_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ); -void RE_AddAdditiveLightToScene( const vec3_t org, float intensity, float r, float g, float b ); -void RE_RenderScene( const refdef_t *fd ); - -#ifdef RAVENMD4 -/* -============================================================= - -UNCOMPRESSING BONES - -============================================================= -*/ - -#define MC_BITS_X (16) -#define MC_BITS_Y (16) -#define MC_BITS_Z (16) -#define MC_BITS_VECT (16) - -#define MC_SCALE_X (1.0f/64) -#define MC_SCALE_Y (1.0f/64) -#define MC_SCALE_Z (1.0f/64) - -void MC_UnCompress(float mat[3][4],const unsigned char * comp); -#endif - -/* -============================================================= - -ANIMATED MODELS - -============================================================= -*/ - -// void R_MakeAnimModel( model_t *model ); haven't seen this one really, so not needed I guess. -void R_AddAnimSurfaces( trRefEntity_t *ent ); -void RB_SurfaceAnim( md4Surface_t *surfType ); -#ifdef RAVENMD4 -void R_MDRAddAnimSurfaces( trRefEntity_t *ent ); -void RB_MDRSurfaceAnim( md4Surface_t *surface ); -#endif -qboolean R_LoadIQM (model_t *mod, void *buffer, int filesize, const char *name ); -void R_AddIQMSurfaces( trRefEntity_t *ent ); -void RB_IQMSurfaceAnim( surfaceType_t *surface ); -int R_IQMLerpTag( orientation_t *tag, iqmData_t *data, - int startFrame, int endFrame, - float frac, const char *tagName ); - -/* -============================================================= - -IMAGE LOADERS - -============================================================= -*/ - -void R_LoadBMP( const char *name, byte **pic, int *width, int *height ); -void R_LoadJPG( const char *name, byte **pic, int *width, int *height ); -void R_LoadPCX( const char *name, byte **pic, int *width, int *height ); -void R_LoadPNG( const char *name, byte **pic, int *width, int *height ); -void R_LoadTGA( const char *name, byte **pic, int *width, int *height ); - -/* -============================================================= -============================================================= -*/ -void R_TransformModelToClip( const vec3_t src, const float *modelMatrix, const float *projectionMatrix, - vec4_t eye, vec4_t dst ); -void R_TransformClipToWindow( const vec4_t clip, const viewParms_t *view, vec4_t normalized, vec4_t window ); - -void RB_DeformTessGeometry( void ); - -void RB_CalcEnvironmentTexCoords( float *dstTexCoords ); -void RB_CalcFogTexCoords( float *dstTexCoords ); -void RB_CalcScrollTexCoords( const float scroll[2], float *dstTexCoords ); -void RB_CalcRotateTexCoords( float rotSpeed, float *dstTexCoords ); -void RB_CalcScaleTexCoords( const float scale[2], float *dstTexCoords ); -void RB_CalcTurbulentTexCoords( const waveForm_t *wf, float *dstTexCoords ); -void RB_CalcTransformTexCoords( const texModInfo_t *tmi, float *dstTexCoords ); -void RB_CalcModulateColorsByFog( unsigned char *dstColors ); -void RB_CalcModulateAlphasByFog( unsigned char *dstColors ); -void RB_CalcModulateRGBAsByFog( unsigned char *dstColors ); -void RB_CalcWaveAlpha( const waveForm_t *wf, unsigned char *dstColors ); -void RB_CalcWaveColor( const waveForm_t *wf, unsigned char *dstColors ); -void RB_CalcAlphaFromEntity( unsigned char *dstColors ); -void RB_CalcAlphaFromOneMinusEntity( unsigned char *dstColors ); -void RB_CalcStretchTexCoords( const waveForm_t *wf, float *texCoords ); -void RB_CalcColorFromEntity( unsigned char *dstColors ); -void RB_CalcColorFromOneMinusEntity( unsigned char *dstColors ); -void RB_CalcSpecularAlpha( unsigned char *alphas ); -void RB_CalcDiffuseColor( unsigned char *colors ); - -/* -============================================================= - -RENDERER BACK END FUNCTIONS - -============================================================= -*/ - -void RB_ExecuteRenderCommands( const void *data ); - -/* -============================================================= - -RENDERER BACK END COMMAND QUEUE - -============================================================= -*/ - -#define MAX_RENDER_COMMANDS 0x40000 - -typedef struct { - byte cmds[MAX_RENDER_COMMANDS]; - int used; -} renderCommandList_t; - -typedef struct { - int commandId; - float color[4]; -} setColorCommand_t; - -typedef struct { - int commandId; - int buffer; -} drawBufferCommand_t; - -typedef struct { - int commandId; - image_t *image; - int width; - int height; - void *data; -} subImageCommand_t; - -typedef struct { - int commandId; -} swapBuffersCommand_t; - -typedef struct { - int commandId; - int buffer; -} endFrameCommand_t; - -typedef struct { - int commandId; - shader_t *shader; - float x, y; - float w, h; - float s1, t1; - float s2, t2; -} stretchPicCommand_t; - -typedef struct { - int commandId; - trRefdef_t refdef; - viewParms_t viewParms; - drawSurf_t *drawSurfs; - int numDrawSurfs; -} drawSurfsCommand_t; - -typedef struct { - int commandId; - int x; - int y; - int width; - int height; - char *fileName; - qboolean jpeg; -} screenshotCommand_t; - -typedef struct { - int commandId; - int width; - int height; - byte *captureBuffer; - byte *encodeBuffer; - qboolean motionJpeg; -} videoFrameCommand_t; - -typedef struct -{ - int commandId; - - GLboolean rgba[4]; -} colorMaskCommand_t; - -typedef struct -{ - int commandId; -} clearDepthCommand_t; - -typedef enum { - RC_END_OF_LIST, - RC_SET_COLOR, - RC_STRETCH_PIC, - RC_DRAW_SURFS, - RC_DRAW_BUFFER, - RC_SWAP_BUFFERS, - RC_SCREENSHOT, - RC_VIDEOFRAME, - RC_COLORMASK, - RC_CLEARDEPTH -} renderCommand_t; - - -// these are sort of arbitrary limits. -// the limits apply to the sum of all scenes in a frame -- -// the main view, all the 3D icons, etc -#define MAX_POLYS 600 -#define MAX_POLYVERTS 3000 - -// all of the information needed by the back end must be -// contained in a backEndData_t -typedef struct { - drawSurf_t drawSurfs[MAX_DRAWSURFS]; - dlight_t dlights[MAX_DLIGHTS]; - trRefEntity_t entities[MAX_REFENTITIES]; - srfPoly_t *polys;//[MAX_POLYS]; - polyVert_t *polyVerts;//[MAX_POLYVERTS]; - renderCommandList_t commands; -} backEndData_t; - -extern int max_polys; -extern int max_polyverts; - -extern backEndData_t *backEndData; // the second one may not be allocated - -extern volatile renderCommandList_t *renderCommandList; - - -void *R_GetCommandBuffer( int bytes ); -void RB_ExecuteRenderCommands( const void *data ); - -void R_IssuePendingRenderCommands( void ); - -void R_AddDrawSurfCmd( drawSurf_t *drawSurfs, int numDrawSurfs ); - -void RE_SetColor( const float *rgba ); -void RE_SetClipRegion( const float *region ); -void RE_StretchPic ( float x, float y, float w, float h, - float s1, float t1, float s2, float t2, qhandle_t hShader ); -void RE_BeginFrame( stereoFrame_t stereoFrame ); -void RE_EndFrame( int *frontEndMsec, int *backEndMsec ); -void RE_SaveJPG(char * filename, int quality, int image_width, int image_height, - unsigned char *image_buffer, int padding); -size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality, - int image_width, int image_height, byte *image_buffer, int padding); -void RE_TakeVideoFrame( int width, int height, - byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg ); - -// font stuff -void R_InitFreeType( void ); -void R_DoneFreeType( void ); -void RE_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font); - - -#endif //TR_LOCAL_H diff --git a/src/renderer/tr_main.c b/src/renderer/tr_main.c deleted file mode 100644 index 3b82a777..00000000 --- a/src/renderer/tr_main.c +++ /dev/null @@ -1,1399 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_main.c -- main control flow for each frame - -#include "tr_local.h" - -#include // memcpy - -trGlobals_t tr; - -static float s_flipMatrix[16] = { - // convert from our coordinate system (looking down X) - // to OpenGL's coordinate system (looking down -Z) - 0, 0, -1, 0, - -1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 0, 1 -}; - - -refimport_t ri; - -// entities that will have procedurally generated surfaces will just -// point at this for their sorting surface -surfaceType_t entitySurface = SF_ENTITY; - -/* -================= -R_CullLocalBox - -Returns CULL_IN, CULL_CLIP, or CULL_OUT -================= -*/ -int R_CullLocalBox (vec3_t bounds[2]) { - int i, j; - vec3_t transformed[8]; - float dists[8]; - vec3_t v; - cplane_t *frust; - int anyBack; - int front, back; - - if ( r_nocull->integer ) { - return CULL_CLIP; - } - - // transform into world space - for (i = 0 ; i < 8 ; i++) { - v[0] = bounds[i&1][0]; - v[1] = bounds[(i>>1)&1][1]; - v[2] = bounds[(i>>2)&1][2]; - - VectorCopy( tr.or.origin, transformed[i] ); - VectorMA( transformed[i], v[0], tr.or.axis[0], transformed[i] ); - VectorMA( transformed[i], v[1], tr.or.axis[1], transformed[i] ); - VectorMA( transformed[i], v[2], tr.or.axis[2], transformed[i] ); - } - - // check against frustum planes - anyBack = 0; - for (i = 0 ; i < 4 ; i++) { - frust = &tr.viewParms.frustum[i]; - - front = back = 0; - for (j = 0 ; j < 8 ; j++) { - dists[j] = DotProduct(transformed[j], frust->normal); - if ( dists[j] > frust->dist ) { - front = 1; - if ( back ) { - break; // a point is in front - } - } else { - back = 1; - } - } - if ( !front ) { - // all points were behind one of the planes - return CULL_OUT; - } - anyBack |= back; - } - - if ( !anyBack ) { - return CULL_IN; // completely inside frustum - } - - return CULL_CLIP; // partially clipped -} - -/* -** R_CullLocalPointAndRadius -*/ -int R_CullLocalPointAndRadius( vec3_t pt, float radius ) -{ - vec3_t transformed; - - R_LocalPointToWorld( pt, transformed ); - - return R_CullPointAndRadius( transformed, radius ); -} - -/* -** R_CullPointAndRadius -*/ -int R_CullPointAndRadius( vec3_t pt, float radius ) -{ - int i; - float dist; - cplane_t *frust; - qboolean mightBeClipped = qfalse; - - if ( r_nocull->integer ) { - return CULL_CLIP; - } - - // check against frustum planes - for (i = 0 ; i < 4 ; i++) - { - frust = &tr.viewParms.frustum[i]; - - dist = DotProduct( pt, frust->normal) - frust->dist; - if ( dist < -radius ) - { - return CULL_OUT; - } - else if ( dist <= radius ) - { - mightBeClipped = qtrue; - } - } - - if ( mightBeClipped ) - { - return CULL_CLIP; - } - - return CULL_IN; // completely inside frustum -} - - -/* -================= -R_LocalNormalToWorld - -================= -*/ -void R_LocalNormalToWorld (vec3_t local, vec3_t world) { - world[0] = local[0] * tr.or.axis[0][0] + local[1] * tr.or.axis[1][0] + local[2] * tr.or.axis[2][0]; - world[1] = local[0] * tr.or.axis[0][1] + local[1] * tr.or.axis[1][1] + local[2] * tr.or.axis[2][1]; - world[2] = local[0] * tr.or.axis[0][2] + local[1] * tr.or.axis[1][2] + local[2] * tr.or.axis[2][2]; -} - -/* -================= -R_LocalPointToWorld - -================= -*/ -void R_LocalPointToWorld (vec3_t local, vec3_t world) { - world[0] = local[0] * tr.or.axis[0][0] + local[1] * tr.or.axis[1][0] + local[2] * tr.or.axis[2][0] + tr.or.origin[0]; - world[1] = local[0] * tr.or.axis[0][1] + local[1] * tr.or.axis[1][1] + local[2] * tr.or.axis[2][1] + tr.or.origin[1]; - world[2] = local[0] * tr.or.axis[0][2] + local[1] * tr.or.axis[1][2] + local[2] * tr.or.axis[2][2] + tr.or.origin[2]; -} - -/* -================= -R_WorldToLocal - -================= -*/ -void R_WorldToLocal (vec3_t world, vec3_t local) { - local[0] = DotProduct(world, tr.or.axis[0]); - local[1] = DotProduct(world, tr.or.axis[1]); - local[2] = DotProduct(world, tr.or.axis[2]); -} - -/* -========================== -R_TransformModelToClip - -========================== -*/ -void R_TransformModelToClip( const vec3_t src, const float *modelMatrix, const float *projectionMatrix, - vec4_t eye, vec4_t dst ) { - int i; - - for ( i = 0 ; i < 4 ; i++ ) { - eye[i] = - src[0] * modelMatrix[ i + 0 * 4 ] + - src[1] * modelMatrix[ i + 1 * 4 ] + - src[2] * modelMatrix[ i + 2 * 4 ] + - 1 * modelMatrix[ i + 3 * 4 ]; - } - - for ( i = 0 ; i < 4 ; i++ ) { - dst[i] = - eye[0] * projectionMatrix[ i + 0 * 4 ] + - eye[1] * projectionMatrix[ i + 1 * 4 ] + - eye[2] * projectionMatrix[ i + 2 * 4 ] + - eye[3] * projectionMatrix[ i + 3 * 4 ]; - } -} - -/* -========================== -R_TransformClipToWindow - -========================== -*/ -void R_TransformClipToWindow( const vec4_t clip, const viewParms_t *view, vec4_t normalized, vec4_t window ) { - normalized[0] = clip[0] / clip[3]; - normalized[1] = clip[1] / clip[3]; - normalized[2] = ( clip[2] + clip[3] ) / ( 2 * clip[3] ); - - window[0] = 0.5f * ( 1.0f + normalized[0] ) * view->viewportWidth; - window[1] = 0.5f * ( 1.0f + normalized[1] ) * view->viewportHeight; - window[2] = normalized[2]; - - window[0] = (int) ( window[0] + 0.5 ); - window[1] = (int) ( window[1] + 0.5 ); -} - - -/* -========================== -myGlMultMatrix - -========================== -*/ -void myGlMultMatrix( const float *a, const float *b, float *out ) { - int i, j; - - for ( i = 0 ; i < 4 ; i++ ) { - for ( j = 0 ; j < 4 ; j++ ) { - out[ i * 4 + j ] = - a [ i * 4 + 0 ] * b [ 0 * 4 + j ] - + a [ i * 4 + 1 ] * b [ 1 * 4 + j ] - + a [ i * 4 + 2 ] * b [ 2 * 4 + j ] - + a [ i * 4 + 3 ] * b [ 3 * 4 + j ]; - } - } -} - -/* -================= -R_RotateForEntity - -Generates an orientation for an entity and viewParms -Does NOT produce any GL calls -Called by both the front end and the back end -================= -*/ -void R_RotateForEntity( const trRefEntity_t *ent, const viewParms_t *viewParms, - orientationr_t *or ) { - float glMatrix[16]; - vec3_t delta; - float axisLength; - - if ( ent->e.reType != RT_MODEL ) { - *or = viewParms->world; - return; - } - - VectorCopy( ent->e.origin, or->origin ); - - VectorCopy( ent->e.axis[0], or->axis[0] ); - VectorCopy( ent->e.axis[1], or->axis[1] ); - VectorCopy( ent->e.axis[2], or->axis[2] ); - - glMatrix[0] = or->axis[0][0]; - glMatrix[4] = or->axis[1][0]; - glMatrix[8] = or->axis[2][0]; - glMatrix[12] = or->origin[0]; - - glMatrix[1] = or->axis[0][1]; - glMatrix[5] = or->axis[1][1]; - glMatrix[9] = or->axis[2][1]; - glMatrix[13] = or->origin[1]; - - glMatrix[2] = or->axis[0][2]; - glMatrix[6] = or->axis[1][2]; - glMatrix[10] = or->axis[2][2]; - glMatrix[14] = or->origin[2]; - - glMatrix[3] = 0; - glMatrix[7] = 0; - glMatrix[11] = 0; - glMatrix[15] = 1; - - myGlMultMatrix( glMatrix, viewParms->world.modelMatrix, or->modelMatrix ); - - // calculate the viewer origin in the model's space - // needed for fog, specular, and environment mapping - VectorSubtract( viewParms->or.origin, or->origin, delta ); - - // compensate for scale in the axes if necessary - if ( ent->e.nonNormalizedAxes ) { - axisLength = VectorLength( ent->e.axis[0] ); - if ( !axisLength ) { - axisLength = 0; - } else { - axisLength = 1.0f / axisLength; - } - } else { - axisLength = 1.0f; - } - - or->viewOrigin[0] = DotProduct( delta, or->axis[0] ) * axisLength; - or->viewOrigin[1] = DotProduct( delta, or->axis[1] ) * axisLength; - or->viewOrigin[2] = DotProduct( delta, or->axis[2] ) * axisLength; -} - -/* -================= -R_RotateForViewer - -Sets up the modelview matrix for a given viewParm -================= -*/ -void R_RotateForViewer (void) -{ - float viewerMatrix[16]; - vec3_t origin; - - Com_Memset (&tr.or, 0, sizeof(tr.or)); - tr.or.axis[0][0] = 1; - tr.or.axis[1][1] = 1; - tr.or.axis[2][2] = 1; - VectorCopy (tr.viewParms.or.origin, tr.or.viewOrigin); - - // transform by the camera placement - VectorCopy( tr.viewParms.or.origin, origin ); - - viewerMatrix[0] = tr.viewParms.or.axis[0][0]; - viewerMatrix[4] = tr.viewParms.or.axis[0][1]; - viewerMatrix[8] = tr.viewParms.or.axis[0][2]; - viewerMatrix[12] = -origin[0] * viewerMatrix[0] + -origin[1] * viewerMatrix[4] + -origin[2] * viewerMatrix[8]; - - viewerMatrix[1] = tr.viewParms.or.axis[1][0]; - viewerMatrix[5] = tr.viewParms.or.axis[1][1]; - viewerMatrix[9] = tr.viewParms.or.axis[1][2]; - viewerMatrix[13] = -origin[0] * viewerMatrix[1] + -origin[1] * viewerMatrix[5] + -origin[2] * viewerMatrix[9]; - - viewerMatrix[2] = tr.viewParms.or.axis[2][0]; - viewerMatrix[6] = tr.viewParms.or.axis[2][1]; - viewerMatrix[10] = tr.viewParms.or.axis[2][2]; - viewerMatrix[14] = -origin[0] * viewerMatrix[2] + -origin[1] * viewerMatrix[6] + -origin[2] * viewerMatrix[10]; - - viewerMatrix[3] = 0; - viewerMatrix[7] = 0; - viewerMatrix[11] = 0; - viewerMatrix[15] = 1; - - // convert from our coordinate system (looking down X) - // to OpenGL's coordinate system (looking down -Z) - myGlMultMatrix( viewerMatrix, s_flipMatrix, tr.or.modelMatrix ); - - tr.viewParms.world = tr.or; - -} - -/* -** SetFarClip -*/ -static void R_SetFarClip( void ) -{ - float farthestCornerDistance = 0; - int i; - - // if not rendering the world (icons, menus, etc) - // set a 2k far clip plane - if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { - tr.viewParms.zFar = 2048; - return; - } - - // - // set far clipping planes dynamically - // - farthestCornerDistance = 0; - for ( i = 0; i < 8; i++ ) - { - vec3_t v; - vec3_t vecTo; - float distance; - - if ( i & 1 ) - { - v[0] = tr.viewParms.visBounds[0][0]; - } - else - { - v[0] = tr.viewParms.visBounds[1][0]; - } - - if ( i & 2 ) - { - v[1] = tr.viewParms.visBounds[0][1]; - } - else - { - v[1] = tr.viewParms.visBounds[1][1]; - } - - if ( i & 4 ) - { - v[2] = tr.viewParms.visBounds[0][2]; - } - else - { - v[2] = tr.viewParms.visBounds[1][2]; - } - - VectorSubtract( v, tr.viewParms.or.origin, vecTo ); - - distance = vecTo[0] * vecTo[0] + vecTo[1] * vecTo[1] + vecTo[2] * vecTo[2]; - - if ( distance > farthestCornerDistance ) - { - farthestCornerDistance = distance; - } - } - tr.viewParms.zFar = sqrt( farthestCornerDistance ); -} - -/* -================= -R_SetupFrustum - -Set up the culling frustum planes for the current view using the results we got from computing the first two rows of -the projection matrix. -================= -*/ -void R_SetupFrustum (viewParms_t *dest, float xmin, float xmax, float ymax, float zProj, float stereoSep) -{ - vec3_t ofsorigin; - float oppleg, adjleg, length; - int i; - - if(stereoSep == 0 && xmin == -xmax) - { - // symmetric case can be simplified - VectorCopy(dest->or.origin, ofsorigin); - - length = sqrt(xmax * xmax + zProj * zProj); - oppleg = xmax / length; - adjleg = zProj / length; - - VectorScale(dest->or.axis[0], oppleg, dest->frustum[0].normal); - VectorMA(dest->frustum[0].normal, adjleg, dest->or.axis[1], dest->frustum[0].normal); - - VectorScale(dest->or.axis[0], oppleg, dest->frustum[1].normal); - VectorMA(dest->frustum[1].normal, -adjleg, dest->or.axis[1], dest->frustum[1].normal); - } - else - { - // In stereo rendering, due to the modification of the projection matrix, dest->or.origin is not the - // actual origin that we're rendering so offset the tip of the view pyramid. - VectorMA(dest->or.origin, stereoSep, dest->or.axis[1], ofsorigin); - - oppleg = xmax + stereoSep; - length = sqrt(oppleg * oppleg + zProj * zProj); - VectorScale(dest->or.axis[0], oppleg / length, dest->frustum[0].normal); - VectorMA(dest->frustum[0].normal, zProj / length, dest->or.axis[1], dest->frustum[0].normal); - - oppleg = xmin + stereoSep; - length = sqrt(oppleg * oppleg + zProj * zProj); - VectorScale(dest->or.axis[0], -oppleg / length, dest->frustum[1].normal); - VectorMA(dest->frustum[1].normal, -zProj / length, dest->or.axis[1], dest->frustum[1].normal); - } - - length = sqrt(ymax * ymax + zProj * zProj); - oppleg = ymax / length; - adjleg = zProj / length; - - VectorScale(dest->or.axis[0], oppleg, dest->frustum[2].normal); - VectorMA(dest->frustum[2].normal, adjleg, dest->or.axis[2], dest->frustum[2].normal); - - VectorScale(dest->or.axis[0], oppleg, dest->frustum[3].normal); - VectorMA(dest->frustum[3].normal, -adjleg, dest->or.axis[2], dest->frustum[3].normal); - - for (i=0 ; i<4 ; i++) { - dest->frustum[i].type = PLANE_NON_AXIAL; - dest->frustum[i].dist = DotProduct (ofsorigin, dest->frustum[i].normal); - SetPlaneSignbits( &dest->frustum[i] ); - } -} - -/* -=============== -R_SetupProjection -=============== -*/ -void R_SetupProjection(viewParms_t *dest, float zProj, qboolean computeFrustum) -{ - float xmin, xmax, ymin, ymax; - float width, height, stereoSep = r_stereoSeparation->value; - - /* - * offset the view origin of the viewer for stereo rendering - * by setting the projection matrix appropriately. - */ - - if(stereoSep != 0) - { - if(dest->stereoFrame == STEREO_LEFT) - stereoSep = zProj / stereoSep; - else if(dest->stereoFrame == STEREO_RIGHT) - stereoSep = zProj / -stereoSep; - else - stereoSep = 0; - } - - ymax = zProj * tan(dest->fovY * M_PI / 360.0f); - ymin = -ymax; - - xmax = zProj * tan(dest->fovX * M_PI / 360.0f); - xmin = -xmax; - - width = xmax - xmin; - height = ymax - ymin; - - dest->projectionMatrix[0] = 2 * zProj / width; - dest->projectionMatrix[4] = 0; - dest->projectionMatrix[8] = (xmax + xmin + 2 * stereoSep) / width; - dest->projectionMatrix[12] = 2 * zProj * stereoSep / width; - - dest->projectionMatrix[1] = 0; - dest->projectionMatrix[5] = 2 * zProj / height; - dest->projectionMatrix[9] = ( ymax + ymin ) / height; // normally 0 - dest->projectionMatrix[13] = 0; - - dest->projectionMatrix[3] = 0; - dest->projectionMatrix[7] = 0; - dest->projectionMatrix[11] = -1; - dest->projectionMatrix[15] = 0; - - // Now that we have all the data for the projection matrix we can also setup the view frustum. - if(computeFrustum) - R_SetupFrustum(dest, xmin, xmax, ymax, zProj, stereoSep); -} - -/* -=============== -R_SetupProjectionZ - -Sets the z-component transformation part in the projection matrix -=============== -*/ -void R_SetupProjectionZ(viewParms_t *dest) -{ - float zNear, zFar, depth; - - zNear = r_znear->value; - zFar = dest->zFar; - depth = zFar - zNear; - - dest->projectionMatrix[2] = 0; - dest->projectionMatrix[6] = 0; - dest->projectionMatrix[10] = -( zFar + zNear ) / depth; - dest->projectionMatrix[14] = -2 * zFar * zNear / depth; -} - -/* -================= -R_MirrorPoint -================= -*/ -void R_MirrorPoint (vec3_t in, orientation_t *surface, orientation_t *camera, vec3_t out) { - int i; - vec3_t local; - vec3_t transformed; - float d; - - VectorSubtract( in, surface->origin, local ); - - VectorClear( transformed ); - for ( i = 0 ; i < 3 ; i++ ) { - d = DotProduct(local, surface->axis[i]); - VectorMA( transformed, d, camera->axis[i], transformed ); - } - - VectorAdd( transformed, camera->origin, out ); -} - -void R_MirrorVector (vec3_t in, orientation_t *surface, orientation_t *camera, vec3_t out) { - int i; - float d; - - VectorClear( out ); - for ( i = 0 ; i < 3 ; i++ ) { - d = DotProduct(in, surface->axis[i]); - VectorMA( out, d, camera->axis[i], out ); - } -} - - -/* -============= -R_PlaneForSurface -============= -*/ -void R_PlaneForSurface (surfaceType_t *surfType, cplane_t *plane) { - srfTriangles_t *tri; - srfPoly_t *poly; - drawVert_t *v1, *v2, *v3; - vec4_t plane4; - - if (!surfType) { - Com_Memset (plane, 0, sizeof(*plane)); - plane->normal[0] = 1; - return; - } - switch (*surfType) { - case SF_FACE: - *plane = ((srfSurfaceFace_t *)surfType)->plane; - return; - case SF_TRIANGLES: - tri = (srfTriangles_t *)surfType; - v1 = tri->verts + tri->indexes[0]; - v2 = tri->verts + tri->indexes[1]; - v3 = tri->verts + tri->indexes[2]; - PlaneFromPoints( plane4, v1->xyz, v2->xyz, v3->xyz ); - VectorCopy( plane4, plane->normal ); - plane->dist = plane4[3]; - return; - case SF_POLY: - poly = (srfPoly_t *)surfType; - PlaneFromPoints( plane4, poly->verts[0].xyz, poly->verts[1].xyz, poly->verts[2].xyz ); - VectorCopy( plane4, plane->normal ); - plane->dist = plane4[3]; - return; - default: - Com_Memset (plane, 0, sizeof(*plane)); - plane->normal[0] = 1; - return; - } -} - -/* -================= -R_GetPortalOrientation - -entityNum is the entity that the portal surface is a part of, which may -be moving and rotating. - -Returns qtrue if it should be mirrored -================= -*/ -qboolean R_GetPortalOrientations( drawSurf_t *drawSurf, int entityNum, - orientation_t *surface, orientation_t *camera, - vec3_t pvsOrigin, qboolean *mirror ) { - int i; - cplane_t originalPlane, plane; - trRefEntity_t *e; - float d; - vec3_t transformed; - - // create plane axis for the portal we are seeing - R_PlaneForSurface( drawSurf->surface, &originalPlane ); - - // rotate the plane if necessary - if ( entityNum != REFENTITYNUM_WORLD ) { - tr.currentEntityNum = entityNum; - tr.currentEntity = &tr.refdef.entities[entityNum]; - - // get the orientation of the entity - R_RotateForEntity( tr.currentEntity, &tr.viewParms, &tr.or ); - - // rotate the plane, but keep the non-rotated version for matching - // against the portalSurface entities - R_LocalNormalToWorld( originalPlane.normal, plane.normal ); - plane.dist = originalPlane.dist + DotProduct( plane.normal, tr.or.origin ); - - // translate the original plane - originalPlane.dist = originalPlane.dist + DotProduct( originalPlane.normal, tr.or.origin ); - } else { - plane = originalPlane; - } - - VectorCopy( plane.normal, surface->axis[0] ); - PerpendicularVector( surface->axis[1], surface->axis[0] ); - CrossProduct( surface->axis[0], surface->axis[1], surface->axis[2] ); - - // locate the portal entity closest to this plane. - // origin will be the origin of the portal, origin2 will be - // the origin of the camera - for ( i = 0 ; i < tr.refdef.num_entities ; i++ ) { - e = &tr.refdef.entities[i]; - if ( e->e.reType != RT_PORTALSURFACE ) { - continue; - } - - d = DotProduct( e->e.origin, originalPlane.normal ) - originalPlane.dist; - if ( d > 64 || d < -64) { - continue; - } - - // get the pvsOrigin from the entity - VectorCopy( e->e.oldorigin, pvsOrigin ); - - // if the entity is just a mirror, don't use as a camera point - if ( e->e.oldorigin[0] == e->e.origin[0] && - e->e.oldorigin[1] == e->e.origin[1] && - e->e.oldorigin[2] == e->e.origin[2] ) { - VectorScale( plane.normal, plane.dist, surface->origin ); - VectorCopy( surface->origin, camera->origin ); - VectorSubtract( vec3_origin, surface->axis[0], camera->axis[0] ); - VectorCopy( surface->axis[1], camera->axis[1] ); - VectorCopy( surface->axis[2], camera->axis[2] ); - - *mirror = qtrue; - return qtrue; - } - - // project the origin onto the surface plane to get - // an origin point we can rotate around - d = DotProduct( e->e.origin, plane.normal ) - plane.dist; - VectorMA( e->e.origin, -d, surface->axis[0], surface->origin ); - - // now get the camera origin and orientation - VectorCopy( e->e.oldorigin, camera->origin ); - AxisCopy( e->e.axis, camera->axis ); - VectorSubtract( vec3_origin, camera->axis[0], camera->axis[0] ); - VectorSubtract( vec3_origin, camera->axis[1], camera->axis[1] ); - - // optionally rotate - if ( e->e.oldframe ) { - // if a speed is specified - if ( e->e.frame ) { - // continuous rotate - d = (tr.refdef.time/1000.0f) * e->e.frame; - VectorCopy( camera->axis[1], transformed ); - RotatePointAroundVector( camera->axis[1], camera->axis[0], transformed, d ); - CrossProduct( camera->axis[0], camera->axis[1], camera->axis[2] ); - } else { - // bobbing rotate, with skinNum being the rotation offset - d = sin( tr.refdef.time * 0.003f ); - d = e->e.skinNum + d * 4; - VectorCopy( camera->axis[1], transformed ); - RotatePointAroundVector( camera->axis[1], camera->axis[0], transformed, d ); - CrossProduct( camera->axis[0], camera->axis[1], camera->axis[2] ); - } - } - else if ( e->e.skinNum ) { - d = e->e.skinNum; - VectorCopy( camera->axis[1], transformed ); - RotatePointAroundVector( camera->axis[1], camera->axis[0], transformed, d ); - CrossProduct( camera->axis[0], camera->axis[1], camera->axis[2] ); - } - *mirror = qfalse; - return qtrue; - } - - // if we didn't locate a portal entity, don't render anything. - // We don't want to just treat it as a mirror, because without a - // portal entity the server won't have communicated a proper entity set - // in the snapshot - - // unfortunately, with local movement prediction it is easily possible - // to see a surface before the server has communicated the matching - // portal surface entity, so we don't want to print anything here... - - //ri.Printf( PRINT_ALL, "Portal surface without a portal entity\n" ); - - return qfalse; -} - -static qboolean IsMirror( const drawSurf_t *drawSurf, int entityNum ) -{ - int i; - cplane_t originalPlane, plane; - trRefEntity_t *e; - float d; - - // create plane axis for the portal we are seeing - R_PlaneForSurface( drawSurf->surface, &originalPlane ); - - // rotate the plane if necessary - if ( entityNum != REFENTITYNUM_WORLD ) - { - tr.currentEntityNum = entityNum; - tr.currentEntity = &tr.refdef.entities[entityNum]; - - // get the orientation of the entity - R_RotateForEntity( tr.currentEntity, &tr.viewParms, &tr.or ); - - // rotate the plane, but keep the non-rotated version for matching - // against the portalSurface entities - R_LocalNormalToWorld( originalPlane.normal, plane.normal ); - plane.dist = originalPlane.dist + DotProduct( plane.normal, tr.or.origin ); - - // translate the original plane - originalPlane.dist = originalPlane.dist + DotProduct( originalPlane.normal, tr.or.origin ); - } - else - { - plane = originalPlane; - } - - // locate the portal entity closest to this plane. - // origin will be the origin of the portal, origin2 will be - // the origin of the camera - for ( i = 0 ; i < tr.refdef.num_entities ; i++ ) - { - e = &tr.refdef.entities[i]; - if ( e->e.reType != RT_PORTALSURFACE ) { - continue; - } - - d = DotProduct( e->e.origin, originalPlane.normal ) - originalPlane.dist; - if ( d > 64 || d < -64) { - continue; - } - - // if the entity is just a mirror, don't use as a camera point - if ( e->e.oldorigin[0] == e->e.origin[0] && - e->e.oldorigin[1] == e->e.origin[1] && - e->e.oldorigin[2] == e->e.origin[2] ) - { - return qtrue; - } - - return qfalse; - } - return qfalse; -} - -/* -** SurfIsOffscreen -** -** Determines if a surface is completely offscreen. -*/ -static qboolean SurfIsOffscreen( const drawSurf_t *drawSurf, vec4_t clipDest[128] ) { - float shortest = 100000000; - int entityNum; - int numTriangles; - shader_t *shader; - int fogNum; - int dlighted; - vec4_t clip, eye; - int i; - unsigned int pointOr = 0; - unsigned int pointAnd = (unsigned int)~0; - - R_RotateForViewer(); - - R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted ); - RB_BeginSurface( shader, fogNum ); - rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface ); - - assert( tess.numVertexes < 128 ); - - for ( i = 0; i < tess.numVertexes; i++ ) - { - int j; - unsigned int pointFlags = 0; - - R_TransformModelToClip( tess.xyz[i], tr.or.modelMatrix, tr.viewParms.projectionMatrix, eye, clip ); - - for ( j = 0; j < 3; j++ ) - { - if ( clip[j] >= clip[3] ) - { - pointFlags |= (1 << (j*2)); - } - else if ( clip[j] <= -clip[3] ) - { - pointFlags |= ( 1 << (j*2+1)); - } - } - pointAnd &= pointFlags; - pointOr |= pointFlags; - } - - // trivially reject - if ( pointAnd ) - { - return qtrue; - } - - // determine if this surface is backfaced and also determine the distance - // to the nearest vertex so we can cull based on portal range. Culling - // based on vertex distance isn't 100% correct (we should be checking for - // range to the surface), but it's good enough for the types of portals - // we have in the game right now. - numTriangles = tess.numIndexes / 3; - - for ( i = 0; i < tess.numIndexes; i += 3 ) - { - vec3_t normal; - float len; - - VectorSubtract( tess.xyz[tess.indexes[i]], tr.viewParms.or.origin, normal ); - - len = VectorLengthSquared( normal ); // lose the sqrt - if ( len < shortest ) - { - shortest = len; - } - - if ( DotProduct( normal, tess.normal[tess.indexes[i]] ) >= 0 ) - { - numTriangles--; - } - } - if ( !numTriangles ) - { - return qtrue; - } - - // mirrors can early out at this point, since we don't do a fade over distance - // with them (although we could) - if ( IsMirror( drawSurf, entityNum ) ) - { - return qfalse; - } - - if ( shortest > (tess.shader->portalRange*tess.shader->portalRange) ) - { - return qtrue; - } - - return qfalse; -} - -/* -======================== -R_MirrorViewBySurface - -Returns qtrue if another view has been rendered -======================== -*/ -qboolean R_MirrorViewBySurface (drawSurf_t *drawSurf, int entityNum) { - vec4_t clipDest[128]; - viewParms_t newParms; - viewParms_t oldParms; - orientation_t surface, camera; - - // don't recursively mirror - if (tr.viewParms.isPortal) { - ri.Printf( PRINT_DEVELOPER, "WARNING: recursive mirror/portal found\n" ); - return qfalse; - } - - if ( r_noportals->integer || (r_fastsky->integer == 1) ) { - return qfalse; - } - - // trivially reject portal/mirror - if ( SurfIsOffscreen( drawSurf, clipDest ) ) { - return qfalse; - } - - // save old viewParms so we can return to it after the mirror view - oldParms = tr.viewParms; - - newParms = tr.viewParms; - newParms.isPortal = qtrue; - if ( !R_GetPortalOrientations( drawSurf, entityNum, &surface, &camera, - newParms.pvsOrigin, &newParms.isMirror ) ) { - return qfalse; // bad portal, no portalentity - } - - R_MirrorPoint (oldParms.or.origin, &surface, &camera, newParms.or.origin ); - - VectorSubtract( vec3_origin, camera.axis[0], newParms.portalPlane.normal ); - newParms.portalPlane.dist = DotProduct( camera.origin, newParms.portalPlane.normal ); - - R_MirrorVector (oldParms.or.axis[0], &surface, &camera, newParms.or.axis[0]); - R_MirrorVector (oldParms.or.axis[1], &surface, &camera, newParms.or.axis[1]); - R_MirrorVector (oldParms.or.axis[2], &surface, &camera, newParms.or.axis[2]); - - // OPTIMIZE: restrict the viewport on the mirrored view - - // render the mirror view - R_RenderView (&newParms); - - tr.viewParms = oldParms; - - return qtrue; -} - -/* -================= -R_SpriteFogNum - -See if a sprite is inside a fog volume -================= -*/ -int R_SpriteFogNum( trRefEntity_t *ent ) { - int i, j; - fog_t *fog; - - if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { - return 0; - } - - for ( i = 1 ; i < tr.world->numfogs ; i++ ) { - fog = &tr.world->fogs[i]; - for ( j = 0 ; j < 3 ; j++ ) { - if ( ent->e.origin[j] - ent->e.radius >= fog->bounds[1][j] ) { - break; - } - if ( ent->e.origin[j] + ent->e.radius <= fog->bounds[0][j] ) { - break; - } - } - if ( j == 3 ) { - return i; - } - } - - return 0; -} - -/* -========================================================================================== - -DRAWSURF SORTING - -========================================================================================== -*/ - -/* -=============== -R_Radix -=============== -*/ -static ID_INLINE void R_Radix( int byte, int size, drawSurf_t *source, drawSurf_t *dest ) -{ - int count[ 256 ] = { 0 }; - int index[ 256 ]; - int i; - unsigned char *sortKey = NULL; - unsigned char *end = NULL; - - sortKey = ( (unsigned char *)&source[ 0 ].sort ) + byte; - end = sortKey + ( size * sizeof( drawSurf_t ) ); - for( ; sortKey < end; sortKey += sizeof( drawSurf_t ) ) - ++count[ *sortKey ]; - - index[ 0 ] = 0; - - for( i = 1; i < 256; ++i ) - index[ i ] = index[ i - 1 ] + count[ i - 1 ]; - - sortKey = ( (unsigned char *)&source[ 0 ].sort ) + byte; - for( i = 0; i < size; ++i, sortKey += sizeof( drawSurf_t ) ) - dest[ index[ *sortKey ]++ ] = source[ i ]; -} - -/* -=============== -R_RadixSort - -Radix sort with 4 byte size buckets -=============== -*/ -static void R_RadixSort( drawSurf_t *source, int size ) -{ - static drawSurf_t scratch[ MAX_DRAWSURFS ]; -#ifdef Q3_LITTLE_ENDIAN - R_Radix( 0, size, source, scratch ); - R_Radix( 1, size, scratch, source ); - R_Radix( 2, size, source, scratch ); - R_Radix( 3, size, scratch, source ); -#else - R_Radix( 3, size, source, scratch ); - R_Radix( 2, size, scratch, source ); - R_Radix( 1, size, source, scratch ); - R_Radix( 0, size, scratch, source ); -#endif //Q3_LITTLE_ENDIAN -} - -//========================================================================================== - -/* -================= -R_AddDrawSurf -================= -*/ -void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, - int fogIndex, int dlightMap ) { - int index; - - // instead of checking for overflow, we just mask the index - // so it wraps around - index = tr.refdef.numDrawSurfs & DRAWSURF_MASK; - // the sort data is packed into a single 32 bit value so it can be - // compared quickly during the qsorting process - tr.refdef.drawSurfs[index].sort = (shader->sortedIndex << QSORT_SHADERNUM_SHIFT) - | tr.shiftedEntityNum | ( fogIndex << QSORT_FOGNUM_SHIFT ) | (int)dlightMap; - tr.refdef.drawSurfs[index].surface = surface; - tr.refdef.numDrawSurfs++; -} - -/* -================= -R_DecomposeSort -================= -*/ -void R_DecomposeSort( unsigned sort, int *entityNum, shader_t **shader, - int *fogNum, int *dlightMap ) { - *fogNum = ( sort >> QSORT_FOGNUM_SHIFT ) & 31; - *shader = tr.sortedShaders[ ( sort >> QSORT_SHADERNUM_SHIFT ) & (MAX_SHADERS-1) ]; - *entityNum = ( sort >> QSORT_REFENTITYNUM_SHIFT ) & REFENTITYNUM_MASK; - *dlightMap = sort & 3; -} - -/* -================= -R_SortDrawSurfs -================= -*/ -void R_SortDrawSurfs( drawSurf_t *drawSurfs, int numDrawSurfs ) { - shader_t *shader; - int fogNum; - int entityNum; - int dlighted; - int i; - - // it is possible for some views to not have any surfaces - if ( numDrawSurfs < 1 ) { - // we still need to add it for hyperspace cases - R_AddDrawSurfCmd( drawSurfs, numDrawSurfs ); - return; - } - - // if we overflowed MAX_DRAWSURFS, the drawsurfs - // wrapped around in the buffer and we will be missing - // the first surfaces, not the last ones - if ( numDrawSurfs > MAX_DRAWSURFS ) { - numDrawSurfs = MAX_DRAWSURFS; - } - - // sort the drawsurfs by sort type, then orientation, then shader - R_RadixSort( drawSurfs, numDrawSurfs ); - - // check for any pass through drawing, which - // may cause another view to be rendered first - for ( i = 0 ; i < numDrawSurfs ; i++ ) { - R_DecomposeSort( (drawSurfs+i)->sort, &entityNum, &shader, &fogNum, &dlighted ); - - if ( shader->sort > SS_PORTAL ) { - break; - } - - // no shader should ever have this sort type - if ( shader->sort == SS_BAD ) { - ri.Error (ERR_DROP, "Shader '%s'with sort == SS_BAD", shader->name ); - } - - // if the mirror was completely clipped away, we may need to check another surface - if ( R_MirrorViewBySurface( (drawSurfs+i), entityNum) ) { - // this is a debug option to see exactly what is being mirrored - if ( r_portalOnly->integer ) { - return; - } - break; // only one mirror view at a time - } - } - - R_AddDrawSurfCmd( drawSurfs, numDrawSurfs ); -} - -/* -============= -R_AddEntitySurfaces -============= -*/ -void R_AddEntitySurfaces (void) { - trRefEntity_t *ent; - shader_t *shader; - - if ( !r_drawentities->integer ) { - return; - } - - for ( tr.currentEntityNum = 0; - tr.currentEntityNum < tr.refdef.num_entities; - tr.currentEntityNum++ ) { - ent = tr.currentEntity = &tr.refdef.entities[tr.currentEntityNum]; - - ent->needDlights = qfalse; - - // preshift the value we are going to OR into the drawsurf sort - tr.shiftedEntityNum = tr.currentEntityNum << QSORT_REFENTITYNUM_SHIFT; - - // - // the weapon model must be handled special -- - // we don't want the hacked weapon position showing in - // mirrors, because the true body position will already be drawn - // - if ( (ent->e.renderfx & RF_FIRST_PERSON) && tr.viewParms.isPortal) { - continue; - } - - // simple generated models, like sprites and beams, are not culled - switch ( ent->e.reType ) { - case RT_PORTALSURFACE: - break; // don't draw anything - case RT_SPRITE: - case RT_BEAM: - case RT_LIGHTNING: - case RT_RAIL_CORE: - case RT_RAIL_RINGS: - // self blood sprites, talk balloons, etc should not be drawn in the primary - // view. We can't just do this check for all entities, because md3 - // entities may still want to cast shadows from them - if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal) { - continue; - } - shader = R_GetShaderByHandle( ent->e.customShader ); - R_AddDrawSurf( &entitySurface, shader, R_SpriteFogNum( ent ), 0 ); - break; - - case RT_MODEL: - // we must set up parts of tr.or for model culling - R_RotateForEntity( ent, &tr.viewParms, &tr.or ); - - tr.currentModel = R_GetModelByHandle( ent->e.hModel ); - if (!tr.currentModel) { - R_AddDrawSurf( &entitySurface, tr.defaultShader, 0, 0 ); - } else { - switch ( tr.currentModel->type ) { - case MOD_MESH: - R_AddMD3Surfaces( ent ); - break; - case MOD_MD4: - R_AddAnimSurfaces( ent ); - break; -#ifdef RAVENMD4 - case MOD_MDR: - R_MDRAddAnimSurfaces( ent ); - break; -#endif - case MOD_IQM: - R_AddIQMSurfaces( ent ); - break; - case MOD_BRUSH: - R_AddBrushModelSurfaces( ent ); - break; - case MOD_BAD: // null model axis - if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal) { - break; - } - R_AddDrawSurf( &entitySurface, tr.defaultShader, 0, 0 ); - break; - default: - ri.Error( ERR_DROP, "R_AddEntitySurfaces: Bad modeltype" ); - break; - } - } - break; - default: - ri.Error( ERR_DROP, "R_AddEntitySurfaces: Bad reType" ); - } - } - -} - - -/* -==================== -R_GenerateDrawSurfs -==================== -*/ -void R_GenerateDrawSurfs( void ) { - R_AddWorldSurfaces (); - - R_AddPolygonSurfaces(); - - // set the projection matrix with the minimum zfar - // now that we have the world bounded - // this needs to be done before entities are - // added, because they use the projection - // matrix for lod calculation - - // dynamically compute far clip plane distance - R_SetFarClip(); - - // we know the size of the clipping volume. Now set the rest of the projection matrix. - R_SetupProjectionZ (&tr.viewParms); - - R_AddEntitySurfaces (); -} - -/* -================ -R_DebugPolygon -================ -*/ -void R_DebugPolygon( int color, int numPoints, float *points ) { - int i; - - GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); - - // draw solid shade - - qglColor3f( color&1, (color>>1)&1, (color>>2)&1 ); - qglBegin( GL_POLYGON ); - for ( i = 0 ; i < numPoints ; i++ ) { - qglVertex3fv( points + i * 3 ); - } - qglEnd(); - - // draw wireframe outline - GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); - qglDepthRange( 0, 0 ); - qglColor3f( 1, 1, 1 ); - qglBegin( GL_POLYGON ); - for ( i = 0 ; i < numPoints ; i++ ) { - qglVertex3fv( points + i * 3 ); - } - qglEnd(); - qglDepthRange( 0, 1 ); -} - -/* -==================== -R_DebugGraphics - -Visualization aid for movement clipping debugging -==================== -*/ -void R_DebugGraphics( void ) { - if ( !r_debugSurface->integer ) { - return; - } - - R_IssuePendingRenderCommands(); - - GL_Bind( tr.whiteImage); - GL_Cull( CT_FRONT_SIDED ); - ri.CM_DrawDebugSurface( R_DebugPolygon ); -} - - -/* -================ -R_RenderView - -A view may be either the actual camera view, -or a mirror / remote location -================ -*/ -void R_RenderView (viewParms_t *parms) { - int firstDrawSurf; - - if ( parms->viewportWidth <= 0 || parms->viewportHeight <= 0 ) { - return; - } - - tr.viewCount++; - - tr.viewParms = *parms; - tr.viewParms.frameSceneNum = tr.frameSceneNum; - tr.viewParms.frameCount = tr.frameCount; - - firstDrawSurf = tr.refdef.numDrawSurfs; - - tr.viewCount++; - - // set viewParms.world - R_RotateForViewer (); - - R_SetupProjection(&tr.viewParms, r_zproj->value, qtrue); - - R_GenerateDrawSurfs(); - - R_SortDrawSurfs( tr.refdef.drawSurfs + firstDrawSurf, tr.refdef.numDrawSurfs - firstDrawSurf ); - - // draw main system development information (surface outlines, etc) - R_DebugGraphics(); -} - - - diff --git a/src/renderer/tr_marks.c b/src/renderer/tr_marks.c deleted file mode 100644 index 6bcd7cb1..00000000 --- a/src/renderer/tr_marks.c +++ /dev/null @@ -1,459 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_marks.c -- polygon projection on the world polygons - -#include "tr_local.h" -//#include "assert.h" - -#define MAX_VERTS_ON_POLY 64 - -#define MARKER_OFFSET 0 // 1 - -/* -============= -R_ChopPolyBehindPlane - -Out must have space for two more vertexes than in -============= -*/ -#define SIDE_FRONT 0 -#define SIDE_BACK 1 -#define SIDE_ON 2 -static void R_ChopPolyBehindPlane( int numInPoints, vec3_t inPoints[MAX_VERTS_ON_POLY], - int *numOutPoints, vec3_t outPoints[MAX_VERTS_ON_POLY], - vec3_t normal, vec_t dist, vec_t epsilon) { - float dists[MAX_VERTS_ON_POLY+4]; - int sides[MAX_VERTS_ON_POLY+4]; - int counts[3]; - float dot; - int i, j; - float *p1, *p2, *clip; - float d; - - // don't clip if it might overflow - if ( numInPoints >= MAX_VERTS_ON_POLY - 2 ) { - *numOutPoints = 0; - return; - } - - counts[0] = counts[1] = counts[2] = 0; - - // determine sides for each point - for ( i = 0 ; i < numInPoints ; i++ ) { - dot = DotProduct( inPoints[i], normal ); - dot -= dist; - dists[i] = dot; - if ( dot > epsilon ) { - sides[i] = SIDE_FRONT; - } else if ( dot < -epsilon ) { - sides[i] = SIDE_BACK; - } else { - sides[i] = SIDE_ON; - } - counts[sides[i]]++; - } - sides[i] = sides[0]; - dists[i] = dists[0]; - - *numOutPoints = 0; - - if ( !counts[0] ) { - return; - } - if ( !counts[1] ) { - *numOutPoints = numInPoints; - Com_Memcpy( outPoints, inPoints, numInPoints * sizeof(vec3_t) ); - return; - } - - for ( i = 0 ; i < numInPoints ; i++ ) { - p1 = inPoints[i]; - clip = outPoints[ *numOutPoints ]; - - if ( sides[i] == SIDE_ON ) { - VectorCopy( p1, clip ); - (*numOutPoints)++; - continue; - } - - if ( sides[i] == SIDE_FRONT ) { - VectorCopy( p1, clip ); - (*numOutPoints)++; - clip = outPoints[ *numOutPoints ]; - } - - if ( sides[i+1] == SIDE_ON || sides[i+1] == sides[i] ) { - continue; - } - - // generate a split point - p2 = inPoints[ (i+1) % numInPoints ]; - - d = dists[i] - dists[i+1]; - if ( d == 0 ) { - dot = 0; - } else { - dot = dists[i] / d; - } - - // clip xyz - - for (j=0 ; j<3 ; j++) { - clip[j] = p1[j] + dot * ( p2[j] - p1[j] ); - } - - (*numOutPoints)++; - } -} - -/* -================= -R_BoxSurfaces_r - -================= -*/ -void R_BoxSurfaces_r(mnode_t *node, vec3_t mins, vec3_t maxs, surfaceType_t **list, int listsize, int *listlength, vec3_t dir) { - - int s, c; - msurface_t *surf, **mark; - - // do the tail recursion in a loop - while ( node->contents == -1 ) { - s = BoxOnPlaneSide( mins, maxs, node->plane ); - if (s == 1) { - node = node->children[0]; - } else if (s == 2) { - node = node->children[1]; - } else { - R_BoxSurfaces_r(node->children[0], mins, maxs, list, listsize, listlength, dir); - node = node->children[1]; - } - } - - // add the individual surfaces - mark = node->firstmarksurface; - c = node->nummarksurfaces; - while (c--) { - // - if (*listlength >= listsize) break; - // - surf = *mark; - // check if the surface has NOIMPACT or NOMARKS set - if ( ( surf->shader->surfaceFlags & ( SURF_NOIMPACT | SURF_NOMARKS ) ) - || ( surf->shader->contentFlags & CONTENTS_FOG ) ) { - surf->viewCount = tr.viewCount; - } - // extra check for surfaces to avoid list overflows - else if (*(surf->data) == SF_FACE) { - // the face plane should go through the box - s = BoxOnPlaneSide( mins, maxs, &(( srfSurfaceFace_t * ) surf->data)->plane ); - if (s == 1 || s == 2) { - surf->viewCount = tr.viewCount; - } else if (DotProduct((( srfSurfaceFace_t * ) surf->data)->plane.normal, dir) > -0.5) { - // don't add faces that make sharp angles with the projection direction - surf->viewCount = tr.viewCount; - } - } - else if (*(surfaceType_t *) (surf->data) != SF_GRID && - *(surfaceType_t *) (surf->data) != SF_TRIANGLES) - surf->viewCount = tr.viewCount; - // check the viewCount because the surface may have - // already been added if it spans multiple leafs - if (surf->viewCount != tr.viewCount) { - surf->viewCount = tr.viewCount; - list[*listlength] = (surfaceType_t *) surf->data; - (*listlength)++; - } - mark++; - } -} - -/* -================= -R_AddMarkFragments - -================= -*/ -void R_AddMarkFragments(int numClipPoints, vec3_t clipPoints[2][MAX_VERTS_ON_POLY], - int numPlanes, vec3_t *normals, float *dists, - int maxPoints, vec3_t pointBuffer, - int maxFragments, markFragment_t *fragmentBuffer, - int *returnedPoints, int *returnedFragments, - vec3_t mins, vec3_t maxs) { - int pingPong, i; - markFragment_t *mf; - - // chop the surface by all the bounding planes of the to be projected polygon - pingPong = 0; - - for ( i = 0 ; i < numPlanes ; i++ ) { - - R_ChopPolyBehindPlane( numClipPoints, clipPoints[pingPong], - &numClipPoints, clipPoints[!pingPong], - normals[i], dists[i], 0.5 ); - pingPong ^= 1; - if ( numClipPoints == 0 ) { - break; - } - } - // completely clipped away? - if ( numClipPoints == 0 ) { - return; - } - - // add this fragment to the returned list - if ( numClipPoints + (*returnedPoints) > maxPoints ) { - return; // not enough space for this polygon - } - /* - // all the clip points should be within the bounding box - for ( i = 0 ; i < numClipPoints ; i++ ) { - int j; - for ( j = 0 ; j < 3 ; j++ ) { - if (clipPoints[pingPong][i][j] < mins[j] - 0.5) break; - if (clipPoints[pingPong][i][j] > maxs[j] + 0.5) break; - } - if (j < 3) break; - } - if (i < numClipPoints) return; - */ - - mf = fragmentBuffer + (*returnedFragments); - mf->firstPoint = (*returnedPoints); - mf->numPoints = numClipPoints; - Com_Memcpy( pointBuffer + (*returnedPoints) * 3, clipPoints[pingPong], numClipPoints * sizeof(vec3_t) ); - - (*returnedPoints) += numClipPoints; - (*returnedFragments)++; -} - -/* -================= -R_MarkFragments - -================= -*/ -int R_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection, - int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ) { - int numsurfaces, numPlanes; - int i, j, k, m, n; - surfaceType_t *surfaces[64]; - vec3_t mins, maxs; - int returnedFragments; - int returnedPoints; - vec3_t normals[MAX_VERTS_ON_POLY+2]; - float dists[MAX_VERTS_ON_POLY+2]; - vec3_t clipPoints[2][MAX_VERTS_ON_POLY]; - int numClipPoints; - float *v; - srfGridMesh_t *cv; - drawVert_t *dv; - vec3_t normal; - vec3_t projectionDir; - vec3_t v1, v2; - int *indexes; - - if (numPoints <= 0) { - return 0; - } - - //increment view count for double check prevention - tr.viewCount++; - - // - VectorNormalize2( projection, projectionDir ); - // find all the brushes that are to be considered - ClearBounds( mins, maxs ); - for ( i = 0 ; i < numPoints ; i++ ) { - vec3_t temp; - - AddPointToBounds( points[i], mins, maxs ); - VectorAdd( points[i], projection, temp ); - AddPointToBounds( temp, mins, maxs ); - // make sure we get all the leafs (also the one(s) in front of the hit surface) - VectorMA( points[i], -20, projectionDir, temp ); - AddPointToBounds( temp, mins, maxs ); - } - - if (numPoints > MAX_VERTS_ON_POLY) numPoints = MAX_VERTS_ON_POLY; - // create the bounding planes for the to be projected polygon - for ( i = 0 ; i < numPoints ; i++ ) { - VectorSubtract(points[(i+1)%numPoints], points[i], v1); - VectorAdd(points[i], projection, v2); - VectorSubtract(points[i], v2, v2); - CrossProduct(v1, v2, normals[i]); - VectorNormalizeFast(normals[i]); - dists[i] = DotProduct(normals[i], points[i]); - } - // add near and far clipping planes for projection - VectorCopy(projectionDir, normals[numPoints]); - dists[numPoints] = DotProduct(normals[numPoints], points[0]) - 32; - VectorCopy(projectionDir, normals[numPoints+1]); - VectorInverse(normals[numPoints+1]); - dists[numPoints+1] = DotProduct(normals[numPoints+1], points[0]) - 20; - numPlanes = numPoints + 2; - - numsurfaces = 0; - R_BoxSurfaces_r(tr.world->nodes, mins, maxs, surfaces, 64, &numsurfaces, projectionDir); - //assert(numsurfaces <= 64); - //assert(numsurfaces != 64); - - returnedPoints = 0; - returnedFragments = 0; - - for ( i = 0 ; i < numsurfaces ; i++ ) { - - if (*surfaces[i] == SF_GRID) { - - cv = (srfGridMesh_t *) surfaces[i]; - for ( m = 0 ; m < cv->height - 1 ; m++ ) { - for ( n = 0 ; n < cv->width - 1 ; n++ ) { - // We triangulate the grid and chop all triangles within - // the bounding planes of the to be projected polygon. - // LOD is not taken into account, not such a big deal though. - // - // It's probably much nicer to chop the grid itself and deal - // with this grid as a normal SF_GRID surface so LOD will - // be applied. However the LOD of that chopped grid must - // be synced with the LOD of the original curve. - // One way to do this; the chopped grid shares vertices with - // the original curve. When LOD is applied to the original - // curve the unused vertices are flagged. Now the chopped curve - // should skip the flagged vertices. This still leaves the - // problems with the vertices at the chopped grid edges. - // - // To avoid issues when LOD applied to "hollow curves" (like - // the ones around many jump pads) we now just add a 2 unit - // offset to the triangle vertices. - // The offset is added in the vertex normal vector direction - // so all triangles will still fit together. - // The 2 unit offset should avoid pretty much all LOD problems. - - numClipPoints = 3; - - dv = cv->verts + m * cv->width + n; - - VectorCopy(dv[0].xyz, clipPoints[0][0]); - VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[0].normal, clipPoints[0][0]); - VectorCopy(dv[cv->width].xyz, clipPoints[0][1]); - VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]); - VectorCopy(dv[1].xyz, clipPoints[0][2]); - VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[1].normal, clipPoints[0][2]); - // check the normal of this triangle - VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1); - VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2); - CrossProduct(v1, v2, normal); - VectorNormalizeFast(normal); - if (DotProduct(normal, projectionDir) < -0.1) { - // add the fragments of this triangle - R_AddMarkFragments(numClipPoints, clipPoints, - numPlanes, normals, dists, - maxPoints, pointBuffer, - maxFragments, fragmentBuffer, - &returnedPoints, &returnedFragments, mins, maxs); - - if ( returnedFragments == maxFragments ) { - return returnedFragments; // not enough space for more fragments - } - } - - VectorCopy(dv[1].xyz, clipPoints[0][0]); - VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[1].normal, clipPoints[0][0]); - VectorCopy(dv[cv->width].xyz, clipPoints[0][1]); - VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]); - VectorCopy(dv[cv->width+1].xyz, clipPoints[0][2]); - VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[cv->width+1].normal, clipPoints[0][2]); - // check the normal of this triangle - VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1); - VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2); - CrossProduct(v1, v2, normal); - VectorNormalizeFast(normal); - if (DotProduct(normal, projectionDir) < -0.05) { - // add the fragments of this triangle - R_AddMarkFragments(numClipPoints, clipPoints, - numPlanes, normals, dists, - maxPoints, pointBuffer, - maxFragments, fragmentBuffer, - &returnedPoints, &returnedFragments, mins, maxs); - - if ( returnedFragments == maxFragments ) { - return returnedFragments; // not enough space for more fragments - } - } - } - } - } - else if (*surfaces[i] == SF_FACE) { - - srfSurfaceFace_t *surf = ( srfSurfaceFace_t * ) surfaces[i]; - - // check the normal of this face - if (DotProduct(surf->plane.normal, projectionDir) > -0.5) { - continue; - } - - indexes = (int *)( (byte *)surf + surf->ofsIndices ); - for ( k = 0 ; k < surf->numIndices ; k += 3 ) { - for ( j = 0 ; j < 3 ; j++ ) { - v = surf->points[0] + VERTEXSIZE * indexes[k+j];; - VectorMA( v, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j] ); - } - - // add the fragments of this face - R_AddMarkFragments( 3 , clipPoints, - numPlanes, normals, dists, - maxPoints, pointBuffer, - maxFragments, fragmentBuffer, - &returnedPoints, &returnedFragments, mins, maxs); - if ( returnedFragments == maxFragments ) { - return returnedFragments; // not enough space for more fragments - } - } - } - else if(*surfaces[i] == SF_TRIANGLES && r_marksOnTriangleMeshes->integer) { - - srfTriangles_t *surf = (srfTriangles_t *) surfaces[i]; - - for (k = 0; k < surf->numIndexes; k += 3) - { - for(j = 0; j < 3; j++) - { - v = surf->verts[surf->indexes[k + j]].xyz; - VectorMA(v, MARKER_OFFSET, surf->verts[surf->indexes[k + j]].normal, clipPoints[0][j]); - } - - // add the fragments of this face - R_AddMarkFragments(3, clipPoints, - numPlanes, normals, dists, - maxPoints, pointBuffer, - maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs); - if(returnedFragments == maxFragments) - { - return returnedFragments; // not enough space for more fragments - } - } - } - } - return returnedFragments; -} - diff --git a/src/renderer/tr_mesh.c b/src/renderer/tr_mesh.c deleted file mode 100644 index effbae01..00000000 --- a/src/renderer/tr_mesh.c +++ /dev/null @@ -1,417 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_mesh.c: triangle model functions - -#include "tr_local.h" - -static float ProjectRadius( float r, vec3_t location ) -{ - float pr; - float dist; - float c; - vec3_t p; - float projected[4]; - - c = DotProduct( tr.viewParms.or.axis[0], tr.viewParms.or.origin ); - dist = DotProduct( tr.viewParms.or.axis[0], location ) - c; - - if ( dist <= 0 ) - return 0; - - p[0] = 0; - p[1] = fabs( r ); - p[2] = -dist; - - projected[0] = p[0] * tr.viewParms.projectionMatrix[0] + - p[1] * tr.viewParms.projectionMatrix[4] + - p[2] * tr.viewParms.projectionMatrix[8] + - tr.viewParms.projectionMatrix[12]; - - projected[1] = p[0] * tr.viewParms.projectionMatrix[1] + - p[1] * tr.viewParms.projectionMatrix[5] + - p[2] * tr.viewParms.projectionMatrix[9] + - tr.viewParms.projectionMatrix[13]; - - projected[2] = p[0] * tr.viewParms.projectionMatrix[2] + - p[1] * tr.viewParms.projectionMatrix[6] + - p[2] * tr.viewParms.projectionMatrix[10] + - tr.viewParms.projectionMatrix[14]; - - projected[3] = p[0] * tr.viewParms.projectionMatrix[3] + - p[1] * tr.viewParms.projectionMatrix[7] + - p[2] * tr.viewParms.projectionMatrix[11] + - tr.viewParms.projectionMatrix[15]; - - - pr = projected[1] / projected[3]; - - if ( pr > 1.0f ) - pr = 1.0f; - - return pr; -} - -/* -============= -R_CullModel -============= -*/ -static int R_CullModel( md3Header_t *header, trRefEntity_t *ent ) { - vec3_t bounds[2]; - md3Frame_t *oldFrame, *newFrame; - int i; - - // compute frame pointers - newFrame = ( md3Frame_t * ) ( ( byte * ) header + header->ofsFrames ) + ent->e.frame; - oldFrame = ( md3Frame_t * ) ( ( byte * ) header + header->ofsFrames ) + ent->e.oldframe; - - // cull bounding sphere ONLY if this is not an upscaled entity - if ( !ent->e.nonNormalizedAxes ) - { - if ( ent->e.frame == ent->e.oldframe ) - { - switch ( R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ) ) - { - case CULL_OUT: - tr.pc.c_sphere_cull_md3_out++; - return CULL_OUT; - - case CULL_IN: - tr.pc.c_sphere_cull_md3_in++; - return CULL_IN; - - case CULL_CLIP: - tr.pc.c_sphere_cull_md3_clip++; - break; - } - } - else - { - int sphereCull, sphereCullB; - - sphereCull = R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ); - if ( newFrame == oldFrame ) { - sphereCullB = sphereCull; - } else { - sphereCullB = R_CullLocalPointAndRadius( oldFrame->localOrigin, oldFrame->radius ); - } - - if ( sphereCull == sphereCullB ) - { - if ( sphereCull == CULL_OUT ) - { - tr.pc.c_sphere_cull_md3_out++; - return CULL_OUT; - } - else if ( sphereCull == CULL_IN ) - { - tr.pc.c_sphere_cull_md3_in++; - return CULL_IN; - } - else - { - tr.pc.c_sphere_cull_md3_clip++; - } - } - } - } - - // calculate a bounding box in the current coordinate system - for (i = 0 ; i < 3 ; i++) { - bounds[0][i] = oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i]; - bounds[1][i] = oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i]; - } - - switch ( R_CullLocalBox( bounds ) ) - { - case CULL_IN: - tr.pc.c_box_cull_md3_in++; - return CULL_IN; - case CULL_CLIP: - tr.pc.c_box_cull_md3_clip++; - return CULL_CLIP; - case CULL_OUT: - default: - tr.pc.c_box_cull_md3_out++; - return CULL_OUT; - } -} - - -/* -================= -R_ComputeLOD - -================= -*/ -int R_ComputeLOD( trRefEntity_t *ent ) { - float radius; - float flod, lodscale; - float projectedRadius; - md3Frame_t *frame; -#ifdef RAVENMD4 - mdrHeader_t *mdr; - mdrFrame_t *mdrframe; -#endif - int lod; - - if ( tr.currentModel->numLods < 2 ) - { - // model has only 1 LOD level, skip computations and bias - lod = 0; - } - else - { - // multiple LODs exist, so compute projected bounding sphere - // and use that as a criteria for selecting LOD - -#ifdef RAVENMD4 - if(tr.currentModel->type == MOD_MDR) - { - int frameSize; - mdr = (mdrHeader_t *) tr.currentModel->modelData; - frameSize = (size_t) (&((mdrFrame_t *)0)->bones[mdr->numBones]); - - mdrframe = (mdrFrame_t *) ((byte *) mdr + mdr->ofsFrames + frameSize * ent->e.frame); - - radius = RadiusFromBounds(mdrframe->bounds[0], mdrframe->bounds[1]); - } - else -#endif - { - frame = ( md3Frame_t * ) ( ( ( unsigned char * ) tr.currentModel->md3[0] ) + tr.currentModel->md3[0]->ofsFrames ); - - frame += ent->e.frame; - - radius = RadiusFromBounds( frame->bounds[0], frame->bounds[1] ); - } - - if ( ( projectedRadius = ProjectRadius( radius, ent->e.origin ) ) != 0 ) - { - lodscale = r_lodscale->value; - if (lodscale > 20) lodscale = 20; - flod = 1.0f - projectedRadius * lodscale; - } - else - { - // object intersects near view plane, e.g. view weapon - flod = 0; - } - - flod *= tr.currentModel->numLods; - lod = ri.ftol(flod); - - if ( lod < 0 ) - { - lod = 0; - } - else if ( lod >= tr.currentModel->numLods ) - { - lod = tr.currentModel->numLods - 1; - } - } - - lod += r_lodbias->integer; - - if ( lod >= tr.currentModel->numLods ) - lod = tr.currentModel->numLods - 1; - if ( lod < 0 ) - lod = 0; - - return lod; -} - -/* -================= -R_ComputeFogNum - -================= -*/ -int R_ComputeFogNum( md3Header_t *header, trRefEntity_t *ent ) { - int i, j; - fog_t *fog; - md3Frame_t *md3Frame; - vec3_t localOrigin; - - if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { - return 0; - } - - // FIXME: non-normalized axis issues - md3Frame = ( md3Frame_t * ) ( ( byte * ) header + header->ofsFrames ) + ent->e.frame; - VectorAdd( ent->e.origin, md3Frame->localOrigin, localOrigin ); - for ( i = 1 ; i < tr.world->numfogs ; i++ ) { - fog = &tr.world->fogs[i]; - for ( j = 0 ; j < 3 ; j++ ) { - if ( localOrigin[j] - md3Frame->radius >= fog->bounds[1][j] ) { - break; - } - if ( localOrigin[j] + md3Frame->radius <= fog->bounds[0][j] ) { - break; - } - } - if ( j == 3 ) { - return i; - } - } - - return 0; -} - -/* -================= -R_AddMD3Surfaces - -================= -*/ -void R_AddMD3Surfaces( trRefEntity_t *ent ) { - int i; - md3Header_t *header = NULL; - md3Surface_t *surface = NULL; - md3Shader_t *md3Shader = NULL; - shader_t *shader = NULL; - int cull; - int lod; - int fogNum; - qboolean personalModel; - - // don't add third_person objects if not in a portal - personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal; - - if ( ent->e.renderfx & RF_WRAP_FRAMES ) { - ent->e.frame %= tr.currentModel->md3[0]->numFrames; - ent->e.oldframe %= tr.currentModel->md3[0]->numFrames; - } - - // - // Validate the frames so there is no chance of a crash. - // This will write directly into the entity structure, so - // when the surfaces are rendered, they don't need to be - // range checked again. - // - if ( (ent->e.frame >= tr.currentModel->md3[0]->numFrames) - || (ent->e.frame < 0) - || (ent->e.oldframe >= tr.currentModel->md3[0]->numFrames) - || (ent->e.oldframe < 0) ) { - ri.Printf( PRINT_DEVELOPER, "R_AddMD3Surfaces: no such frame %d to %d for '%s'\n", - ent->e.oldframe, ent->e.frame, - tr.currentModel->name ); - ent->e.frame = 0; - ent->e.oldframe = 0; - } - - // - // compute LOD - // - lod = R_ComputeLOD( ent ); - - header = tr.currentModel->md3[lod]; - - // - // cull the entire model if merged bounding box of both frames - // is outside the view frustum. - // - cull = R_CullModel ( header, ent ); - if ( cull == CULL_OUT ) { - return; - } - - // - // set up lighting now that we know we aren't culled - // - if ( !personalModel || r_shadows->integer > 1 ) { - R_SetupEntityLighting( &tr.refdef, ent ); - } - - // - // see if we are in a fog volume - // - fogNum = R_ComputeFogNum( header, ent ); - - // - // draw all surfaces - // - surface = (md3Surface_t *)( (byte *)header + header->ofsSurfaces ); - for ( i = 0 ; i < header->numSurfaces ; i++ ) { - - if ( ent->e.customShader ) { - shader = R_GetShaderByHandle( ent->e.customShader ); - } else if ( ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins ) { - skin_t *skin; - int j; - - skin = R_GetSkinByHandle( ent->e.customSkin ); - - // match the surface name to something in the skin file - shader = tr.defaultShader; - for ( j = 0 ; j < skin->numSurfaces ; j++ ) { - // the names have both been lowercased - if ( !strcmp( skin->surfaces[j]->name, surface->name ) ) { - shader = skin->surfaces[j]->shader; - break; - } - } - if (shader == tr.defaultShader) { - ri.Printf( PRINT_DEVELOPER, "WARNING: no shader for surface %s in skin %s\n", surface->name, skin->name); - } - else if (shader->defaultShader) { - ri.Printf( PRINT_DEVELOPER, "WARNING: shader %s in skin %s not found\n", shader->name, skin->name); - } - } else if ( surface->numShaders <= 0 ) { - shader = tr.defaultShader; - } else { - md3Shader = (md3Shader_t *) ( (byte *)surface + surface->ofsShaders ); - md3Shader += ent->e.skinNum % surface->numShaders; - shader = tr.shaders[ md3Shader->shaderIndex ]; - } - - - // we will add shadows even if the main object isn't visible in the view - - // stencil shadows can't do personal models unless I polyhedron clip - if ( !personalModel - && r_shadows->integer == 2 - && fogNum == 0 - && !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) - && shader->sort == SS_OPAQUE ) { - R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, qfalse ); - } - - // projection shadows work fine with personal models - if ( r_shadows->integer == 3 - && fogNum == 0 - && (ent->e.renderfx & RF_SHADOW_PLANE ) - && shader->sort == SS_OPAQUE ) { - R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, qfalse ); - } - - // don't add third_person objects if not viewing through a portal - if ( !personalModel ) { - R_AddDrawSurf( (void *)surface, shader, fogNum, qfalse ); - } - - surface = (md3Surface_t *)( (byte *)surface + surface->ofsEnd ); - } - -} - diff --git a/src/renderer/tr_model.c b/src/renderer/tr_model.c deleted file mode 100644 index b51c3ce1..00000000 --- a/src/renderer/tr_model.c +++ /dev/null @@ -1,1318 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_models.c -- model loading and caching - -#include "tr_local.h" - -#define LL(x) x=LittleLong(x) - -static qboolean R_LoadMD3(model_t *mod, int lod, void *buffer, const char *name ); -static qboolean R_LoadMD4(model_t *mod, void *buffer, const char *name ); -#ifdef RAVENMD4 -static qboolean R_LoadMDR(model_t *mod, void *buffer, int filesize, const char *name ); -#endif - -/* -==================== -R_RegisterMD3 -==================== -*/ -qhandle_t R_RegisterMD3(const char *name, model_t *mod) -{ - union { - unsigned *u; - void *v; - } buf; - int lod; - int ident; - qboolean loaded = qfalse; - int numLoaded; - char filename[MAX_QPATH], namebuf[MAX_QPATH+20]; - char *fext, defex[] = "md3"; - - numLoaded = 0; - - strcpy(filename, name); - - fext = strchr(filename, '.'); - if(!fext) - fext = defex; - else - { - *fext = '\0'; - fext++; - } - - for (lod = MD3_MAX_LODS - 1 ; lod >= 0 ; lod--) - { - if(lod) - Com_sprintf(namebuf, sizeof(namebuf), "%s_%d.%s", filename, lod, fext); - else - Com_sprintf(namebuf, sizeof(namebuf), "%s.%s", filename, fext); - - ri.FS_ReadFile( namebuf, &buf.v ); - if(!buf.u) - continue; - - ident = LittleLong(* (unsigned *) buf.u); - if (ident == MD4_IDENT) - loaded = R_LoadMD4(mod, buf.u, name); - else - { - if (ident == MD3_IDENT) - loaded = R_LoadMD3(mod, lod, buf.u, name); - else - ri.Printf(PRINT_WARNING,"R_RegisterMD3: unknown fileid for %s\n", name); - } - - ri.FS_FreeFile(buf.v); - - if(loaded) - { - mod->numLods++; - numLoaded++; - } - else - break; - } - - if(numLoaded) - { - // duplicate into higher lod spots that weren't - // loaded, in case the user changes r_lodbias on the fly - for(lod--; lod >= 0; lod--) - { - mod->numLods++; - mod->md3[lod] = mod->md3[lod + 1]; - } - - return mod->index; - } - -#ifdef _DEBUG - ri.Printf(PRINT_WARNING,"R_RegisterMD3: couldn't load %s\n", name); -#endif - - mod->type = MOD_BAD; - return 0; -} - -#ifdef RAVENMD4 -/* -==================== -R_RegisterMDR -==================== -*/ -qhandle_t R_RegisterMDR(const char *name, model_t *mod) -{ - union { - unsigned *u; - void *v; - } buf; - int ident; - qboolean loaded = qfalse; - int filesize; - - filesize = ri.FS_ReadFile(name, (void **) &buf.v); - if(!buf.u) - { - mod->type = MOD_BAD; - return 0; - } - - ident = LittleLong(*(unsigned *)buf.u); - if(ident == MDR_IDENT) - loaded = R_LoadMDR(mod, buf.u, filesize, name); - - ri.FS_FreeFile (buf.v); - - if(!loaded) - { - ri.Printf(PRINT_WARNING,"R_RegisterMDR: couldn't load mdr file %s\n", name); - mod->type = MOD_BAD; - return 0; - } - - return mod->index; -} -#endif - -/* -==================== -R_RegisterIQM -==================== -*/ -qhandle_t R_RegisterIQM(const char *name, model_t *mod) -{ - union { - unsigned *u; - void *v; - } buf; - qboolean loaded = qfalse; - int filesize; - - filesize = ri.FS_ReadFile(name, (void **) &buf.v); - if(!buf.u) - { - mod->type = MOD_BAD; - return 0; - } - - loaded = R_LoadIQM(mod, buf.u, filesize, name); - - ri.FS_FreeFile (buf.v); - - if(!loaded) - { - ri.Printf(PRINT_WARNING,"R_RegisterIQM: couldn't load iqm file %s\n", name); - mod->type = MOD_BAD; - return 0; - } - - return mod->index; -} - - -typedef struct -{ - char *ext; - qhandle_t (*ModelLoader)( const char *, model_t * ); -} modelExtToLoaderMap_t; - -// Note that the ordering indicates the order of preference used -// when there are multiple models of different formats available -static modelExtToLoaderMap_t modelLoaders[ ] = -{ - { "iqm", R_RegisterIQM }, -#ifdef RAVENMD4 - { "mdr", R_RegisterMDR }, -#endif - { "md4", R_RegisterMD3 }, - { "md3", R_RegisterMD3 } -}; - -static int numModelLoaders = ARRAY_LEN(modelLoaders); - -//=============================================================================== - -/* -** R_GetModelByHandle -*/ -model_t *R_GetModelByHandle( qhandle_t index ) { - model_t *mod; - - // out of range gets the defualt model - if ( index < 1 || index >= tr.numModels ) { - return tr.models[0]; - } - - mod = tr.models[index]; - - return mod; -} - -//=============================================================================== - -/* -** R_AllocModel -*/ -model_t *R_AllocModel( void ) { - model_t *mod; - - if ( tr.numModels == MAX_MOD_KNOWN ) { - return NULL; - } - - mod = ri.Hunk_Alloc( sizeof( *tr.models[tr.numModels] ), h_low ); - mod->index = tr.numModels; - tr.models[tr.numModels] = mod; - tr.numModels++; - - return mod; -} - -/* -==================== -RE_RegisterModel - -Loads in a model for the given name - -Zero will be returned if the model fails to load. -An entry will be retained for failed models as an -optimization to prevent disk rescanning if they are -asked for again. -==================== -*/ -qhandle_t RE_RegisterModel( const char *name ) { - model_t *mod; - qhandle_t hModel; - qboolean orgNameFailed = qfalse; - int orgLoader = -1; - int i; - char localName[ MAX_QPATH ]; - const char *ext; - char altName[ MAX_QPATH ]; - - if ( !name || !name[0] ) { - ri.Printf( PRINT_ALL, "RE_RegisterModel: NULL name\n" ); - return 0; - } - - if ( strlen( name ) >= MAX_QPATH ) { - ri.Printf( PRINT_ALL, "Model name exceeds MAX_QPATH\n" ); - return 0; - } - - // - // search the currently loaded models - // - for ( hModel = 1 ; hModel < tr.numModels; hModel++ ) { - mod = tr.models[hModel]; - if ( !strcmp( mod->name, name ) ) { - if( mod->type == MOD_BAD ) { - return 0; - } - return hModel; - } - } - - // allocate a new model_t - - if ( ( mod = R_AllocModel() ) == NULL ) { - ri.Printf( PRINT_WARNING, "RE_RegisterModel: R_AllocModel() failed for '%s'\n", name); - return 0; - } - - // only set the name after the model has been successfully loaded - Q_strncpyz( mod->name, name, sizeof( mod->name ) ); - - - R_IssuePendingRenderCommands(); - - mod->type = MOD_BAD; - mod->numLods = 0; - - // - // load the files - // - Q_strncpyz( localName, name, MAX_QPATH ); - - ext = COM_GetExtension( localName ); - - if( *ext ) - { - // Look for the correct loader and use it - for( i = 0; i < numModelLoaders; i++ ) - { - if( !Q_stricmp( ext, modelLoaders[ i ].ext ) ) - { - // Load - hModel = modelLoaders[ i ].ModelLoader( localName, mod ); - break; - } - } - - // A loader was found - if( i < numModelLoaders ) - { - if( !hModel ) - { - // Loader failed, most likely because the file isn't there; - // try again without the extension - orgNameFailed = qtrue; - orgLoader = i; - COM_StripExtension( name, localName, MAX_QPATH ); - } - else - { - // Something loaded - return mod->index; - } - } - } - - // Try and find a suitable match using all - // the model formats supported - for( i = 0; i < numModelLoaders; i++ ) - { - if (i == orgLoader) - continue; - - Com_sprintf( altName, sizeof (altName), "%s.%s", localName, modelLoaders[ i ].ext ); - - // Load - hModel = modelLoaders[ i ].ModelLoader( altName, mod ); - - if( hModel ) - { - if( orgNameFailed ) - { - ri.Printf( PRINT_DEVELOPER, "WARNING: %s not present, using %s instead\n", - name, altName ); - } - - break; - } - } - - return hModel; -} - -/* -================= -R_LoadMD3 -================= -*/ -static qboolean R_LoadMD3 (model_t *mod, int lod, void *buffer, const char *mod_name ) { - int i, j; - md3Header_t *pinmodel; - md3Frame_t *frame; - md3Surface_t *surf; - md3Shader_t *shader; - md3Triangle_t *tri; - md3St_t *st; - md3XyzNormal_t *xyz; - md3Tag_t *tag; - int version; - int size; - - pinmodel = (md3Header_t *)buffer; - - version = LittleLong (pinmodel->version); - if (version != MD3_VERSION) { - ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has wrong version (%i should be %i)\n", - mod_name, version, MD3_VERSION); - return qfalse; - } - - mod->type = MOD_MESH; - size = LittleLong(pinmodel->ofsEnd); - mod->dataSize += size; - mod->md3[lod] = ri.Hunk_Alloc( size, h_low ); - - Com_Memcpy (mod->md3[lod], buffer, LittleLong(pinmodel->ofsEnd) ); - - LL(mod->md3[lod]->ident); - LL(mod->md3[lod]->version); - LL(mod->md3[lod]->numFrames); - LL(mod->md3[lod]->numTags); - LL(mod->md3[lod]->numSurfaces); - LL(mod->md3[lod]->ofsFrames); - LL(mod->md3[lod]->ofsTags); - LL(mod->md3[lod]->ofsSurfaces); - LL(mod->md3[lod]->ofsEnd); - - if ( mod->md3[lod]->numFrames < 1 ) { - ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has no frames\n", mod_name ); - return qfalse; - } - - // swap all the frames - frame = (md3Frame_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsFrames ); - for ( i = 0 ; i < mod->md3[lod]->numFrames ; i++, frame++) { - frame->radius = LittleFloat( frame->radius ); - for ( j = 0 ; j < 3 ; j++ ) { - frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] ); - frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] ); - frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] ); - } - } - - // swap all the tags - tag = (md3Tag_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsTags ); - for ( i = 0 ; i < mod->md3[lod]->numTags * mod->md3[lod]->numFrames ; i++, tag++) { - for ( j = 0 ; j < 3 ; j++ ) { - tag->origin[j] = LittleFloat( tag->origin[j] ); - tag->axis[0][j] = LittleFloat( tag->axis[0][j] ); - tag->axis[1][j] = LittleFloat( tag->axis[1][j] ); - tag->axis[2][j] = LittleFloat( tag->axis[2][j] ); - } - } - - // swap all the surfaces - surf = (md3Surface_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsSurfaces ); - for ( i = 0 ; i < mod->md3[lod]->numSurfaces ; i++) { - - LL(surf->ident); - LL(surf->flags); - LL(surf->numFrames); - LL(surf->numShaders); - LL(surf->numTriangles); - LL(surf->ofsTriangles); - LL(surf->numVerts); - LL(surf->ofsShaders); - LL(surf->ofsSt); - LL(surf->ofsXyzNormals); - LL(surf->ofsEnd); - - if ( surf->numVerts > SHADER_MAX_VERTEXES ) { - ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has more than %i verts on a surface (%i).\n", - mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); - return qfalse; - } - if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) { - ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has more than %i triangles on a surface (%i).\n", - mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); - return qfalse; - } - - // change to surface identifier - surf->ident = SF_MD3; - - // lowercase the surface name so skin compares are faster - Q_strlwr( surf->name ); - - // strip off a trailing _1 or _2 - // this is a crutch for q3data being a mess - j = strlen( surf->name ); - if ( j > 2 && surf->name[j-2] == '_' ) { - surf->name[j-2] = 0; - } - - // register the shaders - shader = (md3Shader_t *) ( (byte *)surf + surf->ofsShaders ); - for ( j = 0 ; j < surf->numShaders ; j++, shader++ ) { - shader_t *sh; - - sh = R_FindShader( shader->name, LIGHTMAP_NONE, qtrue ); - if ( sh->defaultShader ) { - shader->shaderIndex = 0; - } else { - shader->shaderIndex = sh->index; - } - } - - // swap all the triangles - tri = (md3Triangle_t *) ( (byte *)surf + surf->ofsTriangles ); - for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) { - LL(tri->indexes[0]); - LL(tri->indexes[1]); - LL(tri->indexes[2]); - } - - // swap all the ST - st = (md3St_t *) ( (byte *)surf + surf->ofsSt ); - for ( j = 0 ; j < surf->numVerts ; j++, st++ ) { - st->st[0] = LittleFloat( st->st[0] ); - st->st[1] = LittleFloat( st->st[1] ); - } - - // swap all the XyzNormals - xyz = (md3XyzNormal_t *) ( (byte *)surf + surf->ofsXyzNormals ); - for ( j = 0 ; j < surf->numVerts * surf->numFrames ; j++, xyz++ ) - { - xyz->xyz[0] = LittleShort( xyz->xyz[0] ); - xyz->xyz[1] = LittleShort( xyz->xyz[1] ); - xyz->xyz[2] = LittleShort( xyz->xyz[2] ); - - xyz->normal = LittleShort( xyz->normal ); - } - - - // find the next surface - surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd ); - } - - return qtrue; -} - - -#ifdef RAVENMD4 - -/* -================= -R_LoadMDR -================= -*/ -static qboolean R_LoadMDR( model_t *mod, void *buffer, int filesize, const char *mod_name ) -{ - int i, j, k, l; - mdrHeader_t *pinmodel, *mdr; - mdrFrame_t *frame; - mdrLOD_t *lod, *curlod; - mdrSurface_t *surf, *cursurf; - mdrTriangle_t *tri, *curtri; - mdrVertex_t *v, *curv; - mdrWeight_t *weight, *curweight; - mdrTag_t *tag, *curtag; - int size; - shader_t *sh; - - pinmodel = (mdrHeader_t *)buffer; - - pinmodel->version = LittleLong(pinmodel->version); - if (pinmodel->version != MDR_VERSION) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has wrong version (%i should be %i)\n", mod_name, pinmodel->version, MDR_VERSION); - return qfalse; - } - - size = LittleLong(pinmodel->ofsEnd); - - if(size > filesize) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: Header of %s is broken. Wrong filesize declared!\n", mod_name); - return qfalse; - } - - mod->type = MOD_MDR; - - LL(pinmodel->numFrames); - LL(pinmodel->numBones); - LL(pinmodel->ofsFrames); - - // This is a model that uses some type of compressed Bones. We don't want to uncompress every bone for each rendered frame - // over and over again, we'll uncompress it in this function already, so we must adjust the size of the target md4. - if(pinmodel->ofsFrames < 0) - { - // mdrFrame_t is larger than mdrCompFrame_t: - size += pinmodel->numFrames * sizeof(frame->name); - // now add enough space for the uncompressed bones. - size += pinmodel->numFrames * pinmodel->numBones * ((sizeof(mdrBone_t) - sizeof(mdrCompBone_t))); - } - - // simple bounds check - if(pinmodel->numBones < 0 || - sizeof(*mdr) + pinmodel->numFrames * (sizeof(*frame) + (pinmodel->numBones - 1) * sizeof(*frame->bones)) > size) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); - return qfalse; - } - - mod->dataSize += size; - mod->modelData = mdr = ri.Hunk_Alloc( size, h_low ); - - // Copy all the values over from the file and fix endian issues in the process, if necessary. - - mdr->ident = LittleLong(pinmodel->ident); - mdr->version = pinmodel->version; // Don't need to swap byte order on this one, we already did above. - Q_strncpyz(mdr->name, pinmodel->name, sizeof(mdr->name)); - mdr->numFrames = pinmodel->numFrames; - mdr->numBones = pinmodel->numBones; - mdr->numLODs = LittleLong(pinmodel->numLODs); - mdr->numTags = LittleLong(pinmodel->numTags); - // We don't care about the other offset values, we'll generate them ourselves while loading. - - mod->numLods = mdr->numLODs; - - if ( mdr->numFrames < 1 ) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has no frames\n", mod_name); - return qfalse; - } - - /* The first frame will be put into the first free space after the header */ - frame = (mdrFrame_t *)(mdr + 1); - mdr->ofsFrames = (int)((byte *) frame - (byte *) mdr); - - if (pinmodel->ofsFrames < 0) - { - mdrCompFrame_t *cframe; - - // compressed model... - cframe = (mdrCompFrame_t *)((byte *) pinmodel - pinmodel->ofsFrames); - - for(i = 0; i < mdr->numFrames; i++) - { - for(j = 0; j < 3; j++) - { - frame->bounds[0][j] = LittleFloat(cframe->bounds[0][j]); - frame->bounds[1][j] = LittleFloat(cframe->bounds[1][j]); - frame->localOrigin[j] = LittleFloat(cframe->localOrigin[j]); - } - - frame->radius = LittleFloat(cframe->radius); - frame->name[0] = '\0'; // No name supplied in the compressed version. - - for(j = 0; j < mdr->numBones; j++) - { - for(k = 0; k < (sizeof(cframe->bones[j].Comp) / 2); k++) - { - // Do swapping for the uncompressing functions. They seem to use shorts - // values only, so I assume this will work. Never tested it on other - // platforms, though. - - ((unsigned short *)(cframe->bones[j].Comp))[k] = - LittleShort( ((unsigned short *)(cframe->bones[j].Comp))[k] ); - } - - /* Now do the actual uncompressing */ - MC_UnCompress(frame->bones[j].matrix, cframe->bones[j].Comp); - } - - // Next Frame... - cframe = (mdrCompFrame_t *) &cframe->bones[j]; - frame = (mdrFrame_t *) &frame->bones[j]; - } - } - else - { - mdrFrame_t *curframe; - - // uncompressed model... - // - - curframe = (mdrFrame_t *)((byte *) pinmodel + pinmodel->ofsFrames); - - // swap all the frames - for ( i = 0 ; i < mdr->numFrames ; i++) - { - for(j = 0; j < 3; j++) - { - frame->bounds[0][j] = LittleFloat(curframe->bounds[0][j]); - frame->bounds[1][j] = LittleFloat(curframe->bounds[1][j]); - frame->localOrigin[j] = LittleFloat(curframe->localOrigin[j]); - } - - frame->radius = LittleFloat(curframe->radius); - Q_strncpyz(frame->name, curframe->name, sizeof(frame->name)); - - for (j = 0; j < (int) (mdr->numBones * sizeof(mdrBone_t) / 4); j++) - { - ((float *)frame->bones)[j] = LittleFloat( ((float *)curframe->bones)[j] ); - } - - curframe = (mdrFrame_t *) &curframe->bones[mdr->numBones]; - frame = (mdrFrame_t *) &frame->bones[mdr->numBones]; - } - } - - // frame should now point to the first free address after all frames. - lod = (mdrLOD_t *) frame; - mdr->ofsLODs = (int) ((byte *) lod - (byte *)mdr); - - curlod = (mdrLOD_t *)((byte *) pinmodel + LittleLong(pinmodel->ofsLODs)); - - // swap all the LOD's - for ( l = 0 ; l < mdr->numLODs ; l++) - { - // simple bounds check - if((byte *) (lod + 1) > (byte *) mdr + size) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); - return qfalse; - } - - lod->numSurfaces = LittleLong(curlod->numSurfaces); - - // swap all the surfaces - surf = (mdrSurface_t *) (lod + 1); - lod->ofsSurfaces = (int)((byte *) surf - (byte *) lod); - cursurf = (mdrSurface_t *) ((byte *)curlod + LittleLong(curlod->ofsSurfaces)); - - for ( i = 0 ; i < lod->numSurfaces ; i++) - { - // simple bounds check - if((byte *) (surf + 1) > (byte *) mdr + size) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); - return qfalse; - } - - // first do some copying stuff - - surf->ident = SF_MDR; - Q_strncpyz(surf->name, cursurf->name, sizeof(surf->name)); - Q_strncpyz(surf->shader, cursurf->shader, sizeof(surf->shader)); - - surf->ofsHeader = (byte *) mdr - (byte *) surf; - - surf->numVerts = LittleLong(cursurf->numVerts); - surf->numTriangles = LittleLong(cursurf->numTriangles); - // numBoneReferences and BoneReferences generally seem to be unused - - // now do the checks that may fail. - if ( surf->numVerts > SHADER_MAX_VERTEXES ) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has more than %i verts on a surface (%i).\n", - mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); - return qfalse; - } - if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has more than %i triangles on a surface (%i).\n", - mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); - return qfalse; - } - // lowercase the surface name so skin compares are faster - Q_strlwr( surf->name ); - - // register the shaders - sh = R_FindShader(surf->shader, LIGHTMAP_NONE, qtrue); - if ( sh->defaultShader ) { - surf->shaderIndex = 0; - } else { - surf->shaderIndex = sh->index; - } - - // now copy the vertexes. - v = (mdrVertex_t *) (surf + 1); - surf->ofsVerts = (int)((byte *) v - (byte *) surf); - curv = (mdrVertex_t *) ((byte *)cursurf + LittleLong(cursurf->ofsVerts)); - - for(j = 0; j < surf->numVerts; j++) - { - LL(curv->numWeights); - - // simple bounds check - if(curv->numWeights < 0 || (byte *) (v + 1) + (curv->numWeights - 1) * sizeof(*weight) > (byte *) mdr + size) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); - return qfalse; - } - - v->normal[0] = LittleFloat(curv->normal[0]); - v->normal[1] = LittleFloat(curv->normal[1]); - v->normal[2] = LittleFloat(curv->normal[2]); - - v->texCoords[0] = LittleFloat(curv->texCoords[0]); - v->texCoords[1] = LittleFloat(curv->texCoords[1]); - - v->numWeights = curv->numWeights; - weight = &v->weights[0]; - curweight = &curv->weights[0]; - - // Now copy all the weights - for(k = 0; k < v->numWeights; k++) - { - weight->boneIndex = LittleLong(curweight->boneIndex); - weight->boneWeight = LittleFloat(curweight->boneWeight); - - weight->offset[0] = LittleFloat(curweight->offset[0]); - weight->offset[1] = LittleFloat(curweight->offset[1]); - weight->offset[2] = LittleFloat(curweight->offset[2]); - - weight++; - curweight++; - } - - v = (mdrVertex_t *) weight; - curv = (mdrVertex_t *) curweight; - } - - // we know the offset to the triangles now: - tri = (mdrTriangle_t *) v; - surf->ofsTriangles = (int)((byte *) tri - (byte *) surf); - curtri = (mdrTriangle_t *)((byte *) cursurf + LittleLong(cursurf->ofsTriangles)); - - // simple bounds check - if(surf->numTriangles < 0 || (byte *) (tri + surf->numTriangles) > (byte *) mdr + size) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); - return qfalse; - } - - for(j = 0; j < surf->numTriangles; j++) - { - tri->indexes[0] = LittleLong(curtri->indexes[0]); - tri->indexes[1] = LittleLong(curtri->indexes[1]); - tri->indexes[2] = LittleLong(curtri->indexes[2]); - - tri++; - curtri++; - } - - // tri now points to the end of the surface. - surf->ofsEnd = (byte *) tri - (byte *) surf; - surf = (mdrSurface_t *) tri; - - // find the next surface. - cursurf = (mdrSurface_t *) ((byte *) cursurf + LittleLong(cursurf->ofsEnd)); - } - - // surf points to the next lod now. - lod->ofsEnd = (int)((byte *) surf - (byte *) lod); - lod = (mdrLOD_t *) surf; - - // find the next LOD. - curlod = (mdrLOD_t *)((byte *) curlod + LittleLong(curlod->ofsEnd)); - } - - // lod points to the first tag now, so update the offset too. - tag = (mdrTag_t *) lod; - mdr->ofsTags = (int)((byte *) tag - (byte *) mdr); - curtag = (mdrTag_t *) ((byte *)pinmodel + LittleLong(pinmodel->ofsTags)); - - // simple bounds check - if(mdr->numTags < 0 || (byte *) (tag + mdr->numTags) > (byte *) mdr + size) - { - ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); - return qfalse; - } - - for (i = 0 ; i < mdr->numTags ; i++) - { - tag->boneIndex = LittleLong(curtag->boneIndex); - Q_strncpyz(tag->name, curtag->name, sizeof(tag->name)); - - tag++; - curtag++; - } - - // And finally we know the real offset to the end. - mdr->ofsEnd = (int)((byte *) tag - (byte *) mdr); - - // phew! we're done. - - return qtrue; -} -#endif - -/* -================= -R_LoadMD4 -================= -*/ - -static qboolean R_LoadMD4( model_t *mod, void *buffer, const char *mod_name ) { - int i, j, k, lodindex; - md4Header_t *pinmodel, *md4; - md4Frame_t *frame; - md4LOD_t *lod; - md4Surface_t *surf; - md4Triangle_t *tri; - md4Vertex_t *v; - int version; - int size; - shader_t *sh; - int frameSize; - - pinmodel = (md4Header_t *)buffer; - - version = LittleLong (pinmodel->version); - if (version != MD4_VERSION) { - ri.Printf( PRINT_WARNING, "R_LoadMD4: %s has wrong version (%i should be %i)\n", - mod_name, version, MD4_VERSION); - return qfalse; - } - - mod->type = MOD_MD4; - size = LittleLong(pinmodel->ofsEnd); - mod->dataSize += size; - mod->modelData = md4 = ri.Hunk_Alloc( size, h_low ); - - Com_Memcpy(md4, buffer, size); - - LL(md4->ident); - LL(md4->version); - LL(md4->numFrames); - LL(md4->numBones); - LL(md4->numLODs); - LL(md4->ofsFrames); - LL(md4->ofsLODs); - md4->ofsEnd = size; - - if ( md4->numFrames < 1 ) { - ri.Printf( PRINT_WARNING, "R_LoadMD4: %s has no frames\n", mod_name ); - return qfalse; - } - - // we don't need to swap tags in the renderer, they aren't used - - // swap all the frames - frameSize = (size_t)( &((md4Frame_t *)0)->bones[ md4->numBones ] ); - for ( i = 0 ; i < md4->numFrames ; i++) { - frame = (md4Frame_t *) ( (byte *)md4 + md4->ofsFrames + i * frameSize ); - frame->radius = LittleFloat( frame->radius ); - for ( j = 0 ; j < 3 ; j++ ) { - frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] ); - frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] ); - frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] ); - } - for ( j = 0 ; j < md4->numBones * sizeof( md4Bone_t ) / 4 ; j++ ) { - ((float *)frame->bones)[j] = LittleFloat( ((float *)frame->bones)[j] ); - } - } - - // swap all the LOD's - lod = (md4LOD_t *) ( (byte *)md4 + md4->ofsLODs ); - for ( lodindex = 0 ; lodindex < md4->numLODs ; lodindex++ ) { - - // swap all the surfaces - surf = (md4Surface_t *) ( (byte *)lod + lod->ofsSurfaces ); - for ( i = 0 ; i < lod->numSurfaces ; i++) { - LL(surf->ident); - LL(surf->numTriangles); - LL(surf->ofsTriangles); - LL(surf->numVerts); - LL(surf->ofsVerts); - LL(surf->ofsEnd); - - if ( surf->numVerts > SHADER_MAX_VERTEXES ) { - ri.Printf(PRINT_WARNING, "R_LoadMD4: %s has more than %i verts on a surface (%i).\n", - mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); - return qfalse; - } - if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) { - ri.Printf(PRINT_WARNING, "R_LoadMD4: %s has more than %i triangles on a surface (%i).\n", - mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); - return qfalse; - } - - // change to surface identifier - surf->ident = SF_MD4; - - // lowercase the surface name so skin compares are faster - Q_strlwr( surf->name ); - - // register the shaders - sh = R_FindShader( surf->shader, LIGHTMAP_NONE, qtrue ); - if ( sh->defaultShader ) { - surf->shaderIndex = 0; - } else { - surf->shaderIndex = sh->index; - } - - // swap all the triangles - tri = (md4Triangle_t *) ( (byte *)surf + surf->ofsTriangles ); - for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) { - LL(tri->indexes[0]); - LL(tri->indexes[1]); - LL(tri->indexes[2]); - } - - // swap all the vertexes - // FIXME - // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left - // in for reference. - //v = (md4Vertex_t *) ( (byte *)surf + surf->ofsVerts + 12); - v = (md4Vertex_t *) ( (byte *)surf + surf->ofsVerts); - for ( j = 0 ; j < surf->numVerts ; j++ ) { - v->normal[0] = LittleFloat( v->normal[0] ); - v->normal[1] = LittleFloat( v->normal[1] ); - v->normal[2] = LittleFloat( v->normal[2] ); - - v->texCoords[0] = LittleFloat( v->texCoords[0] ); - v->texCoords[1] = LittleFloat( v->texCoords[1] ); - - v->numWeights = LittleLong( v->numWeights ); - - for ( k = 0 ; k < v->numWeights ; k++ ) { - v->weights[k].boneIndex = LittleLong( v->weights[k].boneIndex ); - v->weights[k].boneWeight = LittleFloat( v->weights[k].boneWeight ); - v->weights[k].offset[0] = LittleFloat( v->weights[k].offset[0] ); - v->weights[k].offset[1] = LittleFloat( v->weights[k].offset[1] ); - v->weights[k].offset[2] = LittleFloat( v->weights[k].offset[2] ); - } - // FIXME - // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left - // in for reference. - //v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights] + 12 ); - v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights]); - } - - // find the next surface - surf = (md4Surface_t *)( (byte *)surf + surf->ofsEnd ); - } - - // find the next LOD - lod = (md4LOD_t *)( (byte *)lod + lod->ofsEnd ); - } - - return qtrue; -} - - - -//============================================================================= - -/* -** RE_BeginRegistration -*/ -void RE_BeginRegistration( glconfig_t *glconfigOut ) { - - R_Init(); - - *glconfigOut = glConfig; - - R_IssuePendingRenderCommands(); - - tr.viewCluster = -1; // force markleafs to regenerate - R_ClearFlares(); - RE_ClearScene(); - - tr.registered = qtrue; - - // NOTE: this sucks, for some reason the first stretch pic is never drawn - // without this we'd see a white flash on a level load because the very - // first time the level shot would not be drawn -// RE_StretchPic(0, 0, 0, 0, 0, 0, 1, 1, 0); -} - -//============================================================================= - -/* -=============== -R_ModelInit -=============== -*/ -void R_ModelInit( void ) { - model_t *mod; - - // leave a space for NULL model - tr.numModels = 0; - - mod = R_AllocModel(); - mod->type = MOD_BAD; -} - - -/* -================ -R_Modellist_f -================ -*/ -void R_Modellist_f( void ) { - int i, j; - model_t *mod; - int total; - int lods; - - total = 0; - for ( i = 1 ; i < tr.numModels; i++ ) { - mod = tr.models[i]; - lods = 1; - for ( j = 1 ; j < MD3_MAX_LODS ; j++ ) { - if ( mod->md3[j] && mod->md3[j] != mod->md3[j-1] ) { - lods++; - } - } - ri.Printf( PRINT_ALL, "%8i : (%i) %s\n",mod->dataSize, lods, mod->name ); - total += mod->dataSize; - } - ri.Printf( PRINT_ALL, "%8i : Total models\n", total ); - -#if 0 // not working right with new hunk - if ( tr.world ) { - ri.Printf( PRINT_ALL, "\n%8i : %s\n", tr.world->dataSize, tr.world->name ); - } -#endif -} - - -//============================================================================= - - -/* -================ -R_GetTag -================ -*/ -static md3Tag_t *R_GetTag( md3Header_t *mod, int frame, const char *tagName ) { - md3Tag_t *tag; - int i; - - if ( frame >= mod->numFrames ) { - // it is possible to have a bad frame while changing models, so don't error - frame = mod->numFrames - 1; - } - - tag = (md3Tag_t *)((byte *)mod + mod->ofsTags) + frame * mod->numTags; - for ( i = 0 ; i < mod->numTags ; i++, tag++ ) { - if ( !strcmp( tag->name, tagName ) ) { - return tag; // found it - } - } - - return NULL; -} - -#ifdef RAVENMD4 -void R_GetAnimTag( mdrHeader_t *mod, int framenum, const char *tagName, md3Tag_t * dest) -{ - int i, j, k; - int frameSize; - mdrFrame_t *frame; - mdrTag_t *tag; - - if ( framenum >= mod->numFrames ) - { - // it is possible to have a bad frame while changing models, so don't error - framenum = mod->numFrames - 1; - } - - tag = (mdrTag_t *)((byte *)mod + mod->ofsTags); - for ( i = 0 ; i < mod->numTags ; i++, tag++ ) - { - if ( !strcmp( tag->name, tagName ) ) - { - Q_strncpyz(dest->name, tag->name, sizeof(dest->name)); - - // uncompressed model... - // - frameSize = (intptr_t)( &((mdrFrame_t *)0)->bones[ mod->numBones ] ); - frame = (mdrFrame_t *)((byte *)mod + mod->ofsFrames + framenum * frameSize ); - - for (j = 0; j < 3; j++) - { - for (k = 0; k < 3; k++) - dest->axis[j][k]=frame->bones[tag->boneIndex].matrix[k][j]; - } - - dest->origin[0]=frame->bones[tag->boneIndex].matrix[0][3]; - dest->origin[1]=frame->bones[tag->boneIndex].matrix[1][3]; - dest->origin[2]=frame->bones[tag->boneIndex].matrix[2][3]; - - return; - } - } - - AxisClear( dest->axis ); - VectorClear( dest->origin ); - strcpy(dest->name,""); -} -#endif - -/* -================ -R_LerpTag -================ -*/ -int R_LerpTag( orientation_t *tag, qhandle_t handle, int startFrame, int endFrame, - float frac, const char *tagName ) { - md3Tag_t *start, *end; -#ifdef RAVENMD4 - md3Tag_t start_space, end_space; -#endif - int i; - float frontLerp, backLerp; - model_t *model; - - model = R_GetModelByHandle( handle ); - if ( !model->md3[0] ) - { -#ifdef RAVENMD4 - if(model->type == MOD_MDR) - { - start = &start_space; - end = &end_space; - R_GetAnimTag((mdrHeader_t *) model->modelData, startFrame, tagName, start); - R_GetAnimTag((mdrHeader_t *) model->modelData, endFrame, tagName, end); - } - else -#endif - if( model->type == MOD_IQM ) { - return R_IQMLerpTag( tag, model->modelData, - startFrame, endFrame, - frac, tagName ); - } else { - - AxisClear( tag->axis ); - VectorClear( tag->origin ); - return qfalse; - - } - } - else - { - start = R_GetTag( model->md3[0], startFrame, tagName ); - end = R_GetTag( model->md3[0], endFrame, tagName ); - if ( !start || !end ) { - AxisClear( tag->axis ); - VectorClear( tag->origin ); - return qfalse; - } - } - - frontLerp = frac; - backLerp = 1.0f - frac; - - for ( i = 0 ; i < 3 ; i++ ) { - tag->origin[i] = start->origin[i] * backLerp + end->origin[i] * frontLerp; - tag->axis[0][i] = start->axis[0][i] * backLerp + end->axis[0][i] * frontLerp; - tag->axis[1][i] = start->axis[1][i] * backLerp + end->axis[1][i] * frontLerp; - tag->axis[2][i] = start->axis[2][i] * backLerp + end->axis[2][i] * frontLerp; - } - VectorNormalize( tag->axis[0] ); - VectorNormalize( tag->axis[1] ); - VectorNormalize( tag->axis[2] ); - return qtrue; -} - - -/* -==================== -R_ModelBounds -==================== -*/ -void R_ModelBounds( qhandle_t handle, vec3_t mins, vec3_t maxs ) { - model_t *model; - - model = R_GetModelByHandle( handle ); - - if(model->type == MOD_BRUSH) { - VectorCopy( model->bmodel->bounds[0], mins ); - VectorCopy( model->bmodel->bounds[1], maxs ); - - return; - } else if (model->type == MOD_MESH) { - md3Header_t *header; - md3Frame_t *frame; - - header = model->md3[0]; - frame = (md3Frame_t *) ((byte *)header + header->ofsFrames); - - VectorCopy( frame->bounds[0], mins ); - VectorCopy( frame->bounds[1], maxs ); - - return; - } else if (model->type == MOD_MD4) { - md4Header_t *header; - md4Frame_t *frame; - - header = (md4Header_t *)model->modelData; - frame = (md4Frame_t *) ((byte *)header + header->ofsFrames); - - VectorCopy( frame->bounds[0], mins ); - VectorCopy( frame->bounds[1], maxs ); - - return; -#ifdef RAVENMD4 - } else if (model->type == MOD_MDR) { - mdrHeader_t *header; - mdrFrame_t *frame; - - header = (mdrHeader_t *)model->modelData; - frame = (mdrFrame_t *) ((byte *)header + header->ofsFrames); - - VectorCopy( frame->bounds[0], mins ); - VectorCopy( frame->bounds[1], maxs ); - - return; -#endif - } else if(model->type == MOD_IQM) { - iqmData_t *iqmData; - - iqmData = model->modelData; - - if(iqmData->bounds) - { - VectorCopy(iqmData->bounds, mins); - VectorCopy(iqmData->bounds + 3, maxs); - return; - } - } - - VectorClear( mins ); - VectorClear( maxs ); -} diff --git a/src/renderer/tr_model_iqm.c b/src/renderer/tr_model_iqm.c deleted file mode 100644 index 98517d55..00000000 --- a/src/renderer/tr_model_iqm.c +++ /dev/null @@ -1,1058 +0,0 @@ -/* -=========================================================================== -Copyright (C) 2011 Thilo Schulz -Copyright (C) 2011 Matthias Bentrup - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "tr_local.h" - -#define LL(x) x=LittleLong(x) - -static qboolean IQM_CheckRange( iqmHeader_t *header, int offset, - int count,int size ) { - // return true if the range specified by offset, count and size - // doesn't fit into the file - return ( count <= 0 || - offset < 0 || - offset > header->filesize || - offset + count * size < 0 || - offset + count * size > header->filesize ); -} -// "multiply" 3x4 matrices, these are assumed to be the top 3 rows -// of a 4x4 matrix with the last row = (0 0 0 1) -static void Matrix34Multiply( float *a, float *b, float *out ) { - out[ 0] = a[0] * b[0] + a[1] * b[4] + a[ 2] * b[ 8]; - out[ 1] = a[0] * b[1] + a[1] * b[5] + a[ 2] * b[ 9]; - out[ 2] = a[0] * b[2] + a[1] * b[6] + a[ 2] * b[10]; - out[ 3] = a[0] * b[3] + a[1] * b[7] + a[ 2] * b[11] + a[ 3]; - out[ 4] = a[4] * b[0] + a[5] * b[4] + a[ 6] * b[ 8]; - out[ 5] = a[4] * b[1] + a[5] * b[5] + a[ 6] * b[ 9]; - out[ 6] = a[4] * b[2] + a[5] * b[6] + a[ 6] * b[10]; - out[ 7] = a[4] * b[3] + a[5] * b[7] + a[ 6] * b[11] + a[ 7]; - out[ 8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[ 8]; - out[ 9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[ 9]; - out[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10]; - out[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11]; -} -static void InterpolateMatrix( float *a, float *b, float lerp, float *mat ) { - float unLerp = 1.0f - lerp; - - mat[ 0] = a[ 0] * unLerp + b[ 0] * lerp; - mat[ 1] = a[ 1] * unLerp + b[ 1] * lerp; - mat[ 2] = a[ 2] * unLerp + b[ 2] * lerp; - mat[ 3] = a[ 3] * unLerp + b[ 3] * lerp; - mat[ 4] = a[ 4] * unLerp + b[ 4] * lerp; - mat[ 5] = a[ 5] * unLerp + b[ 5] * lerp; - mat[ 6] = a[ 6] * unLerp + b[ 6] * lerp; - mat[ 7] = a[ 7] * unLerp + b[ 7] * lerp; - mat[ 8] = a[ 8] * unLerp + b[ 8] * lerp; - mat[ 9] = a[ 9] * unLerp + b[ 9] * lerp; - mat[10] = a[10] * unLerp + b[10] * lerp; - mat[11] = a[11] * unLerp + b[11] * lerp; -} -static void JointToMatrix( vec4_t rot, vec3_t scale, vec3_t trans, - float *mat ) { - float xx = 2.0f * rot[0] * rot[0]; - float yy = 2.0f * rot[1] * rot[1]; - float zz = 2.0f * rot[2] * rot[2]; - float xy = 2.0f * rot[0] * rot[1]; - float xz = 2.0f * rot[0] * rot[2]; - float yz = 2.0f * rot[1] * rot[2]; - float wx = 2.0f * rot[3] * rot[0]; - float wy = 2.0f * rot[3] * rot[1]; - float wz = 2.0f * rot[3] * rot[2]; - - mat[ 0] = scale[0] * (1.0f - (yy + zz)); - mat[ 1] = scale[0] * (xy - wz); - mat[ 2] = scale[0] * (xz + wy); - mat[ 3] = trans[0]; - mat[ 4] = scale[1] * (xy + wz); - mat[ 5] = scale[1] * (1.0f - (xx + zz)); - mat[ 6] = scale[1] * (yz - wx); - mat[ 7] = trans[1]; - mat[ 8] = scale[2] * (xz - wy); - mat[ 9] = scale[2] * (yz + wx); - mat[10] = scale[2] * (1.0f - (xx + yy)); - mat[11] = trans[2]; -} -static void Matrix34Invert( float *inMat, float *outMat ) -{ - vec3_t trans; - float invSqrLen, *v; - - outMat[ 0] = inMat[ 0]; outMat[ 1] = inMat[ 4]; outMat[ 2] = inMat[ 8]; - outMat[ 4] = inMat[ 1]; outMat[ 5] = inMat[ 5]; outMat[ 6] = inMat[ 9]; - outMat[ 8] = inMat[ 2]; outMat[ 9] = inMat[ 6]; outMat[10] = inMat[10]; - - v = outMat + 0; invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); - v = outMat + 4; invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); - v = outMat + 8; invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); - - trans[0] = inMat[ 3]; - trans[1] = inMat[ 7]; - trans[2] = inMat[11]; - - outMat[ 3] = -DotProduct(outMat + 0, trans); - outMat[ 7] = -DotProduct(outMat + 4, trans); - outMat[11] = -DotProduct(outMat + 8, trans); -} - -/* -================= -R_LoadIQM - -Load an IQM model and compute the joint matrices for every frame. -================= -*/ -qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_name ) { - iqmHeader_t *header; - iqmVertexArray_t *vertexarray; - iqmTriangle_t *triangle; - iqmMesh_t *mesh; - iqmJoint_t *joint; - iqmPose_t *pose; - iqmBounds_t *bounds; - unsigned short *framedata; - char *str; - int i, j; - float jointMats[IQM_MAX_JOINTS * 2 * 12]; - float *mat; - size_t size, joint_names; - iqmData_t *iqmData; - srfIQModel_t *surface; - - if( filesize < sizeof(iqmHeader_t) ) { - return qfalse; - } - - header = (iqmHeader_t *)buffer; - if( Q_strncmp( header->magic, IQM_MAGIC, sizeof(header->magic) ) ) { - return qfalse; - } - - LL( header->version ); - if( header->version != IQM_VERSION ) { - ri.Printf(PRINT_WARNING, "R_LoadIQM: %s is a unsupported IQM version (%d), only version %d is supported.\n", - mod_name, header->version, IQM_VERSION); - return qfalse; - } - - LL( header->filesize ); - if( header->filesize > filesize || header->filesize > 16<<20 ) { - return qfalse; - } - - LL( header->flags ); - LL( header->num_text ); - LL( header->ofs_text ); - LL( header->num_meshes ); - LL( header->ofs_meshes ); - LL( header->num_vertexarrays ); - LL( header->num_vertexes ); - LL( header->ofs_vertexarrays ); - LL( header->num_triangles ); - LL( header->ofs_triangles ); - LL( header->ofs_adjacency ); - LL( header->num_joints ); - LL( header->ofs_joints ); - LL( header->num_poses ); - LL( header->ofs_poses ); - LL( header->num_anims ); - LL( header->ofs_anims ); - LL( header->num_frames ); - LL( header->num_framechannels ); - LL( header->ofs_frames ); - LL( header->ofs_bounds ); - LL( header->num_comment ); - LL( header->ofs_comment ); - LL( header->num_extensions ); - LL( header->ofs_extensions ); - - // check ioq3 joint limit - if ( header->num_joints > IQM_MAX_JOINTS ) { - ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %d joints (%d).\n", - mod_name, IQM_MAX_JOINTS, header->num_joints); - return qfalse; - } - - // check and swap vertex arrays - if( IQM_CheckRange( header, header->ofs_vertexarrays, - header->num_vertexarrays, - sizeof(iqmVertexArray_t) ) ) { - return qfalse; - } - vertexarray = (iqmVertexArray_t *)((byte *)header + header->ofs_vertexarrays); - for( i = 0; i < header->num_vertexarrays; i++, vertexarray++ ) { - int j, n, *intPtr; - - if( vertexarray->size <= 0 || vertexarray->size > 4 ) { - return qfalse; - } - - // total number of values - n = header->num_vertexes * vertexarray->size; - - switch( vertexarray->format ) { - case IQM_BYTE: - case IQM_UBYTE: - // 1 byte, no swapping necessary - if( IQM_CheckRange( header, vertexarray->offset, - n, sizeof(byte) ) ) { - return qfalse; - } - break; - case IQM_INT: - case IQM_UINT: - case IQM_FLOAT: - // 4-byte swap - if( IQM_CheckRange( header, vertexarray->offset, - n, sizeof(float) ) ) { - return qfalse; - } - intPtr = (int *)((byte *)header + vertexarray->offset); - for( j = 0; j < n; j++, intPtr++ ) { - LL( *intPtr ); - } - break; - default: - // not supported - return qfalse; - break; - } - - switch( vertexarray->type ) { - case IQM_POSITION: - case IQM_NORMAL: - if( vertexarray->format != IQM_FLOAT || - vertexarray->size != 3 ) { - return qfalse; - } - break; - case IQM_TANGENT: - if( vertexarray->format != IQM_FLOAT || - vertexarray->size != 4 ) { - return qfalse; - } - break; - case IQM_TEXCOORD: - if( vertexarray->format != IQM_FLOAT || - vertexarray->size != 2 ) { - return qfalse; - } - break; - case IQM_BLENDINDEXES: - case IQM_BLENDWEIGHTS: - if( vertexarray->format != IQM_UBYTE || - vertexarray->size != 4 ) { - return qfalse; - } - break; - case IQM_COLOR: - if( vertexarray->format != IQM_UBYTE || - vertexarray->size != 4 ) { - return qfalse; - } - break; - } - } - - // check and swap triangles - if( IQM_CheckRange( header, header->ofs_triangles, - header->num_triangles, sizeof(iqmTriangle_t) ) ) { - return qfalse; - } - triangle = (iqmTriangle_t *)((byte *)header + header->ofs_triangles); - for( i = 0; i < header->num_triangles; i++, triangle++ ) { - LL( triangle->vertex[0] ); - LL( triangle->vertex[1] ); - LL( triangle->vertex[2] ); - - if( triangle->vertex[0] > header->num_vertexes || - triangle->vertex[1] > header->num_vertexes || - triangle->vertex[2] > header->num_vertexes ) { - return qfalse; - } - } - - // check and swap meshes - if( IQM_CheckRange( header, header->ofs_meshes, - header->num_meshes, sizeof(iqmMesh_t) ) ) { - return qfalse; - } - mesh = (iqmMesh_t *)((byte *)header + header->ofs_meshes); - for( i = 0; i < header->num_meshes; i++, mesh++) { - LL( mesh->name ); - LL( mesh->material ); - LL( mesh->first_vertex ); - LL( mesh->num_vertexes ); - LL( mesh->first_triangle ); - LL( mesh->num_triangles ); - - // check ioq3 limits - if ( mesh->num_vertexes > SHADER_MAX_VERTEXES ) - { - ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i verts on a surface (%i).\n", - mod_name, SHADER_MAX_VERTEXES, mesh->num_vertexes ); - return qfalse; - } - if ( mesh->num_triangles*3 > SHADER_MAX_INDEXES ) - { - ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i triangles on a surface (%i).\n", - mod_name, SHADER_MAX_INDEXES / 3, mesh->num_triangles ); - return qfalse; - } - - if( mesh->first_vertex >= header->num_vertexes || - mesh->first_vertex + mesh->num_vertexes > header->num_vertexes || - mesh->first_triangle >= header->num_triangles || - mesh->first_triangle + mesh->num_triangles > header->num_triangles || - mesh->name >= header->num_text || - mesh->material >= header->num_text ) { - return qfalse; - } - } - - // check and swap joints - if( IQM_CheckRange( header, header->ofs_joints, - header->num_joints, sizeof(iqmJoint_t) ) ) { - return qfalse; - } - joint = (iqmJoint_t *)((byte *)header + header->ofs_joints); - joint_names = 0; - for( i = 0; i < header->num_joints; i++, joint++ ) { - LL( joint->name ); - LL( joint->parent ); - LL( joint->translate[0] ); - LL( joint->translate[1] ); - LL( joint->translate[2] ); - LL( joint->rotate[0] ); - LL( joint->rotate[1] ); - LL( joint->rotate[2] ); - LL( joint->rotate[3] ); - LL( joint->scale[0] ); - LL( joint->scale[1] ); - LL( joint->scale[2] ); - - if( joint->parent < -1 || - joint->parent >= (int)header->num_joints || - joint->name >= (int)header->num_text ) { - return qfalse; - } - joint_names += strlen( (char *)header + header->ofs_text + - joint->name ) + 1; - } - - // check and swap poses - if( header->num_poses != header->num_joints ) { - return qfalse; - } - if( IQM_CheckRange( header, header->ofs_poses, - header->num_poses, sizeof(iqmPose_t) ) ) { - return qfalse; - } - pose = (iqmPose_t *)((byte *)header + header->ofs_poses); - for( i = 0; i < header->num_poses; i++, pose++ ) { - LL( pose->parent ); - LL( pose->mask ); - LL( pose->channeloffset[0] ); - LL( pose->channeloffset[1] ); - LL( pose->channeloffset[2] ); - LL( pose->channeloffset[3] ); - LL( pose->channeloffset[4] ); - LL( pose->channeloffset[5] ); - LL( pose->channeloffset[6] ); - LL( pose->channeloffset[7] ); - LL( pose->channeloffset[8] ); - LL( pose->channeloffset[9] ); - LL( pose->channelscale[0] ); - LL( pose->channelscale[1] ); - LL( pose->channelscale[2] ); - LL( pose->channelscale[3] ); - LL( pose->channelscale[4] ); - LL( pose->channelscale[5] ); - LL( pose->channelscale[6] ); - LL( pose->channelscale[7] ); - LL( pose->channelscale[8] ); - LL( pose->channelscale[9] ); - } - - if (header->ofs_bounds) - { - // check and swap model bounds - if(IQM_CheckRange(header, header->ofs_bounds, - header->num_frames, sizeof(*bounds))) - { - return qfalse; - } - bounds = (iqmBounds_t *) ((byte *) header + header->ofs_bounds); - for(i = 0; i < header->num_frames; i++) - { - LL(bounds->bbmin[0]); - LL(bounds->bbmin[1]); - LL(bounds->bbmin[2]); - LL(bounds->bbmax[0]); - LL(bounds->bbmax[1]); - LL(bounds->bbmax[2]); - - bounds++; - } - } - - // allocate the model and copy the data - size = sizeof(iqmData_t); - size += header->num_meshes * sizeof( srfIQModel_t ); - size += header->num_joints * header->num_frames * 12 * sizeof( float ); - if(header->ofs_bounds) - size += header->num_frames * 6 * sizeof(float); // model bounds - size += header->num_vertexes * 3 * sizeof(float); // positions - size += header->num_vertexes * 2 * sizeof(float); // texcoords - size += header->num_vertexes * 3 * sizeof(float); // normals - size += header->num_vertexes * 4 * sizeof(float); // tangents - size += header->num_vertexes * 4 * sizeof(byte); // blendIndexes - size += header->num_vertexes * 4 * sizeof(byte); // blendWeights - size += header->num_vertexes * 4 * sizeof(byte); // colors - size += header->num_joints * sizeof(int); // parents - size += header->num_triangles * 3 * sizeof(int); // triangles - size += joint_names; // joint names - - mod->type = MOD_IQM; - iqmData = (iqmData_t *)ri.Hunk_Alloc( size, h_low ); - mod->modelData = iqmData; - - // fill header - iqmData->num_vertexes = header->num_vertexes; - iqmData->num_triangles = header->num_triangles; - iqmData->num_frames = header->num_frames; - iqmData->num_surfaces = header->num_meshes; - iqmData->num_joints = header->num_joints; - iqmData->surfaces = (srfIQModel_t *)(iqmData + 1); - iqmData->poseMats = (float *) (iqmData->surfaces + iqmData->num_surfaces); - if(header->ofs_bounds) - { - iqmData->bounds = iqmData->poseMats + 12 * header->num_joints * header->num_frames; - iqmData->positions = iqmData->bounds + 6 * header->num_frames; - } - else - iqmData->positions = iqmData->poseMats + 12 * header->num_joints * header->num_frames; - iqmData->texcoords = iqmData->positions + 3 * header->num_vertexes; - iqmData->normals = iqmData->texcoords + 2 * header->num_vertexes; - iqmData->tangents = iqmData->normals + 3 * header->num_vertexes; - iqmData->blendIndexes = (byte *)(iqmData->tangents + 4 * header->num_vertexes); - iqmData->blendWeights = iqmData->blendIndexes + 4 * header->num_vertexes; - iqmData->colors = iqmData->blendWeights + 4 * header->num_vertexes; - iqmData->jointParents = (int *)(iqmData->colors + 4 * header->num_vertexes); - iqmData->triangles = iqmData->jointParents + header->num_joints; - iqmData->names = (char *)(iqmData->triangles + 3 * header->num_triangles); - - // calculate joint matrices and their inverses - // they are needed only until the pose matrices are calculated - mat = jointMats; - joint = (iqmJoint_t *)((byte *)header + header->ofs_joints); - for( i = 0; i < header->num_joints; i++, joint++ ) { - float baseFrame[12], invBaseFrame[12]; - - JointToMatrix( joint->rotate, joint->scale, joint->translate, baseFrame ); - Matrix34Invert( baseFrame, invBaseFrame ); - - if ( joint->parent >= 0 ) - { - Matrix34Multiply( jointMats + 2 * 12 * joint->parent, baseFrame, mat ); - mat += 12; - Matrix34Multiply( invBaseFrame, jointMats + 2 * 12 * joint->parent + 12, mat ); - mat += 12; - } - else - { - Com_Memcpy( mat, baseFrame, sizeof(baseFrame) ); - mat += 12; - Com_Memcpy( mat, invBaseFrame, sizeof(invBaseFrame) ); - mat += 12; - } - } - - // calculate pose matrices - framedata = (unsigned short *)((byte *)header + header->ofs_frames); - mat = iqmData->poseMats; - for( i = 0; i < header->num_frames; i++ ) { - pose = (iqmPose_t *)((byte *)header + header->ofs_poses); - for( j = 0; j < header->num_poses; j++, pose++ ) { - vec3_t translate; - vec4_t rotate; - vec3_t scale; - float mat1[12], mat2[12]; - - translate[0] = pose->channeloffset[0]; - if( pose->mask & 0x001) - translate[0] += *framedata++ * pose->channelscale[0]; - translate[1] = pose->channeloffset[1]; - if( pose->mask & 0x002) - translate[1] += *framedata++ * pose->channelscale[1]; - translate[2] = pose->channeloffset[2]; - if( pose->mask & 0x004) - translate[2] += *framedata++ * pose->channelscale[2]; - - rotate[0] = pose->channeloffset[3]; - if( pose->mask & 0x008) - rotate[0] += *framedata++ * pose->channelscale[3]; - rotate[1] = pose->channeloffset[4]; - if( pose->mask & 0x010) - rotate[1] += *framedata++ * pose->channelscale[4]; - rotate[2] = pose->channeloffset[5]; - if( pose->mask & 0x020) - rotate[2] += *framedata++ * pose->channelscale[5]; - rotate[3] = pose->channeloffset[6]; - if( pose->mask & 0x040) - rotate[3] += *framedata++ * pose->channelscale[6]; - - scale[0] = pose->channeloffset[7]; - if( pose->mask & 0x080) - scale[0] += *framedata++ * pose->channelscale[7]; - scale[1] = pose->channeloffset[8]; - if( pose->mask & 0x100) - scale[1] += *framedata++ * pose->channelscale[8]; - scale[2] = pose->channeloffset[9]; - if( pose->mask & 0x200) - scale[2] += *framedata++ * pose->channelscale[9]; - - // construct transformation matrix - JointToMatrix( rotate, scale, translate, mat1 ); - - if( pose->parent >= 0 ) { - Matrix34Multiply( jointMats + 12 * 2 * pose->parent, - mat1, mat2 ); - } else { - Com_Memcpy( mat2, mat1, sizeof(mat1) ); - } - - Matrix34Multiply( mat2, jointMats + 12 * (2 * j + 1), mat ); - mat += 12; - } - } - - // register shaders - // overwrite the material offset with the shader index - mesh = (iqmMesh_t *)((byte *)header + header->ofs_meshes); - surface = iqmData->surfaces; - str = (char *)header + header->ofs_text; - for( i = 0; i < header->num_meshes; i++, mesh++, surface++ ) { - surface->surfaceType = SF_IQM; - Q_strncpyz(surface->name, str + mesh->name, sizeof (surface->name)); - Q_strlwr(surface->name); // lowercase the surface name so skin compares are faster - surface->shader = R_FindShader( str + mesh->material, LIGHTMAP_NONE, qtrue ); - if( surface->shader->defaultShader ) - surface->shader = tr.defaultShader; - surface->data = iqmData; - surface->first_vertex = mesh->first_vertex; - surface->num_vertexes = mesh->num_vertexes; - surface->first_triangle = mesh->first_triangle; - surface->num_triangles = mesh->num_triangles; - } - - // copy vertexarrays and indexes - vertexarray = (iqmVertexArray_t *)((byte *)header + header->ofs_vertexarrays); - for( i = 0; i < header->num_vertexarrays; i++, vertexarray++ ) { - int n; - - // total number of values - n = header->num_vertexes * vertexarray->size; - - switch( vertexarray->type ) { - case IQM_POSITION: - Com_Memcpy( iqmData->positions, - (byte *)header + vertexarray->offset, - n * sizeof(float) ); - break; - case IQM_NORMAL: - Com_Memcpy( iqmData->normals, - (byte *)header + vertexarray->offset, - n * sizeof(float) ); - break; - case IQM_TANGENT: - Com_Memcpy( iqmData->tangents, - (byte *)header + vertexarray->offset, - n * sizeof(float) ); - break; - case IQM_TEXCOORD: - Com_Memcpy( iqmData->texcoords, - (byte *)header + vertexarray->offset, - n * sizeof(float) ); - break; - case IQM_BLENDINDEXES: - Com_Memcpy( iqmData->blendIndexes, - (byte *)header + vertexarray->offset, - n * sizeof(byte) ); - break; - case IQM_BLENDWEIGHTS: - Com_Memcpy( iqmData->blendWeights, - (byte *)header + vertexarray->offset, - n * sizeof(byte) ); - break; - case IQM_COLOR: - Com_Memcpy( iqmData->colors, - (byte *)header + vertexarray->offset, - n * sizeof(byte) ); - break; - } - } - - // copy joint parents - joint = (iqmJoint_t *)((byte *)header + header->ofs_joints); - for( i = 0; i < header->num_joints; i++, joint++ ) { - iqmData->jointParents[i] = joint->parent; - } - - // copy triangles - triangle = (iqmTriangle_t *)((byte *)header + header->ofs_triangles); - for( i = 0; i < header->num_triangles; i++, triangle++ ) { - iqmData->triangles[3*i+0] = triangle->vertex[0]; - iqmData->triangles[3*i+1] = triangle->vertex[1]; - iqmData->triangles[3*i+2] = triangle->vertex[2]; - } - - // copy joint names - str = iqmData->names; - joint = (iqmJoint_t *)((byte *)header + header->ofs_joints); - for( i = 0; i < header->num_joints; i++, joint++ ) { - char *name = (char *)header + header->ofs_text + - joint->name; - int len = strlen( name ) + 1; - Com_Memcpy( str, name, len ); - str += len; - } - - // copy model bounds - if(header->ofs_bounds) - { - mat = iqmData->bounds; - bounds = (iqmBounds_t *) ((byte *) header + header->ofs_bounds); - for(i = 0; i < header->num_frames; i++) - { - mat[0] = bounds->bbmin[0]; - mat[1] = bounds->bbmin[1]; - mat[2] = bounds->bbmin[2]; - mat[3] = bounds->bbmax[0]; - mat[4] = bounds->bbmax[1]; - mat[5] = bounds->bbmax[2]; - - mat += 6; - bounds++; - } - } - - return qtrue; -} - -/* -============= -R_CullIQM -============= -*/ -static int R_CullIQM( iqmData_t *data, trRefEntity_t *ent ) { - vec3_t bounds[2]; - vec_t *oldBounds, *newBounds; - int i; - - if (!data->bounds) { - tr.pc.c_box_cull_md3_clip++; - return CULL_CLIP; - } - - // compute bounds pointers - oldBounds = data->bounds + 6*ent->e.oldframe; - newBounds = data->bounds + 6*ent->e.frame; - - // calculate a bounding box in the current coordinate system - for (i = 0 ; i < 3 ; i++) { - bounds[0][i] = oldBounds[i] < newBounds[i] ? oldBounds[i] : newBounds[i]; - bounds[1][i] = oldBounds[i+3] > newBounds[i+3] ? oldBounds[i+3] : newBounds[i+3]; - } - - switch ( R_CullLocalBox( bounds ) ) - { - case CULL_IN: - tr.pc.c_box_cull_md3_in++; - return CULL_IN; - case CULL_CLIP: - tr.pc.c_box_cull_md3_clip++; - return CULL_CLIP; - case CULL_OUT: - default: - tr.pc.c_box_cull_md3_out++; - return CULL_OUT; - } -} - -/* -================= -R_ComputeIQMFogNum - -================= -*/ -int R_ComputeIQMFogNum( iqmData_t *data, trRefEntity_t *ent ) { - int i, j; - fog_t *fog; - const vec_t *bounds; - const vec_t defaultBounds[6] = { -8, -8, -8, 8, 8, 8 }; - vec3_t diag, center; - vec3_t localOrigin; - vec_t radius; - - if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { - return 0; - } - - // FIXME: non-normalized axis issues - if (data->bounds) { - bounds = data->bounds + 6*ent->e.frame; - } else { - bounds = defaultBounds; - } - VectorSubtract( bounds+3, bounds, diag ); - VectorMA( bounds, 0.5f, diag, center ); - VectorAdd( ent->e.origin, center, localOrigin ); - radius = 0.5f * VectorLength( diag ); - - for ( i = 1 ; i < tr.world->numfogs ; i++ ) { - fog = &tr.world->fogs[i]; - for ( j = 0 ; j < 3 ; j++ ) { - if ( localOrigin[j] - radius >= fog->bounds[1][j] ) { - break; - } - if ( localOrigin[j] + radius <= fog->bounds[0][j] ) { - break; - } - } - if ( j == 3 ) { - return i; - } - } - - return 0; -} - -/* -================= -R_AddIQMSurfaces - -Add all surfaces of this model -================= -*/ -void R_AddIQMSurfaces( trRefEntity_t *ent ) { - iqmData_t *data; - srfIQModel_t *surface; - int i, j; - qboolean personalModel; - int cull; - int fogNum; - shader_t *shader; - skin_t *skin; - - data = tr.currentModel->modelData; - surface = data->surfaces; - - // don't add third_person objects if not in a portal - personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal; - - if ( ent->e.renderfx & RF_WRAP_FRAMES ) { - ent->e.frame %= data->num_frames; - ent->e.oldframe %= data->num_frames; - } - - // - // Validate the frames so there is no chance of a crash. - // This will write directly into the entity structure, so - // when the surfaces are rendered, they don't need to be - // range checked again. - // - if ( (ent->e.frame >= data->num_frames) - || (ent->e.frame < 0) - || (ent->e.oldframe >= data->num_frames) - || (ent->e.oldframe < 0) ) { - ri.Printf( PRINT_DEVELOPER, "R_AddIQMSurfaces: no such frame %d to %d for '%s'\n", - ent->e.oldframe, ent->e.frame, - tr.currentModel->name ); - ent->e.frame = 0; - ent->e.oldframe = 0; - } - - // - // cull the entire model if merged bounding box of both frames - // is outside the view frustum. - // - cull = R_CullIQM ( data, ent ); - if ( cull == CULL_OUT ) { - return; - } - - // - // set up lighting now that we know we aren't culled - // - if ( !personalModel || r_shadows->integer > 1 ) { - R_SetupEntityLighting( &tr.refdef, ent ); - } - - // - // see if we are in a fog volume - // - fogNum = R_ComputeIQMFogNum( data, ent ); - - for ( i = 0 ; i < data->num_surfaces ; i++ ) { - if(ent->e.customShader) - shader = R_GetShaderByHandle( ent->e.customShader ); - else if(ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins) - { - skin = R_GetSkinByHandle(ent->e.customSkin); - shader = tr.defaultShader; - - for(j = 0; j < skin->numSurfaces; j++) - { - if (!strcmp(skin->surfaces[j]->name, surface->name)) - { - shader = skin->surfaces[j]->shader; - break; - } - } - } else { - shader = surface->shader; - } - - // we will add shadows even if the main object isn't visible in the view - - // stencil shadows can't do personal models unless I polyhedron clip - if ( !personalModel - && r_shadows->integer == 2 - && fogNum == 0 - && !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) - && shader->sort == SS_OPAQUE ) { - R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, 0 ); - } - - // projection shadows work fine with personal models - if ( r_shadows->integer == 3 - && fogNum == 0 - && (ent->e.renderfx & RF_SHADOW_PLANE ) - && shader->sort == SS_OPAQUE ) { - R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, 0 ); - } - - if( !personalModel ) { - R_AddDrawSurf( (void *)surface, shader, fogNum, 0 ); - } - - surface++; - } -} - - -static void ComputeJointMats( iqmData_t *data, int frame, int oldframe, - float backlerp, float *mat ) { - float *mat1, *mat2; - int *joint = data->jointParents; - int i; - - if ( oldframe == frame ) { - mat1 = data->poseMats + 12 * data->num_joints * frame; - for( i = 0; i < data->num_joints; i++, joint++ ) { - if( *joint >= 0 ) { - Matrix34Multiply( mat + 12 * *joint, - mat1 + 12*i, mat + 12*i ); - } else { - Com_Memcpy( mat + 12*i, mat1 + 12*i, 12 * sizeof(float) ); - } - } - } else { - mat1 = data->poseMats + 12 * data->num_joints * frame; - mat2 = data->poseMats + 12 * data->num_joints * oldframe; - - for( i = 0; i < data->num_joints; i++, joint++ ) { - if( *joint >= 0 ) { - float tmpMat[12]; - InterpolateMatrix( mat1 + 12*i, mat2 + 12*i, - backlerp, tmpMat ); - Matrix34Multiply( mat + 12 * *joint, - tmpMat, mat + 12*i ); - - } else { - InterpolateMatrix( mat1 + 12*i, mat2 + 12*i, - backlerp, mat ); - } - } - } -} - - -/* -================= -RB_AddIQMSurfaces - -Compute vertices for this model surface -================= -*/ -void RB_IQMSurfaceAnim( surfaceType_t *surface ) { - srfIQModel_t *surf = (srfIQModel_t *)surface; - iqmData_t *data = surf->data; - float jointMats[IQM_MAX_JOINTS * 12]; - int i; - - vec4_t *outXYZ = &tess.xyz[tess.numVertexes]; - vec4_t *outNormal = &tess.normal[tess.numVertexes]; - vec2_t (*outTexCoord)[2] = &tess.texCoords[tess.numVertexes]; - color4ub_t *outColor = &tess.vertexColors[tess.numVertexes]; - - int frame = backEnd.currentEntity->e.frame % data->num_frames; - int oldframe = backEnd.currentEntity->e.oldframe % data->num_frames; - float backlerp = backEnd.currentEntity->e.backlerp; - - int *tri; - glIndex_t *ptr; - glIndex_t base; - - RB_CHECKOVERFLOW( surf->num_vertexes, surf->num_triangles * 3 ); - - // compute interpolated joint matrices - ComputeJointMats( data, frame, oldframe, backlerp, jointMats ); - - // transform vertexes and fill other data - for( i = 0; i < surf->num_vertexes; - i++, outXYZ++, outNormal++, outTexCoord++, outColor++ ) { - int j, k; - float vtxMat[12]; - float nrmMat[9]; - int vtx = i + surf->first_vertex; - - // compute the vertex matrix by blending the up to - // four blend weights - for( k = 0; k < 12; k++ ) - vtxMat[k] = data->blendWeights[4*vtx] - * jointMats[12*data->blendIndexes[4*vtx] + k]; - for( j = 1; j < 4; j++ ) { - if( data->blendWeights[4*vtx + j] <= 0 ) - break; - for( k = 0; k < 12; k++ ) - vtxMat[k] += data->blendWeights[4*vtx + j] - * jointMats[12*data->blendIndexes[4*vtx + j] + k]; - } - for( k = 0; k < 12; k++ ) - vtxMat[k] *= 1.0f / 255.0f; - - // compute the normal matrix as transpose of the adjoint - // of the vertex matrix - nrmMat[ 0] = vtxMat[ 5]*vtxMat[10] - vtxMat[ 6]*vtxMat[ 9]; - nrmMat[ 1] = vtxMat[ 6]*vtxMat[ 8] - vtxMat[ 4]*vtxMat[10]; - nrmMat[ 2] = vtxMat[ 4]*vtxMat[ 9] - vtxMat[ 5]*vtxMat[ 8]; - nrmMat[ 3] = vtxMat[ 2]*vtxMat[ 9] - vtxMat[ 1]*vtxMat[10]; - nrmMat[ 4] = vtxMat[ 0]*vtxMat[10] - vtxMat[ 2]*vtxMat[ 8]; - nrmMat[ 5] = vtxMat[ 1]*vtxMat[ 8] - vtxMat[ 0]*vtxMat[ 9]; - nrmMat[ 6] = vtxMat[ 1]*vtxMat[ 6] - vtxMat[ 2]*vtxMat[ 5]; - nrmMat[ 7] = vtxMat[ 2]*vtxMat[ 4] - vtxMat[ 0]*vtxMat[ 6]; - nrmMat[ 8] = vtxMat[ 0]*vtxMat[ 5] - vtxMat[ 1]*vtxMat[ 4]; - - (*outTexCoord)[0][0] = data->texcoords[2*vtx + 0]; - (*outTexCoord)[0][1] = data->texcoords[2*vtx + 1]; - (*outTexCoord)[1][0] = (*outTexCoord)[0][0]; - (*outTexCoord)[1][1] = (*outTexCoord)[0][1]; - - (*outXYZ)[0] = - vtxMat[ 0] * data->positions[3*vtx+0] + - vtxMat[ 1] * data->positions[3*vtx+1] + - vtxMat[ 2] * data->positions[3*vtx+2] + - vtxMat[ 3]; - (*outXYZ)[1] = - vtxMat[ 4] * data->positions[3*vtx+0] + - vtxMat[ 5] * data->positions[3*vtx+1] + - vtxMat[ 6] * data->positions[3*vtx+2] + - vtxMat[ 7]; - (*outXYZ)[2] = - vtxMat[ 8] * data->positions[3*vtx+0] + - vtxMat[ 9] * data->positions[3*vtx+1] + - vtxMat[10] * data->positions[3*vtx+2] + - vtxMat[11]; - (*outXYZ)[3] = 1.0f; - - (*outNormal)[0] = - nrmMat[ 0] * data->normals[3*vtx+0] + - nrmMat[ 1] * data->normals[3*vtx+1] + - nrmMat[ 2] * data->normals[3*vtx+2]; - (*outNormal)[1] = - nrmMat[ 3] * data->normals[3*vtx+0] + - nrmMat[ 4] * data->normals[3*vtx+1] + - nrmMat[ 5] * data->normals[3*vtx+2]; - (*outNormal)[2] = - nrmMat[ 6] * data->normals[3*vtx+0] + - nrmMat[ 7] * data->normals[3*vtx+1] + - nrmMat[ 8] * data->normals[3*vtx+2]; - (*outNormal)[3] = 0.0f; - - (*outColor)[0] = data->colors[4*vtx+0]; - (*outColor)[1] = data->colors[4*vtx+1]; - (*outColor)[2] = data->colors[4*vtx+2]; - (*outColor)[3] = data->colors[4*vtx+3]; - } - - tri = data->triangles + 3 * surf->first_triangle; - ptr = &tess.indexes[tess.numIndexes]; - base = tess.numVertexes; - - for( i = 0; i < surf->num_triangles; i++ ) { - *ptr++ = base + (*tri++ - surf->first_vertex); - *ptr++ = base + (*tri++ - surf->first_vertex); - *ptr++ = base + (*tri++ - surf->first_vertex); - } - - tess.numIndexes += 3 * surf->num_triangles; - tess.numVertexes += surf->num_vertexes; -} - -int R_IQMLerpTag( orientation_t *tag, iqmData_t *data, - int startFrame, int endFrame, - float frac, const char *tagName ) { - float jointMats[IQM_MAX_JOINTS * 12]; - int joint; - char *names = data->names; - - // get joint number by reading the joint names - for( joint = 0; joint < data->num_joints; joint++ ) { - if( !strcmp( tagName, names ) ) - break; - names += strlen( names ) + 1; - } - if( joint >= data->num_joints ) { - AxisClear( tag->axis ); - VectorClear( tag->origin ); - return qfalse; - } - - ComputeJointMats( data, startFrame, endFrame, frac, jointMats ); - - tag->axis[0][0] = jointMats[12 * joint + 0]; - tag->axis[1][0] = jointMats[12 * joint + 1]; - tag->axis[2][0] = jointMats[12 * joint + 2]; - tag->origin[0] = jointMats[12 * joint + 3]; - tag->axis[0][1] = jointMats[12 * joint + 4]; - tag->axis[1][1] = jointMats[12 * joint + 5]; - tag->axis[2][1] = jointMats[12 * joint + 6]; - tag->origin[1] = jointMats[12 * joint + 7]; - tag->axis[0][2] = jointMats[12 * joint + 8]; - tag->axis[1][2] = jointMats[12 * joint + 9]; - tag->axis[2][2] = jointMats[12 * joint + 10]; - tag->origin[2] = jointMats[12 * joint + 11]; - - return qtrue; -} diff --git a/src/renderer/tr_noise.c b/src/renderer/tr_noise.c deleted file mode 100644 index b4c4082d..00000000 --- a/src/renderer/tr_noise.c +++ /dev/null @@ -1,92 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_noise.c -#include "tr_local.h" - -#define NOISE_SIZE 256 -#define NOISE_MASK ( NOISE_SIZE - 1 ) - -#define VAL( a ) s_noise_perm[ ( a ) & ( NOISE_MASK )] -#define INDEX( x, y, z, t ) VAL( x + VAL( y + VAL( z + VAL( t ) ) ) ) - -static float s_noise_table[NOISE_SIZE]; -static int s_noise_perm[NOISE_SIZE]; - -static float GetNoiseValue( int x, int y, int z, int t ) -{ - int index = INDEX( ( int ) x, ( int ) y, ( int ) z, ( int ) t ); - - return s_noise_table[index]; -} - -void R_NoiseInit( void ) -{ - int i; - - for ( i = 0; i < NOISE_SIZE; i++ ) - { - s_noise_table[i] = ( float ) ( ( ( rand() / ( float ) RAND_MAX ) * 2.0 - 1.0 ) ); - s_noise_perm[i] = ( unsigned char ) ( rand() / ( float ) RAND_MAX * 255 ); - } -} - -float R_NoiseGet4f( float x, float y, float z, float t ) -{ - int i; - int ix, iy, iz, it; - float fx, fy, fz, ft; - float front[4]; - float back[4]; - float fvalue, bvalue, value[2], finalvalue; - - ix = ( int ) floor( x ); - fx = x - ix; - iy = ( int ) floor( y ); - fy = y - iy; - iz = ( int ) floor( z ); - fz = z - iz; - it = ( int ) floor( t ); - ft = t - it; - - for ( i = 0; i < 2; i++ ) - { - front[0] = GetNoiseValue( ix, iy, iz, it + i ); - front[1] = GetNoiseValue( ix+1, iy, iz, it + i ); - front[2] = GetNoiseValue( ix, iy+1, iz, it + i ); - front[3] = GetNoiseValue( ix+1, iy+1, iz, it + i ); - - back[0] = GetNoiseValue( ix, iy, iz + 1, it + i ); - back[1] = GetNoiseValue( ix+1, iy, iz + 1, it + i ); - back[2] = GetNoiseValue( ix, iy+1, iz + 1, it + i ); - back[3] = GetNoiseValue( ix+1, iy+1, iz + 1, it + i ); - - fvalue = LERP( LERP( front[0], front[1], fx ), LERP( front[2], front[3], fx ), fy ); - bvalue = LERP( LERP( back[0], back[1], fx ), LERP( back[2], back[3], fx ), fy ); - - value[i] = LERP( fvalue, bvalue, fz ); - } - - finalvalue = LERP( value[0], value[1], ft ); - - return finalvalue; -} diff --git a/src/renderer/tr_public.h b/src/renderer/tr_public.h deleted file mode 100644 index f9ededdf..00000000 --- a/src/renderer/tr_public.h +++ /dev/null @@ -1,196 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#ifndef __TR_PUBLIC_H -#define __TR_PUBLIC_H - -#include "tr_types.h" - -#define REF_API_VERSION 8 - -// -// these are the functions exported by the refresh module -// -typedef struct { - // called before the library is unloaded - // if the system is just reconfiguring, pass destroyWindow = qfalse, - // which will keep the screen from flashing to the desktop. - void (*Shutdown)( qboolean destroyWindow ); - - // All data that will be used in a level should be - // registered before rendering any frames to prevent disk hits, - // but they can still be registered at a later time - // if necessary. - // - // BeginRegistration makes any existing media pointers invalid - // and returns the current gl configuration, including screen width - // and height, which can be used by the client to intelligently - // size display elements - void (*BeginRegistration)( glconfig_t *config ); - qhandle_t (*RegisterModel)( const char *name ); - qhandle_t (*RegisterSkin)( const char *name ); - qhandle_t (*RegisterShader)( const char *name ); - qhandle_t (*RegisterShaderNoMip)( const char *name ); - void (*LoadWorld)( const char *name ); - - // the vis data is a large enough block of data that we go to the trouble - // of sharing it with the clipmodel subsystem - void (*SetWorldVisData)( const byte *vis ); - - // EndRegistration will draw a tiny polygon with each texture, forcing - // them to be loaded into card memory - void (*EndRegistration)( void ); - - // a scene is built up by calls to R_ClearScene and the various R_Add functions. - // Nothing is drawn until R_RenderScene is called. - void (*ClearScene)( void ); - void (*AddRefEntityToScene)( const refEntity_t *re ); - void (*AddPolyToScene)( qhandle_t hShader , int numVerts, const polyVert_t *verts, int num ); - int (*LightForPoint)( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ); - void (*AddLightToScene)( const vec3_t org, float intensity, float r, float g, float b ); - void (*AddAdditiveLightToScene)( const vec3_t org, float intensity, float r, float g, float b ); - void (*RenderScene)( const refdef_t *fd ); - - void (*SetColor)( const float *rgba ); // NULL = 1,1,1,1 - void (*SetClipRegion)( const float *region ); - void (*DrawStretchPic) ( float x, float y, float w, float h, - float s1, float t1, float s2, float t2, qhandle_t hShader ); // 0 = white - - // Draw images for cinematic rendering, pass as 32 bit rgba - void (*DrawStretchRaw) (int x, int y, int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty); - void (*UploadCinematic) (int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty); - - void (*BeginFrame)( stereoFrame_t stereoFrame ); - - // if the pointers are not NULL, timing info will be returned - void (*EndFrame)( int *frontEndMsec, int *backEndMsec ); - - - int (*MarkFragments)( int numPoints, const vec3_t *points, const vec3_t projection, - int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ); - - int (*LerpTag)( orientation_t *tag, qhandle_t model, int startFrame, int endFrame, - float frac, const char *tagName ); - void (*ModelBounds)( qhandle_t model, vec3_t mins, vec3_t maxs ); - -#ifdef __USEA3D - void (*A3D_RenderGeometry) (void *pVoidA3D, void *pVoidGeom, void *pVoidMat, void *pVoidGeomStatus); -#endif - void (*RegisterFont)(const char *fontName, int pointSize, fontInfo_t *font); - void (*RemapShader)(const char *oldShader, const char *newShader, const char *offsetTime); - qboolean (*GetEntityToken)( char *buffer, int size ); - qboolean (*inPVS)( const vec3_t p1, const vec3_t p2 ); - - void (*TakeVideoFrame)( int h, int w, byte* captureBuffer, byte *encodeBuffer, qboolean motionJpeg ); -} refexport_t; - -// -// these are the functions imported by the refresh module -// -typedef struct { - // print message on the local console - void (QDECL *Printf)( int printLevel, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); - - // abort the game - void (QDECL *Error)( int errorLevel, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); - - // milliseconds should only be used for profiling, never - // for anything game related. Get time from the refdef - int (*Milliseconds)( void ); - - // stack based memory allocation for per-level things that - // won't be freed -#ifdef HUNK_DEBUG - void *(*Hunk_AllocDebug)( int size, ha_pref pref, char *label, char *file, int line ); -#else - void *(*Hunk_Alloc)( int size, ha_pref pref ); -#endif - void *(*Hunk_AllocateTempMemory)( int size ); - void (*Hunk_FreeTempMemory)( void *block ); - - // dynamic memory allocator for things that need to be freed - void *(*Malloc)( int bytes ); - void (*Free)( void *buf ); - - cvar_t *(*Cvar_Get)( const char *name, const char *value, int flags ); - void (*Cvar_Set)( const char *name, const char *value ); - void (*Cvar_SetValue) (const char *name, float value); - void (*Cvar_CheckRange)( cvar_t *cv, float minVal, float maxVal, qboolean shouldBeIntegral ); - - int (*Cvar_VariableIntegerValue) (const char *var_name); - - void (*Cmd_AddCommand)( const char *name, void(*cmd)(void) ); - void (*Cmd_RemoveCommand)( const char *name ); - - int (*Cmd_Argc) (void); - char *(*Cmd_Argv) (int i); - - void (*Cmd_ExecuteText) (int exec_when, const char *text); - - byte *(*CM_ClusterPVS)(int cluster); - - // visualization for debugging collision detection - void (*CM_DrawDebugSurface)( void (*drawPoly)(int color, int numPoints, float *points) ); - - // a -1 return means the file does not exist - // NULL can be passed for buf to just determine existance - int (*FS_FileIsInPAK)( const char *name, int *pCheckSum ); - long (*FS_ReadFile)( const char *name, void **buf ); - void (*FS_FreeFile)( void *buf ); - char ** (*FS_ListFiles)( const char *name, const char *extension, int *numfilesfound ); - void (*FS_FreeFileList)( char **filelist ); - void (*FS_WriteFile)( const char *qpath, const void *buffer, int size ); - qboolean (*FS_FileExists)( const char *file ); - - // cinematic stuff - void (*CIN_UploadCinematic)(int handle); - int (*CIN_PlayCinematic)( const char *arg0, int xpos, int ypos, int width, int height, int bits); - e_status (*CIN_RunCinematic) (int handle); - - void (*CL_WriteAVIVideoFrame)( const byte *buffer, int size ); - - // input event handling - void (*IN_Init)( void ); - void (*IN_Shutdown)( void ); - void (*IN_Restart)( void ); - - // math - long (*ftol)(float f); - - // system stuff - void (*Sys_SetEnv)( const char *name, const char *value ); - void (*Sys_GLimpSafeInit)( void ); - void (*Sys_GLimpInit)( void ); - qboolean (*Sys_LowPhysicalMemory)( void ); -} refimport_t; - - -// this is the only function actually exported at the linker level -// If the module can't init to a valid rendering state, NULL will be -// returned. -#ifdef USE_RENDERER_DLOPEN -typedef refexport_t* (QDECL *GetRefAPI_t) (int apiVersion, refimport_t * rimp); -#else -refexport_t*GetRefAPI( int apiVersion, refimport_t *rimp ); -#endif - -#endif // __TR_PUBLIC_H diff --git a/src/renderer/tr_scene.c b/src/renderer/tr_scene.c deleted file mode 100644 index 6ee77dc6..00000000 --- a/src/renderer/tr_scene.c +++ /dev/null @@ -1,412 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ - -#include "tr_local.h" - -int r_firstSceneDrawSurf; - -int r_numdlights; -int r_firstSceneDlight; - -int r_numentities; -int r_firstSceneEntity; - -int r_numpolys; -int r_firstScenePoly; - -int r_numpolyverts; - - -/* -==================== -R_InitNextFrame - -==================== -*/ -void R_InitNextFrame( void ) { - backEndData->commands.used = 0; - - r_firstSceneDrawSurf = 0; - - r_numdlights = 0; - r_firstSceneDlight = 0; - - r_numentities = 0; - r_firstSceneEntity = 0; - - r_numpolys = 0; - r_firstScenePoly = 0; - - r_numpolyverts = 0; -} - - -/* -==================== -RE_ClearScene - -==================== -*/ -void RE_ClearScene( void ) { - r_firstSceneDlight = r_numdlights; - r_firstSceneEntity = r_numentities; - r_firstScenePoly = r_numpolys; -} - -/* -=========================================================================== - -DISCRETE POLYS - -=========================================================================== -*/ - -/* -===================== -R_AddPolygonSurfaces - -Adds all the scene's polys into this view's drawsurf list -===================== -*/ -void R_AddPolygonSurfaces( void ) { - int i; - shader_t *sh; - srfPoly_t *poly; - - tr.currentEntityNum = REFENTITYNUM_WORLD; - tr.shiftedEntityNum = tr.currentEntityNum << QSORT_REFENTITYNUM_SHIFT; - - for ( i = 0, poly = tr.refdef.polys; i < tr.refdef.numPolys ; i++, poly++ ) { - sh = R_GetShaderByHandle( poly->hShader ); - R_AddDrawSurf( ( void * )poly, sh, poly->fogIndex, qfalse ); - } -} - -/* -===================== -RE_AddPolyToScene - -===================== -*/ -void RE_AddPolyToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ) { - srfPoly_t *poly; - int i, j; - int fogIndex; - fog_t *fog; - vec3_t bounds[2]; - - if ( !tr.registered ) { - return; - } - - if ( !hShader ) { - ri.Printf( PRINT_WARNING, "WARNING: RE_AddPolyToScene: NULL poly shader\n"); - return; - } - - for ( j = 0; j < numPolys; j++ ) { - if ( r_numpolyverts + numVerts > max_polyverts || r_numpolys >= max_polys ) { - /* - NOTE TTimo this was initially a PRINT_WARNING - but it happens a lot with high fighting scenes and particles - since we don't plan on changing the const and making for room for those effects - simply cut this message to developer only - */ - ri.Printf( PRINT_DEVELOPER, "WARNING: RE_AddPolyToScene: r_max_polys or r_max_polyverts reached\n"); - return; - } - - poly = &backEndData->polys[r_numpolys]; - poly->surfaceType = SF_POLY; - poly->hShader = hShader; - poly->numVerts = numVerts; - poly->verts = &backEndData->polyVerts[r_numpolyverts]; - - Com_Memcpy( poly->verts, &verts[numVerts*j], numVerts * sizeof( *verts ) ); - - if ( glConfig.hardwareType == GLHW_RAGEPRO ) { - poly->verts->modulate[0] = 255; - poly->verts->modulate[1] = 255; - poly->verts->modulate[2] = 255; - poly->verts->modulate[3] = 255; - } - // done. - r_numpolys++; - r_numpolyverts += numVerts; - - // if no world is loaded - if ( tr.world == NULL ) { - fogIndex = 0; - } - // see if it is in a fog volume - else if ( tr.world->numfogs == 1 ) { - fogIndex = 0; - } else { - // find which fog volume the poly is in - VectorCopy( poly->verts[0].xyz, bounds[0] ); - VectorCopy( poly->verts[0].xyz, bounds[1] ); - for ( i = 1 ; i < poly->numVerts ; i++ ) { - AddPointToBounds( poly->verts[i].xyz, bounds[0], bounds[1] ); - } - for ( fogIndex = 1 ; fogIndex < tr.world->numfogs ; fogIndex++ ) { - fog = &tr.world->fogs[fogIndex]; - if ( bounds[1][0] >= fog->bounds[0][0] - && bounds[1][1] >= fog->bounds[0][1] - && bounds[1][2] >= fog->bounds[0][2] - && bounds[0][0] <= fog->bounds[1][0] - && bounds[0][1] <= fog->bounds[1][1] - && bounds[0][2] <= fog->bounds[1][2] ) { - break; - } - } - if ( fogIndex == tr.world->numfogs ) { - fogIndex = 0; - } - } - poly->fogIndex = fogIndex; - } -} - - -//================================================================================= - - -/* -===================== -RE_AddRefEntityToScene - -===================== -*/ -void RE_AddRefEntityToScene( const refEntity_t *ent ) { - if ( !tr.registered ) { - return; - } - if ( r_numentities >= MAX_REFENTITIES ) { - ri.Printf(PRINT_DEVELOPER, "RE_AddRefEntityToScene: Dropping refEntity, reached MAX_REFENTITIES\n"); - return; - } - if ( Q_isnan(ent->origin[0]) || Q_isnan(ent->origin[1]) || Q_isnan(ent->origin[2]) ) { - static qboolean firstTime = qtrue; - if (firstTime) { - firstTime = qfalse; - ri.Printf( PRINT_WARNING, "RE_AddRefEntityToScene passed a refEntity which has an origin with a NaN component\n"); - } - return; - } - if ( (int)ent->reType < 0 || ent->reType >= RT_MAX_REF_ENTITY_TYPE ) { - ri.Error( ERR_DROP, "RE_AddRefEntityToScene: bad reType %i", ent->reType ); - } - - backEndData->entities[r_numentities].e = *ent; - backEndData->entities[r_numentities].lightingCalculated = qfalse; - - r_numentities++; -} - - -/* -===================== -RE_AddDynamicLightToScene - -===================== -*/ -void RE_AddDynamicLightToScene( const vec3_t org, float intensity, float r, float g, float b, int additive ) { - dlight_t *dl; - - if ( !tr.registered ) { - return; - } - if ( r_numdlights >= MAX_DLIGHTS ) { - return; - } - if ( intensity <= 0 ) { - return; - } - // these cards don't have the correct blend mode - if ( glConfig.hardwareType == GLHW_RIVA128 || glConfig.hardwareType == GLHW_PERMEDIA2 ) { - return; - } - dl = &backEndData->dlights[r_numdlights++]; - VectorCopy (org, dl->origin); - dl->radius = intensity; - dl->color[0] = r; - dl->color[1] = g; - dl->color[2] = b; - dl->additive = additive; -} - -/* -===================== -RE_AddLightToScene - -===================== -*/ -void RE_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ) { - RE_AddDynamicLightToScene( org, intensity, r, g, b, qfalse ); -} - -/* -===================== -RE_AddAdditiveLightToScene - -===================== -*/ -void RE_AddAdditiveLightToScene( const vec3_t org, float intensity, float r, float g, float b ) { - RE_AddDynamicLightToScene( org, intensity, r, g, b, qtrue ); -} - -/* -@@@@@@@@@@@@@@@@@@@@@ -RE_RenderScene - -Draw a 3D view into a part of the window, then return -to 2D drawing. - -Rendering a scene may require multiple views to be rendered -to handle mirrors, -@@@@@@@@@@@@@@@@@@@@@ -*/ -void RE_RenderScene( const refdef_t *fd ) { - viewParms_t parms; - int startTime; - - if ( !tr.registered ) { - return; - } - GLimp_LogComment( "====== RE_RenderScene =====\n" ); - - if ( r_norefresh->integer ) { - return; - } - - startTime = ri.Milliseconds(); - - if (!tr.world && !( fd->rdflags & RDF_NOWORLDMODEL ) ) { - ri.Error (ERR_DROP, "R_RenderScene: NULL worldmodel"); - } - - Com_Memcpy( tr.refdef.text, fd->text, sizeof( tr.refdef.text ) ); - - tr.refdef.x = fd->x; - tr.refdef.y = fd->y; - tr.refdef.width = fd->width; - tr.refdef.height = fd->height; - tr.refdef.fov_x = fd->fov_x; - tr.refdef.fov_y = fd->fov_y; - - VectorCopy( fd->vieworg, tr.refdef.vieworg ); - VectorCopy( fd->viewaxis[0], tr.refdef.viewaxis[0] ); - VectorCopy( fd->viewaxis[1], tr.refdef.viewaxis[1] ); - VectorCopy( fd->viewaxis[2], tr.refdef.viewaxis[2] ); - - tr.refdef.time = fd->time; - tr.refdef.rdflags = fd->rdflags; - - // copy the areamask data over and note if it has changed, which - // will force a reset of the visible leafs even if the view hasn't moved - tr.refdef.areamaskModified = qfalse; - if ( ! (tr.refdef.rdflags & RDF_NOWORLDMODEL) ) { - int areaDiff; - int i; - - // compare the area bits - areaDiff = 0; - for (i = 0 ; i < MAX_MAP_AREA_BYTES/4 ; i++) { - areaDiff |= ((int *)tr.refdef.areamask)[i] ^ ((int *)fd->areamask)[i]; - ((int *)tr.refdef.areamask)[i] = ((int *)fd->areamask)[i]; - } - - if ( areaDiff ) { - // a door just opened or something - tr.refdef.areamaskModified = qtrue; - } - } - - - // derived info - - tr.refdef.floatTime = tr.refdef.time * 0.001f; - - tr.refdef.numDrawSurfs = r_firstSceneDrawSurf; - tr.refdef.drawSurfs = backEndData->drawSurfs; - - tr.refdef.num_entities = r_numentities - r_firstSceneEntity; - tr.refdef.entities = &backEndData->entities[r_firstSceneEntity]; - - tr.refdef.num_dlights = r_numdlights - r_firstSceneDlight; - tr.refdef.dlights = &backEndData->dlights[r_firstSceneDlight]; - - tr.refdef.numPolys = r_numpolys - r_firstScenePoly; - tr.refdef.polys = &backEndData->polys[r_firstScenePoly]; - - // turn off dynamic lighting globally by clearing all the - // dlights if it needs to be disabled or if vertex lighting is enabled - if ( r_dynamiclight->integer == 0 || - r_vertexLight->integer == 1 || - glConfig.hardwareType == GLHW_PERMEDIA2 ) { - tr.refdef.num_dlights = 0; - } - - // a single frame may have multiple scenes draw inside it -- - // a 3D game view, 3D status bar renderings, 3D menus, etc. - // They need to be distinguished by the light flare code, because - // the visibility state for a given surface may be different in - // each scene / view. - tr.frameSceneNum++; - tr.sceneCount++; - - // setup view parms for the initial view - // - // set up viewport - // The refdef takes 0-at-the-top y coordinates, so - // convert to GL's 0-at-the-bottom space - // - Com_Memset( &parms, 0, sizeof( parms ) ); - parms.viewportX = tr.refdef.x; - parms.viewportY = glConfig.vidHeight - ( tr.refdef.y + tr.refdef.height ); - parms.viewportWidth = tr.refdef.width; - parms.viewportHeight = tr.refdef.height; - parms.isPortal = qfalse; - - parms.fovX = tr.refdef.fov_x; - parms.fovY = tr.refdef.fov_y; - - parms.stereoFrame = tr.refdef.stereoFrame; - - VectorCopy( fd->vieworg, parms.or.origin ); - VectorCopy( fd->viewaxis[0], parms.or.axis[0] ); - VectorCopy( fd->viewaxis[1], parms.or.axis[1] ); - VectorCopy( fd->viewaxis[2], parms.or.axis[2] ); - - VectorCopy( fd->vieworg, parms.pvsOrigin ); - - R_RenderView( &parms ); - - // the next scene rendered in this frame will tack on after this one - r_firstSceneDrawSurf = tr.refdef.numDrawSurfs; - r_firstSceneEntity = r_numentities; - r_firstSceneDlight = r_numdlights; - r_firstScenePoly = r_numpolys; - - tr.frontEndMsec += ri.Milliseconds() - startTime; -} diff --git a/src/renderer/tr_shade.c b/src/renderer/tr_shade.c deleted file mode 100644 index b8e8d268..00000000 --- a/src/renderer/tr_shade.c +++ /dev/null @@ -1,1528 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_shade.c - -#include "tr_local.h" -#if idppc_altivec && !defined(MACOS_X) -#include -#endif - -/* - - THIS ENTIRE FILE IS BACK END - - This file deals with applying shaders to surface data in the tess struct. -*/ - -/* -================ -R_ArrayElementDiscrete - -This is just for OpenGL conformance testing, it should never be the fastest -================ -*/ -static void APIENTRY R_ArrayElementDiscrete( GLint index ) { - qglColor4ubv( tess.svars.colors[ index ] ); - if ( glState.currenttmu ) { - qglMultiTexCoord2fARB( 0, tess.svars.texcoords[ 0 ][ index ][0], tess.svars.texcoords[ 0 ][ index ][1] ); - qglMultiTexCoord2fARB( 1, tess.svars.texcoords[ 1 ][ index ][0], tess.svars.texcoords[ 1 ][ index ][1] ); - } else { - qglTexCoord2fv( tess.svars.texcoords[ 0 ][ index ] ); - } - qglVertex3fv( tess.xyz[ index ] ); -} - -/* -=================== -R_DrawStripElements - -=================== -*/ -static int c_vertexes; // for seeing how long our average strips are -static int c_begins; -static void R_DrawStripElements( int numIndexes, const glIndex_t *indexes, void ( APIENTRY *element )(GLint) ) { - int i; - int last[3] = { -1, -1, -1 }; - qboolean even; - - c_begins++; - - if ( numIndexes <= 0 ) { - return; - } - - qglBegin( GL_TRIANGLE_STRIP ); - - // prime the strip - element( indexes[0] ); - element( indexes[1] ); - element( indexes[2] ); - c_vertexes += 3; - - last[0] = indexes[0]; - last[1] = indexes[1]; - last[2] = indexes[2]; - - even = qfalse; - - for ( i = 3; i < numIndexes; i += 3 ) - { - // odd numbered triangle in potential strip - if ( !even ) - { - // check previous triangle to see if we're continuing a strip - if ( ( indexes[i+0] == last[2] ) && ( indexes[i+1] == last[1] ) ) - { - element( indexes[i+2] ); - c_vertexes++; - assert( indexes[i+2] < tess.numVertexes ); - even = qtrue; - } - // otherwise we're done with this strip so finish it and start - // a new one - else - { - qglEnd(); - - qglBegin( GL_TRIANGLE_STRIP ); - c_begins++; - - element( indexes[i+0] ); - element( indexes[i+1] ); - element( indexes[i+2] ); - - c_vertexes += 3; - - even = qfalse; - } - } - else - { - // check previous triangle to see if we're continuing a strip - if ( ( last[2] == indexes[i+1] ) && ( last[0] == indexes[i+0] ) ) - { - element( indexes[i+2] ); - c_vertexes++; - - even = qfalse; - } - // otherwise we're done with this strip so finish it and start - // a new one - else - { - qglEnd(); - - qglBegin( GL_TRIANGLE_STRIP ); - c_begins++; - - element( indexes[i+0] ); - element( indexes[i+1] ); - element( indexes[i+2] ); - c_vertexes += 3; - - even = qfalse; - } - } - - // cache the last three vertices - last[0] = indexes[i+0]; - last[1] = indexes[i+1]; - last[2] = indexes[i+2]; - } - - qglEnd(); -} - - - -/* -================== -R_DrawElements - -Optionally performs our own glDrawElements that looks for strip conditions -instead of using the single glDrawElements call that may be inefficient -without compiled vertex arrays. -================== -*/ -static void R_DrawElements( int numIndexes, const glIndex_t *indexes ) { - int primitives; - - primitives = r_primitives->integer; - - // default is to use triangles if compiled vertex arrays are present - if ( primitives == 0 ) { - if ( qglLockArraysEXT ) { - primitives = 2; - } else { - primitives = 1; - } - } - - - if ( primitives == 2 ) { - qglDrawElements( GL_TRIANGLES, - numIndexes, - GL_INDEX_TYPE, - indexes ); - return; - } - - if ( primitives == 1 ) { - R_DrawStripElements( numIndexes, indexes, qglArrayElement ); - return; - } - - if ( primitives == 3 ) { - R_DrawStripElements( numIndexes, indexes, R_ArrayElementDiscrete ); - return; - } - - // anything else will cause no drawing -} - - -/* -============================================================= - -SURFACE SHADERS - -============================================================= -*/ - -shaderCommands_t tess; -static qboolean setArraysOnce; - -/* -================= -R_BindAnimatedImage - -================= -*/ -static void R_BindAnimatedImage( textureBundle_t *bundle ) { - int index; - - if ( bundle->isVideoMap ) { - ri.CIN_RunCinematic(bundle->videoMapHandle); - ri.CIN_UploadCinematic(bundle->videoMapHandle); - return; - } - - if ( bundle->numImageAnimations <= 1 ) { - GL_Bind( bundle->image[0] ); - return; - } - - // it is necessary to do this messy calc to make sure animations line up - // exactly with waveforms of the same frequency - index = ri.ftol(tess.shaderTime * bundle->imageAnimationSpeed * FUNCTABLE_SIZE); - index >>= FUNCTABLE_SIZE2; - - if ( index < 0 ) { - index = 0; // may happen with shader time offsets - } - index %= bundle->numImageAnimations; - - GL_Bind( bundle->image[ index ] ); -} - -/* -================ -DrawTris - -Draws triangle outlines for debugging -================ -*/ -static void DrawTris (shaderCommands_t *input) { - GL_Bind( tr.whiteImage ); - qglColor3f (1,1,1); - - GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE ); - qglDepthRange( 0, 0 ); - - qglDisableClientState (GL_COLOR_ARRAY); - qglDisableClientState (GL_TEXTURE_COORD_ARRAY); - - qglVertexPointer (3, GL_FLOAT, 16, input->xyz); // padded for SIMD - - if (qglLockArraysEXT) { - qglLockArraysEXT(0, input->numVertexes); - GLimp_LogComment( "glLockArraysEXT\n" ); - } - - R_DrawElements( input->numIndexes, input->indexes ); - - if (qglUnlockArraysEXT) { - qglUnlockArraysEXT(); - GLimp_LogComment( "glUnlockArraysEXT\n" ); - } - qglDepthRange( 0, 1 ); -} - - -/* -================ -DrawNormals - -Draws vertex normals for debugging -================ -*/ -static void DrawNormals (shaderCommands_t *input) { - int i; - vec3_t temp; - - GL_Bind( tr.whiteImage ); - qglColor3f (1,1,1); - qglDepthRange( 0, 0 ); // never occluded - GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE ); - - qglBegin (GL_LINES); - for (i = 0 ; i < input->numVertexes ; i++) { - qglVertex3fv (input->xyz[i]); - VectorMA (input->xyz[i], 2, input->normal[i], temp); - qglVertex3fv (temp); - } - qglEnd (); - - qglDepthRange( 0, 1 ); -} - -/* -============== -RB_BeginSurface - -We must set some things up before beginning any tesselation, -because a surface may be forced to perform a RB_End due -to overflow. -============== -*/ -void RB_BeginSurface( shader_t *shader, int fogNum ) { - - shader_t *state = (shader->remappedShader) ? shader->remappedShader : shader; - - tess.numIndexes = 0; - tess.numVertexes = 0; - tess.shader = state; - tess.fogNum = fogNum; - tess.dlightBits = 0; // will be OR'd in by surface functions - tess.xstages = state->stages; - tess.numPasses = state->numUnfoggedPasses; - tess.currentStageIteratorFunc = state->optimalStageIteratorFunc; - - tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; - if (tess.shader->clampTime && tess.shaderTime >= tess.shader->clampTime) { - tess.shaderTime = tess.shader->clampTime; - } - - -} - -/* -=================== -DrawMultitextured - -output = t0 * t1 or t0 + t1 - -t0 = most upstream according to spec -t1 = most downstream according to spec -=================== -*/ -static void DrawMultitextured( shaderCommands_t *input, int stage ) { - shaderStage_t *pStage; - - pStage = tess.xstages[stage]; - - GL_State( pStage->stateBits ); - - // this is an ugly hack to work around a GeForce driver - // bug with multitexture and clip planes - if ( backEnd.viewParms.isPortal ) { - qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); - } - - // - // base - // - GL_SelectTexture( 0 ); - qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[0] ); - R_BindAnimatedImage( &pStage->bundle[0] ); - - // - // lightmap/secondary pass - // - GL_SelectTexture( 1 ); - qglEnable( GL_TEXTURE_2D ); - qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); - - if ( r_lightmap->integer ) { - GL_TexEnv( GL_REPLACE ); - } else { - GL_TexEnv( tess.shader->multitextureEnv ); - } - - qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[1] ); - - R_BindAnimatedImage( &pStage->bundle[1] ); - - R_DrawElements( input->numIndexes, input->indexes ); - - // - // disable texturing on TEXTURE1, then select TEXTURE0 - // - //qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); - qglDisable( GL_TEXTURE_2D ); - - GL_SelectTexture( 0 ); -} - - - -/* -=================== -ProjectDlightTexture - -Perform dynamic lighting with another rendering pass -=================== -*/ -#if idppc_altivec -static void ProjectDlightTexture_altivec( void ) { - int i, l; - vec_t origin0, origin1, origin2; - float texCoords0, texCoords1; - vector float floatColorVec0, floatColorVec1; - vector float modulateVec, colorVec, zero; - vector short colorShort; - vector signed int colorInt; - vector unsigned char floatColorVecPerm, modulatePerm, colorChar; - vector unsigned char vSel = VECCONST_UINT8(0x00, 0x00, 0x00, 0xff, - 0x00, 0x00, 0x00, 0xff, - 0x00, 0x00, 0x00, 0xff, - 0x00, 0x00, 0x00, 0xff); - float *texCoords; - byte *colors; - byte clipBits[SHADER_MAX_VERTEXES]; - float texCoordsArray[SHADER_MAX_VERTEXES][2]; - byte colorArray[SHADER_MAX_VERTEXES][4]; - unsigned hitIndexes[SHADER_MAX_INDEXES]; - int numIndexes; - float scale; - float radius; - vec3_t floatColor; - float modulate = 0.0f; - - if ( !backEnd.refdef.num_dlights ) { - return; - } - - // There has to be a better way to do this so that floatColor - // and/or modulate are already 16-byte aligned. - floatColorVecPerm = vec_lvsl(0,(float *)floatColor); - modulatePerm = vec_lvsl(0,(float *)&modulate); - modulatePerm = (vector unsigned char)vec_splat((vector unsigned int)modulatePerm,0); - zero = (vector float)vec_splat_s8(0); - - for ( l = 0 ; l < backEnd.refdef.num_dlights ; l++ ) { - dlight_t *dl; - - if ( !( tess.dlightBits & ( 1 << l ) ) ) { - continue; // this surface definately doesn't have any of this light - } - texCoords = texCoordsArray[0]; - colors = colorArray[0]; - - dl = &backEnd.refdef.dlights[l]; - origin0 = dl->transformed[0]; - origin1 = dl->transformed[1]; - origin2 = dl->transformed[2]; - radius = dl->radius; - scale = 1.0f / radius; - - if(r_greyscale->integer) - { - float luminance; - - luminance = LUMA(dl->color[0], dl->color[1], dl->color[2]) * 255.0f; - floatColor[0] = floatColor[1] = floatColor[2] = luminance; - } - else if(r_greyscale->value) - { - float luminance; - - luminance = LUMA(dl->color[0], dl->color[1], dl->color[2]) * 255.0f; - floatColor[0] = LERP(dl->color[0] * 255.0f, luminance, r_greyscale->value); - floatColor[1] = LERP(dl->color[1] * 255.0f, luminance, r_greyscale->value); - floatColor[2] = LERP(dl->color[2] * 255.0f, luminance, r_greyscale->value); - } - else - { - floatColor[0] = dl->color[0] * 255.0f; - floatColor[1] = dl->color[1] * 255.0f; - floatColor[2] = dl->color[2] * 255.0f; - } - floatColorVec0 = vec_ld(0, floatColor); - floatColorVec1 = vec_ld(11, floatColor); - floatColorVec0 = vec_perm(floatColorVec0,floatColorVec0,floatColorVecPerm); - for ( i = 0 ; i < tess.numVertexes ; i++, texCoords += 2, colors += 4 ) { - int clip = 0; - vec_t dist0, dist1, dist2; - - dist0 = origin0 - tess.xyz[i][0]; - dist1 = origin1 - tess.xyz[i][1]; - dist2 = origin2 - tess.xyz[i][2]; - - backEnd.pc.c_dlightVertexes++; - - texCoords0 = 0.5f + dist0 * scale; - texCoords1 = 0.5f + dist1 * scale; - - if( !r_dlightBacks->integer && - // dist . tess.normal[i] - ( dist0 * tess.normal[i][0] + - dist1 * tess.normal[i][1] + - dist2 * tess.normal[i][2] ) < 0.0f ) { - clip = 63; - } else { - if ( texCoords0 < 0.0f ) { - clip |= 1; - } else if ( texCoords0 > 1.0f ) { - clip |= 2; - } - if ( texCoords1 < 0.0f ) { - clip |= 4; - } else if ( texCoords1 > 1.0f ) { - clip |= 8; - } - texCoords[0] = texCoords0; - texCoords[1] = texCoords1; - - // modulate the strength based on the height and color - if ( dist2 > radius ) { - clip |= 16; - modulate = 0.0f; - } else if ( dist2 < -radius ) { - clip |= 32; - modulate = 0.0f; - } else { - dist2 = Q_fabs(dist2); - if ( dist2 < radius * 0.5f ) { - modulate = 1.0f; - } else { - modulate = 2.0f * (radius - dist2) * scale; - } - } - } - clipBits[i] = clip; - - modulateVec = vec_ld(0,(float *)&modulate); - modulateVec = vec_perm(modulateVec,modulateVec,modulatePerm); - colorVec = vec_madd(floatColorVec0,modulateVec,zero); - colorInt = vec_cts(colorVec,0); // RGBx - colorShort = vec_pack(colorInt,colorInt); // RGBxRGBx - colorChar = vec_packsu(colorShort,colorShort); // RGBxRGBxRGBxRGBx - colorChar = vec_sel(colorChar,vSel,vSel); // RGBARGBARGBARGBA replace alpha with 255 - vec_ste((vector unsigned int)colorChar,0,(unsigned int *)colors); // store color - } - - // build a list of triangles that need light - numIndexes = 0; - for ( i = 0 ; i < tess.numIndexes ; i += 3 ) { - int a, b, c; - - a = tess.indexes[i]; - b = tess.indexes[i+1]; - c = tess.indexes[i+2]; - if ( clipBits[a] & clipBits[b] & clipBits[c] ) { - continue; // not lighted - } - hitIndexes[numIndexes] = a; - hitIndexes[numIndexes+1] = b; - hitIndexes[numIndexes+2] = c; - numIndexes += 3; - } - - if ( !numIndexes ) { - continue; - } - - qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); - qglTexCoordPointer( 2, GL_FLOAT, 0, texCoordsArray[0] ); - - qglEnableClientState( GL_COLOR_ARRAY ); - qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, colorArray ); - - GL_Bind( tr.dlightImage ); - // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light - // where they aren't rendered - if ( dl->additive ) { - GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); - } - else { - GL_State( GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); - } - R_DrawElements( numIndexes, hitIndexes ); - backEnd.pc.c_totalIndexes += numIndexes; - backEnd.pc.c_dlightIndexes += numIndexes; - } -} -#endif - - -static void ProjectDlightTexture_scalar( void ) { - int i, l; - vec3_t origin; - float *texCoords; - byte *colors; - byte clipBits[SHADER_MAX_VERTEXES]; - float texCoordsArray[SHADER_MAX_VERTEXES][2]; - byte colorArray[SHADER_MAX_VERTEXES][4]; - unsigned hitIndexes[SHADER_MAX_INDEXES]; - int numIndexes; - float scale; - float radius; - vec3_t floatColor; - float modulate = 0.0f; - - if ( !backEnd.refdef.num_dlights ) { - return; - } - - for ( l = 0 ; l < backEnd.refdef.num_dlights ; l++ ) { - dlight_t *dl; - - if ( !( tess.dlightBits & ( 1 << l ) ) ) { - continue; // this surface definately doesn't have any of this light - } - texCoords = texCoordsArray[0]; - colors = colorArray[0]; - - dl = &backEnd.refdef.dlights[l]; - VectorCopy( dl->transformed, origin ); - radius = dl->radius; - scale = 1.0f / radius; - - if(r_greyscale->integer) - { - float luminance; - - luminance = LUMA(dl->color[0], dl->color[1], dl->color[2]) * 255.0f; - floatColor[0] = floatColor[1] = floatColor[2] = luminance; - } - else if(r_greyscale->value) - { - float luminance; - - luminance = LUMA(dl->color[0], dl->color[1], dl->color[2]) * 255.0f; - floatColor[0] = LERP(dl->color[0] * 255.0f, luminance, r_greyscale->value); - floatColor[1] = LERP(dl->color[1] * 255.0f, luminance, r_greyscale->value); - floatColor[2] = LERP(dl->color[2] * 255.0f, luminance, r_greyscale->value); - } - else - { - floatColor[0] = dl->color[0] * 255.0f; - floatColor[1] = dl->color[1] * 255.0f; - floatColor[2] = dl->color[2] * 255.0f; - } - - for ( i = 0 ; i < tess.numVertexes ; i++, texCoords += 2, colors += 4 ) { - int clip = 0; - vec3_t dist; - - VectorSubtract( origin, tess.xyz[i], dist ); - - backEnd.pc.c_dlightVertexes++; - - texCoords[0] = 0.5f + dist[0] * scale; - texCoords[1] = 0.5f + dist[1] * scale; - - if( !r_dlightBacks->integer && - // dist . tess.normal[i] - ( dist[0] * tess.normal[i][0] + - dist[1] * tess.normal[i][1] + - dist[2] * tess.normal[i][2] ) < 0.0f ) { - clip = 63; - } else { - if ( texCoords[0] < 0.0f ) { - clip |= 1; - } else if ( texCoords[0] > 1.0f ) { - clip |= 2; - } - if ( texCoords[1] < 0.0f ) { - clip |= 4; - } else if ( texCoords[1] > 1.0f ) { - clip |= 8; - } - texCoords[0] = texCoords[0]; - texCoords[1] = texCoords[1]; - - // modulate the strength based on the height and color - if ( dist[2] > radius ) { - clip |= 16; - modulate = 0.0f; - } else if ( dist[2] < -radius ) { - clip |= 32; - modulate = 0.0f; - } else { - dist[2] = Q_fabs(dist[2]); - if ( dist[2] < radius * 0.5f ) { - modulate = 1.0f; - } else { - modulate = 2.0f * (radius - dist[2]) * scale; - } - } - } - clipBits[i] = clip; - colors[0] = ri.ftol(floatColor[0] * modulate); - colors[1] = ri.ftol(floatColor[1] * modulate); - colors[2] = ri.ftol(floatColor[2] * modulate); - colors[3] = 255; - } - - // build a list of triangles that need light - numIndexes = 0; - for ( i = 0 ; i < tess.numIndexes ; i += 3 ) { - int a, b, c; - - a = tess.indexes[i]; - b = tess.indexes[i+1]; - c = tess.indexes[i+2]; - if ( clipBits[a] & clipBits[b] & clipBits[c] ) { - continue; // not lighted - } - hitIndexes[numIndexes] = a; - hitIndexes[numIndexes+1] = b; - hitIndexes[numIndexes+2] = c; - numIndexes += 3; - } - - if ( !numIndexes ) { - continue; - } - - qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); - qglTexCoordPointer( 2, GL_FLOAT, 0, texCoordsArray[0] ); - - qglEnableClientState( GL_COLOR_ARRAY ); - qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, colorArray ); - - GL_Bind( tr.dlightImage ); - // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light - // where they aren't rendered - if ( dl->additive ) { - GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); - } - else { - GL_State( GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); - } - R_DrawElements( numIndexes, hitIndexes ); - backEnd.pc.c_totalIndexes += numIndexes; - backEnd.pc.c_dlightIndexes += numIndexes; - } -} - -static void ProjectDlightTexture( void ) { -#if idppc_altivec - if (com_altivec->integer) { - // must be in a seperate function or G3 systems will crash. - ProjectDlightTexture_altivec(); - return; - } -#endif - ProjectDlightTexture_scalar(); -} - - -/* -=================== -RB_FogPass - -Blends a fog texture on top of everything else -=================== -*/ -static void RB_FogPass( void ) { - fog_t *fog; - int i; - - qglEnableClientState( GL_COLOR_ARRAY ); - qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors ); - - qglEnableClientState( GL_TEXTURE_COORD_ARRAY); - qglTexCoordPointer( 2, GL_FLOAT, 0, tess.svars.texcoords[0] ); - - fog = tr.world->fogs + tess.fogNum; - - for ( i = 0; i < tess.numVertexes; i++ ) { - * ( int * )&tess.svars.colors[i] = fog->colorInt; - } - - RB_CalcFogTexCoords( ( float * ) tess.svars.texcoords[0] ); - - GL_Bind( tr.fogImage ); - - if ( tess.shader->fogPass == FP_EQUAL ) { - GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_EQUAL ); - } else { - GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); - } - - R_DrawElements( tess.numIndexes, tess.indexes ); -} - -/* -=============== -ComputeColors -=============== -*/ -static void ComputeColors( shaderStage_t *pStage ) -{ - int i; - - // - // rgbGen - // - switch ( pStage->rgbGen ) - { - case CGEN_IDENTITY: - Com_Memset( tess.svars.colors, 0xff, tess.numVertexes * 4 ); - break; - default: - case CGEN_IDENTITY_LIGHTING: - Com_Memset( tess.svars.colors, tr.identityLightByte, tess.numVertexes * 4 ); - break; - case CGEN_LIGHTING_DIFFUSE: - RB_CalcDiffuseColor( ( unsigned char * ) tess.svars.colors ); - break; - case CGEN_EXACT_VERTEX: - Com_Memcpy( tess.svars.colors, tess.vertexColors, tess.numVertexes * sizeof( tess.vertexColors[0] ) ); - break; - case CGEN_CONST: - for ( i = 0; i < tess.numVertexes; i++ ) { - *(int *)tess.svars.colors[i] = *(int *)pStage->constantColor; - } - break; - case CGEN_VERTEX: - if ( tr.identityLight == 1 ) - { - Com_Memcpy( tess.svars.colors, tess.vertexColors, tess.numVertexes * sizeof( tess.vertexColors[0] ) ); - } - else - { - for ( i = 0; i < tess.numVertexes; i++ ) - { - tess.svars.colors[i][0] = tess.vertexColors[i][0] * tr.identityLight; - tess.svars.colors[i][1] = tess.vertexColors[i][1] * tr.identityLight; - tess.svars.colors[i][2] = tess.vertexColors[i][2] * tr.identityLight; - tess.svars.colors[i][3] = tess.vertexColors[i][3]; - } - } - break; - case CGEN_ONE_MINUS_VERTEX: - if ( tr.identityLight == 1 ) - { - for ( i = 0; i < tess.numVertexes; i++ ) - { - tess.svars.colors[i][0] = 255 - tess.vertexColors[i][0]; - tess.svars.colors[i][1] = 255 - tess.vertexColors[i][1]; - tess.svars.colors[i][2] = 255 - tess.vertexColors[i][2]; - } - } - else - { - for ( i = 0; i < tess.numVertexes; i++ ) - { - tess.svars.colors[i][0] = ( 255 - tess.vertexColors[i][0] ) * tr.identityLight; - tess.svars.colors[i][1] = ( 255 - tess.vertexColors[i][1] ) * tr.identityLight; - tess.svars.colors[i][2] = ( 255 - tess.vertexColors[i][2] ) * tr.identityLight; - } - } - break; - case CGEN_FOG: - { - fog_t *fog; - - fog = tr.world->fogs + tess.fogNum; - - for ( i = 0; i < tess.numVertexes; i++ ) { - * ( int * )&tess.svars.colors[i] = fog->colorInt; - } - } - break; - case CGEN_WAVEFORM: - RB_CalcWaveColor( &pStage->rgbWave, ( unsigned char * ) tess.svars.colors ); - break; - case CGEN_ENTITY: - RB_CalcColorFromEntity( ( unsigned char * ) tess.svars.colors ); - break; - case CGEN_ONE_MINUS_ENTITY: - RB_CalcColorFromOneMinusEntity( ( unsigned char * ) tess.svars.colors ); - break; - } - - // - // alphaGen - // - switch ( pStage->alphaGen ) - { - case AGEN_SKIP: - break; - case AGEN_IDENTITY: - if ( pStage->rgbGen != CGEN_IDENTITY ) { - if ( ( pStage->rgbGen == CGEN_VERTEX && tr.identityLight != 1 ) || - pStage->rgbGen != CGEN_VERTEX ) { - for ( i = 0; i < tess.numVertexes; i++ ) { - tess.svars.colors[i][3] = 0xff; - } - } - } - break; - case AGEN_CONST: - if ( pStage->rgbGen != CGEN_CONST ) { - for ( i = 0; i < tess.numVertexes; i++ ) { - tess.svars.colors[i][3] = pStage->constantColor[3]; - } - } - break; - case AGEN_WAVEFORM: - RB_CalcWaveAlpha( &pStage->alphaWave, ( unsigned char * ) tess.svars.colors ); - break; - case AGEN_LIGHTING_SPECULAR: - RB_CalcSpecularAlpha( ( unsigned char * ) tess.svars.colors ); - break; - case AGEN_ENTITY: - RB_CalcAlphaFromEntity( ( unsigned char * ) tess.svars.colors ); - break; - case AGEN_ONE_MINUS_ENTITY: - RB_CalcAlphaFromOneMinusEntity( ( unsigned char * ) tess.svars.colors ); - break; - case AGEN_VERTEX: - if ( pStage->rgbGen != CGEN_VERTEX ) { - for ( i = 0; i < tess.numVertexes; i++ ) { - tess.svars.colors[i][3] = tess.vertexColors[i][3]; - } - } - break; - case AGEN_ONE_MINUS_VERTEX: - for ( i = 0; i < tess.numVertexes; i++ ) - { - tess.svars.colors[i][3] = 255 - tess.vertexColors[i][3]; - } - break; - case AGEN_PORTAL: - { - unsigned char alpha; - - for ( i = 0; i < tess.numVertexes; i++ ) - { - float len; - vec3_t v; - - VectorSubtract( tess.xyz[i], backEnd.viewParms.or.origin, v ); - len = VectorLength( v ); - - len /= tess.shader->portalRange; - - if ( len < 0 ) - { - alpha = 0; - } - else if ( len > 1 ) - { - alpha = 0xff; - } - else - { - alpha = len * 0xff; - } - - tess.svars.colors[i][3] = alpha; - } - } - break; - } - - // - // fog adjustment for colors to fade out as fog increases - // - if ( tess.fogNum ) - { - switch ( pStage->adjustColorsForFog ) - { - case ACFF_MODULATE_RGB: - RB_CalcModulateColorsByFog( ( unsigned char * ) tess.svars.colors ); - break; - case ACFF_MODULATE_ALPHA: - RB_CalcModulateAlphasByFog( ( unsigned char * ) tess.svars.colors ); - break; - case ACFF_MODULATE_RGBA: - RB_CalcModulateRGBAsByFog( ( unsigned char * ) tess.svars.colors ); - break; - case ACFF_NONE: - break; - } - } - - // if in greyscale rendering mode turn all color values into greyscale. - if(r_greyscale->integer) - { - int scale; - for(i = 0; i < tess.numVertexes; i++) - { - scale = LUMA(tess.svars.colors[i][0], tess.svars.colors[i][1], tess.svars.colors[i][2]); - tess.svars.colors[i][0] = tess.svars.colors[i][1] = tess.svars.colors[i][2] = scale; - } - } - else if(r_greyscale->value) - { - float scale; - - for(i = 0; i < tess.numVertexes; i++) - { - scale = LUMA(tess.svars.colors[i][0], tess.svars.colors[i][1], tess.svars.colors[i][2]); - tess.svars.colors[i][0] = LERP(tess.svars.colors[i][0], scale, r_greyscale->value); - tess.svars.colors[i][1] = LERP(tess.svars.colors[i][1], scale, r_greyscale->value); - tess.svars.colors[i][2] = LERP(tess.svars.colors[i][2], scale, r_greyscale->value); - } - } -} - -/* -=============== -ComputeTexCoords -=============== -*/ -static void ComputeTexCoords( shaderStage_t *pStage ) { - int i; - int b; - - for ( b = 0; b < NUM_TEXTURE_BUNDLES; b++ ) { - int tm; - - // - // generate the texture coordinates - // - switch ( pStage->bundle[b].tcGen ) - { - case TCGEN_IDENTITY: - Com_Memset( tess.svars.texcoords[b], 0, sizeof( float ) * 2 * tess.numVertexes ); - break; - case TCGEN_TEXTURE: - for ( i = 0 ; i < tess.numVertexes ; i++ ) { - tess.svars.texcoords[b][i][0] = tess.texCoords[i][0][0]; - tess.svars.texcoords[b][i][1] = tess.texCoords[i][0][1]; - } - break; - case TCGEN_LIGHTMAP: - for ( i = 0 ; i < tess.numVertexes ; i++ ) { - tess.svars.texcoords[b][i][0] = tess.texCoords[i][1][0]; - tess.svars.texcoords[b][i][1] = tess.texCoords[i][1][1]; - } - break; - case TCGEN_VECTOR: - for ( i = 0 ; i < tess.numVertexes ; i++ ) { - tess.svars.texcoords[b][i][0] = DotProduct( tess.xyz[i], pStage->bundle[b].tcGenVectors[0] ); - tess.svars.texcoords[b][i][1] = DotProduct( tess.xyz[i], pStage->bundle[b].tcGenVectors[1] ); - } - break; - case TCGEN_FOG: - RB_CalcFogTexCoords( ( float * ) tess.svars.texcoords[b] ); - break; - case TCGEN_ENVIRONMENT_MAPPED: - RB_CalcEnvironmentTexCoords( ( float * ) tess.svars.texcoords[b] ); - break; - case TCGEN_BAD: - return; - } - - // - // alter texture coordinates - // - for ( tm = 0; tm < pStage->bundle[b].numTexMods ; tm++ ) { - switch ( pStage->bundle[b].texMods[tm].type ) - { - case TMOD_NONE: - tm = TR_MAX_TEXMODS; // break out of for loop - break; - - case TMOD_TURBULENT: - RB_CalcTurbulentTexCoords( &pStage->bundle[b].texMods[tm].wave, - ( float * ) tess.svars.texcoords[b] ); - break; - - case TMOD_ENTITY_TRANSLATE: - RB_CalcScrollTexCoords( backEnd.currentEntity->e.shaderTexCoord, - ( float * ) tess.svars.texcoords[b] ); - break; - - case TMOD_SCROLL: - RB_CalcScrollTexCoords( pStage->bundle[b].texMods[tm].scroll, - ( float * ) tess.svars.texcoords[b] ); - break; - - case TMOD_SCALE: - RB_CalcScaleTexCoords( pStage->bundle[b].texMods[tm].scale, - ( float * ) tess.svars.texcoords[b] ); - break; - - case TMOD_STRETCH: - RB_CalcStretchTexCoords( &pStage->bundle[b].texMods[tm].wave, - ( float * ) tess.svars.texcoords[b] ); - break; - - case TMOD_TRANSFORM: - RB_CalcTransformTexCoords( &pStage->bundle[b].texMods[tm], - ( float * ) tess.svars.texcoords[b] ); - break; - - case TMOD_ROTATE: - RB_CalcRotateTexCoords( pStage->bundle[b].texMods[tm].rotateSpeed, - ( float * ) tess.svars.texcoords[b] ); - break; - - default: - ri.Error( ERR_DROP, "ERROR: unknown texmod '%d' in shader '%s'", pStage->bundle[b].texMods[tm].type, tess.shader->name ); - break; - } - } - } -} - -/* -** RB_IterateStagesGeneric -*/ -static void RB_IterateStagesGeneric( shaderCommands_t *input ) -{ - int stage; - - for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) - { - shaderStage_t *pStage = tess.xstages[stage]; - - if ( !pStage ) - { - break; - } - - ComputeColors( pStage ); - ComputeTexCoords( pStage ); - - if ( !setArraysOnce ) - { - qglEnableClientState( GL_COLOR_ARRAY ); - qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, input->svars.colors ); - } - - // - // do multitexture - // - if ( pStage->bundle[1].image[0] != 0 ) - { - DrawMultitextured( input, stage ); - } - else - { - if ( !setArraysOnce ) - { - qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[0] ); - } - - // - // set state - // - if ( pStage->bundle[0].vertexLightmap && ( (r_vertexLight->integer && !r_uiFullScreen->integer) || glConfig.hardwareType == GLHW_PERMEDIA2 ) && r_lightmap->integer ) - { - GL_Bind( tr.whiteImage ); - } - else - R_BindAnimatedImage( &pStage->bundle[0] ); - - GL_State( pStage->stateBits ); - - // - // draw - // - R_DrawElements( input->numIndexes, input->indexes ); - } - // allow skipping out to show just lightmaps during development - if ( r_lightmap->integer && ( pStage->bundle[0].isLightmap || pStage->bundle[1].isLightmap || pStage->bundle[0].vertexLightmap ) ) - { - break; - } - } -} - - -/* -** RB_StageIteratorGeneric -*/ -void RB_StageIteratorGeneric( void ) -{ - shaderCommands_t *input; - shader_t *shader; - - input = &tess; - shader = input->shader; - - RB_DeformTessGeometry(); - - // - // log this call - // - if ( r_logFile->integer ) - { - // don't just call LogComment, or we will get - // a call to va() every frame! - GLimp_LogComment( va("--- RB_StageIteratorGeneric( %s ) ---\n", tess.shader->name) ); - } - - // - // set face culling appropriately - // - GL_Cull( shader->cullType ); - - // set polygon offset if necessary - if ( shader->polygonOffset ) - { - qglEnable( GL_POLYGON_OFFSET_FILL ); - qglPolygonOffset( r_offsetFactor->value, r_offsetUnits->value ); - } - - // - // if there is only a single pass then we can enable color - // and texture arrays before we compile, otherwise we need - // to avoid compiling those arrays since they will change - // during multipass rendering - // - if ( tess.numPasses > 1 || shader->multitextureEnv ) - { - setArraysOnce = qfalse; - qglDisableClientState (GL_COLOR_ARRAY); - qglDisableClientState (GL_TEXTURE_COORD_ARRAY); - } - else - { - setArraysOnce = qtrue; - - qglEnableClientState( GL_COLOR_ARRAY); - qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors ); - - qglEnableClientState( GL_TEXTURE_COORD_ARRAY); - qglTexCoordPointer( 2, GL_FLOAT, 0, tess.svars.texcoords[0] ); - } - - // - // lock XYZ - // - qglVertexPointer (3, GL_FLOAT, 16, input->xyz); // padded for SIMD - if (qglLockArraysEXT) - { - qglLockArraysEXT(0, input->numVertexes); - GLimp_LogComment( "glLockArraysEXT\n" ); - } - - // - // enable color and texcoord arrays after the lock if necessary - // - if ( !setArraysOnce ) - { - qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); - qglEnableClientState( GL_COLOR_ARRAY ); - } - - // - // call shader function - // - RB_IterateStagesGeneric( input ); - - // - // now do any dynamic lighting needed - // - if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE - && !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) ) { - ProjectDlightTexture(); - } - - // - // now do fog - // - if ( tess.fogNum && tess.shader->fogPass ) { - RB_FogPass(); - } - - // - // unlock arrays - // - if (qglUnlockArraysEXT) - { - qglUnlockArraysEXT(); - GLimp_LogComment( "glUnlockArraysEXT\n" ); - } - - // - // reset polygon offset - // - if ( shader->polygonOffset ) - { - qglDisable( GL_POLYGON_OFFSET_FILL ); - } -} - - -/* -** RB_StageIteratorVertexLitTexture -*/ -void RB_StageIteratorVertexLitTexture( void ) -{ - shaderCommands_t *input; - shader_t *shader; - - input = &tess; - shader = input->shader; - - // - // compute colors - // - RB_CalcDiffuseColor( ( unsigned char * ) tess.svars.colors ); - - // - // log this call - // - if ( r_logFile->integer ) - { - // don't just call LogComment, or we will get - // a call to va() every frame! - GLimp_LogComment( va("--- RB_StageIteratorVertexLitTexturedUnfogged( %s ) ---\n", tess.shader->name) ); - } - - // - // set face culling appropriately - // - GL_Cull( shader->cullType ); - - // - // set arrays and lock - // - qglEnableClientState( GL_COLOR_ARRAY); - qglEnableClientState( GL_TEXTURE_COORD_ARRAY); - - qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors ); - qglTexCoordPointer( 2, GL_FLOAT, 16, tess.texCoords[0][0] ); - qglVertexPointer (3, GL_FLOAT, 16, input->xyz); - - if ( qglLockArraysEXT ) - { - qglLockArraysEXT(0, input->numVertexes); - GLimp_LogComment( "glLockArraysEXT\n" ); - } - - // - // call special shade routine - // - R_BindAnimatedImage( &tess.xstages[0]->bundle[0] ); - GL_State( tess.xstages[0]->stateBits ); - R_DrawElements( input->numIndexes, input->indexes ); - - // - // now do any dynamic lighting needed - // - if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE ) { - ProjectDlightTexture(); - } - - // - // now do fog - // - if ( tess.fogNum && tess.shader->fogPass ) { - RB_FogPass(); - } - - // - // unlock arrays - // - if (qglUnlockArraysEXT) - { - qglUnlockArraysEXT(); - GLimp_LogComment( "glUnlockArraysEXT\n" ); - } -} - -//define REPLACE_MODE - -void RB_StageIteratorLightmappedMultitexture( void ) { - shaderCommands_t *input; - shader_t *shader; - - input = &tess; - shader = input->shader; - - // - // log this call - // - if ( r_logFile->integer ) { - // don't just call LogComment, or we will get - // a call to va() every frame! - GLimp_LogComment( va("--- RB_StageIteratorLightmappedMultitexture( %s ) ---\n", tess.shader->name) ); - } - - // - // set face culling appropriately - // - GL_Cull( shader->cullType ); - - // - // set color, pointers, and lock - // - GL_State( GLS_DEFAULT ); - qglVertexPointer( 3, GL_FLOAT, 16, input->xyz ); - -#ifdef REPLACE_MODE - qglDisableClientState( GL_COLOR_ARRAY ); - qglColor3f( 1, 1, 1 ); - qglShadeModel( GL_FLAT ); -#else - qglEnableClientState( GL_COLOR_ARRAY ); - qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.constantColor255 ); -#endif - - // - // select base stage - // - GL_SelectTexture( 0 ); - - qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); - R_BindAnimatedImage( &tess.xstages[0]->bundle[0] ); - qglTexCoordPointer( 2, GL_FLOAT, 16, tess.texCoords[0][0] ); - - // - // configure second stage - // - GL_SelectTexture( 1 ); - qglEnable( GL_TEXTURE_2D ); - if ( r_lightmap->integer ) { - GL_TexEnv( GL_REPLACE ); - } else { - GL_TexEnv( GL_MODULATE ); - } - R_BindAnimatedImage( &tess.xstages[0]->bundle[1] ); - qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); - qglTexCoordPointer( 2, GL_FLOAT, 16, tess.texCoords[0][1] ); - - // - // lock arrays - // - if ( qglLockArraysEXT ) { - qglLockArraysEXT(0, input->numVertexes); - GLimp_LogComment( "glLockArraysEXT\n" ); - } - - R_DrawElements( input->numIndexes, input->indexes ); - - // - // disable texturing on TEXTURE1, then select TEXTURE0 - // - qglDisable( GL_TEXTURE_2D ); - qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); - - GL_SelectTexture( 0 ); -#ifdef REPLACE_MODE - GL_TexEnv( GL_MODULATE ); - qglShadeModel( GL_SMOOTH ); -#endif - - // - // now do any dynamic lighting needed - // - if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE ) { - ProjectDlightTexture(); - } - - // - // now do fog - // - if ( tess.fogNum && tess.shader->fogPass ) { - RB_FogPass(); - } - - // - // unlock arrays - // - if ( qglUnlockArraysEXT ) { - qglUnlockArraysEXT(); - GLimp_LogComment( "glUnlockArraysEXT\n" ); - } -} - -/* -** RB_EndSurface -*/ -void RB_EndSurface( void ) { - shaderCommands_t *input; - - input = &tess; - - if (input->numIndexes == 0) { - return; - } - - if (input->indexes[SHADER_MAX_INDEXES-1] != 0) { - ri.Error (ERR_DROP, "RB_EndSurface() - SHADER_MAX_INDEXES hit"); - } - if (input->xyz[SHADER_MAX_VERTEXES-1][0] != 0) { - ri.Error (ERR_DROP, "RB_EndSurface() - SHADER_MAX_VERTEXES hit"); - } - - if ( tess.shader == tr.shadowShader ) { - RB_ShadowTessEnd(); - return; - } - - // for debugging of sort order issues, stop rendering after a given sort value - if ( r_debugSort->integer && r_debugSort->integer < tess.shader->sort ) { - return; - } - - // - // update performance counters - // - backEnd.pc.c_shaders++; - backEnd.pc.c_vertexes += tess.numVertexes; - backEnd.pc.c_indexes += tess.numIndexes; - backEnd.pc.c_totalIndexes += tess.numIndexes * tess.numPasses; - - // - // call off to shader specific tess end function - // - tess.currentStageIteratorFunc(); - - // - // draw debugging stuff - // - if ( r_showtris->integer ) { - DrawTris (input); - } - if ( r_shownormals->integer ) { - DrawNormals (input); - } - // clear shader so we can tell we don't have any unclosed surfaces - tess.numIndexes = 0; - - GLimp_LogComment( "----------\n" ); -} - diff --git a/src/renderer/tr_shade_calc.c b/src/renderer/tr_shade_calc.c deleted file mode 100644 index 58ec6f45..00000000 --- a/src/renderer/tr_shade_calc.c +++ /dev/null @@ -1,1217 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_shade_calc.c - -#include "tr_local.h" -#if idppc_altivec && !defined(MACOS_X) -#include -#endif - - -#define WAVEVALUE( table, base, amplitude, phase, freq ) ((base) + table[ ri.ftol( ( ( (phase) + tess.shaderTime * (freq) ) * FUNCTABLE_SIZE ) ) & FUNCTABLE_MASK ] * (amplitude)) - -static float *TableForFunc( genFunc_t func ) -{ - switch ( func ) - { - case GF_SIN: - return tr.sinTable; - case GF_TRIANGLE: - return tr.triangleTable; - case GF_SQUARE: - return tr.squareTable; - case GF_SAWTOOTH: - return tr.sawToothTable; - case GF_INVERSE_SAWTOOTH: - return tr.inverseSawToothTable; - case GF_NONE: - default: - break; - } - - ri.Error( ERR_DROP, "TableForFunc called with invalid function '%d' in shader '%s'", func, tess.shader->name ); - return NULL; -} - -/* -** EvalWaveForm -** -** Evaluates a given waveForm_t, referencing backEnd.refdef.time directly -*/ -static float EvalWaveForm( const waveForm_t *wf ) -{ - float *table; - - table = TableForFunc( wf->func ); - - return WAVEVALUE( table, wf->base, wf->amplitude, wf->phase, wf->frequency ); -} - -static float EvalWaveFormClamped( const waveForm_t *wf ) -{ - float glow = EvalWaveForm( wf ); - - if ( glow < 0 ) - { - return 0; - } - - if ( glow > 1 ) - { - return 1; - } - - return glow; -} - -/* -** RB_CalcStretchTexCoords -*/ -void RB_CalcStretchTexCoords( const waveForm_t *wf, float *st ) -{ - float p; - texModInfo_t tmi; - - p = 1.0f / EvalWaveForm( wf ); - - tmi.matrix[0][0] = p; - tmi.matrix[1][0] = 0; - tmi.translate[0] = 0.5f - 0.5f * p; - - tmi.matrix[0][1] = 0; - tmi.matrix[1][1] = p; - tmi.translate[1] = 0.5f - 0.5f * p; - - RB_CalcTransformTexCoords( &tmi, st ); -} - -/* -==================================================================== - -DEFORMATIONS - -==================================================================== -*/ - -/* -======================== -RB_CalcDeformVertexes - -======================== -*/ -void RB_CalcDeformVertexes( deformStage_t *ds ) -{ - int i; - vec3_t offset; - float scale; - float *xyz = ( float * ) tess.xyz; - float *normal = ( float * ) tess.normal; - float *table; - - if ( ds->deformationWave.frequency == 0 ) - { - scale = EvalWaveForm( &ds->deformationWave ); - - for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 ) - { - VectorScale( normal, scale, offset ); - - xyz[0] += offset[0]; - xyz[1] += offset[1]; - xyz[2] += offset[2]; - } - } - else - { - table = TableForFunc( ds->deformationWave.func ); - - for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 ) - { - float off = ( xyz[0] + xyz[1] + xyz[2] ) * ds->deformationSpread; - - scale = WAVEVALUE( table, ds->deformationWave.base, - ds->deformationWave.amplitude, - ds->deformationWave.phase + off, - ds->deformationWave.frequency ); - - VectorScale( normal, scale, offset ); - - xyz[0] += offset[0]; - xyz[1] += offset[1]; - xyz[2] += offset[2]; - } - } -} - -/* -========================= -RB_CalcDeformNormals - -Wiggle the normals for wavy environment mapping -========================= -*/ -void RB_CalcDeformNormals( deformStage_t *ds ) { - int i; - float scale; - float *xyz = ( float * ) tess.xyz; - float *normal = ( float * ) tess.normal; - - for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 ) { - scale = 0.98f; - scale = R_NoiseGet4f( xyz[0] * scale, xyz[1] * scale, xyz[2] * scale, - tess.shaderTime * ds->deformationWave.frequency ); - normal[ 0 ] += ds->deformationWave.amplitude * scale; - - scale = 0.98f; - scale = R_NoiseGet4f( 100 + xyz[0] * scale, xyz[1] * scale, xyz[2] * scale, - tess.shaderTime * ds->deformationWave.frequency ); - normal[ 1 ] += ds->deformationWave.amplitude * scale; - - scale = 0.98f; - scale = R_NoiseGet4f( 200 + xyz[0] * scale, xyz[1] * scale, xyz[2] * scale, - tess.shaderTime * ds->deformationWave.frequency ); - normal[ 2 ] += ds->deformationWave.amplitude * scale; - - VectorNormalizeFast( normal ); - } -} - -/* -======================== -RB_CalcBulgeVertexes - -======================== -*/ -void RB_CalcBulgeVertexes( deformStage_t *ds ) { - int i; - const float *st = ( const float * ) tess.texCoords[0]; - float *xyz = ( float * ) tess.xyz; - float *normal = ( float * ) tess.normal; - float now; - - now = backEnd.refdef.time * ds->bulgeSpeed * 0.001f; - - for ( i = 0; i < tess.numVertexes; i++, xyz += 4, st += 4, normal += 4 ) { - int off; - float scale; - - off = (float)( FUNCTABLE_SIZE / (M_PI*2) ) * ( st[0] * ds->bulgeWidth + now ); - - scale = tr.sinTable[ off & FUNCTABLE_MASK ] * ds->bulgeHeight; - - xyz[0] += normal[0] * scale; - xyz[1] += normal[1] * scale; - xyz[2] += normal[2] * scale; - } -} - - -/* -====================== -RB_CalcMoveVertexes - -A deformation that can move an entire surface along a wave path -====================== -*/ -void RB_CalcMoveVertexes( deformStage_t *ds ) { - int i; - float *xyz; - float *table; - float scale; - vec3_t offset; - - table = TableForFunc( ds->deformationWave.func ); - - scale = WAVEVALUE( table, ds->deformationWave.base, - ds->deformationWave.amplitude, - ds->deformationWave.phase, - ds->deformationWave.frequency ); - - VectorScale( ds->moveVector, scale, offset ); - - xyz = ( float * ) tess.xyz; - for ( i = 0; i < tess.numVertexes; i++, xyz += 4 ) { - VectorAdd( xyz, offset, xyz ); - } -} - - -/* -============= -DeformText - -Change a polygon into a bunch of text polygons -============= -*/ -void DeformText( const char *text ) { - int i; - vec3_t origin, width, height; - int len; - int ch; - byte color[4]; - float bottom, top; - vec3_t mid; - - height[0] = 0; - height[1] = 0; - height[2] = -1; - CrossProduct( tess.normal[0], height, width ); - - // find the midpoint of the box - VectorClear( mid ); - bottom = 999999; - top = -999999; - for ( i = 0 ; i < 4 ; i++ ) { - VectorAdd( tess.xyz[i], mid, mid ); - if ( tess.xyz[i][2] < bottom ) { - bottom = tess.xyz[i][2]; - } - if ( tess.xyz[i][2] > top ) { - top = tess.xyz[i][2]; - } - } - VectorScale( mid, 0.25f, origin ); - - // determine the individual character size - height[0] = 0; - height[1] = 0; - height[2] = ( top - bottom ) * 0.5f; - - VectorScale( width, height[2] * -0.75f, width ); - - // determine the starting position - len = strlen( text ); - VectorMA( origin, (len-1), width, origin ); - - // clear the shader indexes - tess.numIndexes = 0; - tess.numVertexes = 0; - - color[0] = color[1] = color[2] = color[3] = 255; - - // draw each character - for ( i = 0 ; i < len ; i++ ) { - ch = text[i]; - ch &= 255; - - if ( ch != ' ' ) { - int row, col; - float frow, fcol, size; - - row = ch>>4; - col = ch&15; - - frow = row*0.0625f; - fcol = col*0.0625f; - size = 0.0625f; - - RB_AddQuadStampExt( origin, width, height, color, fcol, frow, fcol + size, frow + size ); - } - VectorMA( origin, -2, width, origin ); - } -} - -/* -================== -GlobalVectorToLocal -================== -*/ -static void GlobalVectorToLocal( const vec3_t in, vec3_t out ) { - out[0] = DotProduct( in, backEnd.or.axis[0] ); - out[1] = DotProduct( in, backEnd.or.axis[1] ); - out[2] = DotProduct( in, backEnd.or.axis[2] ); -} - -/* -===================== -AutospriteDeform - -Assuming all the triangles for this shader are independant -quads, rebuild them as forward facing sprites -===================== -*/ -static void AutospriteDeform( void ) { - int i; - int oldVerts; - float *xyz; - vec3_t mid, delta; - float radius; - vec3_t left, up; - vec3_t leftDir, upDir; - - if ( tess.numVertexes & 3 ) { - ri.Printf( PRINT_WARNING, "Autosprite shader %s had odd vertex count\n", tess.shader->name ); - } - if ( tess.numIndexes != ( tess.numVertexes >> 2 ) * 6 ) { - ri.Printf( PRINT_WARNING, "Autosprite shader %s had odd index count\n", tess.shader->name ); - } - - oldVerts = tess.numVertexes; - tess.numVertexes = 0; - tess.numIndexes = 0; - - if ( backEnd.currentEntity != &tr.worldEntity ) { - GlobalVectorToLocal( backEnd.viewParms.or.axis[1], leftDir ); - GlobalVectorToLocal( backEnd.viewParms.or.axis[2], upDir ); - } else { - VectorCopy( backEnd.viewParms.or.axis[1], leftDir ); - VectorCopy( backEnd.viewParms.or.axis[2], upDir ); - } - - for ( i = 0 ; i < oldVerts ; i+=4 ) { - // find the midpoint - xyz = tess.xyz[i]; - - mid[0] = 0.25f * (xyz[0] + xyz[4] + xyz[8] + xyz[12]); - mid[1] = 0.25f * (xyz[1] + xyz[5] + xyz[9] + xyz[13]); - mid[2] = 0.25f * (xyz[2] + xyz[6] + xyz[10] + xyz[14]); - - VectorSubtract( xyz, mid, delta ); - radius = VectorLength( delta ) * 0.707f; // / sqrt(2) - - VectorScale( leftDir, radius, left ); - VectorScale( upDir, radius, up ); - - if ( backEnd.viewParms.isMirror ) { - VectorSubtract( vec3_origin, left, left ); - } - - // compensate for scale in the axes if necessary - if ( backEnd.currentEntity->e.nonNormalizedAxes ) { - float axisLength; - axisLength = VectorLength( backEnd.currentEntity->e.axis[0] ); - if ( !axisLength ) { - axisLength = 0; - } else { - axisLength = 1.0f / axisLength; - } - VectorScale(left, axisLength, left); - VectorScale(up, axisLength, up); - } - - RB_AddQuadStamp( mid, left, up, tess.vertexColors[i] ); - } -} - - -/* -===================== -Autosprite2Deform - -Autosprite2 will pivot a rectangular quad along the center of its long axis -===================== -*/ -int edgeVerts[6][2] = { - { 0, 1 }, - { 0, 2 }, - { 0, 3 }, - { 1, 2 }, - { 1, 3 }, - { 2, 3 } -}; - -static void Autosprite2Deform( void ) { - int i, j, k; - int indexes; - float *xyz; - vec3_t forward; - - if ( tess.numVertexes & 3 ) { - ri.Printf( PRINT_WARNING, "Autosprite2 shader %s had odd vertex count", tess.shader->name ); - } - if ( tess.numIndexes != ( tess.numVertexes >> 2 ) * 6 ) { - ri.Printf( PRINT_WARNING, "Autosprite2 shader %s had odd index count", tess.shader->name ); - } - - if ( backEnd.currentEntity != &tr.worldEntity ) { - GlobalVectorToLocal( backEnd.viewParms.or.axis[0], forward ); - } else { - VectorCopy( backEnd.viewParms.or.axis[0], forward ); - } - - // this is a lot of work for two triangles... - // we could precalculate a lot of it is an issue, but it would mess up - // the shader abstraction - for ( i = 0, indexes = 0 ; i < tess.numVertexes ; i+=4, indexes+=6 ) { - float lengths[2]; - int nums[2]; - vec3_t mid[2]; - vec3_t major, minor; - float *v1, *v2; - - // find the midpoint - xyz = tess.xyz[i]; - - // identify the two shortest edges - nums[0] = nums[1] = 0; - lengths[0] = lengths[1] = 999999; - - for ( j = 0 ; j < 6 ; j++ ) { - float l; - vec3_t temp; - - v1 = xyz + 4 * edgeVerts[j][0]; - v2 = xyz + 4 * edgeVerts[j][1]; - - VectorSubtract( v1, v2, temp ); - - l = DotProduct( temp, temp ); - if ( l < lengths[0] ) { - nums[1] = nums[0]; - lengths[1] = lengths[0]; - nums[0] = j; - lengths[0] = l; - } else if ( l < lengths[1] ) { - nums[1] = j; - lengths[1] = l; - } - } - - for ( j = 0 ; j < 2 ; j++ ) { - v1 = xyz + 4 * edgeVerts[nums[j]][0]; - v2 = xyz + 4 * edgeVerts[nums[j]][1]; - - mid[j][0] = 0.5f * (v1[0] + v2[0]); - mid[j][1] = 0.5f * (v1[1] + v2[1]); - mid[j][2] = 0.5f * (v1[2] + v2[2]); - } - - // find the vector of the major axis - VectorSubtract( mid[1], mid[0], major ); - - // cross this with the view direction to get minor axis - CrossProduct( major, forward, minor ); - VectorNormalize( minor ); - - // re-project the points - for ( j = 0 ; j < 2 ; j++ ) { - float l; - - v1 = xyz + 4 * edgeVerts[nums[j]][0]; - v2 = xyz + 4 * edgeVerts[nums[j]][1]; - - l = 0.5 * sqrt( lengths[j] ); - - // we need to see which direction this edge - // is used to determine direction of projection - for ( k = 0 ; k < 5 ; k++ ) { - if ( tess.indexes[ indexes + k ] == i + edgeVerts[nums[j]][0] - && tess.indexes[ indexes + k + 1 ] == i + edgeVerts[nums[j]][1] ) { - break; - } - } - - if ( k == 5 ) { - VectorMA( mid[j], l, minor, v1 ); - VectorMA( mid[j], -l, minor, v2 ); - } else { - VectorMA( mid[j], -l, minor, v1 ); - VectorMA( mid[j], l, minor, v2 ); - } - } - } -} - - -/* -===================== -RB_DeformTessGeometry - -===================== -*/ -void RB_DeformTessGeometry( void ) { - int i; - deformStage_t *ds; - - for ( i = 0 ; i < tess.shader->numDeforms ; i++ ) { - ds = &tess.shader->deforms[ i ]; - - switch ( ds->deformation ) { - case DEFORM_NONE: - break; - case DEFORM_NORMALS: - RB_CalcDeformNormals( ds ); - break; - case DEFORM_WAVE: - RB_CalcDeformVertexes( ds ); - break; - case DEFORM_BULGE: - RB_CalcBulgeVertexes( ds ); - break; - case DEFORM_MOVE: - RB_CalcMoveVertexes( ds ); - break; - case DEFORM_PROJECTION_SHADOW: - RB_ProjectionShadowDeform(); - break; - case DEFORM_AUTOSPRITE: - AutospriteDeform(); - break; - case DEFORM_AUTOSPRITE2: - Autosprite2Deform(); - break; - case DEFORM_TEXT0: - case DEFORM_TEXT1: - case DEFORM_TEXT2: - case DEFORM_TEXT3: - case DEFORM_TEXT4: - case DEFORM_TEXT5: - case DEFORM_TEXT6: - case DEFORM_TEXT7: - DeformText( backEnd.refdef.text[ds->deformation - DEFORM_TEXT0] ); - break; - } - } -} - -/* -==================================================================== - -COLORS - -==================================================================== -*/ - - -/* -** RB_CalcColorFromEntity -*/ -void RB_CalcColorFromEntity( unsigned char *dstColors ) -{ - int i; - int *pColors = ( int * ) dstColors; - int c; - - if ( !backEnd.currentEntity ) - return; - - c = * ( int * ) backEnd.currentEntity->e.shaderRGBA; - - for ( i = 0; i < tess.numVertexes; i++, pColors++ ) - { - *pColors = c; - } -} - -/* -** RB_CalcColorFromOneMinusEntity -*/ -void RB_CalcColorFromOneMinusEntity( unsigned char *dstColors ) -{ - int i; - int *pColors = ( int * ) dstColors; - unsigned char invModulate[4]; - int c; - - if ( !backEnd.currentEntity ) - return; - - invModulate[0] = 255 - backEnd.currentEntity->e.shaderRGBA[0]; - invModulate[1] = 255 - backEnd.currentEntity->e.shaderRGBA[1]; - invModulate[2] = 255 - backEnd.currentEntity->e.shaderRGBA[2]; - invModulate[3] = 255 - backEnd.currentEntity->e.shaderRGBA[3]; // this trashes alpha, but the AGEN block fixes it - - c = * ( int * ) invModulate; - - for ( i = 0; i < tess.numVertexes; i++, pColors++ ) - { - *pColors = c; - } -} - -/* -** RB_CalcAlphaFromEntity -*/ -void RB_CalcAlphaFromEntity( unsigned char *dstColors ) -{ - int i; - - if ( !backEnd.currentEntity ) - return; - - dstColors += 3; - - for ( i = 0; i < tess.numVertexes; i++, dstColors += 4 ) - { - *dstColors = backEnd.currentEntity->e.shaderRGBA[3]; - } -} - -/* -** RB_CalcAlphaFromOneMinusEntity -*/ -void RB_CalcAlphaFromOneMinusEntity( unsigned char *dstColors ) -{ - int i; - - if ( !backEnd.currentEntity ) - return; - - dstColors += 3; - - for ( i = 0; i < tess.numVertexes; i++, dstColors += 4 ) - { - *dstColors = 0xff - backEnd.currentEntity->e.shaderRGBA[3]; - } -} - -/* -** RB_CalcWaveColor -*/ -void RB_CalcWaveColor( const waveForm_t *wf, unsigned char *dstColors ) -{ - int i; - int v; - float glow; - int *colors = ( int * ) dstColors; - byte color[4]; - - - if ( wf->func == GF_NOISE ) { - glow = wf->base + R_NoiseGet4f( 0, 0, 0, ( tess.shaderTime + wf->phase ) * wf->frequency ) * wf->amplitude; - } else { - glow = EvalWaveForm( wf ) * tr.identityLight; - } - - if ( glow < 0 ) { - glow = 0; - } - else if ( glow > 1 ) { - glow = 1; - } - - v = ri.ftol(255 * glow); - color[0] = color[1] = color[2] = v; - color[3] = 255; - v = *(int *)color; - - for ( i = 0; i < tess.numVertexes; i++, colors++ ) { - *colors = v; - } -} - -/* -** RB_CalcWaveAlpha -*/ -void RB_CalcWaveAlpha( const waveForm_t *wf, unsigned char *dstColors ) -{ - int i; - int v; - float glow; - - glow = EvalWaveFormClamped( wf ); - - v = 255 * glow; - - for ( i = 0; i < tess.numVertexes; i++, dstColors += 4 ) - { - dstColors[3] = v; - } -} - -/* -** RB_CalcModulateColorsByFog -*/ -void RB_CalcModulateColorsByFog( unsigned char *colors ) { - int i; - float texCoords[SHADER_MAX_VERTEXES][2]; - - // calculate texcoords so we can derive density - // this is not wasted, because it would only have - // been previously called if the surface was opaque - RB_CalcFogTexCoords( texCoords[0] ); - - for ( i = 0; i < tess.numVertexes; i++, colors += 4 ) { - float f = 1.0 - R_FogFactor( texCoords[i][0], texCoords[i][1] ); - colors[0] *= f; - colors[1] *= f; - colors[2] *= f; - } -} - -/* -** RB_CalcModulateAlphasByFog -*/ -void RB_CalcModulateAlphasByFog( unsigned char *colors ) { - int i; - float texCoords[SHADER_MAX_VERTEXES][2]; - - // calculate texcoords so we can derive density - // this is not wasted, because it would only have - // been previously called if the surface was opaque - RB_CalcFogTexCoords( texCoords[0] ); - - for ( i = 0; i < tess.numVertexes; i++, colors += 4 ) { - float f = 1.0 - R_FogFactor( texCoords[i][0], texCoords[i][1] ); - colors[3] *= f; - } -} - -/* -** RB_CalcModulateRGBAsByFog -*/ -void RB_CalcModulateRGBAsByFog( unsigned char *colors ) { - int i; - float texCoords[SHADER_MAX_VERTEXES][2]; - - // calculate texcoords so we can derive density - // this is not wasted, because it would only have - // been previously called if the surface was opaque - RB_CalcFogTexCoords( texCoords[0] ); - - for ( i = 0; i < tess.numVertexes; i++, colors += 4 ) { - float f = 1.0 - R_FogFactor( texCoords[i][0], texCoords[i][1] ); - colors[0] *= f; - colors[1] *= f; - colors[2] *= f; - colors[3] *= f; - } -} - - -/* -==================================================================== - -TEX COORDS - -==================================================================== -*/ - -/* -======================== -RB_CalcFogTexCoords - -To do the clipped fog plane really correctly, we should use -projected textures, but I don't trust the drivers and it -doesn't fit our shader data. -======================== -*/ -void RB_CalcFogTexCoords( float *st ) { - int i; - float *v; - float s, t; - float eyeT; - qboolean eyeOutside; - fog_t *fog; - vec3_t local; - vec4_t fogDistanceVector, fogDepthVector = {0, 0, 0, 0}; - - fog = tr.world->fogs + tess.fogNum; - - // all fogging distance is based on world Z units - VectorSubtract( backEnd.or.origin, backEnd.viewParms.or.origin, local ); - fogDistanceVector[0] = -backEnd.or.modelMatrix[2]; - fogDistanceVector[1] = -backEnd.or.modelMatrix[6]; - fogDistanceVector[2] = -backEnd.or.modelMatrix[10]; - fogDistanceVector[3] = DotProduct( local, backEnd.viewParms.or.axis[0] ); - - // scale the fog vectors based on the fog's thickness - fogDistanceVector[0] *= fog->tcScale; - fogDistanceVector[1] *= fog->tcScale; - fogDistanceVector[2] *= fog->tcScale; - fogDistanceVector[3] *= fog->tcScale; - - // rotate the gradient vector for this orientation - if ( fog->hasSurface ) { - fogDepthVector[0] = fog->surface[0] * backEnd.or.axis[0][0] + - fog->surface[1] * backEnd.or.axis[0][1] + fog->surface[2] * backEnd.or.axis[0][2]; - fogDepthVector[1] = fog->surface[0] * backEnd.or.axis[1][0] + - fog->surface[1] * backEnd.or.axis[1][1] + fog->surface[2] * backEnd.or.axis[1][2]; - fogDepthVector[2] = fog->surface[0] * backEnd.or.axis[2][0] + - fog->surface[1] * backEnd.or.axis[2][1] + fog->surface[2] * backEnd.or.axis[2][2]; - fogDepthVector[3] = -fog->surface[3] + DotProduct( backEnd.or.origin, fog->surface ); - - eyeT = DotProduct( backEnd.or.viewOrigin, fogDepthVector ) + fogDepthVector[3]; - } else { - eyeT = 1; // non-surface fog always has eye inside - } - - // see if the viewpoint is outside - // this is needed for clipping distance even for constant fog - - if ( eyeT < 0 ) { - eyeOutside = qtrue; - } else { - eyeOutside = qfalse; - } - - fogDistanceVector[3] += 1.0/512; - - // calculate density for each point - for (i = 0, v = tess.xyz[0] ; i < tess.numVertexes ; i++, v += 4) { - // calculate the length in fog - s = DotProduct( v, fogDistanceVector ) + fogDistanceVector[3]; - t = DotProduct( v, fogDepthVector ) + fogDepthVector[3]; - - // partially clipped fogs use the T axis - if ( eyeOutside ) { - if ( t < 1.0 ) { - t = 1.0/32; // point is outside, so no fogging - } else { - t = 1.0/32 + 30.0/32 * t / ( t - eyeT ); // cut the distance at the fog plane - } - } else { - if ( t < 0 ) { - t = 1.0/32; // point is outside, so no fogging - } else { - t = 31.0/32; - } - } - - st[0] = s; - st[1] = t; - st += 2; - } -} - - - -/* -** RB_CalcEnvironmentTexCoords -*/ -void RB_CalcEnvironmentTexCoords( float *st ) -{ - int i; - float *v, *normal; - vec3_t viewer, reflected; - float d; - - v = tess.xyz[0]; - normal = tess.normal[0]; - - for (i = 0 ; i < tess.numVertexes ; i++, v += 4, normal += 4, st += 2 ) - { - VectorSubtract (backEnd.or.viewOrigin, v, viewer); - VectorNormalizeFast (viewer); - - d = DotProduct (normal, viewer); - - reflected[0] = normal[0]*2*d - viewer[0]; - reflected[1] = normal[1]*2*d - viewer[1]; - reflected[2] = normal[2]*2*d - viewer[2]; - - st[0] = 0.5 + reflected[1] * 0.5; - st[1] = 0.5 - reflected[2] * 0.5; - } -} - -/* -** RB_CalcTurbulentTexCoords -*/ -void RB_CalcTurbulentTexCoords( const waveForm_t *wf, float *st ) -{ - int i; - float now; - - now = ( wf->phase + tess.shaderTime * wf->frequency ); - - for ( i = 0; i < tess.numVertexes; i++, st += 2 ) - { - float s = st[0]; - float t = st[1]; - - st[0] = s + tr.sinTable[ ( ( int ) ( ( ( tess.xyz[i][0] + tess.xyz[i][2] )* 1.0/128 * 0.125 + now ) * FUNCTABLE_SIZE ) ) & ( FUNCTABLE_MASK ) ] * wf->amplitude; - st[1] = t + tr.sinTable[ ( ( int ) ( ( tess.xyz[i][1] * 1.0/128 * 0.125 + now ) * FUNCTABLE_SIZE ) ) & ( FUNCTABLE_MASK ) ] * wf->amplitude; - } -} - -/* -** RB_CalcScaleTexCoords -*/ -void RB_CalcScaleTexCoords( const float scale[2], float *st ) -{ - int i; - - for ( i = 0; i < tess.numVertexes; i++, st += 2 ) - { - st[0] *= scale[0]; - st[1] *= scale[1]; - } -} - -/* -** RB_CalcScrollTexCoords -*/ -void RB_CalcScrollTexCoords( const float scrollSpeed[2], float *st ) -{ - int i; - float timeScale = tess.shaderTime; - float adjustedScrollS, adjustedScrollT; - - adjustedScrollS = scrollSpeed[0] * timeScale; - adjustedScrollT = scrollSpeed[1] * timeScale; - - // clamp so coordinates don't continuously get larger, causing problems - // with hardware limits - adjustedScrollS = adjustedScrollS - floor( adjustedScrollS ); - adjustedScrollT = adjustedScrollT - floor( adjustedScrollT ); - - for ( i = 0; i < tess.numVertexes; i++, st += 2 ) - { - st[0] += adjustedScrollS; - st[1] += adjustedScrollT; - } -} - -/* -** RB_CalcTransformTexCoords -*/ -void RB_CalcTransformTexCoords( const texModInfo_t *tmi, float *st ) -{ - int i; - - for ( i = 0; i < tess.numVertexes; i++, st += 2 ) - { - float s = st[0]; - float t = st[1]; - - st[0] = s * tmi->matrix[0][0] + t * tmi->matrix[1][0] + tmi->translate[0]; - st[1] = s * tmi->matrix[0][1] + t * tmi->matrix[1][1] + tmi->translate[1]; - } -} - -/* -** RB_CalcRotateTexCoords -*/ -void RB_CalcRotateTexCoords( float degsPerSecond, float *st ) -{ - float timeScale = tess.shaderTime; - float degs; - int index; - float sinValue, cosValue; - texModInfo_t tmi; - - degs = -degsPerSecond * timeScale; - index = degs * ( FUNCTABLE_SIZE / 360.0f ); - - sinValue = tr.sinTable[ index & FUNCTABLE_MASK ]; - cosValue = tr.sinTable[ ( index + FUNCTABLE_SIZE / 4 ) & FUNCTABLE_MASK ]; - - tmi.matrix[0][0] = cosValue; - tmi.matrix[1][0] = -sinValue; - tmi.translate[0] = 0.5 - 0.5 * cosValue + 0.5 * sinValue; - - tmi.matrix[0][1] = sinValue; - tmi.matrix[1][1] = cosValue; - tmi.translate[1] = 0.5 - 0.5 * sinValue - 0.5 * cosValue; - - RB_CalcTransformTexCoords( &tmi, st ); -} - - -/* -** RB_CalcSpecularAlpha -** -** Calculates specular coefficient and places it in the alpha channel -*/ -vec3_t lightOrigin = { -960, 1980, 96 }; // FIXME: track dynamically - -void RB_CalcSpecularAlpha( unsigned char *alphas ) { - int i; - float *v, *normal; - vec3_t viewer, reflected; - float l, d; - int b; - vec3_t lightDir; - int numVertexes; - - v = tess.xyz[0]; - normal = tess.normal[0]; - - alphas += 3; - - numVertexes = tess.numVertexes; - for (i = 0 ; i < numVertexes ; i++, v += 4, normal += 4, alphas += 4) { - float ilength; - - VectorSubtract( lightOrigin, v, lightDir ); -// ilength = Q_rsqrt( DotProduct( lightDir, lightDir ) ); - VectorNormalizeFast( lightDir ); - - // calculate the specular color - d = DotProduct (normal, lightDir); -// d *= ilength; - - // we don't optimize for the d < 0 case since this tends to - // cause visual artifacts such as faceted "snapping" - reflected[0] = normal[0]*2*d - lightDir[0]; - reflected[1] = normal[1]*2*d - lightDir[1]; - reflected[2] = normal[2]*2*d - lightDir[2]; - - VectorSubtract (backEnd.or.viewOrigin, v, viewer); - ilength = Q_rsqrt( DotProduct( viewer, viewer ) ); - l = DotProduct (reflected, viewer); - l *= ilength; - - if (l < 0) { - b = 0; - } else { - l = l*l; - l = l*l; - b = l * 255; - if (b > 255) { - b = 255; - } - } - - *alphas = b; - } -} - -/* -** RB_CalcDiffuseColor -** -** The basic vertex lighting calc -*/ -#if idppc_altivec -static void RB_CalcDiffuseColor_altivec( unsigned char *colors ) -{ - int i; - float *v, *normal; - trRefEntity_t *ent; - int ambientLightInt; - vec3_t lightDir; - int numVertexes; - vector unsigned char vSel = VECCONST_UINT8(0x00, 0x00, 0x00, 0xff, - 0x00, 0x00, 0x00, 0xff, - 0x00, 0x00, 0x00, 0xff, - 0x00, 0x00, 0x00, 0xff); - vector float ambientLightVec; - vector float directedLightVec; - vector float lightDirVec; - vector float normalVec0, normalVec1; - vector float incomingVec0, incomingVec1, incomingVec2; - vector float zero, jVec; - vector signed int jVecInt; - vector signed short jVecShort; - vector unsigned char jVecChar, normalPerm; - ent = backEnd.currentEntity; - ambientLightInt = ent->ambientLightInt; - // A lot of this could be simplified if we made sure - // entities light info was 16-byte aligned. - jVecChar = vec_lvsl(0, ent->ambientLight); - ambientLightVec = vec_ld(0, (vector float *)ent->ambientLight); - jVec = vec_ld(11, (vector float *)ent->ambientLight); - ambientLightVec = vec_perm(ambientLightVec,jVec,jVecChar); - - jVecChar = vec_lvsl(0, ent->directedLight); - directedLightVec = vec_ld(0,(vector float *)ent->directedLight); - jVec = vec_ld(11,(vector float *)ent->directedLight); - directedLightVec = vec_perm(directedLightVec,jVec,jVecChar); - - jVecChar = vec_lvsl(0, ent->lightDir); - lightDirVec = vec_ld(0,(vector float *)ent->lightDir); - jVec = vec_ld(11,(vector float *)ent->lightDir); - lightDirVec = vec_perm(lightDirVec,jVec,jVecChar); - - zero = (vector float)vec_splat_s8(0); - VectorCopy( ent->lightDir, lightDir ); - - v = tess.xyz[0]; - normal = tess.normal[0]; - - normalPerm = vec_lvsl(0,normal); - numVertexes = tess.numVertexes; - for (i = 0 ; i < numVertexes ; i++, v += 4, normal += 4) { - normalVec0 = vec_ld(0,(vector float *)normal); - normalVec1 = vec_ld(11,(vector float *)normal); - normalVec0 = vec_perm(normalVec0,normalVec1,normalPerm); - incomingVec0 = vec_madd(normalVec0, lightDirVec, zero); - incomingVec1 = vec_sld(incomingVec0,incomingVec0,4); - incomingVec2 = vec_add(incomingVec0,incomingVec1); - incomingVec1 = vec_sld(incomingVec1,incomingVec1,4); - incomingVec2 = vec_add(incomingVec2,incomingVec1); - incomingVec0 = vec_splat(incomingVec2,0); - incomingVec0 = vec_max(incomingVec0,zero); - normalPerm = vec_lvsl(12,normal); - jVec = vec_madd(incomingVec0, directedLightVec, ambientLightVec); - jVecInt = vec_cts(jVec,0); // RGBx - jVecShort = vec_pack(jVecInt,jVecInt); // RGBxRGBx - jVecChar = vec_packsu(jVecShort,jVecShort); // RGBxRGBxRGBxRGBx - jVecChar = vec_sel(jVecChar,vSel,vSel); // RGBARGBARGBARGBA replace alpha with 255 - vec_ste((vector unsigned int)jVecChar,0,(unsigned int *)&colors[i*4]); // store color - } -} -#endif - -static void RB_CalcDiffuseColor_scalar( unsigned char *colors ) -{ - int i, j; - float *v, *normal; - float incoming; - trRefEntity_t *ent; - int ambientLightInt; - vec3_t ambientLight; - vec3_t lightDir; - vec3_t directedLight; - int numVertexes; - ent = backEnd.currentEntity; - ambientLightInt = ent->ambientLightInt; - VectorCopy( ent->ambientLight, ambientLight ); - VectorCopy( ent->directedLight, directedLight ); - VectorCopy( ent->lightDir, lightDir ); - - v = tess.xyz[0]; - normal = tess.normal[0]; - - numVertexes = tess.numVertexes; - for (i = 0 ; i < numVertexes ; i++, v += 4, normal += 4) { - incoming = DotProduct (normal, lightDir); - if ( incoming <= 0 ) { - *(int *)&colors[i*4] = ambientLightInt; - continue; - } - j = ri.ftol(ambientLight[0] + incoming * directedLight[0]); - if ( j > 255 ) { - j = 255; - } - colors[i*4+0] = j; - - j = ri.ftol(ambientLight[1] + incoming * directedLight[1]); - if ( j > 255 ) { - j = 255; - } - colors[i*4+1] = j; - - j = ri.ftol(ambientLight[2] + incoming * directedLight[2]); - if ( j > 255 ) { - j = 255; - } - colors[i*4+2] = j; - - colors[i*4+3] = 255; - } -} - -void RB_CalcDiffuseColor( unsigned char *colors ) -{ -#if idppc_altivec - if (com_altivec->integer) { - // must be in a seperate function or G3 systems will crash. - RB_CalcDiffuseColor_altivec( colors ); - return; - } -#endif - RB_CalcDiffuseColor_scalar( colors ); -} - diff --git a/src/renderer/tr_shader.c b/src/renderer/tr_shader.c deleted file mode 100644 index 8440dd63..00000000 --- a/src/renderer/tr_shader.c +++ /dev/null @@ -1,3068 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#include "tr_local.h" - -// tr_shader.c -- this file deals with the parsing and definition of shaders - -static char *s_shaderText; - -// the shader is parsed into these global variables, then copied into -// dynamically allocated memory if it is valid. -static shaderStage_t stages[MAX_SHADER_STAGES]; -static shader_t shader; -static texModInfo_t texMods[MAX_SHADER_STAGES][TR_MAX_TEXMODS]; - -#define FILE_HASH_SIZE 1024 -static shader_t* hashTable[FILE_HASH_SIZE]; - -#define MAX_SHADERTEXT_HASH 2048 -static char **shaderTextHashTable[MAX_SHADERTEXT_HASH]; - -/* -================ -return a hash value for the filename -================ -*/ -#ifdef __GNUCC__ - #warning TODO: check if long is ok here -#endif -static long generateHashValue( const char *fname, const int size ) { - int i; - long hash; - char letter; - - hash = 0; - i = 0; - while (fname[i] != '\0') { - letter = tolower(fname[i]); - if (letter =='.') break; // don't include extension - if (letter =='\\') letter = '/'; // damn path names - if (letter == PATH_SEP) letter = '/'; // damn path names - hash+=(long)(letter)*(i+119); - i++; - } - hash = (hash ^ (hash >> 10) ^ (hash >> 20)); - hash &= (size-1); - return hash; -} - -void R_RemapShader(const char *shaderName, const char *newShaderName, const char *timeOffset) { - char strippedName[MAX_QPATH]; - int hash; - shader_t *sh, *sh2; - qhandle_t h; - - sh = R_FindShaderByName( shaderName ); - if (sh == NULL || sh == tr.defaultShader) { - h = RE_RegisterShaderLightMap(shaderName, 0); - sh = R_GetShaderByHandle(h); - } - if (sh == NULL || sh == tr.defaultShader) { - ri.Printf( PRINT_WARNING, "WARNING: R_RemapShader: shader %s not found\n", shaderName ); - return; - } - - sh2 = R_FindShaderByName( newShaderName ); - if (sh2 == NULL || sh2 == tr.defaultShader) { - h = RE_RegisterShaderLightMap(newShaderName, 0); - sh2 = R_GetShaderByHandle(h); - } - - if (sh2 == NULL || sh2 == tr.defaultShader) { - ri.Printf( PRINT_WARNING, "WARNING: R_RemapShader: new shader %s not found\n", newShaderName ); - return; - } - - // remap all the shaders with the given name - // even tho they might have different lightmaps - COM_StripExtension(shaderName, strippedName, sizeof(strippedName)); - hash = generateHashValue(strippedName, FILE_HASH_SIZE); - for (sh = hashTable[hash]; sh; sh = sh->next) { - if (Q_stricmp(sh->name, strippedName) == 0) { - if (sh != sh2) { - sh->remappedShader = sh2; - } else { - sh->remappedShader = NULL; - } - } - } - if (timeOffset) { - sh2->timeOffset = atof(timeOffset); - } -} - -/* -=============== -ParseVector -=============== -*/ -static qboolean ParseVector( char **text, int count, float *v ) { - char *token; - int i; - - // FIXME: spaces are currently required after parens, should change parseext... - token = COM_ParseExt( text, qfalse ); - if ( strcmp( token, "(" ) ) { - ri.Printf( PRINT_WARNING, "WARNING: missing parenthesis in shader '%s'\n", shader.name ); - return qfalse; - } - - for ( i = 0 ; i < count ; i++ ) { - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) { - ri.Printf( PRINT_WARNING, "WARNING: missing vector element in shader '%s'\n", shader.name ); - return qfalse; - } - v[i] = atof( token ); - } - - token = COM_ParseExt( text, qfalse ); - if ( strcmp( token, ")" ) ) { - ri.Printf( PRINT_WARNING, "WARNING: missing parenthesis in shader '%s'\n", shader.name ); - return qfalse; - } - - return qtrue; -} - - -/* -=============== -NameToAFunc -=============== -*/ -static unsigned NameToAFunc( const char *funcname ) -{ - if ( !Q_stricmp( funcname, "GT0" ) ) - { - return GLS_ATEST_GT_0; - } - else if ( !Q_stricmp( funcname, "LT128" ) ) - { - return GLS_ATEST_LT_80; - } - else if ( !Q_stricmp( funcname, "GE128" ) ) - { - return GLS_ATEST_GE_80; - } - - ri.Printf( PRINT_WARNING, "WARNING: invalid alphaFunc name '%s' in shader '%s'\n", funcname, shader.name ); - return 0; -} - - -/* -=============== -NameToSrcBlendMode -=============== -*/ -static int NameToSrcBlendMode( const char *name ) -{ - if ( !Q_stricmp( name, "GL_ONE" ) ) - { - return GLS_SRCBLEND_ONE; - } - else if ( !Q_stricmp( name, "GL_ZERO" ) ) - { - return GLS_SRCBLEND_ZERO; - } - else if ( !Q_stricmp( name, "GL_DST_COLOR" ) ) - { - return GLS_SRCBLEND_DST_COLOR; - } - else if ( !Q_stricmp( name, "GL_ONE_MINUS_DST_COLOR" ) ) - { - return GLS_SRCBLEND_ONE_MINUS_DST_COLOR; - } - else if ( !Q_stricmp( name, "GL_SRC_ALPHA" ) ) - { - return GLS_SRCBLEND_SRC_ALPHA; - } - else if ( !Q_stricmp( name, "GL_ONE_MINUS_SRC_ALPHA" ) ) - { - return GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA; - } - else if ( !Q_stricmp( name, "GL_DST_ALPHA" ) ) - { - return GLS_SRCBLEND_DST_ALPHA; - } - else if ( !Q_stricmp( name, "GL_ONE_MINUS_DST_ALPHA" ) ) - { - return GLS_SRCBLEND_ONE_MINUS_DST_ALPHA; - } - else if ( !Q_stricmp( name, "GL_SRC_ALPHA_SATURATE" ) ) - { - return GLS_SRCBLEND_ALPHA_SATURATE; - } - - ri.Printf( PRINT_WARNING, "WARNING: unknown blend mode '%s' in shader '%s', substituting GL_ONE\n", name, shader.name ); - return GLS_SRCBLEND_ONE; -} - -/* -=============== -NameToDstBlendMode -=============== -*/ -static int NameToDstBlendMode( const char *name ) -{ - if ( !Q_stricmp( name, "GL_ONE" ) ) - { - return GLS_DSTBLEND_ONE; - } - else if ( !Q_stricmp( name, "GL_ZERO" ) ) - { - return GLS_DSTBLEND_ZERO; - } - else if ( !Q_stricmp( name, "GL_SRC_ALPHA" ) ) - { - return GLS_DSTBLEND_SRC_ALPHA; - } - else if ( !Q_stricmp( name, "GL_ONE_MINUS_SRC_ALPHA" ) ) - { - return GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; - } - else if ( !Q_stricmp( name, "GL_DST_ALPHA" ) ) - { - return GLS_DSTBLEND_DST_ALPHA; - } - else if ( !Q_stricmp( name, "GL_ONE_MINUS_DST_ALPHA" ) ) - { - return GLS_DSTBLEND_ONE_MINUS_DST_ALPHA; - } - else if ( !Q_stricmp( name, "GL_SRC_COLOR" ) ) - { - return GLS_DSTBLEND_SRC_COLOR; - } - else if ( !Q_stricmp( name, "GL_ONE_MINUS_SRC_COLOR" ) ) - { - return GLS_DSTBLEND_ONE_MINUS_SRC_COLOR; - } - - ri.Printf( PRINT_WARNING, "WARNING: unknown blend mode '%s' in shader '%s', substituting GL_ONE\n", name, shader.name ); - return GLS_DSTBLEND_ONE; -} - -/* -=============== -NameToGenFunc -=============== -*/ -static genFunc_t NameToGenFunc( const char *funcname ) -{ - if ( !Q_stricmp( funcname, "sin" ) ) - { - return GF_SIN; - } - else if ( !Q_stricmp( funcname, "square" ) ) - { - return GF_SQUARE; - } - else if ( !Q_stricmp( funcname, "triangle" ) ) - { - return GF_TRIANGLE; - } - else if ( !Q_stricmp( funcname, "sawtooth" ) ) - { - return GF_SAWTOOTH; - } - else if ( !Q_stricmp( funcname, "inversesawtooth" ) ) - { - return GF_INVERSE_SAWTOOTH; - } - else if ( !Q_stricmp( funcname, "noise" ) ) - { - return GF_NOISE; - } - - ri.Printf( PRINT_WARNING, "WARNING: invalid genfunc name '%s' in shader '%s'\n", funcname, shader.name ); - return GF_SIN; -} - - -/* -=================== -ParseWaveForm -=================== -*/ -static void ParseWaveForm( char **text, waveForm_t *wave ) -{ - char *token; - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); - return; - } - wave->func = NameToGenFunc( token ); - - // BASE, AMP, PHASE, FREQ - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); - return; - } - wave->base = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); - return; - } - wave->amplitude = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); - return; - } - wave->phase = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); - return; - } - wave->frequency = atof( token ); -} - - -/* -=================== -ParseTexMod -=================== -*/ -static void ParseTexMod( char *_text, shaderStage_t *stage ) -{ - const char *token; - char **text = &_text; - texModInfo_t *tmi; - - if ( stage->bundle[0].numTexMods == TR_MAX_TEXMODS ) { - ri.Error( ERR_DROP, "ERROR: too many tcMod stages in shader '%s'", shader.name ); - return; - } - - tmi = &stage->bundle[0].texMods[stage->bundle[0].numTexMods]; - stage->bundle[0].numTexMods++; - - token = COM_ParseExt( text, qfalse ); - - // - // turb - // - if ( !Q_stricmp( token, "turb" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb parms in shader '%s'\n", shader.name ); - return; - } - tmi->wave.base = atof( token ); - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name ); - return; - } - tmi->wave.amplitude = atof( token ); - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name ); - return; - } - tmi->wave.phase = atof( token ); - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name ); - return; - } - tmi->wave.frequency = atof( token ); - - tmi->type = TMOD_TURBULENT; - } - // - // scale - // - else if ( !Q_stricmp( token, "scale" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing scale parms in shader '%s'\n", shader.name ); - return; - } - tmi->scale[0] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing scale parms in shader '%s'\n", shader.name ); - return; - } - tmi->scale[1] = atof( token ); - tmi->type = TMOD_SCALE; - } - // - // scroll - // - else if ( !Q_stricmp( token, "scroll" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing scale scroll parms in shader '%s'\n", shader.name ); - return; - } - tmi->scroll[0] = atof( token ); - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing scale scroll parms in shader '%s'\n", shader.name ); - return; - } - tmi->scroll[1] = atof( token ); - tmi->type = TMOD_SCROLL; - } - // - // stretch - // - else if ( !Q_stricmp( token, "stretch" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); - return; - } - tmi->wave.func = NameToGenFunc( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); - return; - } - tmi->wave.base = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); - return; - } - tmi->wave.amplitude = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); - return; - } - tmi->wave.phase = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); - return; - } - tmi->wave.frequency = atof( token ); - - tmi->type = TMOD_STRETCH; - } - // - // transform - // - else if ( !Q_stricmp( token, "transform" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); - return; - } - tmi->matrix[0][0] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); - return; - } - tmi->matrix[0][1] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); - return; - } - tmi->matrix[1][0] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); - return; - } - tmi->matrix[1][1] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); - return; - } - tmi->translate[0] = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); - return; - } - tmi->translate[1] = atof( token ); - - tmi->type = TMOD_TRANSFORM; - } - // - // rotate - // - else if ( !Q_stricmp( token, "rotate" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing tcMod rotate parms in shader '%s'\n", shader.name ); - return; - } - tmi->rotateSpeed = atof( token ); - tmi->type = TMOD_ROTATE; - } - // - // entityTranslate - // - else if ( !Q_stricmp( token, "entityTranslate" ) ) - { - tmi->type = TMOD_ENTITY_TRANSLATE; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown tcMod '%s' in shader '%s'\n", token, shader.name ); - } -} - - -/* -=================== -ParseStage -=================== -*/ -static qboolean ParseStage( shaderStage_t *stage, char **text ) -{ - char *token; - int depthMaskBits = GLS_DEPTHMASK_TRUE, blendSrcBits = 0, blendDstBits = 0, atestBits = 0, depthFuncBits = 0; - qboolean depthMaskExplicit = qfalse; - - stage->active = qtrue; - - while ( 1 ) - { - token = COM_ParseExt( text, qtrue ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: no matching '}' found\n" ); - return qfalse; - } - - if ( token[0] == '}' ) - { - break; - } - // - // map - // - else if ( !Q_stricmp( token, "map" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'map' keyword in shader '%s'\n", shader.name ); - return qfalse; - } - - if ( !Q_stricmp( token, "$whiteimage" ) ) - { - stage->bundle[0].image[0] = tr.whiteImage; - continue; - } - else if ( !Q_stricmp( token, "$lightmap" ) ) - { - stage->bundle[0].isLightmap = qtrue; - if ( shader.lightmapIndex < 0 || !tr.lightmaps ) { - stage->bundle[0].image[0] = tr.whiteImage; - } else { - stage->bundle[0].image[0] = tr.lightmaps[shader.lightmapIndex]; - } - continue; - } - else - { - stage->bundle[0].image[0] = R_FindImageFile( token, !shader.noMipMaps, !shader.noPicMip, GL_REPEAT ); - if ( !stage->bundle[0].image[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name ); - return qfalse; - } - } - } - // - // clampmap - // - else if ( !Q_stricmp( token, "clampmap" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'clampmap' keyword in shader '%s'\n", shader.name ); - return qfalse; - } - - stage->bundle[0].image[0] = R_FindImageFile( token, !shader.noMipMaps, !shader.noPicMip, GL_CLAMP_TO_EDGE ); - if ( !stage->bundle[0].image[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name ); - return qfalse; - } - } - // - // animMap .... - // - else if ( !Q_stricmp( token, "animMap" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'animMmap' keyword in shader '%s'\n", shader.name ); - return qfalse; - } - stage->bundle[0].imageAnimationSpeed = atof( token ); - - // parse up to MAX_IMAGE_ANIMATIONS animations - while ( 1 ) { - int num; - - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) { - break; - } - num = stage->bundle[0].numImageAnimations; - if ( num < MAX_IMAGE_ANIMATIONS ) { - stage->bundle[0].image[num] = R_FindImageFile( token, !shader.noMipMaps, !shader.noPicMip, GL_REPEAT ); - if ( !stage->bundle[0].image[num] ) - { - ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name ); - return qfalse; - } - stage->bundle[0].numImageAnimations++; - } - } - } - else if ( !Q_stricmp( token, "videoMap" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'videoMmap' keyword in shader '%s'\n", shader.name ); - return qfalse; - } - stage->bundle[0].videoMapHandle = ri.CIN_PlayCinematic( token, 0, 0, 256, 256, (CIN_loop | CIN_silent | CIN_shader)); - if (stage->bundle[0].videoMapHandle != -1) { - stage->bundle[0].isVideoMap = qtrue; - stage->bundle[0].image[0] = tr.scratchImage[stage->bundle[0].videoMapHandle]; - } - } - // - // alphafunc - // - else if ( !Q_stricmp( token, "alphaFunc" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'alphaFunc' keyword in shader '%s'\n", shader.name ); - return qfalse; - } - - atestBits = NameToAFunc( token ); - } - // - // depthFunc - // - else if ( !Q_stricmp( token, "depthfunc" ) ) - { - token = COM_ParseExt( text, qfalse ); - - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'depthfunc' keyword in shader '%s'\n", shader.name ); - return qfalse; - } - - if ( !Q_stricmp( token, "lequal" ) ) - { - depthFuncBits = 0; - } - else if ( !Q_stricmp( token, "equal" ) ) - { - depthFuncBits = GLS_DEPTHFUNC_EQUAL; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown depthfunc '%s' in shader '%s'\n", token, shader.name ); - continue; - } - } - // - // detail - // - else if ( !Q_stricmp( token, "detail" ) ) - { - stage->isDetail = qtrue; - } - // - // blendfunc - // or blendfunc - // - else if ( !Q_stricmp( token, "blendfunc" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parm for blendFunc in shader '%s'\n", shader.name ); - continue; - } - // check for "simple" blends first - if ( !Q_stricmp( token, "add" ) ) { - blendSrcBits = GLS_SRCBLEND_ONE; - blendDstBits = GLS_DSTBLEND_ONE; - } else if ( !Q_stricmp( token, "filter" ) ) { - blendSrcBits = GLS_SRCBLEND_DST_COLOR; - blendDstBits = GLS_DSTBLEND_ZERO; - } else if ( !Q_stricmp( token, "blend" ) ) { - blendSrcBits = GLS_SRCBLEND_SRC_ALPHA; - blendDstBits = GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; - } else { - // complex double blends - blendSrcBits = NameToSrcBlendMode( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parm for blendFunc in shader '%s'\n", shader.name ); - continue; - } - blendDstBits = NameToDstBlendMode( token ); - } - - // clear depth mask for blended surfaces - if ( !depthMaskExplicit ) - { - depthMaskBits = 0; - } - } - // - // rgbGen - // - else if ( !Q_stricmp( token, "rgbGen" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameters for rgbGen in shader '%s'\n", shader.name ); - continue; - } - - if ( !Q_stricmp( token, "wave" ) ) - { - ParseWaveForm( text, &stage->rgbWave ); - stage->rgbGen = CGEN_WAVEFORM; - } - else if ( !Q_stricmp( token, "const" ) ) - { - vec3_t color; - - ParseVector( text, 3, color ); - stage->constantColor[0] = 255 * color[0]; - stage->constantColor[1] = 255 * color[1]; - stage->constantColor[2] = 255 * color[2]; - - stage->rgbGen = CGEN_CONST; - } - else if ( !Q_stricmp( token, "identity" ) ) - { - stage->rgbGen = CGEN_IDENTITY; - } - else if ( !Q_stricmp( token, "identityLighting" ) ) - { - stage->rgbGen = CGEN_IDENTITY_LIGHTING; - } - else if ( !Q_stricmp( token, "entity" ) ) - { - stage->rgbGen = CGEN_ENTITY; - } - else if ( !Q_stricmp( token, "oneMinusEntity" ) ) - { - stage->rgbGen = CGEN_ONE_MINUS_ENTITY; - } - else if ( !Q_stricmp( token, "vertex" ) ) - { - stage->rgbGen = CGEN_VERTEX; - if ( stage->alphaGen == 0 ) { - stage->alphaGen = AGEN_VERTEX; - } - } - else if ( !Q_stricmp( token, "exactVertex" ) ) - { - stage->rgbGen = CGEN_EXACT_VERTEX; - } - else if ( !Q_stricmp( token, "lightingDiffuse" ) ) - { - stage->rgbGen = CGEN_LIGHTING_DIFFUSE; - } - else if ( !Q_stricmp( token, "oneMinusVertex" ) ) - { - stage->rgbGen = CGEN_ONE_MINUS_VERTEX; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown rgbGen parameter '%s' in shader '%s'\n", token, shader.name ); - continue; - } - } - // - // alphaGen - // - else if ( !Q_stricmp( token, "alphaGen" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parameters for alphaGen in shader '%s'\n", shader.name ); - continue; - } - - if ( !Q_stricmp( token, "wave" ) ) - { - ParseWaveForm( text, &stage->alphaWave ); - stage->alphaGen = AGEN_WAVEFORM; - } - else if ( !Q_stricmp( token, "const" ) ) - { - token = COM_ParseExt( text, qfalse ); - stage->constantColor[3] = 255 * atof( token ); - stage->alphaGen = AGEN_CONST; - } - else if ( !Q_stricmp( token, "identity" ) ) - { - stage->alphaGen = AGEN_IDENTITY; - } - else if ( !Q_stricmp( token, "entity" ) ) - { - stage->alphaGen = AGEN_ENTITY; - } - else if ( !Q_stricmp( token, "oneMinusEntity" ) ) - { - stage->alphaGen = AGEN_ONE_MINUS_ENTITY; - } - else if ( !Q_stricmp( token, "vertex" ) ) - { - stage->alphaGen = AGEN_VERTEX; - } - else if ( !Q_stricmp( token, "lightingSpecular" ) ) - { - stage->alphaGen = AGEN_LIGHTING_SPECULAR; - } - else if ( !Q_stricmp( token, "oneMinusVertex" ) ) - { - stage->alphaGen = AGEN_ONE_MINUS_VERTEX; - } - else if ( !Q_stricmp( token, "portal" ) ) - { - stage->alphaGen = AGEN_PORTAL; - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - shader.portalRange = 256; - ri.Printf( PRINT_WARNING, "WARNING: missing range parameter for alphaGen portal in shader '%s', defaulting to 256\n", shader.name ); - } - else - { - shader.portalRange = atof( token ); - } - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown alphaGen parameter '%s' in shader '%s'\n", token, shader.name ); - continue; - } - } - // - // tcGen - // - else if ( !Q_stricmp(token, "texgen") || !Q_stricmp( token, "tcGen" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing texgen parm in shader '%s'\n", shader.name ); - continue; - } - - if ( !Q_stricmp( token, "environment" ) ) - { - stage->bundle[0].tcGen = TCGEN_ENVIRONMENT_MAPPED; - } - else if ( !Q_stricmp( token, "lightmap" ) ) - { - stage->bundle[0].tcGen = TCGEN_LIGHTMAP; - } - else if ( !Q_stricmp( token, "texture" ) || !Q_stricmp( token, "base" ) ) - { - stage->bundle[0].tcGen = TCGEN_TEXTURE; - } - else if ( !Q_stricmp( token, "vector" ) ) - { - ParseVector( text, 3, stage->bundle[0].tcGenVectors[0] ); - ParseVector( text, 3, stage->bundle[0].tcGenVectors[1] ); - - stage->bundle[0].tcGen = TCGEN_VECTOR; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown texgen parm in shader '%s'\n", shader.name ); - } - } - // - // tcMod <...> - // - else if ( !Q_stricmp( token, "tcMod" ) ) - { - char buffer[1024] = ""; - - while ( 1 ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - break; - strcat( buffer, token ); - strcat( buffer, " " ); - } - - ParseTexMod( buffer, stage ); - - continue; - } - // - // depthmask - // - else if ( !Q_stricmp( token, "depthwrite" ) ) - { - depthMaskBits = GLS_DEPTHMASK_TRUE; - depthMaskExplicit = qtrue; - - continue; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown parameter '%s' in shader '%s'\n", token, shader.name ); - return qfalse; - } - } - - // - // if cgen isn't explicitly specified, use either identity or identitylighting - // - if ( stage->rgbGen == CGEN_BAD ) { - if ( blendSrcBits == 0 || - blendSrcBits == GLS_SRCBLEND_ONE || - blendSrcBits == GLS_SRCBLEND_SRC_ALPHA ) { - stage->rgbGen = CGEN_IDENTITY_LIGHTING; - } else { - stage->rgbGen = CGEN_IDENTITY; - } - } - - - // - // implicitly assume that a GL_ONE GL_ZERO blend mask disables blending - // - if ( ( blendSrcBits == GLS_SRCBLEND_ONE ) && - ( blendDstBits == GLS_DSTBLEND_ZERO ) ) - { - blendDstBits = blendSrcBits = 0; - depthMaskBits = GLS_DEPTHMASK_TRUE; - } - - // decide which agens we can skip - if ( stage->alphaGen == AGEN_IDENTITY ) { - if ( stage->rgbGen == CGEN_IDENTITY - || stage->rgbGen == CGEN_LIGHTING_DIFFUSE ) { - stage->alphaGen = AGEN_SKIP; - } - } - - // - // compute state bits - // - stage->stateBits = depthMaskBits | - blendSrcBits | blendDstBits | - atestBits | - depthFuncBits; - - return qtrue; -} - -/* -=============== -ParseDeform - -deformVertexes wave -deformVertexes normal -deformVertexes move -deformVertexes bulge -deformVertexes projectionShadow -deformVertexes autoSprite -deformVertexes autoSprite2 -deformVertexes text[0-7] -=============== -*/ -static void ParseDeform( char **text ) { - char *token; - deformStage_t *ds; - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deform parm in shader '%s'\n", shader.name ); - return; - } - - if ( shader.numDeforms == MAX_SHADER_DEFORMS ) { - ri.Printf( PRINT_WARNING, "WARNING: MAX_SHADER_DEFORMS in '%s'\n", shader.name ); - return; - } - - ds = &shader.deforms[ shader.numDeforms ]; - shader.numDeforms++; - - if ( !Q_stricmp( token, "projectionShadow" ) ) { - ds->deformation = DEFORM_PROJECTION_SHADOW; - return; - } - - if ( !Q_stricmp( token, "autosprite" ) ) { - ds->deformation = DEFORM_AUTOSPRITE; - return; - } - - if ( !Q_stricmp( token, "autosprite2" ) ) { - ds->deformation = DEFORM_AUTOSPRITE2; - return; - } - - if ( !Q_stricmpn( token, "text", 4 ) ) { - int n; - - n = token[4] - '0'; - if ( n < 0 || n > 7 ) { - n = 0; - } - ds->deformation = DEFORM_TEXT0 + n; - return; - } - - if ( !Q_stricmp( token, "bulge" ) ) { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name ); - return; - } - ds->bulgeWidth = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name ); - return; - } - ds->bulgeHeight = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name ); - return; - } - ds->bulgeSpeed = atof( token ); - - ds->deformation = DEFORM_BULGE; - return; - } - - if ( !Q_stricmp( token, "wave" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); - return; - } - - if ( atof( token ) != 0 ) - { - ds->deformationSpread = 1.0f / atof( token ); - } - else - { - ds->deformationSpread = 100.0f; - ri.Printf( PRINT_WARNING, "WARNING: illegal div value of 0 in deformVertexes command for shader '%s'\n", shader.name ); - } - - ParseWaveForm( text, &ds->deformationWave ); - ds->deformation = DEFORM_WAVE; - return; - } - - if ( !Q_stricmp( token, "normal" ) ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); - return; - } - ds->deformationWave.amplitude = atof( token ); - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); - return; - } - ds->deformationWave.frequency = atof( token ); - - ds->deformation = DEFORM_NORMALS; - return; - } - - if ( !Q_stricmp( token, "move" ) ) { - int i; - - for ( i = 0 ; i < 3 ; i++ ) { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) { - ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); - return; - } - ds->moveVector[i] = atof( token ); - } - - ParseWaveForm( text, &ds->deformationWave ); - ds->deformation = DEFORM_MOVE; - return; - } - - ri.Printf( PRINT_WARNING, "WARNING: unknown deformVertexes subtype '%s' found in shader '%s'\n", token, shader.name ); -} - - -/* -=============== -ParseSkyParms - -skyParms -=============== -*/ -static void ParseSkyParms( char **text ) { - char *token; - static char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; - char pathname[MAX_QPATH]; - int i; - - // outerbox - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) { - ri.Printf( PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name ); - return; - } - if ( strcmp( token, "-" ) ) { - for (i=0 ; i<6 ; i++) { - Com_sprintf( pathname, sizeof(pathname), "%s_%s.tga" - , token, suf[i] ); - shader.sky.outerbox[i] = R_FindImageFile( ( char * ) pathname, qtrue, qtrue, GL_CLAMP_TO_EDGE ); - - if ( !shader.sky.outerbox[i] ) { - shader.sky.outerbox[i] = tr.defaultImage; - } - } - } - - // cloudheight - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) { - ri.Printf( PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name ); - return; - } - shader.sky.cloudHeight = atof( token ); - if ( !shader.sky.cloudHeight ) { - shader.sky.cloudHeight = 512; - } - R_InitSkyTexCoords( shader.sky.cloudHeight ); - - - // innerbox - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) { - ri.Printf( PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name ); - return; - } - if ( strcmp( token, "-" ) ) { - for (i=0 ; i<6 ; i++) { - Com_sprintf( pathname, sizeof(pathname), "%s_%s.tga" - , token, suf[i] ); - shader.sky.innerbox[i] = R_FindImageFile( ( char * ) pathname, qtrue, qtrue, GL_REPEAT ); - if ( !shader.sky.innerbox[i] ) { - shader.sky.innerbox[i] = tr.defaultImage; - } - } - } - - shader.isSky = qtrue; -} - - -/* -================= -ParseSort -================= -*/ -void ParseSort( char **text ) { - char *token; - - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) { - ri.Printf( PRINT_WARNING, "WARNING: missing sort parameter in shader '%s'\n", shader.name ); - return; - } - - if ( !Q_stricmp( token, "portal" ) ) { - shader.sort = SS_PORTAL; - } else if ( !Q_stricmp( token, "sky" ) ) { - shader.sort = SS_ENVIRONMENT; - } else if ( !Q_stricmp( token, "opaque" ) ) { - shader.sort = SS_OPAQUE; - }else if ( !Q_stricmp( token, "decal" ) ) { - shader.sort = SS_DECAL; - } else if ( !Q_stricmp( token, "seeThrough" ) ) { - shader.sort = SS_SEE_THROUGH; - } else if ( !Q_stricmp( token, "banner" ) ) { - shader.sort = SS_BANNER; - } else if ( !Q_stricmp( token, "additive" ) ) { - shader.sort = SS_BLEND1; - } else if ( !Q_stricmp( token, "nearest" ) ) { - shader.sort = SS_NEAREST; - } else if ( !Q_stricmp( token, "underwater" ) ) { - shader.sort = SS_UNDERWATER; - } else { - shader.sort = atof( token ); - } -} - - - -// this table is also present in q3map - -typedef struct { - char *name; - int clearSolid, surfaceFlags, contents; -} infoParm_t; - -infoParm_t infoParms[] = { - // server relevant contents - {"water", 1, 0, CONTENTS_WATER }, - {"slime", 1, 0, CONTENTS_SLIME }, // mildly damaging - {"lava", 1, 0, CONTENTS_LAVA }, // very damaging - {"playerclip", 1, 0, CONTENTS_PLAYERCLIP }, - {"monsterclip", 1, 0, CONTENTS_MONSTERCLIP }, - {"nodrop", 1, 0, CONTENTS_NODROP }, // don't drop items or leave bodies (death fog, lava, etc) - {"nonsolid", 1, SURF_NONSOLID, 0}, // clears the solid flag - - // utility relevant attributes - {"origin", 1, 0, CONTENTS_ORIGIN }, // center of rotating brushes - {"trans", 0, 0, CONTENTS_TRANSLUCENT }, // don't eat contained surfaces - {"detail", 0, 0, CONTENTS_DETAIL }, // don't include in structural bsp - {"structural", 0, 0, CONTENTS_STRUCTURAL }, // force into structural bsp even if trnas - {"areaportal", 1, 0, CONTENTS_AREAPORTAL }, // divides areas - {"clusterportal", 1,0, CONTENTS_CLUSTERPORTAL }, // for bots - {"donotenter", 1, 0, CONTENTS_DONOTENTER }, // for bots - - {"fog", 1, 0, CONTENTS_FOG}, // carves surfaces entering - {"sky", 0, SURF_SKY, 0 }, // emit light from an environment map - {"lightfilter", 0, SURF_LIGHTFILTER, 0 }, // filter light going through it - {"alphashadow", 0, SURF_ALPHASHADOW, 0 }, // test light on a per-pixel basis - {"hint", 0, SURF_HINT, 0 }, // use as a primary splitter - - // server attributes - {"slick", 0, SURF_SLICK, 0 }, - {"noimpact", 0, SURF_NOIMPACT, 0 }, // don't make impact explosions or marks - {"nomarks", 0, SURF_NOMARKS, 0 }, // don't make impact marks, but still explode - {"ladder", 0, SURF_LADDER, 0 }, - {"nodamage", 0, SURF_NODAMAGE, 0 }, - {"metalsteps", 0, SURF_METALSTEPS,0 }, - {"flesh", 0, SURF_FLESH, 0 }, - {"nosteps", 0, SURF_NOSTEPS, 0 }, - - // drawsurf attributes - {"nodraw", 0, SURF_NODRAW, 0 }, // don't generate a drawsurface (or a lightmap) - {"pointlight", 0, SURF_POINTLIGHT, 0 }, // sample lighting at vertexes - {"nolightmap", 0, SURF_NOLIGHTMAP,0 }, // don't generate a lightmap - {"nodlight", 0, SURF_NODLIGHT, 0 }, // don't ever add dynamic lights - {"dust", 0, SURF_DUST, 0} // leave a dust trail when walking on this surface -}; - - -/* -=============== -ParseSurfaceParm - -surfaceparm -=============== -*/ -static void ParseSurfaceParm( char **text ) { - char *token; - int numInfoParms = ARRAY_LEN( infoParms ); - int i; - - token = COM_ParseExt( text, qfalse ); - for ( i = 0 ; i < numInfoParms ; i++ ) { - if ( !Q_stricmp( token, infoParms[i].name ) ) { - shader.surfaceFlags |= infoParms[i].surfaceFlags; - shader.contentFlags |= infoParms[i].contents; -#if 0 - if ( infoParms[i].clearSolid ) { - si->contents &= ~CONTENTS_SOLID; - } -#endif - break; - } - } -} - -/* -================= -ParseShader - -The current text pointer is at the explicit text definition of the -shader. Parse it into the global shader variable. Later functions -will optimize it. -================= -*/ -static qboolean ParseShader( char **text ) -{ - char *token; - int s; - - s = 0; - - token = COM_ParseExt( text, qtrue ); - if ( token[0] != '{' ) - { - ri.Printf( PRINT_WARNING, "WARNING: expecting '{', found '%s' instead in shader '%s'\n", token, shader.name ); - return qfalse; - } - - while ( 1 ) - { - token = COM_ParseExt( text, qtrue ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: no concluding '}' in shader %s\n", shader.name ); - return qfalse; - } - - // end of shader definition - if ( token[0] == '}' ) - { - break; - } - // stage definition - else if ( token[0] == '{' ) - { - if ( s >= MAX_SHADER_STAGES ) { - ri.Printf( PRINT_WARNING, "WARNING: too many stages in shader %s\n", shader.name ); - return qfalse; - } - - if ( !ParseStage( &stages[s], text ) ) - { - return qfalse; - } - stages[s].active = qtrue; - s++; - - continue; - } - // skip stuff that only the QuakeEdRadient needs - else if ( !Q_stricmpn( token, "qer", 3 ) ) { - SkipRestOfLine( text ); - continue; - } - // sun parms - else if ( !Q_stricmp( token, "q3map_sun" ) ) { - float a, b; - - token = COM_ParseExt( text, qfalse ); - tr.sunLight[0] = atof( token ); - token = COM_ParseExt( text, qfalse ); - tr.sunLight[1] = atof( token ); - token = COM_ParseExt( text, qfalse ); - tr.sunLight[2] = atof( token ); - - VectorNormalize( tr.sunLight ); - - token = COM_ParseExt( text, qfalse ); - a = atof( token ); - VectorScale( tr.sunLight, a, tr.sunLight); - - token = COM_ParseExt( text, qfalse ); - a = atof( token ); - a = a / 180 * M_PI; - - token = COM_ParseExt( text, qfalse ); - b = atof( token ); - b = b / 180 * M_PI; - - tr.sunDirection[0] = cos( a ) * cos( b ); - tr.sunDirection[1] = sin( a ) * cos( b ); - tr.sunDirection[2] = sin( b ); - } - else if ( !Q_stricmp( token, "deformVertexes" ) ) { - ParseDeform( text ); - continue; - } - else if ( !Q_stricmp( token, "tesssize" ) ) { - SkipRestOfLine( text ); - continue; - } - else if ( !Q_stricmp( token, "clampTime" ) ) { - token = COM_ParseExt( text, qfalse ); - if (token[0]) { - shader.clampTime = atof(token); - } - } - // skip stuff that only the q3map needs - else if ( !Q_stricmpn( token, "q3map", 5 ) ) { - SkipRestOfLine( text ); - continue; - } - // skip stuff that only q3map or the server needs - else if ( !Q_stricmp( token, "surfaceParm" ) ) { - ParseSurfaceParm( text ); - continue; - } - // no mip maps - else if ( !Q_stricmp( token, "nomipmaps" ) ) - { - shader.noMipMaps = qtrue; - shader.noPicMip = qtrue; - continue; - } - // no picmip adjustment - else if ( !Q_stricmp( token, "nopicmip" ) ) - { - shader.noPicMip = qtrue; - continue; - } - // polygonOffset - else if ( !Q_stricmp( token, "polygonOffset" ) ) - { - shader.polygonOffset = qtrue; - continue; - } - // entityMergable, allowing sprite surfaces from multiple entities - // to be merged into one batch. This is a savings for smoke - // puffs and blood, but can't be used for anything where the - // shader calcs (not the surface function) reference the entity color or scroll - else if ( !Q_stricmp( token, "entityMergable" ) ) - { - shader.entityMergable = qtrue; - continue; - } - // fogParms - else if ( !Q_stricmp( token, "fogParms" ) ) - { - if ( !ParseVector( text, 3, shader.fogParms.color ) ) { - return qfalse; - } - - token = COM_ParseExt( text, qfalse ); - if ( !token[0] ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing parm for 'fogParms' keyword in shader '%s'\n", shader.name ); - continue; - } - shader.fogParms.depthForOpaque = atof( token ); - - // skip any old gradient directions - SkipRestOfLine( text ); - continue; - } - // portal - else if ( !Q_stricmp(token, "portal") ) - { - shader.sort = SS_PORTAL; - continue; - } - // skyparms - else if ( !Q_stricmp( token, "skyparms" ) ) - { - ParseSkyParms( text ); - continue; - } - // light determines flaring in q3map, not needed here - else if ( !Q_stricmp(token, "light") ) - { - token = COM_ParseExt( text, qfalse ); - continue; - } - // cull - else if ( !Q_stricmp( token, "cull") ) - { - token = COM_ParseExt( text, qfalse ); - if ( token[0] == 0 ) - { - ri.Printf( PRINT_WARNING, "WARNING: missing cull parms in shader '%s'\n", shader.name ); - continue; - } - - if ( !Q_stricmp( token, "none" ) || !Q_stricmp( token, "twosided" ) || !Q_stricmp( token, "disable" ) ) - { - shader.cullType = CT_TWO_SIDED; - } - else if ( !Q_stricmp( token, "back" ) || !Q_stricmp( token, "backside" ) || !Q_stricmp( token, "backsided" ) ) - { - shader.cullType = CT_BACK_SIDED; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: invalid cull parm '%s' in shader '%s'\n", token, shader.name ); - } - continue; - } - // sort - else if ( !Q_stricmp( token, "sort" ) ) - { - ParseSort( text ); - continue; - } - else - { - ri.Printf( PRINT_WARNING, "WARNING: unknown general shader parameter '%s' in '%s'\n", token, shader.name ); - return qfalse; - } - } - - // - // ignore shaders that don't have any stages, unless it is a sky or fog - // - if ( s == 0 && !shader.isSky && !(shader.contentFlags & CONTENTS_FOG ) ) { - return qfalse; - } - - shader.explicitlyDefined = qtrue; - - return qtrue; -} - -/* -======================================================================================== - -SHADER OPTIMIZATION AND FOGGING - -======================================================================================== -*/ - -/* -=================== -ComputeStageIteratorFunc - -See if we can use on of the simple fastpath stage functions, -otherwise set to the generic stage function -=================== -*/ -static void ComputeStageIteratorFunc( void ) -{ - shader.optimalStageIteratorFunc = RB_StageIteratorGeneric; - - // - // see if this should go into the sky path - // - if ( shader.isSky ) - { - shader.optimalStageIteratorFunc = RB_StageIteratorSky; - goto done; - } - - if ( r_ignoreFastPath->integer ) - { - return; - } - - // - // see if this can go into the vertex lit fast path - // - if ( shader.numUnfoggedPasses == 1 ) - { - if ( stages[0].rgbGen == CGEN_LIGHTING_DIFFUSE ) - { - if ( stages[0].alphaGen == AGEN_IDENTITY ) - { - if ( stages[0].bundle[0].tcGen == TCGEN_TEXTURE ) - { - if ( !shader.polygonOffset ) - { - if ( !shader.multitextureEnv ) - { - if ( !shader.numDeforms ) - { - shader.optimalStageIteratorFunc = RB_StageIteratorVertexLitTexture; - goto done; - } - } - } - } - } - } - } - - // - // see if this can go into an optimized LM, multitextured path - // - if ( shader.numUnfoggedPasses == 1 ) - { - if ( ( stages[0].rgbGen == CGEN_IDENTITY ) && ( stages[0].alphaGen == AGEN_IDENTITY ) ) - { - if ( stages[0].bundle[0].tcGen == TCGEN_TEXTURE && - stages[0].bundle[1].tcGen == TCGEN_LIGHTMAP ) - { - if ( !shader.polygonOffset ) - { - if ( !shader.numDeforms ) - { - if ( shader.multitextureEnv ) - { - shader.optimalStageIteratorFunc = RB_StageIteratorLightmappedMultitexture; - goto done; - } - } - } - } - } - } - -done: - return; -} - -typedef struct { - int blendA; - int blendB; - - int multitextureEnv; - int multitextureBlend; -} collapse_t; - -static collapse_t collapse[] = { - { 0, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, - GL_MODULATE, 0 }, - - { 0, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, - GL_MODULATE, 0 }, - - { GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, - GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, - - { GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, - GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, - - { GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, - GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, - - { GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, - GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, - - { 0, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE, - GL_ADD, 0 }, - - { GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE, - GL_ADD, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE }, -#if 0 - { 0, GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_SRCBLEND_SRC_ALPHA, - GL_DECAL, 0 }, -#endif - { -1 } -}; - -/* -================ -CollapseMultitexture - -Attempt to combine two stages into a single multitexture stage -FIXME: I think modulated add + modulated add collapses incorrectly -================= -*/ -static qboolean CollapseMultitexture( void ) { - int abits, bbits; - int i; - textureBundle_t tmpBundle; - - if ( !qglActiveTextureARB ) { - return qfalse; - } - - // make sure both stages are active - if ( !stages[0].active || !stages[1].active ) { - return qfalse; - } - - // on voodoo2, don't combine different tmus - if ( glConfig.driverType == GLDRV_VOODOO ) { - if ( stages[0].bundle[0].image[0]->TMU == - stages[1].bundle[0].image[0]->TMU ) { - return qfalse; - } - } - - abits = stages[0].stateBits; - bbits = stages[1].stateBits; - - // make sure that both stages have identical state other than blend modes - if ( ( abits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) != - ( bbits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) ) { - return qfalse; - } - - abits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); - bbits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); - - // search for a valid multitexture blend function - for ( i = 0; collapse[i].blendA != -1 ; i++ ) { - if ( abits == collapse[i].blendA - && bbits == collapse[i].blendB ) { - break; - } - } - - // nothing found - if ( collapse[i].blendA == -1 ) { - return qfalse; - } - - // GL_ADD is a separate extension - if ( collapse[i].multitextureEnv == GL_ADD && !glConfig.textureEnvAddAvailable ) { - return qfalse; - } - - // make sure waveforms have identical parameters - if ( ( stages[0].rgbGen != stages[1].rgbGen ) || - ( stages[0].alphaGen != stages[1].alphaGen ) ) { - return qfalse; - } - - // an add collapse can only have identity colors - if ( collapse[i].multitextureEnv == GL_ADD && stages[0].rgbGen != CGEN_IDENTITY ) { - return qfalse; - } - - if ( stages[0].rgbGen == CGEN_WAVEFORM ) - { - if ( memcmp( &stages[0].rgbWave, - &stages[1].rgbWave, - sizeof( stages[0].rgbWave ) ) ) - { - return qfalse; - } - } - if ( stages[0].alphaGen == AGEN_WAVEFORM ) - { - if ( memcmp( &stages[0].alphaWave, - &stages[1].alphaWave, - sizeof( stages[0].alphaWave ) ) ) - { - return qfalse; - } - } - - - // make sure that lightmaps are in bundle 1 for 3dfx - if ( stages[0].bundle[0].isLightmap ) - { - tmpBundle = stages[0].bundle[0]; - stages[0].bundle[0] = stages[1].bundle[0]; - stages[0].bundle[1] = tmpBundle; - } - else - { - stages[0].bundle[1] = stages[1].bundle[0]; - } - - // set the new blend state bits - shader.multitextureEnv = collapse[i].multitextureEnv; - stages[0].stateBits &= ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); - stages[0].stateBits |= collapse[i].multitextureBlend; - - // - // move down subsequent shaders - // - memmove( &stages[1], &stages[2], sizeof( stages[0] ) * ( MAX_SHADER_STAGES - 2 ) ); - Com_Memset( &stages[MAX_SHADER_STAGES-1], 0, sizeof( stages[0] ) ); - - return qtrue; -} - -/* -============= - -FixRenderCommandList -https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=493 -Arnout: this is a nasty issue. Shaders can be registered after drawsurfaces are generated -but before the frame is rendered. This will, for the duration of one frame, cause drawsurfaces -to be rendered with bad shaders. To fix this, need to go through all render commands and fix -sortedIndex. -============== -*/ -static void FixRenderCommandList( int newShader ) { - renderCommandList_t *cmdList = &backEndData->commands; - - if( cmdList ) { - const void *curCmd = cmdList->cmds; - - while ( 1 ) { - curCmd = PADP(curCmd, sizeof(void *)); - - switch ( *(const int *)curCmd ) { - case RC_SET_COLOR: - { - const setColorCommand_t *sc_cmd = (const setColorCommand_t *)curCmd; - curCmd = (const void *)(sc_cmd + 1); - break; - } - case RC_STRETCH_PIC: - { - const stretchPicCommand_t *sp_cmd = (const stretchPicCommand_t *)curCmd; - curCmd = (const void *)(sp_cmd + 1); - break; - } - case RC_DRAW_SURFS: - { - int i; - drawSurf_t *drawSurf; - shader_t *shader; - int fogNum; - int entityNum; - int dlightMap; - int sortedIndex; - const drawSurfsCommand_t *ds_cmd = (const drawSurfsCommand_t *)curCmd; - - for( i = 0, drawSurf = ds_cmd->drawSurfs; i < ds_cmd->numDrawSurfs; i++, drawSurf++ ) { - R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlightMap ); - sortedIndex = (( drawSurf->sort >> QSORT_SHADERNUM_SHIFT ) & (MAX_SHADERS-1)); - if( sortedIndex >= newShader ) { - sortedIndex++; - drawSurf->sort = (sortedIndex << QSORT_SHADERNUM_SHIFT) | entityNum | ( fogNum << QSORT_FOGNUM_SHIFT ) | (int)dlightMap; - } - } - curCmd = (const void *)(ds_cmd + 1); - break; - } - case RC_DRAW_BUFFER: - { - const drawBufferCommand_t *db_cmd = (const drawBufferCommand_t *)curCmd; - curCmd = (const void *)(db_cmd + 1); - break; - } - case RC_SWAP_BUFFERS: - { - const swapBuffersCommand_t *sb_cmd = (const swapBuffersCommand_t *)curCmd; - curCmd = (const void *)(sb_cmd + 1); - break; - } - case RC_END_OF_LIST: - default: - return; - } - } - } -} - -/* -============== -SortNewShader - -Positions the most recently created shader in the tr.sortedShaders[] -array so that the shader->sort key is sorted relative to the other -shaders. - -Sets shader->sortedIndex -============== -*/ -static void SortNewShader( void ) { - int i; - float sort; - shader_t *newShader; - - newShader = tr.shaders[ tr.numShaders - 1 ]; - sort = newShader->sort; - - for ( i = tr.numShaders - 2 ; i >= 0 ; i-- ) { - if ( tr.sortedShaders[ i ]->sort <= sort ) { - break; - } - tr.sortedShaders[i+1] = tr.sortedShaders[i]; - tr.sortedShaders[i+1]->sortedIndex++; - } - - // Arnout: fix rendercommandlist - // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=493 - FixRenderCommandList( i+1 ); - - newShader->sortedIndex = i+1; - tr.sortedShaders[i+1] = newShader; -} - - -/* -==================== -GeneratePermanentShader -==================== -*/ -static shader_t *GeneratePermanentShader( void ) { - shader_t *newShader; - int i, b; - int size, hash; - - if ( tr.numShaders == MAX_SHADERS ) { - ri.Printf( PRINT_WARNING, "WARNING: GeneratePermanentShader - MAX_SHADERS hit\n"); - return tr.defaultShader; - } - - newShader = ri.Hunk_Alloc( sizeof( shader_t ), h_low ); - - *newShader = shader; - - if ( shader.sort <= SS_OPAQUE ) { - newShader->fogPass = FP_EQUAL; - } else if ( shader.contentFlags & CONTENTS_FOG ) { - newShader->fogPass = FP_LE; - } - - tr.shaders[ tr.numShaders ] = newShader; - newShader->index = tr.numShaders; - - tr.sortedShaders[ tr.numShaders ] = newShader; - newShader->sortedIndex = tr.numShaders; - - tr.numShaders++; - - for ( i = 0 ; i < newShader->numUnfoggedPasses ; i++ ) { - if ( !stages[i].active ) { - break; - } - newShader->stages[i] = ri.Hunk_Alloc( sizeof( stages[i] ), h_low ); - *newShader->stages[i] = stages[i]; - - for ( b = 0 ; b < NUM_TEXTURE_BUNDLES ; b++ ) { - size = newShader->stages[i]->bundle[b].numTexMods * sizeof( texModInfo_t ); - newShader->stages[i]->bundle[b].texMods = ri.Hunk_Alloc( size, h_low ); - Com_Memcpy( newShader->stages[i]->bundle[b].texMods, stages[i].bundle[b].texMods, size ); - } - } - - SortNewShader(); - - hash = generateHashValue(newShader->name, FILE_HASH_SIZE); - newShader->next = hashTable[hash]; - hashTable[hash] = newShader; - - return newShader; -} - -/* -================= -VertexLightingCollapse - -If vertex lighting is enabled, only render a single -pass, trying to guess which is the correct one to best aproximate -what it is supposed to look like. -================= -*/ -static void VertexLightingCollapse( void ) { - int stage; - shaderStage_t *bestStage; - int bestImageRank; - int rank; - - // if we aren't opaque, just use the first pass - if ( shader.sort == SS_OPAQUE ) { - - // pick the best texture for the single pass - bestStage = &stages[0]; - bestImageRank = -999999; - - for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) { - shaderStage_t *pStage = &stages[stage]; - - if ( !pStage->active ) { - break; - } - rank = 0; - - if ( pStage->bundle[0].isLightmap ) { - rank -= 100; - } - if ( pStage->bundle[0].tcGen != TCGEN_TEXTURE ) { - rank -= 5; - } - if ( pStage->bundle[0].numTexMods ) { - rank -= 5; - } - if ( pStage->rgbGen != CGEN_IDENTITY && pStage->rgbGen != CGEN_IDENTITY_LIGHTING ) { - rank -= 3; - } - - if ( rank > bestImageRank ) { - bestImageRank = rank; - bestStage = pStage; - } - } - - stages[0].bundle[0] = bestStage->bundle[0]; - stages[0].stateBits &= ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); - stages[0].stateBits |= GLS_DEPTHMASK_TRUE; - if ( shader.lightmapIndex == LIGHTMAP_NONE ) { - stages[0].rgbGen = CGEN_LIGHTING_DIFFUSE; - } else { - stages[0].rgbGen = CGEN_EXACT_VERTEX; - } - stages[0].alphaGen = AGEN_SKIP; - } else { - // don't use a lightmap (tesla coils) - if ( stages[0].bundle[0].isLightmap ) { - stages[0] = stages[1]; - } - - // if we were in a cross-fade cgen, hack it to normal - if ( stages[0].rgbGen == CGEN_ONE_MINUS_ENTITY || stages[1].rgbGen == CGEN_ONE_MINUS_ENTITY ) { - stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; - } - if ( ( stages[0].rgbGen == CGEN_WAVEFORM && stages[0].rgbWave.func == GF_SAWTOOTH ) - && ( stages[1].rgbGen == CGEN_WAVEFORM && stages[1].rgbWave.func == GF_INVERSE_SAWTOOTH ) ) { - stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; - } - if ( ( stages[0].rgbGen == CGEN_WAVEFORM && stages[0].rgbWave.func == GF_INVERSE_SAWTOOTH ) - && ( stages[1].rgbGen == CGEN_WAVEFORM && stages[1].rgbWave.func == GF_SAWTOOTH ) ) { - stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; - } - } - - for ( stage = 1; stage < MAX_SHADER_STAGES; stage++ ) { - shaderStage_t *pStage = &stages[stage]; - - if ( !pStage->active ) { - break; - } - - Com_Memset( pStage, 0, sizeof( *pStage ) ); - } -} - -/* -========================= -FinishShader - -Returns a freshly allocated shader with all the needed info -from the current global working shader -========================= -*/ -static shader_t *FinishShader( void ) { - int stage; - qboolean hasLightmapStage; - qboolean vertexLightmap; - - hasLightmapStage = qfalse; - vertexLightmap = qfalse; - - // - // set sky stuff appropriate - // - if ( shader.isSky ) { - shader.sort = SS_ENVIRONMENT; - } - - // - // set polygon offset - // - if ( shader.polygonOffset && !shader.sort ) { - shader.sort = SS_DECAL; - } - - // - // set appropriate stage information - // - for ( stage = 0; stage < MAX_SHADER_STAGES; ) { - shaderStage_t *pStage = &stages[stage]; - - if ( !pStage->active ) { - break; - } - - // check for a missing texture - if ( !pStage->bundle[0].image[0] ) { - ri.Printf( PRINT_WARNING, "Shader %s has a stage with no image\n", shader.name ); - pStage->active = qfalse; - stage++; - continue; - } - - // - // ditch this stage if it's detail and detail textures are disabled - // - if ( pStage->isDetail && !r_detailTextures->integer ) - { - int index; - - for(index = stage + 1; index < MAX_SHADER_STAGES; index++) - { - if(!stages[index].active) - break; - } - - if(index < MAX_SHADER_STAGES) - memmove(pStage, pStage + 1, sizeof(*pStage) * (index - stage)); - else - { - if(stage + 1 < MAX_SHADER_STAGES) - memmove(pStage, pStage + 1, sizeof(*pStage) * (index - stage - 1)); - - Com_Memset(&stages[index - 1], 0, sizeof(*stages)); - } - - continue; - } - - // - // default texture coordinate generation - // - if ( pStage->bundle[0].isLightmap ) { - if ( pStage->bundle[0].tcGen == TCGEN_BAD ) { - pStage->bundle[0].tcGen = TCGEN_LIGHTMAP; - } - hasLightmapStage = qtrue; - } else { - if ( pStage->bundle[0].tcGen == TCGEN_BAD ) { - pStage->bundle[0].tcGen = TCGEN_TEXTURE; - } - } - - - // not a true lightmap but we want to leave existing - // behaviour in place and not print out a warning - //if (pStage->rgbGen == CGEN_VERTEX) { - // vertexLightmap = qtrue; - //} - - - - // - // determine sort order and fog color adjustment - // - if ( ( pStage->stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) && - ( stages[0].stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) ) { - int blendSrcBits = pStage->stateBits & GLS_SRCBLEND_BITS; - int blendDstBits = pStage->stateBits & GLS_DSTBLEND_BITS; - - // fog color adjustment only works for blend modes that have a contribution - // that aproaches 0 as the modulate values aproach 0 -- - // GL_ONE, GL_ONE - // GL_ZERO, GL_ONE_MINUS_SRC_COLOR - // GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA - - // modulate, additive - if ( ( ( blendSrcBits == GLS_SRCBLEND_ONE ) && ( blendDstBits == GLS_DSTBLEND_ONE ) ) || - ( ( blendSrcBits == GLS_SRCBLEND_ZERO ) && ( blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_COLOR ) ) ) { - pStage->adjustColorsForFog = ACFF_MODULATE_RGB; - } - // strict blend - else if ( ( blendSrcBits == GLS_SRCBLEND_SRC_ALPHA ) && ( blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ) ) - { - pStage->adjustColorsForFog = ACFF_MODULATE_ALPHA; - } - // premultiplied alpha - else if ( ( blendSrcBits == GLS_SRCBLEND_ONE ) && ( blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ) ) - { - pStage->adjustColorsForFog = ACFF_MODULATE_RGBA; - } else { - // we can't adjust this one correctly, so it won't be exactly correct in fog - } - - // don't screw with sort order if this is a portal or environment - if ( !shader.sort ) { - // see through item, like a grill or grate - if ( pStage->stateBits & GLS_DEPTHMASK_TRUE ) { - shader.sort = SS_SEE_THROUGH; - } else { - shader.sort = SS_BLEND0; - } - } - } - - stage++; - } - - // there are times when you will need to manually apply a sort to - // opaque alpha tested shaders that have later blend passes - if ( !shader.sort ) { - shader.sort = SS_OPAQUE; - } - - // - // if we are in r_vertexLight mode, never use a lightmap texture - // - if ( stage > 1 && ( (r_vertexLight->integer && !r_uiFullScreen->integer) || glConfig.hardwareType == GLHW_PERMEDIA2 ) ) { - VertexLightingCollapse(); - stage = 1; - hasLightmapStage = qfalse; - } - - // - // look for multitexture potential - // - if ( stage > 1 && CollapseMultitexture() ) { - stage--; - } - - if ( shader.lightmapIndex >= 0 && !hasLightmapStage ) { - if (vertexLightmap) { - ri.Printf( PRINT_DEVELOPER, "WARNING: shader '%s' has VERTEX forced lightmap!\n", shader.name ); - } else { - ri.Printf( PRINT_DEVELOPER, "WARNING: shader '%s' has lightmap but no lightmap stage!\n", shader.name ); - shader.lightmapIndex = LIGHTMAP_NONE; - } - } - - - // - // compute number of passes - // - shader.numUnfoggedPasses = stage; - - // fogonly shaders don't have any normal passes - if (stage == 0 && !shader.isSky) - shader.sort = SS_FOG; - - // determine which stage iterator function is appropriate - ComputeStageIteratorFunc(); - - return GeneratePermanentShader(); -} - -//======================================================================================== - -/* -==================== -FindShaderInShaderText - -Scans the combined text description of all the shader files for -the given shader name. - -return NULL if not found - -If found, it will return a valid shader -===================== -*/ -static char *FindShaderInShaderText( const char *shadername ) { - - char *token, *p; - - int i, hash; - - hash = generateHashValue(shadername, MAX_SHADERTEXT_HASH); - - if(shaderTextHashTable[hash]) - { - for (i = 0; shaderTextHashTable[hash][i]; i++) - { - p = shaderTextHashTable[hash][i]; - token = COM_ParseExt(&p, qtrue); - - if(!Q_stricmp(token, shadername)) - return p; - } - } - - p = s_shaderText; - - if ( !p ) { - return NULL; - } - - // look for label - while ( 1 ) { - token = COM_ParseExt( &p, qtrue ); - if ( token[0] == 0 ) { - break; - } - - if ( !Q_stricmp( token, shadername ) ) { - return p; - } - else { - // skip the definition - SkipBracedSection( &p ); - } - } - - return NULL; -} - - -/* -================== -R_FindShaderByName - -Will always return a valid shader, but it might be the -default shader if the real one can't be found. -================== -*/ -shader_t *R_FindShaderByName( const char *name ) { - char strippedName[MAX_QPATH]; - int hash; - shader_t *sh; - - if ( (name==NULL) || (name[0] == 0) ) { - return tr.defaultShader; - } - - COM_StripExtension(name, strippedName, sizeof(strippedName)); - - hash = generateHashValue(strippedName, FILE_HASH_SIZE); - - // - // see if the shader is already loaded - // - for (sh=hashTable[hash]; sh; sh=sh->next) { - // NOTE: if there was no shader or image available with the name strippedName - // then a default shader is created with lightmapIndex == LIGHTMAP_NONE, so we - // have to check all default shaders otherwise for every call to R_FindShader - // with that same strippedName a new default shader is created. - if (Q_stricmp(sh->name, strippedName) == 0) { - // match found - return sh; - } - } - - return tr.defaultShader; -} - - -/* -=============== -R_FindShader - -Will always return a valid shader, but it might be the -default shader if the real one can't be found. - -In the interest of not requiring an explicit shader text entry to -be defined for every single image used in the game, three default -shader behaviors can be auto-created for any image: - -If lightmapIndex == LIGHTMAP_NONE, then the image will have -dynamic diffuse lighting applied to it, as apropriate for most -entity skin surfaces. - -If lightmapIndex == LIGHTMAP_2D, then the image will be used -for 2D rendering unless an explicit shader is found - -If lightmapIndex == LIGHTMAP_BY_VERTEX, then the image will use -the vertex rgba modulate values, as apropriate for misc_model -pre-lit surfaces. - -Other lightmapIndex values will have a lightmap stage created -and src*dest blending applied with the texture, as apropriate for -most world construction surfaces. - -=============== -*/ -shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImage ) { - char strippedName[MAX_QPATH]; - int i, hash; - char *shaderText; - image_t *image; - shader_t *sh; - - if ( name[0] == 0 ) { - return tr.defaultShader; - } - - // use (fullbright) vertex lighting if the bsp file doesn't have - // lightmaps - if ( lightmapIndex >= 0 && lightmapIndex >= tr.numLightmaps ) { - lightmapIndex = LIGHTMAP_BY_VERTEX; - } else if ( lightmapIndex < LIGHTMAP_2D ) { - // negative lightmap indexes cause stray pointers (think tr.lightmaps[lightmapIndex]) - ri.Printf( PRINT_WARNING, "WARNING: shader '%s' has invalid lightmap index of %d\n", name, lightmapIndex ); - lightmapIndex = LIGHTMAP_BY_VERTEX; - } - - COM_StripExtension(name, strippedName, sizeof(strippedName)); - - hash = generateHashValue(strippedName, FILE_HASH_SIZE); - - // - // see if the shader is already loaded - // - for (sh = hashTable[hash]; sh; sh = sh->next) { - // NOTE: if there was no shader or image available with the name strippedName - // then a default shader is created with lightmapIndex == LIGHTMAP_NONE, so we - // have to check all default shaders otherwise for every call to R_FindShader - // with that same strippedName a new default shader is created. - if ( (sh->lightmapIndex == lightmapIndex || sh->defaultShader) && - !Q_stricmp(sh->name, strippedName)) { - // match found - return sh; - } - } - - // clear the global shader - Com_Memset( &shader, 0, sizeof( shader ) ); - Com_Memset( &stages, 0, sizeof( stages ) ); - Q_strncpyz(shader.name, strippedName, sizeof(shader.name)); - shader.lightmapIndex = lightmapIndex; - for ( i = 0 ; i < MAX_SHADER_STAGES ; i++ ) { - stages[i].bundle[0].texMods = texMods[i]; - } - - // FIXME: set these "need" values apropriately - shader.needsNormal = qtrue; - shader.needsST1 = qtrue; - shader.needsST2 = qtrue; - shader.needsColor = qtrue; - - // - // attempt to define shader from an explicit parameter file - // - shaderText = FindShaderInShaderText( strippedName ); - if ( shaderText ) { - // enable this when building a pak file to get a global list - // of all explicit shaders - if ( r_printShaders->integer ) { - ri.Printf( PRINT_ALL, "*SHADER* %s\n", name ); - } - - if ( !ParseShader( &shaderText ) ) { - // had errors, so use default shader - shader.defaultShader = qtrue; - } - sh = FinishShader(); - return sh; - } - - - // - // if not defined in the in-memory shader descriptions, - // look for a single supported image file - // - image = R_FindImageFile( name, mipRawImage, mipRawImage, mipRawImage ? GL_REPEAT : GL_CLAMP_TO_EDGE ); - if ( !image ) { - ri.Printf( PRINT_DEVELOPER, "Couldn't find image file for shader %s\n", name ); - shader.defaultShader = qtrue; - return FinishShader(); - } - - // - // create the default shading commands - // - if ( shader.lightmapIndex == LIGHTMAP_NONE ) { - // dynamic colors at vertexes - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_LIGHTING_DIFFUSE; - stages[0].stateBits = GLS_DEFAULT; - } else if ( shader.lightmapIndex == LIGHTMAP_BY_VERTEX ) { - // explicit colors at vertexes - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_EXACT_VERTEX; - stages[0].alphaGen = AGEN_SKIP; - stages[0].stateBits = GLS_DEFAULT; - } else if ( shader.lightmapIndex == LIGHTMAP_2D ) { - // GUI elements - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_VERTEX; - stages[0].alphaGen = AGEN_VERTEX; - stages[0].stateBits = GLS_DEPTHTEST_DISABLE | - GLS_SRCBLEND_SRC_ALPHA | - GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; - } else if ( shader.lightmapIndex == LIGHTMAP_WHITEIMAGE ) { - // fullbright level - stages[0].bundle[0].image[0] = tr.whiteImage; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; - stages[0].stateBits = GLS_DEFAULT; - - stages[1].bundle[0].image[0] = image; - stages[1].active = qtrue; - stages[1].rgbGen = CGEN_IDENTITY; - stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; - } else { - // two pass lightmap - stages[0].bundle[0].image[0] = tr.lightmaps[shader.lightmapIndex]; - stages[0].bundle[0].isLightmap = qtrue; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_IDENTITY; // lightmaps are scaled on creation - // for identitylight - stages[0].stateBits = GLS_DEFAULT; - - stages[1].bundle[0].image[0] = image; - stages[1].active = qtrue; - stages[1].rgbGen = CGEN_IDENTITY; - stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; - } - - return FinishShader(); -} - - -qhandle_t RE_RegisterShaderFromImage(const char *name, int lightmapIndex, image_t *image, qboolean mipRawImage) { - int i, hash; - shader_t *sh; - - hash = generateHashValue(name, FILE_HASH_SIZE); - - // probably not necessary since this function - // only gets called from tr_font.c with lightmapIndex == LIGHTMAP_2D - // but better safe than sorry. - if ( lightmapIndex >= tr.numLightmaps ) { - lightmapIndex = LIGHTMAP_WHITEIMAGE; - } - - // - // see if the shader is already loaded - // - for (sh=hashTable[hash]; sh; sh=sh->next) { - // NOTE: if there was no shader or image available with the name strippedName - // then a default shader is created with lightmapIndex == LIGHTMAP_NONE, so we - // have to check all default shaders otherwise for every call to R_FindShader - // with that same strippedName a new default shader is created. - if ( (sh->lightmapIndex == lightmapIndex || sh->defaultShader) && - // index by name - !Q_stricmp(sh->name, name)) { - // match found - return sh->index; - } - } - - // clear the global shader - Com_Memset( &shader, 0, sizeof( shader ) ); - Com_Memset( &stages, 0, sizeof( stages ) ); - Q_strncpyz(shader.name, name, sizeof(shader.name)); - shader.lightmapIndex = lightmapIndex; - for ( i = 0 ; i < MAX_SHADER_STAGES ; i++ ) { - stages[i].bundle[0].texMods = texMods[i]; - } - - // FIXME: set these "need" values apropriately - shader.needsNormal = qtrue; - shader.needsST1 = qtrue; - shader.needsST2 = qtrue; - shader.needsColor = qtrue; - - // - // create the default shading commands - // - if ( shader.lightmapIndex == LIGHTMAP_NONE ) { - // dynamic colors at vertexes - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_LIGHTING_DIFFUSE; - stages[0].stateBits = GLS_DEFAULT; - } else if ( shader.lightmapIndex == LIGHTMAP_BY_VERTEX ) { - // explicit colors at vertexes - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_EXACT_VERTEX; - stages[0].alphaGen = AGEN_SKIP; - stages[0].stateBits = GLS_DEFAULT; - } else if ( shader.lightmapIndex == LIGHTMAP_2D ) { - // GUI elements - stages[0].bundle[0].image[0] = image; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_VERTEX; - stages[0].alphaGen = AGEN_VERTEX; - stages[0].stateBits = GLS_DEPTHTEST_DISABLE | - GLS_SRCBLEND_SRC_ALPHA | - GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; - } else if ( shader.lightmapIndex == LIGHTMAP_WHITEIMAGE ) { - // fullbright level - stages[0].bundle[0].image[0] = tr.whiteImage; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; - stages[0].stateBits = GLS_DEFAULT; - - stages[1].bundle[0].image[0] = image; - stages[1].active = qtrue; - stages[1].rgbGen = CGEN_IDENTITY; - stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; - } else { - // two pass lightmap - stages[0].bundle[0].image[0] = tr.lightmaps[shader.lightmapIndex]; - stages[0].bundle[0].isLightmap = qtrue; - stages[0].active = qtrue; - stages[0].rgbGen = CGEN_IDENTITY; // lightmaps are scaled on creation - // for identitylight - stages[0].stateBits = GLS_DEFAULT; - - stages[1].bundle[0].image[0] = image; - stages[1].active = qtrue; - stages[1].rgbGen = CGEN_IDENTITY; - stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; - } - - sh = FinishShader(); - return sh->index; -} - - -/* -==================== -RE_RegisterShader - -This is the exported shader entry point for the rest of the system -It will always return an index that will be valid. - -This should really only be used for explicit shaders, because there is no -way to ask for different implicit lighting modes (vertex, lightmap, etc) -==================== -*/ -qhandle_t RE_RegisterShaderLightMap( const char *name, int lightmapIndex ) { - shader_t *sh; - - if ( strlen( name ) >= MAX_QPATH ) { - ri.Printf( PRINT_ALL, "Shader name exceeds MAX_QPATH\n" ); - return 0; - } - - sh = R_FindShader( name, lightmapIndex, qtrue ); - - // we want to return 0 if the shader failed to - // load for some reason, but R_FindShader should - // still keep a name allocated for it, so if - // something calls RE_RegisterShader again with - // the same name, we don't try looking for it again - if ( sh->defaultShader ) { - return 0; - } - - return sh->index; -} - - -/* -==================== -RE_RegisterShader - -This is the exported shader entry point for the rest of the system -It will always return an index that will be valid. - -This should really only be used for explicit shaders, because there is no -way to ask for different implicit lighting modes (vertex, lightmap, etc) -==================== -*/ -qhandle_t RE_RegisterShader( const char *name ) { - shader_t *sh; - - if ( strlen( name ) >= MAX_QPATH ) { - ri.Printf( PRINT_ALL, "Shader name exceeds MAX_QPATH\n" ); - return 0; - } - - sh = R_FindShader( name, LIGHTMAP_2D, qtrue ); - - // we want to return 0 if the shader failed to - // load for some reason, but R_FindShader should - // still keep a name allocated for it, so if - // something calls RE_RegisterShader again with - // the same name, we don't try looking for it again - if ( sh->defaultShader ) { - return 0; - } - - return sh->index; -} - - -/* -==================== -RE_RegisterShaderNoMip - -For menu graphics that should never be picmiped -==================== -*/ -qhandle_t RE_RegisterShaderNoMip( const char *name ) { - shader_t *sh; - - if ( strlen( name ) >= MAX_QPATH ) { - ri.Printf( PRINT_ALL, "Shader name exceeds MAX_QPATH\n" ); - return 0; - } - - sh = R_FindShader( name, LIGHTMAP_2D, qfalse ); - - // we want to return 0 if the shader failed to - // load for some reason, but R_FindShader should - // still keep a name allocated for it, so if - // something calls RE_RegisterShader again with - // the same name, we don't try looking for it again - if ( sh->defaultShader ) { - return 0; - } - - return sh->index; -} - -/* -==================== -R_GetShaderByHandle - -When a handle is passed in by another module, this range checks -it and returns a valid (possibly default) shader_t to be used internally. -==================== -*/ -shader_t *R_GetShaderByHandle( qhandle_t hShader ) { - if ( hShader < 0 ) { - ri.Printf( PRINT_WARNING, "R_GetShaderByHandle: out of range hShader '%d'\n", hShader ); - return tr.defaultShader; - } - if ( hShader >= tr.numShaders ) { - ri.Printf( PRINT_WARNING, "R_GetShaderByHandle: out of range hShader '%d'\n", hShader ); - return tr.defaultShader; - } - return tr.shaders[hShader]; -} - -/* -=============== -R_ShaderList_f - -Dump information on all valid shaders to the console -A second parameter will cause it to print in sorted order -=============== -*/ -void R_ShaderList_f (void) { - int i; - int count; - shader_t *shader; - - ri.Printf (PRINT_ALL, "-----------------------\n"); - - count = 0; - for ( i = 0 ; i < tr.numShaders ; i++ ) { - if ( ri.Cmd_Argc() > 1 ) { - shader = tr.sortedShaders[i]; - } else { - shader = tr.shaders[i]; - } - - ri.Printf( PRINT_ALL, "%i ", shader->numUnfoggedPasses ); - - if (shader->lightmapIndex >= 0 ) { - ri.Printf (PRINT_ALL, "L "); - } else { - ri.Printf (PRINT_ALL, " "); - } - if ( shader->multitextureEnv == GL_ADD ) { - ri.Printf( PRINT_ALL, "MT(a) " ); - } else if ( shader->multitextureEnv == GL_MODULATE ) { - ri.Printf( PRINT_ALL, "MT(m) " ); - } else if ( shader->multitextureEnv == GL_DECAL ) { - ri.Printf( PRINT_ALL, "MT(d) " ); - } else { - ri.Printf( PRINT_ALL, " " ); - } - if ( shader->explicitlyDefined ) { - ri.Printf( PRINT_ALL, "E " ); - } else { - ri.Printf( PRINT_ALL, " " ); - } - - if ( shader->optimalStageIteratorFunc == RB_StageIteratorGeneric ) { - ri.Printf( PRINT_ALL, "gen " ); - } else if ( shader->optimalStageIteratorFunc == RB_StageIteratorSky ) { - ri.Printf( PRINT_ALL, "sky " ); - } else if ( shader->optimalStageIteratorFunc == RB_StageIteratorLightmappedMultitexture ) { - ri.Printf( PRINT_ALL, "lmmt" ); - } else if ( shader->optimalStageIteratorFunc == RB_StageIteratorVertexLitTexture ) { - ri.Printf( PRINT_ALL, "vlt " ); - } else { - ri.Printf( PRINT_ALL, " " ); - } - - if ( shader->defaultShader ) { - ri.Printf (PRINT_ALL, ": %s (DEFAULTED)\n", shader->name); - } else { - ri.Printf (PRINT_ALL, ": %s\n", shader->name); - } - count++; - } - ri.Printf (PRINT_ALL, "%i total shaders\n", count); - ri.Printf (PRINT_ALL, "------------------\n"); -} - -/* -==================== -ScanAndLoadShaderFiles - -Finds and loads all .shader files, combining them into -a single large text block that can be scanned for shader names -===================== -*/ -#define MAX_SHADER_FILES 4096 -static void ScanAndLoadShaderFiles( void ) -{ - char **shaderFiles; - char *buffers[MAX_SHADER_FILES]; - char *p; - int numShaderFiles; - int i; - char *oldp, *token, *hashMem, *textEnd; - int shaderTextHashTableSizes[MAX_SHADERTEXT_HASH], hash, size; - - long sum = 0, summand; - // scan for shader files - shaderFiles = ri.FS_ListFiles( "scripts", ".shader", &numShaderFiles ); - - if ( !shaderFiles || !numShaderFiles ) - { - ri.Printf( PRINT_WARNING, "WARNING: no shader files found\n" ); - return; - } - - if ( numShaderFiles > MAX_SHADER_FILES ) { - numShaderFiles = MAX_SHADER_FILES; - } - - // load and parse shader files - for ( i = 0; i < numShaderFiles; i++ ) - { - char filename[MAX_QPATH]; - - Com_sprintf( filename, sizeof( filename ), "scripts/%s", shaderFiles[i] ); - ri.Printf( PRINT_DEVELOPER, "...loading '%s'\n", filename ); - summand = ri.FS_ReadFile( filename, (void **)&buffers[i] ); - - if ( !buffers[i] ) - ri.Error( ERR_DROP, "Couldn't load %s", filename ); - - // Do a simple check on the shader structure in that file to make sure one bad shader file cannot fuck up all other shaders. - p = buffers[i]; - while(1) - { - token = COM_ParseExt(&p, qtrue); - - if(!*token) - break; - - oldp = p; - - token = COM_ParseExt(&p, qtrue); - if(token[0] != '{' && token[1] != '\0') - { - ri.Printf(PRINT_WARNING, "WARNING: Bad shader file %s has incorrect syntax.\n", filename); - ri.FS_FreeFile(buffers[i]); - buffers[i] = NULL; - break; - } - - SkipBracedSection(&oldp); - p = oldp; - } - - - if (buffers[i]) - sum += summand; - } - - // build single large buffer - s_shaderText = ri.Hunk_Alloc( sum + numShaderFiles*2, h_low ); - s_shaderText[ 0 ] = '\0'; - textEnd = s_shaderText; - - // free in reverse order, so the temp files are all dumped - for ( i = numShaderFiles - 1; i >= 0 ; i-- ) - { - if ( !buffers[i] ) - continue; - - strcat( textEnd, buffers[i] ); - strcat( textEnd, "\n" ); - textEnd += strlen( textEnd ); - ri.FS_FreeFile( buffers[i] ); - } - - COM_Compress( s_shaderText ); - - // free up memory - ri.FS_FreeFileList( shaderFiles ); - - Com_Memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes)); - size = 0; - - p = s_shaderText; - // look for shader names - while ( 1 ) { - token = COM_ParseExt( &p, qtrue ); - if ( token[0] == 0 ) { - break; - } - - hash = generateHashValue(token, MAX_SHADERTEXT_HASH); - shaderTextHashTableSizes[hash]++; - size++; - SkipBracedSection(&p); - } - - size += MAX_SHADERTEXT_HASH; - - hashMem = ri.Hunk_Alloc( size * sizeof(char *), h_low ); - - for (i = 0; i < MAX_SHADERTEXT_HASH; i++) { - shaderTextHashTable[i] = (char **) hashMem; - hashMem = ((char *) hashMem) + ((shaderTextHashTableSizes[i] + 1) * sizeof(char *)); - } - - Com_Memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes)); - - p = s_shaderText; - // look for shader names - while ( 1 ) { - oldp = p; - token = COM_ParseExt( &p, qtrue ); - if ( token[0] == 0 ) { - break; - } - - hash = generateHashValue(token, MAX_SHADERTEXT_HASH); - shaderTextHashTable[hash][shaderTextHashTableSizes[hash]++] = oldp; - - SkipBracedSection(&p); - } - - return; - -} - - -/* -==================== -CreateInternalShaders -==================== -*/ -static void CreateInternalShaders( void ) { - tr.numShaders = 0; - - // init the default shader - Com_Memset( &shader, 0, sizeof( shader ) ); - Com_Memset( &stages, 0, sizeof( stages ) ); - - Q_strncpyz( shader.name, "", sizeof( shader.name ) ); - - shader.lightmapIndex = LIGHTMAP_NONE; - stages[0].bundle[0].image[0] = tr.defaultImage; - stages[0].active = qtrue; - stages[0].stateBits = GLS_DEFAULT; - tr.defaultShader = FinishShader(); - - // shadow shader is just a marker - Q_strncpyz( shader.name, "", sizeof( shader.name ) ); - shader.sort = SS_STENCIL_SHADOW; - tr.shadowShader = FinishShader(); -} - -static void CreateExternalShaders( void ) { - tr.projectionShadowShader = R_FindShader( "projectionShadow", LIGHTMAP_NONE, qtrue ); - tr.flareShader = R_FindShader( "flareShader", LIGHTMAP_NONE, qtrue ); - - // Hack to make fogging work correctly on flares. Fog colors are calculated - // in tr_flare.c already. - if(!tr.flareShader->defaultShader) - { - int index; - - for(index = 0; index < tr.flareShader->numUnfoggedPasses; index++) - { - tr.flareShader->stages[index]->adjustColorsForFog = ACFF_NONE; - tr.flareShader->stages[index]->stateBits |= GLS_DEPTHTEST_DISABLE; - } - } - - tr.sunShader = R_FindShader( "sun", LIGHTMAP_NONE, qtrue ); -} - -/* -================== -R_InitShaders -================== -*/ -void R_InitShaders( void ) { - ri.Printf( PRINT_ALL, "Initializing Shaders\n" ); - - Com_Memset(hashTable, 0, sizeof(hashTable)); - - CreateInternalShaders(); - - ScanAndLoadShaderFiles(); - - CreateExternalShaders(); -} diff --git a/src/renderer/tr_shadows.c b/src/renderer/tr_shadows.c deleted file mode 100644 index 04777799..00000000 --- a/src/renderer/tr_shadows.c +++ /dev/null @@ -1,344 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#include "tr_local.h" - - -/* - - for a projection shadow: - - point[x] += light vector * ( z - shadow plane ) - point[y] += - point[z] = shadow plane - - 1 0 light[x] / light[z] - -*/ - -typedef struct { - int i2; - int facing; -} edgeDef_t; - -#define MAX_EDGE_DEFS 32 - -static edgeDef_t edgeDefs[SHADER_MAX_VERTEXES][MAX_EDGE_DEFS]; -static int numEdgeDefs[SHADER_MAX_VERTEXES]; -static int facing[SHADER_MAX_INDEXES/3]; - -void R_AddEdgeDef( int i1, int i2, int facing ) { - int c; - - c = numEdgeDefs[ i1 ]; - if ( c == MAX_EDGE_DEFS ) { - return; // overflow - } - edgeDefs[ i1 ][ c ].i2 = i2; - edgeDefs[ i1 ][ c ].facing = facing; - - numEdgeDefs[ i1 ]++; -} - -void R_RenderShadowEdges( void ) { - int i; - -#if 0 - int numTris; - - // dumb way -- render every triangle's edges - numTris = tess.numIndexes / 3; - - for ( i = 0 ; i < numTris ; i++ ) { - int i1, i2, i3; - - if ( !facing[i] ) { - continue; - } - - i1 = tess.indexes[ i*3 + 0 ]; - i2 = tess.indexes[ i*3 + 1 ]; - i3 = tess.indexes[ i*3 + 2 ]; - - qglBegin( GL_TRIANGLE_STRIP ); - qglVertex3fv( tess.xyz[ i1 ] ); - qglVertex3fv( tess.xyz[ i1 + tess.numVertexes ] ); - qglVertex3fv( tess.xyz[ i2 ] ); - qglVertex3fv( tess.xyz[ i2 + tess.numVertexes ] ); - qglVertex3fv( tess.xyz[ i3 ] ); - qglVertex3fv( tess.xyz[ i3 + tess.numVertexes ] ); - qglVertex3fv( tess.xyz[ i1 ] ); - qglVertex3fv( tess.xyz[ i1 + tess.numVertexes ] ); - qglEnd(); - } -#else - int c, c2; - int j, k; - int i2; - int c_edges, c_rejected; - int hit[2]; - - // an edge is NOT a silhouette edge if its face doesn't face the light, - // or if it has a reverse paired edge that also faces the light. - // A well behaved polyhedron would have exactly two faces for each edge, - // but lots of models have dangling edges or overfanned edges - c_edges = 0; - c_rejected = 0; - - for ( i = 0 ; i < tess.numVertexes ; i++ ) { - c = numEdgeDefs[ i ]; - for ( j = 0 ; j < c ; j++ ) { - if ( !edgeDefs[ i ][ j ].facing ) { - continue; - } - - hit[0] = 0; - hit[1] = 0; - - i2 = edgeDefs[ i ][ j ].i2; - c2 = numEdgeDefs[ i2 ]; - for ( k = 0 ; k < c2 ; k++ ) { - if ( edgeDefs[ i2 ][ k ].i2 == i ) { - hit[ edgeDefs[ i2 ][ k ].facing ]++; - } - } - - // if it doesn't share the edge with another front facing - // triangle, it is a sil edge - if ( hit[ 1 ] == 0 ) { - qglBegin( GL_TRIANGLE_STRIP ); - qglVertex3fv( tess.xyz[ i ] ); - qglVertex3fv( tess.xyz[ i + tess.numVertexes ] ); - qglVertex3fv( tess.xyz[ i2 ] ); - qglVertex3fv( tess.xyz[ i2 + tess.numVertexes ] ); - qglEnd(); - c_edges++; - } else { - c_rejected++; - } - } - } -#endif -} - -/* -================= -RB_ShadowTessEnd - -triangleFromEdge[ v1 ][ v2 ] - - - set triangle from edge( v1, v2, tri ) - if ( facing[ triangleFromEdge[ v1 ][ v2 ] ] && !facing[ triangleFromEdge[ v2 ][ v1 ] ) { - } -================= -*/ -void RB_ShadowTessEnd( void ) { - int i; - int numTris; - vec3_t lightDir; - GLboolean rgba[4]; - - // we can only do this if we have enough space in the vertex buffers - if ( tess.numVertexes >= SHADER_MAX_VERTEXES / 2 ) { - return; - } - - if ( glConfig.stencilBits < 4 ) { - return; - } - - VectorCopy( backEnd.currentEntity->lightDir, lightDir ); - - // project vertexes away from light direction - for ( i = 0 ; i < tess.numVertexes ; i++ ) { - VectorMA( tess.xyz[i], -512, lightDir, tess.xyz[i+tess.numVertexes] ); - } - - // decide which triangles face the light - Com_Memset( numEdgeDefs, 0, 4 * tess.numVertexes ); - - numTris = tess.numIndexes / 3; - for ( i = 0 ; i < numTris ; i++ ) { - int i1, i2, i3; - vec3_t d1, d2, normal; - float *v1, *v2, *v3; - float d; - - i1 = tess.indexes[ i*3 + 0 ]; - i2 = tess.indexes[ i*3 + 1 ]; - i3 = tess.indexes[ i*3 + 2 ]; - - v1 = tess.xyz[ i1 ]; - v2 = tess.xyz[ i2 ]; - v3 = tess.xyz[ i3 ]; - - VectorSubtract( v2, v1, d1 ); - VectorSubtract( v3, v1, d2 ); - CrossProduct( d1, d2, normal ); - - d = DotProduct( normal, lightDir ); - if ( d > 0 ) { - facing[ i ] = 1; - } else { - facing[ i ] = 0; - } - - // create the edges - R_AddEdgeDef( i1, i2, facing[ i ] ); - R_AddEdgeDef( i2, i3, facing[ i ] ); - R_AddEdgeDef( i3, i1, facing[ i ] ); - } - - // draw the silhouette edges - - GL_Bind( tr.whiteImage ); - qglEnable( GL_CULL_FACE ); - GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO ); - qglColor3f( 0.2f, 0.2f, 0.2f ); - - // don't write to the color buffer - qglGetBooleanv(GL_COLOR_WRITEMASK, rgba); - qglColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); - - qglEnable( GL_STENCIL_TEST ); - qglStencilFunc( GL_ALWAYS, 1, 255 ); - - // mirrors have the culling order reversed - if ( backEnd.viewParms.isMirror ) { - qglCullFace( GL_FRONT ); - qglStencilOp( GL_KEEP, GL_KEEP, GL_INCR ); - - R_RenderShadowEdges(); - - qglCullFace( GL_BACK ); - qglStencilOp( GL_KEEP, GL_KEEP, GL_DECR ); - - R_RenderShadowEdges(); - } else { - qglCullFace( GL_BACK ); - qglStencilOp( GL_KEEP, GL_KEEP, GL_INCR ); - - R_RenderShadowEdges(); - - qglCullFace( GL_FRONT ); - qglStencilOp( GL_KEEP, GL_KEEP, GL_DECR ); - - R_RenderShadowEdges(); - } - - - // reenable writing to the color buffer - qglColorMask(rgba[0], rgba[1], rgba[2], rgba[3]); -} - - -/* -================= -RB_ShadowFinish - -Darken everything that is is a shadow volume. -We have to delay this until everything has been shadowed, -because otherwise shadows from different body parts would -overlap and double darken. -================= -*/ -void RB_ShadowFinish( void ) { - if ( r_shadows->integer != 2 ) { - return; - } - if ( glConfig.stencilBits < 4 ) { - return; - } - qglEnable( GL_STENCIL_TEST ); - qglStencilFunc( GL_NOTEQUAL, 0, 255 ); - - qglDisable (GL_CLIP_PLANE0); - qglDisable (GL_CULL_FACE); - - GL_Bind( tr.whiteImage ); - - qglLoadIdentity (); - - qglColor3f( 0.6f, 0.6f, 0.6f ); - GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO ); - -// qglColor3f( 1, 0, 0 ); -// GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO ); - - qglBegin( GL_QUADS ); - qglVertex3f( -100, 100, -10 ); - qglVertex3f( 100, 100, -10 ); - qglVertex3f( 100, -100, -10 ); - qglVertex3f( -100, -100, -10 ); - qglEnd (); - - qglColor4f(1,1,1,1); - qglDisable( GL_STENCIL_TEST ); -} - - -/* -================= -RB_ProjectionShadowDeform - -================= -*/ -void RB_ProjectionShadowDeform( void ) { - float *xyz; - int i; - float h; - vec3_t ground; - vec3_t light; - float groundDist; - float d; - vec3_t lightDir; - - xyz = ( float * ) tess.xyz; - - ground[0] = backEnd.or.axis[0][2]; - ground[1] = backEnd.or.axis[1][2]; - ground[2] = backEnd.or.axis[2][2]; - - groundDist = backEnd.or.origin[2] - backEnd.currentEntity->e.shadowPlane; - - VectorCopy( backEnd.currentEntity->lightDir, lightDir ); - d = DotProduct( lightDir, ground ); - // don't let the shadows get too long or go negative - if ( d < 0.5 ) { - VectorMA( lightDir, (0.5 - d), ground, lightDir ); - d = DotProduct( lightDir, ground ); - } - d = 1.0 / d; - - light[0] = lightDir[0] * d; - light[1] = lightDir[1] * d; - light[2] = lightDir[2] * d; - - for ( i = 0; i < tess.numVertexes; i++, xyz += 4 ) { - h = DotProduct( xyz, ground ) + groundDist; - - xyz[0] -= light[0] * h; - xyz[1] -= light[1] * h; - xyz[2] -= light[2] * h; - } -} diff --git a/src/renderer/tr_sky.c b/src/renderer/tr_sky.c deleted file mode 100644 index 3b90fdeb..00000000 --- a/src/renderer/tr_sky.c +++ /dev/null @@ -1,846 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_sky.c -#include "tr_local.h" - -#define SKY_SUBDIVISIONS 8 -#define HALF_SKY_SUBDIVISIONS (SKY_SUBDIVISIONS/2) - -static float s_cloudTexCoords[6][SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1][2]; -static float s_cloudTexP[6][SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1]; - -/* -=================================================================================== - -POLYGON TO BOX SIDE PROJECTION - -=================================================================================== -*/ - -static vec3_t sky_clip[6] = -{ - {1,1,0}, - {1,-1,0}, - {0,-1,1}, - {0,1,1}, - {1,0,1}, - {-1,0,1} -}; - -static float sky_mins[2][6], sky_maxs[2][6]; -static float sky_min, sky_max; - -/* -================ -AddSkyPolygon -================ -*/ -static void AddSkyPolygon (int nump, vec3_t vecs) -{ - int i,j; - vec3_t v, av; - float s, t, dv; - int axis; - float *vp; - // s = [0]/[2], t = [1]/[2] - static int vec_to_st[6][3] = - { - {-2,3,1}, - {2,3,-1}, - - {1,3,2}, - {-1,3,-2}, - - {-2,-1,3}, - {-2,1,-3} - - // {-1,2,3}, - // {1,2,-3} - }; - - // decide which face it maps to - VectorCopy (vec3_origin, v); - for (i=0, vp=vecs ; i av[1] && av[0] > av[2]) - { - if (v[0] < 0) - axis = 1; - else - axis = 0; - } - else if (av[1] > av[2] && av[1] > av[0]) - { - if (v[1] < 0) - axis = 3; - else - axis = 2; - } - else - { - if (v[2] < 0) - axis = 5; - else - axis = 4; - } - - // project new texture coords - for (i=0 ; i 0) - dv = vecs[j - 1]; - else - dv = -vecs[-j - 1]; - if (dv < 0.001) - continue; // don't divide by zero - j = vec_to_st[axis][0]; - if (j < 0) - s = -vecs[-j -1] / dv; - else - s = vecs[j-1] / dv; - j = vec_to_st[axis][1]; - if (j < 0) - t = -vecs[-j -1] / dv; - else - t = vecs[j-1] / dv; - - if (s < sky_mins[0][axis]) - sky_mins[0][axis] = s; - if (t < sky_mins[1][axis]) - sky_mins[1][axis] = t; - if (s > sky_maxs[0][axis]) - sky_maxs[0][axis] = s; - if (t > sky_maxs[1][axis]) - sky_maxs[1][axis] = t; - } -} - -#define ON_EPSILON 0.1f // point on plane side epsilon -#define MAX_CLIP_VERTS 64 -/* -================ -ClipSkyPolygon -================ -*/ -static void ClipSkyPolygon (int nump, vec3_t vecs, int stage) -{ - float *norm; - float *v; - qboolean front, back; - float d, e; - float dists[MAX_CLIP_VERTS]; - int sides[MAX_CLIP_VERTS]; - vec3_t newv[2][MAX_CLIP_VERTS]; - int newc[2]; - int i, j; - - if (nump > MAX_CLIP_VERTS-2) - ri.Error (ERR_DROP, "ClipSkyPolygon: MAX_CLIP_VERTS"); - if (stage == 6) - { // fully clipped, so draw it - AddSkyPolygon (nump, vecs); - return; - } - - front = back = qfalse; - norm = sky_clip[stage]; - for (i=0, v = vecs ; i ON_EPSILON) - { - front = qtrue; - sides[i] = SIDE_FRONT; - } - else if (d < -ON_EPSILON) - { - back = qtrue; - sides[i] = SIDE_BACK; - } - else - sides[i] = SIDE_ON; - dists[i] = d; - } - - if (!front || !back) - { // not clipped - ClipSkyPolygon (nump, vecs, stage+1); - return; - } - - // clip it - sides[i] = sides[0]; - dists[i] = dists[0]; - VectorCopy (vecs, (vecs+(i*3)) ); - newc[0] = newc[1] = 0; - - for (i=0, v = vecs ; inumIndexes; i += 3 ) - { - for (j = 0 ; j < 3 ; j++) - { - VectorSubtract( input->xyz[input->indexes[i+j]], - backEnd.viewParms.or.origin, - p[j] ); - } - ClipSkyPolygon( 3, p[0], 0 ); - } -} - -/* -=================================================================================== - -CLOUD VERTEX GENERATION - -=================================================================================== -*/ - -/* -** MakeSkyVec -** -** Parms: s, t range from -1 to 1 -*/ -static void MakeSkyVec( float s, float t, int axis, float outSt[2], vec3_t outXYZ ) -{ - // 1 = s, 2 = t, 3 = 2048 - static int st_to_vec[6][3] = - { - {3,-1,2}, - {-3,1,2}, - - {1,3,2}, - {-1,-3,2}, - - {-2,-1,3}, // 0 degrees yaw, look straight up - {2,-1,-3} // look straight down - }; - - vec3_t b; - int j, k; - float boxSize; - - boxSize = backEnd.viewParms.zFar / 1.75; // div sqrt(3) - b[0] = s*boxSize; - b[1] = t*boxSize; - b[2] = boxSize; - - for (j=0 ; j<3 ; j++) - { - k = st_to_vec[axis][j]; - if (k < 0) - { - outXYZ[j] = -b[-k - 1]; - } - else - { - outXYZ[j] = b[k - 1]; - } - } - - // avoid bilerp seam - s = (s+1)*0.5; - t = (t+1)*0.5; - if (s < sky_min) - { - s = sky_min; - } - else if (s > sky_max) - { - s = sky_max; - } - - if (t < sky_min) - { - t = sky_min; - } - else if (t > sky_max) - { - t = sky_max; - } - - t = 1.0 - t; - - - if ( outSt ) - { - outSt[0] = s; - outSt[1] = t; - } -} - -static int sky_texorder[6] = {0,2,1,3,4,5}; -static vec3_t s_skyPoints[SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1]; -static float s_skyTexCoords[SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1][2]; - -static void DrawSkySide( struct image_s *image, const int mins[2], const int maxs[2] ) -{ - int s, t; - - GL_Bind( image ); - - for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t < maxs[1]+HALF_SKY_SUBDIVISIONS; t++ ) - { - qglBegin( GL_TRIANGLE_STRIP ); - - for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ ) - { - qglTexCoord2fv( s_skyTexCoords[t][s] ); - qglVertex3fv( s_skyPoints[t][s] ); - - qglTexCoord2fv( s_skyTexCoords[t+1][s] ); - qglVertex3fv( s_skyPoints[t+1][s] ); - } - - qglEnd(); - } -} - -static void DrawSkyBox( shader_t *shader ) -{ - int i; - - sky_min = 0; - sky_max = 1; - - Com_Memset( s_skyTexCoords, 0, sizeof( s_skyTexCoords ) ); - - for (i=0 ; i<6 ; i++) - { - int sky_mins_subd[2], sky_maxs_subd[2]; - int s, t; - - sky_mins[0][i] = floor( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; - sky_mins[1][i] = floor( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; - sky_maxs[0][i] = ceil( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; - sky_maxs[1][i] = ceil( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; - - if ( ( sky_mins[0][i] >= sky_maxs[0][i] ) || - ( sky_mins[1][i] >= sky_maxs[1][i] ) ) - { - continue; - } - - sky_mins_subd[0] = sky_mins[0][i] * HALF_SKY_SUBDIVISIONS; - sky_mins_subd[1] = sky_mins[1][i] * HALF_SKY_SUBDIVISIONS; - sky_maxs_subd[0] = sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS; - sky_maxs_subd[1] = sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS; - - if ( sky_mins_subd[0] < -HALF_SKY_SUBDIVISIONS ) - sky_mins_subd[0] = -HALF_SKY_SUBDIVISIONS; - else if ( sky_mins_subd[0] > HALF_SKY_SUBDIVISIONS ) - sky_mins_subd[0] = HALF_SKY_SUBDIVISIONS; - if ( sky_mins_subd[1] < -HALF_SKY_SUBDIVISIONS ) - sky_mins_subd[1] = -HALF_SKY_SUBDIVISIONS; - else if ( sky_mins_subd[1] > HALF_SKY_SUBDIVISIONS ) - sky_mins_subd[1] = HALF_SKY_SUBDIVISIONS; - - if ( sky_maxs_subd[0] < -HALF_SKY_SUBDIVISIONS ) - sky_maxs_subd[0] = -HALF_SKY_SUBDIVISIONS; - else if ( sky_maxs_subd[0] > HALF_SKY_SUBDIVISIONS ) - sky_maxs_subd[0] = HALF_SKY_SUBDIVISIONS; - if ( sky_maxs_subd[1] < -HALF_SKY_SUBDIVISIONS ) - sky_maxs_subd[1] = -HALF_SKY_SUBDIVISIONS; - else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS ) - sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS; - - // - // iterate through the subdivisions - // - for ( t = sky_mins_subd[1]+HALF_SKY_SUBDIVISIONS; t <= sky_maxs_subd[1]+HALF_SKY_SUBDIVISIONS; t++ ) - { - for ( s = sky_mins_subd[0]+HALF_SKY_SUBDIVISIONS; s <= sky_maxs_subd[0]+HALF_SKY_SUBDIVISIONS; s++ ) - { - MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, - ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, - i, - s_skyTexCoords[t][s], - s_skyPoints[t][s] ); - } - } - - DrawSkySide( shader->sky.outerbox[sky_texorder[i]], - sky_mins_subd, - sky_maxs_subd ); - } - -} - -static void FillCloudySkySide( const int mins[2], const int maxs[2], qboolean addIndexes ) -{ - int s, t; - int vertexStart = tess.numVertexes; - int tHeight, sWidth; - - tHeight = maxs[1] - mins[1] + 1; - sWidth = maxs[0] - mins[0] + 1; - - for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t <= maxs[1]+HALF_SKY_SUBDIVISIONS; t++ ) - { - for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ ) - { - VectorAdd( s_skyPoints[t][s], backEnd.viewParms.or.origin, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = s_skyTexCoords[t][s][0]; - tess.texCoords[tess.numVertexes][0][1] = s_skyTexCoords[t][s][1]; - - tess.numVertexes++; - - if ( tess.numVertexes >= SHADER_MAX_VERTEXES ) - { - ri.Error( ERR_DROP, "SHADER_MAX_VERTEXES hit in FillCloudySkySide()" ); - } - } - } - - // only add indexes for one pass, otherwise it would draw multiple times for each pass - if ( addIndexes ) { - for ( t = 0; t < tHeight-1; t++ ) - { - for ( s = 0; s < sWidth-1; s++ ) - { - tess.indexes[tess.numIndexes] = vertexStart + s + t * ( sWidth ); - tess.numIndexes++; - tess.indexes[tess.numIndexes] = vertexStart + s + ( t + 1 ) * ( sWidth ); - tess.numIndexes++; - tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * ( sWidth ); - tess.numIndexes++; - - tess.indexes[tess.numIndexes] = vertexStart + s + ( t + 1 ) * ( sWidth ); - tess.numIndexes++; - tess.indexes[tess.numIndexes] = vertexStart + s + 1 + ( t + 1 ) * ( sWidth ); - tess.numIndexes++; - tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * ( sWidth ); - tess.numIndexes++; - } - } - } -} - -static void FillCloudBox( const shader_t *shader, int stage ) -{ - int i; - - for ( i =0; i < 6; i++ ) - { - int sky_mins_subd[2], sky_maxs_subd[2]; - int s, t; - float MIN_T; - - if ( 1 ) // FIXME? shader->sky.fullClouds ) - { - MIN_T = -HALF_SKY_SUBDIVISIONS; - - // still don't want to draw the bottom, even if fullClouds - if ( i == 5 ) - continue; - } - else - { - switch( i ) - { - case 0: - case 1: - case 2: - case 3: - MIN_T = -1; - break; - case 5: - // don't draw clouds beneath you - continue; - case 4: // top - default: - MIN_T = -HALF_SKY_SUBDIVISIONS; - break; - } - } - - sky_mins[0][i] = floor( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; - sky_mins[1][i] = floor( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; - sky_maxs[0][i] = ceil( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; - sky_maxs[1][i] = ceil( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; - - if ( ( sky_mins[0][i] >= sky_maxs[0][i] ) || - ( sky_mins[1][i] >= sky_maxs[1][i] ) ) - { - continue; - } - - sky_mins_subd[0] = ri.ftol(sky_mins[0][i] * HALF_SKY_SUBDIVISIONS); - sky_mins_subd[1] = ri.ftol(sky_mins[1][i] * HALF_SKY_SUBDIVISIONS); - sky_maxs_subd[0] = ri.ftol(sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS); - sky_maxs_subd[1] = ri.ftol(sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS); - - if ( sky_mins_subd[0] < -HALF_SKY_SUBDIVISIONS ) - sky_mins_subd[0] = -HALF_SKY_SUBDIVISIONS; - else if ( sky_mins_subd[0] > HALF_SKY_SUBDIVISIONS ) - sky_mins_subd[0] = HALF_SKY_SUBDIVISIONS; - if ( sky_mins_subd[1] < MIN_T ) - sky_mins_subd[1] = MIN_T; - else if ( sky_mins_subd[1] > HALF_SKY_SUBDIVISIONS ) - sky_mins_subd[1] = HALF_SKY_SUBDIVISIONS; - - if ( sky_maxs_subd[0] < -HALF_SKY_SUBDIVISIONS ) - sky_maxs_subd[0] = -HALF_SKY_SUBDIVISIONS; - else if ( sky_maxs_subd[0] > HALF_SKY_SUBDIVISIONS ) - sky_maxs_subd[0] = HALF_SKY_SUBDIVISIONS; - if ( sky_maxs_subd[1] < MIN_T ) - sky_maxs_subd[1] = MIN_T; - else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS ) - sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS; - - // - // iterate through the subdivisions - // - for ( t = sky_mins_subd[1]+HALF_SKY_SUBDIVISIONS; t <= sky_maxs_subd[1]+HALF_SKY_SUBDIVISIONS; t++ ) - { - for ( s = sky_mins_subd[0]+HALF_SKY_SUBDIVISIONS; s <= sky_maxs_subd[0]+HALF_SKY_SUBDIVISIONS; s++ ) - { - MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, - ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, - i, - NULL, - s_skyPoints[t][s] ); - - s_skyTexCoords[t][s][0] = s_cloudTexCoords[i][t][s][0]; - s_skyTexCoords[t][s][1] = s_cloudTexCoords[i][t][s][1]; - } - } - - // only add indexes for first stage - FillCloudySkySide( sky_mins_subd, sky_maxs_subd, ( stage == 0 ) ); - } -} - -/* -** R_BuildCloudData -*/ -void R_BuildCloudData( shaderCommands_t *input ) -{ - int i; - shader_t *shader; - - shader = input->shader; - - assert( shader->isSky ); - - sky_min = 1.0 / 256.0f; // FIXME: not correct? - sky_max = 255.0 / 256.0f; - - // set up for drawing - tess.numIndexes = 0; - tess.numVertexes = 0; - - if ( shader->sky.cloudHeight ) - { - for ( i = 0; i < MAX_SHADER_STAGES; i++ ) - { - if ( !tess.xstages[i] ) { - break; - } - FillCloudBox( shader, i ); - } - } -} - -/* -** R_InitSkyTexCoords -** Called when a sky shader is parsed -*/ -#define SQR( a ) ((a)*(a)) -void R_InitSkyTexCoords( float heightCloud ) -{ - int i, s, t; - float radiusWorld = 4096; - float p; - float sRad, tRad; - vec3_t skyVec; - vec3_t v; - - // init zfar so MakeSkyVec works even though - // a world hasn't been bounded - backEnd.viewParms.zFar = 1024; - - for ( i = 0; i < 6; i++ ) - { - for ( t = 0; t <= SKY_SUBDIVISIONS; t++ ) - { - for ( s = 0; s <= SKY_SUBDIVISIONS; s++ ) - { - // compute vector from view origin to sky side integral point - MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, - ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, - i, - NULL, - skyVec ); - - // compute parametric value 'p' that intersects with cloud layer - p = ( 1.0f / ( 2 * DotProduct( skyVec, skyVec ) ) ) * - ( -2 * skyVec[2] * radiusWorld + - 2 * sqrt( SQR( skyVec[2] ) * SQR( radiusWorld ) + - 2 * SQR( skyVec[0] ) * radiusWorld * heightCloud + - SQR( skyVec[0] ) * SQR( heightCloud ) + - 2 * SQR( skyVec[1] ) * radiusWorld * heightCloud + - SQR( skyVec[1] ) * SQR( heightCloud ) + - 2 * SQR( skyVec[2] ) * radiusWorld * heightCloud + - SQR( skyVec[2] ) * SQR( heightCloud ) ) ); - - s_cloudTexP[i][t][s] = p; - - // compute intersection point based on p - VectorScale( skyVec, p, v ); - v[2] += radiusWorld; - - // compute vector from world origin to intersection point 'v' - VectorNormalize( v ); - - sRad = Q_acos( v[0] ); - tRad = Q_acos( v[1] ); - - s_cloudTexCoords[i][t][s][0] = sRad; - s_cloudTexCoords[i][t][s][1] = tRad; - } - } - } -} - -//====================================================================================== - -/* -** RB_DrawSun -*/ -void RB_DrawSun( void ) { - float size; - float dist; - vec3_t origin, vec1, vec2; - vec3_t temp; - - if ( !backEnd.skyRenderedThisView ) { - return; - } - if ( !r_drawSun->integer ) { - return; - } - qglLoadMatrixf( backEnd.viewParms.world.modelMatrix ); - qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]); - - dist = backEnd.viewParms.zFar / 1.75; // div sqrt(3) - size = dist * 0.4; - - VectorScale( tr.sunDirection, dist, origin ); - PerpendicularVector( vec1, tr.sunDirection ); - CrossProduct( tr.sunDirection, vec1, vec2 ); - - VectorScale( vec1, size, vec1 ); - VectorScale( vec2, size, vec2 ); - - // farthest depth range - qglDepthRange( 1.0, 1.0 ); - - // FIXME: use quad stamp - RB_BeginSurface( tr.sunShader, tess.fogNum ); - VectorCopy( origin, temp ); - VectorSubtract( temp, vec1, temp ); - VectorSubtract( temp, vec2, temp ); - VectorCopy( temp, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 0; - tess.vertexColors[tess.numVertexes][0] = 255; - tess.vertexColors[tess.numVertexes][1] = 255; - tess.vertexColors[tess.numVertexes][2] = 255; - tess.numVertexes++; - - VectorCopy( origin, temp ); - VectorAdd( temp, vec1, temp ); - VectorSubtract( temp, vec2, temp ); - VectorCopy( temp, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 1; - tess.vertexColors[tess.numVertexes][0] = 255; - tess.vertexColors[tess.numVertexes][1] = 255; - tess.vertexColors[tess.numVertexes][2] = 255; - tess.numVertexes++; - - VectorCopy( origin, temp ); - VectorAdd( temp, vec1, temp ); - VectorAdd( temp, vec2, temp ); - VectorCopy( temp, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = 1; - tess.texCoords[tess.numVertexes][0][1] = 1; - tess.vertexColors[tess.numVertexes][0] = 255; - tess.vertexColors[tess.numVertexes][1] = 255; - tess.vertexColors[tess.numVertexes][2] = 255; - tess.numVertexes++; - - VectorCopy( origin, temp ); - VectorSubtract( temp, vec1, temp ); - VectorAdd( temp, vec2, temp ); - VectorCopy( temp, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = 1; - tess.texCoords[tess.numVertexes][0][1] = 0; - tess.vertexColors[tess.numVertexes][0] = 255; - tess.vertexColors[tess.numVertexes][1] = 255; - tess.vertexColors[tess.numVertexes][2] = 255; - tess.numVertexes++; - - tess.indexes[tess.numIndexes++] = 0; - tess.indexes[tess.numIndexes++] = 1; - tess.indexes[tess.numIndexes++] = 2; - tess.indexes[tess.numIndexes++] = 0; - tess.indexes[tess.numIndexes++] = 2; - tess.indexes[tess.numIndexes++] = 3; - - RB_EndSurface(); - - // back to normal depth range - qglDepthRange( 0.0, 1.0 ); -} - - - - -/* -================ -RB_StageIteratorSky - -All of the visible sky triangles are in tess - -Other things could be stuck in here, like birds in the sky, etc -================ -*/ -void RB_StageIteratorSky( void ) { - if ( r_fastsky->integer ) { - return; - } - - // go through all the polygons and project them onto - // the sky box to see which blocks on each side need - // to be drawn - RB_ClipSkyPolygons( &tess ); - - // r_showsky will let all the sky blocks be drawn in - // front of everything to allow developers to see how - // much sky is getting sucked in - if ( r_showsky->integer ) { - qglDepthRange( 0.0, 0.0 ); - } else { - qglDepthRange( 1.0, 1.0 ); - } - - // draw the outer skybox - if ( tess.shader->sky.outerbox[0] && tess.shader->sky.outerbox[0] != tr.defaultImage ) { - qglColor3f( tr.identityLight, tr.identityLight, tr.identityLight ); - - qglPushMatrix (); - GL_State( 0 ); - qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]); - - DrawSkyBox( tess.shader ); - - qglPopMatrix(); - } - - // generate the vertexes for all the clouds, which will be drawn - // by the generic shader routine - R_BuildCloudData( &tess ); - - RB_StageIteratorGeneric(); - - // draw the inner skybox - - - // back to normal depth range - qglDepthRange( 0.0, 1.0 ); - - // note that sky was drawn so we will draw a sun later - backEnd.skyRenderedThisView = qtrue; -} - diff --git a/src/renderer/tr_subs.c b/src/renderer/tr_subs.c deleted file mode 100644 index 6f490128..00000000 --- a/src/renderer/tr_subs.c +++ /dev/null @@ -1,48 +0,0 @@ -/* -=========================================================================== -Copyright (C) 2010 James Canete (use.less01@gmail.com) - -This file is part of Quake III Arena source code. - -Quake III Arena source code is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Quake III Arena source code is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Quake III Arena source code; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_subs.c - common function replacements for modular renderer - -#include "tr_local.h" - -void QDECL Com_Printf( const char *msg, ... ) -{ - va_list argptr; - char text[1024]; - - va_start(argptr, msg); - Q_vsnprintf(text, sizeof(text), msg, argptr); - va_end(argptr); - - ri.Printf(PRINT_ALL, "%s", text); -} - -void QDECL Com_Error( int level, const char *error, ... ) -{ - va_list argptr; - char text[1024]; - - va_start(argptr, error); - Q_vsnprintf(text, sizeof(text), error, argptr); - va_end(argptr); - - ri.Error(level, "%s", text); -} diff --git a/src/renderer/tr_surface.c b/src/renderer/tr_surface.c deleted file mode 100644 index 7a836386..00000000 --- a/src/renderer/tr_surface.c +++ /dev/null @@ -1,1245 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// tr_surf.c -#include "tr_local.h" -#if idppc_altivec && !defined(MACOS_X) -#include -#endif - -/* - - THIS ENTIRE FILE IS BACK END - -backEnd.currentEntity will be valid. - -Tess_Begin has already been called for the surface's shader. - -The modelview matrix will be set. - -It is safe to actually issue drawing commands here if you don't want to -use the shader system. -*/ - - -//============================================================================ - - -/* -============== -RB_CheckOverflow -============== -*/ -void RB_CheckOverflow( int verts, int indexes ) { - if (tess.numVertexes + verts < SHADER_MAX_VERTEXES - && tess.numIndexes + indexes < SHADER_MAX_INDEXES) { - return; - } - - RB_EndSurface(); - - if ( verts >= SHADER_MAX_VERTEXES ) { - ri.Error(ERR_DROP, "RB_CheckOverflow: verts > MAX (%d > %d)", verts, SHADER_MAX_VERTEXES ); - } - if ( indexes >= SHADER_MAX_INDEXES ) { - ri.Error(ERR_DROP, "RB_CheckOverflow: indices > MAX (%d > %d)", indexes, SHADER_MAX_INDEXES ); - } - - RB_BeginSurface(tess.shader, tess.fogNum ); -} - - -/* -============== -RB_AddQuadStampExt -============== -*/ -void RB_AddQuadStampExt( vec3_t origin, vec3_t left, vec3_t up, byte *color, float s1, float t1, float s2, float t2 ) { - vec3_t normal; - int ndx; - - RB_CHECKOVERFLOW( 4, 6 ); - - ndx = tess.numVertexes; - - // triangle indexes for a simple quad - tess.indexes[ tess.numIndexes ] = ndx; - tess.indexes[ tess.numIndexes + 1 ] = ndx + 1; - tess.indexes[ tess.numIndexes + 2 ] = ndx + 3; - - tess.indexes[ tess.numIndexes + 3 ] = ndx + 3; - tess.indexes[ tess.numIndexes + 4 ] = ndx + 1; - tess.indexes[ tess.numIndexes + 5 ] = ndx + 2; - - tess.xyz[ndx][0] = origin[0] + left[0] + up[0]; - tess.xyz[ndx][1] = origin[1] + left[1] + up[1]; - tess.xyz[ndx][2] = origin[2] + left[2] + up[2]; - - tess.xyz[ndx+1][0] = origin[0] - left[0] + up[0]; - tess.xyz[ndx+1][1] = origin[1] - left[1] + up[1]; - tess.xyz[ndx+1][2] = origin[2] - left[2] + up[2]; - - tess.xyz[ndx+2][0] = origin[0] - left[0] - up[0]; - tess.xyz[ndx+2][1] = origin[1] - left[1] - up[1]; - tess.xyz[ndx+2][2] = origin[2] - left[2] - up[2]; - - tess.xyz[ndx+3][0] = origin[0] + left[0] - up[0]; - tess.xyz[ndx+3][1] = origin[1] + left[1] - up[1]; - tess.xyz[ndx+3][2] = origin[2] + left[2] - up[2]; - - - // constant normal all the way around - VectorSubtract( vec3_origin, backEnd.viewParms.or.axis[0], normal ); - - tess.normal[ndx][0] = tess.normal[ndx+1][0] = tess.normal[ndx+2][0] = tess.normal[ndx+3][0] = normal[0]; - tess.normal[ndx][1] = tess.normal[ndx+1][1] = tess.normal[ndx+2][1] = tess.normal[ndx+3][1] = normal[1]; - tess.normal[ndx][2] = tess.normal[ndx+1][2] = tess.normal[ndx+2][2] = tess.normal[ndx+3][2] = normal[2]; - - // standard square texture coordinates - tess.texCoords[ndx][0][0] = tess.texCoords[ndx][1][0] = s1; - tess.texCoords[ndx][0][1] = tess.texCoords[ndx][1][1] = t1; - - tess.texCoords[ndx+1][0][0] = tess.texCoords[ndx+1][1][0] = s2; - tess.texCoords[ndx+1][0][1] = tess.texCoords[ndx+1][1][1] = t1; - - tess.texCoords[ndx+2][0][0] = tess.texCoords[ndx+2][1][0] = s2; - tess.texCoords[ndx+2][0][1] = tess.texCoords[ndx+2][1][1] = t2; - - tess.texCoords[ndx+3][0][0] = tess.texCoords[ndx+3][1][0] = s1; - tess.texCoords[ndx+3][0][1] = tess.texCoords[ndx+3][1][1] = t2; - - // constant color all the way around - // should this be identity and let the shader specify from entity? - * ( unsigned int * ) &tess.vertexColors[ndx] = - * ( unsigned int * ) &tess.vertexColors[ndx+1] = - * ( unsigned int * ) &tess.vertexColors[ndx+2] = - * ( unsigned int * ) &tess.vertexColors[ndx+3] = - * ( unsigned int * )color; - - - tess.numVertexes += 4; - tess.numIndexes += 6; -} - -/* -============== -RB_AddQuadStamp -============== -*/ -void RB_AddQuadStamp( vec3_t origin, vec3_t left, vec3_t up, byte *color ) { - RB_AddQuadStampExt( origin, left, up, color, 0, 0, 1, 1 ); -} - -/* -============== -RB_SurfaceSprite -============== -*/ -static void RB_SurfaceSprite( void ) { - vec3_t left, up; - float radius; - - // calculate the xyz locations for the four corners - radius = backEnd.currentEntity->e.radius; - if ( backEnd.currentEntity->e.rotation == 0 ) { - VectorScale( backEnd.viewParms.or.axis[1], radius, left ); - VectorScale( backEnd.viewParms.or.axis[2], radius, up ); - } else { - float s, c; - float ang; - - ang = M_PI * backEnd.currentEntity->e.rotation / 180; - s = sin( ang ); - c = cos( ang ); - - VectorScale( backEnd.viewParms.or.axis[1], c * radius, left ); - VectorMA( left, -s * radius, backEnd.viewParms.or.axis[2], left ); - - VectorScale( backEnd.viewParms.or.axis[2], c * radius, up ); - VectorMA( up, s * radius, backEnd.viewParms.or.axis[1], up ); - } - if ( backEnd.viewParms.isMirror ) { - VectorSubtract( vec3_origin, left, left ); - } - - RB_AddQuadStamp( backEnd.currentEntity->e.origin, left, up, backEnd.currentEntity->e.shaderRGBA ); -} - - -/* -============= -RB_SurfacePolychain -============= -*/ -static void RB_SurfacePolychain( srfPoly_t *p ) { - int i; - int numv; - - RB_CHECKOVERFLOW( p->numVerts, 3*(p->numVerts - 2) ); - - // fan triangles into the tess array - numv = tess.numVertexes; - for ( i = 0; i < p->numVerts; i++ ) { - VectorCopy( p->verts[i].xyz, tess.xyz[numv] ); - tess.texCoords[numv][0][0] = p->verts[i].st[0]; - tess.texCoords[numv][0][1] = p->verts[i].st[1]; - *(int *)&tess.vertexColors[numv] = *(int *)p->verts[ i ].modulate; - - numv++; - } - - // generate fan indexes into the tess array - for ( i = 0; i < p->numVerts-2; i++ ) { - tess.indexes[tess.numIndexes + 0] = tess.numVertexes; - tess.indexes[tess.numIndexes + 1] = tess.numVertexes + i + 1; - tess.indexes[tess.numIndexes + 2] = tess.numVertexes + i + 2; - tess.numIndexes += 3; - } - - tess.numVertexes = numv; -} - - -/* -============= -RB_SurfaceTriangles -============= -*/ -static void RB_SurfaceTriangles( srfTriangles_t *srf ) { - int i; - drawVert_t *dv; - float *xyz, *normal, *texCoords; - byte *color; - int dlightBits; - qboolean needsNormal; - - dlightBits = srf->dlightBits; - tess.dlightBits |= dlightBits; - - RB_CHECKOVERFLOW( srf->numVerts, srf->numIndexes ); - - for ( i = 0 ; i < srf->numIndexes ; i += 3 ) { - tess.indexes[ tess.numIndexes + i + 0 ] = tess.numVertexes + srf->indexes[ i + 0 ]; - tess.indexes[ tess.numIndexes + i + 1 ] = tess.numVertexes + srf->indexes[ i + 1 ]; - tess.indexes[ tess.numIndexes + i + 2 ] = tess.numVertexes + srf->indexes[ i + 2 ]; - } - tess.numIndexes += srf->numIndexes; - - dv = srf->verts; - xyz = tess.xyz[ tess.numVertexes ]; - normal = tess.normal[ tess.numVertexes ]; - texCoords = tess.texCoords[ tess.numVertexes ][0]; - color = tess.vertexColors[ tess.numVertexes ]; - needsNormal = tess.shader->needsNormal; - - for ( i = 0 ; i < srf->numVerts ; i++, dv++, xyz += 4, normal += 4, texCoords += 4, color += 4 ) { - xyz[0] = dv->xyz[0]; - xyz[1] = dv->xyz[1]; - xyz[2] = dv->xyz[2]; - - if ( needsNormal ) { - normal[0] = dv->normal[0]; - normal[1] = dv->normal[1]; - normal[2] = dv->normal[2]; - } - - texCoords[0] = dv->st[0]; - texCoords[1] = dv->st[1]; - - texCoords[2] = dv->lightmap[0]; - texCoords[3] = dv->lightmap[1]; - - *(int *)color = *(int *)dv->color; - } - - for ( i = 0 ; i < srf->numVerts ; i++ ) { - tess.vertexDlightBits[ tess.numVertexes + i] = dlightBits; - } - - tess.numVertexes += srf->numVerts; -} - - - -/* -============== -RB_SurfaceBeam -============== -*/ -static void RB_SurfaceBeam( void ) -{ -#define NUM_BEAM_SEGS 6 - refEntity_t *e; - int i; - vec3_t perpvec; - vec3_t direction, normalized_direction; - vec3_t start_points[NUM_BEAM_SEGS], end_points[NUM_BEAM_SEGS]; - vec3_t oldorigin, origin; - - e = &backEnd.currentEntity->e; - - oldorigin[0] = e->oldorigin[0]; - oldorigin[1] = e->oldorigin[1]; - oldorigin[2] = e->oldorigin[2]; - - origin[0] = e->origin[0]; - origin[1] = e->origin[1]; - origin[2] = e->origin[2]; - - normalized_direction[0] = direction[0] = oldorigin[0] - origin[0]; - normalized_direction[1] = direction[1] = oldorigin[1] - origin[1]; - normalized_direction[2] = direction[2] = oldorigin[2] - origin[2]; - - if ( VectorNormalize( normalized_direction ) == 0 ) - return; - - PerpendicularVector( perpvec, normalized_direction ); - - VectorScale( perpvec, 4, perpvec ); - - for ( i = 0; i < NUM_BEAM_SEGS ; i++ ) - { - RotatePointAroundVector( start_points[i], normalized_direction, perpvec, (360.0/NUM_BEAM_SEGS)*i ); -// VectorAdd( start_points[i], origin, start_points[i] ); - VectorAdd( start_points[i], direction, end_points[i] ); - } - - GL_Bind( tr.whiteImage ); - - GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); - - qglColor3f( 1, 0, 0 ); - - qglBegin( GL_TRIANGLE_STRIP ); - for ( i = 0; i <= NUM_BEAM_SEGS; i++ ) { - qglVertex3fv( start_points[ i % NUM_BEAM_SEGS] ); - qglVertex3fv( end_points[ i % NUM_BEAM_SEGS] ); - } - qglEnd(); -} - -//================================================================================ - -static void DoRailCore( const vec3_t start, const vec3_t end, const vec3_t up, float len, float spanWidth ) -{ - float spanWidth2; - int vbase; - float t = len / 256.0f; - - vbase = tess.numVertexes; - - spanWidth2 = -spanWidth; - - // FIXME: use quad stamp? - VectorMA( start, spanWidth, up, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 0; - tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0] * 0.25; - tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1] * 0.25; - tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2] * 0.25; - tess.numVertexes++; - - VectorMA( start, spanWidth2, up, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = 0; - tess.texCoords[tess.numVertexes][0][1] = 1; - tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0]; - tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1]; - tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2]; - tess.numVertexes++; - - VectorMA( end, spanWidth, up, tess.xyz[tess.numVertexes] ); - - tess.texCoords[tess.numVertexes][0][0] = t; - tess.texCoords[tess.numVertexes][0][1] = 0; - tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0]; - tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1]; - tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2]; - tess.numVertexes++; - - VectorMA( end, spanWidth2, up, tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = t; - tess.texCoords[tess.numVertexes][0][1] = 1; - tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0]; - tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1]; - tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2]; - tess.numVertexes++; - - tess.indexes[tess.numIndexes++] = vbase; - tess.indexes[tess.numIndexes++] = vbase + 1; - tess.indexes[tess.numIndexes++] = vbase + 2; - - tess.indexes[tess.numIndexes++] = vbase + 2; - tess.indexes[tess.numIndexes++] = vbase + 1; - tess.indexes[tess.numIndexes++] = vbase + 3; -} - -static void DoRailDiscs( int numSegs, const vec3_t start, const vec3_t dir, const vec3_t right, const vec3_t up ) -{ - int i; - vec3_t pos[4]; - vec3_t v; - int spanWidth = r_railWidth->integer; - float c, s; - float scale; - - if ( numSegs > 1 ) - numSegs--; - if ( !numSegs ) - return; - - scale = 0.25; - - for ( i = 0; i < 4; i++ ) - { - c = cos( DEG2RAD( 45 + i * 90 ) ); - s = sin( DEG2RAD( 45 + i * 90 ) ); - v[0] = ( right[0] * c + up[0] * s ) * scale * spanWidth; - v[1] = ( right[1] * c + up[1] * s ) * scale * spanWidth; - v[2] = ( right[2] * c + up[2] * s ) * scale * spanWidth; - VectorAdd( start, v, pos[i] ); - - if ( numSegs > 1 ) - { - // offset by 1 segment if we're doing a long distance shot - VectorAdd( pos[i], dir, pos[i] ); - } - } - - for ( i = 0; i < numSegs; i++ ) - { - int j; - - RB_CHECKOVERFLOW( 4, 6 ); - - for ( j = 0; j < 4; j++ ) - { - VectorCopy( pos[j], tess.xyz[tess.numVertexes] ); - tess.texCoords[tess.numVertexes][0][0] = ( j < 2 ); - tess.texCoords[tess.numVertexes][0][1] = ( j && j != 3 ); - tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0]; - tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1]; - tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2]; - tess.numVertexes++; - - VectorAdd( pos[j], dir, pos[j] ); - } - - tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 0; - tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 1; - tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 3; - tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 3; - tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 1; - tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 2; - } -} - -/* -** RB_SurfaceRailRinges -*/ -static void RB_SurfaceRailRings( void ) { - refEntity_t *e; - int numSegs; - int len; - vec3_t vec; - vec3_t right, up; - vec3_t start, end; - - e = &backEnd.currentEntity->e; - - VectorCopy( e->oldorigin, start ); - VectorCopy( e->origin, end ); - - // compute variables - VectorSubtract( end, start, vec ); - len = VectorNormalize( vec ); - MakeNormalVectors( vec, right, up ); - numSegs = ( len ) / r_railSegmentLength->value; - if ( numSegs <= 0 ) { - numSegs = 1; - } - - VectorScale( vec, r_railSegmentLength->value, vec ); - - DoRailDiscs( numSegs, start, vec, right, up ); -} - -/* -** RB_SurfaceRailCore -*/ -static void RB_SurfaceRailCore( void ) { - refEntity_t *e; - int len; - vec3_t right; - vec3_t vec; - vec3_t start, end; - vec3_t v1, v2; - - e = &backEnd.currentEntity->e; - - VectorCopy( e->oldorigin, start ); - VectorCopy( e->origin, end ); - - VectorSubtract( end, start, vec ); - len = VectorNormalize( vec ); - - // compute side vector - VectorSubtract( start, backEnd.viewParms.or.origin, v1 ); - VectorNormalize( v1 ); - VectorSubtract( end, backEnd.viewParms.or.origin, v2 ); - VectorNormalize( v2 ); - CrossProduct( v1, v2, right ); - VectorNormalize( right ); - - DoRailCore( start, end, right, len, r_railCoreWidth->integer ); -} - -/* -** RB_SurfaceLightningBolt -*/ -static void RB_SurfaceLightningBolt( void ) { - refEntity_t *e; - int len; - vec3_t right; - vec3_t vec; - vec3_t start, end; - vec3_t v1, v2; - int i; - - e = &backEnd.currentEntity->e; - - VectorCopy( e->oldorigin, end ); - VectorCopy( e->origin, start ); - - // compute variables - VectorSubtract( end, start, vec ); - len = VectorNormalize( vec ); - - // compute side vector - VectorSubtract( start, backEnd.viewParms.or.origin, v1 ); - VectorNormalize( v1 ); - VectorSubtract( end, backEnd.viewParms.or.origin, v2 ); - VectorNormalize( v2 ); - CrossProduct( v1, v2, right ); - VectorNormalize( right ); - - for ( i = 0 ; i < 4 ; i++ ) { - vec3_t temp; - - DoRailCore( start, end, right, len, 8 ); - RotatePointAroundVector( temp, vec, right, 45 ); - VectorCopy( temp, right ); - } -} - -/* -** VectorArrayNormalize -* -* The inputs to this routing seem to always be close to length = 1.0 (about 0.6 to 2.0) -* This means that we don't have to worry about zero length or enormously long vectors. -*/ -static void VectorArrayNormalize(vec4_t *normals, unsigned int count) -{ -// assert(count); - -#if idppc - { - register float half = 0.5; - register float one = 1.0; - float *components = (float *)normals; - - // Vanilla PPC code, but since PPC has a reciprocal square root estimate instruction, - // runs *much* faster than calling sqrt(). We'll use a single Newton-Raphson - // refinement step to get a little more precision. This seems to yeild results - // that are correct to 3 decimal places and usually correct to at least 4 (sometimes 5). - // (That is, for the given input range of about 0.6 to 2.0). - do { - float x, y, z; - float B, y0, y1; - - x = components[0]; - y = components[1]; - z = components[2]; - components += 4; - B = x*x + y*y + z*z; - -#ifdef __GNUC__ - asm("frsqrte %0,%1" : "=f" (y0) : "f" (B)); -#else - y0 = __frsqrte(B); -#endif - y1 = y0 + half*y0*(one - B*y0*y0); - - x = x * y1; - y = y * y1; - components[-4] = x; - z = z * y1; - components[-3] = y; - components[-2] = z; - } while(count--); - } -#else // No assembly version for this architecture, or C_ONLY defined - // given the input, it's safe to call VectorNormalizeFast - while (count--) { - VectorNormalizeFast(normals[0]); - normals++; - } -#endif - -} - - - -/* -** LerpMeshVertexes -*/ -#if idppc_altivec -static void LerpMeshVertexes_altivec(md3Surface_t *surf, float backlerp) -{ - short *oldXyz, *newXyz, *oldNormals, *newNormals; - float *outXyz, *outNormal; - float oldXyzScale QALIGN(16); - float newXyzScale QALIGN(16); - float oldNormalScale QALIGN(16); - float newNormalScale QALIGN(16); - int vertNum; - unsigned lat, lng; - int numVerts; - - outXyz = tess.xyz[tess.numVertexes]; - outNormal = tess.normal[tess.numVertexes]; - - newXyz = (short *)((byte *)surf + surf->ofsXyzNormals) - + (backEnd.currentEntity->e.frame * surf->numVerts * 4); - newNormals = newXyz + 3; - - newXyzScale = MD3_XYZ_SCALE * (1.0 - backlerp); - newNormalScale = 1.0 - backlerp; - - numVerts = surf->numVerts; - - if ( backlerp == 0 ) { - vector signed short newNormalsVec0; - vector signed short newNormalsVec1; - vector signed int newNormalsIntVec; - vector float newNormalsFloatVec; - vector float newXyzScaleVec; - vector unsigned char newNormalsLoadPermute; - vector unsigned char newNormalsStorePermute; - vector float zero; - - newNormalsStorePermute = vec_lvsl(0,(float *)&newXyzScaleVec); - newXyzScaleVec = *(vector float *)&newXyzScale; - newXyzScaleVec = vec_perm(newXyzScaleVec,newXyzScaleVec,newNormalsStorePermute); - newXyzScaleVec = vec_splat(newXyzScaleVec,0); - newNormalsLoadPermute = vec_lvsl(0,newXyz); - newNormalsStorePermute = vec_lvsr(0,outXyz); - zero = (vector float)vec_splat_s8(0); - // - // just copy the vertexes - // - for (vertNum=0 ; vertNum < numVerts ; vertNum++, - newXyz += 4, newNormals += 4, - outXyz += 4, outNormal += 4) - { - newNormalsLoadPermute = vec_lvsl(0,newXyz); - newNormalsStorePermute = vec_lvsr(0,outXyz); - newNormalsVec0 = vec_ld(0,newXyz); - newNormalsVec1 = vec_ld(16,newXyz); - newNormalsVec0 = vec_perm(newNormalsVec0,newNormalsVec1,newNormalsLoadPermute); - newNormalsIntVec = vec_unpackh(newNormalsVec0); - newNormalsFloatVec = vec_ctf(newNormalsIntVec,0); - newNormalsFloatVec = vec_madd(newNormalsFloatVec,newXyzScaleVec,zero); - newNormalsFloatVec = vec_perm(newNormalsFloatVec,newNormalsFloatVec,newNormalsStorePermute); - //outXyz[0] = newXyz[0] * newXyzScale; - //outXyz[1] = newXyz[1] * newXyzScale; - //outXyz[2] = newXyz[2] * newXyzScale; - - lat = ( newNormals[0] >> 8 ) & 0xff; - lng = ( newNormals[0] & 0xff ); - lat *= (FUNCTABLE_SIZE/256); - lng *= (FUNCTABLE_SIZE/256); - - // decode X as cos( lat ) * sin( long ) - // decode Y as sin( lat ) * sin( long ) - // decode Z as cos( long ) - - outNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; - outNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; - outNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; - - vec_ste(newNormalsFloatVec,0,outXyz); - vec_ste(newNormalsFloatVec,4,outXyz); - vec_ste(newNormalsFloatVec,8,outXyz); - } - } else { - // - // interpolate and copy the vertex and normal - // - oldXyz = (short *)((byte *)surf + surf->ofsXyzNormals) - + (backEnd.currentEntity->e.oldframe * surf->numVerts * 4); - oldNormals = oldXyz + 3; - - oldXyzScale = MD3_XYZ_SCALE * backlerp; - oldNormalScale = backlerp; - - for (vertNum=0 ; vertNum < numVerts ; vertNum++, - oldXyz += 4, newXyz += 4, oldNormals += 4, newNormals += 4, - outXyz += 4, outNormal += 4) - { - vec3_t uncompressedOldNormal, uncompressedNewNormal; - - // interpolate the xyz - outXyz[0] = oldXyz[0] * oldXyzScale + newXyz[0] * newXyzScale; - outXyz[1] = oldXyz[1] * oldXyzScale + newXyz[1] * newXyzScale; - outXyz[2] = oldXyz[2] * oldXyzScale + newXyz[2] * newXyzScale; - - // FIXME: interpolate lat/long instead? - lat = ( newNormals[0] >> 8 ) & 0xff; - lng = ( newNormals[0] & 0xff ); - lat *= 4; - lng *= 4; - uncompressedNewNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; - uncompressedNewNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; - uncompressedNewNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; - - lat = ( oldNormals[0] >> 8 ) & 0xff; - lng = ( oldNormals[0] & 0xff ); - lat *= 4; - lng *= 4; - - uncompressedOldNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; - uncompressedOldNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; - uncompressedOldNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; - - outNormal[0] = uncompressedOldNormal[0] * oldNormalScale + uncompressedNewNormal[0] * newNormalScale; - outNormal[1] = uncompressedOldNormal[1] * oldNormalScale + uncompressedNewNormal[1] * newNormalScale; - outNormal[2] = uncompressedOldNormal[2] * oldNormalScale + uncompressedNewNormal[2] * newNormalScale; - -// VectorNormalize (outNormal); - } - VectorArrayNormalize((vec4_t *)tess.normal[tess.numVertexes], numVerts); - } -} -#endif - -static void LerpMeshVertexes_scalar(md3Surface_t *surf, float backlerp) -{ - short *oldXyz, *newXyz, *oldNormals, *newNormals; - float *outXyz, *outNormal; - float oldXyzScale, newXyzScale; - float oldNormalScale, newNormalScale; - int vertNum; - unsigned lat, lng; - int numVerts; - - outXyz = tess.xyz[tess.numVertexes]; - outNormal = tess.normal[tess.numVertexes]; - - newXyz = (short *)((byte *)surf + surf->ofsXyzNormals) - + (backEnd.currentEntity->e.frame * surf->numVerts * 4); - newNormals = newXyz + 3; - - newXyzScale = MD3_XYZ_SCALE * (1.0 - backlerp); - newNormalScale = 1.0 - backlerp; - - numVerts = surf->numVerts; - - if ( backlerp == 0 ) { - // - // just copy the vertexes - // - for (vertNum=0 ; vertNum < numVerts ; vertNum++, - newXyz += 4, newNormals += 4, - outXyz += 4, outNormal += 4) - { - - outXyz[0] = newXyz[0] * newXyzScale; - outXyz[1] = newXyz[1] * newXyzScale; - outXyz[2] = newXyz[2] * newXyzScale; - - lat = ( newNormals[0] >> 8 ) & 0xff; - lng = ( newNormals[0] & 0xff ); - lat *= (FUNCTABLE_SIZE/256); - lng *= (FUNCTABLE_SIZE/256); - - // decode X as cos( lat ) * sin( long ) - // decode Y as sin( lat ) * sin( long ) - // decode Z as cos( long ) - - outNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; - outNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; - outNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; - } - } else { - // - // interpolate and copy the vertex and normal - // - oldXyz = (short *)((byte *)surf + surf->ofsXyzNormals) - + (backEnd.currentEntity->e.oldframe * surf->numVerts * 4); - oldNormals = oldXyz + 3; - - oldXyzScale = MD3_XYZ_SCALE * backlerp; - oldNormalScale = backlerp; - - for (vertNum=0 ; vertNum < numVerts ; vertNum++, - oldXyz += 4, newXyz += 4, oldNormals += 4, newNormals += 4, - outXyz += 4, outNormal += 4) - { - vec3_t uncompressedOldNormal, uncompressedNewNormal; - - // interpolate the xyz - outXyz[0] = oldXyz[0] * oldXyzScale + newXyz[0] * newXyzScale; - outXyz[1] = oldXyz[1] * oldXyzScale + newXyz[1] * newXyzScale; - outXyz[2] = oldXyz[2] * oldXyzScale + newXyz[2] * newXyzScale; - - // FIXME: interpolate lat/long instead? - lat = ( newNormals[0] >> 8 ) & 0xff; - lng = ( newNormals[0] & 0xff ); - lat *= 4; - lng *= 4; - uncompressedNewNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; - uncompressedNewNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; - uncompressedNewNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; - - lat = ( oldNormals[0] >> 8 ) & 0xff; - lng = ( oldNormals[0] & 0xff ); - lat *= 4; - lng *= 4; - - uncompressedOldNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; - uncompressedOldNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; - uncompressedOldNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; - - outNormal[0] = uncompressedOldNormal[0] * oldNormalScale + uncompressedNewNormal[0] * newNormalScale; - outNormal[1] = uncompressedOldNormal[1] * oldNormalScale + uncompressedNewNormal[1] * newNormalScale; - outNormal[2] = uncompressedOldNormal[2] * oldNormalScale + uncompressedNewNormal[2] * newNormalScale; - -// VectorNormalize (outNormal); - } - VectorArrayNormalize((vec4_t *)tess.normal[tess.numVertexes], numVerts); - } -} - -static void LerpMeshVertexes(md3Surface_t *surf, float backlerp) -{ -#if idppc_altivec - if (com_altivec->integer) { - // must be in a seperate function or G3 systems will crash. - LerpMeshVertexes_altivec( surf, backlerp ); - return; - } -#endif // idppc_altivec - LerpMeshVertexes_scalar( surf, backlerp ); -} - - -/* -============= -RB_SurfaceMesh -============= -*/ -static void RB_SurfaceMesh(md3Surface_t *surface) { - int j; - float backlerp; - int *triangles; - float *texCoords; - int indexes; - int Bob, Doug; - int numVerts; - - if ( backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame ) { - backlerp = 0; - } else { - backlerp = backEnd.currentEntity->e.backlerp; - } - - RB_CHECKOVERFLOW( surface->numVerts, surface->numTriangles*3 ); - - LerpMeshVertexes (surface, backlerp); - - triangles = (int *) ((byte *)surface + surface->ofsTriangles); - indexes = surface->numTriangles * 3; - Bob = tess.numIndexes; - Doug = tess.numVertexes; - for (j = 0 ; j < indexes ; j++) { - tess.indexes[Bob + j] = Doug + triangles[j]; - } - tess.numIndexes += indexes; - - texCoords = (float *) ((byte *)surface + surface->ofsSt); - - numVerts = surface->numVerts; - for ( j = 0; j < numVerts; j++ ) { - tess.texCoords[Doug + j][0][0] = texCoords[j*2+0]; - tess.texCoords[Doug + j][0][1] = texCoords[j*2+1]; - // FIXME: fill in lightmapST for completeness? - } - - tess.numVertexes += surface->numVerts; - -} - - -/* -============== -RB_SurfaceFace -============== -*/ -static void RB_SurfaceFace( srfSurfaceFace_t *surf ) { - int i; - unsigned *indices, *tessIndexes; - float *v; - float *normal; - int ndx; - int Bob; - int numPoints; - int dlightBits; - - RB_CHECKOVERFLOW( surf->numPoints, surf->numIndices ); - - dlightBits = surf->dlightBits; - tess.dlightBits |= dlightBits; - - indices = ( unsigned * ) ( ( ( char * ) surf ) + surf->ofsIndices ); - - Bob = tess.numVertexes; - tessIndexes = tess.indexes + tess.numIndexes; - for ( i = surf->numIndices-1 ; i >= 0 ; i-- ) { - tessIndexes[i] = indices[i] + Bob; - } - - tess.numIndexes += surf->numIndices; - - numPoints = surf->numPoints; - - if ( tess.shader->needsNormal ) { - normal = surf->plane.normal; - for ( i = 0, ndx = tess.numVertexes; i < numPoints; i++, ndx++ ) { - VectorCopy( normal, tess.normal[ndx] ); - } - } - - for ( i = 0, v = surf->points[0], ndx = tess.numVertexes; i < numPoints; i++, v += VERTEXSIZE, ndx++ ) { - VectorCopy( v, tess.xyz[ndx]); - tess.texCoords[ndx][0][0] = v[3]; - tess.texCoords[ndx][0][1] = v[4]; - tess.texCoords[ndx][1][0] = v[5]; - tess.texCoords[ndx][1][1] = v[6]; - * ( unsigned int * ) &tess.vertexColors[ndx] = * ( unsigned int * ) &v[7]; - tess.vertexDlightBits[ndx] = dlightBits; - } - - - tess.numVertexes += surf->numPoints; -} - - -static float LodErrorForVolume( vec3_t local, float radius ) { - vec3_t world; - float d; - - // never let it go negative - if ( r_lodCurveError->value < 0 ) { - return 0; - } - - world[0] = local[0] * backEnd.or.axis[0][0] + local[1] * backEnd.or.axis[1][0] + - local[2] * backEnd.or.axis[2][0] + backEnd.or.origin[0]; - world[1] = local[0] * backEnd.or.axis[0][1] + local[1] * backEnd.or.axis[1][1] + - local[2] * backEnd.or.axis[2][1] + backEnd.or.origin[1]; - world[2] = local[0] * backEnd.or.axis[0][2] + local[1] * backEnd.or.axis[1][2] + - local[2] * backEnd.or.axis[2][2] + backEnd.or.origin[2]; - - VectorSubtract( world, backEnd.viewParms.or.origin, world ); - d = DotProduct( world, backEnd.viewParms.or.axis[0] ); - - if ( d < 0 ) { - d = -d; - } - d -= radius; - if ( d < 1 ) { - d = 1; - } - - return r_lodCurveError->value / d; -} - -/* -============= -RB_SurfaceGrid - -Just copy the grid of points and triangulate -============= -*/ -static void RB_SurfaceGrid( srfGridMesh_t *cv ) { - int i, j; - float *xyz; - float *texCoords; - float *normal; - unsigned char *color; - drawVert_t *dv; - int rows, irows, vrows; - int used; - int widthTable[MAX_GRID_SIZE]; - int heightTable[MAX_GRID_SIZE]; - float lodError; - int lodWidth, lodHeight; - int numVertexes; - int dlightBits; - int *vDlightBits; - qboolean needsNormal; - - dlightBits = cv->dlightBits; - tess.dlightBits |= dlightBits; - - // determine the allowable discrepance - lodError = LodErrorForVolume( cv->lodOrigin, cv->lodRadius ); - - // determine which rows and columns of the subdivision - // we are actually going to use - widthTable[0] = 0; - lodWidth = 1; - for ( i = 1 ; i < cv->width-1 ; i++ ) { - if ( cv->widthLodError[i] <= lodError ) { - widthTable[lodWidth] = i; - lodWidth++; - } - } - widthTable[lodWidth] = cv->width-1; - lodWidth++; - - heightTable[0] = 0; - lodHeight = 1; - for ( i = 1 ; i < cv->height-1 ; i++ ) { - if ( cv->heightLodError[i] <= lodError ) { - heightTable[lodHeight] = i; - lodHeight++; - } - } - heightTable[lodHeight] = cv->height-1; - lodHeight++; - - - // very large grids may have more points or indexes than can be fit - // in the tess structure, so we may have to issue it in multiple passes - - used = 0; - while ( used < lodHeight - 1 ) { - // see how many rows of both verts and indexes we can add without overflowing - do { - vrows = ( SHADER_MAX_VERTEXES - tess.numVertexes ) / lodWidth; - irows = ( SHADER_MAX_INDEXES - tess.numIndexes ) / ( lodWidth * 6 ); - - // if we don't have enough space for at least one strip, flush the buffer - if ( vrows < 2 || irows < 1 ) { - RB_EndSurface(); - RB_BeginSurface(tess.shader, tess.fogNum ); - } else { - break; - } - } while ( 1 ); - - rows = irows; - if ( vrows < irows + 1 ) { - rows = vrows - 1; - } - if ( used + rows > lodHeight ) { - rows = lodHeight - used; - } - - numVertexes = tess.numVertexes; - - xyz = tess.xyz[numVertexes]; - normal = tess.normal[numVertexes]; - texCoords = tess.texCoords[numVertexes][0]; - color = ( unsigned char * ) &tess.vertexColors[numVertexes]; - vDlightBits = &tess.vertexDlightBits[numVertexes]; - needsNormal = tess.shader->needsNormal; - - for ( i = 0 ; i < rows ; i++ ) { - for ( j = 0 ; j < lodWidth ; j++ ) { - dv = cv->verts + heightTable[ used + i ] * cv->width - + widthTable[ j ]; - - xyz[0] = dv->xyz[0]; - xyz[1] = dv->xyz[1]; - xyz[2] = dv->xyz[2]; - texCoords[0] = dv->st[0]; - texCoords[1] = dv->st[1]; - texCoords[2] = dv->lightmap[0]; - texCoords[3] = dv->lightmap[1]; - if ( needsNormal ) { - normal[0] = dv->normal[0]; - normal[1] = dv->normal[1]; - normal[2] = dv->normal[2]; - } - * ( unsigned int * ) color = * ( unsigned int * ) dv->color; - *vDlightBits++ = dlightBits; - xyz += 4; - normal += 4; - texCoords += 4; - color += 4; - } - } - - - // add the indexes - { - int numIndexes; - int w, h; - - h = rows - 1; - w = lodWidth - 1; - numIndexes = tess.numIndexes; - for (i = 0 ; i < h ; i++) { - for (j = 0 ; j < w ; j++) { - int v1, v2, v3, v4; - - // vertex order to be reckognized as tristrips - v1 = numVertexes + i*lodWidth + j + 1; - v2 = v1 - 1; - v3 = v2 + lodWidth; - v4 = v3 + 1; - - tess.indexes[numIndexes] = v2; - tess.indexes[numIndexes+1] = v3; - tess.indexes[numIndexes+2] = v1; - - tess.indexes[numIndexes+3] = v1; - tess.indexes[numIndexes+4] = v3; - tess.indexes[numIndexes+5] = v4; - numIndexes += 6; - } - } - - tess.numIndexes = numIndexes; - } - - tess.numVertexes += rows * lodWidth; - - used += rows - 1; - } -} - - -/* -=========================================================================== - -NULL MODEL - -=========================================================================== -*/ - -/* -=================== -RB_SurfaceAxis - -Draws x/y/z lines from the origin for orientation debugging -=================== -*/ -static void RB_SurfaceAxis( void ) { - GL_Bind( tr.whiteImage ); - qglLineWidth( 3 ); - qglBegin( GL_LINES ); - qglColor3f( 1,0,0 ); - qglVertex3f( 0,0,0 ); - qglVertex3f( 16,0,0 ); - qglColor3f( 0,1,0 ); - qglVertex3f( 0,0,0 ); - qglVertex3f( 0,16,0 ); - qglColor3f( 0,0,1 ); - qglVertex3f( 0,0,0 ); - qglVertex3f( 0,0,16 ); - qglEnd(); - qglLineWidth( 1 ); -} - -//=========================================================================== - -/* -==================== -RB_SurfaceEntity - -Entities that have a single procedurally generated surface -==================== -*/ -static void RB_SurfaceEntity( surfaceType_t *surfType ) { - switch( backEnd.currentEntity->e.reType ) { - case RT_SPRITE: - RB_SurfaceSprite(); - break; - case RT_BEAM: - RB_SurfaceBeam(); - break; - case RT_RAIL_CORE: - RB_SurfaceRailCore(); - break; - case RT_RAIL_RINGS: - RB_SurfaceRailRings(); - break; - case RT_LIGHTNING: - RB_SurfaceLightningBolt(); - break; - default: - RB_SurfaceAxis(); - break; - } - return; -} - -static void RB_SurfaceBad( surfaceType_t *surfType ) { - ri.Printf( PRINT_ALL, "Bad surface tesselated.\n" ); -} - -static void RB_SurfaceFlare(srfFlare_t *surf) -{ - if (r_flares->integer) - RB_AddFlare(surf, tess.fogNum, surf->origin, surf->color, surf->normal); -} - -static void RB_SurfaceDisplayList( srfDisplayList_t *surf ) { - // all apropriate state must be set in RB_BeginSurface - // this isn't implemented yet... - qglCallList( surf->listNum ); -} - -static void RB_SurfaceSkip( void *surf ) { -} - - -void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])( void *) = { - (void(*)(void*))RB_SurfaceBad, // SF_BAD, - (void(*)(void*))RB_SurfaceSkip, // SF_SKIP, - (void(*)(void*))RB_SurfaceFace, // SF_FACE, - (void(*)(void*))RB_SurfaceGrid, // SF_GRID, - (void(*)(void*))RB_SurfaceTriangles, // SF_TRIANGLES, - (void(*)(void*))RB_SurfacePolychain, // SF_POLY, - (void(*)(void*))RB_SurfaceMesh, // SF_MD3, - (void(*)(void*))RB_SurfaceAnim, // SF_MD4, -#ifdef RAVENMD4 - (void(*)(void*))RB_MDRSurfaceAnim, // SF_MDR, -#endif - (void(*)(void*))RB_IQMSurfaceAnim, // SF_IQM, - (void(*)(void*))RB_SurfaceFlare, // SF_FLARE, - (void(*)(void*))RB_SurfaceEntity, // SF_ENTITY - (void(*)(void*))RB_SurfaceDisplayList // SF_DISPLAY_LIST -}; diff --git a/src/renderer/tr_types.h b/src/renderer/tr_types.h deleted file mode 100644 index 0e15c9e8..00000000 --- a/src/renderer/tr_types.h +++ /dev/null @@ -1,221 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -// -#ifndef __TR_TYPES_H -#define __TR_TYPES_H - - -#define MAX_DLIGHTS 32 // can't be increased, because bit flags are used on surfaces - -#define REFENTITYNUM_BITS 10 // can't be increased without changing drawsurf bit packing -#define REFENTITYNUM_MASK ((1<lightingOrigin instead of refEntity->origin - // for lighting. This allows entities to sink into the floor - // with their origin going solid, and allows all parts of a - // player to get the same lighting - -#define RF_SHADOW_PLANE 0x0100 // use refEntity->shadowPlane -#define RF_WRAP_FRAMES 0x0200 // mod the model frames by the maxframes to allow continuous - -// refdef flags -#define RDF_NOWORLDMODEL 0x0001 // used for player configuration screen -#define RDF_HYPERSPACE 0x0004 // teleportation effect - -typedef struct { - vec3_t xyz; - float st[2]; - byte modulate[4]; -} polyVert_t; - -typedef struct poly_s { - qhandle_t hShader; - int numVerts; - polyVert_t *verts; -} poly_t; - -typedef enum { - RT_MODEL, - RT_POLY, - RT_SPRITE, - RT_BEAM, - RT_RAIL_CORE, - RT_RAIL_RINGS, - RT_LIGHTNING, - RT_PORTALSURFACE, // doesn't draw anything, just info for portals - - RT_MAX_REF_ENTITY_TYPE -} refEntityType_t; - -typedef struct { - refEntityType_t reType; - int renderfx; - - qhandle_t hModel; // opaque type outside refresh - - // most recent data - vec3_t lightingOrigin; // so multi-part models can be lit identically (RF_LIGHTING_ORIGIN) - float shadowPlane; // projection shadows go here, stencils go slightly lower - - vec3_t axis[3]; // rotation vectors - qboolean nonNormalizedAxes; // axis are not normalized, i.e. they have scale - float origin[3]; // also used as MODEL_BEAM's "from" - int frame; // also used as MODEL_BEAM's diameter - - // previous data for frame interpolation - float oldorigin[3]; // also used as MODEL_BEAM's "to" - int oldframe; - float backlerp; // 0.0 = current, 1.0 = old - - // texturing - int skinNum; // inline skin index - qhandle_t customSkin; // NULL for default skin - qhandle_t customShader; // use one image for the entire thing - - // misc - byte shaderRGBA[4]; // colors used by rgbgen entity shaders - float shaderTexCoord[2]; // texture coordinates used by tcMod entity modifiers - float shaderTime; // subtracted from refdef time to control effect start times - - // extra sprite information - float radius; - float rotation; -} refEntity_t; - - -#define MAX_RENDER_STRINGS 8 -#define MAX_RENDER_STRING_LENGTH 32 - -typedef struct { - int x, y, width, height; - float fov_x, fov_y; - vec3_t vieworg; - vec3_t viewaxis[3]; // transformation matrix - - // time in milliseconds for shader effects and other time dependent rendering issues - int time; - - int rdflags; // RDF_NOWORLDMODEL, etc - - // 1 bits will prevent the associated area from rendering at all - byte areamask[MAX_MAP_AREA_BYTES]; - - // text messages for deform text shaders - char text[MAX_RENDER_STRINGS][MAX_RENDER_STRING_LENGTH]; -} refdef_t; - - -typedef enum { - STEREO_CENTER, - STEREO_LEFT, - STEREO_RIGHT -} stereoFrame_t; - - -/* -** glconfig_t -** -** Contains variables specific to the OpenGL configuration -** being run right now. These are constant once the OpenGL -** subsystem is initialized. -*/ -typedef enum { - TC_NONE, - TC_S3TC, // this is for the GL_S3_s3tc extension. - TC_S3TC_ARB // this is for the GL_EXT_texture_compression_s3tc extension. -} textureCompression_t; - -typedef enum { - GLDRV_ICD, // driver is integrated with window system - // WARNING: there are tests that check for - // > GLDRV_ICD for minidriverness, so this - // should always be the lowest value in this - // enum set - GLDRV_STANDALONE, // driver is a non-3Dfx standalone driver - GLDRV_VOODOO // driver is a 3Dfx standalone driver -} glDriverType_t; - -typedef enum { - GLHW_GENERIC, // where everthing works the way it should - GLHW_3DFX_2D3D, // Voodoo Banshee or Voodoo3, relevant since if this is - // the hardware type then there can NOT exist a secondary - // display adapter - GLHW_RIVA128, // where you can't interpolate alpha - GLHW_RAGEPRO, // where you can't modulate alpha on alpha textures - GLHW_PERMEDIA2 // where you don't have src*dst -} glHardwareType_t; - -typedef struct { - char renderer_string[MAX_STRING_CHARS]; - char vendor_string[MAX_STRING_CHARS]; - char version_string[MAX_STRING_CHARS]; - char extensions_string[BIG_INFO_STRING]; - - int maxTextureSize; // queried from GL - int numTextureUnits; // multitexture ability - - int colorBits, depthBits, stencilBits; - - glDriverType_t driverType; - glHardwareType_t hardwareType; - - qboolean deviceSupportsGamma; - textureCompression_t textureCompression; - qboolean textureEnvAddAvailable; - - int vidWidth, vidHeight; - // aspect is the screen's physical width / height, which may be different - // than scrWidth / scrHeight if the pixels are non-square - // normal screens should be 4/3, but wide aspect monitors may be 16/9 - float windowAspect; - float displayAspect; - - int displayFrequency; - - // synonymous with "does rendering consume the entire screen?", therefore - // a Voodoo or Voodoo2 will have this set to TRUE, as will a Win32 ICD that - // used CDS. - qboolean isFullscreen; - qboolean stereoEnabled; - qboolean textureFilterAnisotropic; - int maxAnisotropy; -} glconfig_t; - -#endif // __TR_TYPES_H diff --git a/src/renderer/tr_world.c b/src/renderer/tr_world.c deleted file mode 100644 index de9715a8..00000000 --- a/src/renderer/tr_world.c +++ /dev/null @@ -1,669 +0,0 @@ -/* -=========================================================================== -Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2009 Darklegion Development - -This file is part of Tremulous. - -Tremulous is free software; you can redistribute it -and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, -or (at your option) any later version. - -Tremulous is distributed in the hope that it will be -useful, but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -=========================================================================== -*/ -#include "tr_local.h" - - - -/* -================= -R_CullTriSurf - -Returns true if the grid is completely culled away. -Also sets the clipped hint bit in tess -================= -*/ -static qboolean R_CullTriSurf( srfTriangles_t *cv ) { - int boxCull; - - boxCull = R_CullLocalBox( cv->bounds ); - - if ( boxCull == CULL_OUT ) { - return qtrue; - } - return qfalse; -} - -/* -================= -R_CullGrid - -Returns true if the grid is completely culled away. -Also sets the clipped hint bit in tess -================= -*/ -static qboolean R_CullGrid( srfGridMesh_t *cv ) { - int boxCull; - int sphereCull; - - if ( r_nocurves->integer ) { - return qtrue; - } - - if ( tr.currentEntityNum != REFENTITYNUM_WORLD ) { - sphereCull = R_CullLocalPointAndRadius( cv->localOrigin, cv->meshRadius ); - } else { - sphereCull = R_CullPointAndRadius( cv->localOrigin, cv->meshRadius ); - } - - // check for trivial reject - if ( sphereCull == CULL_OUT ) - { - tr.pc.c_sphere_cull_patch_out++; - return qtrue; - } - // check bounding box if necessary - else if ( sphereCull == CULL_CLIP ) - { - tr.pc.c_sphere_cull_patch_clip++; - - boxCull = R_CullLocalBox( cv->meshBounds ); - - if ( boxCull == CULL_OUT ) - { - tr.pc.c_box_cull_patch_out++; - return qtrue; - } - else if ( boxCull == CULL_IN ) - { - tr.pc.c_box_cull_patch_in++; - } - else - { - tr.pc.c_box_cull_patch_clip++; - } - } - else - { - tr.pc.c_sphere_cull_patch_in++; - } - - return qfalse; -} - - -/* -================ -R_CullSurface - -Tries to back face cull surfaces before they are lighted or -added to the sorting list. - -This will also allow mirrors on both sides of a model without recursion. -================ -*/ -static qboolean R_CullSurface( surfaceType_t *surface, shader_t *shader ) { - srfSurfaceFace_t *sface; - float d; - - if ( r_nocull->integer ) { - return qfalse; - } - - if ( *surface == SF_GRID ) { - return R_CullGrid( (srfGridMesh_t *)surface ); - } - - if ( *surface == SF_TRIANGLES ) { - return R_CullTriSurf( (srfTriangles_t *)surface ); - } - - if ( *surface != SF_FACE ) { - return qfalse; - } - - if ( shader->cullType == CT_TWO_SIDED ) { - return qfalse; - } - - // face culling - if ( !r_facePlaneCull->integer ) { - return qfalse; - } - - sface = ( srfSurfaceFace_t * ) surface; - d = DotProduct (tr.or.viewOrigin, sface->plane.normal); - - // don't cull exactly on the plane, because there are levels of rounding - // through the BSP, ICD, and hardware that may cause pixel gaps if an - // epsilon isn't allowed here - if ( shader->cullType == CT_FRONT_SIDED ) { - if ( d < sface->plane.dist - 8 ) { - return qtrue; - } - } else { - if ( d > sface->plane.dist + 8 ) { - return qtrue; - } - } - - return qfalse; -} - - -static int R_DlightFace( srfSurfaceFace_t *face, int dlightBits ) { - float d; - int i; - dlight_t *dl; - - for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { - if ( ! ( dlightBits & ( 1 << i ) ) ) { - continue; - } - dl = &tr.refdef.dlights[i]; - d = DotProduct( dl->origin, face->plane.normal ) - face->plane.dist; - if ( d < -dl->radius || d > dl->radius ) { - // dlight doesn't reach the plane - dlightBits &= ~( 1 << i ); - } - } - - if ( !dlightBits ) { - tr.pc.c_dlightSurfacesCulled++; - } - - face->dlightBits = dlightBits; - return dlightBits; -} - -static int R_DlightGrid( srfGridMesh_t *grid, int dlightBits ) { - int i; - dlight_t *dl; - - for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { - if ( ! ( dlightBits & ( 1 << i ) ) ) { - continue; - } - dl = &tr.refdef.dlights[i]; - if ( dl->origin[0] - dl->radius > grid->meshBounds[1][0] - || dl->origin[0] + dl->radius < grid->meshBounds[0][0] - || dl->origin[1] - dl->radius > grid->meshBounds[1][1] - || dl->origin[1] + dl->radius < grid->meshBounds[0][1] - || dl->origin[2] - dl->radius > grid->meshBounds[1][2] - || dl->origin[2] + dl->radius < grid->meshBounds[0][2] ) { - // dlight doesn't reach the bounds - dlightBits &= ~( 1 << i ); - } - } - - if ( !dlightBits ) { - tr.pc.c_dlightSurfacesCulled++; - } - - grid->dlightBits = dlightBits; - return dlightBits; -} - - -static int R_DlightTrisurf( srfTriangles_t *surf, int dlightBits ) { - // FIXME: more dlight culling to trisurfs... - surf->dlightBits = dlightBits; - return dlightBits; -#if 0 - int i; - dlight_t *dl; - - for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { - if ( ! ( dlightBits & ( 1 << i ) ) ) { - continue; - } - dl = &tr.refdef.dlights[i]; - if ( dl->origin[0] - dl->radius > grid->meshBounds[1][0] - || dl->origin[0] + dl->radius < grid->meshBounds[0][0] - || dl->origin[1] - dl->radius > grid->meshBounds[1][1] - || dl->origin[1] + dl->radius < grid->meshBounds[0][1] - || dl->origin[2] - dl->radius > grid->meshBounds[1][2] - || dl->origin[2] + dl->radius < grid->meshBounds[0][2] ) { - // dlight doesn't reach the bounds - dlightBits &= ~( 1 << i ); - } - } - - if ( !dlightBits ) { - tr.pc.c_dlightSurfacesCulled++; - } - - grid->dlightBits = dlightBits; - return dlightBits; -#endif -} - -/* -==================== -R_DlightSurface - -The given surface is going to be drawn, and it touches a leaf -that is touched by one or more dlights, so try to throw out -more dlights if possible. -==================== -*/ -static int R_DlightSurface( msurface_t *surf, int dlightBits ) { - if ( *surf->data == SF_FACE ) { - dlightBits = R_DlightFace( (srfSurfaceFace_t *)surf->data, dlightBits ); - } else if ( *surf->data == SF_GRID ) { - dlightBits = R_DlightGrid( (srfGridMesh_t *)surf->data, dlightBits ); - } else if ( *surf->data == SF_TRIANGLES ) { - dlightBits = R_DlightTrisurf( (srfTriangles_t *)surf->data, dlightBits ); - } else { - dlightBits = 0; - } - - if ( dlightBits ) { - tr.pc.c_dlightSurfaces++; - } - - return dlightBits; -} - - - -/* -====================== -R_AddWorldSurface -====================== -*/ -static void R_AddWorldSurface( msurface_t *surf, int dlightBits ) { - if ( surf->viewCount == tr.viewCount ) { - return; // already in this view - } - - surf->viewCount = tr.viewCount; - // FIXME: bmodel fog? - - // try to cull before dlighting or adding - if ( R_CullSurface( surf->data, surf->shader ) ) { - return; - } - - // check for dlighting - if ( dlightBits ) { - dlightBits = R_DlightSurface( surf, dlightBits ); - dlightBits = ( dlightBits != 0 ); - } - - R_AddDrawSurf( surf->data, surf->shader, surf->fogIndex, dlightBits ); -} - -/* -============================================================= - - BRUSH MODELS - -============================================================= -*/ - -/* -================= -R_AddBrushModelSurfaces -================= -*/ -void R_AddBrushModelSurfaces ( trRefEntity_t *ent ) { - bmodel_t *bmodel; - int clip; - model_t *pModel; - int i; - - pModel = R_GetModelByHandle( ent->e.hModel ); - - bmodel = pModel->bmodel; - - clip = R_CullLocalBox( bmodel->bounds ); - if ( clip == CULL_OUT ) { - return; - } - - R_SetupEntityLighting( &tr.refdef, ent ); - R_DlightBmodel( bmodel ); - - for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) { - R_AddWorldSurface( bmodel->firstSurface + i, tr.currentEntity->needDlights ); - } -} - - -/* -============================================================= - - WORLD MODEL - -============================================================= -*/ - - -/* -================ -R_RecursiveWorldNode -================ -*/ -static void R_RecursiveWorldNode( mnode_t *node, int planeBits, int dlightBits ) { - - do { - int newDlights[2]; - - // if the node wasn't marked as potentially visible, exit - if (node->visframe != tr.visCount) { - return; - } - - // if the bounding volume is outside the frustum, nothing - // inside can be visible OPTIMIZE: don't do this all the way to leafs? - - if ( !r_nocull->integer ) { - int r; - - if ( planeBits & 1 ) { - r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[0]); - if (r == 2) { - return; // culled - } - if ( r == 1 ) { - planeBits &= ~1; // all descendants will also be in front - } - } - - if ( planeBits & 2 ) { - r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[1]); - if (r == 2) { - return; // culled - } - if ( r == 1 ) { - planeBits &= ~2; // all descendants will also be in front - } - } - - if ( planeBits & 4 ) { - r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[2]); - if (r == 2) { - return; // culled - } - if ( r == 1 ) { - planeBits &= ~4; // all descendants will also be in front - } - } - - if ( planeBits & 8 ) { - r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[3]); - if (r == 2) { - return; // culled - } - if ( r == 1 ) { - planeBits &= ~8; // all descendants will also be in front - } - } - - } - - if ( node->contents != -1 ) { - break; - } - - // node is just a decision point, so go down both sides - // since we don't care about sort orders, just go positive to negative - - // determine which dlights are needed - newDlights[0] = 0; - newDlights[1] = 0; - if ( dlightBits ) { - int i; - - for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { - dlight_t *dl; - float dist; - - if ( dlightBits & ( 1 << i ) ) { - dl = &tr.refdef.dlights[i]; - dist = DotProduct( dl->origin, node->plane->normal ) - node->plane->dist; - - if ( dist > -dl->radius ) { - newDlights[0] |= ( 1 << i ); - } - if ( dist < dl->radius ) { - newDlights[1] |= ( 1 << i ); - } - } - } - } - - // recurse down the children, front side first - R_RecursiveWorldNode (node->children[0], planeBits, newDlights[0] ); - - // tail recurse - node = node->children[1]; - dlightBits = newDlights[1]; - } while ( 1 ); - - { - // leaf node, so add mark surfaces - int c; - msurface_t *surf, **mark; - - tr.pc.c_leafs++; - - // add to z buffer bounds - if ( node->mins[0] < tr.viewParms.visBounds[0][0] ) { - tr.viewParms.visBounds[0][0] = node->mins[0]; - } - if ( node->mins[1] < tr.viewParms.visBounds[0][1] ) { - tr.viewParms.visBounds[0][1] = node->mins[1]; - } - if ( node->mins[2] < tr.viewParms.visBounds[0][2] ) { - tr.viewParms.visBounds[0][2] = node->mins[2]; - } - - if ( node->maxs[0] > tr.viewParms.visBounds[1][0] ) { - tr.viewParms.visBounds[1][0] = node->maxs[0]; - } - if ( node->maxs[1] > tr.viewParms.visBounds[1][1] ) { - tr.viewParms.visBounds[1][1] = node->maxs[1]; - } - if ( node->maxs[2] > tr.viewParms.visBounds[1][2] ) { - tr.viewParms.visBounds[1][2] = node->maxs[2]; - } - - // add the individual surfaces - mark = node->firstmarksurface; - c = node->nummarksurfaces; - while (c--) { - // the surface may have already been added if it - // spans multiple leafs - surf = *mark; - R_AddWorldSurface( surf, dlightBits ); - mark++; - } - } - -} - - -/* -=============== -R_PointInLeaf -=============== -*/ -static mnode_t *R_PointInLeaf( const vec3_t p ) { - mnode_t *node; - float d; - cplane_t *plane; - - if ( !tr.world ) { - ri.Error (ERR_DROP, "R_PointInLeaf: bad model"); - } - - node = tr.world->nodes; - while( 1 ) { - if (node->contents != -1) { - break; - } - plane = node->plane; - d = DotProduct (p,plane->normal) - plane->dist; - if (d > 0) { - node = node->children[0]; - } else { - node = node->children[1]; - } - } - - return node; -} - -/* -============== -R_ClusterPVS -============== -*/ -static const byte *R_ClusterPVS (int cluster) { - if (!tr.world || !tr.world->vis || cluster < 0 || cluster >= tr.world->numClusters ) { - return tr.world->novis; - } - - return tr.world->vis + cluster * tr.world->clusterBytes; -} - -/* -================= -R_inPVS -================= -*/ -qboolean R_inPVS( const vec3_t p1, const vec3_t p2 ) { - mnode_t *leaf; - byte *vis; - - leaf = R_PointInLeaf( p1 ); - vis = ri.CM_ClusterPVS( leaf->cluster ); // why not R_ClusterPVS ?? - leaf = R_PointInLeaf( p2 ); - - if ( !(vis[leaf->cluster>>3] & (1<<(leaf->cluster&7))) ) { - return qfalse; - } - return qtrue; -} - -/* -=============== -R_MarkLeaves - -Mark the leaves and nodes that are in the PVS for the current -cluster -=============== -*/ -static void R_MarkLeaves (void) { - const byte *vis; - mnode_t *leaf, *parent; - int i; - int cluster; - - // lockpvs lets designers walk around to determine the - // extent of the current pvs - if ( r_lockpvs->integer ) { - return; - } - - // current viewcluster - leaf = R_PointInLeaf( tr.viewParms.pvsOrigin ); - cluster = leaf->cluster; - - // if the cluster is the same and the area visibility matrix - // hasn't changed, we don't need to mark everything again - - // if r_showcluster was just turned on, remark everything - if ( tr.viewCluster == cluster && !tr.refdef.areamaskModified - && !r_showcluster->modified ) { - return; - } - - if ( r_showcluster->modified || r_showcluster->integer ) { - r_showcluster->modified = qfalse; - if ( r_showcluster->integer ) { - ri.Printf( PRINT_ALL, "cluster:%i area:%i\n", cluster, leaf->area ); - } - } - - tr.visCount++; - tr.viewCluster = cluster; - - if ( r_novis->integer || tr.viewCluster == -1 ) { - for (i=0 ; inumnodes ; i++) { - if (tr.world->nodes[i].contents != CONTENTS_SOLID) { - tr.world->nodes[i].visframe = tr.visCount; - } - } - return; - } - - vis = R_ClusterPVS (tr.viewCluster); - - for (i=0,leaf=tr.world->nodes ; inumnodes ; i++, leaf++) { - cluster = leaf->cluster; - if ( cluster < 0 || cluster >= tr.world->numClusters ) { - continue; - } - - // check general pvs - if ( !(vis[cluster>>3] & (1<<(cluster&7))) ) { - continue; - } - - // check for door connection - if ( (tr.refdef.areamask[leaf->area>>3] & (1<<(leaf->area&7)) ) ) { - continue; // not visible - } - - parent = leaf; - do { - if (parent->visframe == tr.visCount) - break; - parent->visframe = tr.visCount; - parent = parent->parent; - } while (parent); - } -} - - -/* -============= -R_AddWorldSurfaces -============= -*/ -void R_AddWorldSurfaces (void) { - if ( !r_drawworld->integer ) { - return; - } - - if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { - return; - } - - tr.currentEntityNum = REFENTITYNUM_WORLD; - tr.shiftedEntityNum = tr.currentEntityNum << QSORT_REFENTITYNUM_SHIFT; - - // determine which leaves are in the PVS / areamask - R_MarkLeaves (); - - // clear out the visible min/max - ClearBounds( tr.viewParms.visBounds[0], tr.viewParms.visBounds[1] ); - - // perform frustum culling and add all the potentially visible surfaces - if ( tr.refdef.num_dlights > 32 ) { - tr.refdef.num_dlights = 32 ; - } - R_RecursiveWorldNode( tr.world->nodes, 15, ( 1 << tr.refdef.num_dlights ) - 1 ); -} diff --git a/src/renderercommon/iqm.h b/src/renderercommon/iqm.h new file mode 100644 index 00000000..ab2247ac --- /dev/null +++ b/src/renderercommon/iqm.h @@ -0,0 +1,129 @@ +/* +=========================================================================== +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#ifndef __IQM_H__ +#define __IQM_H__ + +#define IQM_MAGIC "INTERQUAKEMODEL" +#define IQM_VERSION 2 + +#define IQM_MAX_JOINTS 128 + +typedef struct iqmheader +{ + char magic[16]; + unsigned int version; + unsigned int filesize; + unsigned int flags; + unsigned int num_text, ofs_text; + unsigned int num_meshes, ofs_meshes; + unsigned int num_vertexarrays, num_vertexes, ofs_vertexarrays; + unsigned int num_triangles, ofs_triangles, ofs_adjacency; + unsigned int num_joints, ofs_joints; + unsigned int num_poses, ofs_poses; + unsigned int num_anims, ofs_anims; + unsigned int num_frames, num_framechannels, ofs_frames, ofs_bounds; + unsigned int num_comment, ofs_comment; + unsigned int num_extensions, ofs_extensions; +} iqmHeader_t; + +typedef struct iqmmesh +{ + unsigned int name; + unsigned int material; + unsigned int first_vertex, num_vertexes; + unsigned int first_triangle, num_triangles; +} iqmMesh_t; + +enum +{ + IQM_POSITION = 0, + IQM_TEXCOORD = 1, + IQM_NORMAL = 2, + IQM_TANGENT = 3, + IQM_BLENDINDEXES = 4, + IQM_BLENDWEIGHTS = 5, + IQM_COLOR = 6, + IQM_CUSTOM = 0x10 +}; + +enum +{ + IQM_BYTE = 0, + IQM_UBYTE = 1, + IQM_SHORT = 2, + IQM_USHORT = 3, + IQM_INT = 4, + IQM_UINT = 5, + IQM_HALF = 6, + IQM_FLOAT = 7, + IQM_DOUBLE = 8, +}; + +typedef struct iqmtriangle +{ + unsigned int vertex[3]; +} iqmTriangle_t; + +typedef struct iqmjoint +{ + unsigned int name; + int parent; + float translate[3], rotate[4], scale[3]; +} iqmJoint_t; + +typedef struct iqmpose +{ + int parent; + unsigned int mask; + float channeloffset[10]; + float channelscale[10]; +} iqmPose_t; + +typedef struct iqmanim +{ + unsigned int name; + unsigned int first_frame, num_frames; + float framerate; + unsigned int flags; +} iqmAnim_t; + +enum +{ + IQM_LOOP = 1<<0 +}; + +typedef struct iqmvertexarray +{ + unsigned int type; + unsigned int flags; + unsigned int format; + unsigned int size; + unsigned int offset; +} iqmVertexArray_t; + +typedef struct iqmbounds +{ + float bbmin[3], bbmax[3]; + float xyradius, radius; +} iqmBounds_t; + +#endif + diff --git a/src/renderercommon/tr_public.h b/src/renderercommon/tr_public.h new file mode 100644 index 00000000..f9ededdf --- /dev/null +++ b/src/renderercommon/tr_public.h @@ -0,0 +1,196 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +#ifndef __TR_PUBLIC_H +#define __TR_PUBLIC_H + +#include "tr_types.h" + +#define REF_API_VERSION 8 + +// +// these are the functions exported by the refresh module +// +typedef struct { + // called before the library is unloaded + // if the system is just reconfiguring, pass destroyWindow = qfalse, + // which will keep the screen from flashing to the desktop. + void (*Shutdown)( qboolean destroyWindow ); + + // All data that will be used in a level should be + // registered before rendering any frames to prevent disk hits, + // but they can still be registered at a later time + // if necessary. + // + // BeginRegistration makes any existing media pointers invalid + // and returns the current gl configuration, including screen width + // and height, which can be used by the client to intelligently + // size display elements + void (*BeginRegistration)( glconfig_t *config ); + qhandle_t (*RegisterModel)( const char *name ); + qhandle_t (*RegisterSkin)( const char *name ); + qhandle_t (*RegisterShader)( const char *name ); + qhandle_t (*RegisterShaderNoMip)( const char *name ); + void (*LoadWorld)( const char *name ); + + // the vis data is a large enough block of data that we go to the trouble + // of sharing it with the clipmodel subsystem + void (*SetWorldVisData)( const byte *vis ); + + // EndRegistration will draw a tiny polygon with each texture, forcing + // them to be loaded into card memory + void (*EndRegistration)( void ); + + // a scene is built up by calls to R_ClearScene and the various R_Add functions. + // Nothing is drawn until R_RenderScene is called. + void (*ClearScene)( void ); + void (*AddRefEntityToScene)( const refEntity_t *re ); + void (*AddPolyToScene)( qhandle_t hShader , int numVerts, const polyVert_t *verts, int num ); + int (*LightForPoint)( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ); + void (*AddLightToScene)( const vec3_t org, float intensity, float r, float g, float b ); + void (*AddAdditiveLightToScene)( const vec3_t org, float intensity, float r, float g, float b ); + void (*RenderScene)( const refdef_t *fd ); + + void (*SetColor)( const float *rgba ); // NULL = 1,1,1,1 + void (*SetClipRegion)( const float *region ); + void (*DrawStretchPic) ( float x, float y, float w, float h, + float s1, float t1, float s2, float t2, qhandle_t hShader ); // 0 = white + + // Draw images for cinematic rendering, pass as 32 bit rgba + void (*DrawStretchRaw) (int x, int y, int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty); + void (*UploadCinematic) (int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty); + + void (*BeginFrame)( stereoFrame_t stereoFrame ); + + // if the pointers are not NULL, timing info will be returned + void (*EndFrame)( int *frontEndMsec, int *backEndMsec ); + + + int (*MarkFragments)( int numPoints, const vec3_t *points, const vec3_t projection, + int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ); + + int (*LerpTag)( orientation_t *tag, qhandle_t model, int startFrame, int endFrame, + float frac, const char *tagName ); + void (*ModelBounds)( qhandle_t model, vec3_t mins, vec3_t maxs ); + +#ifdef __USEA3D + void (*A3D_RenderGeometry) (void *pVoidA3D, void *pVoidGeom, void *pVoidMat, void *pVoidGeomStatus); +#endif + void (*RegisterFont)(const char *fontName, int pointSize, fontInfo_t *font); + void (*RemapShader)(const char *oldShader, const char *newShader, const char *offsetTime); + qboolean (*GetEntityToken)( char *buffer, int size ); + qboolean (*inPVS)( const vec3_t p1, const vec3_t p2 ); + + void (*TakeVideoFrame)( int h, int w, byte* captureBuffer, byte *encodeBuffer, qboolean motionJpeg ); +} refexport_t; + +// +// these are the functions imported by the refresh module +// +typedef struct { + // print message on the local console + void (QDECL *Printf)( int printLevel, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + + // abort the game + void (QDECL *Error)( int errorLevel, const char *fmt, ...) __attribute__ ((format (printf, 2, 3))); + + // milliseconds should only be used for profiling, never + // for anything game related. Get time from the refdef + int (*Milliseconds)( void ); + + // stack based memory allocation for per-level things that + // won't be freed +#ifdef HUNK_DEBUG + void *(*Hunk_AllocDebug)( int size, ha_pref pref, char *label, char *file, int line ); +#else + void *(*Hunk_Alloc)( int size, ha_pref pref ); +#endif + void *(*Hunk_AllocateTempMemory)( int size ); + void (*Hunk_FreeTempMemory)( void *block ); + + // dynamic memory allocator for things that need to be freed + void *(*Malloc)( int bytes ); + void (*Free)( void *buf ); + + cvar_t *(*Cvar_Get)( const char *name, const char *value, int flags ); + void (*Cvar_Set)( const char *name, const char *value ); + void (*Cvar_SetValue) (const char *name, float value); + void (*Cvar_CheckRange)( cvar_t *cv, float minVal, float maxVal, qboolean shouldBeIntegral ); + + int (*Cvar_VariableIntegerValue) (const char *var_name); + + void (*Cmd_AddCommand)( const char *name, void(*cmd)(void) ); + void (*Cmd_RemoveCommand)( const char *name ); + + int (*Cmd_Argc) (void); + char *(*Cmd_Argv) (int i); + + void (*Cmd_ExecuteText) (int exec_when, const char *text); + + byte *(*CM_ClusterPVS)(int cluster); + + // visualization for debugging collision detection + void (*CM_DrawDebugSurface)( void (*drawPoly)(int color, int numPoints, float *points) ); + + // a -1 return means the file does not exist + // NULL can be passed for buf to just determine existance + int (*FS_FileIsInPAK)( const char *name, int *pCheckSum ); + long (*FS_ReadFile)( const char *name, void **buf ); + void (*FS_FreeFile)( void *buf ); + char ** (*FS_ListFiles)( const char *name, const char *extension, int *numfilesfound ); + void (*FS_FreeFileList)( char **filelist ); + void (*FS_WriteFile)( const char *qpath, const void *buffer, int size ); + qboolean (*FS_FileExists)( const char *file ); + + // cinematic stuff + void (*CIN_UploadCinematic)(int handle); + int (*CIN_PlayCinematic)( const char *arg0, int xpos, int ypos, int width, int height, int bits); + e_status (*CIN_RunCinematic) (int handle); + + void (*CL_WriteAVIVideoFrame)( const byte *buffer, int size ); + + // input event handling + void (*IN_Init)( void ); + void (*IN_Shutdown)( void ); + void (*IN_Restart)( void ); + + // math + long (*ftol)(float f); + + // system stuff + void (*Sys_SetEnv)( const char *name, const char *value ); + void (*Sys_GLimpSafeInit)( void ); + void (*Sys_GLimpInit)( void ); + qboolean (*Sys_LowPhysicalMemory)( void ); +} refimport_t; + + +// this is the only function actually exported at the linker level +// If the module can't init to a valid rendering state, NULL will be +// returned. +#ifdef USE_RENDERER_DLOPEN +typedef refexport_t* (QDECL *GetRefAPI_t) (int apiVersion, refimport_t * rimp); +#else +refexport_t*GetRefAPI( int apiVersion, refimport_t *rimp ); +#endif + +#endif // __TR_PUBLIC_H diff --git a/src/renderercommon/tr_types.h b/src/renderercommon/tr_types.h new file mode 100644 index 00000000..0e15c9e8 --- /dev/null +++ b/src/renderercommon/tr_types.h @@ -0,0 +1,221 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// +#ifndef __TR_TYPES_H +#define __TR_TYPES_H + + +#define MAX_DLIGHTS 32 // can't be increased, because bit flags are used on surfaces + +#define REFENTITYNUM_BITS 10 // can't be increased without changing drawsurf bit packing +#define REFENTITYNUM_MASK ((1<lightingOrigin instead of refEntity->origin + // for lighting. This allows entities to sink into the floor + // with their origin going solid, and allows all parts of a + // player to get the same lighting + +#define RF_SHADOW_PLANE 0x0100 // use refEntity->shadowPlane +#define RF_WRAP_FRAMES 0x0200 // mod the model frames by the maxframes to allow continuous + +// refdef flags +#define RDF_NOWORLDMODEL 0x0001 // used for player configuration screen +#define RDF_HYPERSPACE 0x0004 // teleportation effect + +typedef struct { + vec3_t xyz; + float st[2]; + byte modulate[4]; +} polyVert_t; + +typedef struct poly_s { + qhandle_t hShader; + int numVerts; + polyVert_t *verts; +} poly_t; + +typedef enum { + RT_MODEL, + RT_POLY, + RT_SPRITE, + RT_BEAM, + RT_RAIL_CORE, + RT_RAIL_RINGS, + RT_LIGHTNING, + RT_PORTALSURFACE, // doesn't draw anything, just info for portals + + RT_MAX_REF_ENTITY_TYPE +} refEntityType_t; + +typedef struct { + refEntityType_t reType; + int renderfx; + + qhandle_t hModel; // opaque type outside refresh + + // most recent data + vec3_t lightingOrigin; // so multi-part models can be lit identically (RF_LIGHTING_ORIGIN) + float shadowPlane; // projection shadows go here, stencils go slightly lower + + vec3_t axis[3]; // rotation vectors + qboolean nonNormalizedAxes; // axis are not normalized, i.e. they have scale + float origin[3]; // also used as MODEL_BEAM's "from" + int frame; // also used as MODEL_BEAM's diameter + + // previous data for frame interpolation + float oldorigin[3]; // also used as MODEL_BEAM's "to" + int oldframe; + float backlerp; // 0.0 = current, 1.0 = old + + // texturing + int skinNum; // inline skin index + qhandle_t customSkin; // NULL for default skin + qhandle_t customShader; // use one image for the entire thing + + // misc + byte shaderRGBA[4]; // colors used by rgbgen entity shaders + float shaderTexCoord[2]; // texture coordinates used by tcMod entity modifiers + float shaderTime; // subtracted from refdef time to control effect start times + + // extra sprite information + float radius; + float rotation; +} refEntity_t; + + +#define MAX_RENDER_STRINGS 8 +#define MAX_RENDER_STRING_LENGTH 32 + +typedef struct { + int x, y, width, height; + float fov_x, fov_y; + vec3_t vieworg; + vec3_t viewaxis[3]; // transformation matrix + + // time in milliseconds for shader effects and other time dependent rendering issues + int time; + + int rdflags; // RDF_NOWORLDMODEL, etc + + // 1 bits will prevent the associated area from rendering at all + byte areamask[MAX_MAP_AREA_BYTES]; + + // text messages for deform text shaders + char text[MAX_RENDER_STRINGS][MAX_RENDER_STRING_LENGTH]; +} refdef_t; + + +typedef enum { + STEREO_CENTER, + STEREO_LEFT, + STEREO_RIGHT +} stereoFrame_t; + + +/* +** glconfig_t +** +** Contains variables specific to the OpenGL configuration +** being run right now. These are constant once the OpenGL +** subsystem is initialized. +*/ +typedef enum { + TC_NONE, + TC_S3TC, // this is for the GL_S3_s3tc extension. + TC_S3TC_ARB // this is for the GL_EXT_texture_compression_s3tc extension. +} textureCompression_t; + +typedef enum { + GLDRV_ICD, // driver is integrated with window system + // WARNING: there are tests that check for + // > GLDRV_ICD for minidriverness, so this + // should always be the lowest value in this + // enum set + GLDRV_STANDALONE, // driver is a non-3Dfx standalone driver + GLDRV_VOODOO // driver is a 3Dfx standalone driver +} glDriverType_t; + +typedef enum { + GLHW_GENERIC, // where everthing works the way it should + GLHW_3DFX_2D3D, // Voodoo Banshee or Voodoo3, relevant since if this is + // the hardware type then there can NOT exist a secondary + // display adapter + GLHW_RIVA128, // where you can't interpolate alpha + GLHW_RAGEPRO, // where you can't modulate alpha on alpha textures + GLHW_PERMEDIA2 // where you don't have src*dst +} glHardwareType_t; + +typedef struct { + char renderer_string[MAX_STRING_CHARS]; + char vendor_string[MAX_STRING_CHARS]; + char version_string[MAX_STRING_CHARS]; + char extensions_string[BIG_INFO_STRING]; + + int maxTextureSize; // queried from GL + int numTextureUnits; // multitexture ability + + int colorBits, depthBits, stencilBits; + + glDriverType_t driverType; + glHardwareType_t hardwareType; + + qboolean deviceSupportsGamma; + textureCompression_t textureCompression; + qboolean textureEnvAddAvailable; + + int vidWidth, vidHeight; + // aspect is the screen's physical width / height, which may be different + // than scrWidth / scrHeight if the pixels are non-square + // normal screens should be 4/3, but wide aspect monitors may be 16/9 + float windowAspect; + float displayAspect; + + int displayFrequency; + + // synonymous with "does rendering consume the entire screen?", therefore + // a Voodoo or Voodoo2 will have this set to TRUE, as will a Win32 ICD that + // used CDS. + qboolean isFullscreen; + qboolean stereoEnabled; + qboolean textureFilterAnisotropic; + int maxAnisotropy; +} glconfig_t; + +#endif // __TR_TYPES_H diff --git a/src/renderergl1/qgl.h b/src/renderergl1/qgl.h new file mode 100644 index 00000000..2dbe27db --- /dev/null +++ b/src/renderergl1/qgl.h @@ -0,0 +1,381 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +/* +** QGL.H +*/ + +#ifndef __QGL_H__ +#define __QGL_H__ + +#ifdef USE_LOCAL_HEADERS +# include "SDL_opengl.h" +#else +# include +#endif + +extern void (APIENTRYP qglActiveTextureARB) (GLenum texture); +extern void (APIENTRYP qglClientActiveTextureARB) (GLenum texture); +extern void (APIENTRYP qglMultiTexCoord2fARB) (GLenum target, GLfloat s, GLfloat t); + +extern void (APIENTRYP qglLockArraysEXT) (GLint first, GLsizei count); +extern void (APIENTRYP qglUnlockArraysEXT) (void); + + +//=========================================================================== + +#define qglAccum glAccum +#define qglAlphaFunc glAlphaFunc +#define qglAreTexturesResident glAreTexturesResident +#define qglArrayElement glArrayElement +#define qglBegin glBegin +#define qglBindTexture glBindTexture +#define qglBitmap glBitmap +#define qglBlendFunc glBlendFunc +#define qglCallList glCallList +#define qglCallLists glCallLists +#define qglClear glClear +#define qglClearAccum glClearAccum +#define qglClearColor glClearColor +#define qglClearDepth glClearDepth +#define qglClearIndex glClearIndex +#define qglClearStencil glClearStencil +#define qglClipPlane glClipPlane +#define qglColor3b glColor3b +#define qglColor3bv glColor3bv +#define qglColor3d glColor3d +#define qglColor3dv glColor3dv +#define qglColor3f glColor3f +#define qglColor3fv glColor3fv +#define qglColor3i glColor3i +#define qglColor3iv glColor3iv +#define qglColor3s glColor3s +#define qglColor3sv glColor3sv +#define qglColor3ub glColor3ub +#define qglColor3ubv glColor3ubv +#define qglColor3ui glColor3ui +#define qglColor3uiv glColor3uiv +#define qglColor3us glColor3us +#define qglColor3usv glColor3usv +#define qglColor4b glColor4b +#define qglColor4bv glColor4bv +#define qglColor4d glColor4d +#define qglColor4dv glColor4dv +#define qglColor4f glColor4f +#define qglColor4fv glColor4fv +#define qglColor4i glColor4i +#define qglColor4iv glColor4iv +#define qglColor4s glColor4s +#define qglColor4sv glColor4sv +#define qglColor4ub glColor4ub +#define qglColor4ubv glColor4ubv +#define qglColor4ui glColor4ui +#define qglColor4uiv glColor4uiv +#define qglColor4us glColor4us +#define qglColor4usv glColor4usv +#define qglColorMask glColorMask +#define qglColorMaterial glColorMaterial +#define qglColorPointer glColorPointer +#define qglCopyPixels glCopyPixels +#define qglCopyTexImage1D glCopyTexImage1D +#define qglCopyTexImage2D glCopyTexImage2D +#define qglCopyTexSubImage1D glCopyTexSubImage1D +#define qglCopyTexSubImage2D glCopyTexSubImage2D +#define qglCullFace glCullFace +#define qglDeleteLists glDeleteLists +#define qglDeleteTextures glDeleteTextures +#define qglDepthFunc glDepthFunc +#define qglDepthMask glDepthMask +#define qglDepthRange glDepthRange +#define qglDisable glDisable +#define qglDisableClientState glDisableClientState +#define qglDrawArrays glDrawArrays +#define qglDrawBuffer glDrawBuffer +#define qglDrawElements glDrawElements +#define qglDrawPixels glDrawPixels +#define qglEdgeFlag glEdgeFlag +#define qglEdgeFlagPointer glEdgeFlagPointer +#define qglEdgeFlagv glEdgeFlagv +#define qglEnable glEnable +#define qglEnableClientState glEnableClientState +#define qglEnd glEnd +#define qglEndList glEndList +#define qglEvalCoord1d glEvalCoord1d +#define qglEvalCoord1dv glEvalCoord1dv +#define qglEvalCoord1f glEvalCoord1f +#define qglEvalCoord1fv glEvalCoord1fv +#define qglEvalCoord2d glEvalCoord2d +#define qglEvalCoord2dv glEvalCoord2dv +#define qglEvalCoord2f glEvalCoord2f +#define qglEvalCoord2fv glEvalCoord2fv +#define qglEvalMesh1 glEvalMesh1 +#define qglEvalMesh2 glEvalMesh2 +#define qglEvalPoint1 glEvalPoint1 +#define qglEvalPoint2 glEvalPoint2 +#define qglFeedbackBuffer glFeedbackBuffer +#define qglFinish glFinish +#define qglFlush glFlush +#define qglFogf glFogf +#define qglFogfv glFogfv +#define qglFogi glFogi +#define qglFogiv glFogiv +#define qglFrontFace glFrontFace +#define qglFrustum glFrustum +#define qglGenLists glGenLists +#define qglGenTextures glGenTextures +#define qglGetBooleanv glGetBooleanv +#define qglGetClipPlane glGetClipPlane +#define qglGetDoublev glGetDoublev +#define qglGetError glGetError +#define qglGetFloatv glGetFloatv +#define qglGetIntegerv glGetIntegerv +#define qglGetLightfv glGetLightfv +#define qglGetLightiv glGetLightiv +#define qglGetMapdv glGetMapdv +#define qglGetMapfv glGetMapfv +#define qglGetMapiv glGetMapiv +#define qglGetMaterialfv glGetMaterialfv +#define qglGetMaterialiv glGetMaterialiv +#define qglGetPixelMapfv glGetPixelMapfv +#define qglGetPixelMapuiv glGetPixelMapuiv +#define qglGetPixelMapusv glGetPixelMapusv +#define qglGetPointerv glGetPointerv +#define qglGetPolygonStipple glGetPolygonStipple +#define qglGetString glGetString +#define qglGetTexGendv glGetTexGendv +#define qglGetTexGenfv glGetTexGenfv +#define qglGetTexGeniv glGetTexGeniv +#define qglGetTexImage glGetTexImage +#define qglGetTexLevelParameterfv glGetTexLevelParameterfv +#define qglGetTexLevelParameteriv glGetTexLevelParameteriv +#define qglGetTexParameterfv glGetTexParameterfv +#define qglGetTexParameteriv glGetTexParameteriv +#define qglHint glHint +#define qglIndexMask glIndexMask +#define qglIndexPointer glIndexPointer +#define qglIndexd glIndexd +#define qglIndexdv glIndexdv +#define qglIndexf glIndexf +#define qglIndexfv glIndexfv +#define qglIndexi glIndexi +#define qglIndexiv glIndexiv +#define qglIndexs glIndexs +#define qglIndexsv glIndexsv +#define qglIndexub glIndexub +#define qglIndexubv glIndexubv +#define qglInitNames glInitNames +#define qglInterleavedArrays glInterleavedArrays +#define qglIsEnabled glIsEnabled +#define qglIsList glIsList +#define qglIsTexture glIsTexture +#define qglLightModelf glLightModelf +#define qglLightModelfv glLightModelfv +#define qglLightModeli glLightModeli +#define qglLightModeliv glLightModeliv +#define qglLightf glLightf +#define qglLightfv glLightfv +#define qglLighti glLighti +#define qglLightiv glLightiv +#define qglLineStipple glLineStipple +#define qglLineWidth glLineWidth +#define qglListBase glListBase +#define qglLoadIdentity glLoadIdentity +#define qglLoadMatrixd glLoadMatrixd +#define qglLoadMatrixf glLoadMatrixf +#define qglLoadName glLoadName +#define qglLogicOp glLogicOp +#define qglMap1d glMap1d +#define qglMap1f glMap1f +#define qglMap2d glMap2d +#define qglMap2f glMap2f +#define qglMapGrid1d glMapGrid1d +#define qglMapGrid1f glMapGrid1f +#define qglMapGrid2d glMapGrid2d +#define qglMapGrid2f glMapGrid2f +#define qglMaterialf glMaterialf +#define qglMaterialfv glMaterialfv +#define qglMateriali glMateriali +#define qglMaterialiv glMaterialiv +#define qglMatrixMode glMatrixMode +#define qglMultMatrixd glMultMatrixd +#define qglMultMatrixf glMultMatrixf +#define qglNewList glNewList +#define qglNormal3b glNormal3b +#define qglNormal3bv glNormal3bv +#define qglNormal3d glNormal3d +#define qglNormal3dv glNormal3dv +#define qglNormal3f glNormal3f +#define qglNormal3fv glNormal3fv +#define qglNormal3i glNormal3i +#define qglNormal3iv glNormal3iv +#define qglNormal3s glNormal3s +#define qglNormal3sv glNormal3sv +#define qglNormalPointer glNormalPointer +#define qglOrtho glOrtho +#define qglPassThrough glPassThrough +#define qglPixelMapfv glPixelMapfv +#define qglPixelMapuiv glPixelMapuiv +#define qglPixelMapusv glPixelMapusv +#define qglPixelStoref glPixelStoref +#define qglPixelStorei glPixelStorei +#define qglPixelTransferf glPixelTransferf +#define qglPixelTransferi glPixelTransferi +#define qglPixelZoom glPixelZoom +#define qglPointSize glPointSize +#define qglPolygonMode glPolygonMode +#define qglPolygonOffset glPolygonOffset +#define qglPolygonStipple glPolygonStipple +#define qglPopAttrib glPopAttrib +#define qglPopClientAttrib glPopClientAttrib +#define qglPopMatrix glPopMatrix +#define qglPopName glPopName +#define qglPrioritizeTextures glPrioritizeTextures +#define qglPushAttrib glPushAttrib +#define qglPushClientAttrib glPushClientAttrib +#define qglPushMatrix glPushMatrix +#define qglPushName glPushName +#define qglRasterPos2d glRasterPos2d +#define qglRasterPos2dv glRasterPos2dv +#define qglRasterPos2f glRasterPos2f +#define qglRasterPos2fv glRasterPos2fv +#define qglRasterPos2i glRasterPos2i +#define qglRasterPos2iv glRasterPos2iv +#define qglRasterPos2s glRasterPos2s +#define qglRasterPos2sv glRasterPos2sv +#define qglRasterPos3d glRasterPos3d +#define qglRasterPos3dv glRasterPos3dv +#define qglRasterPos3f glRasterPos3f +#define qglRasterPos3fv glRasterPos3fv +#define qglRasterPos3i glRasterPos3i +#define qglRasterPos3iv glRasterPos3iv +#define qglRasterPos3s glRasterPos3s +#define qglRasterPos3sv glRasterPos3sv +#define qglRasterPos4d glRasterPos4d +#define qglRasterPos4dv glRasterPos4dv +#define qglRasterPos4f glRasterPos4f +#define qglRasterPos4fv glRasterPos4fv +#define qglRasterPos4i glRasterPos4i +#define qglRasterPos4iv glRasterPos4iv +#define qglRasterPos4s glRasterPos4s +#define qglRasterPos4sv glRasterPos4sv +#define qglReadBuffer glReadBuffer +#define qglReadPixels glReadPixels +#define qglRectd glRectd +#define qglRectdv glRectdv +#define qglRectf glRectf +#define qglRectfv glRectfv +#define qglRecti glRecti +#define qglRectiv glRectiv +#define qglRects glRects +#define qglRectsv glRectsv +#define qglRenderMode glRenderMode +#define qglRotated glRotated +#define qglRotatef glRotatef +#define qglScaled glScaled +#define qglScalef glScalef +#define qglScissor glScissor +#define qglSelectBuffer glSelectBuffer +#define qglShadeModel glShadeModel +#define qglStencilFunc glStencilFunc +#define qglStencilMask glStencilMask +#define qglStencilOp glStencilOp +#define qglTexCoord1d glTexCoord1d +#define qglTexCoord1dv glTexCoord1dv +#define qglTexCoord1f glTexCoord1f +#define qglTexCoord1fv glTexCoord1fv +#define qglTexCoord1i glTexCoord1i +#define qglTexCoord1iv glTexCoord1iv +#define qglTexCoord1s glTexCoord1s +#define qglTexCoord1sv glTexCoord1sv +#define qglTexCoord2d glTexCoord2d +#define qglTexCoord2dv glTexCoord2dv +#define qglTexCoord2f glTexCoord2f +#define qglTexCoord2fv glTexCoord2fv +#define qglTexCoord2i glTexCoord2i +#define qglTexCoord2iv glTexCoord2iv +#define qglTexCoord2s glTexCoord2s +#define qglTexCoord2sv glTexCoord2sv +#define qglTexCoord3d glTexCoord3d +#define qglTexCoord3dv glTexCoord3dv +#define qglTexCoord3f glTexCoord3f +#define qglTexCoord3fv glTexCoord3fv +#define qglTexCoord3i glTexCoord3i +#define qglTexCoord3iv glTexCoord3iv +#define qglTexCoord3s glTexCoord3s +#define qglTexCoord3sv glTexCoord3sv +#define qglTexCoord4d glTexCoord4d +#define qglTexCoord4dv glTexCoord4dv +#define qglTexCoord4f glTexCoord4f +#define qglTexCoord4fv glTexCoord4fv +#define qglTexCoord4i glTexCoord4i +#define qglTexCoord4iv glTexCoord4iv +#define qglTexCoord4s glTexCoord4s +#define qglTexCoord4sv glTexCoord4sv +#define qglTexCoordPointer glTexCoordPointer +#define qglTexEnvf glTexEnvf +#define qglTexEnvfv glTexEnvfv +#define qglTexEnvi glTexEnvi +#define qglTexEnviv glTexEnviv +#define qglTexGend glTexGend +#define qglTexGendv glTexGendv +#define qglTexGenf glTexGenf +#define qglTexGenfv glTexGenfv +#define qglTexGeni glTexGeni +#define qglTexGeniv glTexGeniv +#define qglTexImage1D glTexImage1D +#define qglTexImage2D glTexImage2D +#define qglTexParameterf glTexParameterf +#define qglTexParameterfv glTexParameterfv +#define qglTexParameteri glTexParameteri +#define qglTexParameteriv glTexParameteriv +#define qglTexSubImage1D glTexSubImage1D +#define qglTexSubImage2D glTexSubImage2D +#define qglTranslated glTranslated +#define qglTranslatef glTranslatef +#define qglVertex2d glVertex2d +#define qglVertex2dv glVertex2dv +#define qglVertex2f glVertex2f +#define qglVertex2fv glVertex2fv +#define qglVertex2i glVertex2i +#define qglVertex2iv glVertex2iv +#define qglVertex2s glVertex2s +#define qglVertex2sv glVertex2sv +#define qglVertex3d glVertex3d +#define qglVertex3dv glVertex3dv +#define qglVertex3f glVertex3f +#define qglVertex3fv glVertex3fv +#define qglVertex3i glVertex3i +#define qglVertex3iv glVertex3iv +#define qglVertex3s glVertex3s +#define qglVertex3sv glVertex3sv +#define qglVertex4d glVertex4d +#define qglVertex4dv glVertex4dv +#define qglVertex4f glVertex4f +#define qglVertex4fv glVertex4fv +#define qglVertex4i glVertex4i +#define qglVertex4iv glVertex4iv +#define qglVertex4s glVertex4s +#define qglVertex4sv glVertex4sv +#define qglVertexPointer glVertexPointer +#define qglViewport glViewport + +#endif diff --git a/src/renderergl1/tr_animation.c b/src/renderergl1/tr_animation.c new file mode 100644 index 00000000..a9bcd507 --- /dev/null +++ b/src/renderergl1/tr_animation.c @@ -0,0 +1,659 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "tr_local.h" + +/* + +All bones should be an identity orientation to display the mesh exactly +as it is specified. + +For all other frames, the bones represent the transformation from the +orientation of the bone in the base frame to the orientation in this +frame. + +*/ + +/* +============== +R_AddAnimSurfaces +============== +*/ +void R_AddAnimSurfaces( trRefEntity_t *ent ) { + md4Header_t *header; + md4Surface_t *surface; + md4LOD_t *lod; + shader_t *shader; + int i; + + header = (md4Header_t *) tr.currentModel->modelData; + lod = (md4LOD_t *)( (byte *)header + header->ofsLODs ); + + surface = (md4Surface_t *)( (byte *)lod + lod->ofsSurfaces ); + for ( i = 0 ; i < lod->numSurfaces ; i++ ) { + shader = R_GetShaderByHandle( surface->shaderIndex ); + R_AddDrawSurf( (void *)surface, shader, 0 /*fogNum*/, qfalse ); + surface = (md4Surface_t *)( (byte *)surface + surface->ofsEnd ); + } +} + +/* +============== +RB_SurfaceAnim +============== +*/ +void RB_SurfaceAnim( md4Surface_t *surface ) { + int i, j, k; + float frontlerp, backlerp; + int *triangles; + int indexes; + int baseIndex, baseVertex; + int numVerts; + md4Vertex_t *v; + md4Bone_t bones[MD4_MAX_BONES]; + md4Bone_t *bonePtr, *bone; + md4Header_t *header; + md4Frame_t *frame; + md4Frame_t *oldFrame; + int frameSize; + + + if ( backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame ) { + backlerp = 0; + frontlerp = 1; + } else { + backlerp = backEnd.currentEntity->e.backlerp; + frontlerp = 1.0f - backlerp; + } + header = (md4Header_t *)((byte *)surface + surface->ofsHeader); + + frameSize = (size_t)( &((md4Frame_t *)0)->bones[ header->numBones ] ); + + frame = (md4Frame_t *)((byte *)header + header->ofsFrames + + backEnd.currentEntity->e.frame * frameSize ); + oldFrame = (md4Frame_t *)((byte *)header + header->ofsFrames + + backEnd.currentEntity->e.oldframe * frameSize ); + + RB_CheckOverflow( surface->numVerts, surface->numTriangles * 3 ); + + triangles = (int *) ((byte *)surface + surface->ofsTriangles); + indexes = surface->numTriangles * 3; + baseIndex = tess.numIndexes; + baseVertex = tess.numVertexes; + for (j = 0 ; j < indexes ; j++) { + tess.indexes[baseIndex + j] = baseIndex + triangles[j]; + } + tess.numIndexes += indexes; + + // + // lerp all the needed bones + // + if ( !backlerp ) { + // no lerping needed + bonePtr = frame->bones; + } else { + bonePtr = bones; + for ( i = 0 ; i < header->numBones*12 ; i++ ) { + ((float *)bonePtr)[i] = frontlerp * ((float *)frame->bones)[i] + + backlerp * ((float *)oldFrame->bones)[i]; + } + } + + // + // deform the vertexes by the lerped bones + // + numVerts = surface->numVerts; + // FIXME + // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left + // in for reference. + //v = (md4Vertex_t *) ((byte *)surface + surface->ofsVerts + 12); + v = (md4Vertex_t *) ((byte *)surface + surface->ofsVerts); + for ( j = 0; j < numVerts; j++ ) { + vec3_t tempVert, tempNormal; + md4Weight_t *w; + + VectorClear( tempVert ); + VectorClear( tempNormal ); + w = v->weights; + for ( k = 0 ; k < v->numWeights ; k++, w++ ) { + bone = bonePtr + w->boneIndex; + + tempVert[0] += w->boneWeight * ( DotProduct( bone->matrix[0], w->offset ) + bone->matrix[0][3] ); + tempVert[1] += w->boneWeight * ( DotProduct( bone->matrix[1], w->offset ) + bone->matrix[1][3] ); + tempVert[2] += w->boneWeight * ( DotProduct( bone->matrix[2], w->offset ) + bone->matrix[2][3] ); + + tempNormal[0] += w->boneWeight * DotProduct( bone->matrix[0], v->normal ); + tempNormal[1] += w->boneWeight * DotProduct( bone->matrix[1], v->normal ); + tempNormal[2] += w->boneWeight * DotProduct( bone->matrix[2], v->normal ); + } + + tess.xyz[baseVertex + j][0] = tempVert[0]; + tess.xyz[baseVertex + j][1] = tempVert[1]; + tess.xyz[baseVertex + j][2] = tempVert[2]; + + tess.normal[baseVertex + j][0] = tempNormal[0]; + tess.normal[baseVertex + j][1] = tempNormal[1]; + tess.normal[baseVertex + j][2] = tempNormal[2]; + + tess.texCoords[baseVertex + j][0][0] = v->texCoords[0]; + tess.texCoords[baseVertex + j][0][1] = v->texCoords[1]; + + // FIXME + // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left + // in for reference. + //v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights] + 12 ); + v = (md4Vertex_t *)&v->weights[v->numWeights]; + } + + tess.numVertexes += surface->numVerts; +} + + +#ifdef RAVENMD4 + +// copied and adapted from tr_mesh.c + +/* +============= +R_MDRCullModel +============= +*/ + +static int R_MDRCullModel( mdrHeader_t *header, trRefEntity_t *ent ) { + vec3_t bounds[2]; + mdrFrame_t *oldFrame, *newFrame; + int i, frameSize; + + frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] ); + + // compute frame pointers + newFrame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.frame); + oldFrame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.oldframe); + + // cull bounding sphere ONLY if this is not an upscaled entity + if ( !ent->e.nonNormalizedAxes ) + { + if ( ent->e.frame == ent->e.oldframe ) + { + switch ( R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ) ) + { + // Ummm... yeah yeah I know we don't really have an md3 here.. but we pretend + // we do. After all, the purpose of md4s are not that different, are they? + + case CULL_OUT: + tr.pc.c_sphere_cull_md3_out++; + return CULL_OUT; + + case CULL_IN: + tr.pc.c_sphere_cull_md3_in++; + return CULL_IN; + + case CULL_CLIP: + tr.pc.c_sphere_cull_md3_clip++; + break; + } + } + else + { + int sphereCull, sphereCullB; + + sphereCull = R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ); + if ( newFrame == oldFrame ) { + sphereCullB = sphereCull; + } else { + sphereCullB = R_CullLocalPointAndRadius( oldFrame->localOrigin, oldFrame->radius ); + } + + if ( sphereCull == sphereCullB ) + { + if ( sphereCull == CULL_OUT ) + { + tr.pc.c_sphere_cull_md3_out++; + return CULL_OUT; + } + else if ( sphereCull == CULL_IN ) + { + tr.pc.c_sphere_cull_md3_in++; + return CULL_IN; + } + else + { + tr.pc.c_sphere_cull_md3_clip++; + } + } + } + } + + // calculate a bounding box in the current coordinate system + for (i = 0 ; i < 3 ; i++) { + bounds[0][i] = oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i]; + bounds[1][i] = oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i]; + } + + switch ( R_CullLocalBox( bounds ) ) + { + case CULL_IN: + tr.pc.c_box_cull_md3_in++; + return CULL_IN; + case CULL_CLIP: + tr.pc.c_box_cull_md3_clip++; + return CULL_CLIP; + case CULL_OUT: + default: + tr.pc.c_box_cull_md3_out++; + return CULL_OUT; + } +} + +/* +================= +R_MDRComputeFogNum + +================= +*/ + +int R_MDRComputeFogNum( mdrHeader_t *header, trRefEntity_t *ent ) { + int i, j; + fog_t *fog; + mdrFrame_t *mdrFrame; + vec3_t localOrigin; + int frameSize; + + if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { + return 0; + } + + frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] ); + + // FIXME: non-normalized axis issues + mdrFrame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.frame); + VectorAdd( ent->e.origin, mdrFrame->localOrigin, localOrigin ); + for ( i = 1 ; i < tr.world->numfogs ; i++ ) { + fog = &tr.world->fogs[i]; + for ( j = 0 ; j < 3 ; j++ ) { + if ( localOrigin[j] - mdrFrame->radius >= fog->bounds[1][j] ) { + break; + } + if ( localOrigin[j] + mdrFrame->radius <= fog->bounds[0][j] ) { + break; + } + } + if ( j == 3 ) { + return i; + } + } + + return 0; +} + + +/* +============== +R_MDRAddAnimSurfaces +============== +*/ + +// much stuff in there is just copied from R_AddMd3Surfaces in tr_mesh.c + +void R_MDRAddAnimSurfaces( trRefEntity_t *ent ) { + mdrHeader_t *header; + mdrSurface_t *surface; + mdrLOD_t *lod; + shader_t *shader; + skin_t *skin; + int i, j; + int lodnum = 0; + int fogNum = 0; + int cull; + qboolean personalModel; + + header = (mdrHeader_t *) tr.currentModel->modelData; + + personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal; + + if ( ent->e.renderfx & RF_WRAP_FRAMES ) + { + ent->e.frame %= header->numFrames; + ent->e.oldframe %= header->numFrames; + } + + // + // Validate the frames so there is no chance of a crash. + // This will write directly into the entity structure, so + // when the surfaces are rendered, they don't need to be + // range checked again. + // + if ((ent->e.frame >= header->numFrames) + || (ent->e.frame < 0) + || (ent->e.oldframe >= header->numFrames) + || (ent->e.oldframe < 0) ) + { + ri.Printf( PRINT_DEVELOPER, "R_MDRAddAnimSurfaces: no such frame %d to %d for '%s'\n", + ent->e.oldframe, ent->e.frame, tr.currentModel->name ); + ent->e.frame = 0; + ent->e.oldframe = 0; + } + + // + // cull the entire model if merged bounding box of both frames + // is outside the view frustum. + // + cull = R_MDRCullModel (header, ent); + if ( cull == CULL_OUT ) { + return; + } + + // figure out the current LOD of the model we're rendering, and set the lod pointer respectively. + lodnum = R_ComputeLOD(ent); + // check whether this model has as that many LODs at all. If not, try the closest thing we got. + if(header->numLODs <= 0) + return; + if(header->numLODs <= lodnum) + lodnum = header->numLODs - 1; + + lod = (mdrLOD_t *)( (byte *)header + header->ofsLODs); + for(i = 0; i < lodnum; i++) + { + lod = (mdrLOD_t *) ((byte *) lod + lod->ofsEnd); + } + + // set up lighting + if ( !personalModel || r_shadows->integer > 1 ) + { + R_SetupEntityLighting( &tr.refdef, ent ); + } + + // fogNum? + fogNum = R_MDRComputeFogNum( header, ent ); + + surface = (mdrSurface_t *)( (byte *)lod + lod->ofsSurfaces ); + + for ( i = 0 ; i < lod->numSurfaces ; i++ ) + { + + if(ent->e.customShader) + shader = R_GetShaderByHandle(ent->e.customShader); + else if(ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins) + { + skin = R_GetSkinByHandle(ent->e.customSkin); + shader = tr.defaultShader; + + for(j = 0; j < skin->numSurfaces; j++) + { + if (!strcmp(skin->surfaces[j]->name, surface->name)) + { + shader = skin->surfaces[j]->shader; + break; + } + } + } + else if(surface->shaderIndex > 0) + shader = R_GetShaderByHandle( surface->shaderIndex ); + else + shader = tr.defaultShader; + + // we will add shadows even if the main object isn't visible in the view + + // stencil shadows can't do personal models unless I polyhedron clip + if ( !personalModel + && r_shadows->integer == 2 + && fogNum == 0 + && !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) + && shader->sort == SS_OPAQUE ) + { + R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, qfalse ); + } + + // projection shadows work fine with personal models + if ( r_shadows->integer == 3 + && fogNum == 0 + && (ent->e.renderfx & RF_SHADOW_PLANE ) + && shader->sort == SS_OPAQUE ) + { + R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, qfalse ); + } + + if (!personalModel) + R_AddDrawSurf( (void *)surface, shader, fogNum, qfalse ); + + surface = (mdrSurface_t *)( (byte *)surface + surface->ofsEnd ); + } +} + +/* +============== +RB_MDRSurfaceAnim +============== +*/ +void RB_MDRSurfaceAnim( md4Surface_t *surface ) +{ + int i, j, k; + float frontlerp, backlerp; + int *triangles; + int indexes; + int baseIndex, baseVertex; + int numVerts; + mdrVertex_t *v; + mdrHeader_t *header; + mdrFrame_t *frame; + mdrFrame_t *oldFrame; + mdrBone_t bones[MD4_MAX_BONES], *bonePtr, *bone; + + int frameSize; + + // don't lerp if lerping off, or this is the only frame, or the last frame... + // + if (backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame) + { + backlerp = 0; // if backlerp is 0, lerping is off and frontlerp is never used + frontlerp = 1; + } + else + { + backlerp = backEnd.currentEntity->e.backlerp; + frontlerp = 1.0f - backlerp; + } + + header = (mdrHeader_t *)((byte *)surface + surface->ofsHeader); + + frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] ); + + frame = (mdrFrame_t *)((byte *)header + header->ofsFrames + + backEnd.currentEntity->e.frame * frameSize ); + oldFrame = (mdrFrame_t *)((byte *)header + header->ofsFrames + + backEnd.currentEntity->e.oldframe * frameSize ); + + RB_CheckOverflow( surface->numVerts, surface->numTriangles ); + + triangles = (int *) ((byte *)surface + surface->ofsTriangles); + indexes = surface->numTriangles * 3; + baseIndex = tess.numIndexes; + baseVertex = tess.numVertexes; + + // Set up all triangles. + for (j = 0 ; j < indexes ; j++) + { + tess.indexes[baseIndex + j] = baseVertex + triangles[j]; + } + tess.numIndexes += indexes; + + // + // lerp all the needed bones + // + if ( !backlerp ) + { + // no lerping needed + bonePtr = frame->bones; + } + else + { + bonePtr = bones; + + for ( i = 0 ; i < header->numBones*12 ; i++ ) + { + ((float *)bonePtr)[i] = frontlerp * ((float *)frame->bones)[i] + backlerp * ((float *)oldFrame->bones)[i]; + } + } + + // + // deform the vertexes by the lerped bones + // + numVerts = surface->numVerts; + v = (mdrVertex_t *) ((byte *)surface + surface->ofsVerts); + for ( j = 0; j < numVerts; j++ ) + { + vec3_t tempVert, tempNormal; + mdrWeight_t *w; + + VectorClear( tempVert ); + VectorClear( tempNormal ); + w = v->weights; + for ( k = 0 ; k < v->numWeights ; k++, w++ ) + { + bone = bonePtr + w->boneIndex; + + tempVert[0] += w->boneWeight * ( DotProduct( bone->matrix[0], w->offset ) + bone->matrix[0][3] ); + tempVert[1] += w->boneWeight * ( DotProduct( bone->matrix[1], w->offset ) + bone->matrix[1][3] ); + tempVert[2] += w->boneWeight * ( DotProduct( bone->matrix[2], w->offset ) + bone->matrix[2][3] ); + + tempNormal[0] += w->boneWeight * DotProduct( bone->matrix[0], v->normal ); + tempNormal[1] += w->boneWeight * DotProduct( bone->matrix[1], v->normal ); + tempNormal[2] += w->boneWeight * DotProduct( bone->matrix[2], v->normal ); + } + + tess.xyz[baseVertex + j][0] = tempVert[0]; + tess.xyz[baseVertex + j][1] = tempVert[1]; + tess.xyz[baseVertex + j][2] = tempVert[2]; + + tess.normal[baseVertex + j][0] = tempNormal[0]; + tess.normal[baseVertex + j][1] = tempNormal[1]; + tess.normal[baseVertex + j][2] = tempNormal[2]; + + tess.texCoords[baseVertex + j][0][0] = v->texCoords[0]; + tess.texCoords[baseVertex + j][0][1] = v->texCoords[1]; + + v = (mdrVertex_t *)&v->weights[v->numWeights]; + } + + tess.numVertexes += surface->numVerts; +} + + +#define MC_MASK_X ((1<<(MC_BITS_X))-1) +#define MC_MASK_Y ((1<<(MC_BITS_Y))-1) +#define MC_MASK_Z ((1<<(MC_BITS_Z))-1) +#define MC_MASK_VECT ((1<<(MC_BITS_VECT))-1) + +#define MC_SCALE_VECT (1.0f/(float)((1<<(MC_BITS_VECT-1))-2)) + +#define MC_POS_X (0) +#define MC_SHIFT_X (0) + +#define MC_POS_Y ((((MC_BITS_X))/8)) +#define MC_SHIFT_Y ((((MC_BITS_X)%8))) + +#define MC_POS_Z ((((MC_BITS_X+MC_BITS_Y))/8)) +#define MC_SHIFT_Z ((((MC_BITS_X+MC_BITS_Y)%8))) + +#define MC_POS_V11 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z))/8)) +#define MC_SHIFT_V11 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z)%8))) + +#define MC_POS_V12 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT))/8)) +#define MC_SHIFT_V12 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT)%8))) + +#define MC_POS_V13 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*2))/8)) +#define MC_SHIFT_V13 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*2)%8))) + +#define MC_POS_V21 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*3))/8)) +#define MC_SHIFT_V21 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*3)%8))) + +#define MC_POS_V22 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*4))/8)) +#define MC_SHIFT_V22 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*4)%8))) + +#define MC_POS_V23 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*5))/8)) +#define MC_SHIFT_V23 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*5)%8))) + +#define MC_POS_V31 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*6))/8)) +#define MC_SHIFT_V31 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*6)%8))) + +#define MC_POS_V32 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*7))/8)) +#define MC_SHIFT_V32 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*7)%8))) + +#define MC_POS_V33 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*8))/8)) +#define MC_SHIFT_V33 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*8)%8))) + +void MC_UnCompress(float mat[3][4],const unsigned char * comp) +{ + int val; + + val=(int)((unsigned short *)(comp))[0]; + val-=1<<(MC_BITS_X-1); + mat[0][3]=((float)(val))*MC_SCALE_X; + + val=(int)((unsigned short *)(comp))[1]; + val-=1<<(MC_BITS_Y-1); + mat[1][3]=((float)(val))*MC_SCALE_Y; + + val=(int)((unsigned short *)(comp))[2]; + val-=1<<(MC_BITS_Z-1); + mat[2][3]=((float)(val))*MC_SCALE_Z; + + val=(int)((unsigned short *)(comp))[3]; + val-=1<<(MC_BITS_VECT-1); + mat[0][0]=((float)(val))*MC_SCALE_VECT; + + val=(int)((unsigned short *)(comp))[4]; + val-=1<<(MC_BITS_VECT-1); + mat[0][1]=((float)(val))*MC_SCALE_VECT; + + val=(int)((unsigned short *)(comp))[5]; + val-=1<<(MC_BITS_VECT-1); + mat[0][2]=((float)(val))*MC_SCALE_VECT; + + + val=(int)((unsigned short *)(comp))[6]; + val-=1<<(MC_BITS_VECT-1); + mat[1][0]=((float)(val))*MC_SCALE_VECT; + + val=(int)((unsigned short *)(comp))[7]; + val-=1<<(MC_BITS_VECT-1); + mat[1][1]=((float)(val))*MC_SCALE_VECT; + + val=(int)((unsigned short *)(comp))[8]; + val-=1<<(MC_BITS_VECT-1); + mat[1][2]=((float)(val))*MC_SCALE_VECT; + + + val=(int)((unsigned short *)(comp))[9]; + val-=1<<(MC_BITS_VECT-1); + mat[2][0]=((float)(val))*MC_SCALE_VECT; + + val=(int)((unsigned short *)(comp))[10]; + val-=1<<(MC_BITS_VECT-1); + mat[2][1]=((float)(val))*MC_SCALE_VECT; + + val=(int)((unsigned short *)(comp))[11]; + val-=1<<(MC_BITS_VECT-1); + mat[2][2]=((float)(val))*MC_SCALE_VECT; +} +#endif diff --git a/src/renderergl1/tr_backend.c b/src/renderergl1/tr_backend.c new file mode 100644 index 00000000..61495083 --- /dev/null +++ b/src/renderergl1/tr_backend.c @@ -0,0 +1,1162 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +#include "tr_local.h" + +backEndData_t *backEndData; +backEndState_t backEnd; + + +static float s_flipMatrix[16] = { + // convert from our coordinate system (looking down X) + // to OpenGL's coordinate system (looking down -Z) + 0, 0, -1, 0, + -1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 0, 1 +}; + + +/* +** GL_Bind +*/ +void GL_Bind( image_t *image ) { + int texnum; + + if ( !image ) { + ri.Printf( PRINT_WARNING, "GL_Bind: NULL image\n" ); + texnum = tr.defaultImage->texnum; + } else { + texnum = image->texnum; + } + + if ( r_nobind->integer && tr.dlightImage ) { // performance evaluation option + texnum = tr.dlightImage->texnum; + } + + if ( glState.currenttextures[glState.currenttmu] != texnum ) { + image->frameUsed = tr.frameCount; + glState.currenttextures[glState.currenttmu] = texnum; + qglBindTexture (GL_TEXTURE_2D, texnum); + } +} + +/* +** GL_SelectTexture +*/ +void GL_SelectTexture( int unit ) +{ + if ( glState.currenttmu == unit ) + { + return; + } + + if ( unit == 0 ) + { + qglActiveTextureARB( GL_TEXTURE0_ARB ); + GLimp_LogComment( "glActiveTextureARB( GL_TEXTURE0_ARB )\n" ); + qglClientActiveTextureARB( GL_TEXTURE0_ARB ); + GLimp_LogComment( "glClientActiveTextureARB( GL_TEXTURE0_ARB )\n" ); + } + else if ( unit == 1 ) + { + qglActiveTextureARB( GL_TEXTURE1_ARB ); + GLimp_LogComment( "glActiveTextureARB( GL_TEXTURE1_ARB )\n" ); + qglClientActiveTextureARB( GL_TEXTURE1_ARB ); + GLimp_LogComment( "glClientActiveTextureARB( GL_TEXTURE1_ARB )\n" ); + } else { + ri.Error( ERR_DROP, "GL_SelectTexture: unit = %i", unit ); + } + + glState.currenttmu = unit; +} + + +/* +** GL_BindMultitexture +*/ +void GL_BindMultitexture( image_t *image0, GLuint env0, image_t *image1, GLuint env1 ) { + int texnum0, texnum1; + + texnum0 = image0->texnum; + texnum1 = image1->texnum; + + if ( r_nobind->integer && tr.dlightImage ) { // performance evaluation option + texnum0 = texnum1 = tr.dlightImage->texnum; + } + + if ( glState.currenttextures[1] != texnum1 ) { + GL_SelectTexture( 1 ); + image1->frameUsed = tr.frameCount; + glState.currenttextures[1] = texnum1; + qglBindTexture( GL_TEXTURE_2D, texnum1 ); + } + if ( glState.currenttextures[0] != texnum0 ) { + GL_SelectTexture( 0 ); + image0->frameUsed = tr.frameCount; + glState.currenttextures[0] = texnum0; + qglBindTexture( GL_TEXTURE_2D, texnum0 ); + } +} + + +/* +** GL_Cull +*/ +void GL_Cull( int cullType ) { + if ( glState.faceCulling == cullType ) { + return; + } + + glState.faceCulling = cullType; + + if ( cullType == CT_TWO_SIDED ) + { + qglDisable( GL_CULL_FACE ); + } + else + { + qboolean cullFront; + qglEnable( GL_CULL_FACE ); + + cullFront = (cullType == CT_FRONT_SIDED); + if ( backEnd.viewParms.isMirror ) + { + cullFront = !cullFront; + } + + qglCullFace( cullFront ? GL_FRONT : GL_BACK ); + } +} + +/* +** GL_TexEnv +*/ +void GL_TexEnv( int env ) +{ + if ( env == glState.texEnv[glState.currenttmu] ) + { + return; + } + + glState.texEnv[glState.currenttmu] = env; + + + switch ( env ) + { + case GL_MODULATE: + qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + break; + case GL_REPLACE: + qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + break; + case GL_DECAL: + qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL ); + break; + case GL_ADD: + qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD ); + break; + default: + ri.Error( ERR_DROP, "GL_TexEnv: invalid env '%d' passed", env ); + break; + } +} + +/* +** GL_State +** +** This routine is responsible for setting the most commonly changed state +** in Q3. +*/ +void GL_State( unsigned long stateBits ) +{ + unsigned long diff = stateBits ^ glState.glStateBits; + + if ( !diff ) + { + return; + } + + // + // check depthFunc bits + // + if ( diff & GLS_DEPTHFUNC_EQUAL ) + { + if ( stateBits & GLS_DEPTHFUNC_EQUAL ) + { + qglDepthFunc( GL_EQUAL ); + } + else + { + qglDepthFunc( GL_LEQUAL ); + } + } + + // + // check blend bits + // + if ( diff & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) + { + GLenum srcFactor, dstFactor; + + if ( stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) + { + switch ( stateBits & GLS_SRCBLEND_BITS ) + { + case GLS_SRCBLEND_ZERO: + srcFactor = GL_ZERO; + break; + case GLS_SRCBLEND_ONE: + srcFactor = GL_ONE; + break; + case GLS_SRCBLEND_DST_COLOR: + srcFactor = GL_DST_COLOR; + break; + case GLS_SRCBLEND_ONE_MINUS_DST_COLOR: + srcFactor = GL_ONE_MINUS_DST_COLOR; + break; + case GLS_SRCBLEND_SRC_ALPHA: + srcFactor = GL_SRC_ALPHA; + break; + case GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA: + srcFactor = GL_ONE_MINUS_SRC_ALPHA; + break; + case GLS_SRCBLEND_DST_ALPHA: + srcFactor = GL_DST_ALPHA; + break; + case GLS_SRCBLEND_ONE_MINUS_DST_ALPHA: + srcFactor = GL_ONE_MINUS_DST_ALPHA; + break; + case GLS_SRCBLEND_ALPHA_SATURATE: + srcFactor = GL_SRC_ALPHA_SATURATE; + break; + default: + srcFactor = GL_ONE; // to get warning to shut up + ri.Error( ERR_DROP, "GL_State: invalid src blend state bits" ); + break; + } + + switch ( stateBits & GLS_DSTBLEND_BITS ) + { + case GLS_DSTBLEND_ZERO: + dstFactor = GL_ZERO; + break; + case GLS_DSTBLEND_ONE: + dstFactor = GL_ONE; + break; + case GLS_DSTBLEND_SRC_COLOR: + dstFactor = GL_SRC_COLOR; + break; + case GLS_DSTBLEND_ONE_MINUS_SRC_COLOR: + dstFactor = GL_ONE_MINUS_SRC_COLOR; + break; + case GLS_DSTBLEND_SRC_ALPHA: + dstFactor = GL_SRC_ALPHA; + break; + case GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA: + dstFactor = GL_ONE_MINUS_SRC_ALPHA; + break; + case GLS_DSTBLEND_DST_ALPHA: + dstFactor = GL_DST_ALPHA; + break; + case GLS_DSTBLEND_ONE_MINUS_DST_ALPHA: + dstFactor = GL_ONE_MINUS_DST_ALPHA; + break; + default: + dstFactor = GL_ONE; // to get warning to shut up + ri.Error( ERR_DROP, "GL_State: invalid dst blend state bits" ); + break; + } + + qglEnable( GL_BLEND ); + qglBlendFunc( srcFactor, dstFactor ); + } + else + { + qglDisable( GL_BLEND ); + } + } + + // + // check depthmask + // + if ( diff & GLS_DEPTHMASK_TRUE ) + { + if ( stateBits & GLS_DEPTHMASK_TRUE ) + { + qglDepthMask( GL_TRUE ); + } + else + { + qglDepthMask( GL_FALSE ); + } + } + + // + // fill/line mode + // + if ( diff & GLS_POLYMODE_LINE ) + { + if ( stateBits & GLS_POLYMODE_LINE ) + { + qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + } + else + { + qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + } + } + + // + // depthtest + // + if ( diff & GLS_DEPTHTEST_DISABLE ) + { + if ( stateBits & GLS_DEPTHTEST_DISABLE ) + { + qglDisable( GL_DEPTH_TEST ); + } + else + { + qglEnable( GL_DEPTH_TEST ); + } + } + + // + // alpha test + // + if ( diff & GLS_ATEST_BITS ) + { + switch ( stateBits & GLS_ATEST_BITS ) + { + case 0: + qglDisable( GL_ALPHA_TEST ); + break; + case GLS_ATEST_GT_0: + qglEnable( GL_ALPHA_TEST ); + qglAlphaFunc( GL_GREATER, 0.0f ); + break; + case GLS_ATEST_LT_80: + qglEnable( GL_ALPHA_TEST ); + qglAlphaFunc( GL_LESS, 0.5f ); + break; + case GLS_ATEST_GE_80: + qglEnable( GL_ALPHA_TEST ); + qglAlphaFunc( GL_GEQUAL, 0.5f ); + break; + default: + assert( 0 ); + break; + } + } + + glState.glStateBits = stateBits; +} + + + +/* +================ +RB_Hyperspace + +A player has predicted a teleport, but hasn't arrived yet +================ +*/ +static void RB_Hyperspace( void ) { + float c; + + if ( !backEnd.isHyperspace ) { + // do initialization shit + } + + c = ( backEnd.refdef.time & 255 ) / 255.0f; + qglClearColor( c, c, c, 1 ); + qglClear( GL_COLOR_BUFFER_BIT ); + + backEnd.isHyperspace = qtrue; +} + + +static void SetViewportAndScissor( void ) { + qglMatrixMode(GL_PROJECTION); + qglLoadMatrixf( backEnd.viewParms.projectionMatrix ); + qglMatrixMode(GL_MODELVIEW); + + // set the window clipping + qglViewport( backEnd.viewParms.viewportX, backEnd.viewParms.viewportY, + backEnd.viewParms.viewportWidth, backEnd.viewParms.viewportHeight ); + qglScissor( backEnd.viewParms.viewportX, backEnd.viewParms.viewportY, + backEnd.viewParms.viewportWidth, backEnd.viewParms.viewportHeight ); +} + +/* +================= +RB_BeginDrawingView + +Any mirrored or portaled views have already been drawn, so prepare +to actually render the visible surfaces for this view +================= +*/ +void RB_BeginDrawingView (void) { + int clearBits = 0; + + // sync with gl if needed + if ( r_finish->integer == 1 && !glState.finishCalled ) { + qglFinish (); + glState.finishCalled = qtrue; + } + if ( r_finish->integer == 0 ) { + glState.finishCalled = qtrue; + } + + // we will need to change the projection matrix before drawing + // 2D images again + backEnd.projection2D = qfalse; + + // + // set the modelview matrix for the viewer + // + SetViewportAndScissor(); + + // ensures that depth writes are enabled for the depth clear + GL_State( GLS_DEFAULT ); + // clear relevant buffers + clearBits = GL_DEPTH_BUFFER_BIT; + + if ( r_measureOverdraw->integer || r_shadows->integer == 2 ) + { + clearBits |= GL_STENCIL_BUFFER_BIT; + } + if ( r_fastsky->integer && !( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) ) + { + clearBits |= GL_COLOR_BUFFER_BIT; // FIXME: only if sky shaders have been used +#ifdef _DEBUG + qglClearColor( 0.8f, 0.7f, 0.4f, 1.0f ); // FIXME: get color of sky +#else + qglClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); // FIXME: get color of sky +#endif + } + qglClear( clearBits ); + + if ( ( backEnd.refdef.rdflags & RDF_HYPERSPACE ) ) + { + RB_Hyperspace(); + return; + } + else + { + backEnd.isHyperspace = qfalse; + } + + glState.faceCulling = -1; // force face culling to set next time + + // we will only draw a sun if there was sky rendered in this view + backEnd.skyRenderedThisView = qfalse; + + // clip to the plane of the portal + if ( backEnd.viewParms.isPortal ) { + float plane[4]; + double plane2[4]; + + plane[0] = backEnd.viewParms.portalPlane.normal[0]; + plane[1] = backEnd.viewParms.portalPlane.normal[1]; + plane[2] = backEnd.viewParms.portalPlane.normal[2]; + plane[3] = backEnd.viewParms.portalPlane.dist; + + plane2[0] = DotProduct (backEnd.viewParms.or.axis[0], plane); + plane2[1] = DotProduct (backEnd.viewParms.or.axis[1], plane); + plane2[2] = DotProduct (backEnd.viewParms.or.axis[2], plane); + plane2[3] = DotProduct (plane, backEnd.viewParms.or.origin) - plane[3]; + + qglLoadMatrixf( s_flipMatrix ); + qglClipPlane (GL_CLIP_PLANE0, plane2); + qglEnable (GL_CLIP_PLANE0); + } else { + qglDisable (GL_CLIP_PLANE0); + } +} + + +#define MAC_EVENT_PUMP_MSEC 5 + +/* +================== +RB_RenderDrawSurfList +================== +*/ +void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { + shader_t *shader, *oldShader; + int fogNum, oldFogNum; + int entityNum, oldEntityNum; + int dlighted, oldDlighted; + qboolean depthRange, oldDepthRange, isCrosshair, wasCrosshair; + int i; + drawSurf_t *drawSurf; + int oldSort; + float originalTime; + + // save original time for entity shader offsets + originalTime = backEnd.refdef.floatTime; + + // clear the z buffer, set the modelview, etc + RB_BeginDrawingView (); + + // draw everything + oldEntityNum = -1; + backEnd.currentEntity = &tr.worldEntity; + oldShader = NULL; + oldFogNum = -1; + oldDepthRange = qfalse; + wasCrosshair = qfalse; + oldDlighted = qfalse; + oldSort = -1; + depthRange = qfalse; + + backEnd.pc.c_surfaces += numDrawSurfs; + + for (i = 0, drawSurf = drawSurfs ; i < numDrawSurfs ; i++, drawSurf++) { + if ( drawSurf->sort == oldSort ) { + // fast path, same as previous sort + rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface ); + continue; + } + oldSort = drawSurf->sort; + R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted ); + + // + // change the tess parameters if needed + // a "entityMergable" shader is a shader that can have surfaces from seperate + // entities merged into a single batch, like smoke and blood puff sprites + if (shader != oldShader || fogNum != oldFogNum || dlighted != oldDlighted + || ( entityNum != oldEntityNum && !shader->entityMergable ) ) { + if (oldShader != NULL) { + RB_EndSurface(); + } + RB_BeginSurface( shader, fogNum ); + oldShader = shader; + oldFogNum = fogNum; + oldDlighted = dlighted; + } + + // + // change the modelview matrix if needed + // + if ( entityNum != oldEntityNum ) { + depthRange = isCrosshair = qfalse; + + if ( entityNum != REFENTITYNUM_WORLD ) { + backEnd.currentEntity = &backEnd.refdef.entities[entityNum]; + backEnd.refdef.floatTime = originalTime - backEnd.currentEntity->e.shaderTime; + // we have to reset the shaderTime as well otherwise image animations start + // from the wrong frame + tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; + + // set up the transformation matrix + R_RotateForEntity( backEnd.currentEntity, &backEnd.viewParms, &backEnd.or ); + + // set up the dynamic lighting if needed + if ( backEnd.currentEntity->needDlights ) { + R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.or ); + } + + if(backEnd.currentEntity->e.renderfx & RF_DEPTHHACK) + { + // hack the depth range to prevent view model from poking into walls + depthRange = qtrue; + + if(backEnd.currentEntity->e.renderfx & RF_CROSSHAIR) + isCrosshair = qtrue; + } + } else { + backEnd.currentEntity = &tr.worldEntity; + backEnd.refdef.floatTime = originalTime; + backEnd.or = backEnd.viewParms.world; + // we have to reset the shaderTime as well otherwise image animations on + // the world (like water) continue with the wrong frame + tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; + R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.or ); + } + + qglLoadMatrixf( backEnd.or.modelMatrix ); + + // + // change depthrange. Also change projection matrix so first person weapon does not look like coming + // out of the screen. + // + if (oldDepthRange != depthRange || wasCrosshair != isCrosshair) + { + if (depthRange) + { + if(backEnd.viewParms.stereoFrame != STEREO_CENTER) + { + if(isCrosshair) + { + if(oldDepthRange) + { + // was not a crosshair but now is, change back proj matrix + qglMatrixMode(GL_PROJECTION); + qglLoadMatrixf(backEnd.viewParms.projectionMatrix); + qglMatrixMode(GL_MODELVIEW); + } + } + else + { + viewParms_t temp = backEnd.viewParms; + + R_SetupProjection(&temp, r_znear->value, qfalse); + + qglMatrixMode(GL_PROJECTION); + qglLoadMatrixf(temp.projectionMatrix); + qglMatrixMode(GL_MODELVIEW); + } + } + + if(!oldDepthRange) + qglDepthRange (0, 0.3); + } + else + { + if(!wasCrosshair && backEnd.viewParms.stereoFrame != STEREO_CENTER) + { + qglMatrixMode(GL_PROJECTION); + qglLoadMatrixf(backEnd.viewParms.projectionMatrix); + qglMatrixMode(GL_MODELVIEW); + } + + qglDepthRange (0, 1); + } + + oldDepthRange = depthRange; + wasCrosshair = isCrosshair; + } + + oldEntityNum = entityNum; + } + + // add the triangles for this surface + rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface ); + } + + backEnd.refdef.floatTime = originalTime; + + // draw the contents of the last shader batch + if (oldShader != NULL) { + RB_EndSurface(); + } + + // go back to the world modelview matrix + qglLoadMatrixf( backEnd.viewParms.world.modelMatrix ); + if ( depthRange ) { + qglDepthRange (0, 1); + } + +#if 0 + RB_DrawSun(); +#endif + // darken down any stencil shadows + RB_ShadowFinish(); + + // add light flares on lights that aren't obscured + RB_RenderFlares(); +} + + +/* +============================================================================ + +RENDER BACK END FUNCTIONS + +============================================================================ +*/ + +/* +================ +RB_SetGL2D + +================ +*/ +void RB_SetGL2D (void) { + backEnd.projection2D = qtrue; + + // set 2D virtual screen size + qglViewport( 0, 0, glConfig.vidWidth, glConfig.vidHeight ); + qglScissor( 0, 0, glConfig.vidWidth, glConfig.vidHeight ); + qglMatrixMode(GL_PROJECTION); + qglLoadIdentity (); + qglOrtho (0, glConfig.vidWidth, glConfig.vidHeight, 0, 0, 1); + qglMatrixMode(GL_MODELVIEW); + qglLoadIdentity (); + + GL_State( GLS_DEPTHTEST_DISABLE | + GLS_SRCBLEND_SRC_ALPHA | + GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); + + qglDisable( GL_CULL_FACE ); + qglDisable( GL_CLIP_PLANE0 ); + + // set time for 2D shaders + backEnd.refdef.time = ri.Milliseconds(); + backEnd.refdef.floatTime = backEnd.refdef.time * 0.001f; +} + + +/* +============= +RE_StretchRaw + +FIXME: not exactly backend +Stretches a raw 32 bit power of 2 bitmap image over the given screen rectangle. +Used for cinematics. +============= +*/ +void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty) { + int i, j; + int start, end; + + if ( !tr.registered ) { + return; + } + R_IssuePendingRenderCommands(); + + // we definately want to sync every frame for the cinematics + qglFinish(); + + start = 0; + if ( r_speeds->integer ) { + start = ri.Milliseconds(); + } + + // make sure rows and cols are powers of 2 + for ( i = 0 ; ( 1 << i ) < cols ; i++ ) { + } + for ( j = 0 ; ( 1 << j ) < rows ; j++ ) { + } + if ( ( 1 << i ) != cols || ( 1 << j ) != rows) { + ri.Error (ERR_DROP, "Draw_StretchRaw: size not a power of 2: %i by %i", cols, rows); + } + + GL_Bind( tr.scratchImage[client] ); + + // if the scratchImage isn't in the format we want, specify it as a new texture + if ( cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height ) { + tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols; + tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows; + qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + } else { + if (dirty) { + // otherwise, just subimage upload it so that drivers can tell we are going to be changing + // it and don't try and do a texture compression + qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data ); + } + } + + if ( r_speeds->integer ) { + end = ri.Milliseconds(); + ri.Printf( PRINT_ALL, "qglTexSubImage2D %i, %i: %i msec\n", cols, rows, end - start ); + } + + RB_SetGL2D(); + + qglColor3f( tr.identityLight, tr.identityLight, tr.identityLight ); + + qglBegin (GL_QUADS); + qglTexCoord2f ( 0.5f / cols, 0.5f / rows ); + qglVertex2f (x, y); + qglTexCoord2f ( ( cols - 0.5f ) / cols , 0.5f / rows ); + qglVertex2f (x+w, y); + qglTexCoord2f ( ( cols - 0.5f ) / cols, ( rows - 0.5f ) / rows ); + qglVertex2f (x+w, y+h); + qglTexCoord2f ( 0.5f / cols, ( rows - 0.5f ) / rows ); + qglVertex2f (x, y+h); + qglEnd (); +} + +void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty) { + + GL_Bind( tr.scratchImage[client] ); + + // if the scratchImage isn't in the format we want, specify it as a new texture + if ( cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height ) { + tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols; + tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows; + qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + } else { + if (dirty) { + // otherwise, just subimage upload it so that drivers can tell we are going to be changing + // it and don't try and do a texture compression + qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data ); + } + } +} + + +/* +============= +RB_SetColor + +============= +*/ +const void *RB_SetColor( const void *data ) { + const setColorCommand_t *cmd; + + cmd = (const setColorCommand_t *)data; + + backEnd.color2D[0] = cmd->color[0] * 255; + backEnd.color2D[1] = cmd->color[1] * 255; + backEnd.color2D[2] = cmd->color[2] * 255; + backEnd.color2D[3] = cmd->color[3] * 255; + + return (const void *)(cmd + 1); +} + +/* +============= +RB_StretchPic +============= +*/ +const void *RB_StretchPic ( const void *data ) { + const stretchPicCommand_t *cmd; + shader_t *shader; + int numVerts, numIndexes; + + cmd = (const stretchPicCommand_t *)data; + + if ( !backEnd.projection2D ) { + RB_SetGL2D(); + } + + shader = cmd->shader; + if ( shader != tess.shader ) { + if ( tess.numIndexes ) { + RB_EndSurface(); + } + backEnd.currentEntity = &backEnd.entity2D; + RB_BeginSurface( shader, 0 ); + } + + RB_CHECKOVERFLOW( 4, 6 ); + numVerts = tess.numVertexes; + numIndexes = tess.numIndexes; + + tess.numVertexes += 4; + tess.numIndexes += 6; + + tess.indexes[ numIndexes ] = numVerts + 3; + tess.indexes[ numIndexes + 1 ] = numVerts + 0; + tess.indexes[ numIndexes + 2 ] = numVerts + 2; + tess.indexes[ numIndexes + 3 ] = numVerts + 2; + tess.indexes[ numIndexes + 4 ] = numVerts + 0; + tess.indexes[ numIndexes + 5 ] = numVerts + 1; + + *(int *)tess.vertexColors[ numVerts ] = + *(int *)tess.vertexColors[ numVerts + 1 ] = + *(int *)tess.vertexColors[ numVerts + 2 ] = + *(int *)tess.vertexColors[ numVerts + 3 ] = *(int *)backEnd.color2D; + + tess.xyz[ numVerts ][0] = cmd->x; + tess.xyz[ numVerts ][1] = cmd->y; + tess.xyz[ numVerts ][2] = 0; + + tess.texCoords[ numVerts ][0][0] = cmd->s1; + tess.texCoords[ numVerts ][0][1] = cmd->t1; + + tess.xyz[ numVerts + 1 ][0] = cmd->x + cmd->w; + tess.xyz[ numVerts + 1 ][1] = cmd->y; + tess.xyz[ numVerts + 1 ][2] = 0; + + tess.texCoords[ numVerts + 1 ][0][0] = cmd->s2; + tess.texCoords[ numVerts + 1 ][0][1] = cmd->t1; + + tess.xyz[ numVerts + 2 ][0] = cmd->x + cmd->w; + tess.xyz[ numVerts + 2 ][1] = cmd->y + cmd->h; + tess.xyz[ numVerts + 2 ][2] = 0; + + tess.texCoords[ numVerts + 2 ][0][0] = cmd->s2; + tess.texCoords[ numVerts + 2 ][0][1] = cmd->t2; + + tess.xyz[ numVerts + 3 ][0] = cmd->x; + tess.xyz[ numVerts + 3 ][1] = cmd->y + cmd->h; + tess.xyz[ numVerts + 3 ][2] = 0; + + tess.texCoords[ numVerts + 3 ][0][0] = cmd->s1; + tess.texCoords[ numVerts + 3 ][0][1] = cmd->t2; + + return (const void *)(cmd + 1); +} + + +/* +============= +RB_DrawSurfs + +============= +*/ +const void *RB_DrawSurfs( const void *data ) { + const drawSurfsCommand_t *cmd; + + // finish any 2D drawing if needed + if ( tess.numIndexes ) { + RB_EndSurface(); + } + + cmd = (const drawSurfsCommand_t *)data; + + backEnd.refdef = cmd->refdef; + backEnd.viewParms = cmd->viewParms; + + RB_RenderDrawSurfList( cmd->drawSurfs, cmd->numDrawSurfs ); + + return (const void *)(cmd + 1); +} + + +/* +============= +RB_DrawBuffer + +============= +*/ +const void *RB_DrawBuffer( const void *data ) { + const drawBufferCommand_t *cmd; + + cmd = (const drawBufferCommand_t *)data; + + qglDrawBuffer( cmd->buffer ); + + // clear screen for debugging + if ( r_clear->integer ) { + qglClearColor( 1, 0, 0.5, 1 ); + qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + } + + return (const void *)(cmd + 1); +} + +/* +=============== +RB_ShowImages + +Draw all the images to the screen, on top of whatever +was there. This is used to test for texture thrashing. + +Also called by RE_EndRegistration +=============== +*/ +void RB_ShowImages( void ) { + int i; + image_t *image; + float x, y, w, h; + int start, end; + + if ( !backEnd.projection2D ) { + RB_SetGL2D(); + } + + qglClear( GL_COLOR_BUFFER_BIT ); + + qglFinish(); + + start = ri.Milliseconds(); + + for ( i=0 ; iinteger == 2 ) { + w *= image->uploadWidth / 512.0f; + h *= image->uploadHeight / 512.0f; + } + + GL_Bind( image ); + qglBegin (GL_QUADS); + qglTexCoord2f( 0, 0 ); + qglVertex2f( x, y ); + qglTexCoord2f( 1, 0 ); + qglVertex2f( x + w, y ); + qglTexCoord2f( 1, 1 ); + qglVertex2f( x + w, y + h ); + qglTexCoord2f( 0, 1 ); + qglVertex2f( x, y + h ); + qglEnd(); + } + + qglFinish(); + + end = ri.Milliseconds(); + ri.Printf( PRINT_ALL, "%i msec to draw all images\n", end - start ); + +} + +/* +============= +RB_ColorMask + +============= +*/ +const void *RB_ColorMask(const void *data) +{ + const colorMaskCommand_t *cmd = data; + + qglColorMask(cmd->rgba[0], cmd->rgba[1], cmd->rgba[2], cmd->rgba[3]); + + return (const void *)(cmd + 1); +} + +/* +============= +RB_ClearDepth + +============= +*/ +const void *RB_ClearDepth(const void *data) +{ + const clearDepthCommand_t *cmd = data; + + if(tess.numIndexes) + RB_EndSurface(); + + // texture swapping test + if (r_showImages->integer) + RB_ShowImages(); + + qglClear(GL_DEPTH_BUFFER_BIT); + + return (const void *)(cmd + 1); +} + +/* +============= +RB_SwapBuffers + +============= +*/ +const void *RB_SwapBuffers( const void *data ) { + const swapBuffersCommand_t *cmd; + + // finish any 2D drawing if needed + if ( tess.numIndexes ) { + RB_EndSurface(); + } + + // texture swapping test + if ( r_showImages->integer ) { + RB_ShowImages(); + } + + cmd = (const swapBuffersCommand_t *)data; + + // we measure overdraw by reading back the stencil buffer and + // counting up the number of increments that have happened + if ( r_measureOverdraw->integer ) { + int i; + long sum = 0; + unsigned char *stencilReadback; + + stencilReadback = ri.Hunk_AllocateTempMemory( glConfig.vidWidth * glConfig.vidHeight ); + qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencilReadback ); + + for ( i = 0; i < glConfig.vidWidth * glConfig.vidHeight; i++ ) { + sum += stencilReadback[i]; + } + + backEnd.pc.c_overDraw += sum; + ri.Hunk_FreeTempMemory( stencilReadback ); + } + + + if ( !glState.finishCalled ) { + qglFinish(); + } + + GLimp_LogComment( "***************** RB_SwapBuffers *****************\n\n\n" ); + + GLimp_EndFrame(); + + backEnd.projection2D = qfalse; + + return (const void *)(cmd + 1); +} + +/* +==================== +RB_ExecuteRenderCommands +==================== +*/ +void RB_ExecuteRenderCommands( const void *data ) { + int t1, t2; + + t1 = ri.Milliseconds (); + + while ( 1 ) { + data = PADP(data, sizeof(void *)); + + switch ( *(const int *)data ) { + case RC_SET_COLOR: + data = RB_SetColor( data ); + break; + case RC_STRETCH_PIC: + data = RB_StretchPic( data ); + break; + case RC_DRAW_SURFS: + data = RB_DrawSurfs( data ); + break; + case RC_DRAW_BUFFER: + data = RB_DrawBuffer( data ); + break; + case RC_SWAP_BUFFERS: + data = RB_SwapBuffers( data ); + break; + case RC_SCREENSHOT: + data = RB_TakeScreenshotCmd( data ); + break; + case RC_VIDEOFRAME: + data = RB_TakeVideoFrameCmd( data ); + break; + case RC_COLORMASK: + data = RB_ColorMask(data); + break; + case RC_CLEARDEPTH: + data = RB_ClearDepth(data); + break; + case RC_END_OF_LIST: + default: + // stop rendering + t2 = ri.Milliseconds (); + backEnd.pc.msec = t2 - t1; + return; + } + } + +} diff --git a/src/renderergl1/tr_bsp.c b/src/renderergl1/tr_bsp.c new file mode 100644 index 00000000..74596a53 --- /dev/null +++ b/src/renderergl1/tr_bsp.c @@ -0,0 +1,1870 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_map.c + +#include "tr_local.h" + +/* + +Loads and prepares a map file for scene rendering. + +A single entry point: + +void RE_LoadWorldMap( const char *name ); + +*/ + +static world_t s_worldData; +static byte *fileBase; + +int c_subdivisions; +int c_gridVerts; + +//=============================================================================== + +static void HSVtoRGB( float h, float s, float v, float rgb[3] ) +{ + int i; + float f; + float p, q, t; + + h *= 5; + + i = floor( h ); + f = h - i; + + p = v * ( 1 - s ); + q = v * ( 1 - s * f ); + t = v * ( 1 - s * ( 1 - f ) ); + + switch ( i ) + { + case 0: + rgb[0] = v; + rgb[1] = t; + rgb[2] = p; + break; + case 1: + rgb[0] = q; + rgb[1] = v; + rgb[2] = p; + break; + case 2: + rgb[0] = p; + rgb[1] = v; + rgb[2] = t; + break; + case 3: + rgb[0] = p; + rgb[1] = q; + rgb[2] = v; + break; + case 4: + rgb[0] = t; + rgb[1] = p; + rgb[2] = v; + break; + case 5: + rgb[0] = v; + rgb[1] = p; + rgb[2] = q; + break; + } +} + +/* +=============== +R_ColorShiftLightingBytes + +=============== +*/ +static void R_ColorShiftLightingBytes( byte in[4], byte out[4] ) { + int shift, r, g, b; + + // shift the color data based on overbright range + shift = r_mapOverBrightBits->integer - tr.overbrightBits; + + // shift the data based on overbright range + r = in[0] << shift; + g = in[1] << shift; + b = in[2] << shift; + + // normalize by color instead of saturating to white + if ( ( r | g | b ) > 255 ) { + int max; + + max = r > g ? r : g; + max = max > b ? max : b; + r = r * 255 / max; + g = g * 255 / max; + b = b * 255 / max; + } + + out[0] = r; + out[1] = g; + out[2] = b; + out[3] = in[3]; +} + +/* +=============== +R_LoadLightmaps + +=============== +*/ +#define LIGHTMAP_SIZE 128 +static void R_LoadLightmaps( lump_t *l ) { + byte *buf, *buf_p; + int len; + byte image[LIGHTMAP_SIZE*LIGHTMAP_SIZE*4]; + int i, j; + float maxIntensity = 0; + double sumIntensity = 0; + + len = l->filelen; + if ( !len ) { + return; + } + buf = fileBase + l->fileofs; + + // we are about to upload textures + R_IssuePendingRenderCommands(); + + // create all the lightmaps + tr.numLightmaps = len / (LIGHTMAP_SIZE * LIGHTMAP_SIZE * 3); + if ( tr.numLightmaps == 1 ) { + //FIXME: HACK: maps with only one lightmap turn up fullbright for some reason. + //this avoids this, but isn't the correct solution. + tr.numLightmaps++; + } + + // if we are in r_vertexLight mode, we don't need the lightmaps at all + if ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) { + return; + } + + tr.lightmaps = ri.Hunk_Alloc( tr.numLightmaps * sizeof(image_t *), h_low ); + for ( i = 0 ; i < tr.numLightmaps ; i++ ) { + // expand the 24 bit on-disk to 32 bit + buf_p = buf + i * LIGHTMAP_SIZE*LIGHTMAP_SIZE * 3; + + if ( r_lightmap->integer == 2 ) + { // color code by intensity as development tool (FIXME: check range) + for ( j = 0; j < LIGHTMAP_SIZE * LIGHTMAP_SIZE; j++ ) + { + float r = buf_p[j*3+0]; + float g = buf_p[j*3+1]; + float b = buf_p[j*3+2]; + float intensity; + float out[3] = {0.0, 0.0, 0.0}; + + intensity = 0.33f * r + 0.685f * g + 0.063f * b; + + if ( intensity > 255 ) + intensity = 1.0f; + else + intensity /= 255.0f; + + if ( intensity > maxIntensity ) + maxIntensity = intensity; + + HSVtoRGB( intensity, 1.00, 0.50, out ); + + image[j*4+0] = out[0] * 255; + image[j*4+1] = out[1] * 255; + image[j*4+2] = out[2] * 255; + image[j*4+3] = 255; + + sumIntensity += intensity; + } + } else { + for ( j = 0 ; j < LIGHTMAP_SIZE * LIGHTMAP_SIZE; j++ ) { + R_ColorShiftLightingBytes( &buf_p[j*3], &image[j*4] ); + image[j*4+3] = 255; + } + } + tr.lightmaps[i] = R_CreateImage( va("*lightmap%d",i), image, + LIGHTMAP_SIZE, LIGHTMAP_SIZE, qfalse, qfalse, GL_CLAMP_TO_EDGE ); + } + + if ( r_lightmap->integer == 2 ) { + ri.Printf( PRINT_ALL, "Brightest lightmap value: %d\n", ( int ) ( maxIntensity * 255 ) ); + } +} + + +/* +================= +RE_SetWorldVisData + +This is called by the clipmodel subsystem so we can share the 1.8 megs of +space in big maps... +================= +*/ +void RE_SetWorldVisData( const byte *vis ) { + tr.externalVisData = vis; +} + + +/* +================= +R_LoadVisibility +================= +*/ +static void R_LoadVisibility( lump_t *l ) { + int len; + byte *buf; + + len = ( s_worldData.numClusters + 63 ) & ~63; + s_worldData.novis = ri.Hunk_Alloc( len, h_low ); + Com_Memset( s_worldData.novis, 0xff, len ); + + len = l->filelen; + if ( !len ) { + return; + } + buf = fileBase + l->fileofs; + + s_worldData.numClusters = LittleLong( ((int *)buf)[0] ); + s_worldData.clusterBytes = LittleLong( ((int *)buf)[1] ); + + // CM_Load should have given us the vis data to share, so + // we don't need to allocate another copy + if ( tr.externalVisData ) { + s_worldData.vis = tr.externalVisData; + } else { + byte *dest; + + dest = ri.Hunk_Alloc( len - 8, h_low ); + Com_Memcpy( dest, buf + 8, len - 8 ); + s_worldData.vis = dest; + } +} + +//=============================================================================== + + +/* +=============== +ShaderForShaderNum +=============== +*/ +static shader_t *ShaderForShaderNum( int shaderNum, int lightmapNum ) { + shader_t *shader; + dshader_t *dsh; + + int _shaderNum = LittleLong( shaderNum ); + if ( _shaderNum < 0 || _shaderNum >= s_worldData.numShaders ) { + ri.Error( ERR_DROP, "ShaderForShaderNum: bad num %i", _shaderNum ); + } + dsh = &s_worldData.shaders[ _shaderNum ]; + + if ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) { + lightmapNum = LIGHTMAP_BY_VERTEX; + } + + if ( r_fullbright->integer ) { + lightmapNum = LIGHTMAP_WHITEIMAGE; + } + + shader = R_FindShader( dsh->shader, lightmapNum, qtrue ); + + // if the shader had errors, just use default shader + if ( shader->defaultShader ) { + return tr.defaultShader; + } + + return shader; +} + +/* +=============== +ParseFace +=============== +*/ +static void ParseFace( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int *indexes ) { + int i, j; + srfSurfaceFace_t *cv; + int numPoints, numIndexes; + int lightmapNum; + int sfaceSize, ofsIndexes; + + lightmapNum = LittleLong( ds->lightmapNum ); + + // get fog volume + surf->fogIndex = LittleLong( ds->fogNum ) + 1; + + // get shader value + surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapNum ); + if ( r_singleShader->integer && !surf->shader->isSky ) { + surf->shader = tr.defaultShader; + } + + numPoints = LittleLong( ds->numVerts ); + if (numPoints > MAX_FACE_POINTS) { + ri.Printf( PRINT_WARNING, "WARNING: MAX_FACE_POINTS exceeded: %i\n", numPoints); + numPoints = MAX_FACE_POINTS; + surf->shader = tr.defaultShader; + } + + numIndexes = LittleLong( ds->numIndexes ); + + // create the srfSurfaceFace_t + sfaceSize = ( size_t ) &((srfSurfaceFace_t *)0)->points[numPoints]; + ofsIndexes = sfaceSize; + sfaceSize += sizeof( int ) * numIndexes; + + cv = ri.Hunk_Alloc( sfaceSize, h_low ); + cv->surfaceType = SF_FACE; + cv->numPoints = numPoints; + cv->numIndices = numIndexes; + cv->ofsIndices = ofsIndexes; + + verts += LittleLong( ds->firstVert ); + for ( i = 0 ; i < numPoints ; i++ ) { + for ( j = 0 ; j < 3 ; j++ ) { + cv->points[i][j] = LittleFloat( verts[i].xyz[j] ); + } + for ( j = 0 ; j < 2 ; j++ ) { + cv->points[i][3+j] = LittleFloat( verts[i].st[j] ); + cv->points[i][5+j] = LittleFloat( verts[i].lightmap[j] ); + } + R_ColorShiftLightingBytes( verts[i].color, (byte *)&cv->points[i][7] ); + } + + indexes += LittleLong( ds->firstIndex ); + for ( i = 0 ; i < numIndexes ; i++ ) { + ((int *)((byte *)cv + cv->ofsIndices ))[i] = LittleLong( indexes[ i ] ); + } + + // take the plane information from the lightmap vector + for ( i = 0 ; i < 3 ; i++ ) { + cv->plane.normal[i] = LittleFloat( ds->lightmapVecs[2][i] ); + } + cv->plane.dist = DotProduct( cv->points[0], cv->plane.normal ); + SetPlaneSignbits( &cv->plane ); + cv->plane.type = PlaneTypeForNormal( cv->plane.normal ); + + surf->data = (surfaceType_t *)cv; +} + + +/* +=============== +ParseMesh +=============== +*/ +static void ParseMesh ( dsurface_t *ds, drawVert_t *verts, msurface_t *surf ) { + srfGridMesh_t *grid; + int i, j; + int width, height, numPoints; + drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE]; + int lightmapNum; + vec3_t bounds[2]; + vec3_t tmpVec; + static surfaceType_t skipData = SF_SKIP; + + lightmapNum = LittleLong( ds->lightmapNum ); + + // get fog volume + surf->fogIndex = LittleLong( ds->fogNum ) + 1; + + // get shader value + surf->shader = ShaderForShaderNum( ds->shaderNum, lightmapNum ); + if ( r_singleShader->integer && !surf->shader->isSky ) { + surf->shader = tr.defaultShader; + } + + // we may have a nodraw surface, because they might still need to + // be around for movement clipping + if ( s_worldData.shaders[ LittleLong( ds->shaderNum ) ].surfaceFlags & SURF_NODRAW ) { + surf->data = &skipData; + return; + } + + width = LittleLong( ds->patchWidth ); + height = LittleLong( ds->patchHeight ); + + verts += LittleLong( ds->firstVert ); + numPoints = width * height; + for ( i = 0 ; i < numPoints ; i++ ) { + for ( j = 0 ; j < 3 ; j++ ) { + points[i].xyz[j] = LittleFloat( verts[i].xyz[j] ); + points[i].normal[j] = LittleFloat( verts[i].normal[j] ); + } + for ( j = 0 ; j < 2 ; j++ ) { + points[i].st[j] = LittleFloat( verts[i].st[j] ); + points[i].lightmap[j] = LittleFloat( verts[i].lightmap[j] ); + } + R_ColorShiftLightingBytes( verts[i].color, points[i].color ); + } + + // pre-tesseleate + grid = R_SubdividePatchToGrid( width, height, points ); + surf->data = (surfaceType_t *)grid; + + // copy the level of detail origin, which is the center + // of the group of all curves that must subdivide the same + // to avoid cracking + for ( i = 0 ; i < 3 ; i++ ) { + bounds[0][i] = LittleFloat( ds->lightmapVecs[0][i] ); + bounds[1][i] = LittleFloat( ds->lightmapVecs[1][i] ); + } + VectorAdd( bounds[0], bounds[1], bounds[1] ); + VectorScale( bounds[1], 0.5f, grid->lodOrigin ); + VectorSubtract( bounds[0], grid->lodOrigin, tmpVec ); + grid->lodRadius = VectorLength( tmpVec ); +} + +/* +=============== +ParseTriSurf +=============== +*/ +static void ParseTriSurf( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int *indexes ) { + srfTriangles_t *tri; + int i, j; + int numVerts, numIndexes; + + // get fog volume + surf->fogIndex = LittleLong( ds->fogNum ) + 1; + + // get shader + surf->shader = ShaderForShaderNum( ds->shaderNum, LIGHTMAP_BY_VERTEX ); + if ( r_singleShader->integer && !surf->shader->isSky ) { + surf->shader = tr.defaultShader; + } + + numVerts = LittleLong( ds->numVerts ); + numIndexes = LittleLong( ds->numIndexes ); + + tri = ri.Hunk_Alloc( sizeof( *tri ) + numVerts * sizeof( tri->verts[0] ) + + numIndexes * sizeof( tri->indexes[0] ), h_low ); + tri->surfaceType = SF_TRIANGLES; + tri->numVerts = numVerts; + tri->numIndexes = numIndexes; + tri->verts = (drawVert_t *)(tri + 1); + tri->indexes = (int *)(tri->verts + tri->numVerts ); + + surf->data = (surfaceType_t *)tri; + + // copy vertexes + ClearBounds( tri->bounds[0], tri->bounds[1] ); + verts += LittleLong( ds->firstVert ); + for ( i = 0 ; i < numVerts ; i++ ) { + for ( j = 0 ; j < 3 ; j++ ) { + tri->verts[i].xyz[j] = LittleFloat( verts[i].xyz[j] ); + tri->verts[i].normal[j] = LittleFloat( verts[i].normal[j] ); + } + AddPointToBounds( tri->verts[i].xyz, tri->bounds[0], tri->bounds[1] ); + for ( j = 0 ; j < 2 ; j++ ) { + tri->verts[i].st[j] = LittleFloat( verts[i].st[j] ); + tri->verts[i].lightmap[j] = LittleFloat( verts[i].lightmap[j] ); + } + + R_ColorShiftLightingBytes( verts[i].color, tri->verts[i].color ); + } + + // copy indexes + indexes += LittleLong( ds->firstIndex ); + for ( i = 0 ; i < numIndexes ; i++ ) { + tri->indexes[i] = LittleLong( indexes[i] ); + if ( tri->indexes[i] < 0 || tri->indexes[i] >= numVerts ) { + ri.Error( ERR_DROP, "Bad index in triangle surface" ); + } + } +} + +/* +=============== +ParseFlare +=============== +*/ +static void ParseFlare( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int *indexes ) { + srfFlare_t *flare; + int i; + + // get fog volume + surf->fogIndex = LittleLong( ds->fogNum ) + 1; + + // get shader + surf->shader = ShaderForShaderNum( ds->shaderNum, LIGHTMAP_BY_VERTEX ); + if ( r_singleShader->integer && !surf->shader->isSky ) { + surf->shader = tr.defaultShader; + } + + flare = ri.Hunk_Alloc( sizeof( *flare ), h_low ); + flare->surfaceType = SF_FLARE; + + surf->data = (surfaceType_t *)flare; + + for ( i = 0 ; i < 3 ; i++ ) { + flare->origin[i] = LittleFloat( ds->lightmapOrigin[i] ); + flare->color[i] = LittleFloat( ds->lightmapVecs[0][i] ); + flare->normal[i] = LittleFloat( ds->lightmapVecs[2][i] ); + } +} + + +/* +================= +R_MergedWidthPoints + +returns true if there are grid points merged on a width edge +================= +*/ +int R_MergedWidthPoints(srfGridMesh_t *grid, int offset) { + int i, j; + + for (i = 1; i < grid->width-1; i++) { + for (j = i + 1; j < grid->width-1; j++) { + if ( fabs(grid->verts[i + offset].xyz[0] - grid->verts[j + offset].xyz[0]) > .1) continue; + if ( fabs(grid->verts[i + offset].xyz[1] - grid->verts[j + offset].xyz[1]) > .1) continue; + if ( fabs(grid->verts[i + offset].xyz[2] - grid->verts[j + offset].xyz[2]) > .1) continue; + return qtrue; + } + } + return qfalse; +} + +/* +================= +R_MergedHeightPoints + +returns true if there are grid points merged on a height edge +================= +*/ +int R_MergedHeightPoints(srfGridMesh_t *grid, int offset) { + int i, j; + + for (i = 1; i < grid->height-1; i++) { + for (j = i + 1; j < grid->height-1; j++) { + if ( fabs(grid->verts[grid->width * i + offset].xyz[0] - grid->verts[grid->width * j + offset].xyz[0]) > .1) continue; + if ( fabs(grid->verts[grid->width * i + offset].xyz[1] - grid->verts[grid->width * j + offset].xyz[1]) > .1) continue; + if ( fabs(grid->verts[grid->width * i + offset].xyz[2] - grid->verts[grid->width * j + offset].xyz[2]) > .1) continue; + return qtrue; + } + } + return qfalse; +} + +/* +================= +R_FixSharedVertexLodError_r + +NOTE: never sync LoD through grid edges with merged points! + +FIXME: write generalized version that also avoids cracks between a patch and one that meets half way? +================= +*/ +void R_FixSharedVertexLodError_r( int start, srfGridMesh_t *grid1 ) { + int j, k, l, m, n, offset1, offset2, touch; + srfGridMesh_t *grid2; + + for ( j = start; j < s_worldData.numsurfaces; j++ ) { + // + grid2 = (srfGridMesh_t *) s_worldData.surfaces[j].data; + // if this surface is not a grid + if ( grid2->surfaceType != SF_GRID ) continue; + // if the LOD errors are already fixed for this patch + if ( grid2->lodFixed == 2 ) continue; + // grids in the same LOD group should have the exact same lod radius + if ( grid1->lodRadius != grid2->lodRadius ) continue; + // grids in the same LOD group should have the exact same lod origin + if ( grid1->lodOrigin[0] != grid2->lodOrigin[0] ) continue; + if ( grid1->lodOrigin[1] != grid2->lodOrigin[1] ) continue; + if ( grid1->lodOrigin[2] != grid2->lodOrigin[2] ) continue; + // + touch = qfalse; + for (n = 0; n < 2; n++) { + // + if (n) offset1 = (grid1->height-1) * grid1->width; + else offset1 = 0; + if (R_MergedWidthPoints(grid1, offset1)) continue; + for (k = 1; k < grid1->width-1; k++) { + for (m = 0; m < 2; m++) { + + if (m) offset2 = (grid2->height-1) * grid2->width; + else offset2 = 0; + if (R_MergedWidthPoints(grid2, offset2)) continue; + for ( l = 1; l < grid2->width-1; l++) { + // + if ( fabs(grid1->verts[k + offset1].xyz[0] - grid2->verts[l + offset2].xyz[0]) > .1) continue; + if ( fabs(grid1->verts[k + offset1].xyz[1] - grid2->verts[l + offset2].xyz[1]) > .1) continue; + if ( fabs(grid1->verts[k + offset1].xyz[2] - grid2->verts[l + offset2].xyz[2]) > .1) continue; + // ok the points are equal and should have the same lod error + grid2->widthLodError[l] = grid1->widthLodError[k]; + touch = qtrue; + } + } + for (m = 0; m < 2; m++) { + + if (m) offset2 = grid2->width-1; + else offset2 = 0; + if (R_MergedHeightPoints(grid2, offset2)) continue; + for ( l = 1; l < grid2->height-1; l++) { + // + if ( fabs(grid1->verts[k + offset1].xyz[0] - grid2->verts[grid2->width * l + offset2].xyz[0]) > .1) continue; + if ( fabs(grid1->verts[k + offset1].xyz[1] - grid2->verts[grid2->width * l + offset2].xyz[1]) > .1) continue; + if ( fabs(grid1->verts[k + offset1].xyz[2] - grid2->verts[grid2->width * l + offset2].xyz[2]) > .1) continue; + // ok the points are equal and should have the same lod error + grid2->heightLodError[l] = grid1->widthLodError[k]; + touch = qtrue; + } + } + } + } + for (n = 0; n < 2; n++) { + // + if (n) offset1 = grid1->width-1; + else offset1 = 0; + if (R_MergedHeightPoints(grid1, offset1)) continue; + for (k = 1; k < grid1->height-1; k++) { + for (m = 0; m < 2; m++) { + + if (m) offset2 = (grid2->height-1) * grid2->width; + else offset2 = 0; + if (R_MergedWidthPoints(grid2, offset2)) continue; + for ( l = 1; l < grid2->width-1; l++) { + // + if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[0] - grid2->verts[l + offset2].xyz[0]) > .1) continue; + if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[1] - grid2->verts[l + offset2].xyz[1]) > .1) continue; + if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[2] - grid2->verts[l + offset2].xyz[2]) > .1) continue; + // ok the points are equal and should have the same lod error + grid2->widthLodError[l] = grid1->heightLodError[k]; + touch = qtrue; + } + } + for (m = 0; m < 2; m++) { + + if (m) offset2 = grid2->width-1; + else offset2 = 0; + if (R_MergedHeightPoints(grid2, offset2)) continue; + for ( l = 1; l < grid2->height-1; l++) { + // + if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[0] - grid2->verts[grid2->width * l + offset2].xyz[0]) > .1) continue; + if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[1] - grid2->verts[grid2->width * l + offset2].xyz[1]) > .1) continue; + if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[2] - grid2->verts[grid2->width * l + offset2].xyz[2]) > .1) continue; + // ok the points are equal and should have the same lod error + grid2->heightLodError[l] = grid1->heightLodError[k]; + touch = qtrue; + } + } + } + } + if (touch) { + grid2->lodFixed = 2; + R_FixSharedVertexLodError_r ( start, grid2 ); + //NOTE: this would be correct but makes things really slow + //grid2->lodFixed = 1; + } + } +} + +/* +================= +R_FixSharedVertexLodError + +This function assumes that all patches in one group are nicely stitched together for the highest LoD. +If this is not the case this function will still do its job but won't fix the highest LoD cracks. +================= +*/ +void R_FixSharedVertexLodError( void ) { + int i; + srfGridMesh_t *grid1; + + for ( i = 0; i < s_worldData.numsurfaces; i++ ) { + // + grid1 = (srfGridMesh_t *) s_worldData.surfaces[i].data; + // if this surface is not a grid + if ( grid1->surfaceType != SF_GRID ) + continue; + // + if ( grid1->lodFixed ) + continue; + // + grid1->lodFixed = 2; + // recursively fix other patches in the same LOD group + R_FixSharedVertexLodError_r( i + 1, grid1); + } +} + + +/* +=============== +R_StitchPatches +=============== +*/ +int R_StitchPatches( int grid1num, int grid2num ) { + float *v1, *v2; + srfGridMesh_t *grid1, *grid2; + int k, l, m, n, offset1, offset2, row, column; + + grid1 = (srfGridMesh_t *) s_worldData.surfaces[grid1num].data; + grid2 = (srfGridMesh_t *) s_worldData.surfaces[grid2num].data; + for (n = 0; n < 2; n++) { + // + if (n) offset1 = (grid1->height-1) * grid1->width; + else offset1 = 0; + if (R_MergedWidthPoints(grid1, offset1)) + continue; + for (k = 0; k < grid1->width-2; k += 2) { + + for (m = 0; m < 2; m++) { + + if ( grid2->width >= MAX_GRID_SIZE ) + break; + if (m) offset2 = (grid2->height-1) * grid2->width; + else offset2 = 0; + for ( l = 0; l < grid2->width-1; l++) { + // + v1 = grid1->verts[k + offset1].xyz; + v2 = grid2->verts[l + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + + v1 = grid1->verts[k + 2 + offset1].xyz; + v2 = grid2->verts[l + 1 + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + // + v1 = grid2->verts[l + offset2].xyz; + v2 = grid2->verts[l + 1 + offset2].xyz; + if ( fabs(v1[0] - v2[0]) < .01 && + fabs(v1[1] - v2[1]) < .01 && + fabs(v1[2] - v2[2]) < .01) + continue; + // + //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); + // insert column into grid2 right after after column l + if (m) row = grid2->height-1; + else row = 0; + grid2 = R_GridInsertColumn( grid2, l+1, row, + grid1->verts[k + 1 + offset1].xyz, grid1->widthLodError[k+1]); + grid2->lodStitched = qfalse; + s_worldData.surfaces[grid2num].data = (void *) grid2; + return qtrue; + } + } + for (m = 0; m < 2; m++) { + + if (grid2->height >= MAX_GRID_SIZE) + break; + if (m) offset2 = grid2->width-1; + else offset2 = 0; + for ( l = 0; l < grid2->height-1; l++) { + // + v1 = grid1->verts[k + offset1].xyz; + v2 = grid2->verts[grid2->width * l + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + + v1 = grid1->verts[k + 2 + offset1].xyz; + v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + // + v1 = grid2->verts[grid2->width * l + offset2].xyz; + v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) < .01 && + fabs(v1[1] - v2[1]) < .01 && + fabs(v1[2] - v2[2]) < .01) + continue; + // + //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); + // insert row into grid2 right after after row l + if (m) column = grid2->width-1; + else column = 0; + grid2 = R_GridInsertRow( grid2, l+1, column, + grid1->verts[k + 1 + offset1].xyz, grid1->widthLodError[k+1]); + grid2->lodStitched = qfalse; + s_worldData.surfaces[grid2num].data = (void *) grid2; + return qtrue; + } + } + } + } + for (n = 0; n < 2; n++) { + // + if (n) offset1 = grid1->width-1; + else offset1 = 0; + if (R_MergedHeightPoints(grid1, offset1)) + continue; + for (k = 0; k < grid1->height-2; k += 2) { + for (m = 0; m < 2; m++) { + + if ( grid2->width >= MAX_GRID_SIZE ) + break; + if (m) offset2 = (grid2->height-1) * grid2->width; + else offset2 = 0; + for ( l = 0; l < grid2->width-1; l++) { + // + v1 = grid1->verts[grid1->width * k + offset1].xyz; + v2 = grid2->verts[l + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + + v1 = grid1->verts[grid1->width * (k + 2) + offset1].xyz; + v2 = grid2->verts[l + 1 + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + // + v1 = grid2->verts[l + offset2].xyz; + v2 = grid2->verts[(l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) < .01 && + fabs(v1[1] - v2[1]) < .01 && + fabs(v1[2] - v2[2]) < .01) + continue; + // + //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); + // insert column into grid2 right after after column l + if (m) row = grid2->height-1; + else row = 0; + grid2 = R_GridInsertColumn( grid2, l+1, row, + grid1->verts[grid1->width * (k + 1) + offset1].xyz, grid1->heightLodError[k+1]); + grid2->lodStitched = qfalse; + s_worldData.surfaces[grid2num].data = (void *) grid2; + return qtrue; + } + } + for (m = 0; m < 2; m++) { + + if (grid2->height >= MAX_GRID_SIZE) + break; + if (m) offset2 = grid2->width-1; + else offset2 = 0; + for ( l = 0; l < grid2->height-1; l++) { + // + v1 = grid1->verts[grid1->width * k + offset1].xyz; + v2 = grid2->verts[grid2->width * l + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + + v1 = grid1->verts[grid1->width * (k + 2) + offset1].xyz; + v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + // + v1 = grid2->verts[grid2->width * l + offset2].xyz; + v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) < .01 && + fabs(v1[1] - v2[1]) < .01 && + fabs(v1[2] - v2[2]) < .01) + continue; + // + //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); + // insert row into grid2 right after after row l + if (m) column = grid2->width-1; + else column = 0; + grid2 = R_GridInsertRow( grid2, l+1, column, + grid1->verts[grid1->width * (k + 1) + offset1].xyz, grid1->heightLodError[k+1]); + grid2->lodStitched = qfalse; + s_worldData.surfaces[grid2num].data = (void *) grid2; + return qtrue; + } + } + } + } + for (n = 0; n < 2; n++) { + // + if (n) offset1 = (grid1->height-1) * grid1->width; + else offset1 = 0; + if (R_MergedWidthPoints(grid1, offset1)) + continue; + for (k = grid1->width-1; k > 1; k -= 2) { + + for (m = 0; m < 2; m++) { + + if ( grid2->width >= MAX_GRID_SIZE ) + break; + if (m) offset2 = (grid2->height-1) * grid2->width; + else offset2 = 0; + for ( l = 0; l < grid2->width-1; l++) { + // + v1 = grid1->verts[k + offset1].xyz; + v2 = grid2->verts[l + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + + v1 = grid1->verts[k - 2 + offset1].xyz; + v2 = grid2->verts[l + 1 + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + // + v1 = grid2->verts[l + offset2].xyz; + v2 = grid2->verts[(l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) < .01 && + fabs(v1[1] - v2[1]) < .01 && + fabs(v1[2] - v2[2]) < .01) + continue; + // + //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); + // insert column into grid2 right after after column l + if (m) row = grid2->height-1; + else row = 0; + grid2 = R_GridInsertColumn( grid2, l+1, row, + grid1->verts[k - 1 + offset1].xyz, grid1->widthLodError[k+1]); + grid2->lodStitched = qfalse; + s_worldData.surfaces[grid2num].data = (void *) grid2; + return qtrue; + } + } + for (m = 0; m < 2; m++) { + + if (grid2->height >= MAX_GRID_SIZE) + break; + if (m) offset2 = grid2->width-1; + else offset2 = 0; + for ( l = 0; l < grid2->height-1; l++) { + // + v1 = grid1->verts[k + offset1].xyz; + v2 = grid2->verts[grid2->width * l + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + + v1 = grid1->verts[k - 2 + offset1].xyz; + v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + // + v1 = grid2->verts[grid2->width * l + offset2].xyz; + v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) < .01 && + fabs(v1[1] - v2[1]) < .01 && + fabs(v1[2] - v2[2]) < .01) + continue; + // + //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); + // insert row into grid2 right after after row l + if (m) column = grid2->width-1; + else column = 0; + grid2 = R_GridInsertRow( grid2, l+1, column, + grid1->verts[k - 1 + offset1].xyz, grid1->widthLodError[k+1]); + if (!grid2) + break; + grid2->lodStitched = qfalse; + s_worldData.surfaces[grid2num].data = (void *) grid2; + return qtrue; + } + } + } + } + for (n = 0; n < 2; n++) { + // + if (n) offset1 = grid1->width-1; + else offset1 = 0; + if (R_MergedHeightPoints(grid1, offset1)) + continue; + for (k = grid1->height-1; k > 1; k -= 2) { + for (m = 0; m < 2; m++) { + + if ( grid2->width >= MAX_GRID_SIZE ) + break; + if (m) offset2 = (grid2->height-1) * grid2->width; + else offset2 = 0; + for ( l = 0; l < grid2->width-1; l++) { + // + v1 = grid1->verts[grid1->width * k + offset1].xyz; + v2 = grid2->verts[l + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + + v1 = grid1->verts[grid1->width * (k - 2) + offset1].xyz; + v2 = grid2->verts[l + 1 + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + // + v1 = grid2->verts[l + offset2].xyz; + v2 = grid2->verts[(l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) < .01 && + fabs(v1[1] - v2[1]) < .01 && + fabs(v1[2] - v2[2]) < .01) + continue; + // + //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); + // insert column into grid2 right after after column l + if (m) row = grid2->height-1; + else row = 0; + grid2 = R_GridInsertColumn( grid2, l+1, row, + grid1->verts[grid1->width * (k - 1) + offset1].xyz, grid1->heightLodError[k+1]); + grid2->lodStitched = qfalse; + s_worldData.surfaces[grid2num].data = (void *) grid2; + return qtrue; + } + } + for (m = 0; m < 2; m++) { + + if (grid2->height >= MAX_GRID_SIZE) + break; + if (m) offset2 = grid2->width-1; + else offset2 = 0; + for ( l = 0; l < grid2->height-1; l++) { + // + v1 = grid1->verts[grid1->width * k + offset1].xyz; + v2 = grid2->verts[grid2->width * l + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + + v1 = grid1->verts[grid1->width * (k - 2) + offset1].xyz; + v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + // + v1 = grid2->verts[grid2->width * l + offset2].xyz; + v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) < .01 && + fabs(v1[1] - v2[1]) < .01 && + fabs(v1[2] - v2[2]) < .01) + continue; + // + //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); + // insert row into grid2 right after after row l + if (m) column = grid2->width-1; + else column = 0; + grid2 = R_GridInsertRow( grid2, l+1, column, + grid1->verts[grid1->width * (k - 1) + offset1].xyz, grid1->heightLodError[k+1]); + grid2->lodStitched = qfalse; + s_worldData.surfaces[grid2num].data = (void *) grid2; + return qtrue; + } + } + } + } + return qfalse; +} + +/* +=============== +R_TryStitchPatch + +This function will try to stitch patches in the same LoD group together for the highest LoD. + +Only single missing vertice cracks will be fixed. + +Vertices will be joined at the patch side a crack is first found, at the other side +of the patch (on the same row or column) the vertices will not be joined and cracks +might still appear at that side. +=============== +*/ +int R_TryStitchingPatch( int grid1num ) { + int j, numstitches; + srfGridMesh_t *grid1, *grid2; + + numstitches = 0; + grid1 = (srfGridMesh_t *) s_worldData.surfaces[grid1num].data; + for ( j = 0; j < s_worldData.numsurfaces; j++ ) { + // + grid2 = (srfGridMesh_t *) s_worldData.surfaces[j].data; + // if this surface is not a grid + if ( grid2->surfaceType != SF_GRID ) continue; + // grids in the same LOD group should have the exact same lod radius + if ( grid1->lodRadius != grid2->lodRadius ) continue; + // grids in the same LOD group should have the exact same lod origin + if ( grid1->lodOrigin[0] != grid2->lodOrigin[0] ) continue; + if ( grid1->lodOrigin[1] != grid2->lodOrigin[1] ) continue; + if ( grid1->lodOrigin[2] != grid2->lodOrigin[2] ) continue; + // + while (R_StitchPatches(grid1num, j)) + { + numstitches++; + } + } + return numstitches; +} + +/* +=============== +R_StitchAllPatches +=============== +*/ +void R_StitchAllPatches( void ) { + int i, stitched, numstitches; + srfGridMesh_t *grid1; + + numstitches = 0; + do + { + stitched = qfalse; + for ( i = 0; i < s_worldData.numsurfaces; i++ ) { + // + grid1 = (srfGridMesh_t *) s_worldData.surfaces[i].data; + // if this surface is not a grid + if ( grid1->surfaceType != SF_GRID ) + continue; + // + if ( grid1->lodStitched ) + continue; + // + grid1->lodStitched = qtrue; + stitched = qtrue; + // + numstitches += R_TryStitchingPatch( i ); + } + } + while (stitched); + ri.Printf( PRINT_ALL, "stitched %d LoD cracks\n", numstitches ); +} + +/* +=============== +R_MovePatchSurfacesToHunk +=============== +*/ +void R_MovePatchSurfacesToHunk(void) { + int i, size; + srfGridMesh_t *grid, *hunkgrid; + + for ( i = 0; i < s_worldData.numsurfaces; i++ ) { + // + grid = (srfGridMesh_t *) s_worldData.surfaces[i].data; + // if this surface is not a grid + if ( grid->surfaceType != SF_GRID ) + continue; + // + size = (grid->width * grid->height - 1) * sizeof( drawVert_t ) + sizeof( *grid ); + hunkgrid = ri.Hunk_Alloc( size, h_low ); + Com_Memcpy(hunkgrid, grid, size); + + hunkgrid->widthLodError = ri.Hunk_Alloc( grid->width * 4, h_low ); + Com_Memcpy( hunkgrid->widthLodError, grid->widthLodError, grid->width * 4 ); + + hunkgrid->heightLodError = ri.Hunk_Alloc( grid->height * 4, h_low ); + Com_Memcpy( hunkgrid->heightLodError, grid->heightLodError, grid->height * 4 ); + + R_FreeSurfaceGridMesh( grid ); + + s_worldData.surfaces[i].data = (void *) hunkgrid; + } +} + +/* +=============== +R_LoadSurfaces +=============== +*/ +static void R_LoadSurfaces( lump_t *surfs, lump_t *verts, lump_t *indexLump ) { + dsurface_t *in; + msurface_t *out; + drawVert_t *dv; + int *indexes; + int count; + int numFaces, numMeshes, numTriSurfs, numFlares; + int i; + + numFaces = 0; + numMeshes = 0; + numTriSurfs = 0; + numFlares = 0; + + in = (void *)(fileBase + surfs->fileofs); + if (surfs->filelen % sizeof(*in)) + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + count = surfs->filelen / sizeof(*in); + + dv = (void *)(fileBase + verts->fileofs); + if (verts->filelen % sizeof(*dv)) + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + + indexes = (void *)(fileBase + indexLump->fileofs); + if ( indexLump->filelen % sizeof(*indexes)) + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + + out = ri.Hunk_Alloc ( count * sizeof(*out), h_low ); + + s_worldData.surfaces = out; + s_worldData.numsurfaces = count; + + for ( i = 0 ; i < count ; i++, in++, out++ ) { + switch ( LittleLong( in->surfaceType ) ) { + case MST_PATCH: + ParseMesh ( in, dv, out ); + numMeshes++; + break; + case MST_TRIANGLE_SOUP: + ParseTriSurf( in, dv, out, indexes ); + numTriSurfs++; + break; + case MST_PLANAR: + ParseFace( in, dv, out, indexes ); + numFaces++; + break; + case MST_FLARE: + ParseFlare( in, dv, out, indexes ); + numFlares++; + break; + default: + ri.Error( ERR_DROP, "Bad surfaceType" ); + } + } + +#ifdef PATCH_STITCHING + R_StitchAllPatches(); +#endif + + R_FixSharedVertexLodError(); + +#ifdef PATCH_STITCHING + R_MovePatchSurfacesToHunk(); +#endif + + ri.Printf( PRINT_ALL, "...loaded %d faces, %i meshes, %i trisurfs, %i flares\n", + numFaces, numMeshes, numTriSurfs, numFlares ); +} + + + +/* +================= +R_LoadSubmodels +================= +*/ +static void R_LoadSubmodels( lump_t *l ) { + dmodel_t *in; + bmodel_t *out; + int i, j, count; + + in = (void *)(fileBase + l->fileofs); + if (l->filelen % sizeof(*in)) + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + count = l->filelen / sizeof(*in); + + s_worldData.bmodels = out = ri.Hunk_Alloc( count * sizeof(*out), h_low ); + + for ( i=0 ; itype = MOD_BRUSH; + model->bmodel = out; + Com_sprintf( model->name, sizeof( model->name ), "*%d", i ); + + for (j=0 ; j<3 ; j++) { + out->bounds[0][j] = LittleFloat (in->mins[j]); + out->bounds[1][j] = LittleFloat (in->maxs[j]); + } + + out->firstSurface = s_worldData.surfaces + LittleLong( in->firstSurface ); + out->numSurfaces = LittleLong( in->numSurfaces ); + } +} + + + +//================================================================== + +/* +================= +R_SetParent +================= +*/ +static void R_SetParent (mnode_t *node, mnode_t *parent) +{ + node->parent = parent; + if (node->contents != -1) + return; + R_SetParent (node->children[0], node); + R_SetParent (node->children[1], node); +} + +/* +================= +R_LoadNodesAndLeafs +================= +*/ +static void R_LoadNodesAndLeafs (lump_t *nodeLump, lump_t *leafLump) { + int i, j, p; + dnode_t *in; + dleaf_t *inLeaf; + mnode_t *out; + int numNodes, numLeafs; + + in = (void *)(fileBase + nodeLump->fileofs); + if (nodeLump->filelen % sizeof(dnode_t) || + leafLump->filelen % sizeof(dleaf_t) ) { + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + } + numNodes = nodeLump->filelen / sizeof(dnode_t); + numLeafs = leafLump->filelen / sizeof(dleaf_t); + + out = ri.Hunk_Alloc ( (numNodes + numLeafs) * sizeof(*out), h_low); + + s_worldData.nodes = out; + s_worldData.numnodes = numNodes + numLeafs; + s_worldData.numDecisionNodes = numNodes; + + // load nodes + for ( i=0 ; imins[j] = LittleLong (in->mins[j]); + out->maxs[j] = LittleLong (in->maxs[j]); + } + + p = LittleLong(in->planeNum); + out->plane = s_worldData.planes + p; + + out->contents = CONTENTS_NODE; // differentiate from leafs + + for (j=0 ; j<2 ; j++) + { + p = LittleLong (in->children[j]); + if (p >= 0) + out->children[j] = s_worldData.nodes + p; + else + out->children[j] = s_worldData.nodes + numNodes + (-1 - p); + } + } + + // load leafs + inLeaf = (void *)(fileBase + leafLump->fileofs); + for ( i=0 ; imins[j] = LittleLong (inLeaf->mins[j]); + out->maxs[j] = LittleLong (inLeaf->maxs[j]); + } + + out->cluster = LittleLong(inLeaf->cluster); + out->area = LittleLong(inLeaf->area); + + if ( out->cluster >= s_worldData.numClusters ) { + s_worldData.numClusters = out->cluster + 1; + } + + out->firstmarksurface = s_worldData.marksurfaces + + LittleLong(inLeaf->firstLeafSurface); + out->nummarksurfaces = LittleLong(inLeaf->numLeafSurfaces); + } + + // chain decendants + R_SetParent (s_worldData.nodes, NULL); +} + +//============================================================================= + +/* +================= +R_LoadShaders +================= +*/ +static void R_LoadShaders( lump_t *l ) { + int i, count; + dshader_t *in, *out; + + in = (void *)(fileBase + l->fileofs); + if (l->filelen % sizeof(*in)) + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + count = l->filelen / sizeof(*in); + out = ri.Hunk_Alloc ( count*sizeof(*out), h_low ); + + s_worldData.shaders = out; + s_worldData.numShaders = count; + + Com_Memcpy( out, in, count*sizeof(*out) ); + + for ( i=0 ; ifileofs); + if (l->filelen % sizeof(*in)) + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + count = l->filelen / sizeof(*in); + out = ri.Hunk_Alloc ( count*sizeof(*out), h_low); + + s_worldData.marksurfaces = out; + s_worldData.nummarksurfaces = count; + + for ( i=0 ; ifileofs); + if (l->filelen % sizeof(*in)) + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + count = l->filelen / sizeof(*in); + out = ri.Hunk_Alloc ( count*2*sizeof(*out), h_low); + + s_worldData.planes = out; + s_worldData.numplanes = count; + + for ( i=0 ; inormal[j] = LittleFloat (in->normal[j]); + if (out->normal[j] < 0) { + bits |= 1<dist = LittleFloat (in->dist); + out->type = PlaneTypeForNormal( out->normal ); + out->signbits = bits; + } +} + +/* +================= +R_LoadFogs + +================= +*/ +static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) { + int i; + fog_t *out; + dfog_t *fogs; + dbrush_t *brushes, *brush; + dbrushside_t *sides; + int count, brushesCount, sidesCount; + int sideNum; + int planeNum; + shader_t *shader; + float d; + int firstSide; + + fogs = (void *)(fileBase + l->fileofs); + if (l->filelen % sizeof(*fogs)) { + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + } + count = l->filelen / sizeof(*fogs); + + // create fog strucutres for them + s_worldData.numfogs = count + 1; + s_worldData.fogs = ri.Hunk_Alloc ( s_worldData.numfogs*sizeof(*out), h_low); + out = s_worldData.fogs + 1; + + if ( !count ) { + return; + } + + brushes = (void *)(fileBase + brushesLump->fileofs); + if (brushesLump->filelen % sizeof(*brushes)) { + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + } + brushesCount = brushesLump->filelen / sizeof(*brushes); + + sides = (void *)(fileBase + sidesLump->fileofs); + if (sidesLump->filelen % sizeof(*sides)) { + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + } + sidesCount = sidesLump->filelen / sizeof(*sides); + + for ( i=0 ; ioriginalBrushNumber = LittleLong( fogs->brushNum ); + + if ( (unsigned)out->originalBrushNumber >= brushesCount ) { + ri.Error( ERR_DROP, "fog brushNumber out of range" ); + } + brush = brushes + out->originalBrushNumber; + + firstSide = LittleLong( brush->firstSide ); + + if ( (unsigned)firstSide > sidesCount - 6 ) { + ri.Error( ERR_DROP, "fog brush sideNumber out of range" ); + } + + // brushes are always sorted with the axial sides first + sideNum = firstSide + 0; + planeNum = LittleLong( sides[ sideNum ].planeNum ); + out->bounds[0][0] = -s_worldData.planes[ planeNum ].dist; + + sideNum = firstSide + 1; + planeNum = LittleLong( sides[ sideNum ].planeNum ); + out->bounds[1][0] = s_worldData.planes[ planeNum ].dist; + + sideNum = firstSide + 2; + planeNum = LittleLong( sides[ sideNum ].planeNum ); + out->bounds[0][1] = -s_worldData.planes[ planeNum ].dist; + + sideNum = firstSide + 3; + planeNum = LittleLong( sides[ sideNum ].planeNum ); + out->bounds[1][1] = s_worldData.planes[ planeNum ].dist; + + sideNum = firstSide + 4; + planeNum = LittleLong( sides[ sideNum ].planeNum ); + out->bounds[0][2] = -s_worldData.planes[ planeNum ].dist; + + sideNum = firstSide + 5; + planeNum = LittleLong( sides[ sideNum ].planeNum ); + out->bounds[1][2] = s_worldData.planes[ planeNum ].dist; + + // get information from the shader for fog parameters + shader = R_FindShader( fogs->shader, LIGHTMAP_NONE, qtrue ); + + out->parms = shader->fogParms; + + out->colorInt = ColorBytes4 ( shader->fogParms.color[0] * tr.identityLight, + shader->fogParms.color[1] * tr.identityLight, + shader->fogParms.color[2] * tr.identityLight, 1.0 ); + + d = shader->fogParms.depthForOpaque < 1 ? 1 : shader->fogParms.depthForOpaque; + out->tcScale = 1.0f / ( d * 8 ); + + // set the gradient vector + sideNum = LittleLong( fogs->visibleSide ); + + if ( sideNum == -1 ) { + out->hasSurface = qfalse; + } else { + out->hasSurface = qtrue; + planeNum = LittleLong( sides[ firstSide + sideNum ].planeNum ); + VectorSubtract( vec3_origin, s_worldData.planes[ planeNum ].normal, out->surface ); + out->surface[3] = -s_worldData.planes[ planeNum ].dist; + } + + out++; + } + +} + + +/* +================ +R_LoadLightGrid + +================ +*/ +void R_LoadLightGrid( lump_t *l ) { + int i; + vec3_t maxs; + int numGridPoints; + world_t *w; + float *wMins, *wMaxs; + + w = &s_worldData; + + w->lightGridInverseSize[0] = 1.0f / w->lightGridSize[0]; + w->lightGridInverseSize[1] = 1.0f / w->lightGridSize[1]; + w->lightGridInverseSize[2] = 1.0f / w->lightGridSize[2]; + + wMins = w->bmodels[0].bounds[0]; + wMaxs = w->bmodels[0].bounds[1]; + + for ( i = 0 ; i < 3 ; i++ ) { + w->lightGridOrigin[i] = w->lightGridSize[i] * ceil( wMins[i] / w->lightGridSize[i] ); + maxs[i] = w->lightGridSize[i] * floor( wMaxs[i] / w->lightGridSize[i] ); + w->lightGridBounds[i] = (maxs[i] - w->lightGridOrigin[i])/w->lightGridSize[i] + 1; + } + + numGridPoints = w->lightGridBounds[0] * w->lightGridBounds[1] * w->lightGridBounds[2]; + + if ( l->filelen != numGridPoints * 8 ) { + ri.Printf( PRINT_WARNING, "WARNING: light grid mismatch\n" ); + w->lightGridData = NULL; + return; + } + + w->lightGridData = ri.Hunk_Alloc( l->filelen, h_low ); + Com_Memcpy( w->lightGridData, (void *)(fileBase + l->fileofs), l->filelen ); + + // deal with overbright bits + for ( i = 0 ; i < numGridPoints ; i++ ) { + R_ColorShiftLightingBytes( &w->lightGridData[i*8], &w->lightGridData[i*8] ); + R_ColorShiftLightingBytes( &w->lightGridData[i*8+3], &w->lightGridData[i*8+3] ); + } +} + +/* +================ +R_LoadEntities +================ +*/ +void R_LoadEntities( lump_t *l ) { + char *p, *token, *s; + char keyname[MAX_TOKEN_CHARS]; + char value[MAX_TOKEN_CHARS]; + world_t *w; + + w = &s_worldData; + w->lightGridSize[0] = 64; + w->lightGridSize[1] = 64; + w->lightGridSize[2] = 128; + + p = (char *)(fileBase + l->fileofs); + + // store for reference by the cgame + w->entityString = ri.Hunk_Alloc( l->filelen + 1, h_low ); + strcpy( w->entityString, p ); + w->entityParsePoint = w->entityString; + + token = COM_ParseExt( &p, qtrue ); + if (!*token || *token != '{') { + return; + } + + // only parse the world spawn + while ( 1 ) { + // parse key + token = COM_ParseExt( &p, qtrue ); + + if ( !*token || *token == '}' ) { + break; + } + Q_strncpyz(keyname, token, sizeof(keyname)); + + // parse value + token = COM_ParseExt( &p, qtrue ); + + if ( !*token || *token == '}' ) { + break; + } + Q_strncpyz(value, token, sizeof(value)); + + // check for remapping of shaders for vertex lighting + s = "vertexremapshader"; + if (!Q_strncmp(keyname, s, strlen(s)) ) { + s = strchr(value, ';'); + if (!s) { + ri.Printf( PRINT_WARNING, "WARNING: no semi colon in vertexshaderremap '%s'\n", value ); + break; + } + *s++ = 0; + if (r_vertexLight->integer) { + R_RemapShader(value, s, "0"); + } + continue; + } + // check for remapping of shaders + s = "remapshader"; + if (!Q_strncmp(keyname, s, strlen(s)) ) { + s = strchr(value, ';'); + if (!s) { + ri.Printf( PRINT_WARNING, "WARNING: no semi colon in shaderremap '%s'\n", value ); + break; + } + *s++ = 0; + R_RemapShader(value, s, "0"); + continue; + } + // check for a different grid size + if (!Q_stricmp(keyname, "gridsize")) { + sscanf(value, "%f %f %f", &w->lightGridSize[0], &w->lightGridSize[1], &w->lightGridSize[2] ); + continue; + } + } +} + +/* +================= +R_GetEntityToken +================= +*/ +qboolean R_GetEntityToken( char *buffer, int size ) { + const char *s; + + s = COM_Parse( &s_worldData.entityParsePoint ); + Q_strncpyz( buffer, s, size ); + if ( !s_worldData.entityParsePoint || !s[0] ) { + s_worldData.entityParsePoint = s_worldData.entityString; + return qfalse; + } else { + return qtrue; + } +} + +/* +================= +RE_LoadWorldMap + +Called directly from cgame +================= +*/ +void RE_LoadWorldMap( const char *name ) { + int i; + dheader_t *header; + union { + byte *b; + void *v; + } buffer; + byte *startMarker; + + if ( tr.worldMapLoaded ) { + ri.Error( ERR_DROP, "ERROR: attempted to redundantly load world map" ); + } + + // set default sun direction to be used if it isn't + // overridden by a shader + tr.sunDirection[0] = 0.45f; + tr.sunDirection[1] = 0.3f; + tr.sunDirection[2] = 0.9f; + + VectorNormalize( tr.sunDirection ); + + tr.worldMapLoaded = qtrue; + + // load it + ri.FS_ReadFile( name, &buffer.v ); + if ( !buffer.b ) { + ri.Error (ERR_DROP, "RE_LoadWorldMap: %s not found", name); + } + + // clear tr.world so if the level fails to load, the next + // try will not look at the partially loaded version + tr.world = NULL; + + Com_Memset( &s_worldData, 0, sizeof( s_worldData ) ); + Q_strncpyz( s_worldData.name, name, sizeof( s_worldData.name ) ); + + Q_strncpyz( s_worldData.baseName, COM_SkipPath( s_worldData.name ), sizeof( s_worldData.name ) ); + COM_StripExtension(s_worldData.baseName, s_worldData.baseName, sizeof(s_worldData.baseName)); + + startMarker = ri.Hunk_Alloc(0, h_low); + c_gridVerts = 0; + + header = (dheader_t *)buffer.b; + fileBase = (byte *)header; + + i = LittleLong (header->version); + if ( i != BSP_VERSION ) { + ri.Error (ERR_DROP, "RE_LoadWorldMap: %s has wrong version number (%i should be %i)", + name, i, BSP_VERSION); + } + + // swap all the lumps + for (i=0 ; ilumps[LUMP_SHADERS] ); + R_LoadLightmaps( &header->lumps[LUMP_LIGHTMAPS] ); + R_LoadPlanes (&header->lumps[LUMP_PLANES]); + R_LoadFogs( &header->lumps[LUMP_FOGS], &header->lumps[LUMP_BRUSHES], &header->lumps[LUMP_BRUSHSIDES] ); + R_LoadSurfaces( &header->lumps[LUMP_SURFACES], &header->lumps[LUMP_DRAWVERTS], &header->lumps[LUMP_DRAWINDEXES] ); + R_LoadMarksurfaces (&header->lumps[LUMP_LEAFSURFACES]); + R_LoadNodesAndLeafs (&header->lumps[LUMP_NODES], &header->lumps[LUMP_LEAFS]); + R_LoadSubmodels (&header->lumps[LUMP_MODELS]); + R_LoadVisibility( &header->lumps[LUMP_VISIBILITY] ); + R_LoadEntities( &header->lumps[LUMP_ENTITIES] ); + R_LoadLightGrid( &header->lumps[LUMP_LIGHTGRID] ); + + s_worldData.dataSize = (byte *)ri.Hunk_Alloc(0, h_low) - startMarker; + + // only set tr.world now that we know the entire level has loaded properly + tr.world = &s_worldData; + + ri.FS_FreeFile( buffer.v ); +} + diff --git a/src/renderergl1/tr_cmds.c b/src/renderergl1/tr_cmds.c new file mode 100644 index 00000000..e4a2dcfe --- /dev/null +++ b/src/renderergl1/tr_cmds.c @@ -0,0 +1,591 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +#include "tr_local.h" + +volatile renderCommandList_t *renderCommandList; + +/* +===================== +R_PerformanceCounters +===================== +*/ +void R_PerformanceCounters( void ) { + if ( !r_speeds->integer ) { + // clear the counters even if we aren't printing + Com_Memset( &tr.pc, 0, sizeof( tr.pc ) ); + Com_Memset( &backEnd.pc, 0, sizeof( backEnd.pc ) ); + return; + } + + if (r_speeds->integer == 1) { + ri.Printf (PRINT_ALL, "%i/%i shaders/surfs %i leafs %i verts %i/%i tris %.2f mtex %.2f dc\n", + backEnd.pc.c_shaders, backEnd.pc.c_surfaces, tr.pc.c_leafs, backEnd.pc.c_vertexes, + backEnd.pc.c_indexes/3, backEnd.pc.c_totalIndexes/3, + R_SumOfUsedImages()/(1000000.0f), backEnd.pc.c_overDraw / (float)(glConfig.vidWidth * glConfig.vidHeight) ); + } else if (r_speeds->integer == 2) { + ri.Printf (PRINT_ALL, "(patch) %i sin %i sclip %i sout %i bin %i bclip %i bout\n", + tr.pc.c_sphere_cull_patch_in, tr.pc.c_sphere_cull_patch_clip, tr.pc.c_sphere_cull_patch_out, + tr.pc.c_box_cull_patch_in, tr.pc.c_box_cull_patch_clip, tr.pc.c_box_cull_patch_out ); + ri.Printf (PRINT_ALL, "(md3) %i sin %i sclip %i sout %i bin %i bclip %i bout\n", + tr.pc.c_sphere_cull_md3_in, tr.pc.c_sphere_cull_md3_clip, tr.pc.c_sphere_cull_md3_out, + tr.pc.c_box_cull_md3_in, tr.pc.c_box_cull_md3_clip, tr.pc.c_box_cull_md3_out ); + } else if (r_speeds->integer == 3) { + ri.Printf (PRINT_ALL, "viewcluster: %i\n", tr.viewCluster ); + } else if (r_speeds->integer == 4) { + if ( backEnd.pc.c_dlightVertexes ) { + ri.Printf (PRINT_ALL, "dlight srf:%i culled:%i verts:%i tris:%i\n", + tr.pc.c_dlightSurfaces, tr.pc.c_dlightSurfacesCulled, + backEnd.pc.c_dlightVertexes, backEnd.pc.c_dlightIndexes / 3 ); + } + } + else if (r_speeds->integer == 5 ) + { + ri.Printf( PRINT_ALL, "zFar: %.0f\n", tr.viewParms.zFar ); + } + else if (r_speeds->integer == 6 ) + { + ri.Printf( PRINT_ALL, "flare adds:%i tests:%i renders:%i\n", + backEnd.pc.c_flareAdds, backEnd.pc.c_flareTests, backEnd.pc.c_flareRenders ); + } + + Com_Memset( &tr.pc, 0, sizeof( tr.pc ) ); + Com_Memset( &backEnd.pc, 0, sizeof( backEnd.pc ) ); +} + + +/* +==================== +R_IssueRenderCommands +==================== +*/ +void R_IssueRenderCommands( qboolean runPerformanceCounters ) { + renderCommandList_t *cmdList; + + cmdList = &backEndData->commands; + assert(cmdList); + // add an end-of-list command + *(int *)(cmdList->cmds + cmdList->used) = RC_END_OF_LIST; + + // clear it out, in case this is a sync and not a buffer flip + cmdList->used = 0; + + if ( runPerformanceCounters ) { + R_PerformanceCounters(); + } + + // actually start the commands going + if ( !r_skipBackEnd->integer ) { + // let it start on the new batch + RB_ExecuteRenderCommands( cmdList->cmds ); + } +} + + +/* +==================== +R_IssuePendingRenderCommands + +Issue any pending commands and wait for them to complete. +==================== +*/ +void R_IssuePendingRenderCommands( void ) { + if ( !tr.registered ) { + return; + } + R_IssueRenderCommands( qfalse ); +} + +/* +============ +R_GetCommandBuffer + +make sure there is enough command space +============ +*/ +void *R_GetCommandBuffer( int bytes ) { + renderCommandList_t *cmdList; + + cmdList = &backEndData->commands; + bytes = PAD(bytes, sizeof(void *)); + + // always leave room for the end of list command + if ( cmdList->used + bytes + 4 > MAX_RENDER_COMMANDS ) { + if ( bytes > MAX_RENDER_COMMANDS - 4 ) { + ri.Error( ERR_FATAL, "R_GetCommandBuffer: bad size %i", bytes ); + } + // if we run out of room, just start dropping commands + return NULL; + } + + cmdList->used += bytes; + + return cmdList->cmds + cmdList->used - bytes; +} + + +/* +============= +R_AddDrawSurfCmd + +============= +*/ +void R_AddDrawSurfCmd( drawSurf_t *drawSurfs, int numDrawSurfs ) { + drawSurfsCommand_t *cmd; + + cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + if ( !cmd ) { + return; + } + cmd->commandId = RC_DRAW_SURFS; + + cmd->drawSurfs = drawSurfs; + cmd->numDrawSurfs = numDrawSurfs; + + cmd->refdef = tr.refdef; + cmd->viewParms = tr.viewParms; +} + + +/* +============= +RE_SetColor + +Passing NULL will set the color to white +============= +*/ +void RE_SetColor( const float *rgba ) { + setColorCommand_t *cmd; + + if ( !tr.registered ) { + return; + } + cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + if ( !cmd ) { + return; + } + cmd->commandId = RC_SET_COLOR; + if ( !rgba ) { + static float colorWhite[4] = { 1, 1, 1, 1 }; + + rgba = colorWhite; + } + + cmd->color[0] = rgba[0]; + cmd->color[1] = rgba[1]; + cmd->color[2] = rgba[2]; + cmd->color[3] = rgba[3]; +} + +/* +============= +R_ClipRegion +============= +*/ +static qboolean R_ClipRegion ( float *x, float *y, float *w, float *h, + float *s1, float *t1, float *s2, float *t2 ) { + float left, top, right, bottom; + float _s1, _t1, _s2, _t2; + float clipLeft, clipTop, clipRight, clipBottom; + + if (tr.clipRegion[2] <= tr.clipRegion[0] || + tr.clipRegion[3] <= tr.clipRegion[1] ) { + return qfalse; + } + + left = *x; + top = *y; + right = *x + *w; + bottom = *y + *h; + + _s1 = *s1; + _t1 = *t1; + _s2 = *s2; + _t2 = *t2; + + clipLeft = tr.clipRegion[0]; + clipTop = tr.clipRegion[1]; + clipRight = tr.clipRegion[2]; + clipBottom = tr.clipRegion[3]; + + // Completely clipped away + if ( right <= clipLeft || left >= clipRight || + bottom <= clipTop || top >= clipBottom ) { + return qtrue; + } + + // Clip left edge + if ( left < clipLeft ) { + float f = ( clipLeft - left ) / ( right - left ); + *s1 = ( f * ( _s2 - _s1 ) ) + _s1; + *x = clipLeft; + *w -= ( clipLeft - left ); + } + + // Clip right edge + if ( right > clipRight ) { + float f = ( clipRight - right ) / ( left - right ); + *s2 = ( f * ( _s1 - _s2 ) ) + _s2; + *w = clipRight - *x; + } + + // Clip top edge + if ( top < clipTop ) { + float f = ( clipTop - top ) / ( bottom - top ); + *t1 = ( f * ( _t2 - _t1 ) ) + _t1; + *y = clipTop; + *h -= ( clipTop - top ); + } + + // Clip bottom edge + if ( bottom > clipBottom ) { + float f = ( clipBottom - bottom ) / ( top - bottom ); + *t2 = ( f * ( _t1 - _t2 ) ) + _t2; + *h = clipBottom - *y; + } + + return qfalse; +} + +/* +============= +RE_SetClipRegion +============= +*/ +void RE_SetClipRegion( const float *region ) { + if ( region == NULL ) { + Com_Memset( tr.clipRegion, 0, sizeof( vec4_t ) ); + } else { + Vector4Copy( region, tr.clipRegion ); + } +} + +/* +============= +RE_StretchPic +============= +*/ +void RE_StretchPic ( float x, float y, float w, float h, + float s1, float t1, float s2, float t2, qhandle_t hShader ) { + stretchPicCommand_t *cmd; + + if (!tr.registered) { + return; + } + if (R_ClipRegion(&x, &y, &w, &h, &s1, &t1, &s2, &t2)) { + return; + } + cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + if ( !cmd ) { + return; + } + cmd->commandId = RC_STRETCH_PIC; + cmd->shader = R_GetShaderByHandle( hShader ); + cmd->x = x; + cmd->y = y; + cmd->w = w; + cmd->h = h; + cmd->s1 = s1; + cmd->t1 = t1; + cmd->s2 = s2; + cmd->t2 = t2; +} + +#define MODE_RED_CYAN 1 +#define MODE_RED_BLUE 2 +#define MODE_RED_GREEN 3 +#define MODE_GREEN_MAGENTA 4 +#define MODE_MAX MODE_GREEN_MAGENTA + +void R_SetColorMode(GLboolean *rgba, stereoFrame_t stereoFrame, int colormode) +{ + rgba[0] = rgba[1] = rgba[2] = rgba[3] = GL_TRUE; + + if(colormode > MODE_MAX) + { + if(stereoFrame == STEREO_LEFT) + stereoFrame = STEREO_RIGHT; + else if(stereoFrame == STEREO_RIGHT) + stereoFrame = STEREO_LEFT; + + colormode -= MODE_MAX; + } + + if(colormode == MODE_GREEN_MAGENTA) + { + if(stereoFrame == STEREO_LEFT) + rgba[0] = rgba[2] = GL_FALSE; + else if(stereoFrame == STEREO_RIGHT) + rgba[1] = GL_FALSE; + } + else + { + if(stereoFrame == STEREO_LEFT) + rgba[1] = rgba[2] = GL_FALSE; + else if(stereoFrame == STEREO_RIGHT) + { + rgba[0] = GL_FALSE; + + if(colormode == MODE_RED_BLUE) + rgba[1] = GL_FALSE; + else if(colormode == MODE_RED_GREEN) + rgba[2] = GL_FALSE; + } + } +} + + +/* +==================== +RE_BeginFrame + +If running in stereo, RE_BeginFrame will be called twice +for each RE_EndFrame +==================== +*/ +void RE_BeginFrame( stereoFrame_t stereoFrame ) { + drawBufferCommand_t *cmd = NULL; + colorMaskCommand_t *colcmd = NULL; + + if ( !tr.registered ) { + return; + } + glState.finishCalled = qfalse; + + tr.frameCount++; + tr.frameSceneNum = 0; + + // + // do overdraw measurement + // + if ( r_measureOverdraw->integer ) + { + if ( glConfig.stencilBits < 4 ) + { + ri.Printf( PRINT_ALL, "Warning: not enough stencil bits to measure overdraw: %d\n", glConfig.stencilBits ); + ri.Cvar_Set( "r_measureOverdraw", "0" ); + r_measureOverdraw->modified = qfalse; + } + else if ( r_shadows->integer == 2 ) + { + ri.Printf( PRINT_ALL, "Warning: stencil shadows and overdraw measurement are mutually exclusive\n" ); + ri.Cvar_Set( "r_measureOverdraw", "0" ); + r_measureOverdraw->modified = qfalse; + } + else + { + R_IssuePendingRenderCommands(); + qglEnable( GL_STENCIL_TEST ); + qglStencilMask( ~0U ); + qglClearStencil( 0U ); + qglStencilFunc( GL_ALWAYS, 0U, ~0U ); + qglStencilOp( GL_KEEP, GL_INCR, GL_INCR ); + } + r_measureOverdraw->modified = qfalse; + } + else + { + // this is only reached if it was on and is now off + if ( r_measureOverdraw->modified ) { + R_IssuePendingRenderCommands(); + qglDisable( GL_STENCIL_TEST ); + } + r_measureOverdraw->modified = qfalse; + } + + // + // texturemode stuff + // + if ( r_textureMode->modified ) { + R_IssuePendingRenderCommands(); + GL_TextureMode( r_textureMode->string ); + r_textureMode->modified = qfalse; + } + + // + // gamma stuff + // + if ( r_gamma->modified ) { + r_gamma->modified = qfalse; + + R_IssuePendingRenderCommands(); + R_SetColorMappings(); + } + + // check for errors + if ( !r_ignoreGLErrors->integer ) + { + int err; + + R_IssuePendingRenderCommands(); + if ((err = qglGetError()) != GL_NO_ERROR) + ri.Error(ERR_FATAL, "RE_BeginFrame() - glGetError() failed (0x%x)!", err); + } + + if (glConfig.stereoEnabled) { + if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) ) + return; + + cmd->commandId = RC_DRAW_BUFFER; + + if ( stereoFrame == STEREO_LEFT ) { + cmd->buffer = (int)GL_BACK_LEFT; + } else if ( stereoFrame == STEREO_RIGHT ) { + cmd->buffer = (int)GL_BACK_RIGHT; + } else { + ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame ); + } + } + else + { + if(r_anaglyphMode->integer) + { + if(r_anaglyphMode->modified) + { + // clear both, front and backbuffer. + qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + qglClearColor(0.0f, 0.0f, 0.0f, 1.0f); + + qglDrawBuffer(GL_FRONT); + qglClear(GL_COLOR_BUFFER_BIT); + qglDrawBuffer(GL_BACK); + qglClear(GL_COLOR_BUFFER_BIT); + + r_anaglyphMode->modified = qfalse; + } + + if(stereoFrame == STEREO_LEFT) + { + if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) ) + return; + + if( !(colcmd = R_GetCommandBuffer(sizeof(*colcmd))) ) + return; + } + else if(stereoFrame == STEREO_RIGHT) + { + clearDepthCommand_t *cldcmd; + + if( !(cldcmd = R_GetCommandBuffer(sizeof(*cldcmd))) ) + return; + + cldcmd->commandId = RC_CLEARDEPTH; + + if( !(colcmd = R_GetCommandBuffer(sizeof(*colcmd))) ) + return; + } + else + ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame ); + + R_SetColorMode(colcmd->rgba, stereoFrame, r_anaglyphMode->integer); + colcmd->commandId = RC_COLORMASK; + } + else + { + if(stereoFrame != STEREO_CENTER) + ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is disabled, but stereoFrame was %i", stereoFrame ); + + if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) ) + return; + } + + if(cmd) + { + cmd->commandId = RC_DRAW_BUFFER; + + if(r_anaglyphMode->modified) + { + qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + r_anaglyphMode->modified = qfalse; + } + + if (!Q_stricmp(r_drawBuffer->string, "GL_FRONT")) + cmd->buffer = (int)GL_FRONT; + else + cmd->buffer = (int)GL_BACK; + } + } + + tr.refdef.stereoFrame = stereoFrame; +} + + +/* +============= +RE_EndFrame + +Returns the number of msec spent in the back end +============= +*/ +void RE_EndFrame( int *frontEndMsec, int *backEndMsec ) { + swapBuffersCommand_t *cmd; + + if ( !tr.registered ) { + return; + } + cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + if ( !cmd ) { + return; + } + cmd->commandId = RC_SWAP_BUFFERS; + + R_IssueRenderCommands( qtrue ); + + R_InitNextFrame(); + + if ( frontEndMsec ) { + *frontEndMsec = tr.frontEndMsec; + } + tr.frontEndMsec = 0; + if ( backEndMsec ) { + *backEndMsec = backEnd.pc.msec; + } + backEnd.pc.msec = 0; +} + +/* +============= +RE_TakeVideoFrame +============= +*/ +void RE_TakeVideoFrame( int width, int height, + byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg ) +{ + videoFrameCommand_t *cmd; + + if( !tr.registered ) { + return; + } + + cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + if( !cmd ) { + return; + } + + cmd->commandId = RC_VIDEOFRAME; + + cmd->width = width; + cmd->height = height; + cmd->captureBuffer = captureBuffer; + cmd->encodeBuffer = encodeBuffer; + cmd->motionJpeg = motionJpeg; +} diff --git a/src/renderergl1/tr_curve.c b/src/renderergl1/tr_curve.c new file mode 100644 index 00000000..684329b1 --- /dev/null +++ b/src/renderergl1/tr_curve.c @@ -0,0 +1,626 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "tr_local.h" + +/* + +This file does all of the processing necessary to turn a raw grid of points +read from the map file into a srfGridMesh_t ready for rendering. + +The level of detail solution is direction independent, based only on subdivided +distance from the true curve. + +Only a single entry point: + +srfGridMesh_t *R_SubdividePatchToGrid( int width, int height, + drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) { + +*/ + + +/* +============ +LerpDrawVert +============ +*/ +static void LerpDrawVert( drawVert_t *a, drawVert_t *b, drawVert_t *out ) { + out->xyz[0] = 0.5f * (a->xyz[0] + b->xyz[0]); + out->xyz[1] = 0.5f * (a->xyz[1] + b->xyz[1]); + out->xyz[2] = 0.5f * (a->xyz[2] + b->xyz[2]); + + out->st[0] = 0.5f * (a->st[0] + b->st[0]); + out->st[1] = 0.5f * (a->st[1] + b->st[1]); + + out->lightmap[0] = 0.5f * (a->lightmap[0] + b->lightmap[0]); + out->lightmap[1] = 0.5f * (a->lightmap[1] + b->lightmap[1]); + + out->color[0] = (a->color[0] + b->color[0]) >> 1; + out->color[1] = (a->color[1] + b->color[1]) >> 1; + out->color[2] = (a->color[2] + b->color[2]) >> 1; + out->color[3] = (a->color[3] + b->color[3]) >> 1; +} + +/* +============ +Transpose +============ +*/ +static void Transpose( int width, int height, drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { + int i, j; + drawVert_t temp; + + if ( width > height ) { + for ( i = 0 ; i < height ; i++ ) { + for ( j = i + 1 ; j < width ; j++ ) { + if ( j < height ) { + // swap the value + temp = ctrl[j][i]; + ctrl[j][i] = ctrl[i][j]; + ctrl[i][j] = temp; + } else { + // just copy + ctrl[j][i] = ctrl[i][j]; + } + } + } + } else { + for ( i = 0 ; i < width ; i++ ) { + for ( j = i + 1 ; j < height ; j++ ) { + if ( j < width ) { + // swap the value + temp = ctrl[i][j]; + ctrl[i][j] = ctrl[j][i]; + ctrl[j][i] = temp; + } else { + // just copy + ctrl[i][j] = ctrl[j][i]; + } + } + } + } + +} + + +/* +================= +MakeMeshNormals + +Handles all the complicated wrapping and degenerate cases +================= +*/ +static void MakeMeshNormals( int width, int height, drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { + int i, j, k, dist; + vec3_t normal; + vec3_t sum; + int count = 0; + vec3_t base; + vec3_t delta; + int x, y; + drawVert_t *dv; + vec3_t around[8], temp; + qboolean good[8]; + qboolean wrapWidth, wrapHeight; + float len; +static int neighbors[8][2] = { + {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1} + }; + + wrapWidth = qfalse; + for ( i = 0 ; i < height ; i++ ) { + VectorSubtract( ctrl[i][0].xyz, ctrl[i][width-1].xyz, delta ); + len = VectorLengthSquared( delta ); + if ( len > 1.0 ) { + break; + } + } + if ( i == height ) { + wrapWidth = qtrue; + } + + wrapHeight = qfalse; + for ( i = 0 ; i < width ; i++ ) { + VectorSubtract( ctrl[0][i].xyz, ctrl[height-1][i].xyz, delta ); + len = VectorLengthSquared( delta ); + if ( len > 1.0 ) { + break; + } + } + if ( i == width) { + wrapHeight = qtrue; + } + + + for ( i = 0 ; i < width ; i++ ) { + for ( j = 0 ; j < height ; j++ ) { + count = 0; + dv = &ctrl[j][i]; + VectorCopy( dv->xyz, base ); + for ( k = 0 ; k < 8 ; k++ ) { + VectorClear( around[k] ); + good[k] = qfalse; + + for ( dist = 1 ; dist <= 3 ; dist++ ) { + x = i + neighbors[k][0] * dist; + y = j + neighbors[k][1] * dist; + if ( wrapWidth ) { + if ( x < 0 ) { + x = width - 1 + x; + } else if ( x >= width ) { + x = 1 + x - width; + } + } + if ( wrapHeight ) { + if ( y < 0 ) { + y = height - 1 + y; + } else if ( y >= height ) { + y = 1 + y - height; + } + } + + if ( x < 0 || x >= width || y < 0 || y >= height ) { + break; // edge of patch + } + VectorSubtract( ctrl[y][x].xyz, base, temp ); + if ( VectorNormalize2( temp, temp ) == 0 ) { + continue; // degenerate edge, get more dist + } else { + good[k] = qtrue; + VectorCopy( temp, around[k] ); + break; // good edge + } + } + } + + VectorClear( sum ); + for ( k = 0 ; k < 8 ; k++ ) { + if ( !good[k] || !good[(k+1)&7] ) { + continue; // didn't get two points + } + CrossProduct( around[(k+1)&7], around[k], normal ); + if ( VectorNormalize2( normal, normal ) == 0 ) { + continue; + } + VectorAdd( normal, sum, sum ); + count++; + } + //if ( count == 0 ) { + // printf("bad normal\n"); + //} + VectorNormalize2( sum, dv->normal ); + } + } +} + + +/* +============ +InvertCtrl +============ +*/ +static void InvertCtrl( int width, int height, drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { + int i, j; + drawVert_t temp; + + for ( i = 0 ; i < height ; i++ ) { + for ( j = 0 ; j < width/2 ; j++ ) { + temp = ctrl[i][j]; + ctrl[i][j] = ctrl[i][width-1-j]; + ctrl[i][width-1-j] = temp; + } + } +} + + +/* +================= +InvertErrorTable +================= +*/ +static void InvertErrorTable( float errorTable[2][MAX_GRID_SIZE], int width, int height ) { + int i; + float copy[2][MAX_GRID_SIZE]; + + Com_Memcpy( copy, errorTable, sizeof( copy ) ); + + for ( i = 0 ; i < width ; i++ ) { + errorTable[1][i] = copy[0][i]; //[width-1-i]; + } + + for ( i = 0 ; i < height ; i++ ) { + errorTable[0][i] = copy[1][height-1-i]; + } + +} + +/* +================== +PutPointsOnCurve +================== +*/ +static void PutPointsOnCurve( drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], + int width, int height ) { + int i, j; + drawVert_t prev, next; + + for ( i = 0 ; i < width ; i++ ) { + for ( j = 1 ; j < height ; j += 2 ) { + LerpDrawVert( &ctrl[j][i], &ctrl[j+1][i], &prev ); + LerpDrawVert( &ctrl[j][i], &ctrl[j-1][i], &next ); + LerpDrawVert( &prev, &next, &ctrl[j][i] ); + } + } + + + for ( j = 0 ; j < height ; j++ ) { + for ( i = 1 ; i < width ; i += 2 ) { + LerpDrawVert( &ctrl[j][i], &ctrl[j][i+1], &prev ); + LerpDrawVert( &ctrl[j][i], &ctrl[j][i-1], &next ); + LerpDrawVert( &prev, &next, &ctrl[j][i] ); + } + } +} + +/* +================= +R_CreateSurfaceGridMesh +================= +*/ +srfGridMesh_t *R_CreateSurfaceGridMesh(int width, int height, + drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], float errorTable[2][MAX_GRID_SIZE] ) { + int i, j, size; + drawVert_t *vert; + vec3_t tmpVec; + srfGridMesh_t *grid; + + // copy the results out to a grid + size = (width * height - 1) * sizeof( drawVert_t ) + sizeof( *grid ); + +#ifdef PATCH_STITCHING + grid = /*ri.Hunk_Alloc*/ ri.Malloc( size ); + Com_Memset(grid, 0, size); + + grid->widthLodError = /*ri.Hunk_Alloc*/ ri.Malloc( width * 4 ); + Com_Memcpy( grid->widthLodError, errorTable[0], width * 4 ); + + grid->heightLodError = /*ri.Hunk_Alloc*/ ri.Malloc( height * 4 ); + Com_Memcpy( grid->heightLodError, errorTable[1], height * 4 ); +#else + grid = ri.Hunk_Alloc( size ); + Com_Memset(grid, 0, size); + + grid->widthLodError = ri.Hunk_Alloc( width * 4 ); + Com_Memcpy( grid->widthLodError, errorTable[0], width * 4 ); + + grid->heightLodError = ri.Hunk_Alloc( height * 4 ); + Com_Memcpy( grid->heightLodError, errorTable[1], height * 4 ); +#endif + + grid->width = width; + grid->height = height; + grid->surfaceType = SF_GRID; + ClearBounds( grid->meshBounds[0], grid->meshBounds[1] ); + for ( i = 0 ; i < width ; i++ ) { + for ( j = 0 ; j < height ; j++ ) { + vert = &grid->verts[j*width+i]; + *vert = ctrl[j][i]; + AddPointToBounds( vert->xyz, grid->meshBounds[0], grid->meshBounds[1] ); + } + } + + // compute local origin and bounds + VectorAdd( grid->meshBounds[0], grid->meshBounds[1], grid->localOrigin ); + VectorScale( grid->localOrigin, 0.5f, grid->localOrigin ); + VectorSubtract( grid->meshBounds[0], grid->localOrigin, tmpVec ); + grid->meshRadius = VectorLength( tmpVec ); + + VectorCopy( grid->localOrigin, grid->lodOrigin ); + grid->lodRadius = grid->meshRadius; + // + return grid; +} + +/* +================= +R_FreeSurfaceGridMesh +================= +*/ +void R_FreeSurfaceGridMesh( srfGridMesh_t *grid ) { + ri.Free(grid->widthLodError); + ri.Free(grid->heightLodError); + ri.Free(grid); +} + +/* +================= +R_SubdividePatchToGrid +================= +*/ +srfGridMesh_t *R_SubdividePatchToGrid( int width, int height, + drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) { + int i, j, k, l; + drawVert_t_cleared( prev ); + drawVert_t_cleared( next ); + drawVert_t_cleared( mid ); + float len, maxLen; + int dir; + int t; + drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; + float errorTable[2][MAX_GRID_SIZE]; + + for ( i = 0 ; i < width ; i++ ) { + for ( j = 0 ; j < height ; j++ ) { + ctrl[j][i] = points[j*width+i]; + } + } + + for ( dir = 0 ; dir < 2 ; dir++ ) { + + for ( j = 0 ; j < MAX_GRID_SIZE ; j++ ) { + errorTable[dir][j] = 0; + } + + // horizontal subdivisions + for ( j = 0 ; j + 2 < width ; j += 2 ) { + // check subdivided midpoints against control points + + // FIXME: also check midpoints of adjacent patches against the control points + // this would basically stitch all patches in the same LOD group together. + + maxLen = 0; + for ( i = 0 ; i < height ; i++ ) { + vec3_t midxyz; + vec3_t midxyz2; + vec3_t dir; + vec3_t projected; + float d; + + // calculate the point on the curve + for ( l = 0 ; l < 3 ; l++ ) { + midxyz[l] = (ctrl[i][j].xyz[l] + ctrl[i][j+1].xyz[l] * 2 + + ctrl[i][j+2].xyz[l] ) * 0.25f; + } + + // see how far off the line it is + // using dist-from-line will not account for internal + // texture warping, but it gives a lot less polygons than + // dist-from-midpoint + VectorSubtract( midxyz, ctrl[i][j].xyz, midxyz ); + VectorSubtract( ctrl[i][j+2].xyz, ctrl[i][j].xyz, dir ); + VectorNormalize( dir ); + + d = DotProduct( midxyz, dir ); + VectorScale( dir, d, projected ); + VectorSubtract( midxyz, projected, midxyz2); + len = VectorLengthSquared( midxyz2 ); // we will do the sqrt later + if ( len > maxLen ) { + maxLen = len; + } + } + + maxLen = sqrt(maxLen); + + // if all the points are on the lines, remove the entire columns + if ( maxLen < 0.1f ) { + errorTable[dir][j+1] = 999; + continue; + } + + // see if we want to insert subdivided columns + if ( width + 2 > MAX_GRID_SIZE ) { + errorTable[dir][j+1] = 1.0f/maxLen; + continue; // can't subdivide any more + } + + if ( maxLen <= r_subdivisions->value ) { + errorTable[dir][j+1] = 1.0f/maxLen; + continue; // didn't need subdivision + } + + errorTable[dir][j+2] = 1.0f/maxLen; + + // insert two columns and replace the peak + width += 2; + for ( i = 0 ; i < height ; i++ ) { + LerpDrawVert( &ctrl[i][j], &ctrl[i][j+1], &prev ); + LerpDrawVert( &ctrl[i][j+1], &ctrl[i][j+2], &next ); + LerpDrawVert( &prev, &next, &mid ); + + for ( k = width - 1 ; k > j + 3 ; k-- ) { + ctrl[i][k] = ctrl[i][k-2]; + } + ctrl[i][j + 1] = prev; + ctrl[i][j + 2] = mid; + ctrl[i][j + 3] = next; + } + + // back up and recheck this set again, it may need more subdivision + j -= 2; + + } + + Transpose( width, height, ctrl ); + t = width; + width = height; + height = t; + } + + + // put all the aproximating points on the curve + PutPointsOnCurve( ctrl, width, height ); + + // cull out any rows or columns that are colinear + for ( i = 1 ; i < width-1 ; i++ ) { + if ( errorTable[0][i] != 999 ) { + continue; + } + for ( j = i+1 ; j < width ; j++ ) { + for ( k = 0 ; k < height ; k++ ) { + ctrl[k][j-1] = ctrl[k][j]; + } + errorTable[0][j-1] = errorTable[0][j]; + } + width--; + } + + for ( i = 1 ; i < height-1 ; i++ ) { + if ( errorTable[1][i] != 999 ) { + continue; + } + for ( j = i+1 ; j < height ; j++ ) { + for ( k = 0 ; k < width ; k++ ) { + ctrl[j-1][k] = ctrl[j][k]; + } + errorTable[1][j-1] = errorTable[1][j]; + } + height--; + } + +#if 1 + // flip for longest tristrips as an optimization + // the results should be visually identical with or + // without this step + if ( height > width ) { + Transpose( width, height, ctrl ); + InvertErrorTable( errorTable, width, height ); + t = width; + width = height; + height = t; + InvertCtrl( width, height, ctrl ); + } +#endif + + // calculate normals + MakeMeshNormals( width, height, ctrl ); + + return R_CreateSurfaceGridMesh( width, height, ctrl, errorTable ); +} + +/* +=============== +R_GridInsertColumn +=============== +*/ +srfGridMesh_t *R_GridInsertColumn( srfGridMesh_t *grid, int column, int row, vec3_t point, float loderror ) { + int i, j; + int width, height, oldwidth; + drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; + float errorTable[2][MAX_GRID_SIZE]; + float lodRadius; + vec3_t lodOrigin; + + oldwidth = 0; + width = grid->width + 1; + if (width > MAX_GRID_SIZE) + return NULL; + height = grid->height; + for (i = 0; i < width; i++) { + if (i == column) { + //insert new column + for (j = 0; j < grid->height; j++) { + LerpDrawVert( &grid->verts[j * grid->width + i-1], &grid->verts[j * grid->width + i], &ctrl[j][i] ); + if (j == row) + VectorCopy(point, ctrl[j][i].xyz); + } + errorTable[0][i] = loderror; + continue; + } + errorTable[0][i] = grid->widthLodError[oldwidth]; + for (j = 0; j < grid->height; j++) { + ctrl[j][i] = grid->verts[j * grid->width + oldwidth]; + } + oldwidth++; + } + for (j = 0; j < grid->height; j++) { + errorTable[1][j] = grid->heightLodError[j]; + } + // put all the aproximating points on the curve + //PutPointsOnCurve( ctrl, width, height ); + // calculate normals + MakeMeshNormals( width, height, ctrl ); + + VectorCopy(grid->lodOrigin, lodOrigin); + lodRadius = grid->lodRadius; + // free the old grid + R_FreeSurfaceGridMesh(grid); + // create a new grid + grid = R_CreateSurfaceGridMesh( width, height, ctrl, errorTable ); + grid->lodRadius = lodRadius; + VectorCopy(lodOrigin, grid->lodOrigin); + return grid; +} + +/* +=============== +R_GridInsertRow +=============== +*/ +srfGridMesh_t *R_GridInsertRow( srfGridMesh_t *grid, int row, int column, vec3_t point, float loderror ) { + int i, j; + int width, height, oldheight; + drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; + float errorTable[2][MAX_GRID_SIZE]; + float lodRadius; + vec3_t lodOrigin; + + oldheight = 0; + width = grid->width; + height = grid->height + 1; + if (height > MAX_GRID_SIZE) + return NULL; + for (i = 0; i < height; i++) { + if (i == row) { + //insert new row + for (j = 0; j < grid->width; j++) { + LerpDrawVert( &grid->verts[(i-1) * grid->width + j], &grid->verts[i * grid->width + j], &ctrl[i][j] ); + if (j == column) + VectorCopy(point, ctrl[i][j].xyz); + } + errorTable[1][i] = loderror; + continue; + } + errorTable[1][i] = grid->heightLodError[oldheight]; + for (j = 0; j < grid->width; j++) { + ctrl[i][j] = grid->verts[oldheight * grid->width + j]; + } + oldheight++; + } + for (j = 0; j < grid->width; j++) { + errorTable[0][j] = grid->widthLodError[j]; + } + // put all the aproximating points on the curve + //PutPointsOnCurve( ctrl, width, height ); + // calculate normals + MakeMeshNormals( width, height, ctrl ); + + VectorCopy(grid->lodOrigin, lodOrigin); + lodRadius = grid->lodRadius; + // free the old grid + R_FreeSurfaceGridMesh(grid); + // create a new grid + grid = R_CreateSurfaceGridMesh( width, height, ctrl, errorTable ); + grid->lodRadius = lodRadius; + VectorCopy(lodOrigin, grid->lodOrigin); + return grid; +} diff --git a/src/renderergl1/tr_flares.c b/src/renderergl1/tr_flares.c new file mode 100644 index 00000000..9397fbac --- /dev/null +++ b/src/renderergl1/tr_flares.c @@ -0,0 +1,529 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_flares.c + +#include "tr_local.h" + +/* +============================================================================= + +LIGHT FLARES + +A light flare is an effect that takes place inside the eye when bright light +sources are visible. The size of the flare relative to the screen is nearly +constant, irrespective of distance, but the intensity should be proportional to the +projected area of the light source. + +A surface that has been flagged as having a light flare will calculate the depth +buffer value that its midpoint should have when the surface is added. + +After all opaque surfaces have been rendered, the depth buffer is read back for +each flare in view. If the point has not been obscured by a closer surface, the +flare should be drawn. + +Surfaces that have a repeated texture should never be flagged as flaring, because +there will only be a single flare added at the midpoint of the polygon. + +To prevent abrupt popping, the intensity of the flare is interpolated up and +down as it changes visibility. This involves scene to scene state, unlike almost +all other aspects of the renderer, and is complicated by the fact that a single +frame may have multiple scenes. + +RB_RenderFlares() will be called once per view (twice in a mirrored scene, potentially +up to five or more times in a frame with 3D status bar icons). + +============================================================================= +*/ + + +// flare states maintain visibility over multiple frames for fading +// layers: view, mirror, menu +typedef struct flare_s { + struct flare_s *next; // for active chain + + int addedFrame; + + qboolean inPortal; // true if in a portal view of the scene + int frameSceneNum; + void *surface; + int fogNum; + + int fadeTime; + + qboolean visible; // state of last test + float drawIntensity; // may be non 0 even if !visible due to fading + + int windowX, windowY; + float eyeZ; + + vec3_t origin; + vec3_t color; +} flare_t; + +#define MAX_FLARES 256 + +flare_t r_flareStructs[MAX_FLARES]; +flare_t *r_activeFlares, *r_inactiveFlares; + +int flareCoeff; + +/* +================== +R_ClearFlares +================== +*/ +void R_ClearFlares( void ) { + int i; + + Com_Memset( r_flareStructs, 0, sizeof( r_flareStructs ) ); + r_activeFlares = NULL; + r_inactiveFlares = NULL; + + for ( i = 0 ; i < MAX_FLARES ; i++ ) { + r_flareStructs[i].next = r_inactiveFlares; + r_inactiveFlares = &r_flareStructs[i]; + } +} + + +/* +================== +RB_AddFlare + +This is called at surface tesselation time +================== +*/ +void RB_AddFlare( void *surface, int fogNum, vec3_t point, vec3_t color, vec3_t normal ) { + int i; + flare_t *f; + vec3_t local; + float d = 1; + vec4_t eye, clip, normalized, window; + + backEnd.pc.c_flareAdds++; + + if(normal && (normal[0] || normal[1] || normal[2])) + { + VectorSubtract( backEnd.viewParms.or.origin, point, local ); + VectorNormalizeFast(local); + d = DotProduct(local, normal); + + // If the viewer is behind the flare don't add it. + if(d < 0) + return; + } + + // if the point is off the screen, don't bother adding it + // calculate screen coordinates and depth + R_TransformModelToClip( point, backEnd.or.modelMatrix, + backEnd.viewParms.projectionMatrix, eye, clip ); + + // check to see if the point is completely off screen + for ( i = 0 ; i < 3 ; i++ ) { + if ( clip[i] >= clip[3] || clip[i] <= -clip[3] ) { + return; + } + } + + R_TransformClipToWindow( clip, &backEnd.viewParms, normalized, window ); + + if ( window[0] < 0 || window[0] >= backEnd.viewParms.viewportWidth + || window[1] < 0 || window[1] >= backEnd.viewParms.viewportHeight ) { + return; // shouldn't happen, since we check the clip[] above, except for FP rounding + } + + // see if a flare with a matching surface, scene, and view exists + for ( f = r_activeFlares ; f ; f = f->next ) { + if ( f->surface == surface && f->frameSceneNum == backEnd.viewParms.frameSceneNum + && f->inPortal == backEnd.viewParms.isPortal ) { + break; + } + } + + // allocate a new one + if (!f ) { + if ( !r_inactiveFlares ) { + // the list is completely full + return; + } + f = r_inactiveFlares; + r_inactiveFlares = r_inactiveFlares->next; + f->next = r_activeFlares; + r_activeFlares = f; + + f->surface = surface; + f->frameSceneNum = backEnd.viewParms.frameSceneNum; + f->inPortal = backEnd.viewParms.isPortal; + f->addedFrame = -1; + } + + if ( f->addedFrame != backEnd.viewParms.frameCount - 1 ) { + f->visible = qfalse; + f->fadeTime = backEnd.refdef.time - 2000; + } + + f->addedFrame = backEnd.viewParms.frameCount; + f->fogNum = fogNum; + + VectorCopy(point, f->origin); + VectorCopy( color, f->color ); + + // fade the intensity of the flare down as the + // light surface turns away from the viewer + VectorScale( f->color, d, f->color ); + + // save info needed to test + f->windowX = backEnd.viewParms.viewportX + window[0]; + f->windowY = backEnd.viewParms.viewportY + window[1]; + + f->eyeZ = eye[2]; +} + +/* +================== +RB_AddDlightFlares +================== +*/ +void RB_AddDlightFlares( void ) { + dlight_t *l; + int i, j, k; + fog_t *fog = NULL; + + if ( !r_flares->integer ) { + return; + } + + l = backEnd.refdef.dlights; + + if(tr.world) + fog = tr.world->fogs; + + for (i=0 ; inumfogs ; j++ ) { + fog = &tr.world->fogs[j]; + for ( k = 0 ; k < 3 ; k++ ) { + if ( l->origin[k] < fog->bounds[0][k] || l->origin[k] > fog->bounds[1][k] ) { + break; + } + } + if ( k == 3 ) { + break; + } + } + if ( j == tr.world->numfogs ) { + j = 0; + } + } + else + j = 0; + + RB_AddFlare( (void *)l, j, l->origin, l->color, NULL ); + } +} + +/* +=============================================================================== + +FLARE BACK END + +=============================================================================== +*/ + +/* +================== +RB_TestFlare +================== +*/ +void RB_TestFlare( flare_t *f ) { + float depth; + qboolean visible; + float fade; + float screenZ; + + backEnd.pc.c_flareTests++; + + // doing a readpixels is as good as doing a glFinish(), so + // don't bother with another sync + glState.finishCalled = qfalse; + + // read back the z buffer contents + qglReadPixels( f->windowX, f->windowY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth ); + + screenZ = backEnd.viewParms.projectionMatrix[14] / + ( ( 2*depth - 1 ) * backEnd.viewParms.projectionMatrix[11] - backEnd.viewParms.projectionMatrix[10] ); + + visible = ( -f->eyeZ - -screenZ ) < 24; + + if ( visible ) { + if ( !f->visible ) { + f->visible = qtrue; + f->fadeTime = backEnd.refdef.time - 1; + } + fade = ( ( backEnd.refdef.time - f->fadeTime ) /1000.0f ) * r_flareFade->value; + } else { + if ( f->visible ) { + f->visible = qfalse; + f->fadeTime = backEnd.refdef.time - 1; + } + fade = 1.0f - ( ( backEnd.refdef.time - f->fadeTime ) / 1000.0f ) * r_flareFade->value; + } + + if ( fade < 0 ) { + fade = 0; + } + if ( fade > 1 ) { + fade = 1; + } + + f->drawIntensity = fade; +} + + +/* +================== +RB_RenderFlare +================== +*/ +void RB_RenderFlare( flare_t *f ) { + float size; + vec3_t color; + int iColor[3]; + float distance, intensity, factor; + byte fogFactors[3] = {255, 255, 255}; + + backEnd.pc.c_flareRenders++; + + // We don't want too big values anyways when dividing by distance. + if(f->eyeZ > -1.0f) + distance = 1.0f; + else + distance = -f->eyeZ; + + // calculate the flare size.. + size = backEnd.viewParms.viewportWidth * ( r_flareSize->value/640.0f + 8 / distance ); + +/* + * This is an alternative to intensity scaling. It changes the size of the flare on screen instead + * with growing distance. See in the description at the top why this is not the way to go. + // size will change ~ 1/r. + size = backEnd.viewParms.viewportWidth * (r_flareSize->value / (distance * -2.0f)); +*/ + +/* + * As flare sizes stay nearly constant with increasing distance we must decrease the intensity + * to achieve a reasonable visual result. The intensity is ~ (size^2 / distance^2) which can be + * got by considering the ratio of + * (flaresurface on screen) : (Surface of sphere defined by flare origin and distance from flare) + * An important requirement is: + * intensity <= 1 for all distances. + * + * The formula used here to compute the intensity is as follows: + * intensity = flareCoeff * size^2 / (distance + size*sqrt(flareCoeff))^2 + * As you can see, the intensity will have a max. of 1 when the distance is 0. + * The coefficient flareCoeff will determine the falloff speed with increasing distance. + */ + + factor = distance + size * sqrt(flareCoeff); + + intensity = flareCoeff * size * size / (factor * factor); + + VectorScale(f->color, f->drawIntensity * intensity, color); + +// Calculations for fogging + if(tr.world && f->fogNum < tr.world->numfogs) + { + tess.numVertexes = 1; + VectorCopy(f->origin, tess.xyz[0]); + tess.fogNum = f->fogNum; + + RB_CalcModulateColorsByFog(fogFactors); + + // We don't need to render the flare if colors are 0 anyways. + if(!(fogFactors[0] || fogFactors[1] || fogFactors[2])) + return; + } + + iColor[0] = color[0] * fogFactors[0]; + iColor[1] = color[1] * fogFactors[1]; + iColor[2] = color[2] * fogFactors[2]; + + RB_BeginSurface( tr.flareShader, f->fogNum ); + + // FIXME: use quadstamp? + tess.xyz[tess.numVertexes][0] = f->windowX - size; + tess.xyz[tess.numVertexes][1] = f->windowY - size; + tess.texCoords[tess.numVertexes][0][0] = 0; + tess.texCoords[tess.numVertexes][0][1] = 0; + tess.vertexColors[tess.numVertexes][0] = iColor[0]; + tess.vertexColors[tess.numVertexes][1] = iColor[1]; + tess.vertexColors[tess.numVertexes][2] = iColor[2]; + tess.vertexColors[tess.numVertexes][3] = 255; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = f->windowX - size; + tess.xyz[tess.numVertexes][1] = f->windowY + size; + tess.texCoords[tess.numVertexes][0][0] = 0; + tess.texCoords[tess.numVertexes][0][1] = 1; + tess.vertexColors[tess.numVertexes][0] = iColor[0]; + tess.vertexColors[tess.numVertexes][1] = iColor[1]; + tess.vertexColors[tess.numVertexes][2] = iColor[2]; + tess.vertexColors[tess.numVertexes][3] = 255; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = f->windowX + size; + tess.xyz[tess.numVertexes][1] = f->windowY + size; + tess.texCoords[tess.numVertexes][0][0] = 1; + tess.texCoords[tess.numVertexes][0][1] = 1; + tess.vertexColors[tess.numVertexes][0] = iColor[0]; + tess.vertexColors[tess.numVertexes][1] = iColor[1]; + tess.vertexColors[tess.numVertexes][2] = iColor[2]; + tess.vertexColors[tess.numVertexes][3] = 255; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = f->windowX + size; + tess.xyz[tess.numVertexes][1] = f->windowY - size; + tess.texCoords[tess.numVertexes][0][0] = 1; + tess.texCoords[tess.numVertexes][0][1] = 0; + tess.vertexColors[tess.numVertexes][0] = iColor[0]; + tess.vertexColors[tess.numVertexes][1] = iColor[1]; + tess.vertexColors[tess.numVertexes][2] = iColor[2]; + tess.vertexColors[tess.numVertexes][3] = 255; + tess.numVertexes++; + + tess.indexes[tess.numIndexes++] = 0; + tess.indexes[tess.numIndexes++] = 1; + tess.indexes[tess.numIndexes++] = 2; + tess.indexes[tess.numIndexes++] = 0; + tess.indexes[tess.numIndexes++] = 2; + tess.indexes[tess.numIndexes++] = 3; + + RB_EndSurface(); +} + +/* +================== +RB_RenderFlares + +Because flares are simulating an occular effect, they should be drawn after +everything (all views) in the entire frame has been drawn. + +Because of the way portals use the depth buffer to mark off areas, the +needed information would be lost after each view, so we are forced to draw +flares after each view. + +The resulting artifact is that flares in mirrors or portals don't dim properly +when occluded by something in the main view, and portal flares that should +extend past the portal edge will be overwritten. +================== +*/ +void RB_RenderFlares (void) { + flare_t *f; + flare_t **prev; + qboolean draw; + + if ( !r_flares->integer ) { + return; + } + + if(r_flareCoeff->modified) + { + if(r_flareCoeff->value == 0.0f) + flareCoeff = atof(FLARE_STDCOEFF); + else + flareCoeff = r_flareCoeff->value; + + r_flareCoeff->modified = qfalse; + } + + // Reset currentEntity to world so that any previously referenced entities + // don't have influence on the rendering of these flares (i.e. RF_ renderer flags). + backEnd.currentEntity = &tr.worldEntity; + backEnd.or = backEnd.viewParms.world; + +// RB_AddDlightFlares(); + + // perform z buffer readback on each flare in this view + draw = qfalse; + prev = &r_activeFlares; + while ( ( f = *prev ) != NULL ) { + // throw out any flares that weren't added last frame + if ( f->addedFrame < backEnd.viewParms.frameCount - 1 ) { + *prev = f->next; + f->next = r_inactiveFlares; + r_inactiveFlares = f; + continue; + } + + // don't draw any here that aren't from this scene / portal + f->drawIntensity = 0; + if ( f->frameSceneNum == backEnd.viewParms.frameSceneNum + && f->inPortal == backEnd.viewParms.isPortal ) { + RB_TestFlare( f ); + if ( f->drawIntensity ) { + draw = qtrue; + } else { + // this flare has completely faded out, so remove it from the chain + *prev = f->next; + f->next = r_inactiveFlares; + r_inactiveFlares = f; + continue; + } + } + + prev = &f->next; + } + + if ( !draw ) { + return; // none visible + } + + if ( backEnd.viewParms.isPortal ) { + qglDisable (GL_CLIP_PLANE0); + } + + qglPushMatrix(); + qglLoadIdentity(); + qglMatrixMode( GL_PROJECTION ); + qglPushMatrix(); + qglLoadIdentity(); + qglOrtho( backEnd.viewParms.viewportX, backEnd.viewParms.viewportX + backEnd.viewParms.viewportWidth, + backEnd.viewParms.viewportY, backEnd.viewParms.viewportY + backEnd.viewParms.viewportHeight, + -99999, 99999 ); + + for ( f = r_activeFlares ; f ; f = f->next ) { + if ( f->frameSceneNum == backEnd.viewParms.frameSceneNum + && f->inPortal == backEnd.viewParms.isPortal + && f->drawIntensity ) { + RB_RenderFlare( f ); + } + } + + qglPopMatrix(); + qglMatrixMode( GL_MODELVIEW ); + qglPopMatrix(); +} + diff --git a/src/renderergl1/tr_font.c b/src/renderergl1/tr_font.c new file mode 100644 index 00000000..432d1215 --- /dev/null +++ b/src/renderergl1/tr_font.c @@ -0,0 +1,555 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_font.c +// +// +// The font system uses FreeType 2.x to render TrueType fonts for use within the game. +// As of this writing ( Nov, 2000 ) Team Arena uses these fonts for all of the ui and +// about 90% of the cgame presentation. A few areas of the CGAME were left uses the old +// fonts since the code is shared with standard Q3A. +// +// If you include this font rendering code in a commercial product you MUST include the +// following somewhere with your product, see www.freetype.org for specifics or changes. +// The Freetype code also uses some hinting techniques that MIGHT infringe on patents +// held by apple so be aware of that also. +// +// As of Q3A 1.25+ and Team Arena, we are shipping the game with the font rendering code +// disabled. This removes any potential patent issues and it keeps us from having to +// distribute an actual TrueTrype font which is 1. expensive to do and 2. seems to require +// an act of god to accomplish. +// +// What we did was pre-render the fonts using FreeType ( which is why we leave the FreeType +// credit in the credits ) and then saved off the glyph data and then hand touched up the +// font bitmaps so they scale a bit better in GL. +// +// There are limitations in the way fonts are saved and reloaded in that it is based on +// point size and not name. So if you pre-render Helvetica in 18 point and Impact in 18 point +// you will end up with a single 18 point data file and image set. Typically you will want to +// choose 3 sizes to best approximate the scaling you will be doing in the ui scripting system +// +// In the UI Scripting code, a scale of 1.0 is equal to a 48 point font. In Team Arena, we +// use three or four scales, most of them exactly equaling the specific rendered size. We +// rendered three sizes in Team Arena, 12, 16, and 20. +// +// To generate new font data you need to go through the following steps. +// 1. delete the fontImage_x_xx.tga files and fontImage_xx.dat files from the fonts path. +// 2. in a ui script, specificy a font, smallFont, and bigFont keyword with font name and +// point size. the original TrueType fonts must exist in fonts at this point. +// 3. run the game, you should see things normally. +// 4. Exit the game and there will be three dat files and at least three tga files. The +// tga's are in 256x256 pages so if it takes three images to render a 24 point font you +// will end up with fontImage_0_24.tga through fontImage_2_24.tga +// 5. In future runs of the game, the system looks for these images and data files when a s +// specific point sized font is rendered and loads them for use. +// 6. Because of the original beta nature of the FreeType code you will probably want to hand +// touch the font bitmaps. +// +// Currently a define in the project turns on or off the FreeType code which is currently +// defined out. To pre-render new fonts you need enable the define ( BUILD_FREETYPE ) and +// uncheck the exclude from build check box in the FreeType2 area of the Renderer project. + + +#include "tr_local.h" +#include "../qcommon/qcommon.h" + +#ifdef BUILD_FREETYPE +#include +#include FT_ERRORS_H +#include FT_SYSTEM_H +#include FT_IMAGE_H +#include FT_FREETYPE_H +#include FT_OUTLINE_H + +#define _FLOOR(x) ((x) & -64) +#define _CEIL(x) (((x)+63) & -64) +#define _TRUNC(x) ((x) >> 6) + +FT_Library ftLibrary = NULL; +#endif + +#define MAX_FONTS 6 +static int registeredFontCount = 0; +static fontInfo_t registeredFont[MAX_FONTS]; + +#ifdef BUILD_FREETYPE +void R_GetGlyphInfo(FT_GlyphSlot glyph, int *left, int *right, int *width, int *top, int *bottom, int *height, int *pitch) { + *left = _FLOOR( glyph->metrics.horiBearingX ); + *right = _CEIL( glyph->metrics.horiBearingX + glyph->metrics.width ); + *width = _TRUNC(*right - *left); + + *top = _CEIL( glyph->metrics.horiBearingY ); + *bottom = _FLOOR( glyph->metrics.horiBearingY - glyph->metrics.height ); + *height = _TRUNC( *top - *bottom ); + *pitch = ( qtrue ? (*width+3) & -4 : (*width+7) >> 3 ); +} + + +FT_Bitmap *R_RenderGlyph(FT_GlyphSlot glyph, glyphInfo_t* glyphOut) { + FT_Bitmap *bit2; + int left, right, width, top, bottom, height, pitch, size; + + R_GetGlyphInfo(glyph, &left, &right, &width, &top, &bottom, &height, &pitch); + + if ( glyph->format == ft_glyph_format_outline ) { + size = pitch*height; + + bit2 = ri.Malloc(sizeof(FT_Bitmap)); + + bit2->width = width; + bit2->rows = height; + bit2->pitch = pitch; + bit2->pixel_mode = ft_pixel_mode_grays; + //bit2->pixel_mode = ft_pixel_mode_mono; + bit2->buffer = ri.Malloc(pitch*height); + bit2->num_grays = 256; + + Com_Memset( bit2->buffer, 0, size ); + + FT_Outline_Translate( &glyph->outline, -left, -bottom ); + + FT_Outline_Get_Bitmap( ftLibrary, &glyph->outline, bit2 ); + + glyphOut->height = height; + glyphOut->pitch = pitch; + glyphOut->top = (glyph->metrics.horiBearingY >> 6) + 1; + glyphOut->bottom = bottom; + + return bit2; + } else { + ri.Printf(PRINT_ALL, "Non-outline fonts are not supported\n"); + } + return NULL; +} + +void WriteTGA (char *filename, byte *data, int width, int height) { + byte *buffer; + int i, c; + int row; + unsigned char *flip; + unsigned char *src, *dst; + + buffer = ri.Malloc(width*height*4 + 18); + Com_Memset (buffer, 0, 18); + buffer[2] = 2; // uncompressed type + buffer[12] = width&255; + buffer[13] = width>>8; + buffer[14] = height&255; + buffer[15] = height>>8; + buffer[16] = 32; // pixel size + + // swap rgb to bgr + c = 18 + width * height * 4; + for (i=18 ; iglyph, &glyph); + if (bitmap) { + glyph.xSkip = (face->glyph->metrics.horiAdvance >> 6) + 1; + } else { + return &glyph; + } + + if (glyph.height > *maxHeight) { + *maxHeight = glyph.height; + } + + if (calcHeight) { + ri.Free(bitmap->buffer); + ri.Free(bitmap); + return &glyph; + } + +/* + // need to convert to power of 2 sizes so we do not get + // any scaling from the gl upload + for (scaled_width = 1 ; scaled_width < glyph.pitch ; scaled_width<<=1) + ; + for (scaled_height = 1 ; scaled_height < glyph.height ; scaled_height<<=1) + ; +*/ + + scaled_width = glyph.pitch; + scaled_height = glyph.height; + + // we need to make sure we fit + if (*xOut + scaled_width + 1 >= 255) { + *xOut = 0; + *yOut += *maxHeight + 1; + } + + if (*yOut + *maxHeight + 1 >= 255) { + *yOut = -1; + *xOut = -1; + ri.Free(bitmap->buffer); + ri.Free(bitmap); + return &glyph; + } + + + src = bitmap->buffer; + dst = imageOut + (*yOut * 256) + *xOut; + + if (bitmap->pixel_mode == ft_pixel_mode_mono) { + for (i = 0; i < glyph.height; i++) { + int j; + unsigned char *_src = src; + unsigned char *_dst = dst; + unsigned char mask = 0x80; + unsigned char val = *_src; + for (j = 0; j < glyph.pitch; j++) { + if (mask == 0x80) { + val = *_src++; + } + if (val & mask) { + *_dst = 0xff; + } + mask >>= 1; + + if ( mask == 0 ) { + mask = 0x80; + } + _dst++; + } + + src += glyph.pitch; + dst += 256; + } + } else { + for (i = 0; i < glyph.height; i++) { + Com_Memcpy(dst, src, glyph.pitch); + src += glyph.pitch; + dst += 256; + } + } + + // we now have an 8 bit per pixel grey scale bitmap + // that is width wide and pf->ftSize->metrics.y_ppem tall + + glyph.imageHeight = scaled_height; + glyph.imageWidth = scaled_width; + glyph.s = (float)*xOut / 256; + glyph.t = (float)*yOut / 256; + glyph.s2 = glyph.s + (float)scaled_width / 256; + glyph.t2 = glyph.t + (float)scaled_height / 256; + + *xOut += scaled_width + 1; + + ri.Free(bitmap->buffer); + ri.Free(bitmap); + } + + return &glyph; +} +#endif + +static int fdOffset; +static byte *fdFile; + +int readInt( void ) { + int i = fdFile[fdOffset]+(fdFile[fdOffset+1]<<8)+(fdFile[fdOffset+2]<<16)+(fdFile[fdOffset+3]<<24); + fdOffset += 4; + return i; +} + +typedef union { + byte fred[4]; + float ffred; +} poor; + +float readFloat( void ) { + poor me; +#if defined Q3_BIG_ENDIAN + me.fred[0] = fdFile[fdOffset+3]; + me.fred[1] = fdFile[fdOffset+2]; + me.fred[2] = fdFile[fdOffset+1]; + me.fred[3] = fdFile[fdOffset+0]; +#elif defined Q3_LITTLE_ENDIAN + me.fred[0] = fdFile[fdOffset+0]; + me.fred[1] = fdFile[fdOffset+1]; + me.fred[2] = fdFile[fdOffset+2]; + me.fred[3] = fdFile[fdOffset+3]; +#endif + fdOffset += 4; + return me.ffred; +} + +void RE_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font) { +#ifdef BUILD_FREETYPE + FT_Face face; + int j, k, xOut, yOut, lastStart, imageNumber; + int scaledSize, newSize, maxHeight, left; + unsigned char *out, *imageBuff; + glyphInfo_t *glyph; + image_t *image; + qhandle_t h; + float max; + float dpi = 72; + float glyphScale; +#endif + void *faceData; + int i, len; + char name[1024]; + + if (!fontName) { + ri.Printf(PRINT_ALL, "RE_RegisterFont: called with empty name\n"); + return; + } + + if (pointSize <= 0) { + pointSize = 12; + } + + R_IssuePendingRenderCommands(); + + if (registeredFontCount >= MAX_FONTS) { + ri.Printf(PRINT_WARNING, "RE_RegisterFont: Too many fonts registered already.\n"); + return; + } + + Com_sprintf(name, sizeof(name), "fonts/fontImage_%i.dat",pointSize); + for (i = 0; i < registeredFontCount; i++) { + if (Q_stricmp(name, registeredFont[i].name) == 0) { + Com_Memcpy(font, ®isteredFont[i], sizeof(fontInfo_t)); + return; + } + } + + len = ri.FS_ReadFile(name, NULL); + if (len == sizeof(fontInfo_t)) { + ri.FS_ReadFile(name, &faceData); + fdOffset = 0; + fdFile = faceData; + for(i=0; iglyphs[i].height = readInt(); + font->glyphs[i].top = readInt(); + font->glyphs[i].bottom = readInt(); + font->glyphs[i].pitch = readInt(); + font->glyphs[i].xSkip = readInt(); + font->glyphs[i].imageWidth = readInt(); + font->glyphs[i].imageHeight = readInt(); + font->glyphs[i].s = readFloat(); + font->glyphs[i].t = readFloat(); + font->glyphs[i].s2 = readFloat(); + font->glyphs[i].t2 = readFloat(); + font->glyphs[i].glyph = readInt(); + Q_strncpyz(font->glyphs[i].shaderName, (const char *)&fdFile[fdOffset], sizeof(font->glyphs[i].shaderName)); + fdOffset += sizeof(font->glyphs[i].shaderName); + } + font->glyphScale = readFloat(); + Com_Memcpy(font->name, &fdFile[fdOffset], MAX_QPATH); + +// Com_Memcpy(font, faceData, sizeof(fontInfo_t)); + Q_strncpyz(font->name, name, sizeof(font->name)); + for (i = GLYPH_START; i < GLYPH_END; i++) { + font->glyphs[i].glyph = RE_RegisterShaderNoMip(font->glyphs[i].shaderName); + } + Com_Memcpy(®isteredFont[registeredFontCount++], font, sizeof(fontInfo_t)); + return; + } + +#ifndef BUILD_FREETYPE + ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType code not available\n"); +#else + if (ftLibrary == NULL) { + ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType not initialized.\n"); + return; + } + + len = ri.FS_ReadFile(fontName, &faceData); + if (len <= 0) { + ri.Printf(PRINT_WARNING, "RE_RegisterFont: Unable to read font file '%s'\n", fontName); + return; + } + + // allocate on the stack first in case we fail + if (FT_New_Memory_Face( ftLibrary, faceData, len, 0, &face )) { + ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType, unable to allocate new face.\n"); + return; + } + + + if (FT_Set_Char_Size( face, pointSize << 6, pointSize << 6, dpi, dpi)) { + ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType, unable to set face char size.\n"); + return; + } + + //*font = ®isteredFonts[registeredFontCount++]; + + // make a 256x256 image buffer, once it is full, register it, clean it and keep going + // until all glyphs are rendered + + out = ri.Malloc(1024*1024); + if (out == NULL) { + ri.Printf(PRINT_WARNING, "RE_RegisterFont: ri.Malloc failure during output image creation.\n"); + return; + } + Com_Memset(out, 0, 1024*1024); + + maxHeight = 0; + + for (i = GLYPH_START; i < GLYPH_END; i++) { + RE_ConstructGlyphInfo(out, &xOut, &yOut, &maxHeight, face, (unsigned char)i, qtrue); + } + + xOut = 0; + yOut = 0; + i = GLYPH_START; + lastStart = i; + imageNumber = 0; + + while ( i <= GLYPH_END ) { + + glyph = RE_ConstructGlyphInfo(out, &xOut, &yOut, &maxHeight, face, (unsigned char)i, qfalse); + + if (xOut == -1 || yOut == -1 || i == GLYPH_END) { + // ran out of room + // we need to create an image from the bitmap, set all the handles in the glyphs to this point + // + + scaledSize = 256*256; + newSize = scaledSize * 4; + imageBuff = ri.Malloc(newSize); + left = 0; + max = 0; + for ( k = 0; k < (scaledSize) ; k++ ) { + if (max < out[k]) { + max = out[k]; + } + } + + if (max > 0) { + max = 255/max; + } + + for ( k = 0; k < (scaledSize) ; k++ ) { + imageBuff[left++] = 255; + imageBuff[left++] = 255; + imageBuff[left++] = 255; + + imageBuff[left++] = ((float)out[k] * max); + } + + Com_sprintf (name, sizeof(name), "fonts/fontImage_%i_%i.tga", imageNumber++, pointSize); + if (r_saveFontData->integer) { + WriteTGA(name, imageBuff, 256, 256); + } + + //Com_sprintf (name, sizeof(name), "fonts/fontImage_%i_%i", imageNumber++, pointSize); + image = R_CreateImage(name, imageBuff, 256, 256, qfalse, qfalse, GL_CLAMP_TO_EDGE); + h = RE_RegisterShaderFromImage(name, LIGHTMAP_2D, image, qfalse); + for (j = lastStart; j < i; j++) { + font->glyphs[j].glyph = h; + Q_strncpyz(font->glyphs[j].shaderName, name, sizeof(font->glyphs[j].shaderName)); + } + lastStart = i; + Com_Memset(out, 0, 1024*1024); + xOut = 0; + yOut = 0; + ri.Free(imageBuff); + i++; + } else { + Com_Memcpy(&font->glyphs[i], glyph, sizeof(glyphInfo_t)); + i++; + } + } + + // change the scale to be relative to 1 based on 72 dpi ( so dpi of 144 means a scale of .5 ) + glyphScale = 72.0f / dpi; + + // we also need to adjust the scale based on point size relative to 48 points as the ui scaling is based on a 48 point font + glyphScale *= 48.0f / pointSize; + + registeredFont[registeredFontCount].glyphScale = glyphScale; + font->glyphScale = glyphScale; + Com_Memcpy(®isteredFont[registeredFontCount++], font, sizeof(fontInfo_t)); + + if (r_saveFontData->integer) { + ri.FS_WriteFile(va("fonts/fontImage_%i.dat", pointSize), font, sizeof(fontInfo_t)); + } + + ri.Free(out); + + ri.FS_FreeFile(faceData); +#endif +} + + + +void R_InitFreeType(void) { +#ifdef BUILD_FREETYPE + if (FT_Init_FreeType( &ftLibrary )) { + ri.Printf(PRINT_WARNING, "R_InitFreeType: Unable to initialize FreeType.\n"); + } +#endif + registeredFontCount = 0; +} + + +void R_DoneFreeType(void) { +#ifdef BUILD_FREETYPE + if (ftLibrary) { + FT_Done_FreeType( ftLibrary ); + ftLibrary = NULL; + } +#endif + registeredFontCount = 0; +} + diff --git a/src/renderergl1/tr_image.c b/src/renderergl1/tr_image.c new file mode 100644 index 00000000..3338a25b --- /dev/null +++ b/src/renderergl1/tr_image.c @@ -0,0 +1,1608 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_image.c +#include "tr_local.h" + +static byte s_intensitytable[256]; +static unsigned char s_gammatable[256]; + +int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; +int gl_filter_max = GL_LINEAR; + +#define FILE_HASH_SIZE 1024 +static image_t* hashTable[FILE_HASH_SIZE]; + +/* +** R_GammaCorrect +*/ +void R_GammaCorrect( byte *buffer, int bufSize ) { + int i; + + for ( i = 0; i < bufSize; i++ ) { + buffer[i] = s_gammatable[buffer[i]]; + } +} + +typedef struct { + char *name; + int minimize, maximize; +} textureMode_t; + +textureMode_t modes[] = { + {"GL_NEAREST", GL_NEAREST, GL_NEAREST}, + {"GL_LINEAR", GL_LINEAR, GL_LINEAR}, + {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, + {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, + {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, + {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} +}; + +/* +================ +return a hash value for the filename +================ +*/ +static long generateHashValue( const char *fname ) { + int i; + long hash; + char letter; + + hash = 0; + i = 0; + while (fname[i] != '\0') { + letter = tolower(fname[i]); + if (letter =='.') break; // don't include extension + if (letter =='\\') letter = '/'; // damn path names + hash+=(long)(letter)*(i+119); + i++; + } + hash &= (FILE_HASH_SIZE-1); + return hash; +} + +/* +=============== +GL_TextureMode +=============== +*/ +void GL_TextureMode( const char *string ) { + int i; + image_t *glt; + + for ( i=0 ; i< 6 ; i++ ) { + if ( !Q_stricmp( modes[i].name, string ) ) { + break; + } + } + + // hack to prevent trilinear from being set on voodoo, + // because their driver freaks... + if ( i == 5 && glConfig.hardwareType == GLHW_3DFX_2D3D ) { + ri.Printf( PRINT_ALL, "Refusing to set trilinear on a voodoo.\n" ); + i = 3; + } + + + if ( i == 6 ) { + ri.Printf (PRINT_ALL, "bad filter name\n"); + return; + } + + gl_filter_min = modes[i].minimize; + gl_filter_max = modes[i].maximize; + + // change all the existing mipmap texture objects + for ( i = 0 ; i < tr.numImages ; i++ ) { + glt = tr.images[ i ]; + if ( glt->mipmap ) { + GL_Bind (glt); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + } +} + +/* +=============== +R_SumOfUsedImages +=============== +*/ +int R_SumOfUsedImages( void ) { + int total; + int i; + + total = 0; + for ( i = 0; i < tr.numImages; i++ ) { + if ( tr.images[i]->frameUsed == tr.frameCount ) { + total += tr.images[i]->uploadWidth * tr.images[i]->uploadHeight; + } + } + + return total; +} + +/* +=============== +R_ImageList_f +=============== +*/ +void R_ImageList_f( void ) { + int i; + image_t *image; + int texels; + const char *yesno[] = { + "no ", "yes" + }; + + ri.Printf (PRINT_ALL, "\n -w-- -h-- -mm- -TMU- -if-- wrap --name-------\n"); + texels = 0; + + for ( i = 0 ; i < tr.numImages ; i++ ) { + image = tr.images[ i ]; + + texels += image->uploadWidth*image->uploadHeight; + ri.Printf (PRINT_ALL, "%4i: %4i %4i %s %d ", + i, image->uploadWidth, image->uploadHeight, yesno[image->mipmap], image->TMU ); + switch ( image->internalFormat ) { + case 1: + ri.Printf( PRINT_ALL, "I " ); + break; + case 2: + ri.Printf( PRINT_ALL, "IA " ); + break; + case 3: + ri.Printf( PRINT_ALL, "RGB " ); + break; + case 4: + ri.Printf( PRINT_ALL, "RGBA " ); + break; + case GL_RGBA8: + ri.Printf( PRINT_ALL, "RGBA8" ); + break; + case GL_RGB8: + ri.Printf( PRINT_ALL, "RGB8" ); + break; + case GL_RGB4_S3TC: + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + ri.Printf( PRINT_ALL, "S3TC " ); + break; + case GL_RGBA4: + ri.Printf( PRINT_ALL, "RGBA4" ); + break; + case GL_RGB5: + ri.Printf( PRINT_ALL, "RGB5 " ); + break; + default: + ri.Printf( PRINT_ALL, "???? " ); + } + + switch ( image->wrapClampMode ) { + case GL_REPEAT: + ri.Printf( PRINT_ALL, "rept " ); + break; + case GL_CLAMP_TO_EDGE: + ri.Printf( PRINT_ALL, "clmp " ); + break; + default: + ri.Printf( PRINT_ALL, "%4i ", image->wrapClampMode ); + break; + } + + ri.Printf( PRINT_ALL, " %s\n", image->imgName ); + } + ri.Printf (PRINT_ALL, " ---------\n"); + ri.Printf (PRINT_ALL, " %i total texels (not including mipmaps)\n", texels); + ri.Printf (PRINT_ALL, " %i total images\n\n", tr.numImages ); +} + +//======================================================================= + +/* +================ +ResampleTexture + +Used to resample images in a more general than quartering fashion. + +This will only be filtered properly if the resampled size +is greater than half the original size. + +If a larger shrinking is needed, use the mipmap function +before or after. +================ +*/ +static void ResampleTexture( unsigned *in, int inwidth, int inheight, unsigned *out, + int outwidth, int outheight ) { + int i, j; + unsigned *inrow, *inrow2; + unsigned frac, fracstep; + unsigned p1[2048], p2[2048]; + byte *pix1, *pix2, *pix3, *pix4; + + if (outwidth>2048) + ri.Error(ERR_DROP, "ResampleTexture: max width"); + + fracstep = inwidth*0x10000/outwidth; + + frac = fracstep>>2; + for ( i=0 ; i>16); + frac += fracstep; + } + frac = 3*(fracstep>>2); + for ( i=0 ; i>16); + frac += fracstep; + } + + for (i=0 ; i> 1; + for (j=0 ; j>2; + ((byte *)(out+j))[1] = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2; + ((byte *)(out+j))[2] = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2; + ((byte *)(out+j))[3] = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2; + } + } +} + +/* +================ +R_LightScaleTexture + +Scale up the pixel values in a texture to increase the +lighting range +================ +*/ +void R_LightScaleTexture (unsigned *in, int inwidth, int inheight, qboolean only_gamma ) +{ + if ( only_gamma ) + { + if ( !glConfig.deviceSupportsGamma ) + { + int i, c; + byte *p; + + p = (byte *)in; + + c = inwidth*inheight; + for (i=0 ; i> 1; + outHeight = inHeight >> 1; + temp = ri.Hunk_AllocateTempMemory( outWidth * outHeight * 4 ); + + inWidthMask = inWidth - 1; + inHeightMask = inHeight - 1; + + for ( i = 0 ; i < outHeight ; i++ ) { + for ( j = 0 ; j < outWidth ; j++ ) { + outpix = (byte *) ( temp + i * outWidth + j ); + for ( k = 0 ; k < 4 ; k++ ) { + total = + 1 * ((byte *)&in[ ((i*2-1)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask) ])[k] + + 2 * ((byte *)&in[ ((i*2-1)&inHeightMask)*inWidth + ((j*2)&inWidthMask) ])[k] + + 2 * ((byte *)&in[ ((i*2-1)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask) ])[k] + + 1 * ((byte *)&in[ ((i*2-1)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask) ])[k] + + + 2 * ((byte *)&in[ ((i*2)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask) ])[k] + + 4 * ((byte *)&in[ ((i*2)&inHeightMask)*inWidth + ((j*2)&inWidthMask) ])[k] + + 4 * ((byte *)&in[ ((i*2)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask) ])[k] + + 2 * ((byte *)&in[ ((i*2)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask) ])[k] + + + 2 * ((byte *)&in[ ((i*2+1)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask) ])[k] + + 4 * ((byte *)&in[ ((i*2+1)&inHeightMask)*inWidth + ((j*2)&inWidthMask) ])[k] + + 4 * ((byte *)&in[ ((i*2+1)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask) ])[k] + + 2 * ((byte *)&in[ ((i*2+1)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask) ])[k] + + + 1 * ((byte *)&in[ ((i*2+2)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask) ])[k] + + 2 * ((byte *)&in[ ((i*2+2)&inHeightMask)*inWidth + ((j*2)&inWidthMask) ])[k] + + 2 * ((byte *)&in[ ((i*2+2)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask) ])[k] + + 1 * ((byte *)&in[ ((i*2+2)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask) ])[k]; + outpix[k] = total / 36; + } + } + } + + Com_Memcpy( in, temp, outWidth * outHeight * 4 ); + ri.Hunk_FreeTempMemory( temp ); +} + +/* +================ +R_MipMap + +Operates in place, quartering the size of the texture +================ +*/ +static void R_MipMap (byte *in, int width, int height) { + int i, j; + byte *out; + int row; + + if ( !r_simpleMipMaps->integer ) { + R_MipMap2( (unsigned *)in, width, height ); + return; + } + + if ( width == 1 && height == 1 ) { + return; + } + + row = width * 4; + out = in; + width >>= 1; + height >>= 1; + + if ( width == 0 || height == 0 ) { + width += height; // get largest + for (i=0 ; i>1; + out[1] = ( in[1] + in[5] )>>1; + out[2] = ( in[2] + in[6] )>>1; + out[3] = ( in[3] + in[7] )>>1; + } + return; + } + + for (i=0 ; i>2; + out[1] = (in[1] + in[5] + in[row+1] + in[row+5])>>2; + out[2] = (in[2] + in[6] + in[row+2] + in[row+6])>>2; + out[3] = (in[3] + in[7] + in[row+3] + in[row+7])>>2; + } + } +} + + +/* +================== +R_BlendOverTexture + +Apply a color blend over a set of pixels +================== +*/ +static void R_BlendOverTexture( byte *data, int pixelCount, byte blend[4] ) { + int i; + int inverseAlpha; + int premult[3]; + + inverseAlpha = 255 - blend[3]; + premult[0] = blend[0] * blend[3]; + premult[1] = blend[1] * blend[3]; + premult[2] = blend[2] * blend[3]; + + for ( i = 0 ; i < pixelCount ; i++, data+=4 ) { + data[0] = ( data[0] * inverseAlpha + premult[0] ) >> 9; + data[1] = ( data[1] * inverseAlpha + premult[1] ) >> 9; + data[2] = ( data[2] * inverseAlpha + premult[2] ) >> 9; + } +} + +byte mipBlendColors[16][4] = { + {0,0,0,0}, + {255,0,0,128}, + {0,255,0,128}, + {0,0,255,128}, + {255,0,0,128}, + {0,255,0,128}, + {0,0,255,128}, + {255,0,0,128}, + {0,255,0,128}, + {0,0,255,128}, + {255,0,0,128}, + {0,255,0,128}, + {0,0,255,128}, + {255,0,0,128}, + {0,255,0,128}, + {0,0,255,128}, +}; + + +/* +=============== +Upload32 + +=============== +*/ +extern qboolean charSet; +static void Upload32( unsigned *data, + int width, int height, + qboolean mipmap, + qboolean picmip, + qboolean lightMap, + int *format, + int *pUploadWidth, int *pUploadHeight ) +{ + int samples; + unsigned *scaledBuffer = NULL; + unsigned *resampledBuffer = NULL; + int scaled_width, scaled_height; + int i, c; + byte *scan; + GLenum internalFormat = GL_RGB; + float rMax = 0, gMax = 0, bMax = 0; + + // + // convert to exact power of 2 sizes + // + for (scaled_width = 1 ; scaled_width < width ; scaled_width<<=1) + ; + for (scaled_height = 1 ; scaled_height < height ; scaled_height<<=1) + ; + if ( r_roundImagesDown->integer && scaled_width > width ) + scaled_width >>= 1; + if ( r_roundImagesDown->integer && scaled_height > height ) + scaled_height >>= 1; + + if ( scaled_width != width || scaled_height != height ) { + resampledBuffer = ri.Hunk_AllocateTempMemory( scaled_width * scaled_height * 4 ); + ResampleTexture (data, width, height, resampledBuffer, scaled_width, scaled_height); + data = resampledBuffer; + width = scaled_width; + height = scaled_height; + } + + // + // perform optional picmip operation + // + if ( picmip ) { + scaled_width >>= r_picmip->integer; + scaled_height >>= r_picmip->integer; + } + + // + // clamp to minimum size + // + if (scaled_width < 1) { + scaled_width = 1; + } + if (scaled_height < 1) { + scaled_height = 1; + } + + // + // clamp to the current upper OpenGL limit + // scale both axis down equally so we don't have to + // deal with a half mip resampling + // + while ( scaled_width > glConfig.maxTextureSize + || scaled_height > glConfig.maxTextureSize ) { + scaled_width >>= 1; + scaled_height >>= 1; + } + + scaledBuffer = ri.Hunk_AllocateTempMemory( sizeof( unsigned ) * scaled_width * scaled_height ); + + // + // scan the texture for each channel's max values + // and verify if the alpha channel is being used or not + // + c = width*height; + scan = ((byte *)data); + samples = 3; + + if( r_greyscale->integer ) + { + for ( i = 0; i < c; i++ ) + { + byte luma = LUMA(scan[i*4], scan[i*4 + 1], scan[i*4 + 2]); + scan[i*4] = luma; + scan[i*4 + 1] = luma; + scan[i*4 + 2] = luma; + } + } + else if( r_greyscale->value ) + { + for ( i = 0; i < c; i++ ) + { + float luma = LUMA(scan[i*4], scan[i*4 + 1], scan[i*4 + 2]); + scan[i*4] = LERP(scan[i*4], luma, r_greyscale->value); + scan[i*4 + 1] = LERP(scan[i*4 + 1], luma, r_greyscale->value); + scan[i*4 + 2] = LERP(scan[i*4 + 2], luma, r_greyscale->value); + } + } + + if(lightMap) + { + if(r_greyscale->integer) + internalFormat = GL_LUMINANCE; + else + internalFormat = GL_RGB; + } + else + { + for ( i = 0; i < c; i++ ) + { + if ( scan[i*4+0] > rMax ) + { + rMax = scan[i*4+0]; + } + if ( scan[i*4+1] > gMax ) + { + gMax = scan[i*4+1]; + } + if ( scan[i*4+2] > bMax ) + { + bMax = scan[i*4+2]; + } + if ( scan[i*4 + 3] != 255 ) + { + samples = 4; + break; + } + } + // select proper internal format + if ( samples == 3 ) + { + if(r_greyscale->integer) + { + if(r_texturebits->integer == 16) + internalFormat = GL_LUMINANCE8; + else if(r_texturebits->integer == 32) + internalFormat = GL_LUMINANCE16; + else + internalFormat = GL_LUMINANCE; + } + else + { + if ( glConfig.textureCompression == TC_S3TC_ARB ) + { + internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + } + else if ( glConfig.textureCompression == TC_S3TC ) + { + internalFormat = GL_RGB4_S3TC; + } + else if ( r_texturebits->integer == 16 ) + { + internalFormat = GL_RGB5; + } + else if ( r_texturebits->integer == 32 ) + { + internalFormat = GL_RGB8; + } + else + { + internalFormat = GL_RGB; + } + } + } + else if ( samples == 4 ) + { + if(r_greyscale->integer) + { + if(r_texturebits->integer == 16) + internalFormat = GL_LUMINANCE8_ALPHA8; + else if(r_texturebits->integer == 32) + internalFormat = GL_LUMINANCE16_ALPHA16; + else + internalFormat = GL_LUMINANCE_ALPHA; + } + else + { + if ( r_texturebits->integer == 16 ) + { + internalFormat = GL_RGBA4; + } + else if ( r_texturebits->integer == 32 ) + { + internalFormat = GL_RGBA8; + } + else + { + internalFormat = GL_RGBA; + } + } + } + } + + // copy or resample data as appropriate for first MIP level + if ( ( scaled_width == width ) && + ( scaled_height == height ) ) { + if (!mipmap) + { + qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + *pUploadWidth = scaled_width; + *pUploadHeight = scaled_height; + *format = internalFormat; + + goto done; + } + Com_Memcpy (scaledBuffer, data, width*height*4); + } + else + { + // use the normal mip-mapping function to go down from here + while ( width > scaled_width || height > scaled_height ) { + R_MipMap( (byte *)data, width, height ); + width >>= 1; + height >>= 1; + if ( width < 1 ) { + width = 1; + } + if ( height < 1 ) { + height = 1; + } + } + Com_Memcpy( scaledBuffer, data, width * height * 4 ); + } + + R_LightScaleTexture (scaledBuffer, scaled_width, scaled_height, !mipmap ); + + *pUploadWidth = scaled_width; + *pUploadHeight = scaled_height; + *format = internalFormat; + + qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer ); + + if (mipmap) + { + int miplevel; + + miplevel = 0; + while (scaled_width > 1 || scaled_height > 1) + { + R_MipMap( (byte *)scaledBuffer, scaled_width, scaled_height ); + scaled_width >>= 1; + scaled_height >>= 1; + if (scaled_width < 1) + scaled_width = 1; + if (scaled_height < 1) + scaled_height = 1; + miplevel++; + + if ( r_colorMipLevels->integer ) { + R_BlendOverTexture( (byte *)scaledBuffer, scaled_width * scaled_height, mipBlendColors[miplevel] ); + } + + qglTexImage2D (GL_TEXTURE_2D, miplevel, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, scaledBuffer ); + } + } +done: + + if (mipmap) + { + if ( glConfig.textureFilterAnisotropic ) + qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, + (GLint)Com_Clamp( 1, glConfig.maxAnisotropy, r_ext_max_anisotropy->integer ) ); + + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + else + { + if ( glConfig.textureFilterAnisotropic ) + qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 ); + + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + } + + GL_CheckErrors(); + + if ( scaledBuffer != 0 ) + ri.Hunk_FreeTempMemory( scaledBuffer ); + if ( resampledBuffer != 0 ) + ri.Hunk_FreeTempMemory( resampledBuffer ); +} + + +/* +================ +R_CreateImage + +This is the only way any image_t are created +================ +*/ +image_t *R_CreateImage( const char *name, const byte *pic, int width, int height, + qboolean mipmap, qboolean allowPicmip, int glWrapClampMode ) { + image_t *image; + qboolean isLightmap = qfalse; + long hash; + + if (strlen(name) >= MAX_QPATH ) { + ri.Error (ERR_DROP, "R_CreateImage: \"%s\" is too long", name); + } + if ( !strncmp( name, "*lightmap", 9 ) ) { + isLightmap = qtrue; + } + + if ( tr.numImages == MAX_DRAWIMAGES ) { + ri.Error( ERR_DROP, "R_CreateImage: MAX_DRAWIMAGES hit"); + } + + image = tr.images[tr.numImages] = ri.Hunk_Alloc( sizeof( image_t ), h_low ); + image->texnum = 1024 + tr.numImages; + tr.numImages++; + + image->mipmap = mipmap; + image->allowPicmip = allowPicmip; + + strcpy (image->imgName, name); + + image->width = width; + image->height = height; + image->wrapClampMode = glWrapClampMode; + + // lightmaps are always allocated on TMU 1 + if ( qglActiveTextureARB && isLightmap ) { + image->TMU = 1; + } else { + image->TMU = 0; + } + + if ( qglActiveTextureARB ) { + GL_SelectTexture( image->TMU ); + } + + GL_Bind(image); + + Upload32( (unsigned *)pic, image->width, image->height, + image->mipmap, + allowPicmip, + isLightmap, + &image->internalFormat, + &image->uploadWidth, + &image->uploadHeight ); + + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrapClampMode ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrapClampMode ); + + qglBindTexture( GL_TEXTURE_2D, 0 ); + + if ( image->TMU == 1 ) { + GL_SelectTexture( 0 ); + } + + hash = generateHashValue(name); + image->next = hashTable[hash]; + hashTable[hash] = image; + + return image; +} + +//=================================================================== + +typedef struct +{ + char *ext; + void (*ImageLoader)( const char *, unsigned char **, int *, int * ); +} imageExtToLoaderMap_t; + +// Note that the ordering indicates the order of preference used +// when there are multiple images of different formats available +static imageExtToLoaderMap_t imageLoaders[ ] = +{ + { "tga", R_LoadTGA }, + { "jpg", R_LoadJPG }, + { "jpeg", R_LoadJPG }, + { "png", R_LoadPNG }, + { "pcx", R_LoadPCX }, + { "bmp", R_LoadBMP } +}; + +static int numImageLoaders = ARRAY_LEN( imageLoaders ); + +/* +================= +R_LoadImage + +Loads any of the supported image types into a cannonical +32 bit format. +================= +*/ +void R_LoadImage( const char *name, byte **pic, int *width, int *height ) +{ + qboolean orgNameFailed = qfalse; + int orgLoader = -1; + int i; + char localName[ MAX_QPATH ]; + const char *ext; + char *altName; + + *pic = NULL; + *width = 0; + *height = 0; + + Q_strncpyz( localName, name, MAX_QPATH ); + + ext = COM_GetExtension( localName ); + + if( *ext ) + { + // Look for the correct loader and use it + for( i = 0; i < numImageLoaders; i++ ) + { + if( !Q_stricmp( ext, imageLoaders[ i ].ext ) ) + { + // Load + imageLoaders[ i ].ImageLoader( localName, pic, width, height ); + break; + } + } + + // A loader was found + if( i < numImageLoaders ) + { + if( *pic == NULL ) + { + // Loader failed, most likely because the file isn't there; + // try again without the extension + orgNameFailed = qtrue; + orgLoader = i; + COM_StripExtension( name, localName, MAX_QPATH ); + } + else + { + // Something loaded + return; + } + } + } + + // Try and find a suitable match using all + // the image formats supported + for( i = 0; i < numImageLoaders; i++ ) + { + if (i == orgLoader) + continue; + + altName = va( "%s.%s", localName, imageLoaders[ i ].ext ); + + // Load + imageLoaders[ i ].ImageLoader( altName, pic, width, height ); + + if( *pic ) + { + if( orgNameFailed ) + { + ri.Printf( PRINT_DEVELOPER, "WARNING: %s not present, using %s instead\n", + name, altName ); + } + + break; + } + } +} + + +/* +=============== +R_FindImageFile + +Finds or loads the given image. +Returns NULL if it fails, not a default image. +============== +*/ +image_t *R_FindImageFile( const char *name, qboolean mipmap, qboolean allowPicmip, int glWrapClampMode ) { + image_t *image; + int width, height; + byte *pic; + long hash; + + if (!name) { + return NULL; + } + + hash = generateHashValue(name); + + // + // see if the image is already loaded + // + for (image=hashTable[hash]; image; image=image->next) { + if ( !strcmp( name, image->imgName ) ) { + // the white image can be used with any set of parms, but other mismatches are errors + if ( strcmp( name, "*white" ) ) { + if ( image->mipmap != mipmap ) { + ri.Printf( PRINT_DEVELOPER, "WARNING: reused image %s with mixed mipmap parm\n", name ); + } + if ( image->allowPicmip != allowPicmip ) { + ri.Printf( PRINT_DEVELOPER, "WARNING: reused image %s with mixed allowPicmip parm\n", name ); + } + if ( image->wrapClampMode != glWrapClampMode ) { + ri.Printf( PRINT_ALL, "WARNING: reused image %s with mixed glWrapClampMode parm\n", name ); + } + } + return image; + } + } + + // + // load the pic from disk + // + R_LoadImage( name, &pic, &width, &height ); + if ( pic == NULL ) { + return NULL; + } + + image = R_CreateImage( ( char * ) name, pic, width, height, mipmap, allowPicmip, glWrapClampMode ); + ri.Free( pic ); + return image; +} + + +/* +================ +R_CreateDlightImage +================ +*/ +#define DLIGHT_SIZE 16 +static void R_CreateDlightImage( void ) { + int x,y; + byte data[DLIGHT_SIZE][DLIGHT_SIZE][4]; + int b; + + // make a centered inverse-square falloff blob for dynamic lighting + for (x=0 ; x 255) { + b = 255; + } else if ( b < 75 ) { + b = 0; + } + data[y][x][0] = + data[y][x][1] = + data[y][x][2] = b; + data[y][x][3] = 255; + } + } + tr.dlightImage = R_CreateImage("*dlight", (byte *)data, DLIGHT_SIZE, DLIGHT_SIZE, qfalse, qfalse, GL_CLAMP_TO_EDGE ); +} + + +/* +================= +R_InitFogTable +================= +*/ +void R_InitFogTable( void ) { + int i; + float d; + float exp; + + exp = 0.5; + + for ( i = 0 ; i < FOG_TABLE_SIZE ; i++ ) { + d = pow ( (float)i/(FOG_TABLE_SIZE-1), exp ); + + tr.fogTable[i] = d; + } +} + +/* +================ +R_FogFactor + +Returns a 0.0 to 1.0 fog density value +This is called for each texel of the fog texture on startup +and for each vertex of transparent shaders in fog dynamically +================ +*/ +float R_FogFactor( float s, float t ) { + float d; + + s -= 1.0/512; + if ( s < 0 ) { + return 0; + } + if ( t < 1.0/32 ) { + return 0; + } + if ( t < 31.0/32 ) { + s *= (t - 1.0f/32.0f) / (30.0f/32.0f); + } + + // we need to leave a lot of clamp range + s *= 8; + + if ( s > 1.0 ) { + s = 1.0; + } + + d = tr.fogTable[ (int)(s * (FOG_TABLE_SIZE-1)) ]; + + return d; +} + +/* +================ +R_CreateFogImage +================ +*/ +#define FOG_S 256 +#define FOG_T 32 +static void R_CreateFogImage( void ) { + int x,y; + byte *data; + float d; + float borderColor[4]; + + data = ri.Hunk_AllocateTempMemory( FOG_S * FOG_T * 4 ); + + // S is distance, T is depth + for (x=0 ; xinteger; + if ( !glConfig.deviceSupportsGamma ) { + tr.overbrightBits = 0; // need hardware gamma for overbright + } + + // never overbright in windowed mode + if ( !glConfig.isFullscreen ) + { + tr.overbrightBits = 0; + } + + // allow 2 overbright bits in 24 bit, but only 1 in 16 bit + if ( glConfig.colorBits > 16 ) { + if ( tr.overbrightBits > 2 ) { + tr.overbrightBits = 2; + } + } else { + if ( tr.overbrightBits > 1 ) { + tr.overbrightBits = 1; + } + } + if ( tr.overbrightBits < 0 ) { + tr.overbrightBits = 0; + } + + tr.identityLight = 1.0f / ( 1 << tr.overbrightBits ); + tr.identityLightByte = 255 * tr.identityLight; + + + if ( r_intensity->value <= 1 ) { + ri.Cvar_Set( "r_intensity", "1" ); + } + + if ( r_gamma->value < 0.5f ) { + ri.Cvar_Set( "r_gamma", "0.5" ); + } else if ( r_gamma->value > 3.0f ) { + ri.Cvar_Set( "r_gamma", "3.0" ); + } + + g = r_gamma->value; + + shift = tr.overbrightBits; + + for ( i = 0; i < 256; i++ ) { + if ( g == 1 ) { + inf = i; + } else { + inf = 255 * pow ( i/255.0f, 1.0f / g ) + 0.5f; + } + inf <<= shift; + if (inf < 0) { + inf = 0; + } + if (inf > 255) { + inf = 255; + } + s_gammatable[i] = inf; + } + + for (i=0 ; i<256 ; i++) { + j = i * r_intensity->value; + if (j > 255) { + j = 255; + } + s_intensitytable[i] = j; + } + + if ( glConfig.deviceSupportsGamma ) + { + GLimp_SetGamma( s_gammatable, s_gammatable, s_gammatable ); + } +} + +/* +=============== +R_InitImages +=============== +*/ +void R_InitImages( void ) { + Com_Memset(hashTable, 0, sizeof(hashTable)); + // build brightness translation tables + R_SetColorMappings(); + + // create default texture and white texture + R_CreateBuiltinImages(); +} + +/* +=============== +R_DeleteTextures +=============== +*/ +void R_DeleteTextures( void ) { + int i; + + for ( i=0; itexnum ); + } + Com_Memset( tr.images, 0, sizeof( tr.images ) ); + + tr.numImages = 0; + + Com_Memset( glState.currenttextures, 0, sizeof( glState.currenttextures ) ); + if ( qglActiveTextureARB ) { + GL_SelectTexture( 1 ); + qglBindTexture( GL_TEXTURE_2D, 0 ); + GL_SelectTexture( 0 ); + qglBindTexture( GL_TEXTURE_2D, 0 ); + } else { + qglBindTexture( GL_TEXTURE_2D, 0 ); + } +} + +/* +============================================================================ + +SKINS + +============================================================================ +*/ + +/* +================== +CommaParse + +This is unfortunate, but the skin files aren't +compatable with our normal parsing rules. +================== +*/ +static char *CommaParse( char **data_p ) { + int c = 0, len; + char *data; + static char com_token[MAX_TOKEN_CHARS]; + + data = *data_p; + len = 0; + com_token[0] = 0; + + // make sure incoming data is valid + if ( !data ) { + *data_p = NULL; + return com_token; + } + + while ( 1 ) { + // skip whitespace + while( (c = *data) <= ' ') { + if( !c ) { + break; + } + data++; + } + + + c = *data; + + // skip double slash comments + if ( c == '/' && data[1] == '/' ) + { + while (*data && *data != '\n') + data++; + } + // skip /* */ comments + else if ( c=='/' && data[1] == '*' ) + { + while ( *data && ( *data != '*' || data[1] != '/' ) ) + { + data++; + } + if ( *data ) + { + data += 2; + } + } + else + { + break; + } + } + + if ( c == 0 ) { + return ""; + } + + // handle quoted strings + if (c == '\"') + { + data++; + while (1) + { + c = *data++; + if (c=='\"' || !c) + { + com_token[len] = 0; + *data_p = ( char * ) data; + return com_token; + } + if (len < MAX_TOKEN_CHARS) + { + com_token[len] = c; + len++; + } + } + } + + // parse a regular word + do + { + if (len < MAX_TOKEN_CHARS) + { + com_token[len] = c; + len++; + } + data++; + c = *data; + } while (c>32 && c != ',' ); + + if (len == MAX_TOKEN_CHARS) + { +// ri.Printf (PRINT_DEVELOPER, "Token exceeded %i chars, discarded.\n", MAX_TOKEN_CHARS); + len = 0; + } + com_token[len] = 0; + + *data_p = ( char * ) data; + return com_token; +} + + +/* +=============== +RE_RegisterSkin + +=============== +*/ +qhandle_t RE_RegisterSkin( const char *name ) { + qhandle_t hSkin; + skin_t *skin; + skinSurface_t *surf; + union { + char *c; + void *v; + } text; + char *text_p; + char *token; + char surfName[MAX_QPATH]; + + if ( !name || !name[0] ) { + ri.Printf( PRINT_DEVELOPER, "Empty name passed to RE_RegisterSkin\n" ); + return 0; + } + + if ( strlen( name ) >= MAX_QPATH ) { + ri.Printf( PRINT_DEVELOPER, "Skin name exceeds MAX_QPATH\n" ); + return 0; + } + + + // see if the skin is already loaded + for ( hSkin = 1; hSkin < tr.numSkins ; hSkin++ ) { + skin = tr.skins[hSkin]; + if ( !Q_stricmp( skin->name, name ) ) { + if( skin->numSurfaces == 0 ) { + return 0; // default skin + } + return hSkin; + } + } + + // allocate a new skin + if ( tr.numSkins == MAX_SKINS ) { + ri.Printf( PRINT_WARNING, "WARNING: RE_RegisterSkin( '%s' ) MAX_SKINS hit\n", name ); + return 0; + } + tr.numSkins++; + skin = ri.Hunk_Alloc( sizeof( skin_t ), h_low ); + tr.skins[hSkin] = skin; + Q_strncpyz( skin->name, name, sizeof( skin->name ) ); + skin->numSurfaces = 0; + + R_IssuePendingRenderCommands(); + + // If not a .skin file, load as a single shader + if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) { + skin->numSurfaces = 1; + skin->surfaces[0] = ri.Hunk_Alloc( sizeof(skin->surfaces[0]), h_low ); + skin->surfaces[0]->shader = R_FindShader( name, LIGHTMAP_NONE, qtrue ); + return hSkin; + } + + // load and parse the skin file + ri.FS_ReadFile( name, &text.v ); + if ( !text.c ) { + return 0; + } + + text_p = text.c; + while ( text_p && *text_p ) { + // get surface name + token = CommaParse( &text_p ); + Q_strncpyz( surfName, token, sizeof( surfName ) ); + + if ( !token[0] ) { + break; + } + // lowercase the surface name so skin compares are faster + Q_strlwr( surfName ); + + if ( *text_p == ',' ) { + text_p++; + } + + if ( strstr( token, "tag_" ) ) { + continue; + } + + // parse the shader name + token = CommaParse( &text_p ); + + surf = skin->surfaces[ skin->numSurfaces ] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); + Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); + surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue ); + skin->numSurfaces++; + } + + ri.FS_FreeFile( text.v ); + + + // never let a skin have 0 shaders + if ( skin->numSurfaces == 0 ) { + return 0; // use default skin + } + + return hSkin; +} + + +/* +=============== +R_InitSkins +=============== +*/ +void R_InitSkins( void ) { + skin_t *skin; + + tr.numSkins = 1; + + // make the default skin have all default shaders + skin = tr.skins[0] = ri.Hunk_Alloc( sizeof( skin_t ), h_low ); + Q_strncpyz( skin->name, "", sizeof( skin->name ) ); + skin->numSurfaces = 1; + skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces ), h_low ); + skin->surfaces[0]->shader = tr.defaultShader; +} + +/* +=============== +R_GetSkinByHandle +=============== +*/ +skin_t *R_GetSkinByHandle( qhandle_t hSkin ) { + if ( hSkin < 1 || hSkin >= tr.numSkins ) { + return tr.skins[0]; + } + return tr.skins[ hSkin ]; +} + +/* +=============== +R_SkinList_f +=============== +*/ +void R_SkinList_f( void ) { + int i, j; + skin_t *skin; + + ri.Printf (PRINT_ALL, "------------------\n"); + + for ( i = 0 ; i < tr.numSkins ; i++ ) { + skin = tr.skins[i]; + + ri.Printf( PRINT_ALL, "%3i:%s\n", i, skin->name ); + for ( j = 0 ; j < skin->numSurfaces ; j++ ) { + ri.Printf( PRINT_ALL, " %s = %s\n", + skin->surfaces[j]->name, skin->surfaces[j]->shader->name ); + } + } + ri.Printf (PRINT_ALL, "------------------\n"); +} + diff --git a/src/renderergl1/tr_image_bmp.c b/src/renderergl1/tr_image_bmp.c new file mode 100644 index 00000000..eed62c58 --- /dev/null +++ b/src/renderergl1/tr_image_bmp.c @@ -0,0 +1,240 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "tr_local.h" + +typedef struct +{ + char id[2]; + unsigned fileSize; + unsigned reserved0; + unsigned bitmapDataOffset; + unsigned bitmapHeaderSize; + unsigned width; + unsigned height; + unsigned short planes; + unsigned short bitsPerPixel; + unsigned compression; + unsigned bitmapDataSize; + unsigned hRes; + unsigned vRes; + unsigned colors; + unsigned importantColors; + unsigned char palette[256][4]; +} BMPHeader_t; + +void R_LoadBMP( const char *name, byte **pic, int *width, int *height ) +{ + int columns, rows; + unsigned numPixels; + byte *pixbuf; + int row, column; + byte *buf_p; + byte *end; + union { + byte *b; + void *v; + } buffer; + int length; + BMPHeader_t bmpHeader; + byte *bmpRGBA; + + *pic = NULL; + + if(width) + *width = 0; + + if(height) + *height = 0; + + // + // load the file + // + length = ri.FS_ReadFile( ( char * ) name, &buffer.v); + if (!buffer.b || length < 0) { + return; + } + + if (length < 54) + { + ri.Error( ERR_DROP, "LoadBMP: header too short (%s)", name ); + } + + buf_p = buffer.b; + end = buffer.b + length; + + bmpHeader.id[0] = *buf_p++; + bmpHeader.id[1] = *buf_p++; + bmpHeader.fileSize = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.reserved0 = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.bitmapDataOffset = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.bitmapHeaderSize = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.width = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.height = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.planes = LittleShort( * ( short * ) buf_p ); + buf_p += 2; + bmpHeader.bitsPerPixel = LittleShort( * ( short * ) buf_p ); + buf_p += 2; + bmpHeader.compression = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.bitmapDataSize = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.hRes = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.vRes = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.colors = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.importantColors = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + + if ( bmpHeader.bitsPerPixel == 8 ) + { + if (buf_p + sizeof(bmpHeader.palette) > end) + ri.Error( ERR_DROP, "LoadBMP: header too short (%s)", name ); + + Com_Memcpy( bmpHeader.palette, buf_p, sizeof( bmpHeader.palette ) ); + buf_p += sizeof(bmpHeader.palette); + } + + if (buffer.b + bmpHeader.bitmapDataOffset > end) + { + ri.Error( ERR_DROP, "LoadBMP: invalid offset value in header (%s)", name ); + } + + buf_p = buffer.b + bmpHeader.bitmapDataOffset; + + if ( bmpHeader.id[0] != 'B' && bmpHeader.id[1] != 'M' ) + { + ri.Error( ERR_DROP, "LoadBMP: only Windows-style BMP files supported (%s)", name ); + } + if ( bmpHeader.fileSize != length ) + { + ri.Error( ERR_DROP, "LoadBMP: header size does not match file size (%u vs. %u) (%s)", bmpHeader.fileSize, length, name ); + } + if ( bmpHeader.compression != 0 ) + { + ri.Error( ERR_DROP, "LoadBMP: only uncompressed BMP files supported (%s)", name ); + } + if ( bmpHeader.bitsPerPixel < 8 ) + { + ri.Error( ERR_DROP, "LoadBMP: monochrome and 4-bit BMP files not supported (%s)", name ); + } + + switch ( bmpHeader.bitsPerPixel ) + { + case 8: + case 16: + case 24: + case 32: + break; + default: + ri.Error( ERR_DROP, "LoadBMP: illegal pixel_size '%hu' in file '%s'", bmpHeader.bitsPerPixel, name ); + break; + } + + columns = bmpHeader.width; + rows = bmpHeader.height; + if ( rows < 0 ) + rows = -rows; + numPixels = columns * rows; + + if(columns <= 0 || !rows || numPixels > 0x1FFFFFFF // 4*1FFFFFFF == 0x7FFFFFFC < 0x7FFFFFFF + || ((numPixels * 4) / columns) / 4 != rows) + { + ri.Error (ERR_DROP, "LoadBMP: %s has an invalid image size", name); + } + if(buf_p + numPixels*bmpHeader.bitsPerPixel/8 > end) + { + ri.Error (ERR_DROP, "LoadBMP: file truncated (%s)", name); + } + + if ( width ) + *width = columns; + if ( height ) + *height = rows; + + bmpRGBA = ri.Malloc( numPixels * 4 ); + *pic = bmpRGBA; + + + for ( row = rows-1; row >= 0; row-- ) + { + pixbuf = bmpRGBA + row*columns*4; + + for ( column = 0; column < columns; column++ ) + { + unsigned char red, green, blue, alpha; + int palIndex; + unsigned short shortPixel; + + switch ( bmpHeader.bitsPerPixel ) + { + case 8: + palIndex = *buf_p++; + *pixbuf++ = bmpHeader.palette[palIndex][2]; + *pixbuf++ = bmpHeader.palette[palIndex][1]; + *pixbuf++ = bmpHeader.palette[palIndex][0]; + *pixbuf++ = 0xff; + break; + case 16: + shortPixel = * ( unsigned short * ) pixbuf; + pixbuf += 2; + *pixbuf++ = ( shortPixel & ( 31 << 10 ) ) >> 7; + *pixbuf++ = ( shortPixel & ( 31 << 5 ) ) >> 2; + *pixbuf++ = ( shortPixel & ( 31 ) ) << 3; + *pixbuf++ = 0xff; + break; + + case 24: + blue = *buf_p++; + green = *buf_p++; + red = *buf_p++; + *pixbuf++ = red; + *pixbuf++ = green; + *pixbuf++ = blue; + *pixbuf++ = 255; + break; + case 32: + blue = *buf_p++; + green = *buf_p++; + red = *buf_p++; + alpha = *buf_p++; + *pixbuf++ = red; + *pixbuf++ = green; + *pixbuf++ = blue; + *pixbuf++ = alpha; + break; + } + } + } + + ri.FS_FreeFile( buffer.v ); + +} diff --git a/src/renderergl1/tr_image_jpg.c b/src/renderergl1/tr_image_jpg.c new file mode 100644 index 00000000..7d4a793a --- /dev/null +++ b/src/renderergl1/tr_image_jpg.c @@ -0,0 +1,438 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "tr_local.h" + +/* + * Include file for users of JPEG library. + * You will need to have included system headers that define at least + * the typedefs FILE and size_t before you can include jpeglib.h. + * (stdio.h is sufficient on ANSI-conforming systems.) + * You may also wish to include "jerror.h". + */ + +#ifdef USE_INTERNAL_JPEG +# define JPEG_INTERNALS +#endif + +#include + +#ifndef USE_INTERNAL_JPEG +# if JPEG_LIB_VERSION < 80 +# error Need system libjpeg >= 80 +# endif +#endif + +static void R_JPGErrorExit(j_common_ptr cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + + (*cinfo->err->format_message) (cinfo, buffer); + + /* Let the memory manager delete any temp files before we die */ + jpeg_destroy(cinfo); + + ri.Error(ERR_FATAL, "%s", buffer); +} + +static void R_JPGOutputMessage(j_common_ptr cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message) (cinfo, buffer); + + /* Send it to stderr, adding a newline */ + ri.Printf(PRINT_ALL, "%s\n", buffer); +} + +void R_LoadJPG(const char *filename, unsigned char **pic, int *width, int *height) +{ + /* This struct contains the JPEG decompression parameters and pointers to + * working space (which is allocated as needed by the JPEG library). + */ + struct jpeg_decompress_struct cinfo = {NULL}; + /* We use our private extension JPEG error handler. + * Note that this struct must live as long as the main JPEG parameter + * struct, to avoid dangling-pointer problems. + */ + /* This struct represents a JPEG error handler. It is declared separately + * because applications often want to supply a specialized error handler + * (see the second half of this file for an example). But here we just + * take the easy way out and use the standard error handler, which will + * print a message on stderr and call exit() if compression fails. + * Note that this struct must live as long as the main JPEG parameter + * struct, to avoid dangling-pointer problems. + */ + struct jpeg_error_mgr jerr; + /* More stuff */ + JSAMPARRAY buffer; /* Output row buffer */ + unsigned int row_stride; /* physical row width in output buffer */ + unsigned int pixelcount, memcount; + unsigned int sindex, dindex; + byte *out; + int len; + union { + byte *b; + void *v; + } fbuffer; + byte *buf; + + /* In this example we want to open the input file before doing anything else, + * so that the setjmp() error recovery below can assume the file is open. + * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that + * requires it in order to read binary files. + */ + + len = ri.FS_ReadFile ( ( char * ) filename, &fbuffer.v); + if (!fbuffer.b || len < 0) { + return; + } + + /* Step 1: allocate and initialize JPEG decompression object */ + + /* We have to set up the error handler first, in case the initialization + * step fails. (Unlikely, but it could happen if you are out of memory.) + * This routine fills in the contents of struct jerr, and returns jerr's + * address which we place into the link field in cinfo. + */ + cinfo.err = jpeg_std_error(&jerr); + cinfo.err->error_exit = R_JPGErrorExit; + cinfo.err->output_message = R_JPGOutputMessage; + + /* Now we can initialize the JPEG decompression object. */ + jpeg_create_decompress(&cinfo); + + /* Step 2: specify data source (eg, a file) */ + + jpeg_mem_src(&cinfo, fbuffer.b, len); + + /* Step 3: read file parameters with jpeg_read_header() */ + + (void) jpeg_read_header(&cinfo, TRUE); + /* We can ignore the return value from jpeg_read_header since + * (a) suspension is not possible with the stdio data source, and + * (b) we passed TRUE to reject a tables-only JPEG file as an error. + * See libjpeg.doc for more info. + */ + + /* Step 4: set parameters for decompression */ + + /* + * Make sure it always converts images to RGB color space. This will + * automatically convert 8-bit greyscale images to RGB as well. + */ + cinfo.out_color_space = JCS_RGB; + + /* Step 5: Start decompressor */ + + (void) jpeg_start_decompress(&cinfo); + /* We can ignore the return value since suspension is not possible + * with the stdio data source. + */ + + /* We may need to do some setup of our own at this point before reading + * the data. After jpeg_start_decompress() we have the correct scaled + * output image dimensions available, as well as the output colormap + * if we asked for color quantization. + * In this example, we need to make an output work buffer of the right size. + */ + /* JSAMPLEs per row in output buffer */ + + pixelcount = cinfo.output_width * cinfo.output_height; + + if(!cinfo.output_width || !cinfo.output_height + || ((pixelcount * 4) / cinfo.output_width) / 4 != cinfo.output_height + || pixelcount > 0x1FFFFFFF || cinfo.output_components != 3 + ) + { + // Free the memory to make sure we don't leak memory + ri.FS_FreeFile (fbuffer.v); + jpeg_destroy_decompress(&cinfo); + + ri.Error(ERR_DROP, "LoadJPG: %s has an invalid image format: %dx%d*4=%d, components: %d", filename, + cinfo.output_width, cinfo.output_height, pixelcount * 4, cinfo.output_components); + } + + memcount = pixelcount * 4; + row_stride = cinfo.output_width * cinfo.output_components; + + out = ri.Malloc(memcount); + + *width = cinfo.output_width; + *height = cinfo.output_height; + + /* Step 6: while (scan lines remain to be read) */ + /* jpeg_read_scanlines(...); */ + + /* Here we use the library's state variable cinfo.output_scanline as the + * loop counter, so that we don't have to keep track ourselves. + */ + while (cinfo.output_scanline < cinfo.output_height) { + /* jpeg_read_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could ask for + * more than one scanline at a time if that's more convenient. + */ + buf = ((out+(row_stride*cinfo.output_scanline))); + buffer = &buf; + (void) jpeg_read_scanlines(&cinfo, buffer, 1); + } + + buf = out; + + // Expand from RGB to RGBA + sindex = pixelcount * cinfo.output_components; + dindex = memcount; + + do + { + buf[--dindex] = 255; + buf[--dindex] = buf[--sindex]; + buf[--dindex] = buf[--sindex]; + buf[--dindex] = buf[--sindex]; + } while(sindex); + + *pic = out; + + /* Step 7: Finish decompression */ + + jpeg_finish_decompress(&cinfo); + /* We can ignore the return value since suspension is not possible + * with the stdio data source. + */ + + /* Step 8: Release JPEG decompression object */ + + /* This is an important step since it will release a good deal of memory. */ + jpeg_destroy_decompress(&cinfo); + + /* After finish_decompress, we can close the input file. + * Here we postpone it until after no more JPEG errors are possible, + * so as to simplify the setjmp error logic above. (Actually, I don't + * think that jpeg_destroy can do an error exit, but why assume anything...) + */ + ri.FS_FreeFile (fbuffer.v); + + /* At this point you may want to check to see whether any corrupt-data + * warnings occurred (test whether jerr.pub.num_warnings is nonzero). + */ + + /* And we're done! */ +} + + +/* Expanded data destination object for stdio output */ + +typedef struct { + struct jpeg_destination_mgr pub; /* public fields */ + + byte* outfile; /* target stream */ + int size; +} my_destination_mgr; + +typedef my_destination_mgr * my_dest_ptr; + + +/* + * Initialize destination --- called by jpeg_start_compress + * before any data is actually written. + */ + +static void +init_destination (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + dest->pub.next_output_byte = dest->outfile; + dest->pub.free_in_buffer = dest->size; +} + + +/* + * Empty the output buffer --- called whenever buffer fills up. + * + * In typical applications, this should write the entire output buffer + * (ignoring the current state of next_output_byte & free_in_buffer), + * reset the pointer & count to the start of the buffer, and return TRUE + * indicating that the buffer has been dumped. + * + * In applications that need to be able to suspend compression due to output + * overrun, a FALSE return indicates that the buffer cannot be emptied now. + * In this situation, the compressor will return to its caller (possibly with + * an indication that it has not accepted all the supplied scanlines). The + * application should resume compression after it has made more room in the + * output buffer. Note that there are substantial restrictions on the use of + * suspension --- see the documentation. + * + * When suspending, the compressor will back up to a convenient restart point + * (typically the start of the current MCU). next_output_byte & free_in_buffer + * indicate where the restart point will be if the current call returns FALSE. + * Data beyond this point will be regenerated after resumption, so do not + * write it out when emptying the buffer externally. + */ + +static boolean +empty_output_buffer (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + jpeg_destroy_compress(cinfo); + + // Make crash fatal or we would probably leak memory. + ri.Error(ERR_FATAL, "Output buffer for encoded JPEG image has insufficient size of %d bytes", + dest->size); + + return FALSE; +} + +/* + * Terminate destination --- called by jpeg_finish_compress + * after all data has been written. Usually needs to flush buffer. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +static void term_destination(j_compress_ptr cinfo) +{ +} + + +/* + * Prepare for output to a stdio stream. + * The caller must have already opened the stream, and is responsible + * for closing it after finishing compression. + */ + +static void +jpegDest (j_compress_ptr cinfo, byte* outfile, int size) +{ + my_dest_ptr dest; + + /* The destination object is made permanent so that multiple JPEG images + * can be written to the same file without re-executing jpeg_stdio_dest. + * This makes it dangerous to use this manager and a different destination + * manager serially with the same JPEG object, because their private object + * sizes may be different. Caveat programmer. + */ + if (cinfo->dest == NULL) { /* first time for this JPEG object? */ + cinfo->dest = (struct jpeg_destination_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + sizeof(my_destination_mgr)); + } + + dest = (my_dest_ptr) cinfo->dest; + dest->pub.init_destination = init_destination; + dest->pub.empty_output_buffer = empty_output_buffer; + dest->pub.term_destination = term_destination; + dest->outfile = outfile; + dest->size = size; +} + +/* +================= +SaveJPGToBuffer + +Encodes JPEG from image in image_buffer and writes to buffer. +Expects RGB input data +================= +*/ +size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality, + int image_width, int image_height, byte *image_buffer, int padding) +{ + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ + my_dest_ptr dest; + int row_stride; /* physical row width in image buffer */ + size_t outcount; + + /* Step 1: allocate and initialize JPEG compression object */ + cinfo.err = jpeg_std_error(&jerr); + cinfo.err->error_exit = R_JPGErrorExit; + cinfo.err->output_message = R_JPGOutputMessage; + + /* Now we can initialize the JPEG compression object. */ + jpeg_create_compress(&cinfo); + + /* Step 2: specify data destination (eg, a file) */ + /* Note: steps 2 and 3 can be done in either order. */ + jpegDest(&cinfo, buffer, bufSize); + + /* Step 3: set parameters for compression */ + cinfo.image_width = image_width; /* image width and height, in pixels */ + cinfo.image_height = image_height; + cinfo.input_components = 3; /* # of color components per pixel */ + cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); + /* If quality is set high, disable chroma subsampling */ + if (quality >= 85) { + cinfo.comp_info[0].h_samp_factor = 1; + cinfo.comp_info[0].v_samp_factor = 1; + } + + /* Step 4: Start compressor */ + jpeg_start_compress(&cinfo, TRUE); + + /* Step 5: while (scan lines remain to be written) */ + /* jpeg_write_scanlines(...); */ + row_stride = image_width * cinfo.input_components + padding; /* JSAMPLEs per row in image_buffer */ + + while (cinfo.next_scanline < cinfo.image_height) { + /* jpeg_write_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could pass + * more than one scanline at a time if that's more convenient. + */ + row_pointer[0] = &image_buffer[((cinfo.image_height-1)*row_stride)-cinfo.next_scanline * row_stride]; + (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + + /* Step 6: Finish compression */ + jpeg_finish_compress(&cinfo); + + dest = (my_dest_ptr) cinfo.dest; + outcount = dest->size - dest->pub.free_in_buffer; + + /* Step 7: release JPEG compression object */ + jpeg_destroy_compress(&cinfo); + + /* And we're done! */ + return outcount; +} + +void RE_SaveJPG(char * filename, int quality, int image_width, int image_height, byte *image_buffer, int padding) +{ + byte *out; + size_t bufSize; + + bufSize = image_width * image_height * 3; + out = ri.Hunk_AllocateTempMemory(bufSize); + + bufSize = RE_SaveJPGToBuffer(out, bufSize, quality, image_width, image_height, image_buffer, padding); + ri.FS_WriteFile(filename, out, bufSize); + + ri.Hunk_FreeTempMemory(out); +} diff --git a/src/renderergl1/tr_image_pcx.c b/src/renderergl1/tr_image_pcx.c new file mode 100644 index 00000000..8837d5b7 --- /dev/null +++ b/src/renderergl1/tr_image_pcx.c @@ -0,0 +1,176 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + 2008 Ludwig Nussel +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "tr_local.h" + +/* +======================================================================== + +PCX files are used for 8 bit images + +======================================================================== +*/ + +typedef struct { + char manufacturer; + char version; + char encoding; + char bits_per_pixel; + unsigned short xmin,ymin,xmax,ymax; + unsigned short hres,vres; + unsigned char palette[48]; + char reserved; + char color_planes; + unsigned short bytes_per_line; + unsigned short palette_type; + unsigned short hscreensize, vscreensize; + char filler[54]; + unsigned char data[]; +} pcx_t; + +void R_LoadPCX ( const char *filename, byte **pic, int *width, int *height) +{ + union { + byte *b; + void *v; + } raw; + byte *end; + pcx_t *pcx; + int len; + unsigned char dataByte = 0, runLength = 0; + byte *out, *pix; + unsigned short w, h; + byte *pic8; + byte *palette; + int i; + unsigned size = 0; + + if (width) + *width = 0; + if (height) + *height = 0; + *pic = NULL; + + // + // load the file + // + len = ri.FS_ReadFile( ( char * ) filename, &raw.v); + if (!raw.b || len < 0) { + return; + } + + if((unsigned)len < sizeof(pcx_t)) + { + ri.Printf (PRINT_ALL, "PCX truncated: %s\n", filename); + ri.FS_FreeFile (raw.v); + return; + } + + // + // parse the PCX file + // + pcx = (pcx_t *)raw.b; + end = raw.b+len; + + w = LittleShort(pcx->xmax)+1; + h = LittleShort(pcx->ymax)+1; + size = w*h; + + if (pcx->manufacturer != 0x0a + || pcx->version != 5 + || pcx->encoding != 1 + || pcx->color_planes != 1 + || pcx->bits_per_pixel != 8 + || w >= 1024 + || h >= 1024) + { + ri.Printf (PRINT_ALL, "Bad or unsupported pcx file %s (%dx%d@%d)\n", filename, w, h, pcx->bits_per_pixel); + return; + } + + pix = pic8 = ri.Malloc ( size ); + + raw.b = pcx->data; + // FIXME: should use bytes_per_line but original q3 didn't do that either + while(pix < pic8+size) + { + if(runLength > 0) { + *pix++ = dataByte; + --runLength; + continue; + } + + if(raw.b+1 > end) + break; + dataByte = *raw.b++; + + if((dataByte & 0xC0) == 0xC0) + { + if(raw.b+1 > end) + break; + runLength = dataByte & 0x3F; + dataByte = *raw.b++; + } + else + runLength = 1; + } + + if(pix < pic8+size) + { + ri.Printf (PRINT_ALL, "PCX file truncated: %s\n", filename); + ri.FS_FreeFile (pcx); + ri.Free (pic8); + } + + if (raw.b-(byte*)pcx >= end - (byte*)769 || end[-769] != 0x0c) + { + ri.Printf (PRINT_ALL, "PCX missing palette: %s\n", filename); + ri.FS_FreeFile (pcx); + ri.Free (pic8); + return; + } + + palette = end-768; + + pix = out = ri.Malloc(4 * size ); + for (i = 0 ; i < size ; i++) + { + unsigned char p = pic8[i]; + pix[0] = palette[p*3]; + pix[1] = palette[p*3 + 1]; + pix[2] = palette[p*3 + 2]; + pix[3] = 255; + pix += 4; + } + + if (width) + *width = w; + if (height) + *height = h; + + *pic = out; + + ri.FS_FreeFile (pcx); + ri.Free (pic8); +} diff --git a/src/renderergl1/tr_image_png.c b/src/renderergl1/tr_image_png.c new file mode 100644 index 00000000..b30c7fea --- /dev/null +++ b/src/renderergl1/tr_image_png.c @@ -0,0 +1,2486 @@ +/* +=========================================================================== +ioquake3 png decoder +Copyright (C) 2007,2008 Joerg Dietrich + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +=========================================================================== +*/ + +#include "tr_local.h" + +#include "../qcommon/puff.h" + +// we could limit the png size to a lower value here +#ifndef INT_MAX +#define INT_MAX 0x1fffffff +#endif + +/* +================= +PNG LOADING +================= +*/ + +/* + * Quake 3 image format : RGBA + */ + +#define Q3IMAGE_BYTESPERPIXEL (4) + +/* + * PNG specifications + */ + +/* + * The first 8 Bytes of every PNG-File are a fixed signature + * to identify the file as a PNG. + */ + +#define PNG_Signature "\x89\x50\x4E\x47\xD\xA\x1A\xA" +#define PNG_Signature_Size (8) + +/* + * After the signature diverse chunks follow. + * A chunk consists of a header and if Length + * is bigger than 0 a body and a CRC of the body follow. + */ + +struct PNG_ChunkHeader +{ + uint32_t Length; + uint32_t Type; +}; + +#define PNG_ChunkHeader_Size (8) + +typedef uint32_t PNG_ChunkCRC; + +#define PNG_ChunkCRC_Size (4) + +/* + * We use the following ChunkTypes. + * All others are ignored. + */ + +#define MAKE_CHUNKTYPE(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | ((d))) + +#define PNG_ChunkType_IHDR MAKE_CHUNKTYPE('I', 'H', 'D', 'R') +#define PNG_ChunkType_PLTE MAKE_CHUNKTYPE('P', 'L', 'T', 'E') +#define PNG_ChunkType_IDAT MAKE_CHUNKTYPE('I', 'D', 'A', 'T') +#define PNG_ChunkType_IEND MAKE_CHUNKTYPE('I', 'E', 'N', 'D') +#define PNG_ChunkType_tRNS MAKE_CHUNKTYPE('t', 'R', 'N', 'S') + +/* + * Per specification the first chunk after the signature SHALL be IHDR. + */ + +struct PNG_Chunk_IHDR +{ + uint32_t Width; + uint32_t Height; + uint8_t BitDepth; + uint8_t ColourType; + uint8_t CompressionMethod; + uint8_t FilterMethod; + uint8_t InterlaceMethod; +}; + +#define PNG_Chunk_IHDR_Size (13) + +/* + * ColourTypes + */ + +#define PNG_ColourType_Grey (0) +#define PNG_ColourType_True (2) +#define PNG_ColourType_Indexed (3) +#define PNG_ColourType_GreyAlpha (4) +#define PNG_ColourType_TrueAlpha (6) + +/* + * number of colour components + * + * Grey : 1 grey + * True : 1 R, 1 G, 1 B + * Indexed : 1 index + * GreyAlpha : 1 grey, 1 alpha + * TrueAlpha : 1 R, 1 G, 1 B, 1 alpha + */ + +#define PNG_NumColourComponents_Grey (1) +#define PNG_NumColourComponents_True (3) +#define PNG_NumColourComponents_Indexed (1) +#define PNG_NumColourComponents_GreyAlpha (2) +#define PNG_NumColourComponents_TrueAlpha (4) + +/* + * For the different ColourTypes + * different BitDepths are specified. + */ + +#define PNG_BitDepth_1 ( 1) +#define PNG_BitDepth_2 ( 2) +#define PNG_BitDepth_4 ( 4) +#define PNG_BitDepth_8 ( 8) +#define PNG_BitDepth_16 (16) + +/* + * Only one valid CompressionMethod is standardized. + */ + +#define PNG_CompressionMethod_0 (0) + +/* + * Only one valid FilterMethod is currently standardized. + */ + +#define PNG_FilterMethod_0 (0) + +/* + * This FilterMethod defines 5 FilterTypes + */ + +#define PNG_FilterType_None (0) +#define PNG_FilterType_Sub (1) +#define PNG_FilterType_Up (2) +#define PNG_FilterType_Average (3) +#define PNG_FilterType_Paeth (4) + +/* + * Two InterlaceMethods are standardized : + * 0 - NonInterlaced + * 1 - Interlaced + */ + +#define PNG_InterlaceMethod_NonInterlaced (0) +#define PNG_InterlaceMethod_Interlaced (1) + +/* + * The Adam7 interlace method uses 7 passes. + */ + +#define PNG_Adam7_NumPasses (7) + +/* + * The compressed data starts with a header ... + */ + +struct PNG_ZlibHeader +{ + uint8_t CompressionMethod; + uint8_t Flags; +}; + +#define PNG_ZlibHeader_Size (2) + +/* + * ... and is followed by a check value + */ + +#define PNG_ZlibCheckValue_Size (4) + +/* + * Some support functions for buffered files follow. + */ + +/* + * buffered file representation + */ + +struct BufferedFile +{ + byte *Buffer; + int Length; + byte *Ptr; + int BytesLeft; +}; + +/* + * Read a file into a buffer. + */ + +static struct BufferedFile *ReadBufferedFile(const char *name) +{ + struct BufferedFile *BF; + union { + byte *b; + void *v; + } buffer; + + /* + * input verification + */ + + if(!name) + { + return(NULL); + } + + /* + * Allocate control struct. + */ + + BF = ri.Malloc(sizeof(struct BufferedFile)); + if(!BF) + { + return(NULL); + } + + /* + * Initialize the structs components. + */ + + BF->Length = 0; + BF->Buffer = NULL; + BF->Ptr = NULL; + BF->BytesLeft = 0; + + /* + * Read the file. + */ + + BF->Length = ri.FS_ReadFile((char *) name, &buffer.v); + BF->Buffer = buffer.b; + + /* + * Did we get it? Is it big enough? + */ + + if(!(BF->Buffer && (BF->Length > 0))) + { + ri.Free(BF); + + return(NULL); + } + + /* + * Set the pointers and counters. + */ + + BF->Ptr = BF->Buffer; + BF->BytesLeft = BF->Length; + + return(BF); +} + +/* + * Close a buffered file. + */ + +static void CloseBufferedFile(struct BufferedFile *BF) +{ + if(BF) + { + if(BF->Buffer) + { + ri.FS_FreeFile(BF->Buffer); + } + + ri.Free(BF); + } +} + +/* + * Get a pointer to the requested bytes. + */ + +static void *BufferedFileRead(struct BufferedFile *BF, unsigned Length) +{ + void *RetVal; + + /* + * input verification + */ + + if(!(BF && Length)) + { + return(NULL); + } + + /* + * not enough bytes left + */ + + if(Length > BF->BytesLeft) + { + return(NULL); + } + + /* + * the pointer to the requested data + */ + + RetVal = BF->Ptr; + + /* + * Raise the pointer and counter. + */ + + BF->Ptr += Length; + BF->BytesLeft -= Length; + + return(RetVal); +} + +/* + * Rewind the buffer. + */ + +static qboolean BufferedFileRewind(struct BufferedFile *BF, unsigned Offset) +{ + unsigned BytesRead; + + /* + * input verification + */ + + if(!BF) + { + return(qfalse); + } + + /* + * special trick to rewind to the beginning of the buffer + */ + + if(Offset == (unsigned)-1) + { + BF->Ptr = BF->Buffer; + BF->BytesLeft = BF->Length; + + return(qtrue); + } + + /* + * How many bytes do we have already read? + */ + + BytesRead = BF->Ptr - BF->Buffer; + + /* + * We can only rewind to the beginning of the BufferedFile. + */ + + if(Offset > BytesRead) + { + return(qfalse); + } + + /* + * lower the pointer and counter. + */ + + BF->Ptr -= Offset; + BF->BytesLeft += Offset; + + return(qtrue); +} + +/* + * Skip some bytes. + */ + +static qboolean BufferedFileSkip(struct BufferedFile *BF, unsigned Offset) +{ + /* + * input verification + */ + + if(!BF) + { + return(qfalse); + } + + /* + * We can only skip to the end of the BufferedFile. + */ + + if(Offset > BF->BytesLeft) + { + return(qfalse); + } + + /* + * lower the pointer and counter. + */ + + BF->Ptr += Offset; + BF->BytesLeft -= Offset; + + return(qtrue); +} + +/* + * Find a chunk + */ + +static qboolean FindChunk(struct BufferedFile *BF, uint32_t ChunkType) +{ + struct PNG_ChunkHeader *CH; + + uint32_t Length; + uint32_t Type; + + /* + * input verification + */ + + if(!BF) + { + return(qfalse); + } + + /* + * cycle trough the chunks + */ + + while(qtrue) + { + /* + * Read the chunk-header. + */ + + CH = BufferedFileRead(BF, PNG_ChunkHeader_Size); + if(!CH) + { + return(qfalse); + } + + /* + * Do not swap the original types + * they might be needed later. + */ + + Length = BigLong(CH->Length); + Type = BigLong(CH->Type); + + /* + * We found it! + */ + + if(Type == ChunkType) + { + /* + * Rewind to the start of the chunk. + */ + + BufferedFileRewind(BF, PNG_ChunkHeader_Size); + + break; + } + else + { + /* + * Skip the rest of the chunk. + */ + + if(Length) + { + if(!BufferedFileSkip(BF, Length + PNG_ChunkCRC_Size)) + { + return(qfalse); + } + } + } + } + + return(qtrue); +} + +/* + * Decompress all IDATs + */ + +static uint32_t DecompressIDATs(struct BufferedFile *BF, uint8_t **Buffer) +{ + uint8_t *DecompressedData; + uint32_t DecompressedDataLength; + + uint8_t *CompressedData; + uint8_t *CompressedDataPtr; + uint32_t CompressedDataLength; + + struct PNG_ChunkHeader *CH; + + uint32_t Length; + uint32_t Type; + + int BytesToRewind; + + int32_t puffResult; + uint8_t *puffDest; + uint32_t puffDestLen; + uint8_t *puffSrc; + uint32_t puffSrcLen; + + /* + * input verification + */ + + if(!(BF && Buffer)) + { + return(-1); + } + + /* + * some zeroing + */ + + DecompressedData = NULL; + DecompressedDataLength = 0; + *Buffer = DecompressedData; + + CompressedData = NULL; + CompressedDataLength = 0; + + BytesToRewind = 0; + + /* + * Find the first IDAT chunk. + */ + + if(!FindChunk(BF, PNG_ChunkType_IDAT)) + { + return(-1); + } + + /* + * Count the size of the uncompressed data + */ + + while(qtrue) + { + /* + * Read chunk header + */ + + CH = BufferedFileRead(BF, PNG_ChunkHeader_Size); + if(!CH) + { + /* + * Rewind to the start of this adventure + * and return unsuccessfull + */ + + BufferedFileRewind(BF, BytesToRewind); + + return(-1); + } + + /* + * Length and Type of chunk + */ + + Length = BigLong(CH->Length); + Type = BigLong(CH->Type); + + /* + * We have reached the end of the IDAT chunks + */ + + if(!(Type == PNG_ChunkType_IDAT)) + { + BufferedFileRewind(BF, PNG_ChunkHeader_Size); + + break; + } + + /* + * Add chunk header to count. + */ + + BytesToRewind += PNG_ChunkHeader_Size; + + /* + * Skip to next chunk + */ + + if(Length) + { + if(!BufferedFileSkip(BF, Length + PNG_ChunkCRC_Size)) + { + BufferedFileRewind(BF, BytesToRewind); + + return(-1); + } + + BytesToRewind += Length + PNG_ChunkCRC_Size; + CompressedDataLength += Length; + } + } + + BufferedFileRewind(BF, BytesToRewind); + + CompressedData = ri.Malloc(CompressedDataLength); + if(!CompressedData) + { + return(-1); + } + + CompressedDataPtr = CompressedData; + + /* + * Collect the compressed Data + */ + + while(qtrue) + { + /* + * Read chunk header + */ + + CH = BufferedFileRead(BF, PNG_ChunkHeader_Size); + if(!CH) + { + ri.Free(CompressedData); + + return(-1); + } + + /* + * Length and Type of chunk + */ + + Length = BigLong(CH->Length); + Type = BigLong(CH->Type); + + /* + * We have reached the end of the IDAT chunks + */ + + if(!(Type == PNG_ChunkType_IDAT)) + { + BufferedFileRewind(BF, PNG_ChunkHeader_Size); + + break; + } + + /* + * Copy the Data + */ + + if(Length) + { + uint8_t *OrigCompressedData; + + OrigCompressedData = BufferedFileRead(BF, Length); + if(!OrigCompressedData) + { + ri.Free(CompressedData); + + return(-1); + } + + if(!BufferedFileSkip(BF, PNG_ChunkCRC_Size)) + { + ri.Free(CompressedData); + + return(-1); + } + + memcpy(CompressedDataPtr, OrigCompressedData, Length); + CompressedDataPtr += Length; + } + } + + /* + * Let puff() calculate the decompressed data length. + */ + + puffDest = NULL; + puffDestLen = 0; + + /* + * The zlib header and checkvalue don't belong to the compressed data. + */ + + puffSrc = CompressedData + PNG_ZlibHeader_Size; + puffSrcLen = CompressedDataLength - PNG_ZlibHeader_Size - PNG_ZlibCheckValue_Size; + + /* + * first puff() to calculate the size of the uncompressed data + */ + + puffResult = puff(puffDest, &puffDestLen, puffSrc, &puffSrcLen); + if(!((puffResult == 0) && (puffDestLen > 0))) + { + ri.Free(CompressedData); + + return(-1); + } + + /* + * Allocate the buffer for the uncompressed data. + */ + + DecompressedData = ri.Malloc(puffDestLen); + if(!DecompressedData) + { + ri.Free(CompressedData); + + return(-1); + } + + /* + * Set the input again in case something was changed by the last puff() . + */ + + puffDest = DecompressedData; + puffSrc = CompressedData + PNG_ZlibHeader_Size; + puffSrcLen = CompressedDataLength - PNG_ZlibHeader_Size - PNG_ZlibCheckValue_Size; + + /* + * decompression puff() + */ + + puffResult = puff(puffDest, &puffDestLen, puffSrc, &puffSrcLen); + + /* + * The compressed data is not needed anymore. + */ + + ri.Free(CompressedData); + + /* + * Check if the last puff() was successfull. + */ + + if(!((puffResult == 0) && (puffDestLen > 0))) + { + ri.Free(DecompressedData); + + return(-1); + } + + /* + * Set the output of this function. + */ + + DecompressedDataLength = puffDestLen; + *Buffer = DecompressedData; + + return(DecompressedDataLength); +} + +/* + * the Paeth predictor + */ + +static uint8_t PredictPaeth(uint8_t a, uint8_t b, uint8_t c) +{ + /* + * a == Left + * b == Up + * c == UpLeft + */ + + uint8_t Pr; + int p; + int pa, pb, pc; + + p = ((int) a) + ((int) b) - ((int) c); + pa = abs(p - ((int) a)); + pb = abs(p - ((int) b)); + pc = abs(p - ((int) c)); + + if((pa <= pb) && (pa <= pc)) + { + Pr = a; + } + else if(pb <= pc) + { + Pr = b; + } + else + { + Pr = c; + } + + return(Pr); + +} + +/* + * Reverse the filters. + */ + +static qboolean UnfilterImage(uint8_t *DecompressedData, + uint32_t ImageHeight, + uint32_t BytesPerScanline, + uint32_t BytesPerPixel) +{ + uint8_t *DecompPtr; + uint8_t FilterType; + uint8_t *PixelLeft, *PixelUp, *PixelUpLeft; + uint32_t w, h, p; + + /* + * some zeros for the filters + */ + + uint8_t Zeros[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + /* + * input verification + */ + + if(!(DecompressedData && BytesPerPixel)) + { + return(qfalse); + } + + /* + * ImageHeight and BytesPerScanline can be zero in small interlaced images. + */ + + if((!ImageHeight) || (!BytesPerScanline)) + { + return(qtrue); + } + + /* + * Set the pointer to the start of the decompressed Data. + */ + + DecompPtr = DecompressedData; + + /* + * Un-filtering is done in place. + */ + + /* + * Go trough all scanlines. + */ + + for(h = 0; h < ImageHeight; h++) + { + /* + * Every scanline starts with a FilterType byte. + */ + + FilterType = *DecompPtr; + DecompPtr++; + + /* + * Left pixel of the first byte in a scanline is zero. + */ + + PixelLeft = Zeros; + + /* + * Set PixelUp to previous line only if we are on the second line or above. + * + * Plus one byte for the FilterType + */ + + if(h > 0) + { + PixelUp = DecompPtr - (BytesPerScanline + 1); + } + else + { + PixelUp = Zeros; + } + + /* + * The pixel left to the first pixel of the previous scanline is zero too. + */ + + PixelUpLeft = Zeros; + + /* + * Cycle trough all pixels of the scanline. + */ + + for(w = 0; w < (BytesPerScanline / BytesPerPixel); w++) + { + /* + * Cycle trough the bytes of the pixel. + */ + + for(p = 0; p < BytesPerPixel; p++) + { + switch(FilterType) + { + case PNG_FilterType_None : + { + /* + * The byte is unfiltered. + */ + + break; + } + + case PNG_FilterType_Sub : + { + DecompPtr[p] += PixelLeft[p]; + + break; + } + + case PNG_FilterType_Up : + { + DecompPtr[p] += PixelUp[p]; + + break; + } + + case PNG_FilterType_Average : + { + DecompPtr[p] += ((uint8_t) ((((uint16_t) PixelLeft[p]) + ((uint16_t) PixelUp[p])) / 2)); + + break; + } + + case PNG_FilterType_Paeth : + { + DecompPtr[p] += PredictPaeth(PixelLeft[p], PixelUp[p], PixelUpLeft[p]); + + break; + } + + default : + { + return(qfalse); + } + } + } + + PixelLeft = DecompPtr; + + /* + * We only have an upleft pixel if we are on the second line or above. + */ + + if(h > 0) + { + PixelUpLeft = DecompPtr - (BytesPerScanline + 1); + } + + /* + * Skip to the next pixel. + */ + + DecompPtr += BytesPerPixel; + + /* + * We only have a previous line if we are on the second line and above. + */ + + if(h > 0) + { + PixelUp = DecompPtr - (BytesPerScanline + 1); + } + } + } + + return(qtrue); +} + +/* + * Convert a raw input pixel to Quake 3 RGA format. + */ + +static qboolean ConvertPixel(struct PNG_Chunk_IHDR *IHDR, + byte *OutPtr, + uint8_t *DecompPtr, + qboolean HasTransparentColour, + uint8_t *TransparentColour, + uint8_t *OutPal) +{ + /* + * input verification + */ + + if(!(IHDR && OutPtr && DecompPtr && TransparentColour && OutPal)) + { + return(qfalse); + } + + switch(IHDR->ColourType) + { + case PNG_ColourType_Grey : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_1 : + case PNG_BitDepth_2 : + case PNG_BitDepth_4 : + { + uint8_t Step; + uint8_t GreyValue; + + Step = 0xFF / ((1 << IHDR->BitDepth) - 1); + + GreyValue = DecompPtr[0] * Step; + + OutPtr[0] = GreyValue; + OutPtr[1] = GreyValue; + OutPtr[2] = GreyValue; + OutPtr[3] = 0xFF; + + /* + * Grey supports full transparency for one specified colour + */ + + if(HasTransparentColour) + { + if(TransparentColour[1] == DecompPtr[0]) + { + OutPtr[3] = 0x00; + } + } + + + break; + } + + case PNG_BitDepth_8 : + case PNG_BitDepth_16 : + { + OutPtr[0] = DecompPtr[0]; + OutPtr[1] = DecompPtr[0]; + OutPtr[2] = DecompPtr[0]; + OutPtr[3] = 0xFF; + + /* + * Grey supports full transparency for one specified colour + */ + + if(HasTransparentColour) + { + if(IHDR->BitDepth == PNG_BitDepth_8) + { + if(TransparentColour[1] == DecompPtr[0]) + { + OutPtr[3] = 0x00; + } + } + else + { + if((TransparentColour[0] == DecompPtr[0]) && (TransparentColour[1] == DecompPtr[1])) + { + OutPtr[3] = 0x00; + } + } + } + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_True : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_8 : + { + OutPtr[0] = DecompPtr[0]; + OutPtr[1] = DecompPtr[1]; + OutPtr[2] = DecompPtr[2]; + OutPtr[3] = 0xFF; + + /* + * True supports full transparency for one specified colour + */ + + if(HasTransparentColour) + { + if((TransparentColour[1] == DecompPtr[0]) && + (TransparentColour[3] == DecompPtr[1]) && + (TransparentColour[5] == DecompPtr[2])) + { + OutPtr[3] = 0x00; + } + } + + break; + } + + case PNG_BitDepth_16 : + { + /* + * We use only the upper byte. + */ + + OutPtr[0] = DecompPtr[0]; + OutPtr[1] = DecompPtr[2]; + OutPtr[2] = DecompPtr[4]; + OutPtr[3] = 0xFF; + + /* + * True supports full transparency for one specified colour + */ + + if(HasTransparentColour) + { + if((TransparentColour[0] == DecompPtr[0]) && (TransparentColour[1] == DecompPtr[1]) && + (TransparentColour[2] == DecompPtr[2]) && (TransparentColour[3] == DecompPtr[3]) && + (TransparentColour[4] == DecompPtr[4]) && (TransparentColour[5] == DecompPtr[5])) + { + OutPtr[3] = 0x00; + } + } + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_Indexed : + { + OutPtr[0] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 0]; + OutPtr[1] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 1]; + OutPtr[2] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 2]; + OutPtr[3] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 3]; + + break; + } + + case PNG_ColourType_GreyAlpha : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_8 : + { + OutPtr[0] = DecompPtr[0]; + OutPtr[1] = DecompPtr[0]; + OutPtr[2] = DecompPtr[0]; + OutPtr[3] = DecompPtr[1]; + + break; + } + + case PNG_BitDepth_16 : + { + /* + * We use only the upper byte. + */ + + OutPtr[0] = DecompPtr[0]; + OutPtr[1] = DecompPtr[0]; + OutPtr[2] = DecompPtr[0]; + OutPtr[3] = DecompPtr[2]; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_TrueAlpha : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_8 : + { + OutPtr[0] = DecompPtr[0]; + OutPtr[1] = DecompPtr[1]; + OutPtr[2] = DecompPtr[2]; + OutPtr[3] = DecompPtr[3]; + + break; + } + + case PNG_BitDepth_16 : + { + /* + * We use only the upper byte. + */ + + OutPtr[0] = DecompPtr[0]; + OutPtr[1] = DecompPtr[2]; + OutPtr[2] = DecompPtr[4]; + OutPtr[3] = DecompPtr[6]; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + default : + { + return(qfalse); + } + } + + return(qtrue); +} + + +/* + * Decode a non-interlaced image. + */ + +static qboolean DecodeImageNonInterlaced(struct PNG_Chunk_IHDR *IHDR, + byte *OutBuffer, + uint8_t *DecompressedData, + uint32_t DecompressedDataLength, + qboolean HasTransparentColour, + uint8_t *TransparentColour, + uint8_t *OutPal) +{ + uint32_t IHDR_Width; + uint32_t IHDR_Height; + uint32_t BytesPerScanline, BytesPerPixel, PixelsPerByte; + uint32_t w, h, p; + byte *OutPtr; + uint8_t *DecompPtr; + + /* + * input verification + */ + + if(!(IHDR && OutBuffer && DecompressedData && DecompressedDataLength && TransparentColour && OutPal)) + { + return(qfalse); + } + + /* + * byte swapping + */ + + IHDR_Width = BigLong(IHDR->Width); + IHDR_Height = BigLong(IHDR->Height); + + /* + * information for un-filtering + */ + + switch(IHDR->ColourType) + { + case PNG_ColourType_Grey : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_1 : + case PNG_BitDepth_2 : + case PNG_BitDepth_4 : + { + BytesPerPixel = 1; + PixelsPerByte = 8 / IHDR->BitDepth; + + break; + } + + case PNG_BitDepth_8 : + case PNG_BitDepth_16 : + { + BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_Grey; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_True : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_8 : + case PNG_BitDepth_16 : + { + BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_True; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_Indexed : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_1 : + case PNG_BitDepth_2 : + case PNG_BitDepth_4 : + { + BytesPerPixel = 1; + PixelsPerByte = 8 / IHDR->BitDepth; + + break; + } + + case PNG_BitDepth_8 : + { + BytesPerPixel = PNG_NumColourComponents_Indexed; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_GreyAlpha : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_8 : + case PNG_BitDepth_16 : + { + BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_GreyAlpha; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_TrueAlpha : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_8 : + case PNG_BitDepth_16 : + { + BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_TrueAlpha; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + default : + { + return(qfalse); + } + } + + /* + * Calculate the size of one scanline + */ + + BytesPerScanline = (IHDR_Width * BytesPerPixel + (PixelsPerByte - 1)) / PixelsPerByte; + + /* + * Check if we have enough data for the whole image. + */ + + if(!(DecompressedDataLength == ((BytesPerScanline + 1) * IHDR_Height))) + { + return(qfalse); + } + + /* + * Unfilter the image. + */ + + if(!UnfilterImage(DecompressedData, IHDR_Height, BytesPerScanline, BytesPerPixel)) + { + return(qfalse); + } + + /* + * Set the working pointers to the beginning of the buffers. + */ + + OutPtr = OutBuffer; + DecompPtr = DecompressedData; + + /* + * Create the output image. + */ + + for(h = 0; h < IHDR_Height; h++) + { + /* + * Count the pixels on the scanline for those multipixel bytes + */ + + uint32_t CurrPixel; + + /* + * skip FilterType + */ + + DecompPtr++; + + /* + * Reset the pixel count. + */ + + CurrPixel = 0; + + for(w = 0; w < (BytesPerScanline / BytesPerPixel); w++) + { + if(PixelsPerByte > 1) + { + uint8_t Mask; + uint32_t Shift; + uint8_t SinglePixel; + + for(p = 0; p < PixelsPerByte; p++) + { + if(CurrPixel < IHDR_Width) + { + Mask = (1 << IHDR->BitDepth) - 1; + Shift = (PixelsPerByte - 1 - p) * IHDR->BitDepth; + + SinglePixel = ((DecompPtr[0] & (Mask << Shift)) >> Shift); + + if(!ConvertPixel(IHDR, OutPtr, &SinglePixel, HasTransparentColour, TransparentColour, OutPal)) + { + return(qfalse); + } + + OutPtr += Q3IMAGE_BYTESPERPIXEL; + CurrPixel++; + } + } + + } + else + { + if(!ConvertPixel(IHDR, OutPtr, DecompPtr, HasTransparentColour, TransparentColour, OutPal)) + { + return(qfalse); + } + + + OutPtr += Q3IMAGE_BYTESPERPIXEL; + } + + DecompPtr += BytesPerPixel; + } + } + + return(qtrue); +} + +/* + * Decode an interlaced image. + */ + +static qboolean DecodeImageInterlaced(struct PNG_Chunk_IHDR *IHDR, + byte *OutBuffer, + uint8_t *DecompressedData, + uint32_t DecompressedDataLength, + qboolean HasTransparentColour, + uint8_t *TransparentColour, + uint8_t *OutPal) +{ + uint32_t IHDR_Width; + uint32_t IHDR_Height; + uint32_t BytesPerScanline[PNG_Adam7_NumPasses], BytesPerPixel, PixelsPerByte; + uint32_t PassWidth[PNG_Adam7_NumPasses], PassHeight[PNG_Adam7_NumPasses]; + uint32_t WSkip[PNG_Adam7_NumPasses], WOffset[PNG_Adam7_NumPasses], HSkip[PNG_Adam7_NumPasses], HOffset[PNG_Adam7_NumPasses]; + uint32_t w, h, p, a; + byte *OutPtr; + uint8_t *DecompPtr; + uint32_t TargetLength; + + /* + * input verification + */ + + if(!(IHDR && OutBuffer && DecompressedData && DecompressedDataLength && TransparentColour && OutPal)) + { + return(qfalse); + } + + /* + * byte swapping + */ + + IHDR_Width = BigLong(IHDR->Width); + IHDR_Height = BigLong(IHDR->Height); + + /* + * Skip and Offset for the passes. + */ + + WSkip[0] = 8; + WOffset[0] = 0; + HSkip[0] = 8; + HOffset[0] = 0; + + WSkip[1] = 8; + WOffset[1] = 4; + HSkip[1] = 8; + HOffset[1] = 0; + + WSkip[2] = 4; + WOffset[2] = 0; + HSkip[2] = 8; + HOffset[2] = 4; + + WSkip[3] = 4; + WOffset[3] = 2; + HSkip[3] = 4; + HOffset[3] = 0; + + WSkip[4] = 2; + WOffset[4] = 0; + HSkip[4] = 4; + HOffset[4] = 2; + + WSkip[5] = 2; + WOffset[5] = 1; + HSkip[5] = 2; + HOffset[5] = 0; + + WSkip[6] = 1; + WOffset[6] = 0; + HSkip[6] = 2; + HOffset[6] = 1; + + /* + * Calculate the sizes of the passes. + */ + + PassWidth[0] = (IHDR_Width + 7) / 8; + PassHeight[0] = (IHDR_Height + 7) / 8; + + PassWidth[1] = (IHDR_Width + 3) / 8; + PassHeight[1] = (IHDR_Height + 7) / 8; + + PassWidth[2] = (IHDR_Width + 3) / 4; + PassHeight[2] = (IHDR_Height + 3) / 8; + + PassWidth[3] = (IHDR_Width + 1) / 4; + PassHeight[3] = (IHDR_Height + 3) / 4; + + PassWidth[4] = (IHDR_Width + 1) / 2; + PassHeight[4] = (IHDR_Height + 1) / 4; + + PassWidth[5] = (IHDR_Width + 0) / 2; + PassHeight[5] = (IHDR_Height + 1) / 2; + + PassWidth[6] = (IHDR_Width + 0) / 1; + PassHeight[6] = (IHDR_Height + 0) / 2; + + /* + * information for un-filtering + */ + + switch(IHDR->ColourType) + { + case PNG_ColourType_Grey : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_1 : + case PNG_BitDepth_2 : + case PNG_BitDepth_4 : + { + BytesPerPixel = 1; + PixelsPerByte = 8 / IHDR->BitDepth; + + break; + } + + case PNG_BitDepth_8 : + case PNG_BitDepth_16 : + { + BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_Grey; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_True : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_8 : + case PNG_BitDepth_16 : + { + BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_True; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_Indexed : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_1 : + case PNG_BitDepth_2 : + case PNG_BitDepth_4 : + { + BytesPerPixel = 1; + PixelsPerByte = 8 / IHDR->BitDepth; + + break; + } + + case PNG_BitDepth_8 : + { + BytesPerPixel = PNG_NumColourComponents_Indexed; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_GreyAlpha : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_8 : + case PNG_BitDepth_16 : + { + BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_GreyAlpha; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_TrueAlpha : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_8 : + case PNG_BitDepth_16 : + { + BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_TrueAlpha; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + default : + { + return(qfalse); + } + } + + /* + * Calculate the size of the scanlines per pass + */ + + for(a = 0; a < PNG_Adam7_NumPasses; a++) + { + BytesPerScanline[a] = (PassWidth[a] * BytesPerPixel + (PixelsPerByte - 1)) / PixelsPerByte; + } + + /* + * Calculate the size of all passes + */ + + TargetLength = 0; + + for(a = 0; a < PNG_Adam7_NumPasses; a++) + { + TargetLength += ((BytesPerScanline[a] + (BytesPerScanline[a] ? 1 : 0)) * PassHeight[a]); + } + + /* + * Check if we have enough data for the whole image. + */ + + if(!(DecompressedDataLength == TargetLength)) + { + return(qfalse); + } + + /* + * Unfilter the image. + */ + + DecompPtr = DecompressedData; + + for(a = 0; a < PNG_Adam7_NumPasses; a++) + { + if(!UnfilterImage(DecompPtr, PassHeight[a], BytesPerScanline[a], BytesPerPixel)) + { + return(qfalse); + } + + DecompPtr += ((BytesPerScanline[a] + (BytesPerScanline[a] ? 1 : 0)) * PassHeight[a]); + } + + /* + * Set the working pointers to the beginning of the buffers. + */ + + DecompPtr = DecompressedData; + + /* + * Create the output image. + */ + + for(a = 0; a < PNG_Adam7_NumPasses; a++) + { + for(h = 0; h < PassHeight[a]; h++) + { + /* + * Count the pixels on the scanline for those multipixel bytes + */ + + uint32_t CurrPixel; + + /* + * skip FilterType + * but only when the pass has a width bigger than zero + */ + + if(BytesPerScanline[a]) + { + DecompPtr++; + } + + /* + * Reset the pixel count. + */ + + CurrPixel = 0; + + for(w = 0; w < (BytesPerScanline[a] / BytesPerPixel); w++) + { + if(PixelsPerByte > 1) + { + uint8_t Mask; + uint32_t Shift; + uint8_t SinglePixel; + + for(p = 0; p < PixelsPerByte; p++) + { + if(CurrPixel < PassWidth[a]) + { + Mask = (1 << IHDR->BitDepth) - 1; + Shift = (PixelsPerByte - 1 - p) * IHDR->BitDepth; + + SinglePixel = ((DecompPtr[0] & (Mask << Shift)) >> Shift); + + OutPtr = OutBuffer + (((((h * HSkip[a]) + HOffset[a]) * IHDR_Width) + ((CurrPixel * WSkip[a]) + WOffset[a])) * Q3IMAGE_BYTESPERPIXEL); + + if(!ConvertPixel(IHDR, OutPtr, &SinglePixel, HasTransparentColour, TransparentColour, OutPal)) + { + return(qfalse); + } + + CurrPixel++; + } + } + + } + else + { + OutPtr = OutBuffer + (((((h * HSkip[a]) + HOffset[a]) * IHDR_Width) + ((w * WSkip[a]) + WOffset[a])) * Q3IMAGE_BYTESPERPIXEL); + + if(!ConvertPixel(IHDR, OutPtr, DecompPtr, HasTransparentColour, TransparentColour, OutPal)) + { + return(qfalse); + } + } + + DecompPtr += BytesPerPixel; + } + } + } + + return(qtrue); +} + +/* + * The PNG loader + */ + +void R_LoadPNG(const char *name, byte **pic, int *width, int *height) +{ + struct BufferedFile *ThePNG; + byte *OutBuffer; + uint8_t *Signature; + struct PNG_ChunkHeader *CH; + uint32_t ChunkHeaderLength; + uint32_t ChunkHeaderType; + struct PNG_Chunk_IHDR *IHDR; + uint32_t IHDR_Width; + uint32_t IHDR_Height; + PNG_ChunkCRC *CRC; + uint8_t *InPal; + uint8_t *DecompressedData; + uint32_t DecompressedDataLength; + uint32_t i; + + /* + * palette with 256 RGBA entries + */ + + uint8_t OutPal[1024]; + + /* + * transparent colour from the tRNS chunk + */ + + qboolean HasTransparentColour = qfalse; + uint8_t TransparentColour[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + /* + * input verification + */ + + if(!(name && pic)) + { + return; + } + + /* + * Zero out return values. + */ + + *pic = NULL; + + if(width) + { + *width = 0; + } + + if(height) + { + *height = 0; + } + + /* + * Read the file. + */ + + ThePNG = ReadBufferedFile(name); + if(!ThePNG) + { + return; + } + + /* + * Read the siganture of the file. + */ + + Signature = BufferedFileRead(ThePNG, PNG_Signature_Size); + if(!Signature) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Is it a PNG? + */ + + if(memcmp(Signature, PNG_Signature, PNG_Signature_Size)) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Read the first chunk-header. + */ + + CH = BufferedFileRead(ThePNG, PNG_ChunkHeader_Size); + if(!CH) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * PNG multi-byte types are in Big Endian + */ + + ChunkHeaderLength = BigLong(CH->Length); + ChunkHeaderType = BigLong(CH->Type); + + /* + * Check if the first chunk is an IHDR. + */ + + if(!((ChunkHeaderType == PNG_ChunkType_IHDR) && (ChunkHeaderLength == PNG_Chunk_IHDR_Size))) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Read the IHDR. + */ + + IHDR = BufferedFileRead(ThePNG, PNG_Chunk_IHDR_Size); + if(!IHDR) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Read the CRC for IHDR + */ + + CRC = BufferedFileRead(ThePNG, PNG_ChunkCRC_Size); + if(!CRC) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Here we could check the CRC if we wanted to. + */ + + /* + * multi-byte type swapping + */ + + IHDR_Width = BigLong(IHDR->Width); + IHDR_Height = BigLong(IHDR->Height); + + /* + * Check if Width and Height are valid. + */ + + if(!((IHDR_Width > 0) && (IHDR_Height > 0)) + || IHDR_Width > INT_MAX / Q3IMAGE_BYTESPERPIXEL / IHDR_Height) + { + CloseBufferedFile(ThePNG); + + ri.Printf( PRINT_WARNING, "%s: invalid image size\n", name ); + + return; + } + + /* + * Do we need to check if the dimensions of the image are valid for Quake3? + */ + + /* + * Check if CompressionMethod and FilterMethod are valid. + */ + + if(!((IHDR->CompressionMethod == PNG_CompressionMethod_0) && (IHDR->FilterMethod == PNG_FilterMethod_0))) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Check if InterlaceMethod is valid. + */ + + if(!((IHDR->InterlaceMethod == PNG_InterlaceMethod_NonInterlaced) || (IHDR->InterlaceMethod == PNG_InterlaceMethod_Interlaced))) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Read palette for an indexed image. + */ + + if(IHDR->ColourType == PNG_ColourType_Indexed) + { + /* + * We need the palette first. + */ + + if(!FindChunk(ThePNG, PNG_ChunkType_PLTE)) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Read the chunk-header. + */ + + CH = BufferedFileRead(ThePNG, PNG_ChunkHeader_Size); + if(!CH) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * PNG multi-byte types are in Big Endian + */ + + ChunkHeaderLength = BigLong(CH->Length); + ChunkHeaderType = BigLong(CH->Type); + + /* + * Check if the chunk is a PLTE. + */ + + if(!(ChunkHeaderType == PNG_ChunkType_PLTE)) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Check if Length is divisible by 3 + */ + + if(ChunkHeaderLength % 3) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Read the raw palette data + */ + + InPal = BufferedFileRead(ThePNG, ChunkHeaderLength); + if(!InPal) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Read the CRC for the palette + */ + + CRC = BufferedFileRead(ThePNG, PNG_ChunkCRC_Size); + if(!CRC) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Set some default values. + */ + + for(i = 0; i < 256; i++) + { + OutPal[i * Q3IMAGE_BYTESPERPIXEL + 0] = 0x00; + OutPal[i * Q3IMAGE_BYTESPERPIXEL + 1] = 0x00; + OutPal[i * Q3IMAGE_BYTESPERPIXEL + 2] = 0x00; + OutPal[i * Q3IMAGE_BYTESPERPIXEL + 3] = 0xFF; + } + + /* + * Convert to the Quake3 RGBA-format. + */ + + for(i = 0; i < (ChunkHeaderLength / 3); i++) + { + OutPal[i * Q3IMAGE_BYTESPERPIXEL + 0] = InPal[i*3+0]; + OutPal[i * Q3IMAGE_BYTESPERPIXEL + 1] = InPal[i*3+1]; + OutPal[i * Q3IMAGE_BYTESPERPIXEL + 2] = InPal[i*3+2]; + OutPal[i * Q3IMAGE_BYTESPERPIXEL + 3] = 0xFF; + } + } + + /* + * transparency information is sometimes stored in a tRNS chunk + */ + + /* + * Let's see if there is a tRNS chunk + */ + + if(FindChunk(ThePNG, PNG_ChunkType_tRNS)) + { + uint8_t *Trans; + + /* + * Read the chunk-header. + */ + + CH = BufferedFileRead(ThePNG, PNG_ChunkHeader_Size); + if(!CH) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * PNG multi-byte types are in Big Endian + */ + + ChunkHeaderLength = BigLong(CH->Length); + ChunkHeaderType = BigLong(CH->Type); + + /* + * Check if the chunk is a tRNS. + */ + + if(!(ChunkHeaderType == PNG_ChunkType_tRNS)) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Read the transparency information. + */ + + Trans = BufferedFileRead(ThePNG, ChunkHeaderLength); + if(!Trans) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Read the CRC. + */ + + CRC = BufferedFileRead(ThePNG, PNG_ChunkCRC_Size); + if(!CRC) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Only for Grey, True and Indexed ColourType should tRNS exist. + */ + + switch(IHDR->ColourType) + { + case PNG_ColourType_Grey : + { + if(!ChunkHeaderLength == 2) + { + CloseBufferedFile(ThePNG); + + return; + } + + HasTransparentColour = qtrue; + + /* + * Grey can have one colour which is completely transparent. + * This colour is always stored in 16 bits. + */ + + TransparentColour[0] = Trans[0]; + TransparentColour[1] = Trans[1]; + + break; + } + + case PNG_ColourType_True : + { + if(!ChunkHeaderLength == 6) + { + CloseBufferedFile(ThePNG); + + return; + } + + HasTransparentColour = qtrue; + + /* + * True can have one colour which is completely transparent. + * This colour is always stored in 16 bits. + */ + + TransparentColour[0] = Trans[0]; + TransparentColour[1] = Trans[1]; + TransparentColour[2] = Trans[2]; + TransparentColour[3] = Trans[3]; + TransparentColour[4] = Trans[4]; + TransparentColour[5] = Trans[5]; + + break; + } + + case PNG_ColourType_Indexed : + { + /* + * Maximum of 256 one byte transparency entries. + */ + + if(ChunkHeaderLength > 256) + { + CloseBufferedFile(ThePNG); + + return; + } + + HasTransparentColour = qtrue; + + /* + * alpha values for palette entries + */ + + for(i = 0; i < ChunkHeaderLength; i++) + { + OutPal[i * Q3IMAGE_BYTESPERPIXEL + 3] = Trans[i]; + } + + break; + } + + /* + * All other ColourTypes should not have tRNS chunks + */ + + default : + { + CloseBufferedFile(ThePNG); + + return; + } + } + } + + /* + * Rewind to the start of the file. + */ + + if(!BufferedFileRewind(ThePNG, -1)) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Skip the signature + */ + + if(!BufferedFileSkip(ThePNG, PNG_Signature_Size)) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Decompress all IDAT chunks + */ + + DecompressedDataLength = DecompressIDATs(ThePNG, &DecompressedData); + if(!(DecompressedDataLength && DecompressedData)) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Allocate output buffer. + */ + + OutBuffer = ri.Malloc(IHDR_Width * IHDR_Height * Q3IMAGE_BYTESPERPIXEL); + if(!OutBuffer) + { + ri.Free(DecompressedData); + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Interlaced and Non-interlaced images need to be handled differently. + */ + + switch(IHDR->InterlaceMethod) + { + case PNG_InterlaceMethod_NonInterlaced : + { + if(!DecodeImageNonInterlaced(IHDR, OutBuffer, DecompressedData, DecompressedDataLength, HasTransparentColour, TransparentColour, OutPal)) + { + ri.Free(OutBuffer); + ri.Free(DecompressedData); + CloseBufferedFile(ThePNG); + + return; + } + + break; + } + + case PNG_InterlaceMethod_Interlaced : + { + if(!DecodeImageInterlaced(IHDR, OutBuffer, DecompressedData, DecompressedDataLength, HasTransparentColour, TransparentColour, OutPal)) + { + ri.Free(OutBuffer); + ri.Free(DecompressedData); + CloseBufferedFile(ThePNG); + + return; + } + + break; + } + + default : + { + ri.Free(OutBuffer); + ri.Free(DecompressedData); + CloseBufferedFile(ThePNG); + + return; + } + } + + /* + * update the pointer to the image data + */ + + *pic = OutBuffer; + + /* + * Fill width and height. + */ + + if(width) + { + *width = IHDR_Width; + } + + if(height) + { + *height = IHDR_Height; + } + + /* + * DecompressedData is not needed anymore. + */ + + ri.Free(DecompressedData); + + /* + * We have all data, so close the file. + */ + + CloseBufferedFile(ThePNG); +} diff --git a/src/renderergl1/tr_image_tga.c b/src/renderergl1/tr_image_tga.c new file mode 100644 index 00000000..b707c0ed --- /dev/null +++ b/src/renderergl1/tr_image_tga.c @@ -0,0 +1,321 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "tr_local.h" + +/* +======================================================================== + +TGA files are used for 24/32 bit images + +======================================================================== +*/ + +typedef struct _TargaHeader { + unsigned char id_length, colormap_type, image_type; + unsigned short colormap_index, colormap_length; + unsigned char colormap_size; + unsigned short x_origin, y_origin, width, height; + unsigned char pixel_size, attributes; +} TargaHeader; + +void R_LoadTGA ( const char *name, byte **pic, int *width, int *height) +{ + unsigned columns, rows, numPixels; + byte *pixbuf; + int row, column; + byte *buf_p; + byte *end; + union { + byte *b; + void *v; + } buffer; + TargaHeader targa_header; + byte *targa_rgba; + int length; + + *pic = NULL; + + if(width) + *width = 0; + if(height) + *height = 0; + + // + // load the file + // + length = ri.FS_ReadFile ( ( char * ) name, &buffer.v); + if (!buffer.b || length < 0) { + return; + } + + if(length < 18) + { + ri.Error( ERR_DROP, "LoadTGA: header too short (%s)", name ); + } + + buf_p = buffer.b; + end = buffer.b + length; + + targa_header.id_length = buf_p[0]; + targa_header.colormap_type = buf_p[1]; + targa_header.image_type = buf_p[2]; + + memcpy(&targa_header.colormap_index, &buf_p[3], 2); + memcpy(&targa_header.colormap_length, &buf_p[5], 2); + targa_header.colormap_size = buf_p[7]; + memcpy(&targa_header.x_origin, &buf_p[8], 2); + memcpy(&targa_header.y_origin, &buf_p[10], 2); + memcpy(&targa_header.width, &buf_p[12], 2); + memcpy(&targa_header.height, &buf_p[14], 2); + targa_header.pixel_size = buf_p[16]; + targa_header.attributes = buf_p[17]; + + targa_header.colormap_index = LittleShort(targa_header.colormap_index); + targa_header.colormap_length = LittleShort(targa_header.colormap_length); + targa_header.x_origin = LittleShort(targa_header.x_origin); + targa_header.y_origin = LittleShort(targa_header.y_origin); + targa_header.width = LittleShort(targa_header.width); + targa_header.height = LittleShort(targa_header.height); + + buf_p += 18; + + if (targa_header.image_type!=2 + && targa_header.image_type!=10 + && targa_header.image_type != 3 ) + { + ri.Error (ERR_DROP, "LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported"); + } + + if ( targa_header.colormap_type != 0 ) + { + ri.Error( ERR_DROP, "LoadTGA: colormaps not supported" ); + } + + if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 ) + { + ri.Error (ERR_DROP, "LoadTGA: Only 32 or 24 bit images supported (no colormaps)"); + } + + columns = targa_header.width; + rows = targa_header.height; + numPixels = columns * rows * 4; + + if(!columns || !rows || numPixels > 0x7FFFFFFF || numPixels / columns / 4 != rows) + { + ri.Error (ERR_DROP, "LoadTGA: %s has an invalid image size", name); + } + + + targa_rgba = ri.Malloc (numPixels); + + if (targa_header.id_length != 0) + { + if (buf_p + targa_header.id_length > end) + ri.Error( ERR_DROP, "LoadTGA: header too short (%s)", name ); + + buf_p += targa_header.id_length; // skip TARGA image comment + } + + if ( targa_header.image_type==2 || targa_header.image_type == 3 ) + { + if(buf_p + columns*rows*targa_header.pixel_size/8 > end) + { + ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name); + } + + // Uncompressed RGB or gray scale image + for(row=rows-1; row>=0; row--) + { + pixbuf = targa_rgba + row*columns*4; + for(column=0; column=0; row--) { + pixbuf = targa_rgba + row*columns*4; + for(column=0; column end) + ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name); + packetHeader= *buf_p++; + packetSize = 1 + (packetHeader & 0x7f); + if (packetHeader & 0x80) { // run-length packet + if(buf_p + targa_header.pixel_size/8 > end) + ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name); + switch (targa_header.pixel_size) { + case 24: + blue = *buf_p++; + green = *buf_p++; + red = *buf_p++; + alphabyte = 255; + break; + case 32: + blue = *buf_p++; + green = *buf_p++; + red = *buf_p++; + alphabyte = *buf_p++; + break; + default: + ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'", targa_header.pixel_size, name ); + break; + } + + for(j=0;j0) + row--; + else + goto breakOut; + pixbuf = targa_rgba + row*columns*4; + } + } + } + else { // non run-length packet + + if(buf_p + targa_header.pixel_size/8*packetSize > end) + ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name); + for(j=0;j0) + row--; + else + goto breakOut; + pixbuf = targa_rgba + row*columns*4; + } + } + } + } + breakOut:; + } + } + +#if 0 + // TTimo: this is the chunk of code to ensure a behavior that meets TGA specs + // bit 5 set => top-down + if (targa_header.attributes & 0x20) { + unsigned char *flip = (unsigned char*)malloc (columns*4); + unsigned char *src, *dst; + + for (row = 0; row < rows/2; row++) { + src = targa_rgba + row * 4 * columns; + dst = targa_rgba + (rows - row - 1) * 4 * columns; + + memcpy (flip, src, columns*4); + memcpy (src, dst, columns*4); + memcpy (dst, flip, columns*4); + } + free (flip); + } +#endif + // instead we just print a warning + if (targa_header.attributes & 0x20) { + ri.Printf( PRINT_WARNING, "WARNING: '%s' TGA file header declares top-down image, ignoring\n", name); + } + + if (width) + *width = columns; + if (height) + *height = rows; + + *pic = targa_rgba; + + ri.FS_FreeFile (buffer.v); +} diff --git a/src/renderergl1/tr_init.c b/src/renderergl1/tr_init.c new file mode 100644 index 00000000..14b91927 --- /dev/null +++ b/src/renderergl1/tr_init.c @@ -0,0 +1,1375 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_init.c -- functions that are not called every frame + +#include "tr_local.h" + +glconfig_t glConfig; +glstate_t glState; + +static void GfxInfo_f( void ); + +#ifdef USE_RENDERER_DLOPEN +cvar_t *com_altivec; +#endif + +cvar_t *r_flareSize; +cvar_t *r_flareFade; +cvar_t *r_flareCoeff; + +cvar_t *r_railWidth; +cvar_t *r_railCoreWidth; +cvar_t *r_railSegmentLength; + +cvar_t *r_ignoreFastPath; + +cvar_t *r_verbose; +cvar_t *r_ignore; + +cvar_t *r_detailTextures; + +cvar_t *r_znear; +cvar_t *r_zproj; +cvar_t *r_stereoSeparation; + +cvar_t *r_skipBackEnd; + +cvar_t *r_stereoEnabled; +cvar_t *r_anaglyphMode; + +cvar_t *r_greyscale; + +cvar_t *r_ignorehwgamma; +cvar_t *r_measureOverdraw; + +cvar_t *r_inGameVideo; +cvar_t *r_fastsky; +cvar_t *r_drawSun; +cvar_t *r_dynamiclight; +cvar_t *r_dlightBacks; + +cvar_t *r_lodbias; +cvar_t *r_lodscale; + +cvar_t *r_norefresh; +cvar_t *r_drawentities; +cvar_t *r_drawworld; +cvar_t *r_speeds; +cvar_t *r_fullbright; +cvar_t *r_novis; +cvar_t *r_nocull; +cvar_t *r_facePlaneCull; +cvar_t *r_showcluster; +cvar_t *r_nocurves; + +cvar_t *r_allowExtensions; + +cvar_t *r_ext_compressed_textures; +cvar_t *r_ext_multitexture; +cvar_t *r_ext_compiled_vertex_array; +cvar_t *r_ext_texture_env_add; +cvar_t *r_ext_texture_filter_anisotropic; +cvar_t *r_ext_max_anisotropy; + +cvar_t *r_ignoreGLErrors; +cvar_t *r_logFile; + +cvar_t *r_stencilbits; +cvar_t *r_depthbits; +cvar_t *r_colorbits; +cvar_t *r_primitives; +cvar_t *r_texturebits; +cvar_t *r_ext_multisample; + +cvar_t *r_drawBuffer; +cvar_t *r_lightmap; +cvar_t *r_vertexLight; +cvar_t *r_uiFullScreen; +cvar_t *r_shadows; +cvar_t *r_flares; +cvar_t *r_nobind; +cvar_t *r_singleShader; +cvar_t *r_roundImagesDown; +cvar_t *r_colorMipLevels; +cvar_t *r_picmip; +cvar_t *r_showtris; +cvar_t *r_showsky; +cvar_t *r_shownormals; +cvar_t *r_finish; +cvar_t *r_clear; +cvar_t *r_swapInterval; +cvar_t *r_textureMode; +cvar_t *r_offsetFactor; +cvar_t *r_offsetUnits; +cvar_t *r_gamma; +cvar_t *r_intensity; +cvar_t *r_lockpvs; +cvar_t *r_noportals; +cvar_t *r_portalOnly; + +cvar_t *r_subdivisions; +cvar_t *r_lodCurveError; + +cvar_t *r_fullscreen; +cvar_t *r_noborder; + +cvar_t *r_width; +cvar_t *r_height; +cvar_t *r_pixelAspect; + +cvar_t *r_overBrightBits; +cvar_t *r_mapOverBrightBits; + +cvar_t *r_debugSurface; +cvar_t *r_simpleMipMaps; + +cvar_t *r_showImages; + +cvar_t *r_ambientScale; +cvar_t *r_directedScale; +cvar_t *r_debugLight; +cvar_t *r_debugSort; +cvar_t *r_printShaders; +cvar_t *r_saveFontData; + +cvar_t *r_marksOnTriangleMeshes; + +cvar_t *r_aviMotionJpegQuality; +cvar_t *r_screenshotJpegQuality; + +cvar_t *r_maxpolys; +int max_polys; +cvar_t *r_maxpolyverts; +int max_polyverts; + +#define GENERIC_HW_R_PICMIP_DEFAULT "0" +#define GENERIC_HW_R_TEXTUREMODE_DEFAULT "GL_LINEAR_MIPMAP_LINEAR" + +/* +================== +GL_ResolveHardwareType + +Chipset specific configuration +================== +*/ +void GL_ResolveHardwareType( void ) +{ + char buf[ 1024 ]; + cvar_t *lastValidRenderer = ri.Cvar_Get( + "r_lastValidRenderer", "(uninitialized)", CVAR_ARCHIVE ); + + Q_strncpyz( buf, glConfig.renderer_string, sizeof( buf ) ); + Q_strlwr( buf ); + + // NOTE: if changing cvars, do it within this block. This allows them + // to be overridden when testing driver fixes, etc. but only sets + // them to their default state when the hardware is first installed/run. + if( Q_stricmp( lastValidRenderer->string, glConfig.renderer_string ) ) + { + glConfig.hardwareType = GLHW_GENERIC; + + ri.Cvar_Set( "r_textureMode", GENERIC_HW_R_TEXTUREMODE_DEFAULT ); + + // VOODOO GRAPHICS w/ 2MB + if ( strstr( buf, "voodoo graphics/1 tmu/2 mb" ) ) + { + ri.Cvar_Set( "r_picmip", "2" ); + ri.Cvar_Get( "r_picmip", "1", CVAR_ARCHIVE | CVAR_LATCH ); + } + else + { + ri.Cvar_Set( "r_picmip", GENERIC_HW_R_PICMIP_DEFAULT ); + + if ( strstr( buf, "rage 128" ) || strstr( buf, "rage128" ) ) + { + ri.Cvar_Set( "r_finish", "0" ); + } + // Savage3D and Savage4 should always have trilinear enabled + else if ( strstr( buf, "savage3d" ) || strstr( buf, "s3 savage4" ) ) + { + ri.Cvar_Set( "r_texturemode", "GL_LINEAR_MIPMAP_LINEAR" ); + } + } + } + + // + // this is where hardware specific workarounds that should be + // detected/initialized every startup should go. + // + if ( strstr( buf, "banshee" ) || strstr( buf, "voodoo3" ) ) + { + glConfig.hardwareType = GLHW_3DFX_2D3D; + } + // VOODOO GRAPHICS w/ 2MB + else if ( strstr( buf, "voodoo graphics/1 tmu/2 mb" ) ) + { + } + else if ( strstr( buf, "glzicd" ) ) + { + } + else if ( strstr( buf, "rage pro" ) || + strstr( buf, "Rage Pro" ) || + strstr( buf, "ragepro" ) ) + { + glConfig.hardwareType = GLHW_RAGEPRO; + } + else if ( strstr( buf, "rage 128" ) ) + { + } + else if ( strstr( buf, "permedia2" ) ) + { + glConfig.hardwareType = GLHW_PERMEDIA2; + } + else if ( strstr( buf, "riva 128" ) ) + { + glConfig.hardwareType = GLHW_RIVA128; + } + else if ( strstr( buf, "riva tnt " ) ) + { + } +} + +/* +** InitOpenGL +** +** This function is responsible for initializing a valid OpenGL subsystem. This +** is done by calling GLimp_Init (which gives us a working OGL subsystem) then +** setting variables, checking GL constants, and reporting the gfx system config +** to the user. +*/ +static void InitOpenGL( void ) +{ + char renderer_buffer[1024]; + + // + // initialize OS specific portions of the renderer + // + // GLimp_Init directly or indirectly references the following cvars: + // - r_fullscreen + // - r_(width|height|pixelAspect) + // - r_(color|depth|stencil)bits + // - r_ignorehwgamma + // - r_gamma + // + + if ( glConfig.vidWidth == 0 ) + { + GLint temp; + + GLimp_Init(); + + strcpy( renderer_buffer, glConfig.renderer_string ); + Q_strlwr( renderer_buffer ); + + // OpenGL driver constants + qglGetIntegerv( GL_MAX_TEXTURE_SIZE, &temp ); + glConfig.maxTextureSize = temp; + + // stubbed or broken drivers may have reported 0... + if ( glConfig.maxTextureSize <= 0 ) + { + glConfig.maxTextureSize = 0; + } + } + + // set default state + GL_SetDefaultState(); +} + +/* +================== +GL_CheckErrors +================== +*/ +void GL_CheckErrors( void ) { + int err; + char s[64]; + + err = qglGetError(); + if ( err == GL_NO_ERROR ) { + return; + } + if ( r_ignoreGLErrors->integer ) { + return; + } + switch( err ) { + case GL_INVALID_ENUM: + strcpy( s, "GL_INVALID_ENUM" ); + break; + case GL_INVALID_VALUE: + strcpy( s, "GL_INVALID_VALUE" ); + break; + case GL_INVALID_OPERATION: + strcpy( s, "GL_INVALID_OPERATION" ); + break; + case GL_STACK_OVERFLOW: + strcpy( s, "GL_STACK_OVERFLOW" ); + break; + case GL_STACK_UNDERFLOW: + strcpy( s, "GL_STACK_UNDERFLOW" ); + break; + case GL_OUT_OF_MEMORY: + strcpy( s, "GL_OUT_OF_MEMORY" ); + break; + default: + Com_sprintf( s, sizeof(s), "%i", err); + break; + } + + ri.Error( ERR_FATAL, "GL_CheckErrors: %s", s ); +} + + +/* +============================================================================== + + SCREEN SHOTS + +NOTE TTimo +some thoughts about the screenshots system: +screenshots get written in fs_homepath + fs_gamedir +vanilla q3 .. baseq3/screenshots/ *.tga +team arena .. missionpack/screenshots/ *.tga + +two commands: "screenshot" and "screenshotJPEG" +we use statics to store a count and start writing the first screenshot/screenshot????.tga (.jpg) available +(with FS_FileExists / FS_FOpenFileWrite calls) +FIXME: the statics don't get a reinit between fs_game changes + +============================================================================== +*/ + +/* +================== +RB_ReadPixels + +Reads an image but takes care of alignment issues for reading RGB images. + +Reads a minimum offset for where the RGB data starts in the image from +integer stored at pointer offset. When the function has returned the actual +offset was written back to address offset. This address will always have an +alignment of packAlign to ensure efficient copying. + +Stores the length of padding after a line of pixels to address padlen + +Return value must be freed with ri.Hunk_FreeTempMemory() +================== +*/ + +byte *RB_ReadPixels(int x, int y, int width, int height, size_t *offset, int *padlen) +{ + byte *buffer, *bufstart; + int padwidth, linelen; + GLint packAlign; + + qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); + + linelen = width * 3; + padwidth = PAD(linelen, packAlign); + + // Allocate a few more bytes so that we can choose an alignment we like + buffer = ri.Hunk_AllocateTempMemory(padwidth * height + *offset + packAlign - 1); + + bufstart = PADP((intptr_t) buffer + *offset, packAlign); + qglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, bufstart); + + *offset = bufstart - buffer; + *padlen = padwidth - linelen; + + return buffer; +} + +/* +================== +RB_TakeScreenshot +================== +*/ +void RB_TakeScreenshot(int x, int y, int width, int height, char *fileName) +{ + byte *allbuf, *buffer; + byte *srcptr, *destptr; + byte *endline, *endmem; + byte temp; + + int linelen, padlen; + size_t offset = 18, memcount; + + allbuf = RB_ReadPixels(x, y, width, height, &offset, &padlen); + buffer = allbuf + offset - 18; + + Com_Memset (buffer, 0, 18); + buffer[2] = 2; // uncompressed type + buffer[12] = width & 255; + buffer[13] = width >> 8; + buffer[14] = height & 255; + buffer[15] = height >> 8; + buffer[16] = 24; // pixel size + + // swap rgb to bgr and remove padding from line endings + linelen = width * 3; + + srcptr = destptr = allbuf + offset; + endmem = srcptr + (linelen + padlen) * height; + + while(srcptr < endmem) + { + endline = srcptr + linelen; + + while(srcptr < endline) + { + temp = srcptr[0]; + *destptr++ = srcptr[2]; + *destptr++ = srcptr[1]; + *destptr++ = temp; + + srcptr += 3; + } + + // Skip the pad + srcptr += padlen; + } + + memcount = linelen * height; + + // gamma correct + if(glConfig.deviceSupportsGamma) + R_GammaCorrect(allbuf + offset, memcount); + + ri.FS_WriteFile(fileName, buffer, memcount + 18); + + ri.Hunk_FreeTempMemory(allbuf); +} + +/* +================== +RB_TakeScreenshotJPEG +================== +*/ + +void RB_TakeScreenshotJPEG(int x, int y, int width, int height, char *fileName) +{ + byte *buffer; + size_t offset = 0, memcount; + int padlen; + + buffer = RB_ReadPixels(x, y, width, height, &offset, &padlen); + memcount = (width * 3 + padlen) * height; + + // gamma correct + if(glConfig.deviceSupportsGamma) + R_GammaCorrect(buffer + offset, memcount); + + RE_SaveJPG(fileName, r_screenshotJpegQuality->integer, width, height, buffer + offset, padlen); + ri.Hunk_FreeTempMemory(buffer); +} + +/* +================== +RB_TakeScreenshotCmd +================== +*/ +const void *RB_TakeScreenshotCmd( const void *data ) { + const screenshotCommand_t *cmd; + + cmd = (const screenshotCommand_t *)data; + + if (cmd->jpeg) + RB_TakeScreenshotJPEG( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName); + else + RB_TakeScreenshot( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName); + + return (const void *)(cmd + 1); +} + +/* +================== +R_TakeScreenshot +================== +*/ +void R_TakeScreenshot( int x, int y, int width, int height, char *name, qboolean jpeg ) { + static char fileName[MAX_OSPATH]; // bad things if two screenshots per frame? + screenshotCommand_t *cmd; + + cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + if ( !cmd ) { + return; + } + cmd->commandId = RC_SCREENSHOT; + + cmd->x = x; + cmd->y = y; + cmd->width = width; + cmd->height = height; + Q_strncpyz( fileName, name, sizeof(fileName) ); + cmd->fileName = fileName; + cmd->jpeg = jpeg; +} + +/* +================== +R_ScreenshotFilename +================== +*/ +void R_ScreenshotFilename( int lastNumber, char *fileName ) { + int a,b,c,d; + + if ( lastNumber < 0 || lastNumber > 9999 ) { + Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot9999.tga" ); + return; + } + + a = lastNumber / 1000; + lastNumber -= a*1000; + b = lastNumber / 100; + lastNumber -= b*100; + c = lastNumber / 10; + lastNumber -= c*10; + d = lastNumber; + + Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot%i%i%i%i.tga" + , a, b, c, d ); +} + +/* +================== +R_ScreenshotFilename +================== +*/ +void R_ScreenshotFilenameJPEG( int lastNumber, char *fileName ) { + int a,b,c,d; + + if ( lastNumber < 0 || lastNumber > 9999 ) { + Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot9999.jpg" ); + return; + } + + a = lastNumber / 1000; + lastNumber -= a*1000; + b = lastNumber / 100; + lastNumber -= b*100; + c = lastNumber / 10; + lastNumber -= c*10; + d = lastNumber; + + Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot%i%i%i%i.jpg" + , a, b, c, d ); +} + +/* +==================== +R_LevelShot + +levelshots are specialized 128*128 thumbnails for +the menu system, sampled down from full screen distorted images +==================== +*/ +void R_LevelShot( void ) { + char checkname[MAX_OSPATH]; + byte *buffer; + byte *source, *allsource; + byte *src, *dst; + size_t offset = 0; + int padlen; + int x, y; + int r, g, b; + float xScale, yScale; + int xx, yy; + + Com_sprintf(checkname, sizeof(checkname), "levelshots/%s.tga", tr.world->baseName); + + allsource = RB_ReadPixels(0, 0, glConfig.vidWidth, glConfig.vidHeight, &offset, &padlen); + source = allsource + offset; + + buffer = ri.Hunk_AllocateTempMemory(128 * 128*3 + 18); + Com_Memset (buffer, 0, 18); + buffer[2] = 2; // uncompressed type + buffer[12] = 128; + buffer[14] = 128; + buffer[16] = 24; // pixel size + + // resample from source + xScale = glConfig.vidWidth / 512.0f; + yScale = glConfig.vidHeight / 384.0f; + for ( y = 0 ; y < 128 ; y++ ) { + for ( x = 0 ; x < 128 ; x++ ) { + r = g = b = 0; + for ( yy = 0 ; yy < 3 ; yy++ ) { + for ( xx = 0 ; xx < 4 ; xx++ ) { + src = source + (3 * glConfig.vidWidth + padlen) * (int)((y*3 + yy) * yScale) + + 3 * (int) ((x*4 + xx) * xScale); + r += src[0]; + g += src[1]; + b += src[2]; + } + } + dst = buffer + 18 + 3 * ( y * 128 + x ); + dst[0] = b / 12; + dst[1] = g / 12; + dst[2] = r / 12; + } + } + + // gamma correct + if ( glConfig.deviceSupportsGamma ) { + R_GammaCorrect( buffer + 18, 128 * 128 * 3 ); + } + + ri.FS_WriteFile( checkname, buffer, 128 * 128*3 + 18 ); + + ri.Hunk_FreeTempMemory(buffer); + ri.Hunk_FreeTempMemory(allsource); + + ri.Printf( PRINT_ALL, "Wrote %s\n", checkname ); +} + +/* +================== +R_ScreenShot_f + +screenshot +screenshot [silent] +screenshot [levelshot] +screenshot [filename] + +Doesn't print the pacifier message if there is a second arg +================== +*/ +void R_ScreenShot_f (void) { + char checkname[MAX_OSPATH]; + static int lastNumber = -1; + qboolean silent; + + if ( !strcmp( ri.Cmd_Argv(1), "levelshot" ) ) { + R_LevelShot(); + return; + } + + if ( !strcmp( ri.Cmd_Argv(1), "silent" ) ) { + silent = qtrue; + } else { + silent = qfalse; + } + + if ( ri.Cmd_Argc() == 2 && !silent ) { + // explicit filename + Com_sprintf( checkname, MAX_OSPATH, "screenshots/%s.tga", ri.Cmd_Argv( 1 ) ); + } else { + // scan for a free filename + + // if we have saved a previous screenshot, don't scan + // again, because recording demo avis can involve + // thousands of shots + if ( lastNumber == -1 ) { + lastNumber = 0; + } + // scan for a free number + for ( ; lastNumber <= 9999 ; lastNumber++ ) { + R_ScreenshotFilename( lastNumber, checkname ); + + if (!ri.FS_FileExists( checkname )) + { + break; // file doesn't exist + } + } + + if ( lastNumber >= 9999 ) { + ri.Printf (PRINT_ALL, "ScreenShot: Couldn't create a file\n"); + return; + } + + lastNumber++; + } + + R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, qfalse ); + + if ( !silent ) { + ri.Printf (PRINT_ALL, "Wrote %s\n", checkname); + } +} + +void R_ScreenShotJPEG_f (void) { + char checkname[MAX_OSPATH]; + static int lastNumber = -1; + qboolean silent; + + if ( !strcmp( ri.Cmd_Argv(1), "levelshot" ) ) { + R_LevelShot(); + return; + } + + if ( !strcmp( ri.Cmd_Argv(1), "silent" ) ) { + silent = qtrue; + } else { + silent = qfalse; + } + + if ( ri.Cmd_Argc() == 2 && !silent ) { + // explicit filename + Com_sprintf( checkname, MAX_OSPATH, "screenshots/%s.jpg", ri.Cmd_Argv( 1 ) ); + } else { + // scan for a free filename + + // if we have saved a previous screenshot, don't scan + // again, because recording demo avis can involve + // thousands of shots + if ( lastNumber == -1 ) { + lastNumber = 0; + } + // scan for a free number + for ( ; lastNumber <= 9999 ; lastNumber++ ) { + R_ScreenshotFilenameJPEG( lastNumber, checkname ); + + if (!ri.FS_FileExists( checkname )) + { + break; // file doesn't exist + } + } + + if ( lastNumber == 10000 ) { + ri.Printf (PRINT_ALL, "ScreenShot: Couldn't create a file\n"); + return; + } + + lastNumber++; + } + + R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, qtrue ); + + if ( !silent ) { + ri.Printf (PRINT_ALL, "Wrote %s\n", checkname); + } +} + +//============================================================================ + +/* +================== +RB_TakeVideoFrameCmd +================== +*/ +const void *RB_TakeVideoFrameCmd( const void *data ) +{ + const videoFrameCommand_t *cmd; + byte *cBuf; + size_t memcount, linelen; + int padwidth, avipadwidth, padlen, avipadlen; + GLint packAlign; + + cmd = (const videoFrameCommand_t *)data; + + qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); + + linelen = cmd->width * 3; + + // Alignment stuff for glReadPixels + padwidth = PAD(linelen, packAlign); + padlen = padwidth - linelen; + // AVI line padding + avipadwidth = PAD(linelen, AVI_LINE_PADDING); + avipadlen = avipadwidth - linelen; + + cBuf = PADP(cmd->captureBuffer, packAlign); + + qglReadPixels(0, 0, cmd->width, cmd->height, GL_RGB, + GL_UNSIGNED_BYTE, cBuf); + + memcount = padwidth * cmd->height; + + // gamma correct + if(glConfig.deviceSupportsGamma) + R_GammaCorrect(cBuf, memcount); + + if(cmd->motionJpeg) + { + memcount = RE_SaveJPGToBuffer(cmd->encodeBuffer, linelen * cmd->height, + r_aviMotionJpegQuality->integer, + cmd->width, cmd->height, cBuf, padlen); + ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, memcount); + } + else + { + byte *lineend, *memend; + byte *srcptr, *destptr; + + srcptr = cBuf; + destptr = cmd->encodeBuffer; + memend = srcptr + memcount; + + // swap R and B and remove line paddings + while(srcptr < memend) + { + lineend = srcptr + linelen; + while(srcptr < lineend) + { + *destptr++ = srcptr[2]; + *destptr++ = srcptr[1]; + *destptr++ = srcptr[0]; + srcptr += 3; + } + + Com_Memset(destptr, '\0', avipadlen); + destptr += avipadlen; + + srcptr += padlen; + } + + ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, avipadwidth * cmd->height); + } + + return (const void *)(cmd + 1); +} + +//============================================================================ + +/* +** GL_SetDefaultState +*/ +void GL_SetDefaultState( void ) +{ + qglClearDepth( 1.0f ); + + qglCullFace(GL_FRONT); + + qglColor4f (1,1,1,1); + + // initialize downstream texture unit if we're running + // in a multitexture environment + if ( qglActiveTextureARB ) { + GL_SelectTexture( 1 ); + GL_TextureMode( r_textureMode->string ); + GL_TexEnv( GL_MODULATE ); + qglDisable( GL_TEXTURE_2D ); + GL_SelectTexture( 0 ); + } + + qglEnable(GL_TEXTURE_2D); + GL_TextureMode( r_textureMode->string ); + GL_TexEnv( GL_MODULATE ); + + qglShadeModel( GL_SMOOTH ); + qglDepthFunc( GL_LEQUAL ); + + // the vertex array is always enabled, but the color and texture + // arrays are enabled and disabled around the compiled vertex array call + qglEnableClientState (GL_VERTEX_ARRAY); + + // + // make sure our GL state vector is set correctly + // + glState.glStateBits = GLS_DEPTHTEST_DISABLE | GLS_DEPTHMASK_TRUE; + + qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + qglDepthMask( GL_TRUE ); + qglDisable( GL_DEPTH_TEST ); + qglEnable( GL_SCISSOR_TEST ); + qglDisable( GL_CULL_FACE ); + qglDisable( GL_BLEND ); +} + +/* +================ +R_PrintLongString + +Workaround for ri.Printf's 1024 characters buffer limit. +================ +*/ +void R_PrintLongString(const char *string) { + char buffer[1024]; + const char *p; + int size = strlen(string); + + p = string; + while(size > 0) + { + Q_strncpyz(buffer, p, sizeof (buffer) ); + ri.Printf( PRINT_ALL, "%s", buffer ); + p += 1023; + size -= 1023; + } +} + +/* +================ +GfxInfo_f +================ +*/ +void GfxInfo_f( void ) +{ + const char *enablestrings[] = + { + "disabled", + "enabled" + }; + const char *fsstrings[] = + { + "windowed", + "fullscreen" + }; + + ri.Printf( PRINT_ALL, "\nGL_VENDOR: %s\n", glConfig.vendor_string ); + ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", glConfig.renderer_string ); + ri.Printf( PRINT_ALL, "GL_VERSION: %s\n", glConfig.version_string ); + ri.Printf( PRINT_ALL, "GL_EXTENSIONS: " ); + R_PrintLongString( glConfig.extensions_string ); + ri.Printf( PRINT_ALL, "\n" ); + ri.Printf( PRINT_ALL, "GL_MAX_TEXTURE_SIZE: %d\n", glConfig.maxTextureSize ); + ri.Printf( PRINT_ALL, "GL_MAX_TEXTURE_UNITS_ARB: %d\n", glConfig.numTextureUnits ); + ri.Printf( PRINT_ALL, "\nPIXELFORMAT: color(%d-bits) Z(%d-bit) stencil(%d-bits)\n", glConfig.colorBits, glConfig.depthBits, glConfig.stencilBits ); + ri.Printf( PRINT_ALL, "MODE: %d x %d %s hz:", glConfig.vidWidth, glConfig.vidHeight, fsstrings[r_fullscreen->integer == 1] ); + if ( glConfig.displayFrequency ) + { + ri.Printf( PRINT_ALL, "%d\n", glConfig.displayFrequency ); + } + else + { + ri.Printf( PRINT_ALL, "N/A\n" ); + } + if ( glConfig.deviceSupportsGamma ) + { + ri.Printf( PRINT_ALL, "GAMMA: hardware w/ %d overbright bits\n", tr.overbrightBits ); + } + else + { + ri.Printf( PRINT_ALL, "GAMMA: software w/ %d overbright bits\n", tr.overbrightBits ); + } + + // rendering primitives + { + int primitives; + + // default is to use triangles if compiled vertex arrays are present + ri.Printf( PRINT_ALL, "rendering primitives: " ); + primitives = r_primitives->integer; + if ( primitives == 0 ) { + if ( qglLockArraysEXT ) { + primitives = 2; + } else { + primitives = 1; + } + } + if ( primitives == -1 ) { + ri.Printf( PRINT_ALL, "none\n" ); + } else if ( primitives == 2 ) { + ri.Printf( PRINT_ALL, "single glDrawElements\n" ); + } else if ( primitives == 1 ) { + ri.Printf( PRINT_ALL, "multiple glArrayElement\n" ); + } else if ( primitives == 3 ) { + ri.Printf( PRINT_ALL, "multiple glColor4ubv + glTexCoord2fv + glVertex3fv\n" ); + } + } + + ri.Printf( PRINT_ALL, "texturemode: %s\n", r_textureMode->string ); + ri.Printf( PRINT_ALL, "picmip: %d\n", r_picmip->integer ); + ri.Printf( PRINT_ALL, "texture bits: %d\n", r_texturebits->integer ); + ri.Printf( PRINT_ALL, "multitexture: %s\n", enablestrings[qglActiveTextureARB != 0] ); + ri.Printf( PRINT_ALL, "compiled vertex arrays: %s\n", enablestrings[qglLockArraysEXT != 0 ] ); + ri.Printf( PRINT_ALL, "texenv add: %s\n", enablestrings[glConfig.textureEnvAddAvailable != 0] ); + ri.Printf( PRINT_ALL, "compressed textures: %s\n", enablestrings[glConfig.textureCompression!=TC_NONE] ); + if ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) + { + ri.Printf( PRINT_ALL, "HACK: using vertex lightmap approximation\n" ); + } + if ( glConfig.hardwareType == GLHW_RAGEPRO ) + { + ri.Printf( PRINT_ALL, "HACK: ragePro approximations\n" ); + } + if ( glConfig.hardwareType == GLHW_RIVA128 ) + { + ri.Printf( PRINT_ALL, "HACK: riva128 approximations\n" ); + } + if ( r_finish->integer ) { + ri.Printf( PRINT_ALL, "Forcing glFinish\n" ); + } +} + +/* +=============== +R_Register +=============== +*/ +void R_Register( void ) +{ + #ifdef USE_RENDERER_DLOPEN + com_altivec = ri.Cvar_Get("com_altivec", "1", CVAR_ARCHIVE); + #endif + + // + // latched and archived variables + // + r_allowExtensions = ri.Cvar_Get( "r_allowExtensions", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_ext_compressed_textures = ri.Cvar_Get( "r_ext_compressed_textures", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_ext_multitexture = ri.Cvar_Get( "r_ext_multitexture", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_ext_compiled_vertex_array = ri.Cvar_Get( "r_ext_compiled_vertex_array", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_texture_env_add = ri.Cvar_Get( "r_ext_texture_env_add", "1", CVAR_ARCHIVE | CVAR_LATCH); + + r_picmip = ri.Cvar_Get ("r_picmip", GENERIC_HW_R_PICMIP_DEFAULT, + CVAR_ARCHIVE | CVAR_LATCH ); + r_ext_texture_filter_anisotropic = ri.Cvar_Get( "r_ext_texture_filter_anisotropic", + "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_ext_max_anisotropy = ri.Cvar_Get( "r_ext_max_anisotropy", "2", CVAR_ARCHIVE | CVAR_LATCH ); + + r_roundImagesDown = ri.Cvar_Get ("r_roundImagesDown", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_colorMipLevels = ri.Cvar_Get ("r_colorMipLevels", "0", CVAR_LATCH ); + ri.Cvar_CheckRange( r_picmip, 0, 16, qtrue ); + r_detailTextures = ri.Cvar_Get( "r_detailtextures", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_texturebits = ri.Cvar_Get( "r_texturebits", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_colorbits = ri.Cvar_Get( "r_colorbits", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_stencilbits = ri.Cvar_Get( "r_stencilbits", "8", CVAR_ARCHIVE | CVAR_LATCH ); + r_depthbits = ri.Cvar_Get( "r_depthbits", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_ext_multisample = ri.Cvar_Get( "r_ext_multisample", "0", CVAR_ARCHIVE | CVAR_LATCH ); + ri.Cvar_CheckRange( r_ext_multisample, 0, 4, qtrue ); + r_overBrightBits = ri.Cvar_Get ("r_overBrightBits", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_ignorehwgamma = ri.Cvar_Get( "r_ignorehwgamma", "0", CVAR_ARCHIVE | CVAR_LATCH); + r_fullscreen = ri.Cvar_Get( "r_fullscreen", "1", CVAR_ARCHIVE ); + r_noborder = ri.Cvar_Get("r_noborder", "0", CVAR_ARCHIVE); + r_width = ri.Cvar_Get( "r_width", "640", CVAR_ARCHIVE | CVAR_LATCH ); + r_height = ri.Cvar_Get( "r_height", "480", CVAR_ARCHIVE | CVAR_LATCH ); + r_pixelAspect = ri.Cvar_Get( "r_pixelAspect", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_simpleMipMaps = ri.Cvar_Get( "r_simpleMipMaps", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_vertexLight = ri.Cvar_Get( "r_vertexLight", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_uiFullScreen = ri.Cvar_Get( "r_uifullscreen", "0", 0); + r_subdivisions = ri.Cvar_Get ("r_subdivisions", "4", CVAR_ARCHIVE | CVAR_LATCH); + r_stereoEnabled = ri.Cvar_Get( "r_stereoEnabled", "0", CVAR_ARCHIVE | CVAR_LATCH); + r_ignoreFastPath = ri.Cvar_Get( "r_ignoreFastPath", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_greyscale = ri.Cvar_Get("r_greyscale", "0", CVAR_ARCHIVE | CVAR_LATCH); + ri.Cvar_CheckRange(r_greyscale, 0, 1, qfalse); + + // + // temporary latched variables that can only change over a restart + // + r_fullbright = ri.Cvar_Get ("r_fullbright", "0", CVAR_LATCH|CVAR_CHEAT ); + r_mapOverBrightBits = ri.Cvar_Get ("r_mapOverBrightBits", "2", CVAR_LATCH ); + r_intensity = ri.Cvar_Get ("r_intensity", "1", CVAR_LATCH ); + r_singleShader = ri.Cvar_Get ("r_singleShader", "0", CVAR_CHEAT | CVAR_LATCH ); + + // + // archived variables that can change at any time + // + r_lodCurveError = ri.Cvar_Get( "r_lodCurveError", "250", CVAR_ARCHIVE|CVAR_CHEAT ); + r_lodbias = ri.Cvar_Get( "r_lodbias", "0", CVAR_ARCHIVE ); + r_flares = ri.Cvar_Get ("r_flares", "0", CVAR_ARCHIVE ); + r_znear = ri.Cvar_Get( "r_znear", "1", CVAR_CHEAT ); + ri.Cvar_CheckRange( r_znear, 0.001f, 200, qfalse ); + r_zproj = ri.Cvar_Get( "r_zproj", "64", CVAR_ARCHIVE ); + r_stereoSeparation = ri.Cvar_Get( "r_stereoSeparation", "64", CVAR_ARCHIVE ); + r_ignoreGLErrors = ri.Cvar_Get( "r_ignoreGLErrors", "1", CVAR_ARCHIVE ); + r_fastsky = ri.Cvar_Get( "r_fastsky", "0", CVAR_ARCHIVE ); + r_inGameVideo = ri.Cvar_Get( "r_inGameVideo", "1", CVAR_ARCHIVE ); + r_drawSun = ri.Cvar_Get( "r_drawSun", "0", CVAR_ARCHIVE ); + r_dynamiclight = ri.Cvar_Get( "r_dynamiclight", "1", CVAR_ARCHIVE ); + r_dlightBacks = ri.Cvar_Get( "r_dlightBacks", "1", CVAR_ARCHIVE ); + r_finish = ri.Cvar_Get ("r_finish", "0", CVAR_ARCHIVE); + r_textureMode = ri.Cvar_Get( "r_textureMode", + GENERIC_HW_R_TEXTUREMODE_DEFAULT, CVAR_ARCHIVE ); + r_swapInterval = ri.Cvar_Get( "r_swapInterval", "0", + CVAR_ARCHIVE | CVAR_LATCH ); + r_gamma = ri.Cvar_Get( "r_gamma", "1", CVAR_ARCHIVE ); + r_facePlaneCull = ri.Cvar_Get ("r_facePlaneCull", "1", CVAR_ARCHIVE ); + + r_railWidth = ri.Cvar_Get( "r_railWidth", "16", CVAR_ARCHIVE ); + r_railCoreWidth = ri.Cvar_Get( "r_railCoreWidth", "6", CVAR_ARCHIVE ); + r_railSegmentLength = ri.Cvar_Get( "r_railSegmentLength", "32", CVAR_ARCHIVE ); + + r_primitives = ri.Cvar_Get( "r_primitives", "0", CVAR_ARCHIVE ); + + r_ambientScale = ri.Cvar_Get( "r_ambientScale", "0.6", CVAR_CHEAT ); + r_directedScale = ri.Cvar_Get( "r_directedScale", "1", CVAR_CHEAT ); + + r_anaglyphMode = ri.Cvar_Get("r_anaglyphMode", "0", CVAR_ARCHIVE); + + // + // temporary variables that can change at any time + // + r_showImages = ri.Cvar_Get( "r_showImages", "0", CVAR_TEMP ); + + r_debugLight = ri.Cvar_Get( "r_debuglight", "0", CVAR_TEMP ); + r_debugSort = ri.Cvar_Get( "r_debugSort", "0", CVAR_CHEAT ); + r_printShaders = ri.Cvar_Get( "r_printShaders", "0", 0 ); + r_saveFontData = ri.Cvar_Get( "r_saveFontData", "0", 0 ); + + r_nocurves = ri.Cvar_Get ("r_nocurves", "0", CVAR_CHEAT ); + r_drawworld = ri.Cvar_Get ("r_drawworld", "1", CVAR_CHEAT ); + r_lightmap = ri.Cvar_Get ("r_lightmap", "0", CVAR_CHEAT ); + r_portalOnly = ri.Cvar_Get ("r_portalOnly", "0", CVAR_CHEAT ); + + r_flareSize = ri.Cvar_Get ("r_flareSize", "40", CVAR_CHEAT); + r_flareFade = ri.Cvar_Get ("r_flareFade", "7", CVAR_CHEAT); + r_flareCoeff = ri.Cvar_Get ("r_flareCoeff", FLARE_STDCOEFF, CVAR_CHEAT); + + r_skipBackEnd = ri.Cvar_Get ("r_skipBackEnd", "0", CVAR_CHEAT); + + r_measureOverdraw = ri.Cvar_Get( "r_measureOverdraw", "0", CVAR_CHEAT ); + r_lodscale = ri.Cvar_Get( "r_lodscale", "5", CVAR_CHEAT ); + r_norefresh = ri.Cvar_Get ("r_norefresh", "0", CVAR_CHEAT); + r_drawentities = ri.Cvar_Get ("r_drawentities", "1", CVAR_CHEAT ); + r_ignore = ri.Cvar_Get( "r_ignore", "1", CVAR_CHEAT ); + r_nocull = ri.Cvar_Get ("r_nocull", "0", CVAR_CHEAT); + r_novis = ri.Cvar_Get ("r_novis", "0", CVAR_CHEAT); + r_showcluster = ri.Cvar_Get ("r_showcluster", "0", CVAR_CHEAT); + r_speeds = ri.Cvar_Get ("r_speeds", "0", CVAR_CHEAT); + r_verbose = ri.Cvar_Get( "r_verbose", "0", CVAR_CHEAT ); + r_logFile = ri.Cvar_Get( "r_logFile", "0", CVAR_CHEAT ); + r_debugSurface = ri.Cvar_Get ("r_debugSurface", "0", CVAR_CHEAT); + r_nobind = ri.Cvar_Get ("r_nobind", "0", CVAR_CHEAT); + r_showtris = ri.Cvar_Get ("r_showtris", "0", CVAR_CHEAT); + r_showsky = ri.Cvar_Get ("r_showsky", "0", CVAR_CHEAT); + r_shownormals = ri.Cvar_Get ("r_shownormals", "0", CVAR_CHEAT); + r_clear = ri.Cvar_Get ("r_clear", "0", CVAR_CHEAT); + r_offsetFactor = ri.Cvar_Get( "r_offsetfactor", "-1", CVAR_CHEAT ); + r_offsetUnits = ri.Cvar_Get( "r_offsetunits", "-2", CVAR_CHEAT ); + r_drawBuffer = ri.Cvar_Get( "r_drawBuffer", "GL_BACK", CVAR_CHEAT ); + r_lockpvs = ri.Cvar_Get ("r_lockpvs", "0", CVAR_CHEAT); + r_noportals = ri.Cvar_Get ("r_noportals", "0", CVAR_CHEAT); + r_shadows = ri.Cvar_Get( "cg_shadows", "1", 0 ); + + r_marksOnTriangleMeshes = ri.Cvar_Get("r_marksOnTriangleMeshes", "0", CVAR_ARCHIVE); + + r_aviMotionJpegQuality = ri.Cvar_Get("r_aviMotionJpegQuality", "90", CVAR_ARCHIVE); + r_screenshotJpegQuality = ri.Cvar_Get("r_screenshotJpegQuality", "90", CVAR_ARCHIVE); + + r_maxpolys = ri.Cvar_Get( "r_maxpolys", va("%d", MAX_POLYS), 0); + r_maxpolyverts = ri.Cvar_Get( "r_maxpolyverts", va("%d", MAX_POLYVERTS), 0); + + // make sure all the commands added here are also + // removed in R_Shutdown + ri.Cmd_AddCommand( "imagelist", R_ImageList_f ); + ri.Cmd_AddCommand( "shaderlist", R_ShaderList_f ); + ri.Cmd_AddCommand( "skinlist", R_SkinList_f ); + ri.Cmd_AddCommand( "modellist", R_Modellist_f ); + ri.Cmd_AddCommand( "screenshot", R_ScreenShot_f ); + ri.Cmd_AddCommand( "screenshotJPEG", R_ScreenShotJPEG_f ); + ri.Cmd_AddCommand( "gfxinfo", GfxInfo_f ); + ri.Cmd_AddCommand( "minimize", GLimp_Minimize ); +} + +/* +=============== +R_Init +=============== +*/ +void R_Init( void ) { + int err; + int i; + byte *ptr; + + ri.Printf( PRINT_ALL, "----- R_Init -----\n" ); + + // clear all our internal state + Com_Memset( &tr, 0, sizeof( tr ) ); + Com_Memset( &backEnd, 0, sizeof( backEnd ) ); + Com_Memset( &tess, 0, sizeof( tess ) ); + +// Swap_Init(); + + if ( (intptr_t)tess.xyz & 15 ) { + ri.Printf( PRINT_WARNING, "tess.xyz not 16 byte aligned\n" ); + } + Com_Memset( tess.constantColor255, 255, sizeof( tess.constantColor255 ) ); + + // + // init function tables + // + for ( i = 0; i < FUNCTABLE_SIZE; i++ ) + { + tr.sinTable[i] = sin( DEG2RAD( i * 360.0f / ( ( float ) ( FUNCTABLE_SIZE - 1 ) ) ) ); + tr.squareTable[i] = ( i < FUNCTABLE_SIZE/2 ) ? 1.0f : -1.0f; + tr.sawToothTable[i] = (float)i / FUNCTABLE_SIZE; + tr.inverseSawToothTable[i] = 1.0f - tr.sawToothTable[i]; + + if ( i < FUNCTABLE_SIZE / 2 ) + { + if ( i < FUNCTABLE_SIZE / 4 ) + { + tr.triangleTable[i] = ( float ) i / ( FUNCTABLE_SIZE / 4 ); + } + else + { + tr.triangleTable[i] = 1.0f - tr.triangleTable[i-FUNCTABLE_SIZE / 4]; + } + } + else + { + tr.triangleTable[i] = -tr.triangleTable[i-FUNCTABLE_SIZE/2]; + } + } + + R_InitFogTable(); + + R_NoiseInit(); + + R_Register(); + + max_polys = r_maxpolys->integer; + if (max_polys < MAX_POLYS) + max_polys = MAX_POLYS; + + max_polyverts = r_maxpolyverts->integer; + if (max_polyverts < MAX_POLYVERTS) + max_polyverts = MAX_POLYVERTS; + + ptr = ri.Hunk_Alloc( sizeof( *backEndData ) + sizeof(srfPoly_t) * max_polys + sizeof(polyVert_t) * max_polyverts, h_low); + backEndData = (backEndData_t *) ptr; + backEndData->polys = (srfPoly_t *) ((char *) ptr + sizeof( *backEndData )); + backEndData->polyVerts = (polyVert_t *) ((char *) ptr + sizeof( *backEndData ) + sizeof(srfPoly_t) * max_polys); + R_InitNextFrame(); + + InitOpenGL(); + + R_InitImages(); + + R_InitShaders(); + + R_InitSkins(); + + R_ModelInit(); + + R_InitFreeType(); + + + err = qglGetError(); + if ( err != GL_NO_ERROR ) + ri.Printf (PRINT_ALL, "glGetError() = 0x%x\n", err); + + // print info + GfxInfo_f(); + ri.Printf( PRINT_ALL, "----- finished R_Init -----\n" ); +} + +/* +=============== +RE_Shutdown +=============== +*/ +void RE_Shutdown( qboolean destroyWindow ) { + + ri.Printf( PRINT_ALL, "RE_Shutdown( %i )\n", destroyWindow ); + + ri.Cmd_RemoveCommand ("modellist"); + ri.Cmd_RemoveCommand ("screenshotJPEG"); + ri.Cmd_RemoveCommand ("screenshot"); + ri.Cmd_RemoveCommand ("imagelist"); + ri.Cmd_RemoveCommand ("shaderlist"); + ri.Cmd_RemoveCommand ("skinlist"); + ri.Cmd_RemoveCommand ("gfxinfo"); + ri.Cmd_RemoveCommand("minimize"); + ri.Cmd_RemoveCommand( "shaderstate" ); + + + if ( tr.registered ) { + R_IssuePendingRenderCommands(); + R_DeleteTextures(); + } + + R_DoneFreeType(); + + // shut down platform specific OpenGL stuff + if ( destroyWindow ) { + GLimp_Shutdown(); + } + + tr.registered = qfalse; +} + + +/* +============= +RE_EndRegistration + +Touch all images to make sure they are resident +============= +*/ +void RE_EndRegistration( void ) { + R_IssuePendingRenderCommands(); + if (!ri.Sys_LowPhysicalMemory()) { + RB_ShowImages(); + } +} + + +/* +@@@@@@@@@@@@@@@@@@@@@ +GetRefAPI + +@@@@@@@@@@@@@@@@@@@@@ +*/ +#ifdef USE_RENDERER_DLOPEN +Q_EXPORT refexport_t* QDECL GetRefAPI ( int apiVersion, refimport_t *rimp ) { +#else +refexport_t *GetRefAPI ( int apiVersion, refimport_t *rimp ) { +#endif + + static refexport_t re; + + ri = *rimp; + + Com_Memset( &re, 0, sizeof( re ) ); + + if ( apiVersion != REF_API_VERSION ) { + ri.Printf(PRINT_ALL, "Mismatched REF_API_VERSION: expected %i, got %i\n", + REF_API_VERSION, apiVersion ); + return NULL; + } + + // the RE_ functions are Renderer Entry points + + re.Shutdown = RE_Shutdown; + + re.BeginRegistration = RE_BeginRegistration; + re.RegisterModel = RE_RegisterModel; + re.RegisterSkin = RE_RegisterSkin; + re.RegisterShader = RE_RegisterShader; + re.RegisterShaderNoMip = RE_RegisterShaderNoMip; + re.LoadWorld = RE_LoadWorldMap; + re.SetWorldVisData = RE_SetWorldVisData; + re.EndRegistration = RE_EndRegistration; + + re.BeginFrame = RE_BeginFrame; + re.EndFrame = RE_EndFrame; + + re.MarkFragments = R_MarkFragments; + re.LerpTag = R_LerpTag; + re.ModelBounds = R_ModelBounds; + + re.ClearScene = RE_ClearScene; + re.AddRefEntityToScene = RE_AddRefEntityToScene; + re.AddPolyToScene = RE_AddPolyToScene; + re.LightForPoint = R_LightForPoint; + re.AddLightToScene = RE_AddLightToScene; + re.AddAdditiveLightToScene = RE_AddAdditiveLightToScene; + re.RenderScene = RE_RenderScene; + + re.SetColor = RE_SetColor; + re.SetClipRegion = RE_SetClipRegion; + re.DrawStretchPic = RE_StretchPic; + re.DrawStretchRaw = RE_StretchRaw; + re.UploadCinematic = RE_UploadCinematic; + + re.RegisterFont = RE_RegisterFont; + re.RemapShader = R_RemapShader; + re.GetEntityToken = R_GetEntityToken; + re.inPVS = R_inPVS; + + re.TakeVideoFrame = RE_TakeVideoFrame; + + return &re; +} diff --git a/src/renderergl1/tr_light.c b/src/renderergl1/tr_light.c new file mode 100644 index 00000000..36c1c480 --- /dev/null +++ b/src/renderergl1/tr_light.c @@ -0,0 +1,395 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_light.c + +#include "tr_local.h" + +#define DLIGHT_AT_RADIUS 16 +// at the edge of a dlight's influence, this amount of light will be added + +#define DLIGHT_MINIMUM_RADIUS 16 +// never calculate a range less than this to prevent huge light numbers + + +/* +=============== +R_TransformDlights + +Transforms the origins of an array of dlights. +Used by both the front end (for DlightBmodel) and +the back end (before doing the lighting calculation) +=============== +*/ +void R_TransformDlights( int count, dlight_t *dl, orientationr_t *or) { + int i; + vec3_t temp; + + for ( i = 0 ; i < count ; i++, dl++ ) { + VectorSubtract( dl->origin, or->origin, temp ); + dl->transformed[0] = DotProduct( temp, or->axis[0] ); + dl->transformed[1] = DotProduct( temp, or->axis[1] ); + dl->transformed[2] = DotProduct( temp, or->axis[2] ); + } +} + +/* +============= +R_DlightBmodel + +Determine which dynamic lights may effect this bmodel +============= +*/ +void R_DlightBmodel( bmodel_t *bmodel ) { + int i, j; + dlight_t *dl; + int mask; + msurface_t *surf; + + // transform all the lights + R_TransformDlights( tr.refdef.num_dlights, tr.refdef.dlights, &tr.or ); + + mask = 0; + for ( i=0 ; itransformed[j] - bmodel->bounds[1][j] > dl->radius ) { + break; + } + if ( bmodel->bounds[0][j] - dl->transformed[j] > dl->radius ) { + break; + } + } + if ( j < 3 ) { + continue; + } + + // we need to check this light + mask |= 1 << i; + } + + tr.currentEntity->needDlights = (mask != 0); + + // set the dlight bits in all the surfaces + for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) { + surf = bmodel->firstSurface + i; + + if ( *surf->data == SF_FACE ) { + ((srfSurfaceFace_t *)surf->data)->dlightBits = mask; + } else if ( *surf->data == SF_GRID ) { + ((srfGridMesh_t *)surf->data)->dlightBits = mask; + } else if ( *surf->data == SF_TRIANGLES ) { + ((srfTriangles_t *)surf->data)->dlightBits = mask; + } + } +} + + +/* +============================================================================= + +LIGHT SAMPLING + +============================================================================= +*/ + +extern cvar_t *r_ambientScale; +extern cvar_t *r_directedScale; +extern cvar_t *r_debugLight; + +/* +================= +R_SetupEntityLightingGrid + +================= +*/ +static void R_SetupEntityLightingGrid( trRefEntity_t *ent ) { + vec3_t lightOrigin; + int pos[3]; + int i, j; + byte *gridData; + float frac[3]; + int gridStep[3]; + vec3_t direction; + float totalFactor; + + if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) { + // seperate lightOrigins are needed so an object that is + // sinking into the ground can still be lit, and so + // multi-part models can be lit identically + VectorCopy( ent->e.lightingOrigin, lightOrigin ); + } else { + VectorCopy( ent->e.origin, lightOrigin ); + } + + VectorSubtract( lightOrigin, tr.world->lightGridOrigin, lightOrigin ); + for ( i = 0 ; i < 3 ; i++ ) { + float v; + + v = lightOrigin[i]*tr.world->lightGridInverseSize[i]; + pos[i] = floor( v ); + frac[i] = v - pos[i]; + if ( pos[i] < 0 ) { + pos[i] = 0; + } else if ( pos[i] >= tr.world->lightGridBounds[i] - 1 ) { + pos[i] = tr.world->lightGridBounds[i] - 1; + } + } + + VectorClear( ent->ambientLight ); + VectorClear( ent->directedLight ); + VectorClear( direction ); + + assert( tr.world->lightGridData ); // NULL with -nolight maps + + // trilerp the light value + gridStep[0] = 8; + gridStep[1] = 8 * tr.world->lightGridBounds[0]; + gridStep[2] = 8 * tr.world->lightGridBounds[0] * tr.world->lightGridBounds[1]; + gridData = tr.world->lightGridData + pos[0] * gridStep[0] + + pos[1] * gridStep[1] + pos[2] * gridStep[2]; + + totalFactor = 0; + for ( i = 0 ; i < 8 ; i++ ) { + float factor; + byte *data; + int lat, lng; + vec3_t normal; + #if idppc + float d0, d1, d2, d3, d4, d5; + #endif + factor = 1.0; + data = gridData; + for ( j = 0 ; j < 3 ; j++ ) { + if ( i & (1<ambientLight[0] += factor * d0; + ent->ambientLight[1] += factor * d1; + ent->ambientLight[2] += factor * d2; + + ent->directedLight[0] += factor * d3; + ent->directedLight[1] += factor * d4; + ent->directedLight[2] += factor * d5; + #else + ent->ambientLight[0] += factor * data[0]; + ent->ambientLight[1] += factor * data[1]; + ent->ambientLight[2] += factor * data[2]; + + ent->directedLight[0] += factor * data[3]; + ent->directedLight[1] += factor * data[4]; + ent->directedLight[2] += factor * data[5]; + #endif + lat = data[7]; + lng = data[6]; + lat *= (FUNCTABLE_SIZE/256); + lng *= (FUNCTABLE_SIZE/256); + + // decode X as cos( lat ) * sin( long ) + // decode Y as sin( lat ) * sin( long ) + // decode Z as cos( long ) + + normal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; + normal[1] = tr.sinTable[lat] * tr.sinTable[lng]; + normal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; + + VectorMA( direction, factor, normal, direction ); + } + + if ( totalFactor > 0 && totalFactor < 0.99 ) { + totalFactor = 1.0f / totalFactor; + VectorScale( ent->ambientLight, totalFactor, ent->ambientLight ); + VectorScale( ent->directedLight, totalFactor, ent->directedLight ); + } + + VectorScale( ent->ambientLight, r_ambientScale->value, ent->ambientLight ); + VectorScale( ent->directedLight, r_directedScale->value, ent->directedLight ); + + VectorNormalize2( direction, ent->lightDir ); +} + + +/* +=============== +LogLight +=============== +*/ +static void LogLight( trRefEntity_t *ent ) { + int max1, max2; + + if ( !(ent->e.renderfx & RF_FIRST_PERSON ) ) { + return; + } + + max1 = ent->ambientLight[0]; + if ( ent->ambientLight[1] > max1 ) { + max1 = ent->ambientLight[1]; + } else if ( ent->ambientLight[2] > max1 ) { + max1 = ent->ambientLight[2]; + } + + max2 = ent->directedLight[0]; + if ( ent->directedLight[1] > max2 ) { + max2 = ent->directedLight[1]; + } else if ( ent->directedLight[2] > max2 ) { + max2 = ent->directedLight[2]; + } + + ri.Printf( PRINT_ALL, "amb:%i dir:%i\n", max1, max2 ); +} + +/* +================= +R_SetupEntityLighting + +Calculates all the lighting values that will be used +by the Calc_* functions +================= +*/ +void R_SetupEntityLighting( const trRefdef_t *refdef, trRefEntity_t *ent ) { + int i; + dlight_t *dl; + float power; + vec3_t dir; + float d; + vec3_t lightDir; + vec3_t lightOrigin; + + // lighting calculations + if ( ent->lightingCalculated ) { + return; + } + ent->lightingCalculated = qtrue; + + // + // trace a sample point down to find ambient light + // + if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) { + // seperate lightOrigins are needed so an object that is + // sinking into the ground can still be lit, and so + // multi-part models can be lit identically + VectorCopy( ent->e.lightingOrigin, lightOrigin ); + } else { + VectorCopy( ent->e.origin, lightOrigin ); + } + + // if NOWORLDMODEL, only use dynamic lights (menu system, etc) + if ( !(refdef->rdflags & RDF_NOWORLDMODEL ) + && tr.world->lightGridData ) { + R_SetupEntityLightingGrid( ent ); + } else { + ent->ambientLight[0] = ent->ambientLight[1] = + ent->ambientLight[2] = tr.identityLight * 150; + ent->directedLight[0] = ent->directedLight[1] = + ent->directedLight[2] = tr.identityLight * 150; + VectorCopy( tr.sunDirection, ent->lightDir ); + } + + // bonus items and view weapons have a fixed minimum add + if ( 1 /* ent->e.renderfx & RF_MINLIGHT */ ) { + // give everything a minimum light add + ent->ambientLight[0] += tr.identityLight * 32; + ent->ambientLight[1] += tr.identityLight * 32; + ent->ambientLight[2] += tr.identityLight * 32; + } + + // + // modify the light by dynamic lights + // + d = VectorLength( ent->directedLight ); + VectorScale( ent->lightDir, d, lightDir ); + + for ( i = 0 ; i < refdef->num_dlights ; i++ ) { + dl = &refdef->dlights[i]; + VectorSubtract( dl->origin, lightOrigin, dir ); + d = VectorNormalize( dir ); + + power = DLIGHT_AT_RADIUS * ( dl->radius * dl->radius ); + if ( d < DLIGHT_MINIMUM_RADIUS ) { + d = DLIGHT_MINIMUM_RADIUS; + } + d = power / ( d * d ); + + VectorMA( ent->directedLight, d, dl->color, ent->directedLight ); + VectorMA( lightDir, d, dir, lightDir ); + } + + // clamp ambient + for ( i = 0 ; i < 3 ; i++ ) { + if ( ent->ambientLight[i] > tr.identityLightByte ) { + ent->ambientLight[i] = tr.identityLightByte; + } + } + + if ( r_debugLight->integer ) { + LogLight( ent ); + } + + // save out the byte packet version + ((byte *)&ent->ambientLightInt)[0] = ri.ftol(ent->ambientLight[0]); + ((byte *)&ent->ambientLightInt)[1] = ri.ftol(ent->ambientLight[1]); + ((byte *)&ent->ambientLightInt)[2] = ri.ftol(ent->ambientLight[2]); + ((byte *)&ent->ambientLightInt)[3] = 0xff; + + // transform the direction to local space + VectorNormalize( lightDir ); + ent->lightDir[0] = DotProduct( lightDir, ent->e.axis[0] ); + ent->lightDir[1] = DotProduct( lightDir, ent->e.axis[1] ); + ent->lightDir[2] = DotProduct( lightDir, ent->e.axis[2] ); +} + +/* +================= +R_LightForPoint +================= +*/ +int R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ) +{ + trRefEntity_t ent; + + if ( tr.world->lightGridData == NULL ) + return qfalse; + + Com_Memset(&ent, 0, sizeof(ent)); + VectorCopy( point, ent.e.origin ); + R_SetupEntityLightingGrid( &ent ); + VectorCopy(ent.ambientLight, ambientLight); + VectorCopy(ent.directedLight, directedLight); + VectorCopy(ent.lightDir, lightDir); + + return qtrue; +} diff --git a/src/renderergl1/tr_local.h b/src/renderergl1/tr_local.h new file mode 100644 index 00000000..3e6766f8 --- /dev/null +++ b/src/renderergl1/tr_local.h @@ -0,0 +1,1728 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + + +#ifndef TR_LOCAL_H +#define TR_LOCAL_H + +#include "../qcommon/q_shared.h" +#include "../qcommon/qfiles.h" +#include "../qcommon/qcommon.h" +#include "../renderercommon/tr_public.h" +#include "qgl.h" +#include "../renderercommon/iqm.h" + +#define GL_INDEX_TYPE GL_UNSIGNED_INT +typedef unsigned int glIndex_t; + +// 14 bits +// can't be increased without changing bit packing for drawsurfs +// see QSORT_SHADERNUM_SHIFT +#define SHADERNUM_BITS 14 +#define MAX_SHADERS (1<or.origin in local coordinates + float modelMatrix[16]; +} orientationr_t; + +typedef struct image_s { + char imgName[MAX_QPATH]; // game path, including extension + int width, height; // source image + int uploadWidth, uploadHeight; // after power of two and picmip but not including clamp to MAX_TEXTURE_SIZE + GLuint texnum; // gl texture binding + + int frameUsed; // for texture usage in frame statistics + + int internalFormat; + int TMU; // only needed for voodoo2 + + qboolean mipmap; + qboolean allowPicmip; + int wrapClampMode; // GL_CLAMP_TO_EDGE or GL_REPEAT + + struct image_s* next; +} image_t; + +//=============================================================================== + +typedef enum { + SS_BAD, + SS_PORTAL, // mirrors, portals, viewscreens + SS_ENVIRONMENT, // sky box + SS_OPAQUE, // opaque + + SS_DECAL, // scorch marks, etc. + SS_SEE_THROUGH, // ladders, grates, grills that may have small blended edges + // in addition to alpha test + SS_BANNER, + + SS_FOG, + + SS_UNDERWATER, // for items that should be drawn in front of the water plane + + SS_BLEND0, // regular transparency and filters + SS_BLEND1, // generally only used for additive type effects + SS_BLEND2, + SS_BLEND3, + + SS_BLEND6, + SS_STENCIL_SHADOW, + SS_ALMOST_NEAREST, // gun smoke puffs + + SS_NEAREST // blood blobs +} shaderSort_t; + + +#define MAX_SHADER_STAGES 8 + +typedef enum { + GF_NONE, + + GF_SIN, + GF_SQUARE, + GF_TRIANGLE, + GF_SAWTOOTH, + GF_INVERSE_SAWTOOTH, + + GF_NOISE + +} genFunc_t; + + +typedef enum { + DEFORM_NONE, + DEFORM_WAVE, + DEFORM_NORMALS, + DEFORM_BULGE, + DEFORM_MOVE, + DEFORM_PROJECTION_SHADOW, + DEFORM_AUTOSPRITE, + DEFORM_AUTOSPRITE2, + DEFORM_TEXT0, + DEFORM_TEXT1, + DEFORM_TEXT2, + DEFORM_TEXT3, + DEFORM_TEXT4, + DEFORM_TEXT5, + DEFORM_TEXT6, + DEFORM_TEXT7 +} deform_t; + +typedef enum { + AGEN_IDENTITY, + AGEN_SKIP, + AGEN_ENTITY, + AGEN_ONE_MINUS_ENTITY, + AGEN_VERTEX, + AGEN_ONE_MINUS_VERTEX, + AGEN_LIGHTING_SPECULAR, + AGEN_WAVEFORM, + AGEN_PORTAL, + AGEN_CONST +} alphaGen_t; + +typedef enum { + CGEN_BAD, + CGEN_IDENTITY_LIGHTING, // tr.identityLight + CGEN_IDENTITY, // always (1,1,1,1) + CGEN_ENTITY, // grabbed from entity's modulate field + CGEN_ONE_MINUS_ENTITY, // grabbed from 1 - entity.modulate + CGEN_EXACT_VERTEX, // tess.vertexColors + CGEN_VERTEX, // tess.vertexColors * tr.identityLight + CGEN_ONE_MINUS_VERTEX, + CGEN_WAVEFORM, // programmatically generated + CGEN_LIGHTING_DIFFUSE, + CGEN_FOG, // standard fog + CGEN_CONST // fixed color +} colorGen_t; + +typedef enum { + TCGEN_BAD, + TCGEN_IDENTITY, // clear to 0,0 + TCGEN_LIGHTMAP, + TCGEN_TEXTURE, + TCGEN_ENVIRONMENT_MAPPED, + TCGEN_FOG, + TCGEN_VECTOR // S and T from world coordinates +} texCoordGen_t; + +typedef enum { + ACFF_NONE, + ACFF_MODULATE_RGB, + ACFF_MODULATE_RGBA, + ACFF_MODULATE_ALPHA +} acff_t; + +typedef struct { + genFunc_t func; + + float base; + float amplitude; + float phase; + float frequency; +} waveForm_t; + +#define TR_MAX_TEXMODS 4 + +typedef enum { + TMOD_NONE, + TMOD_TRANSFORM, + TMOD_TURBULENT, + TMOD_SCROLL, + TMOD_SCALE, + TMOD_STRETCH, + TMOD_ROTATE, + TMOD_ENTITY_TRANSLATE +} texMod_t; + +#define MAX_SHADER_DEFORMS 3 +typedef struct { + deform_t deformation; // vertex coordinate modification type + + vec3_t moveVector; + waveForm_t deformationWave; + float deformationSpread; + + float bulgeWidth; + float bulgeHeight; + float bulgeSpeed; +} deformStage_t; + + +typedef struct { + texMod_t type; + + // used for TMOD_TURBULENT and TMOD_STRETCH + waveForm_t wave; + + // used for TMOD_TRANSFORM + float matrix[2][2]; // s' = s * m[0][0] + t * m[1][0] + trans[0] + float translate[2]; // t' = s * m[0][1] + t * m[0][1] + trans[1] + + // used for TMOD_SCALE + float scale[2]; // s *= scale[0] + // t *= scale[1] + + // used for TMOD_SCROLL + float scroll[2]; // s' = s + scroll[0] * time + // t' = t + scroll[1] * time + + // + = clockwise + // - = counterclockwise + float rotateSpeed; + +} texModInfo_t; + + +#define MAX_IMAGE_ANIMATIONS 8 + +typedef struct { + image_t *image[MAX_IMAGE_ANIMATIONS]; + int numImageAnimations; + float imageAnimationSpeed; + + texCoordGen_t tcGen; + vec3_t tcGenVectors[2]; + + int numTexMods; + texModInfo_t *texMods; + + int videoMapHandle; + qboolean isLightmap; + qboolean vertexLightmap; + qboolean isVideoMap; +} textureBundle_t; + +#define NUM_TEXTURE_BUNDLES 2 + +typedef struct { + qboolean active; + + textureBundle_t bundle[NUM_TEXTURE_BUNDLES]; + + waveForm_t rgbWave; + colorGen_t rgbGen; + + waveForm_t alphaWave; + alphaGen_t alphaGen; + + byte constantColor[4]; // for CGEN_CONST and AGEN_CONST + + unsigned stateBits; // GLS_xxxx mask + + acff_t adjustColorsForFog; + + qboolean isDetail; +} shaderStage_t; + +struct shaderCommands_s; + +// any change in the LIGHTMAP_* defines here MUST be reflected in +// R_FindShader() in tr_bsp.c +#define LIGHTMAP_2D -4 // shader is for 2D rendering +#define LIGHTMAP_BY_VERTEX -3 // pre-lit triangle models +#define LIGHTMAP_WHITEIMAGE -2 +#define LIGHTMAP_NONE -1 + +typedef enum { + CT_FRONT_SIDED, + CT_BACK_SIDED, + CT_TWO_SIDED +} cullType_t; + +typedef enum { + FP_NONE, // surface is translucent and will just be adjusted properly + FP_EQUAL, // surface is opaque but possibly alpha tested + FP_LE // surface is trnaslucent, but still needs a fog pass (fog surface) +} fogPass_t; + +typedef struct { + float cloudHeight; + image_t *outerbox[6], *innerbox[6]; +} skyParms_t; + +typedef struct { + vec3_t color; + float depthForOpaque; +} fogParms_t; + + +typedef struct shader_s { + char name[MAX_QPATH]; // game path, including extension + int lightmapIndex; // for a shader to match, both name and lightmapIndex must match + + int index; // this shader == tr.shaders[index] + int sortedIndex; // this shader == tr.sortedShaders[sortedIndex] + + float sort; // lower numbered shaders draw before higher numbered + + qboolean defaultShader; // we want to return index 0 if the shader failed to + // load for some reason, but R_FindShader should + // still keep a name allocated for it, so if + // something calls RE_RegisterShader again with + // the same name, we don't try looking for it again + + qboolean explicitlyDefined; // found in a .shader file + + int surfaceFlags; // if explicitlyDefined, this will have SURF_* flags + int contentFlags; + + qboolean entityMergable; // merge across entites optimizable (smoke, blood) + + qboolean isSky; + skyParms_t sky; + fogParms_t fogParms; + + float portalRange; // distance to fog out at + + int multitextureEnv; // 0, GL_MODULATE, GL_ADD (FIXME: put in stage) + + cullType_t cullType; // CT_FRONT_SIDED, CT_BACK_SIDED, or CT_TWO_SIDED + qboolean polygonOffset; // set for decals and other items that must be offset + qboolean noMipMaps; // for console fonts, 2D elements, etc. + qboolean noPicMip; // for images that must always be full resolution + + fogPass_t fogPass; // draw a blended pass, possibly with depth test equals + + qboolean needsNormal; // not all shaders will need all data to be gathered + qboolean needsST1; + qboolean needsST2; + qboolean needsColor; + + int numDeforms; + deformStage_t deforms[MAX_SHADER_DEFORMS]; + + int numUnfoggedPasses; + shaderStage_t *stages[MAX_SHADER_STAGES]; + + void (*optimalStageIteratorFunc)( void ); + + float clampTime; // time this shader is clamped to + float timeOffset; // current time offset for this shader + + int numStates; // if non-zero this is a state shader + struct shader_s *currentShader; // current state if this is a state shader + struct shader_s *parentShader; // current state if this is a state shader + int currentState; // current state index for cycle purposes + long expireTime; // time in milliseconds this expires + + struct shader_s *remappedShader; // current shader this one is remapped too + + int shaderStates[MAX_STATES_PER_SHADER]; // index to valid shader states + + struct shader_s *next; +} shader_t; + +typedef struct shaderState_s { + char shaderName[MAX_QPATH]; // name of shader this state belongs to + char name[MAX_STATE_NAME]; // name of this state + char stateShader[MAX_QPATH]; // shader this name invokes + int cycleTime; // time this cycle lasts, <= 0 is forever + shader_t *shader; +} shaderState_t; + + +// trRefdef_t holds everything that comes in refdef_t, +// as well as the locally generated scene information +typedef struct { + int x, y, width, height; + float fov_x, fov_y; + vec3_t vieworg; + vec3_t viewaxis[3]; // transformation matrix + + stereoFrame_t stereoFrame; + + int time; // time in milliseconds for shader effects and other time dependent rendering issues + int rdflags; // RDF_NOWORLDMODEL, etc + + // 1 bits will prevent the associated area from rendering at all + byte areamask[MAX_MAP_AREA_BYTES]; + qboolean areamaskModified; // qtrue if areamask changed since last scene + + float floatTime; // tr.refdef.time / 1000.0 + + // text messages for deform text shaders + char text[MAX_RENDER_STRINGS][MAX_RENDER_STRING_LENGTH]; + + int num_entities; + trRefEntity_t *entities; + + int num_dlights; + struct dlight_s *dlights; + + int numPolys; + struct srfPoly_s *polys; + + int numDrawSurfs; + struct drawSurf_s *drawSurfs; + + +} trRefdef_t; + + +//================================================================================= + +// skins allow models to be retextured without modifying the model file +typedef struct { + char name[MAX_QPATH]; + shader_t *shader; +} skinSurface_t; + +typedef struct skin_s { + char name[MAX_QPATH]; // game path, including extension + int numSurfaces; + skinSurface_t *surfaces[MD3_MAX_SURFACES]; +} skin_t; + + +typedef struct { + int originalBrushNumber; + vec3_t bounds[2]; + + unsigned colorInt; // in packed byte format + float tcScale; // texture coordinate vector scales + fogParms_t parms; + + // for clipping distance in fog when outside + qboolean hasSurface; + float surface[4]; +} fog_t; + +typedef struct { + orientationr_t or; + orientationr_t world; + vec3_t pvsOrigin; // may be different than or.origin for portals + qboolean isPortal; // true if this view is through a portal + qboolean isMirror; // the portal is a mirror, invert the face culling + int frameSceneNum; // copied from tr.frameSceneNum + int frameCount; // copied from tr.frameCount + cplane_t portalPlane; // clip anything behind this if mirroring + int viewportX, viewportY, viewportWidth, viewportHeight; + float fovX, fovY; + float projectionMatrix[16]; + cplane_t frustum[4]; + vec3_t visBounds[2]; + float zFar; + stereoFrame_t stereoFrame; +} viewParms_t; + + +/* +============================================================================== + +SURFACES + +============================================================================== +*/ + +// any changes in surfaceType must be mirrored in rb_surfaceTable[] +typedef enum { + SF_BAD, + SF_SKIP, // ignore + SF_FACE, + SF_GRID, + SF_TRIANGLES, + SF_POLY, + SF_MD3, + SF_MD4, +#ifdef RAVENMD4 + SF_MDR, +#endif + SF_IQM, + SF_FLARE, + SF_ENTITY, // beams, rails, lightning, etc that can be determined by entity + SF_DISPLAY_LIST, + + SF_NUM_SURFACE_TYPES, + SF_MAX = 0x7fffffff // ensures that sizeof( surfaceType_t ) == sizeof( int ) +} surfaceType_t; + +typedef struct drawSurf_s { + unsigned sort; // bit combination for fast compares + surfaceType_t *surface; // any of surface*_t +} drawSurf_t; + +#define MAX_FACE_POINTS 64 + +#define MAX_PATCH_SIZE 32 // max dimensions of a patch mesh in map file +#define MAX_GRID_SIZE 65 // max dimensions of a grid mesh in memory + +// when cgame directly specifies a polygon, it becomes a srfPoly_t +// as soon as it is called +typedef struct srfPoly_s { + surfaceType_t surfaceType; + qhandle_t hShader; + int fogIndex; + int numVerts; + polyVert_t *verts; +} srfPoly_t; + +typedef struct srfDisplayList_s { + surfaceType_t surfaceType; + int listNum; +} srfDisplayList_t; + + +typedef struct srfFlare_s { + surfaceType_t surfaceType; + vec3_t origin; + vec3_t normal; + vec3_t color; +} srfFlare_t; + +typedef struct srfGridMesh_s { + surfaceType_t surfaceType; + + // dynamic lighting information + int dlightBits; + + // culling information + vec3_t meshBounds[2]; + vec3_t localOrigin; + float meshRadius; + + // lod information, which may be different + // than the culling information to allow for + // groups of curves that LOD as a unit + vec3_t lodOrigin; + float lodRadius; + int lodFixed; + int lodStitched; + + // vertexes + int width, height; + float *widthLodError; + float *heightLodError; + drawVert_t verts[1]; // variable sized +} srfGridMesh_t; + + + +#define VERTEXSIZE 8 +typedef struct { + surfaceType_t surfaceType; + cplane_t plane; + + // dynamic lighting information + int dlightBits; + + // triangle definitions (no normals at points) + int numPoints; + int numIndices; + int ofsIndices; + float points[1][VERTEXSIZE]; // variable sized + // there is a variable length list of indices here also +} srfSurfaceFace_t; + + +// misc_models in maps are turned into direct geometry by q3map +typedef struct { + surfaceType_t surfaceType; + + // dynamic lighting information + int dlightBits; + + // culling information (FIXME: use this!) + vec3_t bounds[2]; + vec3_t localOrigin; + float radius; + + // triangle definitions + int numIndexes; + int *indexes; + + int numVerts; + drawVert_t *verts; +} srfTriangles_t; + +// inter-quake-model +typedef struct { + int num_vertexes; + int num_triangles; + int num_frames; + int num_surfaces; + int num_joints; + struct srfIQModel_s *surfaces; + + float *positions; + float *texcoords; + float *normals; + float *tangents; + byte *blendIndexes; + byte *blendWeights; + byte *colors; + int *triangles; + + int *jointParents; + float *poseMats; + float *bounds; + char *names; +} iqmData_t; + +// inter-quake-model surface +typedef struct srfIQModel_s { + surfaceType_t surfaceType; + char name[MAX_QPATH]; + shader_t *shader; + iqmData_t *data; + int first_vertex, num_vertexes; + int first_triangle, num_triangles; +} srfIQModel_t; + + +extern void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])(void *); + +/* +============================================================================== + +BRUSH MODELS + +============================================================================== +*/ + + +// +// in memory representation +// + +#define SIDE_FRONT 0 +#define SIDE_BACK 1 +#define SIDE_ON 2 + +typedef struct msurface_s { + int viewCount; // if == tr.viewCount, already added + struct shader_s *shader; + int fogIndex; + + surfaceType_t *data; // any of srf*_t +} msurface_t; + + + +#define CONTENTS_NODE -1 +typedef struct mnode_s { + // common with leaf and node + int contents; // -1 for nodes, to differentiate from leafs + int visframe; // node needs to be traversed if current + vec3_t mins, maxs; // for bounding box culling + struct mnode_s *parent; + + // node specific + cplane_t *plane; + struct mnode_s *children[2]; + + // leaf specific + int cluster; + int area; + + msurface_t **firstmarksurface; + int nummarksurfaces; +} mnode_t; + +typedef struct { + vec3_t bounds[2]; // for culling + msurface_t *firstSurface; + int numSurfaces; +} bmodel_t; + +typedef struct { + char name[MAX_QPATH]; // ie: maps/tim_dm2.bsp + char baseName[MAX_QPATH]; // ie: tim_dm2 + + int dataSize; + + int numShaders; + dshader_t *shaders; + + bmodel_t *bmodels; + + int numplanes; + cplane_t *planes; + + int numnodes; // includes leafs + int numDecisionNodes; + mnode_t *nodes; + + int numsurfaces; + msurface_t *surfaces; + + int nummarksurfaces; + msurface_t **marksurfaces; + + int numfogs; + fog_t *fogs; + + vec3_t lightGridOrigin; + vec3_t lightGridSize; + vec3_t lightGridInverseSize; + int lightGridBounds[3]; + byte *lightGridData; + + + int numClusters; + int clusterBytes; + const byte *vis; // may be passed in by CM_LoadMap to save space + + byte *novis; // clusterBytes of 0xff + + char *entityString; + char *entityParsePoint; +} world_t; + +//====================================================================== + +typedef enum { + MOD_BAD, + MOD_BRUSH, + MOD_MESH, + MOD_MD4, +#ifdef RAVENMD4 + MOD_MDR, +#endif + MOD_IQM +} modtype_t; + +typedef struct model_s { + char name[MAX_QPATH]; + modtype_t type; + int index; // model = tr.models[model->index] + + int dataSize; // just for listing purposes + bmodel_t *bmodel; // only if type == MOD_BRUSH + md3Header_t *md3[MD3_MAX_LODS]; // only if type == MOD_MESH + void *modelData; // only if type == (MOD_MD4 | MOD_MDR | MOD_IQM) + + int numLods; +} model_t; + + +#define MAX_MOD_KNOWN 1024 + +void R_ModelInit (void); +model_t *R_GetModelByHandle( qhandle_t hModel ); +int R_LerpTag( orientation_t *tag, qhandle_t handle, int startFrame, int endFrame, + float frac, const char *tagName ); +void R_ModelBounds( qhandle_t handle, vec3_t mins, vec3_t maxs ); + +void R_Modellist_f (void); + +//==================================================== +extern refimport_t ri; + +#define MAX_DRAWIMAGES 2048 +#define MAX_SKINS 1024 + + +#define MAX_DRAWSURFS 0x10000 +#define DRAWSURF_MASK (MAX_DRAWSURFS-1) + +/* + +the drawsurf sort data is packed into a single 32 bit value so it can be +compared quickly during the qsorting process + +the bits are allocated as follows: + +0 - 1 : dlightmap index +//2 : used to be clipped flag REMOVED - 03.21.00 rad +2 - 6 : fog index +11 - 20 : entity index +21 - 31 : sorted shader index + + TTimo - 1.32 +0-1 : dlightmap index +2-6 : fog index +7-16 : entity index +17-30 : sorted shader index +*/ +#define QSORT_FOGNUM_SHIFT 2 +#define QSORT_REFENTITYNUM_SHIFT 7 +#define QSORT_SHADERNUM_SHIFT (QSORT_REFENTITYNUM_SHIFT+REFENTITYNUM_BITS) +#if (QSORT_SHADERNUM_SHIFT+SHADERNUM_BITS) > 32 + #error "Need to update sorting, too many bits." +#endif + +extern int gl_filter_min, gl_filter_max; + +/* +** performanceCounters_t +*/ +typedef struct { + int c_sphere_cull_patch_in, c_sphere_cull_patch_clip, c_sphere_cull_patch_out; + int c_box_cull_patch_in, c_box_cull_patch_clip, c_box_cull_patch_out; + int c_sphere_cull_md3_in, c_sphere_cull_md3_clip, c_sphere_cull_md3_out; + int c_box_cull_md3_in, c_box_cull_md3_clip, c_box_cull_md3_out; + + int c_leafs; + int c_dlightSurfaces; + int c_dlightSurfacesCulled; +} frontEndCounters_t; + +#define FOG_TABLE_SIZE 256 +#define FUNCTABLE_SIZE 1024 +#define FUNCTABLE_SIZE2 10 +#define FUNCTABLE_MASK (FUNCTABLE_SIZE-1) + + +// the renderer front end should never modify glstate_t +typedef struct { + int currenttextures[2]; + int currenttmu; + qboolean finishCalled; + int texEnv[2]; + int faceCulling; + unsigned long glStateBits; +} glstate_t; + + +typedef struct { + int c_surfaces, c_shaders, c_vertexes, c_indexes, c_totalIndexes; + float c_overDraw; + + int c_dlightVertexes; + int c_dlightIndexes; + + int c_flareAdds; + int c_flareTests; + int c_flareRenders; + + int msec; // total msec for backend run +} backEndCounters_t; + +// all state modified by the back end is seperated +// from the front end state +typedef struct { + trRefdef_t refdef; + viewParms_t viewParms; + orientationr_t or; + backEndCounters_t pc; + qboolean isHyperspace; + trRefEntity_t *currentEntity; + qboolean skyRenderedThisView; // flag for drawing sun + + qboolean projection2D; // if qtrue, drawstretchpic doesn't need to change modes + byte color2D[4]; + qboolean vertexes2D; // shader needs to be finished + trRefEntity_t entity2D; // currentEntity will point at this when doing 2D rendering +} backEndState_t; + +/* +** trGlobals_t +** +** Most renderer globals are defined here. +** backend functions should never modify any of these fields, +** but may read fields that aren't dynamically modified +** by the frontend. +*/ +typedef struct { + qboolean registered; // cleared at shutdown, set at beginRegistration + + int visCount; // incremented every time a new vis cluster is entered + int frameCount; // incremented every frame + int sceneCount; // incremented every scene + int viewCount; // incremented every view (twice a scene if portaled) + // and every R_MarkFragments call + + int frameSceneNum; // zeroed at RE_BeginFrame + + qboolean worldMapLoaded; + world_t *world; + + const byte *externalVisData; // from RE_SetWorldVisData, shared with CM_Load + + image_t *defaultImage; + image_t *scratchImage[32]; + image_t *fogImage; + image_t *dlightImage; // inverse-quare highlight for projective adding + image_t *flareImage; + image_t *whiteImage; // full of 0xff + image_t *identityLightImage; // full of tr.identityLightByte + + shader_t *defaultShader; + shader_t *shadowShader; + shader_t *projectionShadowShader; + + shader_t *flareShader; + shader_t *sunShader; + + int numLightmaps; + image_t **lightmaps; + + trRefEntity_t *currentEntity; + trRefEntity_t worldEntity; // point currentEntity at this when rendering world + int currentEntityNum; + int shiftedEntityNum; // currentEntityNum << QSORT_REFENTITYNUM_SHIFT + model_t *currentModel; + + viewParms_t viewParms; + + float identityLight; // 1.0 / ( 1 << overbrightBits ) + int identityLightByte; // identityLight * 255 + int overbrightBits; // r_overbrightBits->integer, but set to 0 if no hw gamma + + orientationr_t or; // for current entity + + trRefdef_t refdef; + + int viewCluster; + + vec3_t sunLight; // from the sky shader for this level + vec3_t sunDirection; + + frontEndCounters_t pc; + int frontEndMsec; // not in pc due to clearing issue + + vec4_t clipRegion; // 2D clipping region + + // + // put large tables at the end, so most elements will be + // within the +/32K indexed range on risc processors + // + model_t *models[MAX_MOD_KNOWN]; + int numModels; + + int numImages; + image_t *images[MAX_DRAWIMAGES]; + + // shader indexes from other modules will be looked up in tr.shaders[] + // shader indexes from drawsurfs will be looked up in sortedShaders[] + // lower indexed sortedShaders must be rendered first (opaque surfaces before translucent) + int numShaders; + shader_t *shaders[MAX_SHADERS]; + shader_t *sortedShaders[MAX_SHADERS]; + + int numSkins; + skin_t *skins[MAX_SKINS]; + + float sinTable[FUNCTABLE_SIZE]; + float squareTable[FUNCTABLE_SIZE]; + float triangleTable[FUNCTABLE_SIZE]; + float sawToothTable[FUNCTABLE_SIZE]; + float inverseSawToothTable[FUNCTABLE_SIZE]; + float fogTable[FOG_TABLE_SIZE]; +} trGlobals_t; + +extern backEndState_t backEnd; +extern trGlobals_t tr; +extern glconfig_t glConfig; // outside of TR since it shouldn't be cleared during ref re-init +extern glstate_t glState; // outside of TR since it shouldn't be cleared during ref re-init + + +// +// cvars +// +extern cvar_t *r_flareSize; +extern cvar_t *r_flareFade; +// coefficient for the flare intensity falloff function. +#define FLARE_STDCOEFF "150" +extern cvar_t *r_flareCoeff; + +extern cvar_t *r_railWidth; +extern cvar_t *r_railCoreWidth; +extern cvar_t *r_railSegmentLength; + +extern cvar_t *r_ignore; // used for debugging anything +extern cvar_t *r_verbose; // used for verbose debug spew +extern cvar_t *r_ignoreFastPath; // allows us to ignore our Tess fast paths + +extern cvar_t *r_znear; // near Z clip plane +extern cvar_t *r_zproj; // z distance of projection plane +extern cvar_t *r_stereoSeparation; // separation of cameras for stereo rendering + +extern cvar_t *r_stencilbits; // number of desired stencil bits +extern cvar_t *r_depthbits; // number of desired depth bits +extern cvar_t *r_colorbits; // number of desired color bits, only relevant for fullscreen +extern cvar_t *r_texturebits; // number of desired texture bits +extern cvar_t *r_ext_multisample; + // 0 = use framebuffer depth + // 16 = use 16-bit textures + // 32 = use 32-bit textures + // all else = error + +extern cvar_t *r_measureOverdraw; // enables stencil buffer overdraw measurement + +extern cvar_t *r_lodbias; // push/pull LOD transitions +extern cvar_t *r_lodscale; + +extern cvar_t *r_primitives; // "0" = based on compiled vertex array existance + // "1" = glDrawElemet tristrips + // "2" = glDrawElements triangles + // "-1" = no drawing + +extern cvar_t *r_inGameVideo; // controls whether in game video should be draw +extern cvar_t *r_fastsky; // controls whether sky should be cleared or drawn +extern cvar_t *r_drawSun; // controls drawing of sun quad +extern cvar_t *r_dynamiclight; // dynamic lights enabled/disabled +extern cvar_t *r_dlightBacks; // dlight non-facing surfaces for continuity + +extern cvar_t *r_norefresh; // bypasses the ref rendering +extern cvar_t *r_drawentities; // disable/enable entity rendering +extern cvar_t *r_drawworld; // disable/enable world rendering +extern cvar_t *r_speeds; // various levels of information display +extern cvar_t *r_detailTextures; // enables/disables detail texturing stages +extern cvar_t *r_novis; // disable/enable usage of PVS +extern cvar_t *r_nocull; +extern cvar_t *r_facePlaneCull; // enables culling of planar surfaces with back side test +extern cvar_t *r_nocurves; +extern cvar_t *r_showcluster; + +extern cvar_t *r_width; +extern cvar_t *r_height; +extern cvar_t *r_pixelAspect; + +extern cvar_t *r_fullscreen; +extern cvar_t *r_noborder; +extern cvar_t *r_gamma; +extern cvar_t *r_ignorehwgamma; // overrides hardware gamma capabilities + +extern cvar_t *r_allowExtensions; // global enable/disable of OpenGL extensions +extern cvar_t *r_ext_compressed_textures; // these control use of specific extensions +extern cvar_t *r_ext_multitexture; +extern cvar_t *r_ext_compiled_vertex_array; +extern cvar_t *r_ext_texture_env_add; + +extern cvar_t *r_ext_texture_filter_anisotropic; +extern cvar_t *r_ext_max_anisotropy; + +extern cvar_t *r_nobind; // turns off binding to appropriate textures +extern cvar_t *r_singleShader; // make most world faces use default shader +extern cvar_t *r_roundImagesDown; +extern cvar_t *r_colorMipLevels; // development aid to see texture mip usage +extern cvar_t *r_picmip; // controls picmip values +extern cvar_t *r_finish; +extern cvar_t *r_drawBuffer; +extern cvar_t *r_swapInterval; +extern cvar_t *r_textureMode; +extern cvar_t *r_offsetFactor; +extern cvar_t *r_offsetUnits; + +extern cvar_t *r_fullbright; // avoid lightmap pass +extern cvar_t *r_lightmap; // render lightmaps only +extern cvar_t *r_vertexLight; // vertex lighting mode for better performance +extern cvar_t *r_uiFullScreen; // ui is running fullscreen + +extern cvar_t *r_logFile; // number of frames to emit GL logs +extern cvar_t *r_showtris; // enables wireframe rendering of the world +extern cvar_t *r_showsky; // forces sky in front of all surfaces +extern cvar_t *r_shownormals; // draws wireframe normals +extern cvar_t *r_clear; // force screen clear every frame + +extern cvar_t *r_shadows; // controls shadows: 0 = none, 1 = blur, 2 = stencil, 3 = black planar projection +extern cvar_t *r_flares; // light flares + +extern cvar_t *r_intensity; + +extern cvar_t *r_lockpvs; +extern cvar_t *r_noportals; +extern cvar_t *r_portalOnly; + +extern cvar_t *r_subdivisions; +extern cvar_t *r_lodCurveError; +extern cvar_t *r_skipBackEnd; + +extern cvar_t *r_stereoEnabled; +extern cvar_t *r_anaglyphMode; + +extern cvar_t *r_greyscale; + +extern cvar_t *r_ignoreGLErrors; + +extern cvar_t *r_overBrightBits; +extern cvar_t *r_mapOverBrightBits; + +extern cvar_t *r_debugSurface; +extern cvar_t *r_simpleMipMaps; + +extern cvar_t *r_showImages; +extern cvar_t *r_debugSort; + +extern cvar_t *r_printShaders; +extern cvar_t *r_saveFontData; + +extern cvar_t *r_marksOnTriangleMeshes; + +//==================================================================== + +float R_NoiseGet4f( float x, float y, float z, float t ); +void R_NoiseInit( void ); + +void R_SwapBuffers( int ); + +void R_RenderView( viewParms_t *parms ); + +void R_AddMD3Surfaces( trRefEntity_t *e ); +void R_AddNullModelSurfaces( trRefEntity_t *e ); +void R_AddBeamSurfaces( trRefEntity_t *e ); +void R_AddRailSurfaces( trRefEntity_t *e, qboolean isUnderwater ); +void R_AddLightningBoltSurfaces( trRefEntity_t *e ); + +void R_AddPolygonSurfaces( void ); + +void R_DecomposeSort( unsigned sort, int *entityNum, shader_t **shader, + int *fogNum, int *dlightMap ); + +void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, int fogIndex, int dlightMap ); + + +#define CULL_IN 0 // completely unclipped +#define CULL_CLIP 1 // clipped by one or more planes +#define CULL_OUT 2 // completely outside the clipping planes +void R_LocalNormalToWorld (vec3_t local, vec3_t world); +void R_LocalPointToWorld (vec3_t local, vec3_t world); +int R_CullLocalBox (vec3_t bounds[2]); +int R_CullPointAndRadius( vec3_t origin, float radius ); +int R_CullLocalPointAndRadius( vec3_t origin, float radius ); + +void R_SetupProjection(viewParms_t *dest, float zProj, qboolean computeFrustum); +void R_RotateForEntity( const trRefEntity_t *ent, const viewParms_t *viewParms, orientationr_t *or ); + +/* +** GL wrapper/helper functions +*/ +void GL_Bind( image_t *image ); +void GL_SetDefaultState (void); +void GL_SelectTexture( int unit ); +void GL_TextureMode( const char *string ); +void GL_CheckErrors( void ); +void GL_State( unsigned long stateVector ); +void GL_TexEnv( int env ); +void GL_Cull( int cullType ); + +#define GLS_SRCBLEND_ZERO 0x00000001 +#define GLS_SRCBLEND_ONE 0x00000002 +#define GLS_SRCBLEND_DST_COLOR 0x00000003 +#define GLS_SRCBLEND_ONE_MINUS_DST_COLOR 0x00000004 +#define GLS_SRCBLEND_SRC_ALPHA 0x00000005 +#define GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA 0x00000006 +#define GLS_SRCBLEND_DST_ALPHA 0x00000007 +#define GLS_SRCBLEND_ONE_MINUS_DST_ALPHA 0x00000008 +#define GLS_SRCBLEND_ALPHA_SATURATE 0x00000009 +#define GLS_SRCBLEND_BITS 0x0000000f + +#define GLS_DSTBLEND_ZERO 0x00000010 +#define GLS_DSTBLEND_ONE 0x00000020 +#define GLS_DSTBLEND_SRC_COLOR 0x00000030 +#define GLS_DSTBLEND_ONE_MINUS_SRC_COLOR 0x00000040 +#define GLS_DSTBLEND_SRC_ALPHA 0x00000050 +#define GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA 0x00000060 +#define GLS_DSTBLEND_DST_ALPHA 0x00000070 +#define GLS_DSTBLEND_ONE_MINUS_DST_ALPHA 0x00000080 +#define GLS_DSTBLEND_BITS 0x000000f0 + +#define GLS_DEPTHMASK_TRUE 0x00000100 + +#define GLS_POLYMODE_LINE 0x00001000 + +#define GLS_DEPTHTEST_DISABLE 0x00010000 +#define GLS_DEPTHFUNC_EQUAL 0x00020000 + +#define GLS_ATEST_GT_0 0x10000000 +#define GLS_ATEST_LT_80 0x20000000 +#define GLS_ATEST_GE_80 0x40000000 +#define GLS_ATEST_BITS 0x70000000 + +#define GLS_DEFAULT GLS_DEPTHMASK_TRUE + +void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty); +void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty); + +void RE_BeginFrame( stereoFrame_t stereoFrame ); +void RE_BeginRegistration( glconfig_t *glconfig ); +void RE_LoadWorldMap( const char *mapname ); +void RE_SetWorldVisData( const byte *vis ); +qhandle_t RE_RegisterModel( const char *name ); +qhandle_t RE_RegisterSkin( const char *name ); +void RE_Shutdown( qboolean destroyWindow ); + +qboolean R_GetEntityToken( char *buffer, int size ); + +model_t *R_AllocModel( void ); + +void R_Init( void ); +image_t *R_FindImageFile( const char *name, qboolean mipmap, qboolean allowPicmip, int glWrapClampMode ); + +image_t *R_CreateImage( const char *name, const byte *pic, int width, int height, qboolean mipmap, + qboolean allowPicmip, int wrapClampMode ); + +void R_SetColorMappings( void ); +void R_GammaCorrect( byte *buffer, int bufSize ); + +void R_ImageList_f( void ); +void R_SkinList_f( void ); +// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=516 +const void *RB_TakeScreenshotCmd( const void *data ); +void R_ScreenShot_f( void ); + +void R_InitFogTable( void ); +float R_FogFactor( float s, float t ); +void R_InitImages( void ); +void R_DeleteTextures( void ); +int R_SumOfUsedImages( void ); +void R_InitSkins( void ); +skin_t *R_GetSkinByHandle( qhandle_t hSkin ); + +int R_ComputeLOD( trRefEntity_t *ent ); + +const void *RB_TakeVideoFrameCmd( const void *data ); + +// +// tr_shader.c +// +qhandle_t RE_RegisterShaderLightMap( const char *name, int lightmapIndex ); +qhandle_t RE_RegisterShader( const char *name ); +qhandle_t RE_RegisterShaderNoMip( const char *name ); +qhandle_t RE_RegisterShaderFromImage(const char *name, int lightmapIndex, image_t *image, qboolean mipRawImage); + +shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImage ); +shader_t *R_GetShaderByHandle( qhandle_t hShader ); +shader_t *R_GetShaderByState( int index, long *cycleTime ); +shader_t *R_FindShaderByName( const char *name ); +void R_InitShaders( void ); +void R_ShaderList_f( void ); +void R_RemapShader(const char *oldShader, const char *newShader, const char *timeOffset); + +/* +==================================================================== + +IMPLEMENTATION SPECIFIC FUNCTIONS + +==================================================================== +*/ + +void GLimp_Init( void ); +void GLimp_Shutdown( void ); +void GLimp_EndFrame( void ); + +void GLimp_LogComment( char *comment ); +void GLimp_Minimize(void); + +// NOTE TTimo linux works with float gamma value, not the gamma table +// the params won't be used, getting the r_gamma cvar directly +void GLimp_SetGamma( unsigned char red[256], + unsigned char green[256], + unsigned char blue[256] ); + +void GL_ResolveHardwareType( void ); + +/* +==================================================================== + +TESSELATOR/SHADER DECLARATIONS + +==================================================================== +*/ +typedef byte color4ub_t[4]; + +typedef struct stageVars +{ + color4ub_t colors[SHADER_MAX_VERTEXES]; + vec2_t texcoords[NUM_TEXTURE_BUNDLES][SHADER_MAX_VERTEXES]; +} stageVars_t; + + +typedef struct shaderCommands_s +{ + glIndex_t indexes[SHADER_MAX_INDEXES] QALIGN(16); + vec4_t xyz[SHADER_MAX_VERTEXES] QALIGN(16); + vec4_t normal[SHADER_MAX_VERTEXES] QALIGN(16); + vec2_t texCoords[SHADER_MAX_VERTEXES][2] QALIGN(16); + color4ub_t vertexColors[SHADER_MAX_VERTEXES] QALIGN(16); + int vertexDlightBits[SHADER_MAX_VERTEXES] QALIGN(16); + + stageVars_t svars QALIGN(16); + + color4ub_t constantColor255[SHADER_MAX_VERTEXES] QALIGN(16); + + shader_t *shader; + float shaderTime; + int fogNum; + + int dlightBits; // or together of all vertexDlightBits + + int numIndexes; + int numVertexes; + + // info extracted from current shader + int numPasses; + void (*currentStageIteratorFunc)( void ); + shaderStage_t **xstages; +} shaderCommands_t; + +extern shaderCommands_t tess; + +void RB_BeginSurface(shader_t *shader, int fogNum ); +void RB_EndSurface(void); +void RB_CheckOverflow( int verts, int indexes ); +#define RB_CHECKOVERFLOW(v,i) if (tess.numVertexes + (v) >= SHADER_MAX_VERTEXES || tess.numIndexes + (i) >= SHADER_MAX_INDEXES ) {RB_CheckOverflow(v,i);} + +void RB_StageIteratorGeneric( void ); +void RB_StageIteratorSky( void ); +void RB_StageIteratorVertexLitTexture( void ); +void RB_StageIteratorLightmappedMultitexture( void ); + +void RB_AddQuadStamp( vec3_t origin, vec3_t left, vec3_t up, byte *color ); +void RB_AddQuadStampExt( vec3_t origin, vec3_t left, vec3_t up, byte *color, float s1, float t1, float s2, float t2 ); + +void RB_ShowImages( void ); + + +/* +============================================================ + +WORLD MAP + +============================================================ +*/ + +void R_AddBrushModelSurfaces( trRefEntity_t *e ); +void R_AddWorldSurfaces( void ); +qboolean R_inPVS( const vec3_t p1, const vec3_t p2 ); + + +/* +============================================================ + +FLARES + +============================================================ +*/ + +void R_ClearFlares( void ); + +void RB_AddFlare( void *surface, int fogNum, vec3_t point, vec3_t color, vec3_t normal ); +void RB_AddDlightFlares( void ); +void RB_RenderFlares (void); + +/* +============================================================ + +LIGHTS + +============================================================ +*/ + +void R_DlightBmodel( bmodel_t *bmodel ); +void R_SetupEntityLighting( const trRefdef_t *refdef, trRefEntity_t *ent ); +void R_TransformDlights( int count, dlight_t *dl, orientationr_t *or ); +int R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ); + + +/* +============================================================ + +SHADOWS + +============================================================ +*/ + +void RB_ShadowTessEnd( void ); +void RB_ShadowFinish( void ); +void RB_ProjectionShadowDeform( void ); + +/* +============================================================ + +SKIES + +============================================================ +*/ + +void R_BuildCloudData( shaderCommands_t *shader ); +void R_InitSkyTexCoords( float cloudLayerHeight ); +void R_DrawSkyBox( shaderCommands_t *shader ); +void RB_DrawSun( void ); +void RB_ClipSkyPolygons( shaderCommands_t *shader ); + +/* +============================================================ + +CURVE TESSELATION + +============================================================ +*/ + +#define PATCH_STITCHING + +srfGridMesh_t *R_SubdividePatchToGrid( int width, int height, + drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ); +srfGridMesh_t *R_GridInsertColumn( srfGridMesh_t *grid, int column, int row, vec3_t point, float loderror ); +srfGridMesh_t *R_GridInsertRow( srfGridMesh_t *grid, int row, int column, vec3_t point, float loderror ); +void R_FreeSurfaceGridMesh( srfGridMesh_t *grid ); + +/* +============================================================ + +MARKERS, POLYGON PROJECTION ON WORLD POLYGONS + +============================================================ +*/ + +int R_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection, + int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ); + + +/* +============================================================ + +SCENE GENERATION + +============================================================ +*/ + +void R_InitNextFrame( void ); + +void RE_ClearScene( void ); +void RE_AddRefEntityToScene( const refEntity_t *ent ); +void RE_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts, int num ); +void RE_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ); +void RE_AddAdditiveLightToScene( const vec3_t org, float intensity, float r, float g, float b ); +void RE_RenderScene( const refdef_t *fd ); + +#ifdef RAVENMD4 +/* +============================================================= + +UNCOMPRESSING BONES + +============================================================= +*/ + +#define MC_BITS_X (16) +#define MC_BITS_Y (16) +#define MC_BITS_Z (16) +#define MC_BITS_VECT (16) + +#define MC_SCALE_X (1.0f/64) +#define MC_SCALE_Y (1.0f/64) +#define MC_SCALE_Z (1.0f/64) + +void MC_UnCompress(float mat[3][4],const unsigned char * comp); +#endif + +/* +============================================================= + +ANIMATED MODELS + +============================================================= +*/ + +// void R_MakeAnimModel( model_t *model ); haven't seen this one really, so not needed I guess. +void R_AddAnimSurfaces( trRefEntity_t *ent ); +void RB_SurfaceAnim( md4Surface_t *surfType ); +#ifdef RAVENMD4 +void R_MDRAddAnimSurfaces( trRefEntity_t *ent ); +void RB_MDRSurfaceAnim( md4Surface_t *surface ); +#endif +qboolean R_LoadIQM (model_t *mod, void *buffer, int filesize, const char *name ); +void R_AddIQMSurfaces( trRefEntity_t *ent ); +void RB_IQMSurfaceAnim( surfaceType_t *surface ); +int R_IQMLerpTag( orientation_t *tag, iqmData_t *data, + int startFrame, int endFrame, + float frac, const char *tagName ); + +/* +============================================================= + +IMAGE LOADERS + +============================================================= +*/ + +void R_LoadBMP( const char *name, byte **pic, int *width, int *height ); +void R_LoadJPG( const char *name, byte **pic, int *width, int *height ); +void R_LoadPCX( const char *name, byte **pic, int *width, int *height ); +void R_LoadPNG( const char *name, byte **pic, int *width, int *height ); +void R_LoadTGA( const char *name, byte **pic, int *width, int *height ); + +/* +============================================================= +============================================================= +*/ +void R_TransformModelToClip( const vec3_t src, const float *modelMatrix, const float *projectionMatrix, + vec4_t eye, vec4_t dst ); +void R_TransformClipToWindow( const vec4_t clip, const viewParms_t *view, vec4_t normalized, vec4_t window ); + +void RB_DeformTessGeometry( void ); + +void RB_CalcEnvironmentTexCoords( float *dstTexCoords ); +void RB_CalcFogTexCoords( float *dstTexCoords ); +void RB_CalcScrollTexCoords( const float scroll[2], float *dstTexCoords ); +void RB_CalcRotateTexCoords( float rotSpeed, float *dstTexCoords ); +void RB_CalcScaleTexCoords( const float scale[2], float *dstTexCoords ); +void RB_CalcTurbulentTexCoords( const waveForm_t *wf, float *dstTexCoords ); +void RB_CalcTransformTexCoords( const texModInfo_t *tmi, float *dstTexCoords ); +void RB_CalcModulateColorsByFog( unsigned char *dstColors ); +void RB_CalcModulateAlphasByFog( unsigned char *dstColors ); +void RB_CalcModulateRGBAsByFog( unsigned char *dstColors ); +void RB_CalcWaveAlpha( const waveForm_t *wf, unsigned char *dstColors ); +void RB_CalcWaveColor( const waveForm_t *wf, unsigned char *dstColors ); +void RB_CalcAlphaFromEntity( unsigned char *dstColors ); +void RB_CalcAlphaFromOneMinusEntity( unsigned char *dstColors ); +void RB_CalcStretchTexCoords( const waveForm_t *wf, float *texCoords ); +void RB_CalcColorFromEntity( unsigned char *dstColors ); +void RB_CalcColorFromOneMinusEntity( unsigned char *dstColors ); +void RB_CalcSpecularAlpha( unsigned char *alphas ); +void RB_CalcDiffuseColor( unsigned char *colors ); + +/* +============================================================= + +RENDERER BACK END FUNCTIONS + +============================================================= +*/ + +void RB_ExecuteRenderCommands( const void *data ); + +/* +============================================================= + +RENDERER BACK END COMMAND QUEUE + +============================================================= +*/ + +#define MAX_RENDER_COMMANDS 0x40000 + +typedef struct { + byte cmds[MAX_RENDER_COMMANDS]; + int used; +} renderCommandList_t; + +typedef struct { + int commandId; + float color[4]; +} setColorCommand_t; + +typedef struct { + int commandId; + int buffer; +} drawBufferCommand_t; + +typedef struct { + int commandId; + image_t *image; + int width; + int height; + void *data; +} subImageCommand_t; + +typedef struct { + int commandId; +} swapBuffersCommand_t; + +typedef struct { + int commandId; + int buffer; +} endFrameCommand_t; + +typedef struct { + int commandId; + shader_t *shader; + float x, y; + float w, h; + float s1, t1; + float s2, t2; +} stretchPicCommand_t; + +typedef struct { + int commandId; + trRefdef_t refdef; + viewParms_t viewParms; + drawSurf_t *drawSurfs; + int numDrawSurfs; +} drawSurfsCommand_t; + +typedef struct { + int commandId; + int x; + int y; + int width; + int height; + char *fileName; + qboolean jpeg; +} screenshotCommand_t; + +typedef struct { + int commandId; + int width; + int height; + byte *captureBuffer; + byte *encodeBuffer; + qboolean motionJpeg; +} videoFrameCommand_t; + +typedef struct +{ + int commandId; + + GLboolean rgba[4]; +} colorMaskCommand_t; + +typedef struct +{ + int commandId; +} clearDepthCommand_t; + +typedef enum { + RC_END_OF_LIST, + RC_SET_COLOR, + RC_STRETCH_PIC, + RC_DRAW_SURFS, + RC_DRAW_BUFFER, + RC_SWAP_BUFFERS, + RC_SCREENSHOT, + RC_VIDEOFRAME, + RC_COLORMASK, + RC_CLEARDEPTH +} renderCommand_t; + + +// these are sort of arbitrary limits. +// the limits apply to the sum of all scenes in a frame -- +// the main view, all the 3D icons, etc +#define MAX_POLYS 600 +#define MAX_POLYVERTS 3000 + +// all of the information needed by the back end must be +// contained in a backEndData_t +typedef struct { + drawSurf_t drawSurfs[MAX_DRAWSURFS]; + dlight_t dlights[MAX_DLIGHTS]; + trRefEntity_t entities[MAX_REFENTITIES]; + srfPoly_t *polys;//[MAX_POLYS]; + polyVert_t *polyVerts;//[MAX_POLYVERTS]; + renderCommandList_t commands; +} backEndData_t; + +extern int max_polys; +extern int max_polyverts; + +extern backEndData_t *backEndData; // the second one may not be allocated + +extern volatile renderCommandList_t *renderCommandList; + + +void *R_GetCommandBuffer( int bytes ); +void RB_ExecuteRenderCommands( const void *data ); + +void R_IssuePendingRenderCommands( void ); + +void R_AddDrawSurfCmd( drawSurf_t *drawSurfs, int numDrawSurfs ); + +void RE_SetColor( const float *rgba ); +void RE_SetClipRegion( const float *region ); +void RE_StretchPic ( float x, float y, float w, float h, + float s1, float t1, float s2, float t2, qhandle_t hShader ); +void RE_BeginFrame( stereoFrame_t stereoFrame ); +void RE_EndFrame( int *frontEndMsec, int *backEndMsec ); +void RE_SaveJPG(char * filename, int quality, int image_width, int image_height, + unsigned char *image_buffer, int padding); +size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality, + int image_width, int image_height, byte *image_buffer, int padding); +void RE_TakeVideoFrame( int width, int height, + byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg ); + +// font stuff +void R_InitFreeType( void ); +void R_DoneFreeType( void ); +void RE_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font); + + +#endif //TR_LOCAL_H diff --git a/src/renderergl1/tr_main.c b/src/renderergl1/tr_main.c new file mode 100644 index 00000000..3b82a777 --- /dev/null +++ b/src/renderergl1/tr_main.c @@ -0,0 +1,1399 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_main.c -- main control flow for each frame + +#include "tr_local.h" + +#include // memcpy + +trGlobals_t tr; + +static float s_flipMatrix[16] = { + // convert from our coordinate system (looking down X) + // to OpenGL's coordinate system (looking down -Z) + 0, 0, -1, 0, + -1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 0, 1 +}; + + +refimport_t ri; + +// entities that will have procedurally generated surfaces will just +// point at this for their sorting surface +surfaceType_t entitySurface = SF_ENTITY; + +/* +================= +R_CullLocalBox + +Returns CULL_IN, CULL_CLIP, or CULL_OUT +================= +*/ +int R_CullLocalBox (vec3_t bounds[2]) { + int i, j; + vec3_t transformed[8]; + float dists[8]; + vec3_t v; + cplane_t *frust; + int anyBack; + int front, back; + + if ( r_nocull->integer ) { + return CULL_CLIP; + } + + // transform into world space + for (i = 0 ; i < 8 ; i++) { + v[0] = bounds[i&1][0]; + v[1] = bounds[(i>>1)&1][1]; + v[2] = bounds[(i>>2)&1][2]; + + VectorCopy( tr.or.origin, transformed[i] ); + VectorMA( transformed[i], v[0], tr.or.axis[0], transformed[i] ); + VectorMA( transformed[i], v[1], tr.or.axis[1], transformed[i] ); + VectorMA( transformed[i], v[2], tr.or.axis[2], transformed[i] ); + } + + // check against frustum planes + anyBack = 0; + for (i = 0 ; i < 4 ; i++) { + frust = &tr.viewParms.frustum[i]; + + front = back = 0; + for (j = 0 ; j < 8 ; j++) { + dists[j] = DotProduct(transformed[j], frust->normal); + if ( dists[j] > frust->dist ) { + front = 1; + if ( back ) { + break; // a point is in front + } + } else { + back = 1; + } + } + if ( !front ) { + // all points were behind one of the planes + return CULL_OUT; + } + anyBack |= back; + } + + if ( !anyBack ) { + return CULL_IN; // completely inside frustum + } + + return CULL_CLIP; // partially clipped +} + +/* +** R_CullLocalPointAndRadius +*/ +int R_CullLocalPointAndRadius( vec3_t pt, float radius ) +{ + vec3_t transformed; + + R_LocalPointToWorld( pt, transformed ); + + return R_CullPointAndRadius( transformed, radius ); +} + +/* +** R_CullPointAndRadius +*/ +int R_CullPointAndRadius( vec3_t pt, float radius ) +{ + int i; + float dist; + cplane_t *frust; + qboolean mightBeClipped = qfalse; + + if ( r_nocull->integer ) { + return CULL_CLIP; + } + + // check against frustum planes + for (i = 0 ; i < 4 ; i++) + { + frust = &tr.viewParms.frustum[i]; + + dist = DotProduct( pt, frust->normal) - frust->dist; + if ( dist < -radius ) + { + return CULL_OUT; + } + else if ( dist <= radius ) + { + mightBeClipped = qtrue; + } + } + + if ( mightBeClipped ) + { + return CULL_CLIP; + } + + return CULL_IN; // completely inside frustum +} + + +/* +================= +R_LocalNormalToWorld + +================= +*/ +void R_LocalNormalToWorld (vec3_t local, vec3_t world) { + world[0] = local[0] * tr.or.axis[0][0] + local[1] * tr.or.axis[1][0] + local[2] * tr.or.axis[2][0]; + world[1] = local[0] * tr.or.axis[0][1] + local[1] * tr.or.axis[1][1] + local[2] * tr.or.axis[2][1]; + world[2] = local[0] * tr.or.axis[0][2] + local[1] * tr.or.axis[1][2] + local[2] * tr.or.axis[2][2]; +} + +/* +================= +R_LocalPointToWorld + +================= +*/ +void R_LocalPointToWorld (vec3_t local, vec3_t world) { + world[0] = local[0] * tr.or.axis[0][0] + local[1] * tr.or.axis[1][0] + local[2] * tr.or.axis[2][0] + tr.or.origin[0]; + world[1] = local[0] * tr.or.axis[0][1] + local[1] * tr.or.axis[1][1] + local[2] * tr.or.axis[2][1] + tr.or.origin[1]; + world[2] = local[0] * tr.or.axis[0][2] + local[1] * tr.or.axis[1][2] + local[2] * tr.or.axis[2][2] + tr.or.origin[2]; +} + +/* +================= +R_WorldToLocal + +================= +*/ +void R_WorldToLocal (vec3_t world, vec3_t local) { + local[0] = DotProduct(world, tr.or.axis[0]); + local[1] = DotProduct(world, tr.or.axis[1]); + local[2] = DotProduct(world, tr.or.axis[2]); +} + +/* +========================== +R_TransformModelToClip + +========================== +*/ +void R_TransformModelToClip( const vec3_t src, const float *modelMatrix, const float *projectionMatrix, + vec4_t eye, vec4_t dst ) { + int i; + + for ( i = 0 ; i < 4 ; i++ ) { + eye[i] = + src[0] * modelMatrix[ i + 0 * 4 ] + + src[1] * modelMatrix[ i + 1 * 4 ] + + src[2] * modelMatrix[ i + 2 * 4 ] + + 1 * modelMatrix[ i + 3 * 4 ]; + } + + for ( i = 0 ; i < 4 ; i++ ) { + dst[i] = + eye[0] * projectionMatrix[ i + 0 * 4 ] + + eye[1] * projectionMatrix[ i + 1 * 4 ] + + eye[2] * projectionMatrix[ i + 2 * 4 ] + + eye[3] * projectionMatrix[ i + 3 * 4 ]; + } +} + +/* +========================== +R_TransformClipToWindow + +========================== +*/ +void R_TransformClipToWindow( const vec4_t clip, const viewParms_t *view, vec4_t normalized, vec4_t window ) { + normalized[0] = clip[0] / clip[3]; + normalized[1] = clip[1] / clip[3]; + normalized[2] = ( clip[2] + clip[3] ) / ( 2 * clip[3] ); + + window[0] = 0.5f * ( 1.0f + normalized[0] ) * view->viewportWidth; + window[1] = 0.5f * ( 1.0f + normalized[1] ) * view->viewportHeight; + window[2] = normalized[2]; + + window[0] = (int) ( window[0] + 0.5 ); + window[1] = (int) ( window[1] + 0.5 ); +} + + +/* +========================== +myGlMultMatrix + +========================== +*/ +void myGlMultMatrix( const float *a, const float *b, float *out ) { + int i, j; + + for ( i = 0 ; i < 4 ; i++ ) { + for ( j = 0 ; j < 4 ; j++ ) { + out[ i * 4 + j ] = + a [ i * 4 + 0 ] * b [ 0 * 4 + j ] + + a [ i * 4 + 1 ] * b [ 1 * 4 + j ] + + a [ i * 4 + 2 ] * b [ 2 * 4 + j ] + + a [ i * 4 + 3 ] * b [ 3 * 4 + j ]; + } + } +} + +/* +================= +R_RotateForEntity + +Generates an orientation for an entity and viewParms +Does NOT produce any GL calls +Called by both the front end and the back end +================= +*/ +void R_RotateForEntity( const trRefEntity_t *ent, const viewParms_t *viewParms, + orientationr_t *or ) { + float glMatrix[16]; + vec3_t delta; + float axisLength; + + if ( ent->e.reType != RT_MODEL ) { + *or = viewParms->world; + return; + } + + VectorCopy( ent->e.origin, or->origin ); + + VectorCopy( ent->e.axis[0], or->axis[0] ); + VectorCopy( ent->e.axis[1], or->axis[1] ); + VectorCopy( ent->e.axis[2], or->axis[2] ); + + glMatrix[0] = or->axis[0][0]; + glMatrix[4] = or->axis[1][0]; + glMatrix[8] = or->axis[2][0]; + glMatrix[12] = or->origin[0]; + + glMatrix[1] = or->axis[0][1]; + glMatrix[5] = or->axis[1][1]; + glMatrix[9] = or->axis[2][1]; + glMatrix[13] = or->origin[1]; + + glMatrix[2] = or->axis[0][2]; + glMatrix[6] = or->axis[1][2]; + glMatrix[10] = or->axis[2][2]; + glMatrix[14] = or->origin[2]; + + glMatrix[3] = 0; + glMatrix[7] = 0; + glMatrix[11] = 0; + glMatrix[15] = 1; + + myGlMultMatrix( glMatrix, viewParms->world.modelMatrix, or->modelMatrix ); + + // calculate the viewer origin in the model's space + // needed for fog, specular, and environment mapping + VectorSubtract( viewParms->or.origin, or->origin, delta ); + + // compensate for scale in the axes if necessary + if ( ent->e.nonNormalizedAxes ) { + axisLength = VectorLength( ent->e.axis[0] ); + if ( !axisLength ) { + axisLength = 0; + } else { + axisLength = 1.0f / axisLength; + } + } else { + axisLength = 1.0f; + } + + or->viewOrigin[0] = DotProduct( delta, or->axis[0] ) * axisLength; + or->viewOrigin[1] = DotProduct( delta, or->axis[1] ) * axisLength; + or->viewOrigin[2] = DotProduct( delta, or->axis[2] ) * axisLength; +} + +/* +================= +R_RotateForViewer + +Sets up the modelview matrix for a given viewParm +================= +*/ +void R_RotateForViewer (void) +{ + float viewerMatrix[16]; + vec3_t origin; + + Com_Memset (&tr.or, 0, sizeof(tr.or)); + tr.or.axis[0][0] = 1; + tr.or.axis[1][1] = 1; + tr.or.axis[2][2] = 1; + VectorCopy (tr.viewParms.or.origin, tr.or.viewOrigin); + + // transform by the camera placement + VectorCopy( tr.viewParms.or.origin, origin ); + + viewerMatrix[0] = tr.viewParms.or.axis[0][0]; + viewerMatrix[4] = tr.viewParms.or.axis[0][1]; + viewerMatrix[8] = tr.viewParms.or.axis[0][2]; + viewerMatrix[12] = -origin[0] * viewerMatrix[0] + -origin[1] * viewerMatrix[4] + -origin[2] * viewerMatrix[8]; + + viewerMatrix[1] = tr.viewParms.or.axis[1][0]; + viewerMatrix[5] = tr.viewParms.or.axis[1][1]; + viewerMatrix[9] = tr.viewParms.or.axis[1][2]; + viewerMatrix[13] = -origin[0] * viewerMatrix[1] + -origin[1] * viewerMatrix[5] + -origin[2] * viewerMatrix[9]; + + viewerMatrix[2] = tr.viewParms.or.axis[2][0]; + viewerMatrix[6] = tr.viewParms.or.axis[2][1]; + viewerMatrix[10] = tr.viewParms.or.axis[2][2]; + viewerMatrix[14] = -origin[0] * viewerMatrix[2] + -origin[1] * viewerMatrix[6] + -origin[2] * viewerMatrix[10]; + + viewerMatrix[3] = 0; + viewerMatrix[7] = 0; + viewerMatrix[11] = 0; + viewerMatrix[15] = 1; + + // convert from our coordinate system (looking down X) + // to OpenGL's coordinate system (looking down -Z) + myGlMultMatrix( viewerMatrix, s_flipMatrix, tr.or.modelMatrix ); + + tr.viewParms.world = tr.or; + +} + +/* +** SetFarClip +*/ +static void R_SetFarClip( void ) +{ + float farthestCornerDistance = 0; + int i; + + // if not rendering the world (icons, menus, etc) + // set a 2k far clip plane + if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { + tr.viewParms.zFar = 2048; + return; + } + + // + // set far clipping planes dynamically + // + farthestCornerDistance = 0; + for ( i = 0; i < 8; i++ ) + { + vec3_t v; + vec3_t vecTo; + float distance; + + if ( i & 1 ) + { + v[0] = tr.viewParms.visBounds[0][0]; + } + else + { + v[0] = tr.viewParms.visBounds[1][0]; + } + + if ( i & 2 ) + { + v[1] = tr.viewParms.visBounds[0][1]; + } + else + { + v[1] = tr.viewParms.visBounds[1][1]; + } + + if ( i & 4 ) + { + v[2] = tr.viewParms.visBounds[0][2]; + } + else + { + v[2] = tr.viewParms.visBounds[1][2]; + } + + VectorSubtract( v, tr.viewParms.or.origin, vecTo ); + + distance = vecTo[0] * vecTo[0] + vecTo[1] * vecTo[1] + vecTo[2] * vecTo[2]; + + if ( distance > farthestCornerDistance ) + { + farthestCornerDistance = distance; + } + } + tr.viewParms.zFar = sqrt( farthestCornerDistance ); +} + +/* +================= +R_SetupFrustum + +Set up the culling frustum planes for the current view using the results we got from computing the first two rows of +the projection matrix. +================= +*/ +void R_SetupFrustum (viewParms_t *dest, float xmin, float xmax, float ymax, float zProj, float stereoSep) +{ + vec3_t ofsorigin; + float oppleg, adjleg, length; + int i; + + if(stereoSep == 0 && xmin == -xmax) + { + // symmetric case can be simplified + VectorCopy(dest->or.origin, ofsorigin); + + length = sqrt(xmax * xmax + zProj * zProj); + oppleg = xmax / length; + adjleg = zProj / length; + + VectorScale(dest->or.axis[0], oppleg, dest->frustum[0].normal); + VectorMA(dest->frustum[0].normal, adjleg, dest->or.axis[1], dest->frustum[0].normal); + + VectorScale(dest->or.axis[0], oppleg, dest->frustum[1].normal); + VectorMA(dest->frustum[1].normal, -adjleg, dest->or.axis[1], dest->frustum[1].normal); + } + else + { + // In stereo rendering, due to the modification of the projection matrix, dest->or.origin is not the + // actual origin that we're rendering so offset the tip of the view pyramid. + VectorMA(dest->or.origin, stereoSep, dest->or.axis[1], ofsorigin); + + oppleg = xmax + stereoSep; + length = sqrt(oppleg * oppleg + zProj * zProj); + VectorScale(dest->or.axis[0], oppleg / length, dest->frustum[0].normal); + VectorMA(dest->frustum[0].normal, zProj / length, dest->or.axis[1], dest->frustum[0].normal); + + oppleg = xmin + stereoSep; + length = sqrt(oppleg * oppleg + zProj * zProj); + VectorScale(dest->or.axis[0], -oppleg / length, dest->frustum[1].normal); + VectorMA(dest->frustum[1].normal, -zProj / length, dest->or.axis[1], dest->frustum[1].normal); + } + + length = sqrt(ymax * ymax + zProj * zProj); + oppleg = ymax / length; + adjleg = zProj / length; + + VectorScale(dest->or.axis[0], oppleg, dest->frustum[2].normal); + VectorMA(dest->frustum[2].normal, adjleg, dest->or.axis[2], dest->frustum[2].normal); + + VectorScale(dest->or.axis[0], oppleg, dest->frustum[3].normal); + VectorMA(dest->frustum[3].normal, -adjleg, dest->or.axis[2], dest->frustum[3].normal); + + for (i=0 ; i<4 ; i++) { + dest->frustum[i].type = PLANE_NON_AXIAL; + dest->frustum[i].dist = DotProduct (ofsorigin, dest->frustum[i].normal); + SetPlaneSignbits( &dest->frustum[i] ); + } +} + +/* +=============== +R_SetupProjection +=============== +*/ +void R_SetupProjection(viewParms_t *dest, float zProj, qboolean computeFrustum) +{ + float xmin, xmax, ymin, ymax; + float width, height, stereoSep = r_stereoSeparation->value; + + /* + * offset the view origin of the viewer for stereo rendering + * by setting the projection matrix appropriately. + */ + + if(stereoSep != 0) + { + if(dest->stereoFrame == STEREO_LEFT) + stereoSep = zProj / stereoSep; + else if(dest->stereoFrame == STEREO_RIGHT) + stereoSep = zProj / -stereoSep; + else + stereoSep = 0; + } + + ymax = zProj * tan(dest->fovY * M_PI / 360.0f); + ymin = -ymax; + + xmax = zProj * tan(dest->fovX * M_PI / 360.0f); + xmin = -xmax; + + width = xmax - xmin; + height = ymax - ymin; + + dest->projectionMatrix[0] = 2 * zProj / width; + dest->projectionMatrix[4] = 0; + dest->projectionMatrix[8] = (xmax + xmin + 2 * stereoSep) / width; + dest->projectionMatrix[12] = 2 * zProj * stereoSep / width; + + dest->projectionMatrix[1] = 0; + dest->projectionMatrix[5] = 2 * zProj / height; + dest->projectionMatrix[9] = ( ymax + ymin ) / height; // normally 0 + dest->projectionMatrix[13] = 0; + + dest->projectionMatrix[3] = 0; + dest->projectionMatrix[7] = 0; + dest->projectionMatrix[11] = -1; + dest->projectionMatrix[15] = 0; + + // Now that we have all the data for the projection matrix we can also setup the view frustum. + if(computeFrustum) + R_SetupFrustum(dest, xmin, xmax, ymax, zProj, stereoSep); +} + +/* +=============== +R_SetupProjectionZ + +Sets the z-component transformation part in the projection matrix +=============== +*/ +void R_SetupProjectionZ(viewParms_t *dest) +{ + float zNear, zFar, depth; + + zNear = r_znear->value; + zFar = dest->zFar; + depth = zFar - zNear; + + dest->projectionMatrix[2] = 0; + dest->projectionMatrix[6] = 0; + dest->projectionMatrix[10] = -( zFar + zNear ) / depth; + dest->projectionMatrix[14] = -2 * zFar * zNear / depth; +} + +/* +================= +R_MirrorPoint +================= +*/ +void R_MirrorPoint (vec3_t in, orientation_t *surface, orientation_t *camera, vec3_t out) { + int i; + vec3_t local; + vec3_t transformed; + float d; + + VectorSubtract( in, surface->origin, local ); + + VectorClear( transformed ); + for ( i = 0 ; i < 3 ; i++ ) { + d = DotProduct(local, surface->axis[i]); + VectorMA( transformed, d, camera->axis[i], transformed ); + } + + VectorAdd( transformed, camera->origin, out ); +} + +void R_MirrorVector (vec3_t in, orientation_t *surface, orientation_t *camera, vec3_t out) { + int i; + float d; + + VectorClear( out ); + for ( i = 0 ; i < 3 ; i++ ) { + d = DotProduct(in, surface->axis[i]); + VectorMA( out, d, camera->axis[i], out ); + } +} + + +/* +============= +R_PlaneForSurface +============= +*/ +void R_PlaneForSurface (surfaceType_t *surfType, cplane_t *plane) { + srfTriangles_t *tri; + srfPoly_t *poly; + drawVert_t *v1, *v2, *v3; + vec4_t plane4; + + if (!surfType) { + Com_Memset (plane, 0, sizeof(*plane)); + plane->normal[0] = 1; + return; + } + switch (*surfType) { + case SF_FACE: + *plane = ((srfSurfaceFace_t *)surfType)->plane; + return; + case SF_TRIANGLES: + tri = (srfTriangles_t *)surfType; + v1 = tri->verts + tri->indexes[0]; + v2 = tri->verts + tri->indexes[1]; + v3 = tri->verts + tri->indexes[2]; + PlaneFromPoints( plane4, v1->xyz, v2->xyz, v3->xyz ); + VectorCopy( plane4, plane->normal ); + plane->dist = plane4[3]; + return; + case SF_POLY: + poly = (srfPoly_t *)surfType; + PlaneFromPoints( plane4, poly->verts[0].xyz, poly->verts[1].xyz, poly->verts[2].xyz ); + VectorCopy( plane4, plane->normal ); + plane->dist = plane4[3]; + return; + default: + Com_Memset (plane, 0, sizeof(*plane)); + plane->normal[0] = 1; + return; + } +} + +/* +================= +R_GetPortalOrientation + +entityNum is the entity that the portal surface is a part of, which may +be moving and rotating. + +Returns qtrue if it should be mirrored +================= +*/ +qboolean R_GetPortalOrientations( drawSurf_t *drawSurf, int entityNum, + orientation_t *surface, orientation_t *camera, + vec3_t pvsOrigin, qboolean *mirror ) { + int i; + cplane_t originalPlane, plane; + trRefEntity_t *e; + float d; + vec3_t transformed; + + // create plane axis for the portal we are seeing + R_PlaneForSurface( drawSurf->surface, &originalPlane ); + + // rotate the plane if necessary + if ( entityNum != REFENTITYNUM_WORLD ) { + tr.currentEntityNum = entityNum; + tr.currentEntity = &tr.refdef.entities[entityNum]; + + // get the orientation of the entity + R_RotateForEntity( tr.currentEntity, &tr.viewParms, &tr.or ); + + // rotate the plane, but keep the non-rotated version for matching + // against the portalSurface entities + R_LocalNormalToWorld( originalPlane.normal, plane.normal ); + plane.dist = originalPlane.dist + DotProduct( plane.normal, tr.or.origin ); + + // translate the original plane + originalPlane.dist = originalPlane.dist + DotProduct( originalPlane.normal, tr.or.origin ); + } else { + plane = originalPlane; + } + + VectorCopy( plane.normal, surface->axis[0] ); + PerpendicularVector( surface->axis[1], surface->axis[0] ); + CrossProduct( surface->axis[0], surface->axis[1], surface->axis[2] ); + + // locate the portal entity closest to this plane. + // origin will be the origin of the portal, origin2 will be + // the origin of the camera + for ( i = 0 ; i < tr.refdef.num_entities ; i++ ) { + e = &tr.refdef.entities[i]; + if ( e->e.reType != RT_PORTALSURFACE ) { + continue; + } + + d = DotProduct( e->e.origin, originalPlane.normal ) - originalPlane.dist; + if ( d > 64 || d < -64) { + continue; + } + + // get the pvsOrigin from the entity + VectorCopy( e->e.oldorigin, pvsOrigin ); + + // if the entity is just a mirror, don't use as a camera point + if ( e->e.oldorigin[0] == e->e.origin[0] && + e->e.oldorigin[1] == e->e.origin[1] && + e->e.oldorigin[2] == e->e.origin[2] ) { + VectorScale( plane.normal, plane.dist, surface->origin ); + VectorCopy( surface->origin, camera->origin ); + VectorSubtract( vec3_origin, surface->axis[0], camera->axis[0] ); + VectorCopy( surface->axis[1], camera->axis[1] ); + VectorCopy( surface->axis[2], camera->axis[2] ); + + *mirror = qtrue; + return qtrue; + } + + // project the origin onto the surface plane to get + // an origin point we can rotate around + d = DotProduct( e->e.origin, plane.normal ) - plane.dist; + VectorMA( e->e.origin, -d, surface->axis[0], surface->origin ); + + // now get the camera origin and orientation + VectorCopy( e->e.oldorigin, camera->origin ); + AxisCopy( e->e.axis, camera->axis ); + VectorSubtract( vec3_origin, camera->axis[0], camera->axis[0] ); + VectorSubtract( vec3_origin, camera->axis[1], camera->axis[1] ); + + // optionally rotate + if ( e->e.oldframe ) { + // if a speed is specified + if ( e->e.frame ) { + // continuous rotate + d = (tr.refdef.time/1000.0f) * e->e.frame; + VectorCopy( camera->axis[1], transformed ); + RotatePointAroundVector( camera->axis[1], camera->axis[0], transformed, d ); + CrossProduct( camera->axis[0], camera->axis[1], camera->axis[2] ); + } else { + // bobbing rotate, with skinNum being the rotation offset + d = sin( tr.refdef.time * 0.003f ); + d = e->e.skinNum + d * 4; + VectorCopy( camera->axis[1], transformed ); + RotatePointAroundVector( camera->axis[1], camera->axis[0], transformed, d ); + CrossProduct( camera->axis[0], camera->axis[1], camera->axis[2] ); + } + } + else if ( e->e.skinNum ) { + d = e->e.skinNum; + VectorCopy( camera->axis[1], transformed ); + RotatePointAroundVector( camera->axis[1], camera->axis[0], transformed, d ); + CrossProduct( camera->axis[0], camera->axis[1], camera->axis[2] ); + } + *mirror = qfalse; + return qtrue; + } + + // if we didn't locate a portal entity, don't render anything. + // We don't want to just treat it as a mirror, because without a + // portal entity the server won't have communicated a proper entity set + // in the snapshot + + // unfortunately, with local movement prediction it is easily possible + // to see a surface before the server has communicated the matching + // portal surface entity, so we don't want to print anything here... + + //ri.Printf( PRINT_ALL, "Portal surface without a portal entity\n" ); + + return qfalse; +} + +static qboolean IsMirror( const drawSurf_t *drawSurf, int entityNum ) +{ + int i; + cplane_t originalPlane, plane; + trRefEntity_t *e; + float d; + + // create plane axis for the portal we are seeing + R_PlaneForSurface( drawSurf->surface, &originalPlane ); + + // rotate the plane if necessary + if ( entityNum != REFENTITYNUM_WORLD ) + { + tr.currentEntityNum = entityNum; + tr.currentEntity = &tr.refdef.entities[entityNum]; + + // get the orientation of the entity + R_RotateForEntity( tr.currentEntity, &tr.viewParms, &tr.or ); + + // rotate the plane, but keep the non-rotated version for matching + // against the portalSurface entities + R_LocalNormalToWorld( originalPlane.normal, plane.normal ); + plane.dist = originalPlane.dist + DotProduct( plane.normal, tr.or.origin ); + + // translate the original plane + originalPlane.dist = originalPlane.dist + DotProduct( originalPlane.normal, tr.or.origin ); + } + else + { + plane = originalPlane; + } + + // locate the portal entity closest to this plane. + // origin will be the origin of the portal, origin2 will be + // the origin of the camera + for ( i = 0 ; i < tr.refdef.num_entities ; i++ ) + { + e = &tr.refdef.entities[i]; + if ( e->e.reType != RT_PORTALSURFACE ) { + continue; + } + + d = DotProduct( e->e.origin, originalPlane.normal ) - originalPlane.dist; + if ( d > 64 || d < -64) { + continue; + } + + // if the entity is just a mirror, don't use as a camera point + if ( e->e.oldorigin[0] == e->e.origin[0] && + e->e.oldorigin[1] == e->e.origin[1] && + e->e.oldorigin[2] == e->e.origin[2] ) + { + return qtrue; + } + + return qfalse; + } + return qfalse; +} + +/* +** SurfIsOffscreen +** +** Determines if a surface is completely offscreen. +*/ +static qboolean SurfIsOffscreen( const drawSurf_t *drawSurf, vec4_t clipDest[128] ) { + float shortest = 100000000; + int entityNum; + int numTriangles; + shader_t *shader; + int fogNum; + int dlighted; + vec4_t clip, eye; + int i; + unsigned int pointOr = 0; + unsigned int pointAnd = (unsigned int)~0; + + R_RotateForViewer(); + + R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted ); + RB_BeginSurface( shader, fogNum ); + rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface ); + + assert( tess.numVertexes < 128 ); + + for ( i = 0; i < tess.numVertexes; i++ ) + { + int j; + unsigned int pointFlags = 0; + + R_TransformModelToClip( tess.xyz[i], tr.or.modelMatrix, tr.viewParms.projectionMatrix, eye, clip ); + + for ( j = 0; j < 3; j++ ) + { + if ( clip[j] >= clip[3] ) + { + pointFlags |= (1 << (j*2)); + } + else if ( clip[j] <= -clip[3] ) + { + pointFlags |= ( 1 << (j*2+1)); + } + } + pointAnd &= pointFlags; + pointOr |= pointFlags; + } + + // trivially reject + if ( pointAnd ) + { + return qtrue; + } + + // determine if this surface is backfaced and also determine the distance + // to the nearest vertex so we can cull based on portal range. Culling + // based on vertex distance isn't 100% correct (we should be checking for + // range to the surface), but it's good enough for the types of portals + // we have in the game right now. + numTriangles = tess.numIndexes / 3; + + for ( i = 0; i < tess.numIndexes; i += 3 ) + { + vec3_t normal; + float len; + + VectorSubtract( tess.xyz[tess.indexes[i]], tr.viewParms.or.origin, normal ); + + len = VectorLengthSquared( normal ); // lose the sqrt + if ( len < shortest ) + { + shortest = len; + } + + if ( DotProduct( normal, tess.normal[tess.indexes[i]] ) >= 0 ) + { + numTriangles--; + } + } + if ( !numTriangles ) + { + return qtrue; + } + + // mirrors can early out at this point, since we don't do a fade over distance + // with them (although we could) + if ( IsMirror( drawSurf, entityNum ) ) + { + return qfalse; + } + + if ( shortest > (tess.shader->portalRange*tess.shader->portalRange) ) + { + return qtrue; + } + + return qfalse; +} + +/* +======================== +R_MirrorViewBySurface + +Returns qtrue if another view has been rendered +======================== +*/ +qboolean R_MirrorViewBySurface (drawSurf_t *drawSurf, int entityNum) { + vec4_t clipDest[128]; + viewParms_t newParms; + viewParms_t oldParms; + orientation_t surface, camera; + + // don't recursively mirror + if (tr.viewParms.isPortal) { + ri.Printf( PRINT_DEVELOPER, "WARNING: recursive mirror/portal found\n" ); + return qfalse; + } + + if ( r_noportals->integer || (r_fastsky->integer == 1) ) { + return qfalse; + } + + // trivially reject portal/mirror + if ( SurfIsOffscreen( drawSurf, clipDest ) ) { + return qfalse; + } + + // save old viewParms so we can return to it after the mirror view + oldParms = tr.viewParms; + + newParms = tr.viewParms; + newParms.isPortal = qtrue; + if ( !R_GetPortalOrientations( drawSurf, entityNum, &surface, &camera, + newParms.pvsOrigin, &newParms.isMirror ) ) { + return qfalse; // bad portal, no portalentity + } + + R_MirrorPoint (oldParms.or.origin, &surface, &camera, newParms.or.origin ); + + VectorSubtract( vec3_origin, camera.axis[0], newParms.portalPlane.normal ); + newParms.portalPlane.dist = DotProduct( camera.origin, newParms.portalPlane.normal ); + + R_MirrorVector (oldParms.or.axis[0], &surface, &camera, newParms.or.axis[0]); + R_MirrorVector (oldParms.or.axis[1], &surface, &camera, newParms.or.axis[1]); + R_MirrorVector (oldParms.or.axis[2], &surface, &camera, newParms.or.axis[2]); + + // OPTIMIZE: restrict the viewport on the mirrored view + + // render the mirror view + R_RenderView (&newParms); + + tr.viewParms = oldParms; + + return qtrue; +} + +/* +================= +R_SpriteFogNum + +See if a sprite is inside a fog volume +================= +*/ +int R_SpriteFogNum( trRefEntity_t *ent ) { + int i, j; + fog_t *fog; + + if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { + return 0; + } + + for ( i = 1 ; i < tr.world->numfogs ; i++ ) { + fog = &tr.world->fogs[i]; + for ( j = 0 ; j < 3 ; j++ ) { + if ( ent->e.origin[j] - ent->e.radius >= fog->bounds[1][j] ) { + break; + } + if ( ent->e.origin[j] + ent->e.radius <= fog->bounds[0][j] ) { + break; + } + } + if ( j == 3 ) { + return i; + } + } + + return 0; +} + +/* +========================================================================================== + +DRAWSURF SORTING + +========================================================================================== +*/ + +/* +=============== +R_Radix +=============== +*/ +static ID_INLINE void R_Radix( int byte, int size, drawSurf_t *source, drawSurf_t *dest ) +{ + int count[ 256 ] = { 0 }; + int index[ 256 ]; + int i; + unsigned char *sortKey = NULL; + unsigned char *end = NULL; + + sortKey = ( (unsigned char *)&source[ 0 ].sort ) + byte; + end = sortKey + ( size * sizeof( drawSurf_t ) ); + for( ; sortKey < end; sortKey += sizeof( drawSurf_t ) ) + ++count[ *sortKey ]; + + index[ 0 ] = 0; + + for( i = 1; i < 256; ++i ) + index[ i ] = index[ i - 1 ] + count[ i - 1 ]; + + sortKey = ( (unsigned char *)&source[ 0 ].sort ) + byte; + for( i = 0; i < size; ++i, sortKey += sizeof( drawSurf_t ) ) + dest[ index[ *sortKey ]++ ] = source[ i ]; +} + +/* +=============== +R_RadixSort + +Radix sort with 4 byte size buckets +=============== +*/ +static void R_RadixSort( drawSurf_t *source, int size ) +{ + static drawSurf_t scratch[ MAX_DRAWSURFS ]; +#ifdef Q3_LITTLE_ENDIAN + R_Radix( 0, size, source, scratch ); + R_Radix( 1, size, scratch, source ); + R_Radix( 2, size, source, scratch ); + R_Radix( 3, size, scratch, source ); +#else + R_Radix( 3, size, source, scratch ); + R_Radix( 2, size, scratch, source ); + R_Radix( 1, size, source, scratch ); + R_Radix( 0, size, scratch, source ); +#endif //Q3_LITTLE_ENDIAN +} + +//========================================================================================== + +/* +================= +R_AddDrawSurf +================= +*/ +void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, + int fogIndex, int dlightMap ) { + int index; + + // instead of checking for overflow, we just mask the index + // so it wraps around + index = tr.refdef.numDrawSurfs & DRAWSURF_MASK; + // the sort data is packed into a single 32 bit value so it can be + // compared quickly during the qsorting process + tr.refdef.drawSurfs[index].sort = (shader->sortedIndex << QSORT_SHADERNUM_SHIFT) + | tr.shiftedEntityNum | ( fogIndex << QSORT_FOGNUM_SHIFT ) | (int)dlightMap; + tr.refdef.drawSurfs[index].surface = surface; + tr.refdef.numDrawSurfs++; +} + +/* +================= +R_DecomposeSort +================= +*/ +void R_DecomposeSort( unsigned sort, int *entityNum, shader_t **shader, + int *fogNum, int *dlightMap ) { + *fogNum = ( sort >> QSORT_FOGNUM_SHIFT ) & 31; + *shader = tr.sortedShaders[ ( sort >> QSORT_SHADERNUM_SHIFT ) & (MAX_SHADERS-1) ]; + *entityNum = ( sort >> QSORT_REFENTITYNUM_SHIFT ) & REFENTITYNUM_MASK; + *dlightMap = sort & 3; +} + +/* +================= +R_SortDrawSurfs +================= +*/ +void R_SortDrawSurfs( drawSurf_t *drawSurfs, int numDrawSurfs ) { + shader_t *shader; + int fogNum; + int entityNum; + int dlighted; + int i; + + // it is possible for some views to not have any surfaces + if ( numDrawSurfs < 1 ) { + // we still need to add it for hyperspace cases + R_AddDrawSurfCmd( drawSurfs, numDrawSurfs ); + return; + } + + // if we overflowed MAX_DRAWSURFS, the drawsurfs + // wrapped around in the buffer and we will be missing + // the first surfaces, not the last ones + if ( numDrawSurfs > MAX_DRAWSURFS ) { + numDrawSurfs = MAX_DRAWSURFS; + } + + // sort the drawsurfs by sort type, then orientation, then shader + R_RadixSort( drawSurfs, numDrawSurfs ); + + // check for any pass through drawing, which + // may cause another view to be rendered first + for ( i = 0 ; i < numDrawSurfs ; i++ ) { + R_DecomposeSort( (drawSurfs+i)->sort, &entityNum, &shader, &fogNum, &dlighted ); + + if ( shader->sort > SS_PORTAL ) { + break; + } + + // no shader should ever have this sort type + if ( shader->sort == SS_BAD ) { + ri.Error (ERR_DROP, "Shader '%s'with sort == SS_BAD", shader->name ); + } + + // if the mirror was completely clipped away, we may need to check another surface + if ( R_MirrorViewBySurface( (drawSurfs+i), entityNum) ) { + // this is a debug option to see exactly what is being mirrored + if ( r_portalOnly->integer ) { + return; + } + break; // only one mirror view at a time + } + } + + R_AddDrawSurfCmd( drawSurfs, numDrawSurfs ); +} + +/* +============= +R_AddEntitySurfaces +============= +*/ +void R_AddEntitySurfaces (void) { + trRefEntity_t *ent; + shader_t *shader; + + if ( !r_drawentities->integer ) { + return; + } + + for ( tr.currentEntityNum = 0; + tr.currentEntityNum < tr.refdef.num_entities; + tr.currentEntityNum++ ) { + ent = tr.currentEntity = &tr.refdef.entities[tr.currentEntityNum]; + + ent->needDlights = qfalse; + + // preshift the value we are going to OR into the drawsurf sort + tr.shiftedEntityNum = tr.currentEntityNum << QSORT_REFENTITYNUM_SHIFT; + + // + // the weapon model must be handled special -- + // we don't want the hacked weapon position showing in + // mirrors, because the true body position will already be drawn + // + if ( (ent->e.renderfx & RF_FIRST_PERSON) && tr.viewParms.isPortal) { + continue; + } + + // simple generated models, like sprites and beams, are not culled + switch ( ent->e.reType ) { + case RT_PORTALSURFACE: + break; // don't draw anything + case RT_SPRITE: + case RT_BEAM: + case RT_LIGHTNING: + case RT_RAIL_CORE: + case RT_RAIL_RINGS: + // self blood sprites, talk balloons, etc should not be drawn in the primary + // view. We can't just do this check for all entities, because md3 + // entities may still want to cast shadows from them + if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal) { + continue; + } + shader = R_GetShaderByHandle( ent->e.customShader ); + R_AddDrawSurf( &entitySurface, shader, R_SpriteFogNum( ent ), 0 ); + break; + + case RT_MODEL: + // we must set up parts of tr.or for model culling + R_RotateForEntity( ent, &tr.viewParms, &tr.or ); + + tr.currentModel = R_GetModelByHandle( ent->e.hModel ); + if (!tr.currentModel) { + R_AddDrawSurf( &entitySurface, tr.defaultShader, 0, 0 ); + } else { + switch ( tr.currentModel->type ) { + case MOD_MESH: + R_AddMD3Surfaces( ent ); + break; + case MOD_MD4: + R_AddAnimSurfaces( ent ); + break; +#ifdef RAVENMD4 + case MOD_MDR: + R_MDRAddAnimSurfaces( ent ); + break; +#endif + case MOD_IQM: + R_AddIQMSurfaces( ent ); + break; + case MOD_BRUSH: + R_AddBrushModelSurfaces( ent ); + break; + case MOD_BAD: // null model axis + if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal) { + break; + } + R_AddDrawSurf( &entitySurface, tr.defaultShader, 0, 0 ); + break; + default: + ri.Error( ERR_DROP, "R_AddEntitySurfaces: Bad modeltype" ); + break; + } + } + break; + default: + ri.Error( ERR_DROP, "R_AddEntitySurfaces: Bad reType" ); + } + } + +} + + +/* +==================== +R_GenerateDrawSurfs +==================== +*/ +void R_GenerateDrawSurfs( void ) { + R_AddWorldSurfaces (); + + R_AddPolygonSurfaces(); + + // set the projection matrix with the minimum zfar + // now that we have the world bounded + // this needs to be done before entities are + // added, because they use the projection + // matrix for lod calculation + + // dynamically compute far clip plane distance + R_SetFarClip(); + + // we know the size of the clipping volume. Now set the rest of the projection matrix. + R_SetupProjectionZ (&tr.viewParms); + + R_AddEntitySurfaces (); +} + +/* +================ +R_DebugPolygon +================ +*/ +void R_DebugPolygon( int color, int numPoints, float *points ) { + int i; + + GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + + // draw solid shade + + qglColor3f( color&1, (color>>1)&1, (color>>2)&1 ); + qglBegin( GL_POLYGON ); + for ( i = 0 ; i < numPoints ; i++ ) { + qglVertex3fv( points + i * 3 ); + } + qglEnd(); + + // draw wireframe outline + GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + qglDepthRange( 0, 0 ); + qglColor3f( 1, 1, 1 ); + qglBegin( GL_POLYGON ); + for ( i = 0 ; i < numPoints ; i++ ) { + qglVertex3fv( points + i * 3 ); + } + qglEnd(); + qglDepthRange( 0, 1 ); +} + +/* +==================== +R_DebugGraphics + +Visualization aid for movement clipping debugging +==================== +*/ +void R_DebugGraphics( void ) { + if ( !r_debugSurface->integer ) { + return; + } + + R_IssuePendingRenderCommands(); + + GL_Bind( tr.whiteImage); + GL_Cull( CT_FRONT_SIDED ); + ri.CM_DrawDebugSurface( R_DebugPolygon ); +} + + +/* +================ +R_RenderView + +A view may be either the actual camera view, +or a mirror / remote location +================ +*/ +void R_RenderView (viewParms_t *parms) { + int firstDrawSurf; + + if ( parms->viewportWidth <= 0 || parms->viewportHeight <= 0 ) { + return; + } + + tr.viewCount++; + + tr.viewParms = *parms; + tr.viewParms.frameSceneNum = tr.frameSceneNum; + tr.viewParms.frameCount = tr.frameCount; + + firstDrawSurf = tr.refdef.numDrawSurfs; + + tr.viewCount++; + + // set viewParms.world + R_RotateForViewer (); + + R_SetupProjection(&tr.viewParms, r_zproj->value, qtrue); + + R_GenerateDrawSurfs(); + + R_SortDrawSurfs( tr.refdef.drawSurfs + firstDrawSurf, tr.refdef.numDrawSurfs - firstDrawSurf ); + + // draw main system development information (surface outlines, etc) + R_DebugGraphics(); +} + + + diff --git a/src/renderergl1/tr_marks.c b/src/renderergl1/tr_marks.c new file mode 100644 index 00000000..6bcd7cb1 --- /dev/null +++ b/src/renderergl1/tr_marks.c @@ -0,0 +1,459 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_marks.c -- polygon projection on the world polygons + +#include "tr_local.h" +//#include "assert.h" + +#define MAX_VERTS_ON_POLY 64 + +#define MARKER_OFFSET 0 // 1 + +/* +============= +R_ChopPolyBehindPlane + +Out must have space for two more vertexes than in +============= +*/ +#define SIDE_FRONT 0 +#define SIDE_BACK 1 +#define SIDE_ON 2 +static void R_ChopPolyBehindPlane( int numInPoints, vec3_t inPoints[MAX_VERTS_ON_POLY], + int *numOutPoints, vec3_t outPoints[MAX_VERTS_ON_POLY], + vec3_t normal, vec_t dist, vec_t epsilon) { + float dists[MAX_VERTS_ON_POLY+4]; + int sides[MAX_VERTS_ON_POLY+4]; + int counts[3]; + float dot; + int i, j; + float *p1, *p2, *clip; + float d; + + // don't clip if it might overflow + if ( numInPoints >= MAX_VERTS_ON_POLY - 2 ) { + *numOutPoints = 0; + return; + } + + counts[0] = counts[1] = counts[2] = 0; + + // determine sides for each point + for ( i = 0 ; i < numInPoints ; i++ ) { + dot = DotProduct( inPoints[i], normal ); + dot -= dist; + dists[i] = dot; + if ( dot > epsilon ) { + sides[i] = SIDE_FRONT; + } else if ( dot < -epsilon ) { + sides[i] = SIDE_BACK; + } else { + sides[i] = SIDE_ON; + } + counts[sides[i]]++; + } + sides[i] = sides[0]; + dists[i] = dists[0]; + + *numOutPoints = 0; + + if ( !counts[0] ) { + return; + } + if ( !counts[1] ) { + *numOutPoints = numInPoints; + Com_Memcpy( outPoints, inPoints, numInPoints * sizeof(vec3_t) ); + return; + } + + for ( i = 0 ; i < numInPoints ; i++ ) { + p1 = inPoints[i]; + clip = outPoints[ *numOutPoints ]; + + if ( sides[i] == SIDE_ON ) { + VectorCopy( p1, clip ); + (*numOutPoints)++; + continue; + } + + if ( sides[i] == SIDE_FRONT ) { + VectorCopy( p1, clip ); + (*numOutPoints)++; + clip = outPoints[ *numOutPoints ]; + } + + if ( sides[i+1] == SIDE_ON || sides[i+1] == sides[i] ) { + continue; + } + + // generate a split point + p2 = inPoints[ (i+1) % numInPoints ]; + + d = dists[i] - dists[i+1]; + if ( d == 0 ) { + dot = 0; + } else { + dot = dists[i] / d; + } + + // clip xyz + + for (j=0 ; j<3 ; j++) { + clip[j] = p1[j] + dot * ( p2[j] - p1[j] ); + } + + (*numOutPoints)++; + } +} + +/* +================= +R_BoxSurfaces_r + +================= +*/ +void R_BoxSurfaces_r(mnode_t *node, vec3_t mins, vec3_t maxs, surfaceType_t **list, int listsize, int *listlength, vec3_t dir) { + + int s, c; + msurface_t *surf, **mark; + + // do the tail recursion in a loop + while ( node->contents == -1 ) { + s = BoxOnPlaneSide( mins, maxs, node->plane ); + if (s == 1) { + node = node->children[0]; + } else if (s == 2) { + node = node->children[1]; + } else { + R_BoxSurfaces_r(node->children[0], mins, maxs, list, listsize, listlength, dir); + node = node->children[1]; + } + } + + // add the individual surfaces + mark = node->firstmarksurface; + c = node->nummarksurfaces; + while (c--) { + // + if (*listlength >= listsize) break; + // + surf = *mark; + // check if the surface has NOIMPACT or NOMARKS set + if ( ( surf->shader->surfaceFlags & ( SURF_NOIMPACT | SURF_NOMARKS ) ) + || ( surf->shader->contentFlags & CONTENTS_FOG ) ) { + surf->viewCount = tr.viewCount; + } + // extra check for surfaces to avoid list overflows + else if (*(surf->data) == SF_FACE) { + // the face plane should go through the box + s = BoxOnPlaneSide( mins, maxs, &(( srfSurfaceFace_t * ) surf->data)->plane ); + if (s == 1 || s == 2) { + surf->viewCount = tr.viewCount; + } else if (DotProduct((( srfSurfaceFace_t * ) surf->data)->plane.normal, dir) > -0.5) { + // don't add faces that make sharp angles with the projection direction + surf->viewCount = tr.viewCount; + } + } + else if (*(surfaceType_t *) (surf->data) != SF_GRID && + *(surfaceType_t *) (surf->data) != SF_TRIANGLES) + surf->viewCount = tr.viewCount; + // check the viewCount because the surface may have + // already been added if it spans multiple leafs + if (surf->viewCount != tr.viewCount) { + surf->viewCount = tr.viewCount; + list[*listlength] = (surfaceType_t *) surf->data; + (*listlength)++; + } + mark++; + } +} + +/* +================= +R_AddMarkFragments + +================= +*/ +void R_AddMarkFragments(int numClipPoints, vec3_t clipPoints[2][MAX_VERTS_ON_POLY], + int numPlanes, vec3_t *normals, float *dists, + int maxPoints, vec3_t pointBuffer, + int maxFragments, markFragment_t *fragmentBuffer, + int *returnedPoints, int *returnedFragments, + vec3_t mins, vec3_t maxs) { + int pingPong, i; + markFragment_t *mf; + + // chop the surface by all the bounding planes of the to be projected polygon + pingPong = 0; + + for ( i = 0 ; i < numPlanes ; i++ ) { + + R_ChopPolyBehindPlane( numClipPoints, clipPoints[pingPong], + &numClipPoints, clipPoints[!pingPong], + normals[i], dists[i], 0.5 ); + pingPong ^= 1; + if ( numClipPoints == 0 ) { + break; + } + } + // completely clipped away? + if ( numClipPoints == 0 ) { + return; + } + + // add this fragment to the returned list + if ( numClipPoints + (*returnedPoints) > maxPoints ) { + return; // not enough space for this polygon + } + /* + // all the clip points should be within the bounding box + for ( i = 0 ; i < numClipPoints ; i++ ) { + int j; + for ( j = 0 ; j < 3 ; j++ ) { + if (clipPoints[pingPong][i][j] < mins[j] - 0.5) break; + if (clipPoints[pingPong][i][j] > maxs[j] + 0.5) break; + } + if (j < 3) break; + } + if (i < numClipPoints) return; + */ + + mf = fragmentBuffer + (*returnedFragments); + mf->firstPoint = (*returnedPoints); + mf->numPoints = numClipPoints; + Com_Memcpy( pointBuffer + (*returnedPoints) * 3, clipPoints[pingPong], numClipPoints * sizeof(vec3_t) ); + + (*returnedPoints) += numClipPoints; + (*returnedFragments)++; +} + +/* +================= +R_MarkFragments + +================= +*/ +int R_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection, + int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ) { + int numsurfaces, numPlanes; + int i, j, k, m, n; + surfaceType_t *surfaces[64]; + vec3_t mins, maxs; + int returnedFragments; + int returnedPoints; + vec3_t normals[MAX_VERTS_ON_POLY+2]; + float dists[MAX_VERTS_ON_POLY+2]; + vec3_t clipPoints[2][MAX_VERTS_ON_POLY]; + int numClipPoints; + float *v; + srfGridMesh_t *cv; + drawVert_t *dv; + vec3_t normal; + vec3_t projectionDir; + vec3_t v1, v2; + int *indexes; + + if (numPoints <= 0) { + return 0; + } + + //increment view count for double check prevention + tr.viewCount++; + + // + VectorNormalize2( projection, projectionDir ); + // find all the brushes that are to be considered + ClearBounds( mins, maxs ); + for ( i = 0 ; i < numPoints ; i++ ) { + vec3_t temp; + + AddPointToBounds( points[i], mins, maxs ); + VectorAdd( points[i], projection, temp ); + AddPointToBounds( temp, mins, maxs ); + // make sure we get all the leafs (also the one(s) in front of the hit surface) + VectorMA( points[i], -20, projectionDir, temp ); + AddPointToBounds( temp, mins, maxs ); + } + + if (numPoints > MAX_VERTS_ON_POLY) numPoints = MAX_VERTS_ON_POLY; + // create the bounding planes for the to be projected polygon + for ( i = 0 ; i < numPoints ; i++ ) { + VectorSubtract(points[(i+1)%numPoints], points[i], v1); + VectorAdd(points[i], projection, v2); + VectorSubtract(points[i], v2, v2); + CrossProduct(v1, v2, normals[i]); + VectorNormalizeFast(normals[i]); + dists[i] = DotProduct(normals[i], points[i]); + } + // add near and far clipping planes for projection + VectorCopy(projectionDir, normals[numPoints]); + dists[numPoints] = DotProduct(normals[numPoints], points[0]) - 32; + VectorCopy(projectionDir, normals[numPoints+1]); + VectorInverse(normals[numPoints+1]); + dists[numPoints+1] = DotProduct(normals[numPoints+1], points[0]) - 20; + numPlanes = numPoints + 2; + + numsurfaces = 0; + R_BoxSurfaces_r(tr.world->nodes, mins, maxs, surfaces, 64, &numsurfaces, projectionDir); + //assert(numsurfaces <= 64); + //assert(numsurfaces != 64); + + returnedPoints = 0; + returnedFragments = 0; + + for ( i = 0 ; i < numsurfaces ; i++ ) { + + if (*surfaces[i] == SF_GRID) { + + cv = (srfGridMesh_t *) surfaces[i]; + for ( m = 0 ; m < cv->height - 1 ; m++ ) { + for ( n = 0 ; n < cv->width - 1 ; n++ ) { + // We triangulate the grid and chop all triangles within + // the bounding planes of the to be projected polygon. + // LOD is not taken into account, not such a big deal though. + // + // It's probably much nicer to chop the grid itself and deal + // with this grid as a normal SF_GRID surface so LOD will + // be applied. However the LOD of that chopped grid must + // be synced with the LOD of the original curve. + // One way to do this; the chopped grid shares vertices with + // the original curve. When LOD is applied to the original + // curve the unused vertices are flagged. Now the chopped curve + // should skip the flagged vertices. This still leaves the + // problems with the vertices at the chopped grid edges. + // + // To avoid issues when LOD applied to "hollow curves" (like + // the ones around many jump pads) we now just add a 2 unit + // offset to the triangle vertices. + // The offset is added in the vertex normal vector direction + // so all triangles will still fit together. + // The 2 unit offset should avoid pretty much all LOD problems. + + numClipPoints = 3; + + dv = cv->verts + m * cv->width + n; + + VectorCopy(dv[0].xyz, clipPoints[0][0]); + VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[0].normal, clipPoints[0][0]); + VectorCopy(dv[cv->width].xyz, clipPoints[0][1]); + VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]); + VectorCopy(dv[1].xyz, clipPoints[0][2]); + VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[1].normal, clipPoints[0][2]); + // check the normal of this triangle + VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1); + VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2); + CrossProduct(v1, v2, normal); + VectorNormalizeFast(normal); + if (DotProduct(normal, projectionDir) < -0.1) { + // add the fragments of this triangle + R_AddMarkFragments(numClipPoints, clipPoints, + numPlanes, normals, dists, + maxPoints, pointBuffer, + maxFragments, fragmentBuffer, + &returnedPoints, &returnedFragments, mins, maxs); + + if ( returnedFragments == maxFragments ) { + return returnedFragments; // not enough space for more fragments + } + } + + VectorCopy(dv[1].xyz, clipPoints[0][0]); + VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[1].normal, clipPoints[0][0]); + VectorCopy(dv[cv->width].xyz, clipPoints[0][1]); + VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]); + VectorCopy(dv[cv->width+1].xyz, clipPoints[0][2]); + VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[cv->width+1].normal, clipPoints[0][2]); + // check the normal of this triangle + VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1); + VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2); + CrossProduct(v1, v2, normal); + VectorNormalizeFast(normal); + if (DotProduct(normal, projectionDir) < -0.05) { + // add the fragments of this triangle + R_AddMarkFragments(numClipPoints, clipPoints, + numPlanes, normals, dists, + maxPoints, pointBuffer, + maxFragments, fragmentBuffer, + &returnedPoints, &returnedFragments, mins, maxs); + + if ( returnedFragments == maxFragments ) { + return returnedFragments; // not enough space for more fragments + } + } + } + } + } + else if (*surfaces[i] == SF_FACE) { + + srfSurfaceFace_t *surf = ( srfSurfaceFace_t * ) surfaces[i]; + + // check the normal of this face + if (DotProduct(surf->plane.normal, projectionDir) > -0.5) { + continue; + } + + indexes = (int *)( (byte *)surf + surf->ofsIndices ); + for ( k = 0 ; k < surf->numIndices ; k += 3 ) { + for ( j = 0 ; j < 3 ; j++ ) { + v = surf->points[0] + VERTEXSIZE * indexes[k+j];; + VectorMA( v, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j] ); + } + + // add the fragments of this face + R_AddMarkFragments( 3 , clipPoints, + numPlanes, normals, dists, + maxPoints, pointBuffer, + maxFragments, fragmentBuffer, + &returnedPoints, &returnedFragments, mins, maxs); + if ( returnedFragments == maxFragments ) { + return returnedFragments; // not enough space for more fragments + } + } + } + else if(*surfaces[i] == SF_TRIANGLES && r_marksOnTriangleMeshes->integer) { + + srfTriangles_t *surf = (srfTriangles_t *) surfaces[i]; + + for (k = 0; k < surf->numIndexes; k += 3) + { + for(j = 0; j < 3; j++) + { + v = surf->verts[surf->indexes[k + j]].xyz; + VectorMA(v, MARKER_OFFSET, surf->verts[surf->indexes[k + j]].normal, clipPoints[0][j]); + } + + // add the fragments of this face + R_AddMarkFragments(3, clipPoints, + numPlanes, normals, dists, + maxPoints, pointBuffer, + maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs); + if(returnedFragments == maxFragments) + { + return returnedFragments; // not enough space for more fragments + } + } + } + } + return returnedFragments; +} + diff --git a/src/renderergl1/tr_mesh.c b/src/renderergl1/tr_mesh.c new file mode 100644 index 00000000..effbae01 --- /dev/null +++ b/src/renderergl1/tr_mesh.c @@ -0,0 +1,417 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_mesh.c: triangle model functions + +#include "tr_local.h" + +static float ProjectRadius( float r, vec3_t location ) +{ + float pr; + float dist; + float c; + vec3_t p; + float projected[4]; + + c = DotProduct( tr.viewParms.or.axis[0], tr.viewParms.or.origin ); + dist = DotProduct( tr.viewParms.or.axis[0], location ) - c; + + if ( dist <= 0 ) + return 0; + + p[0] = 0; + p[1] = fabs( r ); + p[2] = -dist; + + projected[0] = p[0] * tr.viewParms.projectionMatrix[0] + + p[1] * tr.viewParms.projectionMatrix[4] + + p[2] * tr.viewParms.projectionMatrix[8] + + tr.viewParms.projectionMatrix[12]; + + projected[1] = p[0] * tr.viewParms.projectionMatrix[1] + + p[1] * tr.viewParms.projectionMatrix[5] + + p[2] * tr.viewParms.projectionMatrix[9] + + tr.viewParms.projectionMatrix[13]; + + projected[2] = p[0] * tr.viewParms.projectionMatrix[2] + + p[1] * tr.viewParms.projectionMatrix[6] + + p[2] * tr.viewParms.projectionMatrix[10] + + tr.viewParms.projectionMatrix[14]; + + projected[3] = p[0] * tr.viewParms.projectionMatrix[3] + + p[1] * tr.viewParms.projectionMatrix[7] + + p[2] * tr.viewParms.projectionMatrix[11] + + tr.viewParms.projectionMatrix[15]; + + + pr = projected[1] / projected[3]; + + if ( pr > 1.0f ) + pr = 1.0f; + + return pr; +} + +/* +============= +R_CullModel +============= +*/ +static int R_CullModel( md3Header_t *header, trRefEntity_t *ent ) { + vec3_t bounds[2]; + md3Frame_t *oldFrame, *newFrame; + int i; + + // compute frame pointers + newFrame = ( md3Frame_t * ) ( ( byte * ) header + header->ofsFrames ) + ent->e.frame; + oldFrame = ( md3Frame_t * ) ( ( byte * ) header + header->ofsFrames ) + ent->e.oldframe; + + // cull bounding sphere ONLY if this is not an upscaled entity + if ( !ent->e.nonNormalizedAxes ) + { + if ( ent->e.frame == ent->e.oldframe ) + { + switch ( R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ) ) + { + case CULL_OUT: + tr.pc.c_sphere_cull_md3_out++; + return CULL_OUT; + + case CULL_IN: + tr.pc.c_sphere_cull_md3_in++; + return CULL_IN; + + case CULL_CLIP: + tr.pc.c_sphere_cull_md3_clip++; + break; + } + } + else + { + int sphereCull, sphereCullB; + + sphereCull = R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ); + if ( newFrame == oldFrame ) { + sphereCullB = sphereCull; + } else { + sphereCullB = R_CullLocalPointAndRadius( oldFrame->localOrigin, oldFrame->radius ); + } + + if ( sphereCull == sphereCullB ) + { + if ( sphereCull == CULL_OUT ) + { + tr.pc.c_sphere_cull_md3_out++; + return CULL_OUT; + } + else if ( sphereCull == CULL_IN ) + { + tr.pc.c_sphere_cull_md3_in++; + return CULL_IN; + } + else + { + tr.pc.c_sphere_cull_md3_clip++; + } + } + } + } + + // calculate a bounding box in the current coordinate system + for (i = 0 ; i < 3 ; i++) { + bounds[0][i] = oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i]; + bounds[1][i] = oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i]; + } + + switch ( R_CullLocalBox( bounds ) ) + { + case CULL_IN: + tr.pc.c_box_cull_md3_in++; + return CULL_IN; + case CULL_CLIP: + tr.pc.c_box_cull_md3_clip++; + return CULL_CLIP; + case CULL_OUT: + default: + tr.pc.c_box_cull_md3_out++; + return CULL_OUT; + } +} + + +/* +================= +R_ComputeLOD + +================= +*/ +int R_ComputeLOD( trRefEntity_t *ent ) { + float radius; + float flod, lodscale; + float projectedRadius; + md3Frame_t *frame; +#ifdef RAVENMD4 + mdrHeader_t *mdr; + mdrFrame_t *mdrframe; +#endif + int lod; + + if ( tr.currentModel->numLods < 2 ) + { + // model has only 1 LOD level, skip computations and bias + lod = 0; + } + else + { + // multiple LODs exist, so compute projected bounding sphere + // and use that as a criteria for selecting LOD + +#ifdef RAVENMD4 + if(tr.currentModel->type == MOD_MDR) + { + int frameSize; + mdr = (mdrHeader_t *) tr.currentModel->modelData; + frameSize = (size_t) (&((mdrFrame_t *)0)->bones[mdr->numBones]); + + mdrframe = (mdrFrame_t *) ((byte *) mdr + mdr->ofsFrames + frameSize * ent->e.frame); + + radius = RadiusFromBounds(mdrframe->bounds[0], mdrframe->bounds[1]); + } + else +#endif + { + frame = ( md3Frame_t * ) ( ( ( unsigned char * ) tr.currentModel->md3[0] ) + tr.currentModel->md3[0]->ofsFrames ); + + frame += ent->e.frame; + + radius = RadiusFromBounds( frame->bounds[0], frame->bounds[1] ); + } + + if ( ( projectedRadius = ProjectRadius( radius, ent->e.origin ) ) != 0 ) + { + lodscale = r_lodscale->value; + if (lodscale > 20) lodscale = 20; + flod = 1.0f - projectedRadius * lodscale; + } + else + { + // object intersects near view plane, e.g. view weapon + flod = 0; + } + + flod *= tr.currentModel->numLods; + lod = ri.ftol(flod); + + if ( lod < 0 ) + { + lod = 0; + } + else if ( lod >= tr.currentModel->numLods ) + { + lod = tr.currentModel->numLods - 1; + } + } + + lod += r_lodbias->integer; + + if ( lod >= tr.currentModel->numLods ) + lod = tr.currentModel->numLods - 1; + if ( lod < 0 ) + lod = 0; + + return lod; +} + +/* +================= +R_ComputeFogNum + +================= +*/ +int R_ComputeFogNum( md3Header_t *header, trRefEntity_t *ent ) { + int i, j; + fog_t *fog; + md3Frame_t *md3Frame; + vec3_t localOrigin; + + if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { + return 0; + } + + // FIXME: non-normalized axis issues + md3Frame = ( md3Frame_t * ) ( ( byte * ) header + header->ofsFrames ) + ent->e.frame; + VectorAdd( ent->e.origin, md3Frame->localOrigin, localOrigin ); + for ( i = 1 ; i < tr.world->numfogs ; i++ ) { + fog = &tr.world->fogs[i]; + for ( j = 0 ; j < 3 ; j++ ) { + if ( localOrigin[j] - md3Frame->radius >= fog->bounds[1][j] ) { + break; + } + if ( localOrigin[j] + md3Frame->radius <= fog->bounds[0][j] ) { + break; + } + } + if ( j == 3 ) { + return i; + } + } + + return 0; +} + +/* +================= +R_AddMD3Surfaces + +================= +*/ +void R_AddMD3Surfaces( trRefEntity_t *ent ) { + int i; + md3Header_t *header = NULL; + md3Surface_t *surface = NULL; + md3Shader_t *md3Shader = NULL; + shader_t *shader = NULL; + int cull; + int lod; + int fogNum; + qboolean personalModel; + + // don't add third_person objects if not in a portal + personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal; + + if ( ent->e.renderfx & RF_WRAP_FRAMES ) { + ent->e.frame %= tr.currentModel->md3[0]->numFrames; + ent->e.oldframe %= tr.currentModel->md3[0]->numFrames; + } + + // + // Validate the frames so there is no chance of a crash. + // This will write directly into the entity structure, so + // when the surfaces are rendered, they don't need to be + // range checked again. + // + if ( (ent->e.frame >= tr.currentModel->md3[0]->numFrames) + || (ent->e.frame < 0) + || (ent->e.oldframe >= tr.currentModel->md3[0]->numFrames) + || (ent->e.oldframe < 0) ) { + ri.Printf( PRINT_DEVELOPER, "R_AddMD3Surfaces: no such frame %d to %d for '%s'\n", + ent->e.oldframe, ent->e.frame, + tr.currentModel->name ); + ent->e.frame = 0; + ent->e.oldframe = 0; + } + + // + // compute LOD + // + lod = R_ComputeLOD( ent ); + + header = tr.currentModel->md3[lod]; + + // + // cull the entire model if merged bounding box of both frames + // is outside the view frustum. + // + cull = R_CullModel ( header, ent ); + if ( cull == CULL_OUT ) { + return; + } + + // + // set up lighting now that we know we aren't culled + // + if ( !personalModel || r_shadows->integer > 1 ) { + R_SetupEntityLighting( &tr.refdef, ent ); + } + + // + // see if we are in a fog volume + // + fogNum = R_ComputeFogNum( header, ent ); + + // + // draw all surfaces + // + surface = (md3Surface_t *)( (byte *)header + header->ofsSurfaces ); + for ( i = 0 ; i < header->numSurfaces ; i++ ) { + + if ( ent->e.customShader ) { + shader = R_GetShaderByHandle( ent->e.customShader ); + } else if ( ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins ) { + skin_t *skin; + int j; + + skin = R_GetSkinByHandle( ent->e.customSkin ); + + // match the surface name to something in the skin file + shader = tr.defaultShader; + for ( j = 0 ; j < skin->numSurfaces ; j++ ) { + // the names have both been lowercased + if ( !strcmp( skin->surfaces[j]->name, surface->name ) ) { + shader = skin->surfaces[j]->shader; + break; + } + } + if (shader == tr.defaultShader) { + ri.Printf( PRINT_DEVELOPER, "WARNING: no shader for surface %s in skin %s\n", surface->name, skin->name); + } + else if (shader->defaultShader) { + ri.Printf( PRINT_DEVELOPER, "WARNING: shader %s in skin %s not found\n", shader->name, skin->name); + } + } else if ( surface->numShaders <= 0 ) { + shader = tr.defaultShader; + } else { + md3Shader = (md3Shader_t *) ( (byte *)surface + surface->ofsShaders ); + md3Shader += ent->e.skinNum % surface->numShaders; + shader = tr.shaders[ md3Shader->shaderIndex ]; + } + + + // we will add shadows even if the main object isn't visible in the view + + // stencil shadows can't do personal models unless I polyhedron clip + if ( !personalModel + && r_shadows->integer == 2 + && fogNum == 0 + && !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) + && shader->sort == SS_OPAQUE ) { + R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, qfalse ); + } + + // projection shadows work fine with personal models + if ( r_shadows->integer == 3 + && fogNum == 0 + && (ent->e.renderfx & RF_SHADOW_PLANE ) + && shader->sort == SS_OPAQUE ) { + R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, qfalse ); + } + + // don't add third_person objects if not viewing through a portal + if ( !personalModel ) { + R_AddDrawSurf( (void *)surface, shader, fogNum, qfalse ); + } + + surface = (md3Surface_t *)( (byte *)surface + surface->ofsEnd ); + } + +} + diff --git a/src/renderergl1/tr_model.c b/src/renderergl1/tr_model.c new file mode 100644 index 00000000..b51c3ce1 --- /dev/null +++ b/src/renderergl1/tr_model.c @@ -0,0 +1,1318 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_models.c -- model loading and caching + +#include "tr_local.h" + +#define LL(x) x=LittleLong(x) + +static qboolean R_LoadMD3(model_t *mod, int lod, void *buffer, const char *name ); +static qboolean R_LoadMD4(model_t *mod, void *buffer, const char *name ); +#ifdef RAVENMD4 +static qboolean R_LoadMDR(model_t *mod, void *buffer, int filesize, const char *name ); +#endif + +/* +==================== +R_RegisterMD3 +==================== +*/ +qhandle_t R_RegisterMD3(const char *name, model_t *mod) +{ + union { + unsigned *u; + void *v; + } buf; + int lod; + int ident; + qboolean loaded = qfalse; + int numLoaded; + char filename[MAX_QPATH], namebuf[MAX_QPATH+20]; + char *fext, defex[] = "md3"; + + numLoaded = 0; + + strcpy(filename, name); + + fext = strchr(filename, '.'); + if(!fext) + fext = defex; + else + { + *fext = '\0'; + fext++; + } + + for (lod = MD3_MAX_LODS - 1 ; lod >= 0 ; lod--) + { + if(lod) + Com_sprintf(namebuf, sizeof(namebuf), "%s_%d.%s", filename, lod, fext); + else + Com_sprintf(namebuf, sizeof(namebuf), "%s.%s", filename, fext); + + ri.FS_ReadFile( namebuf, &buf.v ); + if(!buf.u) + continue; + + ident = LittleLong(* (unsigned *) buf.u); + if (ident == MD4_IDENT) + loaded = R_LoadMD4(mod, buf.u, name); + else + { + if (ident == MD3_IDENT) + loaded = R_LoadMD3(mod, lod, buf.u, name); + else + ri.Printf(PRINT_WARNING,"R_RegisterMD3: unknown fileid for %s\n", name); + } + + ri.FS_FreeFile(buf.v); + + if(loaded) + { + mod->numLods++; + numLoaded++; + } + else + break; + } + + if(numLoaded) + { + // duplicate into higher lod spots that weren't + // loaded, in case the user changes r_lodbias on the fly + for(lod--; lod >= 0; lod--) + { + mod->numLods++; + mod->md3[lod] = mod->md3[lod + 1]; + } + + return mod->index; + } + +#ifdef _DEBUG + ri.Printf(PRINT_WARNING,"R_RegisterMD3: couldn't load %s\n", name); +#endif + + mod->type = MOD_BAD; + return 0; +} + +#ifdef RAVENMD4 +/* +==================== +R_RegisterMDR +==================== +*/ +qhandle_t R_RegisterMDR(const char *name, model_t *mod) +{ + union { + unsigned *u; + void *v; + } buf; + int ident; + qboolean loaded = qfalse; + int filesize; + + filesize = ri.FS_ReadFile(name, (void **) &buf.v); + if(!buf.u) + { + mod->type = MOD_BAD; + return 0; + } + + ident = LittleLong(*(unsigned *)buf.u); + if(ident == MDR_IDENT) + loaded = R_LoadMDR(mod, buf.u, filesize, name); + + ri.FS_FreeFile (buf.v); + + if(!loaded) + { + ri.Printf(PRINT_WARNING,"R_RegisterMDR: couldn't load mdr file %s\n", name); + mod->type = MOD_BAD; + return 0; + } + + return mod->index; +} +#endif + +/* +==================== +R_RegisterIQM +==================== +*/ +qhandle_t R_RegisterIQM(const char *name, model_t *mod) +{ + union { + unsigned *u; + void *v; + } buf; + qboolean loaded = qfalse; + int filesize; + + filesize = ri.FS_ReadFile(name, (void **) &buf.v); + if(!buf.u) + { + mod->type = MOD_BAD; + return 0; + } + + loaded = R_LoadIQM(mod, buf.u, filesize, name); + + ri.FS_FreeFile (buf.v); + + if(!loaded) + { + ri.Printf(PRINT_WARNING,"R_RegisterIQM: couldn't load iqm file %s\n", name); + mod->type = MOD_BAD; + return 0; + } + + return mod->index; +} + + +typedef struct +{ + char *ext; + qhandle_t (*ModelLoader)( const char *, model_t * ); +} modelExtToLoaderMap_t; + +// Note that the ordering indicates the order of preference used +// when there are multiple models of different formats available +static modelExtToLoaderMap_t modelLoaders[ ] = +{ + { "iqm", R_RegisterIQM }, +#ifdef RAVENMD4 + { "mdr", R_RegisterMDR }, +#endif + { "md4", R_RegisterMD3 }, + { "md3", R_RegisterMD3 } +}; + +static int numModelLoaders = ARRAY_LEN(modelLoaders); + +//=============================================================================== + +/* +** R_GetModelByHandle +*/ +model_t *R_GetModelByHandle( qhandle_t index ) { + model_t *mod; + + // out of range gets the defualt model + if ( index < 1 || index >= tr.numModels ) { + return tr.models[0]; + } + + mod = tr.models[index]; + + return mod; +} + +//=============================================================================== + +/* +** R_AllocModel +*/ +model_t *R_AllocModel( void ) { + model_t *mod; + + if ( tr.numModels == MAX_MOD_KNOWN ) { + return NULL; + } + + mod = ri.Hunk_Alloc( sizeof( *tr.models[tr.numModels] ), h_low ); + mod->index = tr.numModels; + tr.models[tr.numModels] = mod; + tr.numModels++; + + return mod; +} + +/* +==================== +RE_RegisterModel + +Loads in a model for the given name + +Zero will be returned if the model fails to load. +An entry will be retained for failed models as an +optimization to prevent disk rescanning if they are +asked for again. +==================== +*/ +qhandle_t RE_RegisterModel( const char *name ) { + model_t *mod; + qhandle_t hModel; + qboolean orgNameFailed = qfalse; + int orgLoader = -1; + int i; + char localName[ MAX_QPATH ]; + const char *ext; + char altName[ MAX_QPATH ]; + + if ( !name || !name[0] ) { + ri.Printf( PRINT_ALL, "RE_RegisterModel: NULL name\n" ); + return 0; + } + + if ( strlen( name ) >= MAX_QPATH ) { + ri.Printf( PRINT_ALL, "Model name exceeds MAX_QPATH\n" ); + return 0; + } + + // + // search the currently loaded models + // + for ( hModel = 1 ; hModel < tr.numModels; hModel++ ) { + mod = tr.models[hModel]; + if ( !strcmp( mod->name, name ) ) { + if( mod->type == MOD_BAD ) { + return 0; + } + return hModel; + } + } + + // allocate a new model_t + + if ( ( mod = R_AllocModel() ) == NULL ) { + ri.Printf( PRINT_WARNING, "RE_RegisterModel: R_AllocModel() failed for '%s'\n", name); + return 0; + } + + // only set the name after the model has been successfully loaded + Q_strncpyz( mod->name, name, sizeof( mod->name ) ); + + + R_IssuePendingRenderCommands(); + + mod->type = MOD_BAD; + mod->numLods = 0; + + // + // load the files + // + Q_strncpyz( localName, name, MAX_QPATH ); + + ext = COM_GetExtension( localName ); + + if( *ext ) + { + // Look for the correct loader and use it + for( i = 0; i < numModelLoaders; i++ ) + { + if( !Q_stricmp( ext, modelLoaders[ i ].ext ) ) + { + // Load + hModel = modelLoaders[ i ].ModelLoader( localName, mod ); + break; + } + } + + // A loader was found + if( i < numModelLoaders ) + { + if( !hModel ) + { + // Loader failed, most likely because the file isn't there; + // try again without the extension + orgNameFailed = qtrue; + orgLoader = i; + COM_StripExtension( name, localName, MAX_QPATH ); + } + else + { + // Something loaded + return mod->index; + } + } + } + + // Try and find a suitable match using all + // the model formats supported + for( i = 0; i < numModelLoaders; i++ ) + { + if (i == orgLoader) + continue; + + Com_sprintf( altName, sizeof (altName), "%s.%s", localName, modelLoaders[ i ].ext ); + + // Load + hModel = modelLoaders[ i ].ModelLoader( altName, mod ); + + if( hModel ) + { + if( orgNameFailed ) + { + ri.Printf( PRINT_DEVELOPER, "WARNING: %s not present, using %s instead\n", + name, altName ); + } + + break; + } + } + + return hModel; +} + +/* +================= +R_LoadMD3 +================= +*/ +static qboolean R_LoadMD3 (model_t *mod, int lod, void *buffer, const char *mod_name ) { + int i, j; + md3Header_t *pinmodel; + md3Frame_t *frame; + md3Surface_t *surf; + md3Shader_t *shader; + md3Triangle_t *tri; + md3St_t *st; + md3XyzNormal_t *xyz; + md3Tag_t *tag; + int version; + int size; + + pinmodel = (md3Header_t *)buffer; + + version = LittleLong (pinmodel->version); + if (version != MD3_VERSION) { + ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has wrong version (%i should be %i)\n", + mod_name, version, MD3_VERSION); + return qfalse; + } + + mod->type = MOD_MESH; + size = LittleLong(pinmodel->ofsEnd); + mod->dataSize += size; + mod->md3[lod] = ri.Hunk_Alloc( size, h_low ); + + Com_Memcpy (mod->md3[lod], buffer, LittleLong(pinmodel->ofsEnd) ); + + LL(mod->md3[lod]->ident); + LL(mod->md3[lod]->version); + LL(mod->md3[lod]->numFrames); + LL(mod->md3[lod]->numTags); + LL(mod->md3[lod]->numSurfaces); + LL(mod->md3[lod]->ofsFrames); + LL(mod->md3[lod]->ofsTags); + LL(mod->md3[lod]->ofsSurfaces); + LL(mod->md3[lod]->ofsEnd); + + if ( mod->md3[lod]->numFrames < 1 ) { + ri.Printf( PRINT_WARNING, "R_LoadMD3: %s has no frames\n", mod_name ); + return qfalse; + } + + // swap all the frames + frame = (md3Frame_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsFrames ); + for ( i = 0 ; i < mod->md3[lod]->numFrames ; i++, frame++) { + frame->radius = LittleFloat( frame->radius ); + for ( j = 0 ; j < 3 ; j++ ) { + frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] ); + frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] ); + frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] ); + } + } + + // swap all the tags + tag = (md3Tag_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsTags ); + for ( i = 0 ; i < mod->md3[lod]->numTags * mod->md3[lod]->numFrames ; i++, tag++) { + for ( j = 0 ; j < 3 ; j++ ) { + tag->origin[j] = LittleFloat( tag->origin[j] ); + tag->axis[0][j] = LittleFloat( tag->axis[0][j] ); + tag->axis[1][j] = LittleFloat( tag->axis[1][j] ); + tag->axis[2][j] = LittleFloat( tag->axis[2][j] ); + } + } + + // swap all the surfaces + surf = (md3Surface_t *) ( (byte *)mod->md3[lod] + mod->md3[lod]->ofsSurfaces ); + for ( i = 0 ; i < mod->md3[lod]->numSurfaces ; i++) { + + LL(surf->ident); + LL(surf->flags); + LL(surf->numFrames); + LL(surf->numShaders); + LL(surf->numTriangles); + LL(surf->ofsTriangles); + LL(surf->numVerts); + LL(surf->ofsShaders); + LL(surf->ofsSt); + LL(surf->ofsXyzNormals); + LL(surf->ofsEnd); + + if ( surf->numVerts > SHADER_MAX_VERTEXES ) { + ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has more than %i verts on a surface (%i).\n", + mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); + return qfalse; + } + if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) { + ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has more than %i triangles on a surface (%i).\n", + mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); + return qfalse; + } + + // change to surface identifier + surf->ident = SF_MD3; + + // lowercase the surface name so skin compares are faster + Q_strlwr( surf->name ); + + // strip off a trailing _1 or _2 + // this is a crutch for q3data being a mess + j = strlen( surf->name ); + if ( j > 2 && surf->name[j-2] == '_' ) { + surf->name[j-2] = 0; + } + + // register the shaders + shader = (md3Shader_t *) ( (byte *)surf + surf->ofsShaders ); + for ( j = 0 ; j < surf->numShaders ; j++, shader++ ) { + shader_t *sh; + + sh = R_FindShader( shader->name, LIGHTMAP_NONE, qtrue ); + if ( sh->defaultShader ) { + shader->shaderIndex = 0; + } else { + shader->shaderIndex = sh->index; + } + } + + // swap all the triangles + tri = (md3Triangle_t *) ( (byte *)surf + surf->ofsTriangles ); + for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) { + LL(tri->indexes[0]); + LL(tri->indexes[1]); + LL(tri->indexes[2]); + } + + // swap all the ST + st = (md3St_t *) ( (byte *)surf + surf->ofsSt ); + for ( j = 0 ; j < surf->numVerts ; j++, st++ ) { + st->st[0] = LittleFloat( st->st[0] ); + st->st[1] = LittleFloat( st->st[1] ); + } + + // swap all the XyzNormals + xyz = (md3XyzNormal_t *) ( (byte *)surf + surf->ofsXyzNormals ); + for ( j = 0 ; j < surf->numVerts * surf->numFrames ; j++, xyz++ ) + { + xyz->xyz[0] = LittleShort( xyz->xyz[0] ); + xyz->xyz[1] = LittleShort( xyz->xyz[1] ); + xyz->xyz[2] = LittleShort( xyz->xyz[2] ); + + xyz->normal = LittleShort( xyz->normal ); + } + + + // find the next surface + surf = (md3Surface_t *)( (byte *)surf + surf->ofsEnd ); + } + + return qtrue; +} + + +#ifdef RAVENMD4 + +/* +================= +R_LoadMDR +================= +*/ +static qboolean R_LoadMDR( model_t *mod, void *buffer, int filesize, const char *mod_name ) +{ + int i, j, k, l; + mdrHeader_t *pinmodel, *mdr; + mdrFrame_t *frame; + mdrLOD_t *lod, *curlod; + mdrSurface_t *surf, *cursurf; + mdrTriangle_t *tri, *curtri; + mdrVertex_t *v, *curv; + mdrWeight_t *weight, *curweight; + mdrTag_t *tag, *curtag; + int size; + shader_t *sh; + + pinmodel = (mdrHeader_t *)buffer; + + pinmodel->version = LittleLong(pinmodel->version); + if (pinmodel->version != MDR_VERSION) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has wrong version (%i should be %i)\n", mod_name, pinmodel->version, MDR_VERSION); + return qfalse; + } + + size = LittleLong(pinmodel->ofsEnd); + + if(size > filesize) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: Header of %s is broken. Wrong filesize declared!\n", mod_name); + return qfalse; + } + + mod->type = MOD_MDR; + + LL(pinmodel->numFrames); + LL(pinmodel->numBones); + LL(pinmodel->ofsFrames); + + // This is a model that uses some type of compressed Bones. We don't want to uncompress every bone for each rendered frame + // over and over again, we'll uncompress it in this function already, so we must adjust the size of the target md4. + if(pinmodel->ofsFrames < 0) + { + // mdrFrame_t is larger than mdrCompFrame_t: + size += pinmodel->numFrames * sizeof(frame->name); + // now add enough space for the uncompressed bones. + size += pinmodel->numFrames * pinmodel->numBones * ((sizeof(mdrBone_t) - sizeof(mdrCompBone_t))); + } + + // simple bounds check + if(pinmodel->numBones < 0 || + sizeof(*mdr) + pinmodel->numFrames * (sizeof(*frame) + (pinmodel->numBones - 1) * sizeof(*frame->bones)) > size) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); + return qfalse; + } + + mod->dataSize += size; + mod->modelData = mdr = ri.Hunk_Alloc( size, h_low ); + + // Copy all the values over from the file and fix endian issues in the process, if necessary. + + mdr->ident = LittleLong(pinmodel->ident); + mdr->version = pinmodel->version; // Don't need to swap byte order on this one, we already did above. + Q_strncpyz(mdr->name, pinmodel->name, sizeof(mdr->name)); + mdr->numFrames = pinmodel->numFrames; + mdr->numBones = pinmodel->numBones; + mdr->numLODs = LittleLong(pinmodel->numLODs); + mdr->numTags = LittleLong(pinmodel->numTags); + // We don't care about the other offset values, we'll generate them ourselves while loading. + + mod->numLods = mdr->numLODs; + + if ( mdr->numFrames < 1 ) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has no frames\n", mod_name); + return qfalse; + } + + /* The first frame will be put into the first free space after the header */ + frame = (mdrFrame_t *)(mdr + 1); + mdr->ofsFrames = (int)((byte *) frame - (byte *) mdr); + + if (pinmodel->ofsFrames < 0) + { + mdrCompFrame_t *cframe; + + // compressed model... + cframe = (mdrCompFrame_t *)((byte *) pinmodel - pinmodel->ofsFrames); + + for(i = 0; i < mdr->numFrames; i++) + { + for(j = 0; j < 3; j++) + { + frame->bounds[0][j] = LittleFloat(cframe->bounds[0][j]); + frame->bounds[1][j] = LittleFloat(cframe->bounds[1][j]); + frame->localOrigin[j] = LittleFloat(cframe->localOrigin[j]); + } + + frame->radius = LittleFloat(cframe->radius); + frame->name[0] = '\0'; // No name supplied in the compressed version. + + for(j = 0; j < mdr->numBones; j++) + { + for(k = 0; k < (sizeof(cframe->bones[j].Comp) / 2); k++) + { + // Do swapping for the uncompressing functions. They seem to use shorts + // values only, so I assume this will work. Never tested it on other + // platforms, though. + + ((unsigned short *)(cframe->bones[j].Comp))[k] = + LittleShort( ((unsigned short *)(cframe->bones[j].Comp))[k] ); + } + + /* Now do the actual uncompressing */ + MC_UnCompress(frame->bones[j].matrix, cframe->bones[j].Comp); + } + + // Next Frame... + cframe = (mdrCompFrame_t *) &cframe->bones[j]; + frame = (mdrFrame_t *) &frame->bones[j]; + } + } + else + { + mdrFrame_t *curframe; + + // uncompressed model... + // + + curframe = (mdrFrame_t *)((byte *) pinmodel + pinmodel->ofsFrames); + + // swap all the frames + for ( i = 0 ; i < mdr->numFrames ; i++) + { + for(j = 0; j < 3; j++) + { + frame->bounds[0][j] = LittleFloat(curframe->bounds[0][j]); + frame->bounds[1][j] = LittleFloat(curframe->bounds[1][j]); + frame->localOrigin[j] = LittleFloat(curframe->localOrigin[j]); + } + + frame->radius = LittleFloat(curframe->radius); + Q_strncpyz(frame->name, curframe->name, sizeof(frame->name)); + + for (j = 0; j < (int) (mdr->numBones * sizeof(mdrBone_t) / 4); j++) + { + ((float *)frame->bones)[j] = LittleFloat( ((float *)curframe->bones)[j] ); + } + + curframe = (mdrFrame_t *) &curframe->bones[mdr->numBones]; + frame = (mdrFrame_t *) &frame->bones[mdr->numBones]; + } + } + + // frame should now point to the first free address after all frames. + lod = (mdrLOD_t *) frame; + mdr->ofsLODs = (int) ((byte *) lod - (byte *)mdr); + + curlod = (mdrLOD_t *)((byte *) pinmodel + LittleLong(pinmodel->ofsLODs)); + + // swap all the LOD's + for ( l = 0 ; l < mdr->numLODs ; l++) + { + // simple bounds check + if((byte *) (lod + 1) > (byte *) mdr + size) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); + return qfalse; + } + + lod->numSurfaces = LittleLong(curlod->numSurfaces); + + // swap all the surfaces + surf = (mdrSurface_t *) (lod + 1); + lod->ofsSurfaces = (int)((byte *) surf - (byte *) lod); + cursurf = (mdrSurface_t *) ((byte *)curlod + LittleLong(curlod->ofsSurfaces)); + + for ( i = 0 ; i < lod->numSurfaces ; i++) + { + // simple bounds check + if((byte *) (surf + 1) > (byte *) mdr + size) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); + return qfalse; + } + + // first do some copying stuff + + surf->ident = SF_MDR; + Q_strncpyz(surf->name, cursurf->name, sizeof(surf->name)); + Q_strncpyz(surf->shader, cursurf->shader, sizeof(surf->shader)); + + surf->ofsHeader = (byte *) mdr - (byte *) surf; + + surf->numVerts = LittleLong(cursurf->numVerts); + surf->numTriangles = LittleLong(cursurf->numTriangles); + // numBoneReferences and BoneReferences generally seem to be unused + + // now do the checks that may fail. + if ( surf->numVerts > SHADER_MAX_VERTEXES ) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has more than %i verts on a surface (%i).\n", + mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); + return qfalse; + } + if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has more than %i triangles on a surface (%i).\n", + mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); + return qfalse; + } + // lowercase the surface name so skin compares are faster + Q_strlwr( surf->name ); + + // register the shaders + sh = R_FindShader(surf->shader, LIGHTMAP_NONE, qtrue); + if ( sh->defaultShader ) { + surf->shaderIndex = 0; + } else { + surf->shaderIndex = sh->index; + } + + // now copy the vertexes. + v = (mdrVertex_t *) (surf + 1); + surf->ofsVerts = (int)((byte *) v - (byte *) surf); + curv = (mdrVertex_t *) ((byte *)cursurf + LittleLong(cursurf->ofsVerts)); + + for(j = 0; j < surf->numVerts; j++) + { + LL(curv->numWeights); + + // simple bounds check + if(curv->numWeights < 0 || (byte *) (v + 1) + (curv->numWeights - 1) * sizeof(*weight) > (byte *) mdr + size) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); + return qfalse; + } + + v->normal[0] = LittleFloat(curv->normal[0]); + v->normal[1] = LittleFloat(curv->normal[1]); + v->normal[2] = LittleFloat(curv->normal[2]); + + v->texCoords[0] = LittleFloat(curv->texCoords[0]); + v->texCoords[1] = LittleFloat(curv->texCoords[1]); + + v->numWeights = curv->numWeights; + weight = &v->weights[0]; + curweight = &curv->weights[0]; + + // Now copy all the weights + for(k = 0; k < v->numWeights; k++) + { + weight->boneIndex = LittleLong(curweight->boneIndex); + weight->boneWeight = LittleFloat(curweight->boneWeight); + + weight->offset[0] = LittleFloat(curweight->offset[0]); + weight->offset[1] = LittleFloat(curweight->offset[1]); + weight->offset[2] = LittleFloat(curweight->offset[2]); + + weight++; + curweight++; + } + + v = (mdrVertex_t *) weight; + curv = (mdrVertex_t *) curweight; + } + + // we know the offset to the triangles now: + tri = (mdrTriangle_t *) v; + surf->ofsTriangles = (int)((byte *) tri - (byte *) surf); + curtri = (mdrTriangle_t *)((byte *) cursurf + LittleLong(cursurf->ofsTriangles)); + + // simple bounds check + if(surf->numTriangles < 0 || (byte *) (tri + surf->numTriangles) > (byte *) mdr + size) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); + return qfalse; + } + + for(j = 0; j < surf->numTriangles; j++) + { + tri->indexes[0] = LittleLong(curtri->indexes[0]); + tri->indexes[1] = LittleLong(curtri->indexes[1]); + tri->indexes[2] = LittleLong(curtri->indexes[2]); + + tri++; + curtri++; + } + + // tri now points to the end of the surface. + surf->ofsEnd = (byte *) tri - (byte *) surf; + surf = (mdrSurface_t *) tri; + + // find the next surface. + cursurf = (mdrSurface_t *) ((byte *) cursurf + LittleLong(cursurf->ofsEnd)); + } + + // surf points to the next lod now. + lod->ofsEnd = (int)((byte *) surf - (byte *) lod); + lod = (mdrLOD_t *) surf; + + // find the next LOD. + curlod = (mdrLOD_t *)((byte *) curlod + LittleLong(curlod->ofsEnd)); + } + + // lod points to the first tag now, so update the offset too. + tag = (mdrTag_t *) lod; + mdr->ofsTags = (int)((byte *) tag - (byte *) mdr); + curtag = (mdrTag_t *) ((byte *)pinmodel + LittleLong(pinmodel->ofsTags)); + + // simple bounds check + if(mdr->numTags < 0 || (byte *) (tag + mdr->numTags) > (byte *) mdr + size) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); + return qfalse; + } + + for (i = 0 ; i < mdr->numTags ; i++) + { + tag->boneIndex = LittleLong(curtag->boneIndex); + Q_strncpyz(tag->name, curtag->name, sizeof(tag->name)); + + tag++; + curtag++; + } + + // And finally we know the real offset to the end. + mdr->ofsEnd = (int)((byte *) tag - (byte *) mdr); + + // phew! we're done. + + return qtrue; +} +#endif + +/* +================= +R_LoadMD4 +================= +*/ + +static qboolean R_LoadMD4( model_t *mod, void *buffer, const char *mod_name ) { + int i, j, k, lodindex; + md4Header_t *pinmodel, *md4; + md4Frame_t *frame; + md4LOD_t *lod; + md4Surface_t *surf; + md4Triangle_t *tri; + md4Vertex_t *v; + int version; + int size; + shader_t *sh; + int frameSize; + + pinmodel = (md4Header_t *)buffer; + + version = LittleLong (pinmodel->version); + if (version != MD4_VERSION) { + ri.Printf( PRINT_WARNING, "R_LoadMD4: %s has wrong version (%i should be %i)\n", + mod_name, version, MD4_VERSION); + return qfalse; + } + + mod->type = MOD_MD4; + size = LittleLong(pinmodel->ofsEnd); + mod->dataSize += size; + mod->modelData = md4 = ri.Hunk_Alloc( size, h_low ); + + Com_Memcpy(md4, buffer, size); + + LL(md4->ident); + LL(md4->version); + LL(md4->numFrames); + LL(md4->numBones); + LL(md4->numLODs); + LL(md4->ofsFrames); + LL(md4->ofsLODs); + md4->ofsEnd = size; + + if ( md4->numFrames < 1 ) { + ri.Printf( PRINT_WARNING, "R_LoadMD4: %s has no frames\n", mod_name ); + return qfalse; + } + + // we don't need to swap tags in the renderer, they aren't used + + // swap all the frames + frameSize = (size_t)( &((md4Frame_t *)0)->bones[ md4->numBones ] ); + for ( i = 0 ; i < md4->numFrames ; i++) { + frame = (md4Frame_t *) ( (byte *)md4 + md4->ofsFrames + i * frameSize ); + frame->radius = LittleFloat( frame->radius ); + for ( j = 0 ; j < 3 ; j++ ) { + frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] ); + frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] ); + frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] ); + } + for ( j = 0 ; j < md4->numBones * sizeof( md4Bone_t ) / 4 ; j++ ) { + ((float *)frame->bones)[j] = LittleFloat( ((float *)frame->bones)[j] ); + } + } + + // swap all the LOD's + lod = (md4LOD_t *) ( (byte *)md4 + md4->ofsLODs ); + for ( lodindex = 0 ; lodindex < md4->numLODs ; lodindex++ ) { + + // swap all the surfaces + surf = (md4Surface_t *) ( (byte *)lod + lod->ofsSurfaces ); + for ( i = 0 ; i < lod->numSurfaces ; i++) { + LL(surf->ident); + LL(surf->numTriangles); + LL(surf->ofsTriangles); + LL(surf->numVerts); + LL(surf->ofsVerts); + LL(surf->ofsEnd); + + if ( surf->numVerts > SHADER_MAX_VERTEXES ) { + ri.Printf(PRINT_WARNING, "R_LoadMD4: %s has more than %i verts on a surface (%i).\n", + mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); + return qfalse; + } + if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) { + ri.Printf(PRINT_WARNING, "R_LoadMD4: %s has more than %i triangles on a surface (%i).\n", + mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); + return qfalse; + } + + // change to surface identifier + surf->ident = SF_MD4; + + // lowercase the surface name so skin compares are faster + Q_strlwr( surf->name ); + + // register the shaders + sh = R_FindShader( surf->shader, LIGHTMAP_NONE, qtrue ); + if ( sh->defaultShader ) { + surf->shaderIndex = 0; + } else { + surf->shaderIndex = sh->index; + } + + // swap all the triangles + tri = (md4Triangle_t *) ( (byte *)surf + surf->ofsTriangles ); + for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) { + LL(tri->indexes[0]); + LL(tri->indexes[1]); + LL(tri->indexes[2]); + } + + // swap all the vertexes + // FIXME + // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left + // in for reference. + //v = (md4Vertex_t *) ( (byte *)surf + surf->ofsVerts + 12); + v = (md4Vertex_t *) ( (byte *)surf + surf->ofsVerts); + for ( j = 0 ; j < surf->numVerts ; j++ ) { + v->normal[0] = LittleFloat( v->normal[0] ); + v->normal[1] = LittleFloat( v->normal[1] ); + v->normal[2] = LittleFloat( v->normal[2] ); + + v->texCoords[0] = LittleFloat( v->texCoords[0] ); + v->texCoords[1] = LittleFloat( v->texCoords[1] ); + + v->numWeights = LittleLong( v->numWeights ); + + for ( k = 0 ; k < v->numWeights ; k++ ) { + v->weights[k].boneIndex = LittleLong( v->weights[k].boneIndex ); + v->weights[k].boneWeight = LittleFloat( v->weights[k].boneWeight ); + v->weights[k].offset[0] = LittleFloat( v->weights[k].offset[0] ); + v->weights[k].offset[1] = LittleFloat( v->weights[k].offset[1] ); + v->weights[k].offset[2] = LittleFloat( v->weights[k].offset[2] ); + } + // FIXME + // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left + // in for reference. + //v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights] + 12 ); + v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights]); + } + + // find the next surface + surf = (md4Surface_t *)( (byte *)surf + surf->ofsEnd ); + } + + // find the next LOD + lod = (md4LOD_t *)( (byte *)lod + lod->ofsEnd ); + } + + return qtrue; +} + + + +//============================================================================= + +/* +** RE_BeginRegistration +*/ +void RE_BeginRegistration( glconfig_t *glconfigOut ) { + + R_Init(); + + *glconfigOut = glConfig; + + R_IssuePendingRenderCommands(); + + tr.viewCluster = -1; // force markleafs to regenerate + R_ClearFlares(); + RE_ClearScene(); + + tr.registered = qtrue; + + // NOTE: this sucks, for some reason the first stretch pic is never drawn + // without this we'd see a white flash on a level load because the very + // first time the level shot would not be drawn +// RE_StretchPic(0, 0, 0, 0, 0, 0, 1, 1, 0); +} + +//============================================================================= + +/* +=============== +R_ModelInit +=============== +*/ +void R_ModelInit( void ) { + model_t *mod; + + // leave a space for NULL model + tr.numModels = 0; + + mod = R_AllocModel(); + mod->type = MOD_BAD; +} + + +/* +================ +R_Modellist_f +================ +*/ +void R_Modellist_f( void ) { + int i, j; + model_t *mod; + int total; + int lods; + + total = 0; + for ( i = 1 ; i < tr.numModels; i++ ) { + mod = tr.models[i]; + lods = 1; + for ( j = 1 ; j < MD3_MAX_LODS ; j++ ) { + if ( mod->md3[j] && mod->md3[j] != mod->md3[j-1] ) { + lods++; + } + } + ri.Printf( PRINT_ALL, "%8i : (%i) %s\n",mod->dataSize, lods, mod->name ); + total += mod->dataSize; + } + ri.Printf( PRINT_ALL, "%8i : Total models\n", total ); + +#if 0 // not working right with new hunk + if ( tr.world ) { + ri.Printf( PRINT_ALL, "\n%8i : %s\n", tr.world->dataSize, tr.world->name ); + } +#endif +} + + +//============================================================================= + + +/* +================ +R_GetTag +================ +*/ +static md3Tag_t *R_GetTag( md3Header_t *mod, int frame, const char *tagName ) { + md3Tag_t *tag; + int i; + + if ( frame >= mod->numFrames ) { + // it is possible to have a bad frame while changing models, so don't error + frame = mod->numFrames - 1; + } + + tag = (md3Tag_t *)((byte *)mod + mod->ofsTags) + frame * mod->numTags; + for ( i = 0 ; i < mod->numTags ; i++, tag++ ) { + if ( !strcmp( tag->name, tagName ) ) { + return tag; // found it + } + } + + return NULL; +} + +#ifdef RAVENMD4 +void R_GetAnimTag( mdrHeader_t *mod, int framenum, const char *tagName, md3Tag_t * dest) +{ + int i, j, k; + int frameSize; + mdrFrame_t *frame; + mdrTag_t *tag; + + if ( framenum >= mod->numFrames ) + { + // it is possible to have a bad frame while changing models, so don't error + framenum = mod->numFrames - 1; + } + + tag = (mdrTag_t *)((byte *)mod + mod->ofsTags); + for ( i = 0 ; i < mod->numTags ; i++, tag++ ) + { + if ( !strcmp( tag->name, tagName ) ) + { + Q_strncpyz(dest->name, tag->name, sizeof(dest->name)); + + // uncompressed model... + // + frameSize = (intptr_t)( &((mdrFrame_t *)0)->bones[ mod->numBones ] ); + frame = (mdrFrame_t *)((byte *)mod + mod->ofsFrames + framenum * frameSize ); + + for (j = 0; j < 3; j++) + { + for (k = 0; k < 3; k++) + dest->axis[j][k]=frame->bones[tag->boneIndex].matrix[k][j]; + } + + dest->origin[0]=frame->bones[tag->boneIndex].matrix[0][3]; + dest->origin[1]=frame->bones[tag->boneIndex].matrix[1][3]; + dest->origin[2]=frame->bones[tag->boneIndex].matrix[2][3]; + + return; + } + } + + AxisClear( dest->axis ); + VectorClear( dest->origin ); + strcpy(dest->name,""); +} +#endif + +/* +================ +R_LerpTag +================ +*/ +int R_LerpTag( orientation_t *tag, qhandle_t handle, int startFrame, int endFrame, + float frac, const char *tagName ) { + md3Tag_t *start, *end; +#ifdef RAVENMD4 + md3Tag_t start_space, end_space; +#endif + int i; + float frontLerp, backLerp; + model_t *model; + + model = R_GetModelByHandle( handle ); + if ( !model->md3[0] ) + { +#ifdef RAVENMD4 + if(model->type == MOD_MDR) + { + start = &start_space; + end = &end_space; + R_GetAnimTag((mdrHeader_t *) model->modelData, startFrame, tagName, start); + R_GetAnimTag((mdrHeader_t *) model->modelData, endFrame, tagName, end); + } + else +#endif + if( model->type == MOD_IQM ) { + return R_IQMLerpTag( tag, model->modelData, + startFrame, endFrame, + frac, tagName ); + } else { + + AxisClear( tag->axis ); + VectorClear( tag->origin ); + return qfalse; + + } + } + else + { + start = R_GetTag( model->md3[0], startFrame, tagName ); + end = R_GetTag( model->md3[0], endFrame, tagName ); + if ( !start || !end ) { + AxisClear( tag->axis ); + VectorClear( tag->origin ); + return qfalse; + } + } + + frontLerp = frac; + backLerp = 1.0f - frac; + + for ( i = 0 ; i < 3 ; i++ ) { + tag->origin[i] = start->origin[i] * backLerp + end->origin[i] * frontLerp; + tag->axis[0][i] = start->axis[0][i] * backLerp + end->axis[0][i] * frontLerp; + tag->axis[1][i] = start->axis[1][i] * backLerp + end->axis[1][i] * frontLerp; + tag->axis[2][i] = start->axis[2][i] * backLerp + end->axis[2][i] * frontLerp; + } + VectorNormalize( tag->axis[0] ); + VectorNormalize( tag->axis[1] ); + VectorNormalize( tag->axis[2] ); + return qtrue; +} + + +/* +==================== +R_ModelBounds +==================== +*/ +void R_ModelBounds( qhandle_t handle, vec3_t mins, vec3_t maxs ) { + model_t *model; + + model = R_GetModelByHandle( handle ); + + if(model->type == MOD_BRUSH) { + VectorCopy( model->bmodel->bounds[0], mins ); + VectorCopy( model->bmodel->bounds[1], maxs ); + + return; + } else if (model->type == MOD_MESH) { + md3Header_t *header; + md3Frame_t *frame; + + header = model->md3[0]; + frame = (md3Frame_t *) ((byte *)header + header->ofsFrames); + + VectorCopy( frame->bounds[0], mins ); + VectorCopy( frame->bounds[1], maxs ); + + return; + } else if (model->type == MOD_MD4) { + md4Header_t *header; + md4Frame_t *frame; + + header = (md4Header_t *)model->modelData; + frame = (md4Frame_t *) ((byte *)header + header->ofsFrames); + + VectorCopy( frame->bounds[0], mins ); + VectorCopy( frame->bounds[1], maxs ); + + return; +#ifdef RAVENMD4 + } else if (model->type == MOD_MDR) { + mdrHeader_t *header; + mdrFrame_t *frame; + + header = (mdrHeader_t *)model->modelData; + frame = (mdrFrame_t *) ((byte *)header + header->ofsFrames); + + VectorCopy( frame->bounds[0], mins ); + VectorCopy( frame->bounds[1], maxs ); + + return; +#endif + } else if(model->type == MOD_IQM) { + iqmData_t *iqmData; + + iqmData = model->modelData; + + if(iqmData->bounds) + { + VectorCopy(iqmData->bounds, mins); + VectorCopy(iqmData->bounds + 3, maxs); + return; + } + } + + VectorClear( mins ); + VectorClear( maxs ); +} diff --git a/src/renderergl1/tr_model_iqm.c b/src/renderergl1/tr_model_iqm.c new file mode 100644 index 00000000..98517d55 --- /dev/null +++ b/src/renderergl1/tr_model_iqm.c @@ -0,0 +1,1058 @@ +/* +=========================================================================== +Copyright (C) 2011 Thilo Schulz +Copyright (C) 2011 Matthias Bentrup + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "tr_local.h" + +#define LL(x) x=LittleLong(x) + +static qboolean IQM_CheckRange( iqmHeader_t *header, int offset, + int count,int size ) { + // return true if the range specified by offset, count and size + // doesn't fit into the file + return ( count <= 0 || + offset < 0 || + offset > header->filesize || + offset + count * size < 0 || + offset + count * size > header->filesize ); +} +// "multiply" 3x4 matrices, these are assumed to be the top 3 rows +// of a 4x4 matrix with the last row = (0 0 0 1) +static void Matrix34Multiply( float *a, float *b, float *out ) { + out[ 0] = a[0] * b[0] + a[1] * b[4] + a[ 2] * b[ 8]; + out[ 1] = a[0] * b[1] + a[1] * b[5] + a[ 2] * b[ 9]; + out[ 2] = a[0] * b[2] + a[1] * b[6] + a[ 2] * b[10]; + out[ 3] = a[0] * b[3] + a[1] * b[7] + a[ 2] * b[11] + a[ 3]; + out[ 4] = a[4] * b[0] + a[5] * b[4] + a[ 6] * b[ 8]; + out[ 5] = a[4] * b[1] + a[5] * b[5] + a[ 6] * b[ 9]; + out[ 6] = a[4] * b[2] + a[5] * b[6] + a[ 6] * b[10]; + out[ 7] = a[4] * b[3] + a[5] * b[7] + a[ 6] * b[11] + a[ 7]; + out[ 8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[ 8]; + out[ 9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[ 9]; + out[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10]; + out[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11]; +} +static void InterpolateMatrix( float *a, float *b, float lerp, float *mat ) { + float unLerp = 1.0f - lerp; + + mat[ 0] = a[ 0] * unLerp + b[ 0] * lerp; + mat[ 1] = a[ 1] * unLerp + b[ 1] * lerp; + mat[ 2] = a[ 2] * unLerp + b[ 2] * lerp; + mat[ 3] = a[ 3] * unLerp + b[ 3] * lerp; + mat[ 4] = a[ 4] * unLerp + b[ 4] * lerp; + mat[ 5] = a[ 5] * unLerp + b[ 5] * lerp; + mat[ 6] = a[ 6] * unLerp + b[ 6] * lerp; + mat[ 7] = a[ 7] * unLerp + b[ 7] * lerp; + mat[ 8] = a[ 8] * unLerp + b[ 8] * lerp; + mat[ 9] = a[ 9] * unLerp + b[ 9] * lerp; + mat[10] = a[10] * unLerp + b[10] * lerp; + mat[11] = a[11] * unLerp + b[11] * lerp; +} +static void JointToMatrix( vec4_t rot, vec3_t scale, vec3_t trans, + float *mat ) { + float xx = 2.0f * rot[0] * rot[0]; + float yy = 2.0f * rot[1] * rot[1]; + float zz = 2.0f * rot[2] * rot[2]; + float xy = 2.0f * rot[0] * rot[1]; + float xz = 2.0f * rot[0] * rot[2]; + float yz = 2.0f * rot[1] * rot[2]; + float wx = 2.0f * rot[3] * rot[0]; + float wy = 2.0f * rot[3] * rot[1]; + float wz = 2.0f * rot[3] * rot[2]; + + mat[ 0] = scale[0] * (1.0f - (yy + zz)); + mat[ 1] = scale[0] * (xy - wz); + mat[ 2] = scale[0] * (xz + wy); + mat[ 3] = trans[0]; + mat[ 4] = scale[1] * (xy + wz); + mat[ 5] = scale[1] * (1.0f - (xx + zz)); + mat[ 6] = scale[1] * (yz - wx); + mat[ 7] = trans[1]; + mat[ 8] = scale[2] * (xz - wy); + mat[ 9] = scale[2] * (yz + wx); + mat[10] = scale[2] * (1.0f - (xx + yy)); + mat[11] = trans[2]; +} +static void Matrix34Invert( float *inMat, float *outMat ) +{ + vec3_t trans; + float invSqrLen, *v; + + outMat[ 0] = inMat[ 0]; outMat[ 1] = inMat[ 4]; outMat[ 2] = inMat[ 8]; + outMat[ 4] = inMat[ 1]; outMat[ 5] = inMat[ 5]; outMat[ 6] = inMat[ 9]; + outMat[ 8] = inMat[ 2]; outMat[ 9] = inMat[ 6]; outMat[10] = inMat[10]; + + v = outMat + 0; invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); + v = outMat + 4; invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); + v = outMat + 8; invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); + + trans[0] = inMat[ 3]; + trans[1] = inMat[ 7]; + trans[2] = inMat[11]; + + outMat[ 3] = -DotProduct(outMat + 0, trans); + outMat[ 7] = -DotProduct(outMat + 4, trans); + outMat[11] = -DotProduct(outMat + 8, trans); +} + +/* +================= +R_LoadIQM + +Load an IQM model and compute the joint matrices for every frame. +================= +*/ +qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_name ) { + iqmHeader_t *header; + iqmVertexArray_t *vertexarray; + iqmTriangle_t *triangle; + iqmMesh_t *mesh; + iqmJoint_t *joint; + iqmPose_t *pose; + iqmBounds_t *bounds; + unsigned short *framedata; + char *str; + int i, j; + float jointMats[IQM_MAX_JOINTS * 2 * 12]; + float *mat; + size_t size, joint_names; + iqmData_t *iqmData; + srfIQModel_t *surface; + + if( filesize < sizeof(iqmHeader_t) ) { + return qfalse; + } + + header = (iqmHeader_t *)buffer; + if( Q_strncmp( header->magic, IQM_MAGIC, sizeof(header->magic) ) ) { + return qfalse; + } + + LL( header->version ); + if( header->version != IQM_VERSION ) { + ri.Printf(PRINT_WARNING, "R_LoadIQM: %s is a unsupported IQM version (%d), only version %d is supported.\n", + mod_name, header->version, IQM_VERSION); + return qfalse; + } + + LL( header->filesize ); + if( header->filesize > filesize || header->filesize > 16<<20 ) { + return qfalse; + } + + LL( header->flags ); + LL( header->num_text ); + LL( header->ofs_text ); + LL( header->num_meshes ); + LL( header->ofs_meshes ); + LL( header->num_vertexarrays ); + LL( header->num_vertexes ); + LL( header->ofs_vertexarrays ); + LL( header->num_triangles ); + LL( header->ofs_triangles ); + LL( header->ofs_adjacency ); + LL( header->num_joints ); + LL( header->ofs_joints ); + LL( header->num_poses ); + LL( header->ofs_poses ); + LL( header->num_anims ); + LL( header->ofs_anims ); + LL( header->num_frames ); + LL( header->num_framechannels ); + LL( header->ofs_frames ); + LL( header->ofs_bounds ); + LL( header->num_comment ); + LL( header->ofs_comment ); + LL( header->num_extensions ); + LL( header->ofs_extensions ); + + // check ioq3 joint limit + if ( header->num_joints > IQM_MAX_JOINTS ) { + ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %d joints (%d).\n", + mod_name, IQM_MAX_JOINTS, header->num_joints); + return qfalse; + } + + // check and swap vertex arrays + if( IQM_CheckRange( header, header->ofs_vertexarrays, + header->num_vertexarrays, + sizeof(iqmVertexArray_t) ) ) { + return qfalse; + } + vertexarray = (iqmVertexArray_t *)((byte *)header + header->ofs_vertexarrays); + for( i = 0; i < header->num_vertexarrays; i++, vertexarray++ ) { + int j, n, *intPtr; + + if( vertexarray->size <= 0 || vertexarray->size > 4 ) { + return qfalse; + } + + // total number of values + n = header->num_vertexes * vertexarray->size; + + switch( vertexarray->format ) { + case IQM_BYTE: + case IQM_UBYTE: + // 1 byte, no swapping necessary + if( IQM_CheckRange( header, vertexarray->offset, + n, sizeof(byte) ) ) { + return qfalse; + } + break; + case IQM_INT: + case IQM_UINT: + case IQM_FLOAT: + // 4-byte swap + if( IQM_CheckRange( header, vertexarray->offset, + n, sizeof(float) ) ) { + return qfalse; + } + intPtr = (int *)((byte *)header + vertexarray->offset); + for( j = 0; j < n; j++, intPtr++ ) { + LL( *intPtr ); + } + break; + default: + // not supported + return qfalse; + break; + } + + switch( vertexarray->type ) { + case IQM_POSITION: + case IQM_NORMAL: + if( vertexarray->format != IQM_FLOAT || + vertexarray->size != 3 ) { + return qfalse; + } + break; + case IQM_TANGENT: + if( vertexarray->format != IQM_FLOAT || + vertexarray->size != 4 ) { + return qfalse; + } + break; + case IQM_TEXCOORD: + if( vertexarray->format != IQM_FLOAT || + vertexarray->size != 2 ) { + return qfalse; + } + break; + case IQM_BLENDINDEXES: + case IQM_BLENDWEIGHTS: + if( vertexarray->format != IQM_UBYTE || + vertexarray->size != 4 ) { + return qfalse; + } + break; + case IQM_COLOR: + if( vertexarray->format != IQM_UBYTE || + vertexarray->size != 4 ) { + return qfalse; + } + break; + } + } + + // check and swap triangles + if( IQM_CheckRange( header, header->ofs_triangles, + header->num_triangles, sizeof(iqmTriangle_t) ) ) { + return qfalse; + } + triangle = (iqmTriangle_t *)((byte *)header + header->ofs_triangles); + for( i = 0; i < header->num_triangles; i++, triangle++ ) { + LL( triangle->vertex[0] ); + LL( triangle->vertex[1] ); + LL( triangle->vertex[2] ); + + if( triangle->vertex[0] > header->num_vertexes || + triangle->vertex[1] > header->num_vertexes || + triangle->vertex[2] > header->num_vertexes ) { + return qfalse; + } + } + + // check and swap meshes + if( IQM_CheckRange( header, header->ofs_meshes, + header->num_meshes, sizeof(iqmMesh_t) ) ) { + return qfalse; + } + mesh = (iqmMesh_t *)((byte *)header + header->ofs_meshes); + for( i = 0; i < header->num_meshes; i++, mesh++) { + LL( mesh->name ); + LL( mesh->material ); + LL( mesh->first_vertex ); + LL( mesh->num_vertexes ); + LL( mesh->first_triangle ); + LL( mesh->num_triangles ); + + // check ioq3 limits + if ( mesh->num_vertexes > SHADER_MAX_VERTEXES ) + { + ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i verts on a surface (%i).\n", + mod_name, SHADER_MAX_VERTEXES, mesh->num_vertexes ); + return qfalse; + } + if ( mesh->num_triangles*3 > SHADER_MAX_INDEXES ) + { + ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i triangles on a surface (%i).\n", + mod_name, SHADER_MAX_INDEXES / 3, mesh->num_triangles ); + return qfalse; + } + + if( mesh->first_vertex >= header->num_vertexes || + mesh->first_vertex + mesh->num_vertexes > header->num_vertexes || + mesh->first_triangle >= header->num_triangles || + mesh->first_triangle + mesh->num_triangles > header->num_triangles || + mesh->name >= header->num_text || + mesh->material >= header->num_text ) { + return qfalse; + } + } + + // check and swap joints + if( IQM_CheckRange( header, header->ofs_joints, + header->num_joints, sizeof(iqmJoint_t) ) ) { + return qfalse; + } + joint = (iqmJoint_t *)((byte *)header + header->ofs_joints); + joint_names = 0; + for( i = 0; i < header->num_joints; i++, joint++ ) { + LL( joint->name ); + LL( joint->parent ); + LL( joint->translate[0] ); + LL( joint->translate[1] ); + LL( joint->translate[2] ); + LL( joint->rotate[0] ); + LL( joint->rotate[1] ); + LL( joint->rotate[2] ); + LL( joint->rotate[3] ); + LL( joint->scale[0] ); + LL( joint->scale[1] ); + LL( joint->scale[2] ); + + if( joint->parent < -1 || + joint->parent >= (int)header->num_joints || + joint->name >= (int)header->num_text ) { + return qfalse; + } + joint_names += strlen( (char *)header + header->ofs_text + + joint->name ) + 1; + } + + // check and swap poses + if( header->num_poses != header->num_joints ) { + return qfalse; + } + if( IQM_CheckRange( header, header->ofs_poses, + header->num_poses, sizeof(iqmPose_t) ) ) { + return qfalse; + } + pose = (iqmPose_t *)((byte *)header + header->ofs_poses); + for( i = 0; i < header->num_poses; i++, pose++ ) { + LL( pose->parent ); + LL( pose->mask ); + LL( pose->channeloffset[0] ); + LL( pose->channeloffset[1] ); + LL( pose->channeloffset[2] ); + LL( pose->channeloffset[3] ); + LL( pose->channeloffset[4] ); + LL( pose->channeloffset[5] ); + LL( pose->channeloffset[6] ); + LL( pose->channeloffset[7] ); + LL( pose->channeloffset[8] ); + LL( pose->channeloffset[9] ); + LL( pose->channelscale[0] ); + LL( pose->channelscale[1] ); + LL( pose->channelscale[2] ); + LL( pose->channelscale[3] ); + LL( pose->channelscale[4] ); + LL( pose->channelscale[5] ); + LL( pose->channelscale[6] ); + LL( pose->channelscale[7] ); + LL( pose->channelscale[8] ); + LL( pose->channelscale[9] ); + } + + if (header->ofs_bounds) + { + // check and swap model bounds + if(IQM_CheckRange(header, header->ofs_bounds, + header->num_frames, sizeof(*bounds))) + { + return qfalse; + } + bounds = (iqmBounds_t *) ((byte *) header + header->ofs_bounds); + for(i = 0; i < header->num_frames; i++) + { + LL(bounds->bbmin[0]); + LL(bounds->bbmin[1]); + LL(bounds->bbmin[2]); + LL(bounds->bbmax[0]); + LL(bounds->bbmax[1]); + LL(bounds->bbmax[2]); + + bounds++; + } + } + + // allocate the model and copy the data + size = sizeof(iqmData_t); + size += header->num_meshes * sizeof( srfIQModel_t ); + size += header->num_joints * header->num_frames * 12 * sizeof( float ); + if(header->ofs_bounds) + size += header->num_frames * 6 * sizeof(float); // model bounds + size += header->num_vertexes * 3 * sizeof(float); // positions + size += header->num_vertexes * 2 * sizeof(float); // texcoords + size += header->num_vertexes * 3 * sizeof(float); // normals + size += header->num_vertexes * 4 * sizeof(float); // tangents + size += header->num_vertexes * 4 * sizeof(byte); // blendIndexes + size += header->num_vertexes * 4 * sizeof(byte); // blendWeights + size += header->num_vertexes * 4 * sizeof(byte); // colors + size += header->num_joints * sizeof(int); // parents + size += header->num_triangles * 3 * sizeof(int); // triangles + size += joint_names; // joint names + + mod->type = MOD_IQM; + iqmData = (iqmData_t *)ri.Hunk_Alloc( size, h_low ); + mod->modelData = iqmData; + + // fill header + iqmData->num_vertexes = header->num_vertexes; + iqmData->num_triangles = header->num_triangles; + iqmData->num_frames = header->num_frames; + iqmData->num_surfaces = header->num_meshes; + iqmData->num_joints = header->num_joints; + iqmData->surfaces = (srfIQModel_t *)(iqmData + 1); + iqmData->poseMats = (float *) (iqmData->surfaces + iqmData->num_surfaces); + if(header->ofs_bounds) + { + iqmData->bounds = iqmData->poseMats + 12 * header->num_joints * header->num_frames; + iqmData->positions = iqmData->bounds + 6 * header->num_frames; + } + else + iqmData->positions = iqmData->poseMats + 12 * header->num_joints * header->num_frames; + iqmData->texcoords = iqmData->positions + 3 * header->num_vertexes; + iqmData->normals = iqmData->texcoords + 2 * header->num_vertexes; + iqmData->tangents = iqmData->normals + 3 * header->num_vertexes; + iqmData->blendIndexes = (byte *)(iqmData->tangents + 4 * header->num_vertexes); + iqmData->blendWeights = iqmData->blendIndexes + 4 * header->num_vertexes; + iqmData->colors = iqmData->blendWeights + 4 * header->num_vertexes; + iqmData->jointParents = (int *)(iqmData->colors + 4 * header->num_vertexes); + iqmData->triangles = iqmData->jointParents + header->num_joints; + iqmData->names = (char *)(iqmData->triangles + 3 * header->num_triangles); + + // calculate joint matrices and their inverses + // they are needed only until the pose matrices are calculated + mat = jointMats; + joint = (iqmJoint_t *)((byte *)header + header->ofs_joints); + for( i = 0; i < header->num_joints; i++, joint++ ) { + float baseFrame[12], invBaseFrame[12]; + + JointToMatrix( joint->rotate, joint->scale, joint->translate, baseFrame ); + Matrix34Invert( baseFrame, invBaseFrame ); + + if ( joint->parent >= 0 ) + { + Matrix34Multiply( jointMats + 2 * 12 * joint->parent, baseFrame, mat ); + mat += 12; + Matrix34Multiply( invBaseFrame, jointMats + 2 * 12 * joint->parent + 12, mat ); + mat += 12; + } + else + { + Com_Memcpy( mat, baseFrame, sizeof(baseFrame) ); + mat += 12; + Com_Memcpy( mat, invBaseFrame, sizeof(invBaseFrame) ); + mat += 12; + } + } + + // calculate pose matrices + framedata = (unsigned short *)((byte *)header + header->ofs_frames); + mat = iqmData->poseMats; + for( i = 0; i < header->num_frames; i++ ) { + pose = (iqmPose_t *)((byte *)header + header->ofs_poses); + for( j = 0; j < header->num_poses; j++, pose++ ) { + vec3_t translate; + vec4_t rotate; + vec3_t scale; + float mat1[12], mat2[12]; + + translate[0] = pose->channeloffset[0]; + if( pose->mask & 0x001) + translate[0] += *framedata++ * pose->channelscale[0]; + translate[1] = pose->channeloffset[1]; + if( pose->mask & 0x002) + translate[1] += *framedata++ * pose->channelscale[1]; + translate[2] = pose->channeloffset[2]; + if( pose->mask & 0x004) + translate[2] += *framedata++ * pose->channelscale[2]; + + rotate[0] = pose->channeloffset[3]; + if( pose->mask & 0x008) + rotate[0] += *framedata++ * pose->channelscale[3]; + rotate[1] = pose->channeloffset[4]; + if( pose->mask & 0x010) + rotate[1] += *framedata++ * pose->channelscale[4]; + rotate[2] = pose->channeloffset[5]; + if( pose->mask & 0x020) + rotate[2] += *framedata++ * pose->channelscale[5]; + rotate[3] = pose->channeloffset[6]; + if( pose->mask & 0x040) + rotate[3] += *framedata++ * pose->channelscale[6]; + + scale[0] = pose->channeloffset[7]; + if( pose->mask & 0x080) + scale[0] += *framedata++ * pose->channelscale[7]; + scale[1] = pose->channeloffset[8]; + if( pose->mask & 0x100) + scale[1] += *framedata++ * pose->channelscale[8]; + scale[2] = pose->channeloffset[9]; + if( pose->mask & 0x200) + scale[2] += *framedata++ * pose->channelscale[9]; + + // construct transformation matrix + JointToMatrix( rotate, scale, translate, mat1 ); + + if( pose->parent >= 0 ) { + Matrix34Multiply( jointMats + 12 * 2 * pose->parent, + mat1, mat2 ); + } else { + Com_Memcpy( mat2, mat1, sizeof(mat1) ); + } + + Matrix34Multiply( mat2, jointMats + 12 * (2 * j + 1), mat ); + mat += 12; + } + } + + // register shaders + // overwrite the material offset with the shader index + mesh = (iqmMesh_t *)((byte *)header + header->ofs_meshes); + surface = iqmData->surfaces; + str = (char *)header + header->ofs_text; + for( i = 0; i < header->num_meshes; i++, mesh++, surface++ ) { + surface->surfaceType = SF_IQM; + Q_strncpyz(surface->name, str + mesh->name, sizeof (surface->name)); + Q_strlwr(surface->name); // lowercase the surface name so skin compares are faster + surface->shader = R_FindShader( str + mesh->material, LIGHTMAP_NONE, qtrue ); + if( surface->shader->defaultShader ) + surface->shader = tr.defaultShader; + surface->data = iqmData; + surface->first_vertex = mesh->first_vertex; + surface->num_vertexes = mesh->num_vertexes; + surface->first_triangle = mesh->first_triangle; + surface->num_triangles = mesh->num_triangles; + } + + // copy vertexarrays and indexes + vertexarray = (iqmVertexArray_t *)((byte *)header + header->ofs_vertexarrays); + for( i = 0; i < header->num_vertexarrays; i++, vertexarray++ ) { + int n; + + // total number of values + n = header->num_vertexes * vertexarray->size; + + switch( vertexarray->type ) { + case IQM_POSITION: + Com_Memcpy( iqmData->positions, + (byte *)header + vertexarray->offset, + n * sizeof(float) ); + break; + case IQM_NORMAL: + Com_Memcpy( iqmData->normals, + (byte *)header + vertexarray->offset, + n * sizeof(float) ); + break; + case IQM_TANGENT: + Com_Memcpy( iqmData->tangents, + (byte *)header + vertexarray->offset, + n * sizeof(float) ); + break; + case IQM_TEXCOORD: + Com_Memcpy( iqmData->texcoords, + (byte *)header + vertexarray->offset, + n * sizeof(float) ); + break; + case IQM_BLENDINDEXES: + Com_Memcpy( iqmData->blendIndexes, + (byte *)header + vertexarray->offset, + n * sizeof(byte) ); + break; + case IQM_BLENDWEIGHTS: + Com_Memcpy( iqmData->blendWeights, + (byte *)header + vertexarray->offset, + n * sizeof(byte) ); + break; + case IQM_COLOR: + Com_Memcpy( iqmData->colors, + (byte *)header + vertexarray->offset, + n * sizeof(byte) ); + break; + } + } + + // copy joint parents + joint = (iqmJoint_t *)((byte *)header + header->ofs_joints); + for( i = 0; i < header->num_joints; i++, joint++ ) { + iqmData->jointParents[i] = joint->parent; + } + + // copy triangles + triangle = (iqmTriangle_t *)((byte *)header + header->ofs_triangles); + for( i = 0; i < header->num_triangles; i++, triangle++ ) { + iqmData->triangles[3*i+0] = triangle->vertex[0]; + iqmData->triangles[3*i+1] = triangle->vertex[1]; + iqmData->triangles[3*i+2] = triangle->vertex[2]; + } + + // copy joint names + str = iqmData->names; + joint = (iqmJoint_t *)((byte *)header + header->ofs_joints); + for( i = 0; i < header->num_joints; i++, joint++ ) { + char *name = (char *)header + header->ofs_text + + joint->name; + int len = strlen( name ) + 1; + Com_Memcpy( str, name, len ); + str += len; + } + + // copy model bounds + if(header->ofs_bounds) + { + mat = iqmData->bounds; + bounds = (iqmBounds_t *) ((byte *) header + header->ofs_bounds); + for(i = 0; i < header->num_frames; i++) + { + mat[0] = bounds->bbmin[0]; + mat[1] = bounds->bbmin[1]; + mat[2] = bounds->bbmin[2]; + mat[3] = bounds->bbmax[0]; + mat[4] = bounds->bbmax[1]; + mat[5] = bounds->bbmax[2]; + + mat += 6; + bounds++; + } + } + + return qtrue; +} + +/* +============= +R_CullIQM +============= +*/ +static int R_CullIQM( iqmData_t *data, trRefEntity_t *ent ) { + vec3_t bounds[2]; + vec_t *oldBounds, *newBounds; + int i; + + if (!data->bounds) { + tr.pc.c_box_cull_md3_clip++; + return CULL_CLIP; + } + + // compute bounds pointers + oldBounds = data->bounds + 6*ent->e.oldframe; + newBounds = data->bounds + 6*ent->e.frame; + + // calculate a bounding box in the current coordinate system + for (i = 0 ; i < 3 ; i++) { + bounds[0][i] = oldBounds[i] < newBounds[i] ? oldBounds[i] : newBounds[i]; + bounds[1][i] = oldBounds[i+3] > newBounds[i+3] ? oldBounds[i+3] : newBounds[i+3]; + } + + switch ( R_CullLocalBox( bounds ) ) + { + case CULL_IN: + tr.pc.c_box_cull_md3_in++; + return CULL_IN; + case CULL_CLIP: + tr.pc.c_box_cull_md3_clip++; + return CULL_CLIP; + case CULL_OUT: + default: + tr.pc.c_box_cull_md3_out++; + return CULL_OUT; + } +} + +/* +================= +R_ComputeIQMFogNum + +================= +*/ +int R_ComputeIQMFogNum( iqmData_t *data, trRefEntity_t *ent ) { + int i, j; + fog_t *fog; + const vec_t *bounds; + const vec_t defaultBounds[6] = { -8, -8, -8, 8, 8, 8 }; + vec3_t diag, center; + vec3_t localOrigin; + vec_t radius; + + if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { + return 0; + } + + // FIXME: non-normalized axis issues + if (data->bounds) { + bounds = data->bounds + 6*ent->e.frame; + } else { + bounds = defaultBounds; + } + VectorSubtract( bounds+3, bounds, diag ); + VectorMA( bounds, 0.5f, diag, center ); + VectorAdd( ent->e.origin, center, localOrigin ); + radius = 0.5f * VectorLength( diag ); + + for ( i = 1 ; i < tr.world->numfogs ; i++ ) { + fog = &tr.world->fogs[i]; + for ( j = 0 ; j < 3 ; j++ ) { + if ( localOrigin[j] - radius >= fog->bounds[1][j] ) { + break; + } + if ( localOrigin[j] + radius <= fog->bounds[0][j] ) { + break; + } + } + if ( j == 3 ) { + return i; + } + } + + return 0; +} + +/* +================= +R_AddIQMSurfaces + +Add all surfaces of this model +================= +*/ +void R_AddIQMSurfaces( trRefEntity_t *ent ) { + iqmData_t *data; + srfIQModel_t *surface; + int i, j; + qboolean personalModel; + int cull; + int fogNum; + shader_t *shader; + skin_t *skin; + + data = tr.currentModel->modelData; + surface = data->surfaces; + + // don't add third_person objects if not in a portal + personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal; + + if ( ent->e.renderfx & RF_WRAP_FRAMES ) { + ent->e.frame %= data->num_frames; + ent->e.oldframe %= data->num_frames; + } + + // + // Validate the frames so there is no chance of a crash. + // This will write directly into the entity structure, so + // when the surfaces are rendered, they don't need to be + // range checked again. + // + if ( (ent->e.frame >= data->num_frames) + || (ent->e.frame < 0) + || (ent->e.oldframe >= data->num_frames) + || (ent->e.oldframe < 0) ) { + ri.Printf( PRINT_DEVELOPER, "R_AddIQMSurfaces: no such frame %d to %d for '%s'\n", + ent->e.oldframe, ent->e.frame, + tr.currentModel->name ); + ent->e.frame = 0; + ent->e.oldframe = 0; + } + + // + // cull the entire model if merged bounding box of both frames + // is outside the view frustum. + // + cull = R_CullIQM ( data, ent ); + if ( cull == CULL_OUT ) { + return; + } + + // + // set up lighting now that we know we aren't culled + // + if ( !personalModel || r_shadows->integer > 1 ) { + R_SetupEntityLighting( &tr.refdef, ent ); + } + + // + // see if we are in a fog volume + // + fogNum = R_ComputeIQMFogNum( data, ent ); + + for ( i = 0 ; i < data->num_surfaces ; i++ ) { + if(ent->e.customShader) + shader = R_GetShaderByHandle( ent->e.customShader ); + else if(ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins) + { + skin = R_GetSkinByHandle(ent->e.customSkin); + shader = tr.defaultShader; + + for(j = 0; j < skin->numSurfaces; j++) + { + if (!strcmp(skin->surfaces[j]->name, surface->name)) + { + shader = skin->surfaces[j]->shader; + break; + } + } + } else { + shader = surface->shader; + } + + // we will add shadows even if the main object isn't visible in the view + + // stencil shadows can't do personal models unless I polyhedron clip + if ( !personalModel + && r_shadows->integer == 2 + && fogNum == 0 + && !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) + && shader->sort == SS_OPAQUE ) { + R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, 0 ); + } + + // projection shadows work fine with personal models + if ( r_shadows->integer == 3 + && fogNum == 0 + && (ent->e.renderfx & RF_SHADOW_PLANE ) + && shader->sort == SS_OPAQUE ) { + R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, 0 ); + } + + if( !personalModel ) { + R_AddDrawSurf( (void *)surface, shader, fogNum, 0 ); + } + + surface++; + } +} + + +static void ComputeJointMats( iqmData_t *data, int frame, int oldframe, + float backlerp, float *mat ) { + float *mat1, *mat2; + int *joint = data->jointParents; + int i; + + if ( oldframe == frame ) { + mat1 = data->poseMats + 12 * data->num_joints * frame; + for( i = 0; i < data->num_joints; i++, joint++ ) { + if( *joint >= 0 ) { + Matrix34Multiply( mat + 12 * *joint, + mat1 + 12*i, mat + 12*i ); + } else { + Com_Memcpy( mat + 12*i, mat1 + 12*i, 12 * sizeof(float) ); + } + } + } else { + mat1 = data->poseMats + 12 * data->num_joints * frame; + mat2 = data->poseMats + 12 * data->num_joints * oldframe; + + for( i = 0; i < data->num_joints; i++, joint++ ) { + if( *joint >= 0 ) { + float tmpMat[12]; + InterpolateMatrix( mat1 + 12*i, mat2 + 12*i, + backlerp, tmpMat ); + Matrix34Multiply( mat + 12 * *joint, + tmpMat, mat + 12*i ); + + } else { + InterpolateMatrix( mat1 + 12*i, mat2 + 12*i, + backlerp, mat ); + } + } + } +} + + +/* +================= +RB_AddIQMSurfaces + +Compute vertices for this model surface +================= +*/ +void RB_IQMSurfaceAnim( surfaceType_t *surface ) { + srfIQModel_t *surf = (srfIQModel_t *)surface; + iqmData_t *data = surf->data; + float jointMats[IQM_MAX_JOINTS * 12]; + int i; + + vec4_t *outXYZ = &tess.xyz[tess.numVertexes]; + vec4_t *outNormal = &tess.normal[tess.numVertexes]; + vec2_t (*outTexCoord)[2] = &tess.texCoords[tess.numVertexes]; + color4ub_t *outColor = &tess.vertexColors[tess.numVertexes]; + + int frame = backEnd.currentEntity->e.frame % data->num_frames; + int oldframe = backEnd.currentEntity->e.oldframe % data->num_frames; + float backlerp = backEnd.currentEntity->e.backlerp; + + int *tri; + glIndex_t *ptr; + glIndex_t base; + + RB_CHECKOVERFLOW( surf->num_vertexes, surf->num_triangles * 3 ); + + // compute interpolated joint matrices + ComputeJointMats( data, frame, oldframe, backlerp, jointMats ); + + // transform vertexes and fill other data + for( i = 0; i < surf->num_vertexes; + i++, outXYZ++, outNormal++, outTexCoord++, outColor++ ) { + int j, k; + float vtxMat[12]; + float nrmMat[9]; + int vtx = i + surf->first_vertex; + + // compute the vertex matrix by blending the up to + // four blend weights + for( k = 0; k < 12; k++ ) + vtxMat[k] = data->blendWeights[4*vtx] + * jointMats[12*data->blendIndexes[4*vtx] + k]; + for( j = 1; j < 4; j++ ) { + if( data->blendWeights[4*vtx + j] <= 0 ) + break; + for( k = 0; k < 12; k++ ) + vtxMat[k] += data->blendWeights[4*vtx + j] + * jointMats[12*data->blendIndexes[4*vtx + j] + k]; + } + for( k = 0; k < 12; k++ ) + vtxMat[k] *= 1.0f / 255.0f; + + // compute the normal matrix as transpose of the adjoint + // of the vertex matrix + nrmMat[ 0] = vtxMat[ 5]*vtxMat[10] - vtxMat[ 6]*vtxMat[ 9]; + nrmMat[ 1] = vtxMat[ 6]*vtxMat[ 8] - vtxMat[ 4]*vtxMat[10]; + nrmMat[ 2] = vtxMat[ 4]*vtxMat[ 9] - vtxMat[ 5]*vtxMat[ 8]; + nrmMat[ 3] = vtxMat[ 2]*vtxMat[ 9] - vtxMat[ 1]*vtxMat[10]; + nrmMat[ 4] = vtxMat[ 0]*vtxMat[10] - vtxMat[ 2]*vtxMat[ 8]; + nrmMat[ 5] = vtxMat[ 1]*vtxMat[ 8] - vtxMat[ 0]*vtxMat[ 9]; + nrmMat[ 6] = vtxMat[ 1]*vtxMat[ 6] - vtxMat[ 2]*vtxMat[ 5]; + nrmMat[ 7] = vtxMat[ 2]*vtxMat[ 4] - vtxMat[ 0]*vtxMat[ 6]; + nrmMat[ 8] = vtxMat[ 0]*vtxMat[ 5] - vtxMat[ 1]*vtxMat[ 4]; + + (*outTexCoord)[0][0] = data->texcoords[2*vtx + 0]; + (*outTexCoord)[0][1] = data->texcoords[2*vtx + 1]; + (*outTexCoord)[1][0] = (*outTexCoord)[0][0]; + (*outTexCoord)[1][1] = (*outTexCoord)[0][1]; + + (*outXYZ)[0] = + vtxMat[ 0] * data->positions[3*vtx+0] + + vtxMat[ 1] * data->positions[3*vtx+1] + + vtxMat[ 2] * data->positions[3*vtx+2] + + vtxMat[ 3]; + (*outXYZ)[1] = + vtxMat[ 4] * data->positions[3*vtx+0] + + vtxMat[ 5] * data->positions[3*vtx+1] + + vtxMat[ 6] * data->positions[3*vtx+2] + + vtxMat[ 7]; + (*outXYZ)[2] = + vtxMat[ 8] * data->positions[3*vtx+0] + + vtxMat[ 9] * data->positions[3*vtx+1] + + vtxMat[10] * data->positions[3*vtx+2] + + vtxMat[11]; + (*outXYZ)[3] = 1.0f; + + (*outNormal)[0] = + nrmMat[ 0] * data->normals[3*vtx+0] + + nrmMat[ 1] * data->normals[3*vtx+1] + + nrmMat[ 2] * data->normals[3*vtx+2]; + (*outNormal)[1] = + nrmMat[ 3] * data->normals[3*vtx+0] + + nrmMat[ 4] * data->normals[3*vtx+1] + + nrmMat[ 5] * data->normals[3*vtx+2]; + (*outNormal)[2] = + nrmMat[ 6] * data->normals[3*vtx+0] + + nrmMat[ 7] * data->normals[3*vtx+1] + + nrmMat[ 8] * data->normals[3*vtx+2]; + (*outNormal)[3] = 0.0f; + + (*outColor)[0] = data->colors[4*vtx+0]; + (*outColor)[1] = data->colors[4*vtx+1]; + (*outColor)[2] = data->colors[4*vtx+2]; + (*outColor)[3] = data->colors[4*vtx+3]; + } + + tri = data->triangles + 3 * surf->first_triangle; + ptr = &tess.indexes[tess.numIndexes]; + base = tess.numVertexes; + + for( i = 0; i < surf->num_triangles; i++ ) { + *ptr++ = base + (*tri++ - surf->first_vertex); + *ptr++ = base + (*tri++ - surf->first_vertex); + *ptr++ = base + (*tri++ - surf->first_vertex); + } + + tess.numIndexes += 3 * surf->num_triangles; + tess.numVertexes += surf->num_vertexes; +} + +int R_IQMLerpTag( orientation_t *tag, iqmData_t *data, + int startFrame, int endFrame, + float frac, const char *tagName ) { + float jointMats[IQM_MAX_JOINTS * 12]; + int joint; + char *names = data->names; + + // get joint number by reading the joint names + for( joint = 0; joint < data->num_joints; joint++ ) { + if( !strcmp( tagName, names ) ) + break; + names += strlen( names ) + 1; + } + if( joint >= data->num_joints ) { + AxisClear( tag->axis ); + VectorClear( tag->origin ); + return qfalse; + } + + ComputeJointMats( data, startFrame, endFrame, frac, jointMats ); + + tag->axis[0][0] = jointMats[12 * joint + 0]; + tag->axis[1][0] = jointMats[12 * joint + 1]; + tag->axis[2][0] = jointMats[12 * joint + 2]; + tag->origin[0] = jointMats[12 * joint + 3]; + tag->axis[0][1] = jointMats[12 * joint + 4]; + tag->axis[1][1] = jointMats[12 * joint + 5]; + tag->axis[2][1] = jointMats[12 * joint + 6]; + tag->origin[1] = jointMats[12 * joint + 7]; + tag->axis[0][2] = jointMats[12 * joint + 8]; + tag->axis[1][2] = jointMats[12 * joint + 9]; + tag->axis[2][2] = jointMats[12 * joint + 10]; + tag->origin[2] = jointMats[12 * joint + 11]; + + return qtrue; +} diff --git a/src/renderergl1/tr_noise.c b/src/renderergl1/tr_noise.c new file mode 100644 index 00000000..b4c4082d --- /dev/null +++ b/src/renderergl1/tr_noise.c @@ -0,0 +1,92 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_noise.c +#include "tr_local.h" + +#define NOISE_SIZE 256 +#define NOISE_MASK ( NOISE_SIZE - 1 ) + +#define VAL( a ) s_noise_perm[ ( a ) & ( NOISE_MASK )] +#define INDEX( x, y, z, t ) VAL( x + VAL( y + VAL( z + VAL( t ) ) ) ) + +static float s_noise_table[NOISE_SIZE]; +static int s_noise_perm[NOISE_SIZE]; + +static float GetNoiseValue( int x, int y, int z, int t ) +{ + int index = INDEX( ( int ) x, ( int ) y, ( int ) z, ( int ) t ); + + return s_noise_table[index]; +} + +void R_NoiseInit( void ) +{ + int i; + + for ( i = 0; i < NOISE_SIZE; i++ ) + { + s_noise_table[i] = ( float ) ( ( ( rand() / ( float ) RAND_MAX ) * 2.0 - 1.0 ) ); + s_noise_perm[i] = ( unsigned char ) ( rand() / ( float ) RAND_MAX * 255 ); + } +} + +float R_NoiseGet4f( float x, float y, float z, float t ) +{ + int i; + int ix, iy, iz, it; + float fx, fy, fz, ft; + float front[4]; + float back[4]; + float fvalue, bvalue, value[2], finalvalue; + + ix = ( int ) floor( x ); + fx = x - ix; + iy = ( int ) floor( y ); + fy = y - iy; + iz = ( int ) floor( z ); + fz = z - iz; + it = ( int ) floor( t ); + ft = t - it; + + for ( i = 0; i < 2; i++ ) + { + front[0] = GetNoiseValue( ix, iy, iz, it + i ); + front[1] = GetNoiseValue( ix+1, iy, iz, it + i ); + front[2] = GetNoiseValue( ix, iy+1, iz, it + i ); + front[3] = GetNoiseValue( ix+1, iy+1, iz, it + i ); + + back[0] = GetNoiseValue( ix, iy, iz + 1, it + i ); + back[1] = GetNoiseValue( ix+1, iy, iz + 1, it + i ); + back[2] = GetNoiseValue( ix, iy+1, iz + 1, it + i ); + back[3] = GetNoiseValue( ix+1, iy+1, iz + 1, it + i ); + + fvalue = LERP( LERP( front[0], front[1], fx ), LERP( front[2], front[3], fx ), fy ); + bvalue = LERP( LERP( back[0], back[1], fx ), LERP( back[2], back[3], fx ), fy ); + + value[i] = LERP( fvalue, bvalue, fz ); + } + + finalvalue = LERP( value[0], value[1], ft ); + + return finalvalue; +} diff --git a/src/renderergl1/tr_scene.c b/src/renderergl1/tr_scene.c new file mode 100644 index 00000000..6ee77dc6 --- /dev/null +++ b/src/renderergl1/tr_scene.c @@ -0,0 +1,412 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "tr_local.h" + +int r_firstSceneDrawSurf; + +int r_numdlights; +int r_firstSceneDlight; + +int r_numentities; +int r_firstSceneEntity; + +int r_numpolys; +int r_firstScenePoly; + +int r_numpolyverts; + + +/* +==================== +R_InitNextFrame + +==================== +*/ +void R_InitNextFrame( void ) { + backEndData->commands.used = 0; + + r_firstSceneDrawSurf = 0; + + r_numdlights = 0; + r_firstSceneDlight = 0; + + r_numentities = 0; + r_firstSceneEntity = 0; + + r_numpolys = 0; + r_firstScenePoly = 0; + + r_numpolyverts = 0; +} + + +/* +==================== +RE_ClearScene + +==================== +*/ +void RE_ClearScene( void ) { + r_firstSceneDlight = r_numdlights; + r_firstSceneEntity = r_numentities; + r_firstScenePoly = r_numpolys; +} + +/* +=========================================================================== + +DISCRETE POLYS + +=========================================================================== +*/ + +/* +===================== +R_AddPolygonSurfaces + +Adds all the scene's polys into this view's drawsurf list +===================== +*/ +void R_AddPolygonSurfaces( void ) { + int i; + shader_t *sh; + srfPoly_t *poly; + + tr.currentEntityNum = REFENTITYNUM_WORLD; + tr.shiftedEntityNum = tr.currentEntityNum << QSORT_REFENTITYNUM_SHIFT; + + for ( i = 0, poly = tr.refdef.polys; i < tr.refdef.numPolys ; i++, poly++ ) { + sh = R_GetShaderByHandle( poly->hShader ); + R_AddDrawSurf( ( void * )poly, sh, poly->fogIndex, qfalse ); + } +} + +/* +===================== +RE_AddPolyToScene + +===================== +*/ +void RE_AddPolyToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ) { + srfPoly_t *poly; + int i, j; + int fogIndex; + fog_t *fog; + vec3_t bounds[2]; + + if ( !tr.registered ) { + return; + } + + if ( !hShader ) { + ri.Printf( PRINT_WARNING, "WARNING: RE_AddPolyToScene: NULL poly shader\n"); + return; + } + + for ( j = 0; j < numPolys; j++ ) { + if ( r_numpolyverts + numVerts > max_polyverts || r_numpolys >= max_polys ) { + /* + NOTE TTimo this was initially a PRINT_WARNING + but it happens a lot with high fighting scenes and particles + since we don't plan on changing the const and making for room for those effects + simply cut this message to developer only + */ + ri.Printf( PRINT_DEVELOPER, "WARNING: RE_AddPolyToScene: r_max_polys or r_max_polyverts reached\n"); + return; + } + + poly = &backEndData->polys[r_numpolys]; + poly->surfaceType = SF_POLY; + poly->hShader = hShader; + poly->numVerts = numVerts; + poly->verts = &backEndData->polyVerts[r_numpolyverts]; + + Com_Memcpy( poly->verts, &verts[numVerts*j], numVerts * sizeof( *verts ) ); + + if ( glConfig.hardwareType == GLHW_RAGEPRO ) { + poly->verts->modulate[0] = 255; + poly->verts->modulate[1] = 255; + poly->verts->modulate[2] = 255; + poly->verts->modulate[3] = 255; + } + // done. + r_numpolys++; + r_numpolyverts += numVerts; + + // if no world is loaded + if ( tr.world == NULL ) { + fogIndex = 0; + } + // see if it is in a fog volume + else if ( tr.world->numfogs == 1 ) { + fogIndex = 0; + } else { + // find which fog volume the poly is in + VectorCopy( poly->verts[0].xyz, bounds[0] ); + VectorCopy( poly->verts[0].xyz, bounds[1] ); + for ( i = 1 ; i < poly->numVerts ; i++ ) { + AddPointToBounds( poly->verts[i].xyz, bounds[0], bounds[1] ); + } + for ( fogIndex = 1 ; fogIndex < tr.world->numfogs ; fogIndex++ ) { + fog = &tr.world->fogs[fogIndex]; + if ( bounds[1][0] >= fog->bounds[0][0] + && bounds[1][1] >= fog->bounds[0][1] + && bounds[1][2] >= fog->bounds[0][2] + && bounds[0][0] <= fog->bounds[1][0] + && bounds[0][1] <= fog->bounds[1][1] + && bounds[0][2] <= fog->bounds[1][2] ) { + break; + } + } + if ( fogIndex == tr.world->numfogs ) { + fogIndex = 0; + } + } + poly->fogIndex = fogIndex; + } +} + + +//================================================================================= + + +/* +===================== +RE_AddRefEntityToScene + +===================== +*/ +void RE_AddRefEntityToScene( const refEntity_t *ent ) { + if ( !tr.registered ) { + return; + } + if ( r_numentities >= MAX_REFENTITIES ) { + ri.Printf(PRINT_DEVELOPER, "RE_AddRefEntityToScene: Dropping refEntity, reached MAX_REFENTITIES\n"); + return; + } + if ( Q_isnan(ent->origin[0]) || Q_isnan(ent->origin[1]) || Q_isnan(ent->origin[2]) ) { + static qboolean firstTime = qtrue; + if (firstTime) { + firstTime = qfalse; + ri.Printf( PRINT_WARNING, "RE_AddRefEntityToScene passed a refEntity which has an origin with a NaN component\n"); + } + return; + } + if ( (int)ent->reType < 0 || ent->reType >= RT_MAX_REF_ENTITY_TYPE ) { + ri.Error( ERR_DROP, "RE_AddRefEntityToScene: bad reType %i", ent->reType ); + } + + backEndData->entities[r_numentities].e = *ent; + backEndData->entities[r_numentities].lightingCalculated = qfalse; + + r_numentities++; +} + + +/* +===================== +RE_AddDynamicLightToScene + +===================== +*/ +void RE_AddDynamicLightToScene( const vec3_t org, float intensity, float r, float g, float b, int additive ) { + dlight_t *dl; + + if ( !tr.registered ) { + return; + } + if ( r_numdlights >= MAX_DLIGHTS ) { + return; + } + if ( intensity <= 0 ) { + return; + } + // these cards don't have the correct blend mode + if ( glConfig.hardwareType == GLHW_RIVA128 || glConfig.hardwareType == GLHW_PERMEDIA2 ) { + return; + } + dl = &backEndData->dlights[r_numdlights++]; + VectorCopy (org, dl->origin); + dl->radius = intensity; + dl->color[0] = r; + dl->color[1] = g; + dl->color[2] = b; + dl->additive = additive; +} + +/* +===================== +RE_AddLightToScene + +===================== +*/ +void RE_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ) { + RE_AddDynamicLightToScene( org, intensity, r, g, b, qfalse ); +} + +/* +===================== +RE_AddAdditiveLightToScene + +===================== +*/ +void RE_AddAdditiveLightToScene( const vec3_t org, float intensity, float r, float g, float b ) { + RE_AddDynamicLightToScene( org, intensity, r, g, b, qtrue ); +} + +/* +@@@@@@@@@@@@@@@@@@@@@ +RE_RenderScene + +Draw a 3D view into a part of the window, then return +to 2D drawing. + +Rendering a scene may require multiple views to be rendered +to handle mirrors, +@@@@@@@@@@@@@@@@@@@@@ +*/ +void RE_RenderScene( const refdef_t *fd ) { + viewParms_t parms; + int startTime; + + if ( !tr.registered ) { + return; + } + GLimp_LogComment( "====== RE_RenderScene =====\n" ); + + if ( r_norefresh->integer ) { + return; + } + + startTime = ri.Milliseconds(); + + if (!tr.world && !( fd->rdflags & RDF_NOWORLDMODEL ) ) { + ri.Error (ERR_DROP, "R_RenderScene: NULL worldmodel"); + } + + Com_Memcpy( tr.refdef.text, fd->text, sizeof( tr.refdef.text ) ); + + tr.refdef.x = fd->x; + tr.refdef.y = fd->y; + tr.refdef.width = fd->width; + tr.refdef.height = fd->height; + tr.refdef.fov_x = fd->fov_x; + tr.refdef.fov_y = fd->fov_y; + + VectorCopy( fd->vieworg, tr.refdef.vieworg ); + VectorCopy( fd->viewaxis[0], tr.refdef.viewaxis[0] ); + VectorCopy( fd->viewaxis[1], tr.refdef.viewaxis[1] ); + VectorCopy( fd->viewaxis[2], tr.refdef.viewaxis[2] ); + + tr.refdef.time = fd->time; + tr.refdef.rdflags = fd->rdflags; + + // copy the areamask data over and note if it has changed, which + // will force a reset of the visible leafs even if the view hasn't moved + tr.refdef.areamaskModified = qfalse; + if ( ! (tr.refdef.rdflags & RDF_NOWORLDMODEL) ) { + int areaDiff; + int i; + + // compare the area bits + areaDiff = 0; + for (i = 0 ; i < MAX_MAP_AREA_BYTES/4 ; i++) { + areaDiff |= ((int *)tr.refdef.areamask)[i] ^ ((int *)fd->areamask)[i]; + ((int *)tr.refdef.areamask)[i] = ((int *)fd->areamask)[i]; + } + + if ( areaDiff ) { + // a door just opened or something + tr.refdef.areamaskModified = qtrue; + } + } + + + // derived info + + tr.refdef.floatTime = tr.refdef.time * 0.001f; + + tr.refdef.numDrawSurfs = r_firstSceneDrawSurf; + tr.refdef.drawSurfs = backEndData->drawSurfs; + + tr.refdef.num_entities = r_numentities - r_firstSceneEntity; + tr.refdef.entities = &backEndData->entities[r_firstSceneEntity]; + + tr.refdef.num_dlights = r_numdlights - r_firstSceneDlight; + tr.refdef.dlights = &backEndData->dlights[r_firstSceneDlight]; + + tr.refdef.numPolys = r_numpolys - r_firstScenePoly; + tr.refdef.polys = &backEndData->polys[r_firstScenePoly]; + + // turn off dynamic lighting globally by clearing all the + // dlights if it needs to be disabled or if vertex lighting is enabled + if ( r_dynamiclight->integer == 0 || + r_vertexLight->integer == 1 || + glConfig.hardwareType == GLHW_PERMEDIA2 ) { + tr.refdef.num_dlights = 0; + } + + // a single frame may have multiple scenes draw inside it -- + // a 3D game view, 3D status bar renderings, 3D menus, etc. + // They need to be distinguished by the light flare code, because + // the visibility state for a given surface may be different in + // each scene / view. + tr.frameSceneNum++; + tr.sceneCount++; + + // setup view parms for the initial view + // + // set up viewport + // The refdef takes 0-at-the-top y coordinates, so + // convert to GL's 0-at-the-bottom space + // + Com_Memset( &parms, 0, sizeof( parms ) ); + parms.viewportX = tr.refdef.x; + parms.viewportY = glConfig.vidHeight - ( tr.refdef.y + tr.refdef.height ); + parms.viewportWidth = tr.refdef.width; + parms.viewportHeight = tr.refdef.height; + parms.isPortal = qfalse; + + parms.fovX = tr.refdef.fov_x; + parms.fovY = tr.refdef.fov_y; + + parms.stereoFrame = tr.refdef.stereoFrame; + + VectorCopy( fd->vieworg, parms.or.origin ); + VectorCopy( fd->viewaxis[0], parms.or.axis[0] ); + VectorCopy( fd->viewaxis[1], parms.or.axis[1] ); + VectorCopy( fd->viewaxis[2], parms.or.axis[2] ); + + VectorCopy( fd->vieworg, parms.pvsOrigin ); + + R_RenderView( &parms ); + + // the next scene rendered in this frame will tack on after this one + r_firstSceneDrawSurf = tr.refdef.numDrawSurfs; + r_firstSceneEntity = r_numentities; + r_firstSceneDlight = r_numdlights; + r_firstScenePoly = r_numpolys; + + tr.frontEndMsec += ri.Milliseconds() - startTime; +} diff --git a/src/renderergl1/tr_shade.c b/src/renderergl1/tr_shade.c new file mode 100644 index 00000000..b8e8d268 --- /dev/null +++ b/src/renderergl1/tr_shade.c @@ -0,0 +1,1528 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_shade.c + +#include "tr_local.h" +#if idppc_altivec && !defined(MACOS_X) +#include +#endif + +/* + + THIS ENTIRE FILE IS BACK END + + This file deals with applying shaders to surface data in the tess struct. +*/ + +/* +================ +R_ArrayElementDiscrete + +This is just for OpenGL conformance testing, it should never be the fastest +================ +*/ +static void APIENTRY R_ArrayElementDiscrete( GLint index ) { + qglColor4ubv( tess.svars.colors[ index ] ); + if ( glState.currenttmu ) { + qglMultiTexCoord2fARB( 0, tess.svars.texcoords[ 0 ][ index ][0], tess.svars.texcoords[ 0 ][ index ][1] ); + qglMultiTexCoord2fARB( 1, tess.svars.texcoords[ 1 ][ index ][0], tess.svars.texcoords[ 1 ][ index ][1] ); + } else { + qglTexCoord2fv( tess.svars.texcoords[ 0 ][ index ] ); + } + qglVertex3fv( tess.xyz[ index ] ); +} + +/* +=================== +R_DrawStripElements + +=================== +*/ +static int c_vertexes; // for seeing how long our average strips are +static int c_begins; +static void R_DrawStripElements( int numIndexes, const glIndex_t *indexes, void ( APIENTRY *element )(GLint) ) { + int i; + int last[3] = { -1, -1, -1 }; + qboolean even; + + c_begins++; + + if ( numIndexes <= 0 ) { + return; + } + + qglBegin( GL_TRIANGLE_STRIP ); + + // prime the strip + element( indexes[0] ); + element( indexes[1] ); + element( indexes[2] ); + c_vertexes += 3; + + last[0] = indexes[0]; + last[1] = indexes[1]; + last[2] = indexes[2]; + + even = qfalse; + + for ( i = 3; i < numIndexes; i += 3 ) + { + // odd numbered triangle in potential strip + if ( !even ) + { + // check previous triangle to see if we're continuing a strip + if ( ( indexes[i+0] == last[2] ) && ( indexes[i+1] == last[1] ) ) + { + element( indexes[i+2] ); + c_vertexes++; + assert( indexes[i+2] < tess.numVertexes ); + even = qtrue; + } + // otherwise we're done with this strip so finish it and start + // a new one + else + { + qglEnd(); + + qglBegin( GL_TRIANGLE_STRIP ); + c_begins++; + + element( indexes[i+0] ); + element( indexes[i+1] ); + element( indexes[i+2] ); + + c_vertexes += 3; + + even = qfalse; + } + } + else + { + // check previous triangle to see if we're continuing a strip + if ( ( last[2] == indexes[i+1] ) && ( last[0] == indexes[i+0] ) ) + { + element( indexes[i+2] ); + c_vertexes++; + + even = qfalse; + } + // otherwise we're done with this strip so finish it and start + // a new one + else + { + qglEnd(); + + qglBegin( GL_TRIANGLE_STRIP ); + c_begins++; + + element( indexes[i+0] ); + element( indexes[i+1] ); + element( indexes[i+2] ); + c_vertexes += 3; + + even = qfalse; + } + } + + // cache the last three vertices + last[0] = indexes[i+0]; + last[1] = indexes[i+1]; + last[2] = indexes[i+2]; + } + + qglEnd(); +} + + + +/* +================== +R_DrawElements + +Optionally performs our own glDrawElements that looks for strip conditions +instead of using the single glDrawElements call that may be inefficient +without compiled vertex arrays. +================== +*/ +static void R_DrawElements( int numIndexes, const glIndex_t *indexes ) { + int primitives; + + primitives = r_primitives->integer; + + // default is to use triangles if compiled vertex arrays are present + if ( primitives == 0 ) { + if ( qglLockArraysEXT ) { + primitives = 2; + } else { + primitives = 1; + } + } + + + if ( primitives == 2 ) { + qglDrawElements( GL_TRIANGLES, + numIndexes, + GL_INDEX_TYPE, + indexes ); + return; + } + + if ( primitives == 1 ) { + R_DrawStripElements( numIndexes, indexes, qglArrayElement ); + return; + } + + if ( primitives == 3 ) { + R_DrawStripElements( numIndexes, indexes, R_ArrayElementDiscrete ); + return; + } + + // anything else will cause no drawing +} + + +/* +============================================================= + +SURFACE SHADERS + +============================================================= +*/ + +shaderCommands_t tess; +static qboolean setArraysOnce; + +/* +================= +R_BindAnimatedImage + +================= +*/ +static void R_BindAnimatedImage( textureBundle_t *bundle ) { + int index; + + if ( bundle->isVideoMap ) { + ri.CIN_RunCinematic(bundle->videoMapHandle); + ri.CIN_UploadCinematic(bundle->videoMapHandle); + return; + } + + if ( bundle->numImageAnimations <= 1 ) { + GL_Bind( bundle->image[0] ); + return; + } + + // it is necessary to do this messy calc to make sure animations line up + // exactly with waveforms of the same frequency + index = ri.ftol(tess.shaderTime * bundle->imageAnimationSpeed * FUNCTABLE_SIZE); + index >>= FUNCTABLE_SIZE2; + + if ( index < 0 ) { + index = 0; // may happen with shader time offsets + } + index %= bundle->numImageAnimations; + + GL_Bind( bundle->image[ index ] ); +} + +/* +================ +DrawTris + +Draws triangle outlines for debugging +================ +*/ +static void DrawTris (shaderCommands_t *input) { + GL_Bind( tr.whiteImage ); + qglColor3f (1,1,1); + + GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE ); + qglDepthRange( 0, 0 ); + + qglDisableClientState (GL_COLOR_ARRAY); + qglDisableClientState (GL_TEXTURE_COORD_ARRAY); + + qglVertexPointer (3, GL_FLOAT, 16, input->xyz); // padded for SIMD + + if (qglLockArraysEXT) { + qglLockArraysEXT(0, input->numVertexes); + GLimp_LogComment( "glLockArraysEXT\n" ); + } + + R_DrawElements( input->numIndexes, input->indexes ); + + if (qglUnlockArraysEXT) { + qglUnlockArraysEXT(); + GLimp_LogComment( "glUnlockArraysEXT\n" ); + } + qglDepthRange( 0, 1 ); +} + + +/* +================ +DrawNormals + +Draws vertex normals for debugging +================ +*/ +static void DrawNormals (shaderCommands_t *input) { + int i; + vec3_t temp; + + GL_Bind( tr.whiteImage ); + qglColor3f (1,1,1); + qglDepthRange( 0, 0 ); // never occluded + GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE ); + + qglBegin (GL_LINES); + for (i = 0 ; i < input->numVertexes ; i++) { + qglVertex3fv (input->xyz[i]); + VectorMA (input->xyz[i], 2, input->normal[i], temp); + qglVertex3fv (temp); + } + qglEnd (); + + qglDepthRange( 0, 1 ); +} + +/* +============== +RB_BeginSurface + +We must set some things up before beginning any tesselation, +because a surface may be forced to perform a RB_End due +to overflow. +============== +*/ +void RB_BeginSurface( shader_t *shader, int fogNum ) { + + shader_t *state = (shader->remappedShader) ? shader->remappedShader : shader; + + tess.numIndexes = 0; + tess.numVertexes = 0; + tess.shader = state; + tess.fogNum = fogNum; + tess.dlightBits = 0; // will be OR'd in by surface functions + tess.xstages = state->stages; + tess.numPasses = state->numUnfoggedPasses; + tess.currentStageIteratorFunc = state->optimalStageIteratorFunc; + + tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; + if (tess.shader->clampTime && tess.shaderTime >= tess.shader->clampTime) { + tess.shaderTime = tess.shader->clampTime; + } + + +} + +/* +=================== +DrawMultitextured + +output = t0 * t1 or t0 + t1 + +t0 = most upstream according to spec +t1 = most downstream according to spec +=================== +*/ +static void DrawMultitextured( shaderCommands_t *input, int stage ) { + shaderStage_t *pStage; + + pStage = tess.xstages[stage]; + + GL_State( pStage->stateBits ); + + // this is an ugly hack to work around a GeForce driver + // bug with multitexture and clip planes + if ( backEnd.viewParms.isPortal ) { + qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + } + + // + // base + // + GL_SelectTexture( 0 ); + qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[0] ); + R_BindAnimatedImage( &pStage->bundle[0] ); + + // + // lightmap/secondary pass + // + GL_SelectTexture( 1 ); + qglEnable( GL_TEXTURE_2D ); + qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); + + if ( r_lightmap->integer ) { + GL_TexEnv( GL_REPLACE ); + } else { + GL_TexEnv( tess.shader->multitextureEnv ); + } + + qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[1] ); + + R_BindAnimatedImage( &pStage->bundle[1] ); + + R_DrawElements( input->numIndexes, input->indexes ); + + // + // disable texturing on TEXTURE1, then select TEXTURE0 + // + //qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); + qglDisable( GL_TEXTURE_2D ); + + GL_SelectTexture( 0 ); +} + + + +/* +=================== +ProjectDlightTexture + +Perform dynamic lighting with another rendering pass +=================== +*/ +#if idppc_altivec +static void ProjectDlightTexture_altivec( void ) { + int i, l; + vec_t origin0, origin1, origin2; + float texCoords0, texCoords1; + vector float floatColorVec0, floatColorVec1; + vector float modulateVec, colorVec, zero; + vector short colorShort; + vector signed int colorInt; + vector unsigned char floatColorVecPerm, modulatePerm, colorChar; + vector unsigned char vSel = VECCONST_UINT8(0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff); + float *texCoords; + byte *colors; + byte clipBits[SHADER_MAX_VERTEXES]; + float texCoordsArray[SHADER_MAX_VERTEXES][2]; + byte colorArray[SHADER_MAX_VERTEXES][4]; + unsigned hitIndexes[SHADER_MAX_INDEXES]; + int numIndexes; + float scale; + float radius; + vec3_t floatColor; + float modulate = 0.0f; + + if ( !backEnd.refdef.num_dlights ) { + return; + } + + // There has to be a better way to do this so that floatColor + // and/or modulate are already 16-byte aligned. + floatColorVecPerm = vec_lvsl(0,(float *)floatColor); + modulatePerm = vec_lvsl(0,(float *)&modulate); + modulatePerm = (vector unsigned char)vec_splat((vector unsigned int)modulatePerm,0); + zero = (vector float)vec_splat_s8(0); + + for ( l = 0 ; l < backEnd.refdef.num_dlights ; l++ ) { + dlight_t *dl; + + if ( !( tess.dlightBits & ( 1 << l ) ) ) { + continue; // this surface definately doesn't have any of this light + } + texCoords = texCoordsArray[0]; + colors = colorArray[0]; + + dl = &backEnd.refdef.dlights[l]; + origin0 = dl->transformed[0]; + origin1 = dl->transformed[1]; + origin2 = dl->transformed[2]; + radius = dl->radius; + scale = 1.0f / radius; + + if(r_greyscale->integer) + { + float luminance; + + luminance = LUMA(dl->color[0], dl->color[1], dl->color[2]) * 255.0f; + floatColor[0] = floatColor[1] = floatColor[2] = luminance; + } + else if(r_greyscale->value) + { + float luminance; + + luminance = LUMA(dl->color[0], dl->color[1], dl->color[2]) * 255.0f; + floatColor[0] = LERP(dl->color[0] * 255.0f, luminance, r_greyscale->value); + floatColor[1] = LERP(dl->color[1] * 255.0f, luminance, r_greyscale->value); + floatColor[2] = LERP(dl->color[2] * 255.0f, luminance, r_greyscale->value); + } + else + { + floatColor[0] = dl->color[0] * 255.0f; + floatColor[1] = dl->color[1] * 255.0f; + floatColor[2] = dl->color[2] * 255.0f; + } + floatColorVec0 = vec_ld(0, floatColor); + floatColorVec1 = vec_ld(11, floatColor); + floatColorVec0 = vec_perm(floatColorVec0,floatColorVec0,floatColorVecPerm); + for ( i = 0 ; i < tess.numVertexes ; i++, texCoords += 2, colors += 4 ) { + int clip = 0; + vec_t dist0, dist1, dist2; + + dist0 = origin0 - tess.xyz[i][0]; + dist1 = origin1 - tess.xyz[i][1]; + dist2 = origin2 - tess.xyz[i][2]; + + backEnd.pc.c_dlightVertexes++; + + texCoords0 = 0.5f + dist0 * scale; + texCoords1 = 0.5f + dist1 * scale; + + if( !r_dlightBacks->integer && + // dist . tess.normal[i] + ( dist0 * tess.normal[i][0] + + dist1 * tess.normal[i][1] + + dist2 * tess.normal[i][2] ) < 0.0f ) { + clip = 63; + } else { + if ( texCoords0 < 0.0f ) { + clip |= 1; + } else if ( texCoords0 > 1.0f ) { + clip |= 2; + } + if ( texCoords1 < 0.0f ) { + clip |= 4; + } else if ( texCoords1 > 1.0f ) { + clip |= 8; + } + texCoords[0] = texCoords0; + texCoords[1] = texCoords1; + + // modulate the strength based on the height and color + if ( dist2 > radius ) { + clip |= 16; + modulate = 0.0f; + } else if ( dist2 < -radius ) { + clip |= 32; + modulate = 0.0f; + } else { + dist2 = Q_fabs(dist2); + if ( dist2 < radius * 0.5f ) { + modulate = 1.0f; + } else { + modulate = 2.0f * (radius - dist2) * scale; + } + } + } + clipBits[i] = clip; + + modulateVec = vec_ld(0,(float *)&modulate); + modulateVec = vec_perm(modulateVec,modulateVec,modulatePerm); + colorVec = vec_madd(floatColorVec0,modulateVec,zero); + colorInt = vec_cts(colorVec,0); // RGBx + colorShort = vec_pack(colorInt,colorInt); // RGBxRGBx + colorChar = vec_packsu(colorShort,colorShort); // RGBxRGBxRGBxRGBx + colorChar = vec_sel(colorChar,vSel,vSel); // RGBARGBARGBARGBA replace alpha with 255 + vec_ste((vector unsigned int)colorChar,0,(unsigned int *)colors); // store color + } + + // build a list of triangles that need light + numIndexes = 0; + for ( i = 0 ; i < tess.numIndexes ; i += 3 ) { + int a, b, c; + + a = tess.indexes[i]; + b = tess.indexes[i+1]; + c = tess.indexes[i+2]; + if ( clipBits[a] & clipBits[b] & clipBits[c] ) { + continue; // not lighted + } + hitIndexes[numIndexes] = a; + hitIndexes[numIndexes+1] = b; + hitIndexes[numIndexes+2] = c; + numIndexes += 3; + } + + if ( !numIndexes ) { + continue; + } + + qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); + qglTexCoordPointer( 2, GL_FLOAT, 0, texCoordsArray[0] ); + + qglEnableClientState( GL_COLOR_ARRAY ); + qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, colorArray ); + + GL_Bind( tr.dlightImage ); + // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light + // where they aren't rendered + if ( dl->additive ) { + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); + } + else { + GL_State( GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); + } + R_DrawElements( numIndexes, hitIndexes ); + backEnd.pc.c_totalIndexes += numIndexes; + backEnd.pc.c_dlightIndexes += numIndexes; + } +} +#endif + + +static void ProjectDlightTexture_scalar( void ) { + int i, l; + vec3_t origin; + float *texCoords; + byte *colors; + byte clipBits[SHADER_MAX_VERTEXES]; + float texCoordsArray[SHADER_MAX_VERTEXES][2]; + byte colorArray[SHADER_MAX_VERTEXES][4]; + unsigned hitIndexes[SHADER_MAX_INDEXES]; + int numIndexes; + float scale; + float radius; + vec3_t floatColor; + float modulate = 0.0f; + + if ( !backEnd.refdef.num_dlights ) { + return; + } + + for ( l = 0 ; l < backEnd.refdef.num_dlights ; l++ ) { + dlight_t *dl; + + if ( !( tess.dlightBits & ( 1 << l ) ) ) { + continue; // this surface definately doesn't have any of this light + } + texCoords = texCoordsArray[0]; + colors = colorArray[0]; + + dl = &backEnd.refdef.dlights[l]; + VectorCopy( dl->transformed, origin ); + radius = dl->radius; + scale = 1.0f / radius; + + if(r_greyscale->integer) + { + float luminance; + + luminance = LUMA(dl->color[0], dl->color[1], dl->color[2]) * 255.0f; + floatColor[0] = floatColor[1] = floatColor[2] = luminance; + } + else if(r_greyscale->value) + { + float luminance; + + luminance = LUMA(dl->color[0], dl->color[1], dl->color[2]) * 255.0f; + floatColor[0] = LERP(dl->color[0] * 255.0f, luminance, r_greyscale->value); + floatColor[1] = LERP(dl->color[1] * 255.0f, luminance, r_greyscale->value); + floatColor[2] = LERP(dl->color[2] * 255.0f, luminance, r_greyscale->value); + } + else + { + floatColor[0] = dl->color[0] * 255.0f; + floatColor[1] = dl->color[1] * 255.0f; + floatColor[2] = dl->color[2] * 255.0f; + } + + for ( i = 0 ; i < tess.numVertexes ; i++, texCoords += 2, colors += 4 ) { + int clip = 0; + vec3_t dist; + + VectorSubtract( origin, tess.xyz[i], dist ); + + backEnd.pc.c_dlightVertexes++; + + texCoords[0] = 0.5f + dist[0] * scale; + texCoords[1] = 0.5f + dist[1] * scale; + + if( !r_dlightBacks->integer && + // dist . tess.normal[i] + ( dist[0] * tess.normal[i][0] + + dist[1] * tess.normal[i][1] + + dist[2] * tess.normal[i][2] ) < 0.0f ) { + clip = 63; + } else { + if ( texCoords[0] < 0.0f ) { + clip |= 1; + } else if ( texCoords[0] > 1.0f ) { + clip |= 2; + } + if ( texCoords[1] < 0.0f ) { + clip |= 4; + } else if ( texCoords[1] > 1.0f ) { + clip |= 8; + } + texCoords[0] = texCoords[0]; + texCoords[1] = texCoords[1]; + + // modulate the strength based on the height and color + if ( dist[2] > radius ) { + clip |= 16; + modulate = 0.0f; + } else if ( dist[2] < -radius ) { + clip |= 32; + modulate = 0.0f; + } else { + dist[2] = Q_fabs(dist[2]); + if ( dist[2] < radius * 0.5f ) { + modulate = 1.0f; + } else { + modulate = 2.0f * (radius - dist[2]) * scale; + } + } + } + clipBits[i] = clip; + colors[0] = ri.ftol(floatColor[0] * modulate); + colors[1] = ri.ftol(floatColor[1] * modulate); + colors[2] = ri.ftol(floatColor[2] * modulate); + colors[3] = 255; + } + + // build a list of triangles that need light + numIndexes = 0; + for ( i = 0 ; i < tess.numIndexes ; i += 3 ) { + int a, b, c; + + a = tess.indexes[i]; + b = tess.indexes[i+1]; + c = tess.indexes[i+2]; + if ( clipBits[a] & clipBits[b] & clipBits[c] ) { + continue; // not lighted + } + hitIndexes[numIndexes] = a; + hitIndexes[numIndexes+1] = b; + hitIndexes[numIndexes+2] = c; + numIndexes += 3; + } + + if ( !numIndexes ) { + continue; + } + + qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); + qglTexCoordPointer( 2, GL_FLOAT, 0, texCoordsArray[0] ); + + qglEnableClientState( GL_COLOR_ARRAY ); + qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, colorArray ); + + GL_Bind( tr.dlightImage ); + // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light + // where they aren't rendered + if ( dl->additive ) { + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); + } + else { + GL_State( GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); + } + R_DrawElements( numIndexes, hitIndexes ); + backEnd.pc.c_totalIndexes += numIndexes; + backEnd.pc.c_dlightIndexes += numIndexes; + } +} + +static void ProjectDlightTexture( void ) { +#if idppc_altivec + if (com_altivec->integer) { + // must be in a seperate function or G3 systems will crash. + ProjectDlightTexture_altivec(); + return; + } +#endif + ProjectDlightTexture_scalar(); +} + + +/* +=================== +RB_FogPass + +Blends a fog texture on top of everything else +=================== +*/ +static void RB_FogPass( void ) { + fog_t *fog; + int i; + + qglEnableClientState( GL_COLOR_ARRAY ); + qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors ); + + qglEnableClientState( GL_TEXTURE_COORD_ARRAY); + qglTexCoordPointer( 2, GL_FLOAT, 0, tess.svars.texcoords[0] ); + + fog = tr.world->fogs + tess.fogNum; + + for ( i = 0; i < tess.numVertexes; i++ ) { + * ( int * )&tess.svars.colors[i] = fog->colorInt; + } + + RB_CalcFogTexCoords( ( float * ) tess.svars.texcoords[0] ); + + GL_Bind( tr.fogImage ); + + if ( tess.shader->fogPass == FP_EQUAL ) { + GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_EQUAL ); + } else { + GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); + } + + R_DrawElements( tess.numIndexes, tess.indexes ); +} + +/* +=============== +ComputeColors +=============== +*/ +static void ComputeColors( shaderStage_t *pStage ) +{ + int i; + + // + // rgbGen + // + switch ( pStage->rgbGen ) + { + case CGEN_IDENTITY: + Com_Memset( tess.svars.colors, 0xff, tess.numVertexes * 4 ); + break; + default: + case CGEN_IDENTITY_LIGHTING: + Com_Memset( tess.svars.colors, tr.identityLightByte, tess.numVertexes * 4 ); + break; + case CGEN_LIGHTING_DIFFUSE: + RB_CalcDiffuseColor( ( unsigned char * ) tess.svars.colors ); + break; + case CGEN_EXACT_VERTEX: + Com_Memcpy( tess.svars.colors, tess.vertexColors, tess.numVertexes * sizeof( tess.vertexColors[0] ) ); + break; + case CGEN_CONST: + for ( i = 0; i < tess.numVertexes; i++ ) { + *(int *)tess.svars.colors[i] = *(int *)pStage->constantColor; + } + break; + case CGEN_VERTEX: + if ( tr.identityLight == 1 ) + { + Com_Memcpy( tess.svars.colors, tess.vertexColors, tess.numVertexes * sizeof( tess.vertexColors[0] ) ); + } + else + { + for ( i = 0; i < tess.numVertexes; i++ ) + { + tess.svars.colors[i][0] = tess.vertexColors[i][0] * tr.identityLight; + tess.svars.colors[i][1] = tess.vertexColors[i][1] * tr.identityLight; + tess.svars.colors[i][2] = tess.vertexColors[i][2] * tr.identityLight; + tess.svars.colors[i][3] = tess.vertexColors[i][3]; + } + } + break; + case CGEN_ONE_MINUS_VERTEX: + if ( tr.identityLight == 1 ) + { + for ( i = 0; i < tess.numVertexes; i++ ) + { + tess.svars.colors[i][0] = 255 - tess.vertexColors[i][0]; + tess.svars.colors[i][1] = 255 - tess.vertexColors[i][1]; + tess.svars.colors[i][2] = 255 - tess.vertexColors[i][2]; + } + } + else + { + for ( i = 0; i < tess.numVertexes; i++ ) + { + tess.svars.colors[i][0] = ( 255 - tess.vertexColors[i][0] ) * tr.identityLight; + tess.svars.colors[i][1] = ( 255 - tess.vertexColors[i][1] ) * tr.identityLight; + tess.svars.colors[i][2] = ( 255 - tess.vertexColors[i][2] ) * tr.identityLight; + } + } + break; + case CGEN_FOG: + { + fog_t *fog; + + fog = tr.world->fogs + tess.fogNum; + + for ( i = 0; i < tess.numVertexes; i++ ) { + * ( int * )&tess.svars.colors[i] = fog->colorInt; + } + } + break; + case CGEN_WAVEFORM: + RB_CalcWaveColor( &pStage->rgbWave, ( unsigned char * ) tess.svars.colors ); + break; + case CGEN_ENTITY: + RB_CalcColorFromEntity( ( unsigned char * ) tess.svars.colors ); + break; + case CGEN_ONE_MINUS_ENTITY: + RB_CalcColorFromOneMinusEntity( ( unsigned char * ) tess.svars.colors ); + break; + } + + // + // alphaGen + // + switch ( pStage->alphaGen ) + { + case AGEN_SKIP: + break; + case AGEN_IDENTITY: + if ( pStage->rgbGen != CGEN_IDENTITY ) { + if ( ( pStage->rgbGen == CGEN_VERTEX && tr.identityLight != 1 ) || + pStage->rgbGen != CGEN_VERTEX ) { + for ( i = 0; i < tess.numVertexes; i++ ) { + tess.svars.colors[i][3] = 0xff; + } + } + } + break; + case AGEN_CONST: + if ( pStage->rgbGen != CGEN_CONST ) { + for ( i = 0; i < tess.numVertexes; i++ ) { + tess.svars.colors[i][3] = pStage->constantColor[3]; + } + } + break; + case AGEN_WAVEFORM: + RB_CalcWaveAlpha( &pStage->alphaWave, ( unsigned char * ) tess.svars.colors ); + break; + case AGEN_LIGHTING_SPECULAR: + RB_CalcSpecularAlpha( ( unsigned char * ) tess.svars.colors ); + break; + case AGEN_ENTITY: + RB_CalcAlphaFromEntity( ( unsigned char * ) tess.svars.colors ); + break; + case AGEN_ONE_MINUS_ENTITY: + RB_CalcAlphaFromOneMinusEntity( ( unsigned char * ) tess.svars.colors ); + break; + case AGEN_VERTEX: + if ( pStage->rgbGen != CGEN_VERTEX ) { + for ( i = 0; i < tess.numVertexes; i++ ) { + tess.svars.colors[i][3] = tess.vertexColors[i][3]; + } + } + break; + case AGEN_ONE_MINUS_VERTEX: + for ( i = 0; i < tess.numVertexes; i++ ) + { + tess.svars.colors[i][3] = 255 - tess.vertexColors[i][3]; + } + break; + case AGEN_PORTAL: + { + unsigned char alpha; + + for ( i = 0; i < tess.numVertexes; i++ ) + { + float len; + vec3_t v; + + VectorSubtract( tess.xyz[i], backEnd.viewParms.or.origin, v ); + len = VectorLength( v ); + + len /= tess.shader->portalRange; + + if ( len < 0 ) + { + alpha = 0; + } + else if ( len > 1 ) + { + alpha = 0xff; + } + else + { + alpha = len * 0xff; + } + + tess.svars.colors[i][3] = alpha; + } + } + break; + } + + // + // fog adjustment for colors to fade out as fog increases + // + if ( tess.fogNum ) + { + switch ( pStage->adjustColorsForFog ) + { + case ACFF_MODULATE_RGB: + RB_CalcModulateColorsByFog( ( unsigned char * ) tess.svars.colors ); + break; + case ACFF_MODULATE_ALPHA: + RB_CalcModulateAlphasByFog( ( unsigned char * ) tess.svars.colors ); + break; + case ACFF_MODULATE_RGBA: + RB_CalcModulateRGBAsByFog( ( unsigned char * ) tess.svars.colors ); + break; + case ACFF_NONE: + break; + } + } + + // if in greyscale rendering mode turn all color values into greyscale. + if(r_greyscale->integer) + { + int scale; + for(i = 0; i < tess.numVertexes; i++) + { + scale = LUMA(tess.svars.colors[i][0], tess.svars.colors[i][1], tess.svars.colors[i][2]); + tess.svars.colors[i][0] = tess.svars.colors[i][1] = tess.svars.colors[i][2] = scale; + } + } + else if(r_greyscale->value) + { + float scale; + + for(i = 0; i < tess.numVertexes; i++) + { + scale = LUMA(tess.svars.colors[i][0], tess.svars.colors[i][1], tess.svars.colors[i][2]); + tess.svars.colors[i][0] = LERP(tess.svars.colors[i][0], scale, r_greyscale->value); + tess.svars.colors[i][1] = LERP(tess.svars.colors[i][1], scale, r_greyscale->value); + tess.svars.colors[i][2] = LERP(tess.svars.colors[i][2], scale, r_greyscale->value); + } + } +} + +/* +=============== +ComputeTexCoords +=============== +*/ +static void ComputeTexCoords( shaderStage_t *pStage ) { + int i; + int b; + + for ( b = 0; b < NUM_TEXTURE_BUNDLES; b++ ) { + int tm; + + // + // generate the texture coordinates + // + switch ( pStage->bundle[b].tcGen ) + { + case TCGEN_IDENTITY: + Com_Memset( tess.svars.texcoords[b], 0, sizeof( float ) * 2 * tess.numVertexes ); + break; + case TCGEN_TEXTURE: + for ( i = 0 ; i < tess.numVertexes ; i++ ) { + tess.svars.texcoords[b][i][0] = tess.texCoords[i][0][0]; + tess.svars.texcoords[b][i][1] = tess.texCoords[i][0][1]; + } + break; + case TCGEN_LIGHTMAP: + for ( i = 0 ; i < tess.numVertexes ; i++ ) { + tess.svars.texcoords[b][i][0] = tess.texCoords[i][1][0]; + tess.svars.texcoords[b][i][1] = tess.texCoords[i][1][1]; + } + break; + case TCGEN_VECTOR: + for ( i = 0 ; i < tess.numVertexes ; i++ ) { + tess.svars.texcoords[b][i][0] = DotProduct( tess.xyz[i], pStage->bundle[b].tcGenVectors[0] ); + tess.svars.texcoords[b][i][1] = DotProduct( tess.xyz[i], pStage->bundle[b].tcGenVectors[1] ); + } + break; + case TCGEN_FOG: + RB_CalcFogTexCoords( ( float * ) tess.svars.texcoords[b] ); + break; + case TCGEN_ENVIRONMENT_MAPPED: + RB_CalcEnvironmentTexCoords( ( float * ) tess.svars.texcoords[b] ); + break; + case TCGEN_BAD: + return; + } + + // + // alter texture coordinates + // + for ( tm = 0; tm < pStage->bundle[b].numTexMods ; tm++ ) { + switch ( pStage->bundle[b].texMods[tm].type ) + { + case TMOD_NONE: + tm = TR_MAX_TEXMODS; // break out of for loop + break; + + case TMOD_TURBULENT: + RB_CalcTurbulentTexCoords( &pStage->bundle[b].texMods[tm].wave, + ( float * ) tess.svars.texcoords[b] ); + break; + + case TMOD_ENTITY_TRANSLATE: + RB_CalcScrollTexCoords( backEnd.currentEntity->e.shaderTexCoord, + ( float * ) tess.svars.texcoords[b] ); + break; + + case TMOD_SCROLL: + RB_CalcScrollTexCoords( pStage->bundle[b].texMods[tm].scroll, + ( float * ) tess.svars.texcoords[b] ); + break; + + case TMOD_SCALE: + RB_CalcScaleTexCoords( pStage->bundle[b].texMods[tm].scale, + ( float * ) tess.svars.texcoords[b] ); + break; + + case TMOD_STRETCH: + RB_CalcStretchTexCoords( &pStage->bundle[b].texMods[tm].wave, + ( float * ) tess.svars.texcoords[b] ); + break; + + case TMOD_TRANSFORM: + RB_CalcTransformTexCoords( &pStage->bundle[b].texMods[tm], + ( float * ) tess.svars.texcoords[b] ); + break; + + case TMOD_ROTATE: + RB_CalcRotateTexCoords( pStage->bundle[b].texMods[tm].rotateSpeed, + ( float * ) tess.svars.texcoords[b] ); + break; + + default: + ri.Error( ERR_DROP, "ERROR: unknown texmod '%d' in shader '%s'", pStage->bundle[b].texMods[tm].type, tess.shader->name ); + break; + } + } + } +} + +/* +** RB_IterateStagesGeneric +*/ +static void RB_IterateStagesGeneric( shaderCommands_t *input ) +{ + int stage; + + for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) + { + shaderStage_t *pStage = tess.xstages[stage]; + + if ( !pStage ) + { + break; + } + + ComputeColors( pStage ); + ComputeTexCoords( pStage ); + + if ( !setArraysOnce ) + { + qglEnableClientState( GL_COLOR_ARRAY ); + qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, input->svars.colors ); + } + + // + // do multitexture + // + if ( pStage->bundle[1].image[0] != 0 ) + { + DrawMultitextured( input, stage ); + } + else + { + if ( !setArraysOnce ) + { + qglTexCoordPointer( 2, GL_FLOAT, 0, input->svars.texcoords[0] ); + } + + // + // set state + // + if ( pStage->bundle[0].vertexLightmap && ( (r_vertexLight->integer && !r_uiFullScreen->integer) || glConfig.hardwareType == GLHW_PERMEDIA2 ) && r_lightmap->integer ) + { + GL_Bind( tr.whiteImage ); + } + else + R_BindAnimatedImage( &pStage->bundle[0] ); + + GL_State( pStage->stateBits ); + + // + // draw + // + R_DrawElements( input->numIndexes, input->indexes ); + } + // allow skipping out to show just lightmaps during development + if ( r_lightmap->integer && ( pStage->bundle[0].isLightmap || pStage->bundle[1].isLightmap || pStage->bundle[0].vertexLightmap ) ) + { + break; + } + } +} + + +/* +** RB_StageIteratorGeneric +*/ +void RB_StageIteratorGeneric( void ) +{ + shaderCommands_t *input; + shader_t *shader; + + input = &tess; + shader = input->shader; + + RB_DeformTessGeometry(); + + // + // log this call + // + if ( r_logFile->integer ) + { + // don't just call LogComment, or we will get + // a call to va() every frame! + GLimp_LogComment( va("--- RB_StageIteratorGeneric( %s ) ---\n", tess.shader->name) ); + } + + // + // set face culling appropriately + // + GL_Cull( shader->cullType ); + + // set polygon offset if necessary + if ( shader->polygonOffset ) + { + qglEnable( GL_POLYGON_OFFSET_FILL ); + qglPolygonOffset( r_offsetFactor->value, r_offsetUnits->value ); + } + + // + // if there is only a single pass then we can enable color + // and texture arrays before we compile, otherwise we need + // to avoid compiling those arrays since they will change + // during multipass rendering + // + if ( tess.numPasses > 1 || shader->multitextureEnv ) + { + setArraysOnce = qfalse; + qglDisableClientState (GL_COLOR_ARRAY); + qglDisableClientState (GL_TEXTURE_COORD_ARRAY); + } + else + { + setArraysOnce = qtrue; + + qglEnableClientState( GL_COLOR_ARRAY); + qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors ); + + qglEnableClientState( GL_TEXTURE_COORD_ARRAY); + qglTexCoordPointer( 2, GL_FLOAT, 0, tess.svars.texcoords[0] ); + } + + // + // lock XYZ + // + qglVertexPointer (3, GL_FLOAT, 16, input->xyz); // padded for SIMD + if (qglLockArraysEXT) + { + qglLockArraysEXT(0, input->numVertexes); + GLimp_LogComment( "glLockArraysEXT\n" ); + } + + // + // enable color and texcoord arrays after the lock if necessary + // + if ( !setArraysOnce ) + { + qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); + qglEnableClientState( GL_COLOR_ARRAY ); + } + + // + // call shader function + // + RB_IterateStagesGeneric( input ); + + // + // now do any dynamic lighting needed + // + if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE + && !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) ) { + ProjectDlightTexture(); + } + + // + // now do fog + // + if ( tess.fogNum && tess.shader->fogPass ) { + RB_FogPass(); + } + + // + // unlock arrays + // + if (qglUnlockArraysEXT) + { + qglUnlockArraysEXT(); + GLimp_LogComment( "glUnlockArraysEXT\n" ); + } + + // + // reset polygon offset + // + if ( shader->polygonOffset ) + { + qglDisable( GL_POLYGON_OFFSET_FILL ); + } +} + + +/* +** RB_StageIteratorVertexLitTexture +*/ +void RB_StageIteratorVertexLitTexture( void ) +{ + shaderCommands_t *input; + shader_t *shader; + + input = &tess; + shader = input->shader; + + // + // compute colors + // + RB_CalcDiffuseColor( ( unsigned char * ) tess.svars.colors ); + + // + // log this call + // + if ( r_logFile->integer ) + { + // don't just call LogComment, or we will get + // a call to va() every frame! + GLimp_LogComment( va("--- RB_StageIteratorVertexLitTexturedUnfogged( %s ) ---\n", tess.shader->name) ); + } + + // + // set face culling appropriately + // + GL_Cull( shader->cullType ); + + // + // set arrays and lock + // + qglEnableClientState( GL_COLOR_ARRAY); + qglEnableClientState( GL_TEXTURE_COORD_ARRAY); + + qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.svars.colors ); + qglTexCoordPointer( 2, GL_FLOAT, 16, tess.texCoords[0][0] ); + qglVertexPointer (3, GL_FLOAT, 16, input->xyz); + + if ( qglLockArraysEXT ) + { + qglLockArraysEXT(0, input->numVertexes); + GLimp_LogComment( "glLockArraysEXT\n" ); + } + + // + // call special shade routine + // + R_BindAnimatedImage( &tess.xstages[0]->bundle[0] ); + GL_State( tess.xstages[0]->stateBits ); + R_DrawElements( input->numIndexes, input->indexes ); + + // + // now do any dynamic lighting needed + // + if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE ) { + ProjectDlightTexture(); + } + + // + // now do fog + // + if ( tess.fogNum && tess.shader->fogPass ) { + RB_FogPass(); + } + + // + // unlock arrays + // + if (qglUnlockArraysEXT) + { + qglUnlockArraysEXT(); + GLimp_LogComment( "glUnlockArraysEXT\n" ); + } +} + +//define REPLACE_MODE + +void RB_StageIteratorLightmappedMultitexture( void ) { + shaderCommands_t *input; + shader_t *shader; + + input = &tess; + shader = input->shader; + + // + // log this call + // + if ( r_logFile->integer ) { + // don't just call LogComment, or we will get + // a call to va() every frame! + GLimp_LogComment( va("--- RB_StageIteratorLightmappedMultitexture( %s ) ---\n", tess.shader->name) ); + } + + // + // set face culling appropriately + // + GL_Cull( shader->cullType ); + + // + // set color, pointers, and lock + // + GL_State( GLS_DEFAULT ); + qglVertexPointer( 3, GL_FLOAT, 16, input->xyz ); + +#ifdef REPLACE_MODE + qglDisableClientState( GL_COLOR_ARRAY ); + qglColor3f( 1, 1, 1 ); + qglShadeModel( GL_FLAT ); +#else + qglEnableClientState( GL_COLOR_ARRAY ); + qglColorPointer( 4, GL_UNSIGNED_BYTE, 0, tess.constantColor255 ); +#endif + + // + // select base stage + // + GL_SelectTexture( 0 ); + + qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); + R_BindAnimatedImage( &tess.xstages[0]->bundle[0] ); + qglTexCoordPointer( 2, GL_FLOAT, 16, tess.texCoords[0][0] ); + + // + // configure second stage + // + GL_SelectTexture( 1 ); + qglEnable( GL_TEXTURE_2D ); + if ( r_lightmap->integer ) { + GL_TexEnv( GL_REPLACE ); + } else { + GL_TexEnv( GL_MODULATE ); + } + R_BindAnimatedImage( &tess.xstages[0]->bundle[1] ); + qglEnableClientState( GL_TEXTURE_COORD_ARRAY ); + qglTexCoordPointer( 2, GL_FLOAT, 16, tess.texCoords[0][1] ); + + // + // lock arrays + // + if ( qglLockArraysEXT ) { + qglLockArraysEXT(0, input->numVertexes); + GLimp_LogComment( "glLockArraysEXT\n" ); + } + + R_DrawElements( input->numIndexes, input->indexes ); + + // + // disable texturing on TEXTURE1, then select TEXTURE0 + // + qglDisable( GL_TEXTURE_2D ); + qglDisableClientState( GL_TEXTURE_COORD_ARRAY ); + + GL_SelectTexture( 0 ); +#ifdef REPLACE_MODE + GL_TexEnv( GL_MODULATE ); + qglShadeModel( GL_SMOOTH ); +#endif + + // + // now do any dynamic lighting needed + // + if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE ) { + ProjectDlightTexture(); + } + + // + // now do fog + // + if ( tess.fogNum && tess.shader->fogPass ) { + RB_FogPass(); + } + + // + // unlock arrays + // + if ( qglUnlockArraysEXT ) { + qglUnlockArraysEXT(); + GLimp_LogComment( "glUnlockArraysEXT\n" ); + } +} + +/* +** RB_EndSurface +*/ +void RB_EndSurface( void ) { + shaderCommands_t *input; + + input = &tess; + + if (input->numIndexes == 0) { + return; + } + + if (input->indexes[SHADER_MAX_INDEXES-1] != 0) { + ri.Error (ERR_DROP, "RB_EndSurface() - SHADER_MAX_INDEXES hit"); + } + if (input->xyz[SHADER_MAX_VERTEXES-1][0] != 0) { + ri.Error (ERR_DROP, "RB_EndSurface() - SHADER_MAX_VERTEXES hit"); + } + + if ( tess.shader == tr.shadowShader ) { + RB_ShadowTessEnd(); + return; + } + + // for debugging of sort order issues, stop rendering after a given sort value + if ( r_debugSort->integer && r_debugSort->integer < tess.shader->sort ) { + return; + } + + // + // update performance counters + // + backEnd.pc.c_shaders++; + backEnd.pc.c_vertexes += tess.numVertexes; + backEnd.pc.c_indexes += tess.numIndexes; + backEnd.pc.c_totalIndexes += tess.numIndexes * tess.numPasses; + + // + // call off to shader specific tess end function + // + tess.currentStageIteratorFunc(); + + // + // draw debugging stuff + // + if ( r_showtris->integer ) { + DrawTris (input); + } + if ( r_shownormals->integer ) { + DrawNormals (input); + } + // clear shader so we can tell we don't have any unclosed surfaces + tess.numIndexes = 0; + + GLimp_LogComment( "----------\n" ); +} + diff --git a/src/renderergl1/tr_shade_calc.c b/src/renderergl1/tr_shade_calc.c new file mode 100644 index 00000000..58ec6f45 --- /dev/null +++ b/src/renderergl1/tr_shade_calc.c @@ -0,0 +1,1217 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_shade_calc.c + +#include "tr_local.h" +#if idppc_altivec && !defined(MACOS_X) +#include +#endif + + +#define WAVEVALUE( table, base, amplitude, phase, freq ) ((base) + table[ ri.ftol( ( ( (phase) + tess.shaderTime * (freq) ) * FUNCTABLE_SIZE ) ) & FUNCTABLE_MASK ] * (amplitude)) + +static float *TableForFunc( genFunc_t func ) +{ + switch ( func ) + { + case GF_SIN: + return tr.sinTable; + case GF_TRIANGLE: + return tr.triangleTable; + case GF_SQUARE: + return tr.squareTable; + case GF_SAWTOOTH: + return tr.sawToothTable; + case GF_INVERSE_SAWTOOTH: + return tr.inverseSawToothTable; + case GF_NONE: + default: + break; + } + + ri.Error( ERR_DROP, "TableForFunc called with invalid function '%d' in shader '%s'", func, tess.shader->name ); + return NULL; +} + +/* +** EvalWaveForm +** +** Evaluates a given waveForm_t, referencing backEnd.refdef.time directly +*/ +static float EvalWaveForm( const waveForm_t *wf ) +{ + float *table; + + table = TableForFunc( wf->func ); + + return WAVEVALUE( table, wf->base, wf->amplitude, wf->phase, wf->frequency ); +} + +static float EvalWaveFormClamped( const waveForm_t *wf ) +{ + float glow = EvalWaveForm( wf ); + + if ( glow < 0 ) + { + return 0; + } + + if ( glow > 1 ) + { + return 1; + } + + return glow; +} + +/* +** RB_CalcStretchTexCoords +*/ +void RB_CalcStretchTexCoords( const waveForm_t *wf, float *st ) +{ + float p; + texModInfo_t tmi; + + p = 1.0f / EvalWaveForm( wf ); + + tmi.matrix[0][0] = p; + tmi.matrix[1][0] = 0; + tmi.translate[0] = 0.5f - 0.5f * p; + + tmi.matrix[0][1] = 0; + tmi.matrix[1][1] = p; + tmi.translate[1] = 0.5f - 0.5f * p; + + RB_CalcTransformTexCoords( &tmi, st ); +} + +/* +==================================================================== + +DEFORMATIONS + +==================================================================== +*/ + +/* +======================== +RB_CalcDeformVertexes + +======================== +*/ +void RB_CalcDeformVertexes( deformStage_t *ds ) +{ + int i; + vec3_t offset; + float scale; + float *xyz = ( float * ) tess.xyz; + float *normal = ( float * ) tess.normal; + float *table; + + if ( ds->deformationWave.frequency == 0 ) + { + scale = EvalWaveForm( &ds->deformationWave ); + + for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 ) + { + VectorScale( normal, scale, offset ); + + xyz[0] += offset[0]; + xyz[1] += offset[1]; + xyz[2] += offset[2]; + } + } + else + { + table = TableForFunc( ds->deformationWave.func ); + + for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 ) + { + float off = ( xyz[0] + xyz[1] + xyz[2] ) * ds->deformationSpread; + + scale = WAVEVALUE( table, ds->deformationWave.base, + ds->deformationWave.amplitude, + ds->deformationWave.phase + off, + ds->deformationWave.frequency ); + + VectorScale( normal, scale, offset ); + + xyz[0] += offset[0]; + xyz[1] += offset[1]; + xyz[2] += offset[2]; + } + } +} + +/* +========================= +RB_CalcDeformNormals + +Wiggle the normals for wavy environment mapping +========================= +*/ +void RB_CalcDeformNormals( deformStage_t *ds ) { + int i; + float scale; + float *xyz = ( float * ) tess.xyz; + float *normal = ( float * ) tess.normal; + + for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 ) { + scale = 0.98f; + scale = R_NoiseGet4f( xyz[0] * scale, xyz[1] * scale, xyz[2] * scale, + tess.shaderTime * ds->deformationWave.frequency ); + normal[ 0 ] += ds->deformationWave.amplitude * scale; + + scale = 0.98f; + scale = R_NoiseGet4f( 100 + xyz[0] * scale, xyz[1] * scale, xyz[2] * scale, + tess.shaderTime * ds->deformationWave.frequency ); + normal[ 1 ] += ds->deformationWave.amplitude * scale; + + scale = 0.98f; + scale = R_NoiseGet4f( 200 + xyz[0] * scale, xyz[1] * scale, xyz[2] * scale, + tess.shaderTime * ds->deformationWave.frequency ); + normal[ 2 ] += ds->deformationWave.amplitude * scale; + + VectorNormalizeFast( normal ); + } +} + +/* +======================== +RB_CalcBulgeVertexes + +======================== +*/ +void RB_CalcBulgeVertexes( deformStage_t *ds ) { + int i; + const float *st = ( const float * ) tess.texCoords[0]; + float *xyz = ( float * ) tess.xyz; + float *normal = ( float * ) tess.normal; + float now; + + now = backEnd.refdef.time * ds->bulgeSpeed * 0.001f; + + for ( i = 0; i < tess.numVertexes; i++, xyz += 4, st += 4, normal += 4 ) { + int off; + float scale; + + off = (float)( FUNCTABLE_SIZE / (M_PI*2) ) * ( st[0] * ds->bulgeWidth + now ); + + scale = tr.sinTable[ off & FUNCTABLE_MASK ] * ds->bulgeHeight; + + xyz[0] += normal[0] * scale; + xyz[1] += normal[1] * scale; + xyz[2] += normal[2] * scale; + } +} + + +/* +====================== +RB_CalcMoveVertexes + +A deformation that can move an entire surface along a wave path +====================== +*/ +void RB_CalcMoveVertexes( deformStage_t *ds ) { + int i; + float *xyz; + float *table; + float scale; + vec3_t offset; + + table = TableForFunc( ds->deformationWave.func ); + + scale = WAVEVALUE( table, ds->deformationWave.base, + ds->deformationWave.amplitude, + ds->deformationWave.phase, + ds->deformationWave.frequency ); + + VectorScale( ds->moveVector, scale, offset ); + + xyz = ( float * ) tess.xyz; + for ( i = 0; i < tess.numVertexes; i++, xyz += 4 ) { + VectorAdd( xyz, offset, xyz ); + } +} + + +/* +============= +DeformText + +Change a polygon into a bunch of text polygons +============= +*/ +void DeformText( const char *text ) { + int i; + vec3_t origin, width, height; + int len; + int ch; + byte color[4]; + float bottom, top; + vec3_t mid; + + height[0] = 0; + height[1] = 0; + height[2] = -1; + CrossProduct( tess.normal[0], height, width ); + + // find the midpoint of the box + VectorClear( mid ); + bottom = 999999; + top = -999999; + for ( i = 0 ; i < 4 ; i++ ) { + VectorAdd( tess.xyz[i], mid, mid ); + if ( tess.xyz[i][2] < bottom ) { + bottom = tess.xyz[i][2]; + } + if ( tess.xyz[i][2] > top ) { + top = tess.xyz[i][2]; + } + } + VectorScale( mid, 0.25f, origin ); + + // determine the individual character size + height[0] = 0; + height[1] = 0; + height[2] = ( top - bottom ) * 0.5f; + + VectorScale( width, height[2] * -0.75f, width ); + + // determine the starting position + len = strlen( text ); + VectorMA( origin, (len-1), width, origin ); + + // clear the shader indexes + tess.numIndexes = 0; + tess.numVertexes = 0; + + color[0] = color[1] = color[2] = color[3] = 255; + + // draw each character + for ( i = 0 ; i < len ; i++ ) { + ch = text[i]; + ch &= 255; + + if ( ch != ' ' ) { + int row, col; + float frow, fcol, size; + + row = ch>>4; + col = ch&15; + + frow = row*0.0625f; + fcol = col*0.0625f; + size = 0.0625f; + + RB_AddQuadStampExt( origin, width, height, color, fcol, frow, fcol + size, frow + size ); + } + VectorMA( origin, -2, width, origin ); + } +} + +/* +================== +GlobalVectorToLocal +================== +*/ +static void GlobalVectorToLocal( const vec3_t in, vec3_t out ) { + out[0] = DotProduct( in, backEnd.or.axis[0] ); + out[1] = DotProduct( in, backEnd.or.axis[1] ); + out[2] = DotProduct( in, backEnd.or.axis[2] ); +} + +/* +===================== +AutospriteDeform + +Assuming all the triangles for this shader are independant +quads, rebuild them as forward facing sprites +===================== +*/ +static void AutospriteDeform( void ) { + int i; + int oldVerts; + float *xyz; + vec3_t mid, delta; + float radius; + vec3_t left, up; + vec3_t leftDir, upDir; + + if ( tess.numVertexes & 3 ) { + ri.Printf( PRINT_WARNING, "Autosprite shader %s had odd vertex count\n", tess.shader->name ); + } + if ( tess.numIndexes != ( tess.numVertexes >> 2 ) * 6 ) { + ri.Printf( PRINT_WARNING, "Autosprite shader %s had odd index count\n", tess.shader->name ); + } + + oldVerts = tess.numVertexes; + tess.numVertexes = 0; + tess.numIndexes = 0; + + if ( backEnd.currentEntity != &tr.worldEntity ) { + GlobalVectorToLocal( backEnd.viewParms.or.axis[1], leftDir ); + GlobalVectorToLocal( backEnd.viewParms.or.axis[2], upDir ); + } else { + VectorCopy( backEnd.viewParms.or.axis[1], leftDir ); + VectorCopy( backEnd.viewParms.or.axis[2], upDir ); + } + + for ( i = 0 ; i < oldVerts ; i+=4 ) { + // find the midpoint + xyz = tess.xyz[i]; + + mid[0] = 0.25f * (xyz[0] + xyz[4] + xyz[8] + xyz[12]); + mid[1] = 0.25f * (xyz[1] + xyz[5] + xyz[9] + xyz[13]); + mid[2] = 0.25f * (xyz[2] + xyz[6] + xyz[10] + xyz[14]); + + VectorSubtract( xyz, mid, delta ); + radius = VectorLength( delta ) * 0.707f; // / sqrt(2) + + VectorScale( leftDir, radius, left ); + VectorScale( upDir, radius, up ); + + if ( backEnd.viewParms.isMirror ) { + VectorSubtract( vec3_origin, left, left ); + } + + // compensate for scale in the axes if necessary + if ( backEnd.currentEntity->e.nonNormalizedAxes ) { + float axisLength; + axisLength = VectorLength( backEnd.currentEntity->e.axis[0] ); + if ( !axisLength ) { + axisLength = 0; + } else { + axisLength = 1.0f / axisLength; + } + VectorScale(left, axisLength, left); + VectorScale(up, axisLength, up); + } + + RB_AddQuadStamp( mid, left, up, tess.vertexColors[i] ); + } +} + + +/* +===================== +Autosprite2Deform + +Autosprite2 will pivot a rectangular quad along the center of its long axis +===================== +*/ +int edgeVerts[6][2] = { + { 0, 1 }, + { 0, 2 }, + { 0, 3 }, + { 1, 2 }, + { 1, 3 }, + { 2, 3 } +}; + +static void Autosprite2Deform( void ) { + int i, j, k; + int indexes; + float *xyz; + vec3_t forward; + + if ( tess.numVertexes & 3 ) { + ri.Printf( PRINT_WARNING, "Autosprite2 shader %s had odd vertex count", tess.shader->name ); + } + if ( tess.numIndexes != ( tess.numVertexes >> 2 ) * 6 ) { + ri.Printf( PRINT_WARNING, "Autosprite2 shader %s had odd index count", tess.shader->name ); + } + + if ( backEnd.currentEntity != &tr.worldEntity ) { + GlobalVectorToLocal( backEnd.viewParms.or.axis[0], forward ); + } else { + VectorCopy( backEnd.viewParms.or.axis[0], forward ); + } + + // this is a lot of work for two triangles... + // we could precalculate a lot of it is an issue, but it would mess up + // the shader abstraction + for ( i = 0, indexes = 0 ; i < tess.numVertexes ; i+=4, indexes+=6 ) { + float lengths[2]; + int nums[2]; + vec3_t mid[2]; + vec3_t major, minor; + float *v1, *v2; + + // find the midpoint + xyz = tess.xyz[i]; + + // identify the two shortest edges + nums[0] = nums[1] = 0; + lengths[0] = lengths[1] = 999999; + + for ( j = 0 ; j < 6 ; j++ ) { + float l; + vec3_t temp; + + v1 = xyz + 4 * edgeVerts[j][0]; + v2 = xyz + 4 * edgeVerts[j][1]; + + VectorSubtract( v1, v2, temp ); + + l = DotProduct( temp, temp ); + if ( l < lengths[0] ) { + nums[1] = nums[0]; + lengths[1] = lengths[0]; + nums[0] = j; + lengths[0] = l; + } else if ( l < lengths[1] ) { + nums[1] = j; + lengths[1] = l; + } + } + + for ( j = 0 ; j < 2 ; j++ ) { + v1 = xyz + 4 * edgeVerts[nums[j]][0]; + v2 = xyz + 4 * edgeVerts[nums[j]][1]; + + mid[j][0] = 0.5f * (v1[0] + v2[0]); + mid[j][1] = 0.5f * (v1[1] + v2[1]); + mid[j][2] = 0.5f * (v1[2] + v2[2]); + } + + // find the vector of the major axis + VectorSubtract( mid[1], mid[0], major ); + + // cross this with the view direction to get minor axis + CrossProduct( major, forward, minor ); + VectorNormalize( minor ); + + // re-project the points + for ( j = 0 ; j < 2 ; j++ ) { + float l; + + v1 = xyz + 4 * edgeVerts[nums[j]][0]; + v2 = xyz + 4 * edgeVerts[nums[j]][1]; + + l = 0.5 * sqrt( lengths[j] ); + + // we need to see which direction this edge + // is used to determine direction of projection + for ( k = 0 ; k < 5 ; k++ ) { + if ( tess.indexes[ indexes + k ] == i + edgeVerts[nums[j]][0] + && tess.indexes[ indexes + k + 1 ] == i + edgeVerts[nums[j]][1] ) { + break; + } + } + + if ( k == 5 ) { + VectorMA( mid[j], l, minor, v1 ); + VectorMA( mid[j], -l, minor, v2 ); + } else { + VectorMA( mid[j], -l, minor, v1 ); + VectorMA( mid[j], l, minor, v2 ); + } + } + } +} + + +/* +===================== +RB_DeformTessGeometry + +===================== +*/ +void RB_DeformTessGeometry( void ) { + int i; + deformStage_t *ds; + + for ( i = 0 ; i < tess.shader->numDeforms ; i++ ) { + ds = &tess.shader->deforms[ i ]; + + switch ( ds->deformation ) { + case DEFORM_NONE: + break; + case DEFORM_NORMALS: + RB_CalcDeformNormals( ds ); + break; + case DEFORM_WAVE: + RB_CalcDeformVertexes( ds ); + break; + case DEFORM_BULGE: + RB_CalcBulgeVertexes( ds ); + break; + case DEFORM_MOVE: + RB_CalcMoveVertexes( ds ); + break; + case DEFORM_PROJECTION_SHADOW: + RB_ProjectionShadowDeform(); + break; + case DEFORM_AUTOSPRITE: + AutospriteDeform(); + break; + case DEFORM_AUTOSPRITE2: + Autosprite2Deform(); + break; + case DEFORM_TEXT0: + case DEFORM_TEXT1: + case DEFORM_TEXT2: + case DEFORM_TEXT3: + case DEFORM_TEXT4: + case DEFORM_TEXT5: + case DEFORM_TEXT6: + case DEFORM_TEXT7: + DeformText( backEnd.refdef.text[ds->deformation - DEFORM_TEXT0] ); + break; + } + } +} + +/* +==================================================================== + +COLORS + +==================================================================== +*/ + + +/* +** RB_CalcColorFromEntity +*/ +void RB_CalcColorFromEntity( unsigned char *dstColors ) +{ + int i; + int *pColors = ( int * ) dstColors; + int c; + + if ( !backEnd.currentEntity ) + return; + + c = * ( int * ) backEnd.currentEntity->e.shaderRGBA; + + for ( i = 0; i < tess.numVertexes; i++, pColors++ ) + { + *pColors = c; + } +} + +/* +** RB_CalcColorFromOneMinusEntity +*/ +void RB_CalcColorFromOneMinusEntity( unsigned char *dstColors ) +{ + int i; + int *pColors = ( int * ) dstColors; + unsigned char invModulate[4]; + int c; + + if ( !backEnd.currentEntity ) + return; + + invModulate[0] = 255 - backEnd.currentEntity->e.shaderRGBA[0]; + invModulate[1] = 255 - backEnd.currentEntity->e.shaderRGBA[1]; + invModulate[2] = 255 - backEnd.currentEntity->e.shaderRGBA[2]; + invModulate[3] = 255 - backEnd.currentEntity->e.shaderRGBA[3]; // this trashes alpha, but the AGEN block fixes it + + c = * ( int * ) invModulate; + + for ( i = 0; i < tess.numVertexes; i++, pColors++ ) + { + *pColors = c; + } +} + +/* +** RB_CalcAlphaFromEntity +*/ +void RB_CalcAlphaFromEntity( unsigned char *dstColors ) +{ + int i; + + if ( !backEnd.currentEntity ) + return; + + dstColors += 3; + + for ( i = 0; i < tess.numVertexes; i++, dstColors += 4 ) + { + *dstColors = backEnd.currentEntity->e.shaderRGBA[3]; + } +} + +/* +** RB_CalcAlphaFromOneMinusEntity +*/ +void RB_CalcAlphaFromOneMinusEntity( unsigned char *dstColors ) +{ + int i; + + if ( !backEnd.currentEntity ) + return; + + dstColors += 3; + + for ( i = 0; i < tess.numVertexes; i++, dstColors += 4 ) + { + *dstColors = 0xff - backEnd.currentEntity->e.shaderRGBA[3]; + } +} + +/* +** RB_CalcWaveColor +*/ +void RB_CalcWaveColor( const waveForm_t *wf, unsigned char *dstColors ) +{ + int i; + int v; + float glow; + int *colors = ( int * ) dstColors; + byte color[4]; + + + if ( wf->func == GF_NOISE ) { + glow = wf->base + R_NoiseGet4f( 0, 0, 0, ( tess.shaderTime + wf->phase ) * wf->frequency ) * wf->amplitude; + } else { + glow = EvalWaveForm( wf ) * tr.identityLight; + } + + if ( glow < 0 ) { + glow = 0; + } + else if ( glow > 1 ) { + glow = 1; + } + + v = ri.ftol(255 * glow); + color[0] = color[1] = color[2] = v; + color[3] = 255; + v = *(int *)color; + + for ( i = 0; i < tess.numVertexes; i++, colors++ ) { + *colors = v; + } +} + +/* +** RB_CalcWaveAlpha +*/ +void RB_CalcWaveAlpha( const waveForm_t *wf, unsigned char *dstColors ) +{ + int i; + int v; + float glow; + + glow = EvalWaveFormClamped( wf ); + + v = 255 * glow; + + for ( i = 0; i < tess.numVertexes; i++, dstColors += 4 ) + { + dstColors[3] = v; + } +} + +/* +** RB_CalcModulateColorsByFog +*/ +void RB_CalcModulateColorsByFog( unsigned char *colors ) { + int i; + float texCoords[SHADER_MAX_VERTEXES][2]; + + // calculate texcoords so we can derive density + // this is not wasted, because it would only have + // been previously called if the surface was opaque + RB_CalcFogTexCoords( texCoords[0] ); + + for ( i = 0; i < tess.numVertexes; i++, colors += 4 ) { + float f = 1.0 - R_FogFactor( texCoords[i][0], texCoords[i][1] ); + colors[0] *= f; + colors[1] *= f; + colors[2] *= f; + } +} + +/* +** RB_CalcModulateAlphasByFog +*/ +void RB_CalcModulateAlphasByFog( unsigned char *colors ) { + int i; + float texCoords[SHADER_MAX_VERTEXES][2]; + + // calculate texcoords so we can derive density + // this is not wasted, because it would only have + // been previously called if the surface was opaque + RB_CalcFogTexCoords( texCoords[0] ); + + for ( i = 0; i < tess.numVertexes; i++, colors += 4 ) { + float f = 1.0 - R_FogFactor( texCoords[i][0], texCoords[i][1] ); + colors[3] *= f; + } +} + +/* +** RB_CalcModulateRGBAsByFog +*/ +void RB_CalcModulateRGBAsByFog( unsigned char *colors ) { + int i; + float texCoords[SHADER_MAX_VERTEXES][2]; + + // calculate texcoords so we can derive density + // this is not wasted, because it would only have + // been previously called if the surface was opaque + RB_CalcFogTexCoords( texCoords[0] ); + + for ( i = 0; i < tess.numVertexes; i++, colors += 4 ) { + float f = 1.0 - R_FogFactor( texCoords[i][0], texCoords[i][1] ); + colors[0] *= f; + colors[1] *= f; + colors[2] *= f; + colors[3] *= f; + } +} + + +/* +==================================================================== + +TEX COORDS + +==================================================================== +*/ + +/* +======================== +RB_CalcFogTexCoords + +To do the clipped fog plane really correctly, we should use +projected textures, but I don't trust the drivers and it +doesn't fit our shader data. +======================== +*/ +void RB_CalcFogTexCoords( float *st ) { + int i; + float *v; + float s, t; + float eyeT; + qboolean eyeOutside; + fog_t *fog; + vec3_t local; + vec4_t fogDistanceVector, fogDepthVector = {0, 0, 0, 0}; + + fog = tr.world->fogs + tess.fogNum; + + // all fogging distance is based on world Z units + VectorSubtract( backEnd.or.origin, backEnd.viewParms.or.origin, local ); + fogDistanceVector[0] = -backEnd.or.modelMatrix[2]; + fogDistanceVector[1] = -backEnd.or.modelMatrix[6]; + fogDistanceVector[2] = -backEnd.or.modelMatrix[10]; + fogDistanceVector[3] = DotProduct( local, backEnd.viewParms.or.axis[0] ); + + // scale the fog vectors based on the fog's thickness + fogDistanceVector[0] *= fog->tcScale; + fogDistanceVector[1] *= fog->tcScale; + fogDistanceVector[2] *= fog->tcScale; + fogDistanceVector[3] *= fog->tcScale; + + // rotate the gradient vector for this orientation + if ( fog->hasSurface ) { + fogDepthVector[0] = fog->surface[0] * backEnd.or.axis[0][0] + + fog->surface[1] * backEnd.or.axis[0][1] + fog->surface[2] * backEnd.or.axis[0][2]; + fogDepthVector[1] = fog->surface[0] * backEnd.or.axis[1][0] + + fog->surface[1] * backEnd.or.axis[1][1] + fog->surface[2] * backEnd.or.axis[1][2]; + fogDepthVector[2] = fog->surface[0] * backEnd.or.axis[2][0] + + fog->surface[1] * backEnd.or.axis[2][1] + fog->surface[2] * backEnd.or.axis[2][2]; + fogDepthVector[3] = -fog->surface[3] + DotProduct( backEnd.or.origin, fog->surface ); + + eyeT = DotProduct( backEnd.or.viewOrigin, fogDepthVector ) + fogDepthVector[3]; + } else { + eyeT = 1; // non-surface fog always has eye inside + } + + // see if the viewpoint is outside + // this is needed for clipping distance even for constant fog + + if ( eyeT < 0 ) { + eyeOutside = qtrue; + } else { + eyeOutside = qfalse; + } + + fogDistanceVector[3] += 1.0/512; + + // calculate density for each point + for (i = 0, v = tess.xyz[0] ; i < tess.numVertexes ; i++, v += 4) { + // calculate the length in fog + s = DotProduct( v, fogDistanceVector ) + fogDistanceVector[3]; + t = DotProduct( v, fogDepthVector ) + fogDepthVector[3]; + + // partially clipped fogs use the T axis + if ( eyeOutside ) { + if ( t < 1.0 ) { + t = 1.0/32; // point is outside, so no fogging + } else { + t = 1.0/32 + 30.0/32 * t / ( t - eyeT ); // cut the distance at the fog plane + } + } else { + if ( t < 0 ) { + t = 1.0/32; // point is outside, so no fogging + } else { + t = 31.0/32; + } + } + + st[0] = s; + st[1] = t; + st += 2; + } +} + + + +/* +** RB_CalcEnvironmentTexCoords +*/ +void RB_CalcEnvironmentTexCoords( float *st ) +{ + int i; + float *v, *normal; + vec3_t viewer, reflected; + float d; + + v = tess.xyz[0]; + normal = tess.normal[0]; + + for (i = 0 ; i < tess.numVertexes ; i++, v += 4, normal += 4, st += 2 ) + { + VectorSubtract (backEnd.or.viewOrigin, v, viewer); + VectorNormalizeFast (viewer); + + d = DotProduct (normal, viewer); + + reflected[0] = normal[0]*2*d - viewer[0]; + reflected[1] = normal[1]*2*d - viewer[1]; + reflected[2] = normal[2]*2*d - viewer[2]; + + st[0] = 0.5 + reflected[1] * 0.5; + st[1] = 0.5 - reflected[2] * 0.5; + } +} + +/* +** RB_CalcTurbulentTexCoords +*/ +void RB_CalcTurbulentTexCoords( const waveForm_t *wf, float *st ) +{ + int i; + float now; + + now = ( wf->phase + tess.shaderTime * wf->frequency ); + + for ( i = 0; i < tess.numVertexes; i++, st += 2 ) + { + float s = st[0]; + float t = st[1]; + + st[0] = s + tr.sinTable[ ( ( int ) ( ( ( tess.xyz[i][0] + tess.xyz[i][2] )* 1.0/128 * 0.125 + now ) * FUNCTABLE_SIZE ) ) & ( FUNCTABLE_MASK ) ] * wf->amplitude; + st[1] = t + tr.sinTable[ ( ( int ) ( ( tess.xyz[i][1] * 1.0/128 * 0.125 + now ) * FUNCTABLE_SIZE ) ) & ( FUNCTABLE_MASK ) ] * wf->amplitude; + } +} + +/* +** RB_CalcScaleTexCoords +*/ +void RB_CalcScaleTexCoords( const float scale[2], float *st ) +{ + int i; + + for ( i = 0; i < tess.numVertexes; i++, st += 2 ) + { + st[0] *= scale[0]; + st[1] *= scale[1]; + } +} + +/* +** RB_CalcScrollTexCoords +*/ +void RB_CalcScrollTexCoords( const float scrollSpeed[2], float *st ) +{ + int i; + float timeScale = tess.shaderTime; + float adjustedScrollS, adjustedScrollT; + + adjustedScrollS = scrollSpeed[0] * timeScale; + adjustedScrollT = scrollSpeed[1] * timeScale; + + // clamp so coordinates don't continuously get larger, causing problems + // with hardware limits + adjustedScrollS = adjustedScrollS - floor( adjustedScrollS ); + adjustedScrollT = adjustedScrollT - floor( adjustedScrollT ); + + for ( i = 0; i < tess.numVertexes; i++, st += 2 ) + { + st[0] += adjustedScrollS; + st[1] += adjustedScrollT; + } +} + +/* +** RB_CalcTransformTexCoords +*/ +void RB_CalcTransformTexCoords( const texModInfo_t *tmi, float *st ) +{ + int i; + + for ( i = 0; i < tess.numVertexes; i++, st += 2 ) + { + float s = st[0]; + float t = st[1]; + + st[0] = s * tmi->matrix[0][0] + t * tmi->matrix[1][0] + tmi->translate[0]; + st[1] = s * tmi->matrix[0][1] + t * tmi->matrix[1][1] + tmi->translate[1]; + } +} + +/* +** RB_CalcRotateTexCoords +*/ +void RB_CalcRotateTexCoords( float degsPerSecond, float *st ) +{ + float timeScale = tess.shaderTime; + float degs; + int index; + float sinValue, cosValue; + texModInfo_t tmi; + + degs = -degsPerSecond * timeScale; + index = degs * ( FUNCTABLE_SIZE / 360.0f ); + + sinValue = tr.sinTable[ index & FUNCTABLE_MASK ]; + cosValue = tr.sinTable[ ( index + FUNCTABLE_SIZE / 4 ) & FUNCTABLE_MASK ]; + + tmi.matrix[0][0] = cosValue; + tmi.matrix[1][0] = -sinValue; + tmi.translate[0] = 0.5 - 0.5 * cosValue + 0.5 * sinValue; + + tmi.matrix[0][1] = sinValue; + tmi.matrix[1][1] = cosValue; + tmi.translate[1] = 0.5 - 0.5 * sinValue - 0.5 * cosValue; + + RB_CalcTransformTexCoords( &tmi, st ); +} + + +/* +** RB_CalcSpecularAlpha +** +** Calculates specular coefficient and places it in the alpha channel +*/ +vec3_t lightOrigin = { -960, 1980, 96 }; // FIXME: track dynamically + +void RB_CalcSpecularAlpha( unsigned char *alphas ) { + int i; + float *v, *normal; + vec3_t viewer, reflected; + float l, d; + int b; + vec3_t lightDir; + int numVertexes; + + v = tess.xyz[0]; + normal = tess.normal[0]; + + alphas += 3; + + numVertexes = tess.numVertexes; + for (i = 0 ; i < numVertexes ; i++, v += 4, normal += 4, alphas += 4) { + float ilength; + + VectorSubtract( lightOrigin, v, lightDir ); +// ilength = Q_rsqrt( DotProduct( lightDir, lightDir ) ); + VectorNormalizeFast( lightDir ); + + // calculate the specular color + d = DotProduct (normal, lightDir); +// d *= ilength; + + // we don't optimize for the d < 0 case since this tends to + // cause visual artifacts such as faceted "snapping" + reflected[0] = normal[0]*2*d - lightDir[0]; + reflected[1] = normal[1]*2*d - lightDir[1]; + reflected[2] = normal[2]*2*d - lightDir[2]; + + VectorSubtract (backEnd.or.viewOrigin, v, viewer); + ilength = Q_rsqrt( DotProduct( viewer, viewer ) ); + l = DotProduct (reflected, viewer); + l *= ilength; + + if (l < 0) { + b = 0; + } else { + l = l*l; + l = l*l; + b = l * 255; + if (b > 255) { + b = 255; + } + } + + *alphas = b; + } +} + +/* +** RB_CalcDiffuseColor +** +** The basic vertex lighting calc +*/ +#if idppc_altivec +static void RB_CalcDiffuseColor_altivec( unsigned char *colors ) +{ + int i; + float *v, *normal; + trRefEntity_t *ent; + int ambientLightInt; + vec3_t lightDir; + int numVertexes; + vector unsigned char vSel = VECCONST_UINT8(0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff); + vector float ambientLightVec; + vector float directedLightVec; + vector float lightDirVec; + vector float normalVec0, normalVec1; + vector float incomingVec0, incomingVec1, incomingVec2; + vector float zero, jVec; + vector signed int jVecInt; + vector signed short jVecShort; + vector unsigned char jVecChar, normalPerm; + ent = backEnd.currentEntity; + ambientLightInt = ent->ambientLightInt; + // A lot of this could be simplified if we made sure + // entities light info was 16-byte aligned. + jVecChar = vec_lvsl(0, ent->ambientLight); + ambientLightVec = vec_ld(0, (vector float *)ent->ambientLight); + jVec = vec_ld(11, (vector float *)ent->ambientLight); + ambientLightVec = vec_perm(ambientLightVec,jVec,jVecChar); + + jVecChar = vec_lvsl(0, ent->directedLight); + directedLightVec = vec_ld(0,(vector float *)ent->directedLight); + jVec = vec_ld(11,(vector float *)ent->directedLight); + directedLightVec = vec_perm(directedLightVec,jVec,jVecChar); + + jVecChar = vec_lvsl(0, ent->lightDir); + lightDirVec = vec_ld(0,(vector float *)ent->lightDir); + jVec = vec_ld(11,(vector float *)ent->lightDir); + lightDirVec = vec_perm(lightDirVec,jVec,jVecChar); + + zero = (vector float)vec_splat_s8(0); + VectorCopy( ent->lightDir, lightDir ); + + v = tess.xyz[0]; + normal = tess.normal[0]; + + normalPerm = vec_lvsl(0,normal); + numVertexes = tess.numVertexes; + for (i = 0 ; i < numVertexes ; i++, v += 4, normal += 4) { + normalVec0 = vec_ld(0,(vector float *)normal); + normalVec1 = vec_ld(11,(vector float *)normal); + normalVec0 = vec_perm(normalVec0,normalVec1,normalPerm); + incomingVec0 = vec_madd(normalVec0, lightDirVec, zero); + incomingVec1 = vec_sld(incomingVec0,incomingVec0,4); + incomingVec2 = vec_add(incomingVec0,incomingVec1); + incomingVec1 = vec_sld(incomingVec1,incomingVec1,4); + incomingVec2 = vec_add(incomingVec2,incomingVec1); + incomingVec0 = vec_splat(incomingVec2,0); + incomingVec0 = vec_max(incomingVec0,zero); + normalPerm = vec_lvsl(12,normal); + jVec = vec_madd(incomingVec0, directedLightVec, ambientLightVec); + jVecInt = vec_cts(jVec,0); // RGBx + jVecShort = vec_pack(jVecInt,jVecInt); // RGBxRGBx + jVecChar = vec_packsu(jVecShort,jVecShort); // RGBxRGBxRGBxRGBx + jVecChar = vec_sel(jVecChar,vSel,vSel); // RGBARGBARGBARGBA replace alpha with 255 + vec_ste((vector unsigned int)jVecChar,0,(unsigned int *)&colors[i*4]); // store color + } +} +#endif + +static void RB_CalcDiffuseColor_scalar( unsigned char *colors ) +{ + int i, j; + float *v, *normal; + float incoming; + trRefEntity_t *ent; + int ambientLightInt; + vec3_t ambientLight; + vec3_t lightDir; + vec3_t directedLight; + int numVertexes; + ent = backEnd.currentEntity; + ambientLightInt = ent->ambientLightInt; + VectorCopy( ent->ambientLight, ambientLight ); + VectorCopy( ent->directedLight, directedLight ); + VectorCopy( ent->lightDir, lightDir ); + + v = tess.xyz[0]; + normal = tess.normal[0]; + + numVertexes = tess.numVertexes; + for (i = 0 ; i < numVertexes ; i++, v += 4, normal += 4) { + incoming = DotProduct (normal, lightDir); + if ( incoming <= 0 ) { + *(int *)&colors[i*4] = ambientLightInt; + continue; + } + j = ri.ftol(ambientLight[0] + incoming * directedLight[0]); + if ( j > 255 ) { + j = 255; + } + colors[i*4+0] = j; + + j = ri.ftol(ambientLight[1] + incoming * directedLight[1]); + if ( j > 255 ) { + j = 255; + } + colors[i*4+1] = j; + + j = ri.ftol(ambientLight[2] + incoming * directedLight[2]); + if ( j > 255 ) { + j = 255; + } + colors[i*4+2] = j; + + colors[i*4+3] = 255; + } +} + +void RB_CalcDiffuseColor( unsigned char *colors ) +{ +#if idppc_altivec + if (com_altivec->integer) { + // must be in a seperate function or G3 systems will crash. + RB_CalcDiffuseColor_altivec( colors ); + return; + } +#endif + RB_CalcDiffuseColor_scalar( colors ); +} + diff --git a/src/renderergl1/tr_shader.c b/src/renderergl1/tr_shader.c new file mode 100644 index 00000000..8440dd63 --- /dev/null +++ b/src/renderergl1/tr_shader.c @@ -0,0 +1,3068 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +#include "tr_local.h" + +// tr_shader.c -- this file deals with the parsing and definition of shaders + +static char *s_shaderText; + +// the shader is parsed into these global variables, then copied into +// dynamically allocated memory if it is valid. +static shaderStage_t stages[MAX_SHADER_STAGES]; +static shader_t shader; +static texModInfo_t texMods[MAX_SHADER_STAGES][TR_MAX_TEXMODS]; + +#define FILE_HASH_SIZE 1024 +static shader_t* hashTable[FILE_HASH_SIZE]; + +#define MAX_SHADERTEXT_HASH 2048 +static char **shaderTextHashTable[MAX_SHADERTEXT_HASH]; + +/* +================ +return a hash value for the filename +================ +*/ +#ifdef __GNUCC__ + #warning TODO: check if long is ok here +#endif +static long generateHashValue( const char *fname, const int size ) { + int i; + long hash; + char letter; + + hash = 0; + i = 0; + while (fname[i] != '\0') { + letter = tolower(fname[i]); + if (letter =='.') break; // don't include extension + if (letter =='\\') letter = '/'; // damn path names + if (letter == PATH_SEP) letter = '/'; // damn path names + hash+=(long)(letter)*(i+119); + i++; + } + hash = (hash ^ (hash >> 10) ^ (hash >> 20)); + hash &= (size-1); + return hash; +} + +void R_RemapShader(const char *shaderName, const char *newShaderName, const char *timeOffset) { + char strippedName[MAX_QPATH]; + int hash; + shader_t *sh, *sh2; + qhandle_t h; + + sh = R_FindShaderByName( shaderName ); + if (sh == NULL || sh == tr.defaultShader) { + h = RE_RegisterShaderLightMap(shaderName, 0); + sh = R_GetShaderByHandle(h); + } + if (sh == NULL || sh == tr.defaultShader) { + ri.Printf( PRINT_WARNING, "WARNING: R_RemapShader: shader %s not found\n", shaderName ); + return; + } + + sh2 = R_FindShaderByName( newShaderName ); + if (sh2 == NULL || sh2 == tr.defaultShader) { + h = RE_RegisterShaderLightMap(newShaderName, 0); + sh2 = R_GetShaderByHandle(h); + } + + if (sh2 == NULL || sh2 == tr.defaultShader) { + ri.Printf( PRINT_WARNING, "WARNING: R_RemapShader: new shader %s not found\n", newShaderName ); + return; + } + + // remap all the shaders with the given name + // even tho they might have different lightmaps + COM_StripExtension(shaderName, strippedName, sizeof(strippedName)); + hash = generateHashValue(strippedName, FILE_HASH_SIZE); + for (sh = hashTable[hash]; sh; sh = sh->next) { + if (Q_stricmp(sh->name, strippedName) == 0) { + if (sh != sh2) { + sh->remappedShader = sh2; + } else { + sh->remappedShader = NULL; + } + } + } + if (timeOffset) { + sh2->timeOffset = atof(timeOffset); + } +} + +/* +=============== +ParseVector +=============== +*/ +static qboolean ParseVector( char **text, int count, float *v ) { + char *token; + int i; + + // FIXME: spaces are currently required after parens, should change parseext... + token = COM_ParseExt( text, qfalse ); + if ( strcmp( token, "(" ) ) { + ri.Printf( PRINT_WARNING, "WARNING: missing parenthesis in shader '%s'\n", shader.name ); + return qfalse; + } + + for ( i = 0 ; i < count ; i++ ) { + token = COM_ParseExt( text, qfalse ); + if ( !token[0] ) { + ri.Printf( PRINT_WARNING, "WARNING: missing vector element in shader '%s'\n", shader.name ); + return qfalse; + } + v[i] = atof( token ); + } + + token = COM_ParseExt( text, qfalse ); + if ( strcmp( token, ")" ) ) { + ri.Printf( PRINT_WARNING, "WARNING: missing parenthesis in shader '%s'\n", shader.name ); + return qfalse; + } + + return qtrue; +} + + +/* +=============== +NameToAFunc +=============== +*/ +static unsigned NameToAFunc( const char *funcname ) +{ + if ( !Q_stricmp( funcname, "GT0" ) ) + { + return GLS_ATEST_GT_0; + } + else if ( !Q_stricmp( funcname, "LT128" ) ) + { + return GLS_ATEST_LT_80; + } + else if ( !Q_stricmp( funcname, "GE128" ) ) + { + return GLS_ATEST_GE_80; + } + + ri.Printf( PRINT_WARNING, "WARNING: invalid alphaFunc name '%s' in shader '%s'\n", funcname, shader.name ); + return 0; +} + + +/* +=============== +NameToSrcBlendMode +=============== +*/ +static int NameToSrcBlendMode( const char *name ) +{ + if ( !Q_stricmp( name, "GL_ONE" ) ) + { + return GLS_SRCBLEND_ONE; + } + else if ( !Q_stricmp( name, "GL_ZERO" ) ) + { + return GLS_SRCBLEND_ZERO; + } + else if ( !Q_stricmp( name, "GL_DST_COLOR" ) ) + { + return GLS_SRCBLEND_DST_COLOR; + } + else if ( !Q_stricmp( name, "GL_ONE_MINUS_DST_COLOR" ) ) + { + return GLS_SRCBLEND_ONE_MINUS_DST_COLOR; + } + else if ( !Q_stricmp( name, "GL_SRC_ALPHA" ) ) + { + return GLS_SRCBLEND_SRC_ALPHA; + } + else if ( !Q_stricmp( name, "GL_ONE_MINUS_SRC_ALPHA" ) ) + { + return GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA; + } + else if ( !Q_stricmp( name, "GL_DST_ALPHA" ) ) + { + return GLS_SRCBLEND_DST_ALPHA; + } + else if ( !Q_stricmp( name, "GL_ONE_MINUS_DST_ALPHA" ) ) + { + return GLS_SRCBLEND_ONE_MINUS_DST_ALPHA; + } + else if ( !Q_stricmp( name, "GL_SRC_ALPHA_SATURATE" ) ) + { + return GLS_SRCBLEND_ALPHA_SATURATE; + } + + ri.Printf( PRINT_WARNING, "WARNING: unknown blend mode '%s' in shader '%s', substituting GL_ONE\n", name, shader.name ); + return GLS_SRCBLEND_ONE; +} + +/* +=============== +NameToDstBlendMode +=============== +*/ +static int NameToDstBlendMode( const char *name ) +{ + if ( !Q_stricmp( name, "GL_ONE" ) ) + { + return GLS_DSTBLEND_ONE; + } + else if ( !Q_stricmp( name, "GL_ZERO" ) ) + { + return GLS_DSTBLEND_ZERO; + } + else if ( !Q_stricmp( name, "GL_SRC_ALPHA" ) ) + { + return GLS_DSTBLEND_SRC_ALPHA; + } + else if ( !Q_stricmp( name, "GL_ONE_MINUS_SRC_ALPHA" ) ) + { + return GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; + } + else if ( !Q_stricmp( name, "GL_DST_ALPHA" ) ) + { + return GLS_DSTBLEND_DST_ALPHA; + } + else if ( !Q_stricmp( name, "GL_ONE_MINUS_DST_ALPHA" ) ) + { + return GLS_DSTBLEND_ONE_MINUS_DST_ALPHA; + } + else if ( !Q_stricmp( name, "GL_SRC_COLOR" ) ) + { + return GLS_DSTBLEND_SRC_COLOR; + } + else if ( !Q_stricmp( name, "GL_ONE_MINUS_SRC_COLOR" ) ) + { + return GLS_DSTBLEND_ONE_MINUS_SRC_COLOR; + } + + ri.Printf( PRINT_WARNING, "WARNING: unknown blend mode '%s' in shader '%s', substituting GL_ONE\n", name, shader.name ); + return GLS_DSTBLEND_ONE; +} + +/* +=============== +NameToGenFunc +=============== +*/ +static genFunc_t NameToGenFunc( const char *funcname ) +{ + if ( !Q_stricmp( funcname, "sin" ) ) + { + return GF_SIN; + } + else if ( !Q_stricmp( funcname, "square" ) ) + { + return GF_SQUARE; + } + else if ( !Q_stricmp( funcname, "triangle" ) ) + { + return GF_TRIANGLE; + } + else if ( !Q_stricmp( funcname, "sawtooth" ) ) + { + return GF_SAWTOOTH; + } + else if ( !Q_stricmp( funcname, "inversesawtooth" ) ) + { + return GF_INVERSE_SAWTOOTH; + } + else if ( !Q_stricmp( funcname, "noise" ) ) + { + return GF_NOISE; + } + + ri.Printf( PRINT_WARNING, "WARNING: invalid genfunc name '%s' in shader '%s'\n", funcname, shader.name ); + return GF_SIN; +} + + +/* +=================== +ParseWaveForm +=================== +*/ +static void ParseWaveForm( char **text, waveForm_t *wave ) +{ + char *token; + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); + return; + } + wave->func = NameToGenFunc( token ); + + // BASE, AMP, PHASE, FREQ + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); + return; + } + wave->base = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); + return; + } + wave->amplitude = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); + return; + } + wave->phase = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); + return; + } + wave->frequency = atof( token ); +} + + +/* +=================== +ParseTexMod +=================== +*/ +static void ParseTexMod( char *_text, shaderStage_t *stage ) +{ + const char *token; + char **text = &_text; + texModInfo_t *tmi; + + if ( stage->bundle[0].numTexMods == TR_MAX_TEXMODS ) { + ri.Error( ERR_DROP, "ERROR: too many tcMod stages in shader '%s'", shader.name ); + return; + } + + tmi = &stage->bundle[0].texMods[stage->bundle[0].numTexMods]; + stage->bundle[0].numTexMods++; + + token = COM_ParseExt( text, qfalse ); + + // + // turb + // + if ( !Q_stricmp( token, "turb" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb parms in shader '%s'\n", shader.name ); + return; + } + tmi->wave.base = atof( token ); + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name ); + return; + } + tmi->wave.amplitude = atof( token ); + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name ); + return; + } + tmi->wave.phase = atof( token ); + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name ); + return; + } + tmi->wave.frequency = atof( token ); + + tmi->type = TMOD_TURBULENT; + } + // + // scale + // + else if ( !Q_stricmp( token, "scale" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing scale parms in shader '%s'\n", shader.name ); + return; + } + tmi->scale[0] = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing scale parms in shader '%s'\n", shader.name ); + return; + } + tmi->scale[1] = atof( token ); + tmi->type = TMOD_SCALE; + } + // + // scroll + // + else if ( !Q_stricmp( token, "scroll" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing scale scroll parms in shader '%s'\n", shader.name ); + return; + } + tmi->scroll[0] = atof( token ); + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing scale scroll parms in shader '%s'\n", shader.name ); + return; + } + tmi->scroll[1] = atof( token ); + tmi->type = TMOD_SCROLL; + } + // + // stretch + // + else if ( !Q_stricmp( token, "stretch" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); + return; + } + tmi->wave.func = NameToGenFunc( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); + return; + } + tmi->wave.base = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); + return; + } + tmi->wave.amplitude = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); + return; + } + tmi->wave.phase = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); + return; + } + tmi->wave.frequency = atof( token ); + + tmi->type = TMOD_STRETCH; + } + // + // transform + // + else if ( !Q_stricmp( token, "transform" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); + return; + } + tmi->matrix[0][0] = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); + return; + } + tmi->matrix[0][1] = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); + return; + } + tmi->matrix[1][0] = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); + return; + } + tmi->matrix[1][1] = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); + return; + } + tmi->translate[0] = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); + return; + } + tmi->translate[1] = atof( token ); + + tmi->type = TMOD_TRANSFORM; + } + // + // rotate + // + else if ( !Q_stricmp( token, "rotate" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing tcMod rotate parms in shader '%s'\n", shader.name ); + return; + } + tmi->rotateSpeed = atof( token ); + tmi->type = TMOD_ROTATE; + } + // + // entityTranslate + // + else if ( !Q_stricmp( token, "entityTranslate" ) ) + { + tmi->type = TMOD_ENTITY_TRANSLATE; + } + else + { + ri.Printf( PRINT_WARNING, "WARNING: unknown tcMod '%s' in shader '%s'\n", token, shader.name ); + } +} + + +/* +=================== +ParseStage +=================== +*/ +static qboolean ParseStage( shaderStage_t *stage, char **text ) +{ + char *token; + int depthMaskBits = GLS_DEPTHMASK_TRUE, blendSrcBits = 0, blendDstBits = 0, atestBits = 0, depthFuncBits = 0; + qboolean depthMaskExplicit = qfalse; + + stage->active = qtrue; + + while ( 1 ) + { + token = COM_ParseExt( text, qtrue ); + if ( !token[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: no matching '}' found\n" ); + return qfalse; + } + + if ( token[0] == '}' ) + { + break; + } + // + // map + // + else if ( !Q_stricmp( token, "map" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( !token[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'map' keyword in shader '%s'\n", shader.name ); + return qfalse; + } + + if ( !Q_stricmp( token, "$whiteimage" ) ) + { + stage->bundle[0].image[0] = tr.whiteImage; + continue; + } + else if ( !Q_stricmp( token, "$lightmap" ) ) + { + stage->bundle[0].isLightmap = qtrue; + if ( shader.lightmapIndex < 0 || !tr.lightmaps ) { + stage->bundle[0].image[0] = tr.whiteImage; + } else { + stage->bundle[0].image[0] = tr.lightmaps[shader.lightmapIndex]; + } + continue; + } + else + { + stage->bundle[0].image[0] = R_FindImageFile( token, !shader.noMipMaps, !shader.noPicMip, GL_REPEAT ); + if ( !stage->bundle[0].image[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name ); + return qfalse; + } + } + } + // + // clampmap + // + else if ( !Q_stricmp( token, "clampmap" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( !token[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'clampmap' keyword in shader '%s'\n", shader.name ); + return qfalse; + } + + stage->bundle[0].image[0] = R_FindImageFile( token, !shader.noMipMaps, !shader.noPicMip, GL_CLAMP_TO_EDGE ); + if ( !stage->bundle[0].image[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name ); + return qfalse; + } + } + // + // animMap .... + // + else if ( !Q_stricmp( token, "animMap" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( !token[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'animMmap' keyword in shader '%s'\n", shader.name ); + return qfalse; + } + stage->bundle[0].imageAnimationSpeed = atof( token ); + + // parse up to MAX_IMAGE_ANIMATIONS animations + while ( 1 ) { + int num; + + token = COM_ParseExt( text, qfalse ); + if ( !token[0] ) { + break; + } + num = stage->bundle[0].numImageAnimations; + if ( num < MAX_IMAGE_ANIMATIONS ) { + stage->bundle[0].image[num] = R_FindImageFile( token, !shader.noMipMaps, !shader.noPicMip, GL_REPEAT ); + if ( !stage->bundle[0].image[num] ) + { + ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name ); + return qfalse; + } + stage->bundle[0].numImageAnimations++; + } + } + } + else if ( !Q_stricmp( token, "videoMap" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( !token[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'videoMmap' keyword in shader '%s'\n", shader.name ); + return qfalse; + } + stage->bundle[0].videoMapHandle = ri.CIN_PlayCinematic( token, 0, 0, 256, 256, (CIN_loop | CIN_silent | CIN_shader)); + if (stage->bundle[0].videoMapHandle != -1) { + stage->bundle[0].isVideoMap = qtrue; + stage->bundle[0].image[0] = tr.scratchImage[stage->bundle[0].videoMapHandle]; + } + } + // + // alphafunc + // + else if ( !Q_stricmp( token, "alphaFunc" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( !token[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'alphaFunc' keyword in shader '%s'\n", shader.name ); + return qfalse; + } + + atestBits = NameToAFunc( token ); + } + // + // depthFunc + // + else if ( !Q_stricmp( token, "depthfunc" ) ) + { + token = COM_ParseExt( text, qfalse ); + + if ( !token[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'depthfunc' keyword in shader '%s'\n", shader.name ); + return qfalse; + } + + if ( !Q_stricmp( token, "lequal" ) ) + { + depthFuncBits = 0; + } + else if ( !Q_stricmp( token, "equal" ) ) + { + depthFuncBits = GLS_DEPTHFUNC_EQUAL; + } + else + { + ri.Printf( PRINT_WARNING, "WARNING: unknown depthfunc '%s' in shader '%s'\n", token, shader.name ); + continue; + } + } + // + // detail + // + else if ( !Q_stricmp( token, "detail" ) ) + { + stage->isDetail = qtrue; + } + // + // blendfunc + // or blendfunc + // + else if ( !Q_stricmp( token, "blendfunc" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parm for blendFunc in shader '%s'\n", shader.name ); + continue; + } + // check for "simple" blends first + if ( !Q_stricmp( token, "add" ) ) { + blendSrcBits = GLS_SRCBLEND_ONE; + blendDstBits = GLS_DSTBLEND_ONE; + } else if ( !Q_stricmp( token, "filter" ) ) { + blendSrcBits = GLS_SRCBLEND_DST_COLOR; + blendDstBits = GLS_DSTBLEND_ZERO; + } else if ( !Q_stricmp( token, "blend" ) ) { + blendSrcBits = GLS_SRCBLEND_SRC_ALPHA; + blendDstBits = GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; + } else { + // complex double blends + blendSrcBits = NameToSrcBlendMode( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parm for blendFunc in shader '%s'\n", shader.name ); + continue; + } + blendDstBits = NameToDstBlendMode( token ); + } + + // clear depth mask for blended surfaces + if ( !depthMaskExplicit ) + { + depthMaskBits = 0; + } + } + // + // rgbGen + // + else if ( !Q_stricmp( token, "rgbGen" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parameters for rgbGen in shader '%s'\n", shader.name ); + continue; + } + + if ( !Q_stricmp( token, "wave" ) ) + { + ParseWaveForm( text, &stage->rgbWave ); + stage->rgbGen = CGEN_WAVEFORM; + } + else if ( !Q_stricmp( token, "const" ) ) + { + vec3_t color; + + ParseVector( text, 3, color ); + stage->constantColor[0] = 255 * color[0]; + stage->constantColor[1] = 255 * color[1]; + stage->constantColor[2] = 255 * color[2]; + + stage->rgbGen = CGEN_CONST; + } + else if ( !Q_stricmp( token, "identity" ) ) + { + stage->rgbGen = CGEN_IDENTITY; + } + else if ( !Q_stricmp( token, "identityLighting" ) ) + { + stage->rgbGen = CGEN_IDENTITY_LIGHTING; + } + else if ( !Q_stricmp( token, "entity" ) ) + { + stage->rgbGen = CGEN_ENTITY; + } + else if ( !Q_stricmp( token, "oneMinusEntity" ) ) + { + stage->rgbGen = CGEN_ONE_MINUS_ENTITY; + } + else if ( !Q_stricmp( token, "vertex" ) ) + { + stage->rgbGen = CGEN_VERTEX; + if ( stage->alphaGen == 0 ) { + stage->alphaGen = AGEN_VERTEX; + } + } + else if ( !Q_stricmp( token, "exactVertex" ) ) + { + stage->rgbGen = CGEN_EXACT_VERTEX; + } + else if ( !Q_stricmp( token, "lightingDiffuse" ) ) + { + stage->rgbGen = CGEN_LIGHTING_DIFFUSE; + } + else if ( !Q_stricmp( token, "oneMinusVertex" ) ) + { + stage->rgbGen = CGEN_ONE_MINUS_VERTEX; + } + else + { + ri.Printf( PRINT_WARNING, "WARNING: unknown rgbGen parameter '%s' in shader '%s'\n", token, shader.name ); + continue; + } + } + // + // alphaGen + // + else if ( !Q_stricmp( token, "alphaGen" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parameters for alphaGen in shader '%s'\n", shader.name ); + continue; + } + + if ( !Q_stricmp( token, "wave" ) ) + { + ParseWaveForm( text, &stage->alphaWave ); + stage->alphaGen = AGEN_WAVEFORM; + } + else if ( !Q_stricmp( token, "const" ) ) + { + token = COM_ParseExt( text, qfalse ); + stage->constantColor[3] = 255 * atof( token ); + stage->alphaGen = AGEN_CONST; + } + else if ( !Q_stricmp( token, "identity" ) ) + { + stage->alphaGen = AGEN_IDENTITY; + } + else if ( !Q_stricmp( token, "entity" ) ) + { + stage->alphaGen = AGEN_ENTITY; + } + else if ( !Q_stricmp( token, "oneMinusEntity" ) ) + { + stage->alphaGen = AGEN_ONE_MINUS_ENTITY; + } + else if ( !Q_stricmp( token, "vertex" ) ) + { + stage->alphaGen = AGEN_VERTEX; + } + else if ( !Q_stricmp( token, "lightingSpecular" ) ) + { + stage->alphaGen = AGEN_LIGHTING_SPECULAR; + } + else if ( !Q_stricmp( token, "oneMinusVertex" ) ) + { + stage->alphaGen = AGEN_ONE_MINUS_VERTEX; + } + else if ( !Q_stricmp( token, "portal" ) ) + { + stage->alphaGen = AGEN_PORTAL; + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + shader.portalRange = 256; + ri.Printf( PRINT_WARNING, "WARNING: missing range parameter for alphaGen portal in shader '%s', defaulting to 256\n", shader.name ); + } + else + { + shader.portalRange = atof( token ); + } + } + else + { + ri.Printf( PRINT_WARNING, "WARNING: unknown alphaGen parameter '%s' in shader '%s'\n", token, shader.name ); + continue; + } + } + // + // tcGen + // + else if ( !Q_stricmp(token, "texgen") || !Q_stricmp( token, "tcGen" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing texgen parm in shader '%s'\n", shader.name ); + continue; + } + + if ( !Q_stricmp( token, "environment" ) ) + { + stage->bundle[0].tcGen = TCGEN_ENVIRONMENT_MAPPED; + } + else if ( !Q_stricmp( token, "lightmap" ) ) + { + stage->bundle[0].tcGen = TCGEN_LIGHTMAP; + } + else if ( !Q_stricmp( token, "texture" ) || !Q_stricmp( token, "base" ) ) + { + stage->bundle[0].tcGen = TCGEN_TEXTURE; + } + else if ( !Q_stricmp( token, "vector" ) ) + { + ParseVector( text, 3, stage->bundle[0].tcGenVectors[0] ); + ParseVector( text, 3, stage->bundle[0].tcGenVectors[1] ); + + stage->bundle[0].tcGen = TCGEN_VECTOR; + } + else + { + ri.Printf( PRINT_WARNING, "WARNING: unknown texgen parm in shader '%s'\n", shader.name ); + } + } + // + // tcMod <...> + // + else if ( !Q_stricmp( token, "tcMod" ) ) + { + char buffer[1024] = ""; + + while ( 1 ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + break; + strcat( buffer, token ); + strcat( buffer, " " ); + } + + ParseTexMod( buffer, stage ); + + continue; + } + // + // depthmask + // + else if ( !Q_stricmp( token, "depthwrite" ) ) + { + depthMaskBits = GLS_DEPTHMASK_TRUE; + depthMaskExplicit = qtrue; + + continue; + } + else + { + ri.Printf( PRINT_WARNING, "WARNING: unknown parameter '%s' in shader '%s'\n", token, shader.name ); + return qfalse; + } + } + + // + // if cgen isn't explicitly specified, use either identity or identitylighting + // + if ( stage->rgbGen == CGEN_BAD ) { + if ( blendSrcBits == 0 || + blendSrcBits == GLS_SRCBLEND_ONE || + blendSrcBits == GLS_SRCBLEND_SRC_ALPHA ) { + stage->rgbGen = CGEN_IDENTITY_LIGHTING; + } else { + stage->rgbGen = CGEN_IDENTITY; + } + } + + + // + // implicitly assume that a GL_ONE GL_ZERO blend mask disables blending + // + if ( ( blendSrcBits == GLS_SRCBLEND_ONE ) && + ( blendDstBits == GLS_DSTBLEND_ZERO ) ) + { + blendDstBits = blendSrcBits = 0; + depthMaskBits = GLS_DEPTHMASK_TRUE; + } + + // decide which agens we can skip + if ( stage->alphaGen == AGEN_IDENTITY ) { + if ( stage->rgbGen == CGEN_IDENTITY + || stage->rgbGen == CGEN_LIGHTING_DIFFUSE ) { + stage->alphaGen = AGEN_SKIP; + } + } + + // + // compute state bits + // + stage->stateBits = depthMaskBits | + blendSrcBits | blendDstBits | + atestBits | + depthFuncBits; + + return qtrue; +} + +/* +=============== +ParseDeform + +deformVertexes wave +deformVertexes normal +deformVertexes move +deformVertexes bulge +deformVertexes projectionShadow +deformVertexes autoSprite +deformVertexes autoSprite2 +deformVertexes text[0-7] +=============== +*/ +static void ParseDeform( char **text ) { + char *token; + deformStage_t *ds; + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing deform parm in shader '%s'\n", shader.name ); + return; + } + + if ( shader.numDeforms == MAX_SHADER_DEFORMS ) { + ri.Printf( PRINT_WARNING, "WARNING: MAX_SHADER_DEFORMS in '%s'\n", shader.name ); + return; + } + + ds = &shader.deforms[ shader.numDeforms ]; + shader.numDeforms++; + + if ( !Q_stricmp( token, "projectionShadow" ) ) { + ds->deformation = DEFORM_PROJECTION_SHADOW; + return; + } + + if ( !Q_stricmp( token, "autosprite" ) ) { + ds->deformation = DEFORM_AUTOSPRITE; + return; + } + + if ( !Q_stricmp( token, "autosprite2" ) ) { + ds->deformation = DEFORM_AUTOSPRITE2; + return; + } + + if ( !Q_stricmpn( token, "text", 4 ) ) { + int n; + + n = token[4] - '0'; + if ( n < 0 || n > 7 ) { + n = 0; + } + ds->deformation = DEFORM_TEXT0 + n; + return; + } + + if ( !Q_stricmp( token, "bulge" ) ) { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name ); + return; + } + ds->bulgeWidth = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name ); + return; + } + ds->bulgeHeight = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name ); + return; + } + ds->bulgeSpeed = atof( token ); + + ds->deformation = DEFORM_BULGE; + return; + } + + if ( !Q_stricmp( token, "wave" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); + return; + } + + if ( atof( token ) != 0 ) + { + ds->deformationSpread = 1.0f / atof( token ); + } + else + { + ds->deformationSpread = 100.0f; + ri.Printf( PRINT_WARNING, "WARNING: illegal div value of 0 in deformVertexes command for shader '%s'\n", shader.name ); + } + + ParseWaveForm( text, &ds->deformationWave ); + ds->deformation = DEFORM_WAVE; + return; + } + + if ( !Q_stricmp( token, "normal" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); + return; + } + ds->deformationWave.amplitude = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); + return; + } + ds->deformationWave.frequency = atof( token ); + + ds->deformation = DEFORM_NORMALS; + return; + } + + if ( !Q_stricmp( token, "move" ) ) { + int i; + + for ( i = 0 ; i < 3 ; i++ ) { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) { + ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); + return; + } + ds->moveVector[i] = atof( token ); + } + + ParseWaveForm( text, &ds->deformationWave ); + ds->deformation = DEFORM_MOVE; + return; + } + + ri.Printf( PRINT_WARNING, "WARNING: unknown deformVertexes subtype '%s' found in shader '%s'\n", token, shader.name ); +} + + +/* +=============== +ParseSkyParms + +skyParms +=============== +*/ +static void ParseSkyParms( char **text ) { + char *token; + static char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; + char pathname[MAX_QPATH]; + int i; + + // outerbox + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) { + ri.Printf( PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name ); + return; + } + if ( strcmp( token, "-" ) ) { + for (i=0 ; i<6 ; i++) { + Com_sprintf( pathname, sizeof(pathname), "%s_%s.tga" + , token, suf[i] ); + shader.sky.outerbox[i] = R_FindImageFile( ( char * ) pathname, qtrue, qtrue, GL_CLAMP_TO_EDGE ); + + if ( !shader.sky.outerbox[i] ) { + shader.sky.outerbox[i] = tr.defaultImage; + } + } + } + + // cloudheight + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) { + ri.Printf( PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name ); + return; + } + shader.sky.cloudHeight = atof( token ); + if ( !shader.sky.cloudHeight ) { + shader.sky.cloudHeight = 512; + } + R_InitSkyTexCoords( shader.sky.cloudHeight ); + + + // innerbox + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) { + ri.Printf( PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name ); + return; + } + if ( strcmp( token, "-" ) ) { + for (i=0 ; i<6 ; i++) { + Com_sprintf( pathname, sizeof(pathname), "%s_%s.tga" + , token, suf[i] ); + shader.sky.innerbox[i] = R_FindImageFile( ( char * ) pathname, qtrue, qtrue, GL_REPEAT ); + if ( !shader.sky.innerbox[i] ) { + shader.sky.innerbox[i] = tr.defaultImage; + } + } + } + + shader.isSky = qtrue; +} + + +/* +================= +ParseSort +================= +*/ +void ParseSort( char **text ) { + char *token; + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) { + ri.Printf( PRINT_WARNING, "WARNING: missing sort parameter in shader '%s'\n", shader.name ); + return; + } + + if ( !Q_stricmp( token, "portal" ) ) { + shader.sort = SS_PORTAL; + } else if ( !Q_stricmp( token, "sky" ) ) { + shader.sort = SS_ENVIRONMENT; + } else if ( !Q_stricmp( token, "opaque" ) ) { + shader.sort = SS_OPAQUE; + }else if ( !Q_stricmp( token, "decal" ) ) { + shader.sort = SS_DECAL; + } else if ( !Q_stricmp( token, "seeThrough" ) ) { + shader.sort = SS_SEE_THROUGH; + } else if ( !Q_stricmp( token, "banner" ) ) { + shader.sort = SS_BANNER; + } else if ( !Q_stricmp( token, "additive" ) ) { + shader.sort = SS_BLEND1; + } else if ( !Q_stricmp( token, "nearest" ) ) { + shader.sort = SS_NEAREST; + } else if ( !Q_stricmp( token, "underwater" ) ) { + shader.sort = SS_UNDERWATER; + } else { + shader.sort = atof( token ); + } +} + + + +// this table is also present in q3map + +typedef struct { + char *name; + int clearSolid, surfaceFlags, contents; +} infoParm_t; + +infoParm_t infoParms[] = { + // server relevant contents + {"water", 1, 0, CONTENTS_WATER }, + {"slime", 1, 0, CONTENTS_SLIME }, // mildly damaging + {"lava", 1, 0, CONTENTS_LAVA }, // very damaging + {"playerclip", 1, 0, CONTENTS_PLAYERCLIP }, + {"monsterclip", 1, 0, CONTENTS_MONSTERCLIP }, + {"nodrop", 1, 0, CONTENTS_NODROP }, // don't drop items or leave bodies (death fog, lava, etc) + {"nonsolid", 1, SURF_NONSOLID, 0}, // clears the solid flag + + // utility relevant attributes + {"origin", 1, 0, CONTENTS_ORIGIN }, // center of rotating brushes + {"trans", 0, 0, CONTENTS_TRANSLUCENT }, // don't eat contained surfaces + {"detail", 0, 0, CONTENTS_DETAIL }, // don't include in structural bsp + {"structural", 0, 0, CONTENTS_STRUCTURAL }, // force into structural bsp even if trnas + {"areaportal", 1, 0, CONTENTS_AREAPORTAL }, // divides areas + {"clusterportal", 1,0, CONTENTS_CLUSTERPORTAL }, // for bots + {"donotenter", 1, 0, CONTENTS_DONOTENTER }, // for bots + + {"fog", 1, 0, CONTENTS_FOG}, // carves surfaces entering + {"sky", 0, SURF_SKY, 0 }, // emit light from an environment map + {"lightfilter", 0, SURF_LIGHTFILTER, 0 }, // filter light going through it + {"alphashadow", 0, SURF_ALPHASHADOW, 0 }, // test light on a per-pixel basis + {"hint", 0, SURF_HINT, 0 }, // use as a primary splitter + + // server attributes + {"slick", 0, SURF_SLICK, 0 }, + {"noimpact", 0, SURF_NOIMPACT, 0 }, // don't make impact explosions or marks + {"nomarks", 0, SURF_NOMARKS, 0 }, // don't make impact marks, but still explode + {"ladder", 0, SURF_LADDER, 0 }, + {"nodamage", 0, SURF_NODAMAGE, 0 }, + {"metalsteps", 0, SURF_METALSTEPS,0 }, + {"flesh", 0, SURF_FLESH, 0 }, + {"nosteps", 0, SURF_NOSTEPS, 0 }, + + // drawsurf attributes + {"nodraw", 0, SURF_NODRAW, 0 }, // don't generate a drawsurface (or a lightmap) + {"pointlight", 0, SURF_POINTLIGHT, 0 }, // sample lighting at vertexes + {"nolightmap", 0, SURF_NOLIGHTMAP,0 }, // don't generate a lightmap + {"nodlight", 0, SURF_NODLIGHT, 0 }, // don't ever add dynamic lights + {"dust", 0, SURF_DUST, 0} // leave a dust trail when walking on this surface +}; + + +/* +=============== +ParseSurfaceParm + +surfaceparm +=============== +*/ +static void ParseSurfaceParm( char **text ) { + char *token; + int numInfoParms = ARRAY_LEN( infoParms ); + int i; + + token = COM_ParseExt( text, qfalse ); + for ( i = 0 ; i < numInfoParms ; i++ ) { + if ( !Q_stricmp( token, infoParms[i].name ) ) { + shader.surfaceFlags |= infoParms[i].surfaceFlags; + shader.contentFlags |= infoParms[i].contents; +#if 0 + if ( infoParms[i].clearSolid ) { + si->contents &= ~CONTENTS_SOLID; + } +#endif + break; + } + } +} + +/* +================= +ParseShader + +The current text pointer is at the explicit text definition of the +shader. Parse it into the global shader variable. Later functions +will optimize it. +================= +*/ +static qboolean ParseShader( char **text ) +{ + char *token; + int s; + + s = 0; + + token = COM_ParseExt( text, qtrue ); + if ( token[0] != '{' ) + { + ri.Printf( PRINT_WARNING, "WARNING: expecting '{', found '%s' instead in shader '%s'\n", token, shader.name ); + return qfalse; + } + + while ( 1 ) + { + token = COM_ParseExt( text, qtrue ); + if ( !token[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: no concluding '}' in shader %s\n", shader.name ); + return qfalse; + } + + // end of shader definition + if ( token[0] == '}' ) + { + break; + } + // stage definition + else if ( token[0] == '{' ) + { + if ( s >= MAX_SHADER_STAGES ) { + ri.Printf( PRINT_WARNING, "WARNING: too many stages in shader %s\n", shader.name ); + return qfalse; + } + + if ( !ParseStage( &stages[s], text ) ) + { + return qfalse; + } + stages[s].active = qtrue; + s++; + + continue; + } + // skip stuff that only the QuakeEdRadient needs + else if ( !Q_stricmpn( token, "qer", 3 ) ) { + SkipRestOfLine( text ); + continue; + } + // sun parms + else if ( !Q_stricmp( token, "q3map_sun" ) ) { + float a, b; + + token = COM_ParseExt( text, qfalse ); + tr.sunLight[0] = atof( token ); + token = COM_ParseExt( text, qfalse ); + tr.sunLight[1] = atof( token ); + token = COM_ParseExt( text, qfalse ); + tr.sunLight[2] = atof( token ); + + VectorNormalize( tr.sunLight ); + + token = COM_ParseExt( text, qfalse ); + a = atof( token ); + VectorScale( tr.sunLight, a, tr.sunLight); + + token = COM_ParseExt( text, qfalse ); + a = atof( token ); + a = a / 180 * M_PI; + + token = COM_ParseExt( text, qfalse ); + b = atof( token ); + b = b / 180 * M_PI; + + tr.sunDirection[0] = cos( a ) * cos( b ); + tr.sunDirection[1] = sin( a ) * cos( b ); + tr.sunDirection[2] = sin( b ); + } + else if ( !Q_stricmp( token, "deformVertexes" ) ) { + ParseDeform( text ); + continue; + } + else if ( !Q_stricmp( token, "tesssize" ) ) { + SkipRestOfLine( text ); + continue; + } + else if ( !Q_stricmp( token, "clampTime" ) ) { + token = COM_ParseExt( text, qfalse ); + if (token[0]) { + shader.clampTime = atof(token); + } + } + // skip stuff that only the q3map needs + else if ( !Q_stricmpn( token, "q3map", 5 ) ) { + SkipRestOfLine( text ); + continue; + } + // skip stuff that only q3map or the server needs + else if ( !Q_stricmp( token, "surfaceParm" ) ) { + ParseSurfaceParm( text ); + continue; + } + // no mip maps + else if ( !Q_stricmp( token, "nomipmaps" ) ) + { + shader.noMipMaps = qtrue; + shader.noPicMip = qtrue; + continue; + } + // no picmip adjustment + else if ( !Q_stricmp( token, "nopicmip" ) ) + { + shader.noPicMip = qtrue; + continue; + } + // polygonOffset + else if ( !Q_stricmp( token, "polygonOffset" ) ) + { + shader.polygonOffset = qtrue; + continue; + } + // entityMergable, allowing sprite surfaces from multiple entities + // to be merged into one batch. This is a savings for smoke + // puffs and blood, but can't be used for anything where the + // shader calcs (not the surface function) reference the entity color or scroll + else if ( !Q_stricmp( token, "entityMergable" ) ) + { + shader.entityMergable = qtrue; + continue; + } + // fogParms + else if ( !Q_stricmp( token, "fogParms" ) ) + { + if ( !ParseVector( text, 3, shader.fogParms.color ) ) { + return qfalse; + } + + token = COM_ParseExt( text, qfalse ); + if ( !token[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parm for 'fogParms' keyword in shader '%s'\n", shader.name ); + continue; + } + shader.fogParms.depthForOpaque = atof( token ); + + // skip any old gradient directions + SkipRestOfLine( text ); + continue; + } + // portal + else if ( !Q_stricmp(token, "portal") ) + { + shader.sort = SS_PORTAL; + continue; + } + // skyparms + else if ( !Q_stricmp( token, "skyparms" ) ) + { + ParseSkyParms( text ); + continue; + } + // light determines flaring in q3map, not needed here + else if ( !Q_stricmp(token, "light") ) + { + token = COM_ParseExt( text, qfalse ); + continue; + } + // cull + else if ( !Q_stricmp( token, "cull") ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing cull parms in shader '%s'\n", shader.name ); + continue; + } + + if ( !Q_stricmp( token, "none" ) || !Q_stricmp( token, "twosided" ) || !Q_stricmp( token, "disable" ) ) + { + shader.cullType = CT_TWO_SIDED; + } + else if ( !Q_stricmp( token, "back" ) || !Q_stricmp( token, "backside" ) || !Q_stricmp( token, "backsided" ) ) + { + shader.cullType = CT_BACK_SIDED; + } + else + { + ri.Printf( PRINT_WARNING, "WARNING: invalid cull parm '%s' in shader '%s'\n", token, shader.name ); + } + continue; + } + // sort + else if ( !Q_stricmp( token, "sort" ) ) + { + ParseSort( text ); + continue; + } + else + { + ri.Printf( PRINT_WARNING, "WARNING: unknown general shader parameter '%s' in '%s'\n", token, shader.name ); + return qfalse; + } + } + + // + // ignore shaders that don't have any stages, unless it is a sky or fog + // + if ( s == 0 && !shader.isSky && !(shader.contentFlags & CONTENTS_FOG ) ) { + return qfalse; + } + + shader.explicitlyDefined = qtrue; + + return qtrue; +} + +/* +======================================================================================== + +SHADER OPTIMIZATION AND FOGGING + +======================================================================================== +*/ + +/* +=================== +ComputeStageIteratorFunc + +See if we can use on of the simple fastpath stage functions, +otherwise set to the generic stage function +=================== +*/ +static void ComputeStageIteratorFunc( void ) +{ + shader.optimalStageIteratorFunc = RB_StageIteratorGeneric; + + // + // see if this should go into the sky path + // + if ( shader.isSky ) + { + shader.optimalStageIteratorFunc = RB_StageIteratorSky; + goto done; + } + + if ( r_ignoreFastPath->integer ) + { + return; + } + + // + // see if this can go into the vertex lit fast path + // + if ( shader.numUnfoggedPasses == 1 ) + { + if ( stages[0].rgbGen == CGEN_LIGHTING_DIFFUSE ) + { + if ( stages[0].alphaGen == AGEN_IDENTITY ) + { + if ( stages[0].bundle[0].tcGen == TCGEN_TEXTURE ) + { + if ( !shader.polygonOffset ) + { + if ( !shader.multitextureEnv ) + { + if ( !shader.numDeforms ) + { + shader.optimalStageIteratorFunc = RB_StageIteratorVertexLitTexture; + goto done; + } + } + } + } + } + } + } + + // + // see if this can go into an optimized LM, multitextured path + // + if ( shader.numUnfoggedPasses == 1 ) + { + if ( ( stages[0].rgbGen == CGEN_IDENTITY ) && ( stages[0].alphaGen == AGEN_IDENTITY ) ) + { + if ( stages[0].bundle[0].tcGen == TCGEN_TEXTURE && + stages[0].bundle[1].tcGen == TCGEN_LIGHTMAP ) + { + if ( !shader.polygonOffset ) + { + if ( !shader.numDeforms ) + { + if ( shader.multitextureEnv ) + { + shader.optimalStageIteratorFunc = RB_StageIteratorLightmappedMultitexture; + goto done; + } + } + } + } + } + } + +done: + return; +} + +typedef struct { + int blendA; + int blendB; + + int multitextureEnv; + int multitextureBlend; +} collapse_t; + +static collapse_t collapse[] = { + { 0, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, + GL_MODULATE, 0 }, + + { 0, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, + GL_MODULATE, 0 }, + + { GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, + GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, + + { GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, + GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, + + { GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, + GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, + + { GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, + GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, + + { 0, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE, + GL_ADD, 0 }, + + { GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE, + GL_ADD, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE }, +#if 0 + { 0, GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_SRCBLEND_SRC_ALPHA, + GL_DECAL, 0 }, +#endif + { -1 } +}; + +/* +================ +CollapseMultitexture + +Attempt to combine two stages into a single multitexture stage +FIXME: I think modulated add + modulated add collapses incorrectly +================= +*/ +static qboolean CollapseMultitexture( void ) { + int abits, bbits; + int i; + textureBundle_t tmpBundle; + + if ( !qglActiveTextureARB ) { + return qfalse; + } + + // make sure both stages are active + if ( !stages[0].active || !stages[1].active ) { + return qfalse; + } + + // on voodoo2, don't combine different tmus + if ( glConfig.driverType == GLDRV_VOODOO ) { + if ( stages[0].bundle[0].image[0]->TMU == + stages[1].bundle[0].image[0]->TMU ) { + return qfalse; + } + } + + abits = stages[0].stateBits; + bbits = stages[1].stateBits; + + // make sure that both stages have identical state other than blend modes + if ( ( abits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) != + ( bbits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) ) { + return qfalse; + } + + abits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); + bbits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); + + // search for a valid multitexture blend function + for ( i = 0; collapse[i].blendA != -1 ; i++ ) { + if ( abits == collapse[i].blendA + && bbits == collapse[i].blendB ) { + break; + } + } + + // nothing found + if ( collapse[i].blendA == -1 ) { + return qfalse; + } + + // GL_ADD is a separate extension + if ( collapse[i].multitextureEnv == GL_ADD && !glConfig.textureEnvAddAvailable ) { + return qfalse; + } + + // make sure waveforms have identical parameters + if ( ( stages[0].rgbGen != stages[1].rgbGen ) || + ( stages[0].alphaGen != stages[1].alphaGen ) ) { + return qfalse; + } + + // an add collapse can only have identity colors + if ( collapse[i].multitextureEnv == GL_ADD && stages[0].rgbGen != CGEN_IDENTITY ) { + return qfalse; + } + + if ( stages[0].rgbGen == CGEN_WAVEFORM ) + { + if ( memcmp( &stages[0].rgbWave, + &stages[1].rgbWave, + sizeof( stages[0].rgbWave ) ) ) + { + return qfalse; + } + } + if ( stages[0].alphaGen == AGEN_WAVEFORM ) + { + if ( memcmp( &stages[0].alphaWave, + &stages[1].alphaWave, + sizeof( stages[0].alphaWave ) ) ) + { + return qfalse; + } + } + + + // make sure that lightmaps are in bundle 1 for 3dfx + if ( stages[0].bundle[0].isLightmap ) + { + tmpBundle = stages[0].bundle[0]; + stages[0].bundle[0] = stages[1].bundle[0]; + stages[0].bundle[1] = tmpBundle; + } + else + { + stages[0].bundle[1] = stages[1].bundle[0]; + } + + // set the new blend state bits + shader.multitextureEnv = collapse[i].multitextureEnv; + stages[0].stateBits &= ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); + stages[0].stateBits |= collapse[i].multitextureBlend; + + // + // move down subsequent shaders + // + memmove( &stages[1], &stages[2], sizeof( stages[0] ) * ( MAX_SHADER_STAGES - 2 ) ); + Com_Memset( &stages[MAX_SHADER_STAGES-1], 0, sizeof( stages[0] ) ); + + return qtrue; +} + +/* +============= + +FixRenderCommandList +https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=493 +Arnout: this is a nasty issue. Shaders can be registered after drawsurfaces are generated +but before the frame is rendered. This will, for the duration of one frame, cause drawsurfaces +to be rendered with bad shaders. To fix this, need to go through all render commands and fix +sortedIndex. +============== +*/ +static void FixRenderCommandList( int newShader ) { + renderCommandList_t *cmdList = &backEndData->commands; + + if( cmdList ) { + const void *curCmd = cmdList->cmds; + + while ( 1 ) { + curCmd = PADP(curCmd, sizeof(void *)); + + switch ( *(const int *)curCmd ) { + case RC_SET_COLOR: + { + const setColorCommand_t *sc_cmd = (const setColorCommand_t *)curCmd; + curCmd = (const void *)(sc_cmd + 1); + break; + } + case RC_STRETCH_PIC: + { + const stretchPicCommand_t *sp_cmd = (const stretchPicCommand_t *)curCmd; + curCmd = (const void *)(sp_cmd + 1); + break; + } + case RC_DRAW_SURFS: + { + int i; + drawSurf_t *drawSurf; + shader_t *shader; + int fogNum; + int entityNum; + int dlightMap; + int sortedIndex; + const drawSurfsCommand_t *ds_cmd = (const drawSurfsCommand_t *)curCmd; + + for( i = 0, drawSurf = ds_cmd->drawSurfs; i < ds_cmd->numDrawSurfs; i++, drawSurf++ ) { + R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlightMap ); + sortedIndex = (( drawSurf->sort >> QSORT_SHADERNUM_SHIFT ) & (MAX_SHADERS-1)); + if( sortedIndex >= newShader ) { + sortedIndex++; + drawSurf->sort = (sortedIndex << QSORT_SHADERNUM_SHIFT) | entityNum | ( fogNum << QSORT_FOGNUM_SHIFT ) | (int)dlightMap; + } + } + curCmd = (const void *)(ds_cmd + 1); + break; + } + case RC_DRAW_BUFFER: + { + const drawBufferCommand_t *db_cmd = (const drawBufferCommand_t *)curCmd; + curCmd = (const void *)(db_cmd + 1); + break; + } + case RC_SWAP_BUFFERS: + { + const swapBuffersCommand_t *sb_cmd = (const swapBuffersCommand_t *)curCmd; + curCmd = (const void *)(sb_cmd + 1); + break; + } + case RC_END_OF_LIST: + default: + return; + } + } + } +} + +/* +============== +SortNewShader + +Positions the most recently created shader in the tr.sortedShaders[] +array so that the shader->sort key is sorted relative to the other +shaders. + +Sets shader->sortedIndex +============== +*/ +static void SortNewShader( void ) { + int i; + float sort; + shader_t *newShader; + + newShader = tr.shaders[ tr.numShaders - 1 ]; + sort = newShader->sort; + + for ( i = tr.numShaders - 2 ; i >= 0 ; i-- ) { + if ( tr.sortedShaders[ i ]->sort <= sort ) { + break; + } + tr.sortedShaders[i+1] = tr.sortedShaders[i]; + tr.sortedShaders[i+1]->sortedIndex++; + } + + // Arnout: fix rendercommandlist + // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=493 + FixRenderCommandList( i+1 ); + + newShader->sortedIndex = i+1; + tr.sortedShaders[i+1] = newShader; +} + + +/* +==================== +GeneratePermanentShader +==================== +*/ +static shader_t *GeneratePermanentShader( void ) { + shader_t *newShader; + int i, b; + int size, hash; + + if ( tr.numShaders == MAX_SHADERS ) { + ri.Printf( PRINT_WARNING, "WARNING: GeneratePermanentShader - MAX_SHADERS hit\n"); + return tr.defaultShader; + } + + newShader = ri.Hunk_Alloc( sizeof( shader_t ), h_low ); + + *newShader = shader; + + if ( shader.sort <= SS_OPAQUE ) { + newShader->fogPass = FP_EQUAL; + } else if ( shader.contentFlags & CONTENTS_FOG ) { + newShader->fogPass = FP_LE; + } + + tr.shaders[ tr.numShaders ] = newShader; + newShader->index = tr.numShaders; + + tr.sortedShaders[ tr.numShaders ] = newShader; + newShader->sortedIndex = tr.numShaders; + + tr.numShaders++; + + for ( i = 0 ; i < newShader->numUnfoggedPasses ; i++ ) { + if ( !stages[i].active ) { + break; + } + newShader->stages[i] = ri.Hunk_Alloc( sizeof( stages[i] ), h_low ); + *newShader->stages[i] = stages[i]; + + for ( b = 0 ; b < NUM_TEXTURE_BUNDLES ; b++ ) { + size = newShader->stages[i]->bundle[b].numTexMods * sizeof( texModInfo_t ); + newShader->stages[i]->bundle[b].texMods = ri.Hunk_Alloc( size, h_low ); + Com_Memcpy( newShader->stages[i]->bundle[b].texMods, stages[i].bundle[b].texMods, size ); + } + } + + SortNewShader(); + + hash = generateHashValue(newShader->name, FILE_HASH_SIZE); + newShader->next = hashTable[hash]; + hashTable[hash] = newShader; + + return newShader; +} + +/* +================= +VertexLightingCollapse + +If vertex lighting is enabled, only render a single +pass, trying to guess which is the correct one to best aproximate +what it is supposed to look like. +================= +*/ +static void VertexLightingCollapse( void ) { + int stage; + shaderStage_t *bestStage; + int bestImageRank; + int rank; + + // if we aren't opaque, just use the first pass + if ( shader.sort == SS_OPAQUE ) { + + // pick the best texture for the single pass + bestStage = &stages[0]; + bestImageRank = -999999; + + for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) { + shaderStage_t *pStage = &stages[stage]; + + if ( !pStage->active ) { + break; + } + rank = 0; + + if ( pStage->bundle[0].isLightmap ) { + rank -= 100; + } + if ( pStage->bundle[0].tcGen != TCGEN_TEXTURE ) { + rank -= 5; + } + if ( pStage->bundle[0].numTexMods ) { + rank -= 5; + } + if ( pStage->rgbGen != CGEN_IDENTITY && pStage->rgbGen != CGEN_IDENTITY_LIGHTING ) { + rank -= 3; + } + + if ( rank > bestImageRank ) { + bestImageRank = rank; + bestStage = pStage; + } + } + + stages[0].bundle[0] = bestStage->bundle[0]; + stages[0].stateBits &= ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); + stages[0].stateBits |= GLS_DEPTHMASK_TRUE; + if ( shader.lightmapIndex == LIGHTMAP_NONE ) { + stages[0].rgbGen = CGEN_LIGHTING_DIFFUSE; + } else { + stages[0].rgbGen = CGEN_EXACT_VERTEX; + } + stages[0].alphaGen = AGEN_SKIP; + } else { + // don't use a lightmap (tesla coils) + if ( stages[0].bundle[0].isLightmap ) { + stages[0] = stages[1]; + } + + // if we were in a cross-fade cgen, hack it to normal + if ( stages[0].rgbGen == CGEN_ONE_MINUS_ENTITY || stages[1].rgbGen == CGEN_ONE_MINUS_ENTITY ) { + stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; + } + if ( ( stages[0].rgbGen == CGEN_WAVEFORM && stages[0].rgbWave.func == GF_SAWTOOTH ) + && ( stages[1].rgbGen == CGEN_WAVEFORM && stages[1].rgbWave.func == GF_INVERSE_SAWTOOTH ) ) { + stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; + } + if ( ( stages[0].rgbGen == CGEN_WAVEFORM && stages[0].rgbWave.func == GF_INVERSE_SAWTOOTH ) + && ( stages[1].rgbGen == CGEN_WAVEFORM && stages[1].rgbWave.func == GF_SAWTOOTH ) ) { + stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; + } + } + + for ( stage = 1; stage < MAX_SHADER_STAGES; stage++ ) { + shaderStage_t *pStage = &stages[stage]; + + if ( !pStage->active ) { + break; + } + + Com_Memset( pStage, 0, sizeof( *pStage ) ); + } +} + +/* +========================= +FinishShader + +Returns a freshly allocated shader with all the needed info +from the current global working shader +========================= +*/ +static shader_t *FinishShader( void ) { + int stage; + qboolean hasLightmapStage; + qboolean vertexLightmap; + + hasLightmapStage = qfalse; + vertexLightmap = qfalse; + + // + // set sky stuff appropriate + // + if ( shader.isSky ) { + shader.sort = SS_ENVIRONMENT; + } + + // + // set polygon offset + // + if ( shader.polygonOffset && !shader.sort ) { + shader.sort = SS_DECAL; + } + + // + // set appropriate stage information + // + for ( stage = 0; stage < MAX_SHADER_STAGES; ) { + shaderStage_t *pStage = &stages[stage]; + + if ( !pStage->active ) { + break; + } + + // check for a missing texture + if ( !pStage->bundle[0].image[0] ) { + ri.Printf( PRINT_WARNING, "Shader %s has a stage with no image\n", shader.name ); + pStage->active = qfalse; + stage++; + continue; + } + + // + // ditch this stage if it's detail and detail textures are disabled + // + if ( pStage->isDetail && !r_detailTextures->integer ) + { + int index; + + for(index = stage + 1; index < MAX_SHADER_STAGES; index++) + { + if(!stages[index].active) + break; + } + + if(index < MAX_SHADER_STAGES) + memmove(pStage, pStage + 1, sizeof(*pStage) * (index - stage)); + else + { + if(stage + 1 < MAX_SHADER_STAGES) + memmove(pStage, pStage + 1, sizeof(*pStage) * (index - stage - 1)); + + Com_Memset(&stages[index - 1], 0, sizeof(*stages)); + } + + continue; + } + + // + // default texture coordinate generation + // + if ( pStage->bundle[0].isLightmap ) { + if ( pStage->bundle[0].tcGen == TCGEN_BAD ) { + pStage->bundle[0].tcGen = TCGEN_LIGHTMAP; + } + hasLightmapStage = qtrue; + } else { + if ( pStage->bundle[0].tcGen == TCGEN_BAD ) { + pStage->bundle[0].tcGen = TCGEN_TEXTURE; + } + } + + + // not a true lightmap but we want to leave existing + // behaviour in place and not print out a warning + //if (pStage->rgbGen == CGEN_VERTEX) { + // vertexLightmap = qtrue; + //} + + + + // + // determine sort order and fog color adjustment + // + if ( ( pStage->stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) && + ( stages[0].stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) ) { + int blendSrcBits = pStage->stateBits & GLS_SRCBLEND_BITS; + int blendDstBits = pStage->stateBits & GLS_DSTBLEND_BITS; + + // fog color adjustment only works for blend modes that have a contribution + // that aproaches 0 as the modulate values aproach 0 -- + // GL_ONE, GL_ONE + // GL_ZERO, GL_ONE_MINUS_SRC_COLOR + // GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA + + // modulate, additive + if ( ( ( blendSrcBits == GLS_SRCBLEND_ONE ) && ( blendDstBits == GLS_DSTBLEND_ONE ) ) || + ( ( blendSrcBits == GLS_SRCBLEND_ZERO ) && ( blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_COLOR ) ) ) { + pStage->adjustColorsForFog = ACFF_MODULATE_RGB; + } + // strict blend + else if ( ( blendSrcBits == GLS_SRCBLEND_SRC_ALPHA ) && ( blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ) ) + { + pStage->adjustColorsForFog = ACFF_MODULATE_ALPHA; + } + // premultiplied alpha + else if ( ( blendSrcBits == GLS_SRCBLEND_ONE ) && ( blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ) ) + { + pStage->adjustColorsForFog = ACFF_MODULATE_RGBA; + } else { + // we can't adjust this one correctly, so it won't be exactly correct in fog + } + + // don't screw with sort order if this is a portal or environment + if ( !shader.sort ) { + // see through item, like a grill or grate + if ( pStage->stateBits & GLS_DEPTHMASK_TRUE ) { + shader.sort = SS_SEE_THROUGH; + } else { + shader.sort = SS_BLEND0; + } + } + } + + stage++; + } + + // there are times when you will need to manually apply a sort to + // opaque alpha tested shaders that have later blend passes + if ( !shader.sort ) { + shader.sort = SS_OPAQUE; + } + + // + // if we are in r_vertexLight mode, never use a lightmap texture + // + if ( stage > 1 && ( (r_vertexLight->integer && !r_uiFullScreen->integer) || glConfig.hardwareType == GLHW_PERMEDIA2 ) ) { + VertexLightingCollapse(); + stage = 1; + hasLightmapStage = qfalse; + } + + // + // look for multitexture potential + // + if ( stage > 1 && CollapseMultitexture() ) { + stage--; + } + + if ( shader.lightmapIndex >= 0 && !hasLightmapStage ) { + if (vertexLightmap) { + ri.Printf( PRINT_DEVELOPER, "WARNING: shader '%s' has VERTEX forced lightmap!\n", shader.name ); + } else { + ri.Printf( PRINT_DEVELOPER, "WARNING: shader '%s' has lightmap but no lightmap stage!\n", shader.name ); + shader.lightmapIndex = LIGHTMAP_NONE; + } + } + + + // + // compute number of passes + // + shader.numUnfoggedPasses = stage; + + // fogonly shaders don't have any normal passes + if (stage == 0 && !shader.isSky) + shader.sort = SS_FOG; + + // determine which stage iterator function is appropriate + ComputeStageIteratorFunc(); + + return GeneratePermanentShader(); +} + +//======================================================================================== + +/* +==================== +FindShaderInShaderText + +Scans the combined text description of all the shader files for +the given shader name. + +return NULL if not found + +If found, it will return a valid shader +===================== +*/ +static char *FindShaderInShaderText( const char *shadername ) { + + char *token, *p; + + int i, hash; + + hash = generateHashValue(shadername, MAX_SHADERTEXT_HASH); + + if(shaderTextHashTable[hash]) + { + for (i = 0; shaderTextHashTable[hash][i]; i++) + { + p = shaderTextHashTable[hash][i]; + token = COM_ParseExt(&p, qtrue); + + if(!Q_stricmp(token, shadername)) + return p; + } + } + + p = s_shaderText; + + if ( !p ) { + return NULL; + } + + // look for label + while ( 1 ) { + token = COM_ParseExt( &p, qtrue ); + if ( token[0] == 0 ) { + break; + } + + if ( !Q_stricmp( token, shadername ) ) { + return p; + } + else { + // skip the definition + SkipBracedSection( &p ); + } + } + + return NULL; +} + + +/* +================== +R_FindShaderByName + +Will always return a valid shader, but it might be the +default shader if the real one can't be found. +================== +*/ +shader_t *R_FindShaderByName( const char *name ) { + char strippedName[MAX_QPATH]; + int hash; + shader_t *sh; + + if ( (name==NULL) || (name[0] == 0) ) { + return tr.defaultShader; + } + + COM_StripExtension(name, strippedName, sizeof(strippedName)); + + hash = generateHashValue(strippedName, FILE_HASH_SIZE); + + // + // see if the shader is already loaded + // + for (sh=hashTable[hash]; sh; sh=sh->next) { + // NOTE: if there was no shader or image available with the name strippedName + // then a default shader is created with lightmapIndex == LIGHTMAP_NONE, so we + // have to check all default shaders otherwise for every call to R_FindShader + // with that same strippedName a new default shader is created. + if (Q_stricmp(sh->name, strippedName) == 0) { + // match found + return sh; + } + } + + return tr.defaultShader; +} + + +/* +=============== +R_FindShader + +Will always return a valid shader, but it might be the +default shader if the real one can't be found. + +In the interest of not requiring an explicit shader text entry to +be defined for every single image used in the game, three default +shader behaviors can be auto-created for any image: + +If lightmapIndex == LIGHTMAP_NONE, then the image will have +dynamic diffuse lighting applied to it, as apropriate for most +entity skin surfaces. + +If lightmapIndex == LIGHTMAP_2D, then the image will be used +for 2D rendering unless an explicit shader is found + +If lightmapIndex == LIGHTMAP_BY_VERTEX, then the image will use +the vertex rgba modulate values, as apropriate for misc_model +pre-lit surfaces. + +Other lightmapIndex values will have a lightmap stage created +and src*dest blending applied with the texture, as apropriate for +most world construction surfaces. + +=============== +*/ +shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImage ) { + char strippedName[MAX_QPATH]; + int i, hash; + char *shaderText; + image_t *image; + shader_t *sh; + + if ( name[0] == 0 ) { + return tr.defaultShader; + } + + // use (fullbright) vertex lighting if the bsp file doesn't have + // lightmaps + if ( lightmapIndex >= 0 && lightmapIndex >= tr.numLightmaps ) { + lightmapIndex = LIGHTMAP_BY_VERTEX; + } else if ( lightmapIndex < LIGHTMAP_2D ) { + // negative lightmap indexes cause stray pointers (think tr.lightmaps[lightmapIndex]) + ri.Printf( PRINT_WARNING, "WARNING: shader '%s' has invalid lightmap index of %d\n", name, lightmapIndex ); + lightmapIndex = LIGHTMAP_BY_VERTEX; + } + + COM_StripExtension(name, strippedName, sizeof(strippedName)); + + hash = generateHashValue(strippedName, FILE_HASH_SIZE); + + // + // see if the shader is already loaded + // + for (sh = hashTable[hash]; sh; sh = sh->next) { + // NOTE: if there was no shader or image available with the name strippedName + // then a default shader is created with lightmapIndex == LIGHTMAP_NONE, so we + // have to check all default shaders otherwise for every call to R_FindShader + // with that same strippedName a new default shader is created. + if ( (sh->lightmapIndex == lightmapIndex || sh->defaultShader) && + !Q_stricmp(sh->name, strippedName)) { + // match found + return sh; + } + } + + // clear the global shader + Com_Memset( &shader, 0, sizeof( shader ) ); + Com_Memset( &stages, 0, sizeof( stages ) ); + Q_strncpyz(shader.name, strippedName, sizeof(shader.name)); + shader.lightmapIndex = lightmapIndex; + for ( i = 0 ; i < MAX_SHADER_STAGES ; i++ ) { + stages[i].bundle[0].texMods = texMods[i]; + } + + // FIXME: set these "need" values apropriately + shader.needsNormal = qtrue; + shader.needsST1 = qtrue; + shader.needsST2 = qtrue; + shader.needsColor = qtrue; + + // + // attempt to define shader from an explicit parameter file + // + shaderText = FindShaderInShaderText( strippedName ); + if ( shaderText ) { + // enable this when building a pak file to get a global list + // of all explicit shaders + if ( r_printShaders->integer ) { + ri.Printf( PRINT_ALL, "*SHADER* %s\n", name ); + } + + if ( !ParseShader( &shaderText ) ) { + // had errors, so use default shader + shader.defaultShader = qtrue; + } + sh = FinishShader(); + return sh; + } + + + // + // if not defined in the in-memory shader descriptions, + // look for a single supported image file + // + image = R_FindImageFile( name, mipRawImage, mipRawImage, mipRawImage ? GL_REPEAT : GL_CLAMP_TO_EDGE ); + if ( !image ) { + ri.Printf( PRINT_DEVELOPER, "Couldn't find image file for shader %s\n", name ); + shader.defaultShader = qtrue; + return FinishShader(); + } + + // + // create the default shading commands + // + if ( shader.lightmapIndex == LIGHTMAP_NONE ) { + // dynamic colors at vertexes + stages[0].bundle[0].image[0] = image; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_LIGHTING_DIFFUSE; + stages[0].stateBits = GLS_DEFAULT; + } else if ( shader.lightmapIndex == LIGHTMAP_BY_VERTEX ) { + // explicit colors at vertexes + stages[0].bundle[0].image[0] = image; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_EXACT_VERTEX; + stages[0].alphaGen = AGEN_SKIP; + stages[0].stateBits = GLS_DEFAULT; + } else if ( shader.lightmapIndex == LIGHTMAP_2D ) { + // GUI elements + stages[0].bundle[0].image[0] = image; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_VERTEX; + stages[0].alphaGen = AGEN_VERTEX; + stages[0].stateBits = GLS_DEPTHTEST_DISABLE | + GLS_SRCBLEND_SRC_ALPHA | + GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; + } else if ( shader.lightmapIndex == LIGHTMAP_WHITEIMAGE ) { + // fullbright level + stages[0].bundle[0].image[0] = tr.whiteImage; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; + stages[0].stateBits = GLS_DEFAULT; + + stages[1].bundle[0].image[0] = image; + stages[1].active = qtrue; + stages[1].rgbGen = CGEN_IDENTITY; + stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; + } else { + // two pass lightmap + stages[0].bundle[0].image[0] = tr.lightmaps[shader.lightmapIndex]; + stages[0].bundle[0].isLightmap = qtrue; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_IDENTITY; // lightmaps are scaled on creation + // for identitylight + stages[0].stateBits = GLS_DEFAULT; + + stages[1].bundle[0].image[0] = image; + stages[1].active = qtrue; + stages[1].rgbGen = CGEN_IDENTITY; + stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; + } + + return FinishShader(); +} + + +qhandle_t RE_RegisterShaderFromImage(const char *name, int lightmapIndex, image_t *image, qboolean mipRawImage) { + int i, hash; + shader_t *sh; + + hash = generateHashValue(name, FILE_HASH_SIZE); + + // probably not necessary since this function + // only gets called from tr_font.c with lightmapIndex == LIGHTMAP_2D + // but better safe than sorry. + if ( lightmapIndex >= tr.numLightmaps ) { + lightmapIndex = LIGHTMAP_WHITEIMAGE; + } + + // + // see if the shader is already loaded + // + for (sh=hashTable[hash]; sh; sh=sh->next) { + // NOTE: if there was no shader or image available with the name strippedName + // then a default shader is created with lightmapIndex == LIGHTMAP_NONE, so we + // have to check all default shaders otherwise for every call to R_FindShader + // with that same strippedName a new default shader is created. + if ( (sh->lightmapIndex == lightmapIndex || sh->defaultShader) && + // index by name + !Q_stricmp(sh->name, name)) { + // match found + return sh->index; + } + } + + // clear the global shader + Com_Memset( &shader, 0, sizeof( shader ) ); + Com_Memset( &stages, 0, sizeof( stages ) ); + Q_strncpyz(shader.name, name, sizeof(shader.name)); + shader.lightmapIndex = lightmapIndex; + for ( i = 0 ; i < MAX_SHADER_STAGES ; i++ ) { + stages[i].bundle[0].texMods = texMods[i]; + } + + // FIXME: set these "need" values apropriately + shader.needsNormal = qtrue; + shader.needsST1 = qtrue; + shader.needsST2 = qtrue; + shader.needsColor = qtrue; + + // + // create the default shading commands + // + if ( shader.lightmapIndex == LIGHTMAP_NONE ) { + // dynamic colors at vertexes + stages[0].bundle[0].image[0] = image; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_LIGHTING_DIFFUSE; + stages[0].stateBits = GLS_DEFAULT; + } else if ( shader.lightmapIndex == LIGHTMAP_BY_VERTEX ) { + // explicit colors at vertexes + stages[0].bundle[0].image[0] = image; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_EXACT_VERTEX; + stages[0].alphaGen = AGEN_SKIP; + stages[0].stateBits = GLS_DEFAULT; + } else if ( shader.lightmapIndex == LIGHTMAP_2D ) { + // GUI elements + stages[0].bundle[0].image[0] = image; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_VERTEX; + stages[0].alphaGen = AGEN_VERTEX; + stages[0].stateBits = GLS_DEPTHTEST_DISABLE | + GLS_SRCBLEND_SRC_ALPHA | + GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; + } else if ( shader.lightmapIndex == LIGHTMAP_WHITEIMAGE ) { + // fullbright level + stages[0].bundle[0].image[0] = tr.whiteImage; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; + stages[0].stateBits = GLS_DEFAULT; + + stages[1].bundle[0].image[0] = image; + stages[1].active = qtrue; + stages[1].rgbGen = CGEN_IDENTITY; + stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; + } else { + // two pass lightmap + stages[0].bundle[0].image[0] = tr.lightmaps[shader.lightmapIndex]; + stages[0].bundle[0].isLightmap = qtrue; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_IDENTITY; // lightmaps are scaled on creation + // for identitylight + stages[0].stateBits = GLS_DEFAULT; + + stages[1].bundle[0].image[0] = image; + stages[1].active = qtrue; + stages[1].rgbGen = CGEN_IDENTITY; + stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; + } + + sh = FinishShader(); + return sh->index; +} + + +/* +==================== +RE_RegisterShader + +This is the exported shader entry point for the rest of the system +It will always return an index that will be valid. + +This should really only be used for explicit shaders, because there is no +way to ask for different implicit lighting modes (vertex, lightmap, etc) +==================== +*/ +qhandle_t RE_RegisterShaderLightMap( const char *name, int lightmapIndex ) { + shader_t *sh; + + if ( strlen( name ) >= MAX_QPATH ) { + ri.Printf( PRINT_ALL, "Shader name exceeds MAX_QPATH\n" ); + return 0; + } + + sh = R_FindShader( name, lightmapIndex, qtrue ); + + // we want to return 0 if the shader failed to + // load for some reason, but R_FindShader should + // still keep a name allocated for it, so if + // something calls RE_RegisterShader again with + // the same name, we don't try looking for it again + if ( sh->defaultShader ) { + return 0; + } + + return sh->index; +} + + +/* +==================== +RE_RegisterShader + +This is the exported shader entry point for the rest of the system +It will always return an index that will be valid. + +This should really only be used for explicit shaders, because there is no +way to ask for different implicit lighting modes (vertex, lightmap, etc) +==================== +*/ +qhandle_t RE_RegisterShader( const char *name ) { + shader_t *sh; + + if ( strlen( name ) >= MAX_QPATH ) { + ri.Printf( PRINT_ALL, "Shader name exceeds MAX_QPATH\n" ); + return 0; + } + + sh = R_FindShader( name, LIGHTMAP_2D, qtrue ); + + // we want to return 0 if the shader failed to + // load for some reason, but R_FindShader should + // still keep a name allocated for it, so if + // something calls RE_RegisterShader again with + // the same name, we don't try looking for it again + if ( sh->defaultShader ) { + return 0; + } + + return sh->index; +} + + +/* +==================== +RE_RegisterShaderNoMip + +For menu graphics that should never be picmiped +==================== +*/ +qhandle_t RE_RegisterShaderNoMip( const char *name ) { + shader_t *sh; + + if ( strlen( name ) >= MAX_QPATH ) { + ri.Printf( PRINT_ALL, "Shader name exceeds MAX_QPATH\n" ); + return 0; + } + + sh = R_FindShader( name, LIGHTMAP_2D, qfalse ); + + // we want to return 0 if the shader failed to + // load for some reason, but R_FindShader should + // still keep a name allocated for it, so if + // something calls RE_RegisterShader again with + // the same name, we don't try looking for it again + if ( sh->defaultShader ) { + return 0; + } + + return sh->index; +} + +/* +==================== +R_GetShaderByHandle + +When a handle is passed in by another module, this range checks +it and returns a valid (possibly default) shader_t to be used internally. +==================== +*/ +shader_t *R_GetShaderByHandle( qhandle_t hShader ) { + if ( hShader < 0 ) { + ri.Printf( PRINT_WARNING, "R_GetShaderByHandle: out of range hShader '%d'\n", hShader ); + return tr.defaultShader; + } + if ( hShader >= tr.numShaders ) { + ri.Printf( PRINT_WARNING, "R_GetShaderByHandle: out of range hShader '%d'\n", hShader ); + return tr.defaultShader; + } + return tr.shaders[hShader]; +} + +/* +=============== +R_ShaderList_f + +Dump information on all valid shaders to the console +A second parameter will cause it to print in sorted order +=============== +*/ +void R_ShaderList_f (void) { + int i; + int count; + shader_t *shader; + + ri.Printf (PRINT_ALL, "-----------------------\n"); + + count = 0; + for ( i = 0 ; i < tr.numShaders ; i++ ) { + if ( ri.Cmd_Argc() > 1 ) { + shader = tr.sortedShaders[i]; + } else { + shader = tr.shaders[i]; + } + + ri.Printf( PRINT_ALL, "%i ", shader->numUnfoggedPasses ); + + if (shader->lightmapIndex >= 0 ) { + ri.Printf (PRINT_ALL, "L "); + } else { + ri.Printf (PRINT_ALL, " "); + } + if ( shader->multitextureEnv == GL_ADD ) { + ri.Printf( PRINT_ALL, "MT(a) " ); + } else if ( shader->multitextureEnv == GL_MODULATE ) { + ri.Printf( PRINT_ALL, "MT(m) " ); + } else if ( shader->multitextureEnv == GL_DECAL ) { + ri.Printf( PRINT_ALL, "MT(d) " ); + } else { + ri.Printf( PRINT_ALL, " " ); + } + if ( shader->explicitlyDefined ) { + ri.Printf( PRINT_ALL, "E " ); + } else { + ri.Printf( PRINT_ALL, " " ); + } + + if ( shader->optimalStageIteratorFunc == RB_StageIteratorGeneric ) { + ri.Printf( PRINT_ALL, "gen " ); + } else if ( shader->optimalStageIteratorFunc == RB_StageIteratorSky ) { + ri.Printf( PRINT_ALL, "sky " ); + } else if ( shader->optimalStageIteratorFunc == RB_StageIteratorLightmappedMultitexture ) { + ri.Printf( PRINT_ALL, "lmmt" ); + } else if ( shader->optimalStageIteratorFunc == RB_StageIteratorVertexLitTexture ) { + ri.Printf( PRINT_ALL, "vlt " ); + } else { + ri.Printf( PRINT_ALL, " " ); + } + + if ( shader->defaultShader ) { + ri.Printf (PRINT_ALL, ": %s (DEFAULTED)\n", shader->name); + } else { + ri.Printf (PRINT_ALL, ": %s\n", shader->name); + } + count++; + } + ri.Printf (PRINT_ALL, "%i total shaders\n", count); + ri.Printf (PRINT_ALL, "------------------\n"); +} + +/* +==================== +ScanAndLoadShaderFiles + +Finds and loads all .shader files, combining them into +a single large text block that can be scanned for shader names +===================== +*/ +#define MAX_SHADER_FILES 4096 +static void ScanAndLoadShaderFiles( void ) +{ + char **shaderFiles; + char *buffers[MAX_SHADER_FILES]; + char *p; + int numShaderFiles; + int i; + char *oldp, *token, *hashMem, *textEnd; + int shaderTextHashTableSizes[MAX_SHADERTEXT_HASH], hash, size; + + long sum = 0, summand; + // scan for shader files + shaderFiles = ri.FS_ListFiles( "scripts", ".shader", &numShaderFiles ); + + if ( !shaderFiles || !numShaderFiles ) + { + ri.Printf( PRINT_WARNING, "WARNING: no shader files found\n" ); + return; + } + + if ( numShaderFiles > MAX_SHADER_FILES ) { + numShaderFiles = MAX_SHADER_FILES; + } + + // load and parse shader files + for ( i = 0; i < numShaderFiles; i++ ) + { + char filename[MAX_QPATH]; + + Com_sprintf( filename, sizeof( filename ), "scripts/%s", shaderFiles[i] ); + ri.Printf( PRINT_DEVELOPER, "...loading '%s'\n", filename ); + summand = ri.FS_ReadFile( filename, (void **)&buffers[i] ); + + if ( !buffers[i] ) + ri.Error( ERR_DROP, "Couldn't load %s", filename ); + + // Do a simple check on the shader structure in that file to make sure one bad shader file cannot fuck up all other shaders. + p = buffers[i]; + while(1) + { + token = COM_ParseExt(&p, qtrue); + + if(!*token) + break; + + oldp = p; + + token = COM_ParseExt(&p, qtrue); + if(token[0] != '{' && token[1] != '\0') + { + ri.Printf(PRINT_WARNING, "WARNING: Bad shader file %s has incorrect syntax.\n", filename); + ri.FS_FreeFile(buffers[i]); + buffers[i] = NULL; + break; + } + + SkipBracedSection(&oldp); + p = oldp; + } + + + if (buffers[i]) + sum += summand; + } + + // build single large buffer + s_shaderText = ri.Hunk_Alloc( sum + numShaderFiles*2, h_low ); + s_shaderText[ 0 ] = '\0'; + textEnd = s_shaderText; + + // free in reverse order, so the temp files are all dumped + for ( i = numShaderFiles - 1; i >= 0 ; i-- ) + { + if ( !buffers[i] ) + continue; + + strcat( textEnd, buffers[i] ); + strcat( textEnd, "\n" ); + textEnd += strlen( textEnd ); + ri.FS_FreeFile( buffers[i] ); + } + + COM_Compress( s_shaderText ); + + // free up memory + ri.FS_FreeFileList( shaderFiles ); + + Com_Memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes)); + size = 0; + + p = s_shaderText; + // look for shader names + while ( 1 ) { + token = COM_ParseExt( &p, qtrue ); + if ( token[0] == 0 ) { + break; + } + + hash = generateHashValue(token, MAX_SHADERTEXT_HASH); + shaderTextHashTableSizes[hash]++; + size++; + SkipBracedSection(&p); + } + + size += MAX_SHADERTEXT_HASH; + + hashMem = ri.Hunk_Alloc( size * sizeof(char *), h_low ); + + for (i = 0; i < MAX_SHADERTEXT_HASH; i++) { + shaderTextHashTable[i] = (char **) hashMem; + hashMem = ((char *) hashMem) + ((shaderTextHashTableSizes[i] + 1) * sizeof(char *)); + } + + Com_Memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes)); + + p = s_shaderText; + // look for shader names + while ( 1 ) { + oldp = p; + token = COM_ParseExt( &p, qtrue ); + if ( token[0] == 0 ) { + break; + } + + hash = generateHashValue(token, MAX_SHADERTEXT_HASH); + shaderTextHashTable[hash][shaderTextHashTableSizes[hash]++] = oldp; + + SkipBracedSection(&p); + } + + return; + +} + + +/* +==================== +CreateInternalShaders +==================== +*/ +static void CreateInternalShaders( void ) { + tr.numShaders = 0; + + // init the default shader + Com_Memset( &shader, 0, sizeof( shader ) ); + Com_Memset( &stages, 0, sizeof( stages ) ); + + Q_strncpyz( shader.name, "", sizeof( shader.name ) ); + + shader.lightmapIndex = LIGHTMAP_NONE; + stages[0].bundle[0].image[0] = tr.defaultImage; + stages[0].active = qtrue; + stages[0].stateBits = GLS_DEFAULT; + tr.defaultShader = FinishShader(); + + // shadow shader is just a marker + Q_strncpyz( shader.name, "", sizeof( shader.name ) ); + shader.sort = SS_STENCIL_SHADOW; + tr.shadowShader = FinishShader(); +} + +static void CreateExternalShaders( void ) { + tr.projectionShadowShader = R_FindShader( "projectionShadow", LIGHTMAP_NONE, qtrue ); + tr.flareShader = R_FindShader( "flareShader", LIGHTMAP_NONE, qtrue ); + + // Hack to make fogging work correctly on flares. Fog colors are calculated + // in tr_flare.c already. + if(!tr.flareShader->defaultShader) + { + int index; + + for(index = 0; index < tr.flareShader->numUnfoggedPasses; index++) + { + tr.flareShader->stages[index]->adjustColorsForFog = ACFF_NONE; + tr.flareShader->stages[index]->stateBits |= GLS_DEPTHTEST_DISABLE; + } + } + + tr.sunShader = R_FindShader( "sun", LIGHTMAP_NONE, qtrue ); +} + +/* +================== +R_InitShaders +================== +*/ +void R_InitShaders( void ) { + ri.Printf( PRINT_ALL, "Initializing Shaders\n" ); + + Com_Memset(hashTable, 0, sizeof(hashTable)); + + CreateInternalShaders(); + + ScanAndLoadShaderFiles(); + + CreateExternalShaders(); +} diff --git a/src/renderergl1/tr_shadows.c b/src/renderergl1/tr_shadows.c new file mode 100644 index 00000000..f412b00d --- /dev/null +++ b/src/renderergl1/tr_shadows.c @@ -0,0 +1,343 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +#include "tr_local.h" + + +/* + + for a projection shadow: + + point[x] += light vector * ( z - shadow plane ) + point[y] += + point[z] = shadow plane + + 1 0 light[x] / light[z] + +*/ + +typedef struct { + int i2; + int facing; +} edgeDef_t; + +#define MAX_EDGE_DEFS 32 + +static edgeDef_t edgeDefs[SHADER_MAX_VERTEXES][MAX_EDGE_DEFS]; +static int numEdgeDefs[SHADER_MAX_VERTEXES]; +static int facing[SHADER_MAX_INDEXES/3]; + +void R_AddEdgeDef( int i1, int i2, int facing ) { + int c; + + c = numEdgeDefs[ i1 ]; + if ( c == MAX_EDGE_DEFS ) { + return; // overflow + } + edgeDefs[ i1 ][ c ].i2 = i2; + edgeDefs[ i1 ][ c ].facing = facing; + + numEdgeDefs[ i1 ]++; +} + +void R_RenderShadowEdges( void ) { + int i; + +#if 0 + int numTris; + + // dumb way -- render every triangle's edges + numTris = tess.numIndexes / 3; + + for ( i = 0 ; i < numTris ; i++ ) { + int i1, i2, i3; + + if ( !facing[i] ) { + continue; + } + + i1 = tess.indexes[ i*3 + 0 ]; + i2 = tess.indexes[ i*3 + 1 ]; + i3 = tess.indexes[ i*3 + 2 ]; + + qglBegin( GL_TRIANGLE_STRIP ); + qglVertex3fv( tess.xyz[ i1 ] ); + qglVertex3fv( tess.xyz[ i1 + tess.numVertexes ] ); + qglVertex3fv( tess.xyz[ i2 ] ); + qglVertex3fv( tess.xyz[ i2 + tess.numVertexes ] ); + qglVertex3fv( tess.xyz[ i3 ] ); + qglVertex3fv( tess.xyz[ i3 + tess.numVertexes ] ); + qglVertex3fv( tess.xyz[ i1 ] ); + qglVertex3fv( tess.xyz[ i1 + tess.numVertexes ] ); + qglEnd(); + } +#else + int c, c2; + int j, k; + int i2; + int c_edges, c_rejected; + int hit[2]; + + // an edge is NOT a silhouette edge if its face doesn't face the light, + // or if it has a reverse paired edge that also faces the light. + // A well behaved polyhedron would have exactly two faces for each edge, + // but lots of models have dangling edges or overfanned edges + c_edges = 0; + c_rejected = 0; + + for ( i = 0 ; i < tess.numVertexes ; i++ ) { + c = numEdgeDefs[ i ]; + for ( j = 0 ; j < c ; j++ ) { + if ( !edgeDefs[ i ][ j ].facing ) { + continue; + } + + hit[0] = 0; + hit[1] = 0; + + i2 = edgeDefs[ i ][ j ].i2; + c2 = numEdgeDefs[ i2 ]; + for ( k = 0 ; k < c2 ; k++ ) { + if ( edgeDefs[ i2 ][ k ].i2 == i ) { + hit[ edgeDefs[ i2 ][ k ].facing ]++; + } + } + + // if it doesn't share the edge with another front facing + // triangle, it is a sil edge + if ( hit[ 1 ] == 0 ) { + qglBegin( GL_TRIANGLE_STRIP ); + qglVertex3fv( tess.xyz[ i ] ); + qglVertex3fv( tess.xyz[ i + tess.numVertexes ] ); + qglVertex3fv( tess.xyz[ i2 ] ); + qglVertex3fv( tess.xyz[ i2 + tess.numVertexes ] ); + qglEnd(); + c_edges++; + } else { + c_rejected++; + } + } + } +#endif +} + +/* +================= +RB_ShadowTessEnd + +triangleFromEdge[ v1 ][ v2 ] + + + set triangle from edge( v1, v2, tri ) + if ( facing[ triangleFromEdge[ v1 ][ v2 ] ] && !facing[ triangleFromEdge[ v2 ][ v1 ] ) { + } +================= +*/ +void RB_ShadowTessEnd( void ) { + int i; + int numTris; + vec3_t lightDir; + GLboolean rgba[4]; + + // we can only do this if we have enough space in the vertex buffers + if ( tess.numVertexes >= SHADER_MAX_VERTEXES / 2 ) { + return; + } + + if ( glConfig.stencilBits < 4 ) { + return; + } + + VectorCopy( backEnd.currentEntity->lightDir, lightDir ); + + // project vertexes away from light direction + for ( i = 0 ; i < tess.numVertexes ; i++ ) { + VectorMA( tess.xyz[i], -512, lightDir, tess.xyz[i+tess.numVertexes] ); + } + + // decide which triangles face the light + Com_Memset( numEdgeDefs, 0, 4 * tess.numVertexes ); + + numTris = tess.numIndexes / 3; + for ( i = 0 ; i < numTris ; i++ ) { + int i1, i2, i3; + vec3_t d1, d2, normal; + float *v1, *v2, *v3; + float d; + + i1 = tess.indexes[ i*3 + 0 ]; + i2 = tess.indexes[ i*3 + 1 ]; + i3 = tess.indexes[ i*3 + 2 ]; + + v1 = tess.xyz[ i1 ]; + v2 = tess.xyz[ i2 ]; + v3 = tess.xyz[ i3 ]; + + VectorSubtract( v2, v1, d1 ); + VectorSubtract( v3, v1, d2 ); + CrossProduct( d1, d2, normal ); + + d = DotProduct( normal, lightDir ); + if ( d > 0 ) { + facing[ i ] = 1; + } else { + facing[ i ] = 0; + } + + // create the edges + R_AddEdgeDef( i1, i2, facing[ i ] ); + R_AddEdgeDef( i2, i3, facing[ i ] ); + R_AddEdgeDef( i3, i1, facing[ i ] ); + } + + // draw the silhouette edges + + GL_Bind( tr.whiteImage ); + qglEnable( GL_CULL_FACE ); + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO ); + qglColor3f( 0.2f, 0.2f, 0.2f ); + + // don't write to the color buffer + qglGetBooleanv(GL_COLOR_WRITEMASK, rgba); + qglColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); + + qglEnable( GL_STENCIL_TEST ); + qglStencilFunc( GL_ALWAYS, 1, 255 ); + + // mirrors have the culling order reversed + if ( backEnd.viewParms.isMirror ) { + qglCullFace( GL_FRONT ); + qglStencilOp( GL_KEEP, GL_KEEP, GL_INCR ); + + R_RenderShadowEdges(); + + qglCullFace( GL_BACK ); + qglStencilOp( GL_KEEP, GL_KEEP, GL_DECR ); + + R_RenderShadowEdges(); + } else { + qglCullFace( GL_BACK ); + qglStencilOp( GL_KEEP, GL_KEEP, GL_INCR ); + + R_RenderShadowEdges(); + + qglCullFace( GL_FRONT ); + qglStencilOp( GL_KEEP, GL_KEEP, GL_DECR ); + + R_RenderShadowEdges(); + } + + + // reenable writing to the color buffer + qglColorMask(rgba[0], rgba[1], rgba[2], rgba[3]); +} + + +/* +================= +RB_ShadowFinish + +Darken everything that is is a shadow volume. +We have to delay this until everything has been shadowed, +because otherwise shadows from different body parts would +overlap and double darken. +================= +*/ +void RB_ShadowFinish( void ) { + if ( r_shadows->integer != 2 ) { + return; + } + if ( glConfig.stencilBits < 4 ) { + return; + } + qglEnable( GL_STENCIL_TEST ); + qglStencilFunc( GL_NOTEQUAL, 0, 255 ); + + qglDisable (GL_CLIP_PLANE0); + qglDisable (GL_CULL_FACE); + + GL_Bind( tr.whiteImage ); + + qglLoadIdentity (); + + qglColor3f( 0.6f, 0.6f, 0.6f ); + GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO ); + +// qglColor3f( 1, 0, 0 ); +// GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO ); + + qglBegin( GL_QUADS ); + qglVertex3f( -100, 100, -10 ); + qglVertex3f( 100, 100, -10 ); + qglVertex3f( 100, -100, -10 ); + qglVertex3f( -100, -100, -10 ); + qglEnd (); + + qglColor4f(1,1,1,1); + qglDisable( GL_STENCIL_TEST ); +} + + +/* +================= +RB_ProjectionShadowDeform + +================= +*/ +void RB_ProjectionShadowDeform( void ) { + float *xyz; + int i; + float h; + vec3_t ground; + vec3_t light; + float groundDist; + float d; + vec3_t lightDir; + + xyz = ( float * ) tess.xyz; + + ground[0] = backEnd.or.axis[0][2]; + ground[1] = backEnd.or.axis[1][2]; + ground[2] = backEnd.or.axis[2][2]; + + groundDist = backEnd.or.origin[2] - backEnd.currentEntity->e.shadowPlane; + + VectorCopy( backEnd.currentEntity->lightDir, lightDir ); + d = DotProduct( lightDir, ground ); + // don't let the shadows get too long or go negative + if ( d < 0.5 ) { + VectorMA( lightDir, (0.5 - d), ground, lightDir ); + d = DotProduct( lightDir, ground ); + } + d = 1.0 / d; + + light[0] = lightDir[0] * d; + light[1] = lightDir[1] * d; + light[2] = lightDir[2] * d; + + for ( i = 0; i < tess.numVertexes; i++, xyz += 4 ) { + h = DotProduct( xyz, ground ) + groundDist; + + xyz[0] -= light[0] * h; + xyz[1] -= light[1] * h; + xyz[2] -= light[2] * h; + } +} diff --git a/src/renderergl1/tr_sky.c b/src/renderergl1/tr_sky.c new file mode 100644 index 00000000..3b90fdeb --- /dev/null +++ b/src/renderergl1/tr_sky.c @@ -0,0 +1,846 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_sky.c +#include "tr_local.h" + +#define SKY_SUBDIVISIONS 8 +#define HALF_SKY_SUBDIVISIONS (SKY_SUBDIVISIONS/2) + +static float s_cloudTexCoords[6][SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1][2]; +static float s_cloudTexP[6][SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1]; + +/* +=================================================================================== + +POLYGON TO BOX SIDE PROJECTION + +=================================================================================== +*/ + +static vec3_t sky_clip[6] = +{ + {1,1,0}, + {1,-1,0}, + {0,-1,1}, + {0,1,1}, + {1,0,1}, + {-1,0,1} +}; + +static float sky_mins[2][6], sky_maxs[2][6]; +static float sky_min, sky_max; + +/* +================ +AddSkyPolygon +================ +*/ +static void AddSkyPolygon (int nump, vec3_t vecs) +{ + int i,j; + vec3_t v, av; + float s, t, dv; + int axis; + float *vp; + // s = [0]/[2], t = [1]/[2] + static int vec_to_st[6][3] = + { + {-2,3,1}, + {2,3,-1}, + + {1,3,2}, + {-1,3,-2}, + + {-2,-1,3}, + {-2,1,-3} + + // {-1,2,3}, + // {1,2,-3} + }; + + // decide which face it maps to + VectorCopy (vec3_origin, v); + for (i=0, vp=vecs ; i av[1] && av[0] > av[2]) + { + if (v[0] < 0) + axis = 1; + else + axis = 0; + } + else if (av[1] > av[2] && av[1] > av[0]) + { + if (v[1] < 0) + axis = 3; + else + axis = 2; + } + else + { + if (v[2] < 0) + axis = 5; + else + axis = 4; + } + + // project new texture coords + for (i=0 ; i 0) + dv = vecs[j - 1]; + else + dv = -vecs[-j - 1]; + if (dv < 0.001) + continue; // don't divide by zero + j = vec_to_st[axis][0]; + if (j < 0) + s = -vecs[-j -1] / dv; + else + s = vecs[j-1] / dv; + j = vec_to_st[axis][1]; + if (j < 0) + t = -vecs[-j -1] / dv; + else + t = vecs[j-1] / dv; + + if (s < sky_mins[0][axis]) + sky_mins[0][axis] = s; + if (t < sky_mins[1][axis]) + sky_mins[1][axis] = t; + if (s > sky_maxs[0][axis]) + sky_maxs[0][axis] = s; + if (t > sky_maxs[1][axis]) + sky_maxs[1][axis] = t; + } +} + +#define ON_EPSILON 0.1f // point on plane side epsilon +#define MAX_CLIP_VERTS 64 +/* +================ +ClipSkyPolygon +================ +*/ +static void ClipSkyPolygon (int nump, vec3_t vecs, int stage) +{ + float *norm; + float *v; + qboolean front, back; + float d, e; + float dists[MAX_CLIP_VERTS]; + int sides[MAX_CLIP_VERTS]; + vec3_t newv[2][MAX_CLIP_VERTS]; + int newc[2]; + int i, j; + + if (nump > MAX_CLIP_VERTS-2) + ri.Error (ERR_DROP, "ClipSkyPolygon: MAX_CLIP_VERTS"); + if (stage == 6) + { // fully clipped, so draw it + AddSkyPolygon (nump, vecs); + return; + } + + front = back = qfalse; + norm = sky_clip[stage]; + for (i=0, v = vecs ; i ON_EPSILON) + { + front = qtrue; + sides[i] = SIDE_FRONT; + } + else if (d < -ON_EPSILON) + { + back = qtrue; + sides[i] = SIDE_BACK; + } + else + sides[i] = SIDE_ON; + dists[i] = d; + } + + if (!front || !back) + { // not clipped + ClipSkyPolygon (nump, vecs, stage+1); + return; + } + + // clip it + sides[i] = sides[0]; + dists[i] = dists[0]; + VectorCopy (vecs, (vecs+(i*3)) ); + newc[0] = newc[1] = 0; + + for (i=0, v = vecs ; inumIndexes; i += 3 ) + { + for (j = 0 ; j < 3 ; j++) + { + VectorSubtract( input->xyz[input->indexes[i+j]], + backEnd.viewParms.or.origin, + p[j] ); + } + ClipSkyPolygon( 3, p[0], 0 ); + } +} + +/* +=================================================================================== + +CLOUD VERTEX GENERATION + +=================================================================================== +*/ + +/* +** MakeSkyVec +** +** Parms: s, t range from -1 to 1 +*/ +static void MakeSkyVec( float s, float t, int axis, float outSt[2], vec3_t outXYZ ) +{ + // 1 = s, 2 = t, 3 = 2048 + static int st_to_vec[6][3] = + { + {3,-1,2}, + {-3,1,2}, + + {1,3,2}, + {-1,-3,2}, + + {-2,-1,3}, // 0 degrees yaw, look straight up + {2,-1,-3} // look straight down + }; + + vec3_t b; + int j, k; + float boxSize; + + boxSize = backEnd.viewParms.zFar / 1.75; // div sqrt(3) + b[0] = s*boxSize; + b[1] = t*boxSize; + b[2] = boxSize; + + for (j=0 ; j<3 ; j++) + { + k = st_to_vec[axis][j]; + if (k < 0) + { + outXYZ[j] = -b[-k - 1]; + } + else + { + outXYZ[j] = b[k - 1]; + } + } + + // avoid bilerp seam + s = (s+1)*0.5; + t = (t+1)*0.5; + if (s < sky_min) + { + s = sky_min; + } + else if (s > sky_max) + { + s = sky_max; + } + + if (t < sky_min) + { + t = sky_min; + } + else if (t > sky_max) + { + t = sky_max; + } + + t = 1.0 - t; + + + if ( outSt ) + { + outSt[0] = s; + outSt[1] = t; + } +} + +static int sky_texorder[6] = {0,2,1,3,4,5}; +static vec3_t s_skyPoints[SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1]; +static float s_skyTexCoords[SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1][2]; + +static void DrawSkySide( struct image_s *image, const int mins[2], const int maxs[2] ) +{ + int s, t; + + GL_Bind( image ); + + for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t < maxs[1]+HALF_SKY_SUBDIVISIONS; t++ ) + { + qglBegin( GL_TRIANGLE_STRIP ); + + for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ ) + { + qglTexCoord2fv( s_skyTexCoords[t][s] ); + qglVertex3fv( s_skyPoints[t][s] ); + + qglTexCoord2fv( s_skyTexCoords[t+1][s] ); + qglVertex3fv( s_skyPoints[t+1][s] ); + } + + qglEnd(); + } +} + +static void DrawSkyBox( shader_t *shader ) +{ + int i; + + sky_min = 0; + sky_max = 1; + + Com_Memset( s_skyTexCoords, 0, sizeof( s_skyTexCoords ) ); + + for (i=0 ; i<6 ; i++) + { + int sky_mins_subd[2], sky_maxs_subd[2]; + int s, t; + + sky_mins[0][i] = floor( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; + sky_mins[1][i] = floor( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; + sky_maxs[0][i] = ceil( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; + sky_maxs[1][i] = ceil( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; + + if ( ( sky_mins[0][i] >= sky_maxs[0][i] ) || + ( sky_mins[1][i] >= sky_maxs[1][i] ) ) + { + continue; + } + + sky_mins_subd[0] = sky_mins[0][i] * HALF_SKY_SUBDIVISIONS; + sky_mins_subd[1] = sky_mins[1][i] * HALF_SKY_SUBDIVISIONS; + sky_maxs_subd[0] = sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS; + sky_maxs_subd[1] = sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS; + + if ( sky_mins_subd[0] < -HALF_SKY_SUBDIVISIONS ) + sky_mins_subd[0] = -HALF_SKY_SUBDIVISIONS; + else if ( sky_mins_subd[0] > HALF_SKY_SUBDIVISIONS ) + sky_mins_subd[0] = HALF_SKY_SUBDIVISIONS; + if ( sky_mins_subd[1] < -HALF_SKY_SUBDIVISIONS ) + sky_mins_subd[1] = -HALF_SKY_SUBDIVISIONS; + else if ( sky_mins_subd[1] > HALF_SKY_SUBDIVISIONS ) + sky_mins_subd[1] = HALF_SKY_SUBDIVISIONS; + + if ( sky_maxs_subd[0] < -HALF_SKY_SUBDIVISIONS ) + sky_maxs_subd[0] = -HALF_SKY_SUBDIVISIONS; + else if ( sky_maxs_subd[0] > HALF_SKY_SUBDIVISIONS ) + sky_maxs_subd[0] = HALF_SKY_SUBDIVISIONS; + if ( sky_maxs_subd[1] < -HALF_SKY_SUBDIVISIONS ) + sky_maxs_subd[1] = -HALF_SKY_SUBDIVISIONS; + else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS ) + sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS; + + // + // iterate through the subdivisions + // + for ( t = sky_mins_subd[1]+HALF_SKY_SUBDIVISIONS; t <= sky_maxs_subd[1]+HALF_SKY_SUBDIVISIONS; t++ ) + { + for ( s = sky_mins_subd[0]+HALF_SKY_SUBDIVISIONS; s <= sky_maxs_subd[0]+HALF_SKY_SUBDIVISIONS; s++ ) + { + MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, + ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, + i, + s_skyTexCoords[t][s], + s_skyPoints[t][s] ); + } + } + + DrawSkySide( shader->sky.outerbox[sky_texorder[i]], + sky_mins_subd, + sky_maxs_subd ); + } + +} + +static void FillCloudySkySide( const int mins[2], const int maxs[2], qboolean addIndexes ) +{ + int s, t; + int vertexStart = tess.numVertexes; + int tHeight, sWidth; + + tHeight = maxs[1] - mins[1] + 1; + sWidth = maxs[0] - mins[0] + 1; + + for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t <= maxs[1]+HALF_SKY_SUBDIVISIONS; t++ ) + { + for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ ) + { + VectorAdd( s_skyPoints[t][s], backEnd.viewParms.or.origin, tess.xyz[tess.numVertexes] ); + tess.texCoords[tess.numVertexes][0][0] = s_skyTexCoords[t][s][0]; + tess.texCoords[tess.numVertexes][0][1] = s_skyTexCoords[t][s][1]; + + tess.numVertexes++; + + if ( tess.numVertexes >= SHADER_MAX_VERTEXES ) + { + ri.Error( ERR_DROP, "SHADER_MAX_VERTEXES hit in FillCloudySkySide()" ); + } + } + } + + // only add indexes for one pass, otherwise it would draw multiple times for each pass + if ( addIndexes ) { + for ( t = 0; t < tHeight-1; t++ ) + { + for ( s = 0; s < sWidth-1; s++ ) + { + tess.indexes[tess.numIndexes] = vertexStart + s + t * ( sWidth ); + tess.numIndexes++; + tess.indexes[tess.numIndexes] = vertexStart + s + ( t + 1 ) * ( sWidth ); + tess.numIndexes++; + tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * ( sWidth ); + tess.numIndexes++; + + tess.indexes[tess.numIndexes] = vertexStart + s + ( t + 1 ) * ( sWidth ); + tess.numIndexes++; + tess.indexes[tess.numIndexes] = vertexStart + s + 1 + ( t + 1 ) * ( sWidth ); + tess.numIndexes++; + tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * ( sWidth ); + tess.numIndexes++; + } + } + } +} + +static void FillCloudBox( const shader_t *shader, int stage ) +{ + int i; + + for ( i =0; i < 6; i++ ) + { + int sky_mins_subd[2], sky_maxs_subd[2]; + int s, t; + float MIN_T; + + if ( 1 ) // FIXME? shader->sky.fullClouds ) + { + MIN_T = -HALF_SKY_SUBDIVISIONS; + + // still don't want to draw the bottom, even if fullClouds + if ( i == 5 ) + continue; + } + else + { + switch( i ) + { + case 0: + case 1: + case 2: + case 3: + MIN_T = -1; + break; + case 5: + // don't draw clouds beneath you + continue; + case 4: // top + default: + MIN_T = -HALF_SKY_SUBDIVISIONS; + break; + } + } + + sky_mins[0][i] = floor( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; + sky_mins[1][i] = floor( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; + sky_maxs[0][i] = ceil( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; + sky_maxs[1][i] = ceil( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; + + if ( ( sky_mins[0][i] >= sky_maxs[0][i] ) || + ( sky_mins[1][i] >= sky_maxs[1][i] ) ) + { + continue; + } + + sky_mins_subd[0] = ri.ftol(sky_mins[0][i] * HALF_SKY_SUBDIVISIONS); + sky_mins_subd[1] = ri.ftol(sky_mins[1][i] * HALF_SKY_SUBDIVISIONS); + sky_maxs_subd[0] = ri.ftol(sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS); + sky_maxs_subd[1] = ri.ftol(sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS); + + if ( sky_mins_subd[0] < -HALF_SKY_SUBDIVISIONS ) + sky_mins_subd[0] = -HALF_SKY_SUBDIVISIONS; + else if ( sky_mins_subd[0] > HALF_SKY_SUBDIVISIONS ) + sky_mins_subd[0] = HALF_SKY_SUBDIVISIONS; + if ( sky_mins_subd[1] < MIN_T ) + sky_mins_subd[1] = MIN_T; + else if ( sky_mins_subd[1] > HALF_SKY_SUBDIVISIONS ) + sky_mins_subd[1] = HALF_SKY_SUBDIVISIONS; + + if ( sky_maxs_subd[0] < -HALF_SKY_SUBDIVISIONS ) + sky_maxs_subd[0] = -HALF_SKY_SUBDIVISIONS; + else if ( sky_maxs_subd[0] > HALF_SKY_SUBDIVISIONS ) + sky_maxs_subd[0] = HALF_SKY_SUBDIVISIONS; + if ( sky_maxs_subd[1] < MIN_T ) + sky_maxs_subd[1] = MIN_T; + else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS ) + sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS; + + // + // iterate through the subdivisions + // + for ( t = sky_mins_subd[1]+HALF_SKY_SUBDIVISIONS; t <= sky_maxs_subd[1]+HALF_SKY_SUBDIVISIONS; t++ ) + { + for ( s = sky_mins_subd[0]+HALF_SKY_SUBDIVISIONS; s <= sky_maxs_subd[0]+HALF_SKY_SUBDIVISIONS; s++ ) + { + MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, + ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, + i, + NULL, + s_skyPoints[t][s] ); + + s_skyTexCoords[t][s][0] = s_cloudTexCoords[i][t][s][0]; + s_skyTexCoords[t][s][1] = s_cloudTexCoords[i][t][s][1]; + } + } + + // only add indexes for first stage + FillCloudySkySide( sky_mins_subd, sky_maxs_subd, ( stage == 0 ) ); + } +} + +/* +** R_BuildCloudData +*/ +void R_BuildCloudData( shaderCommands_t *input ) +{ + int i; + shader_t *shader; + + shader = input->shader; + + assert( shader->isSky ); + + sky_min = 1.0 / 256.0f; // FIXME: not correct? + sky_max = 255.0 / 256.0f; + + // set up for drawing + tess.numIndexes = 0; + tess.numVertexes = 0; + + if ( shader->sky.cloudHeight ) + { + for ( i = 0; i < MAX_SHADER_STAGES; i++ ) + { + if ( !tess.xstages[i] ) { + break; + } + FillCloudBox( shader, i ); + } + } +} + +/* +** R_InitSkyTexCoords +** Called when a sky shader is parsed +*/ +#define SQR( a ) ((a)*(a)) +void R_InitSkyTexCoords( float heightCloud ) +{ + int i, s, t; + float radiusWorld = 4096; + float p; + float sRad, tRad; + vec3_t skyVec; + vec3_t v; + + // init zfar so MakeSkyVec works even though + // a world hasn't been bounded + backEnd.viewParms.zFar = 1024; + + for ( i = 0; i < 6; i++ ) + { + for ( t = 0; t <= SKY_SUBDIVISIONS; t++ ) + { + for ( s = 0; s <= SKY_SUBDIVISIONS; s++ ) + { + // compute vector from view origin to sky side integral point + MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, + ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, + i, + NULL, + skyVec ); + + // compute parametric value 'p' that intersects with cloud layer + p = ( 1.0f / ( 2 * DotProduct( skyVec, skyVec ) ) ) * + ( -2 * skyVec[2] * radiusWorld + + 2 * sqrt( SQR( skyVec[2] ) * SQR( radiusWorld ) + + 2 * SQR( skyVec[0] ) * radiusWorld * heightCloud + + SQR( skyVec[0] ) * SQR( heightCloud ) + + 2 * SQR( skyVec[1] ) * radiusWorld * heightCloud + + SQR( skyVec[1] ) * SQR( heightCloud ) + + 2 * SQR( skyVec[2] ) * radiusWorld * heightCloud + + SQR( skyVec[2] ) * SQR( heightCloud ) ) ); + + s_cloudTexP[i][t][s] = p; + + // compute intersection point based on p + VectorScale( skyVec, p, v ); + v[2] += radiusWorld; + + // compute vector from world origin to intersection point 'v' + VectorNormalize( v ); + + sRad = Q_acos( v[0] ); + tRad = Q_acos( v[1] ); + + s_cloudTexCoords[i][t][s][0] = sRad; + s_cloudTexCoords[i][t][s][1] = tRad; + } + } + } +} + +//====================================================================================== + +/* +** RB_DrawSun +*/ +void RB_DrawSun( void ) { + float size; + float dist; + vec3_t origin, vec1, vec2; + vec3_t temp; + + if ( !backEnd.skyRenderedThisView ) { + return; + } + if ( !r_drawSun->integer ) { + return; + } + qglLoadMatrixf( backEnd.viewParms.world.modelMatrix ); + qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]); + + dist = backEnd.viewParms.zFar / 1.75; // div sqrt(3) + size = dist * 0.4; + + VectorScale( tr.sunDirection, dist, origin ); + PerpendicularVector( vec1, tr.sunDirection ); + CrossProduct( tr.sunDirection, vec1, vec2 ); + + VectorScale( vec1, size, vec1 ); + VectorScale( vec2, size, vec2 ); + + // farthest depth range + qglDepthRange( 1.0, 1.0 ); + + // FIXME: use quad stamp + RB_BeginSurface( tr.sunShader, tess.fogNum ); + VectorCopy( origin, temp ); + VectorSubtract( temp, vec1, temp ); + VectorSubtract( temp, vec2, temp ); + VectorCopy( temp, tess.xyz[tess.numVertexes] ); + tess.texCoords[tess.numVertexes][0][0] = 0; + tess.texCoords[tess.numVertexes][0][1] = 0; + tess.vertexColors[tess.numVertexes][0] = 255; + tess.vertexColors[tess.numVertexes][1] = 255; + tess.vertexColors[tess.numVertexes][2] = 255; + tess.numVertexes++; + + VectorCopy( origin, temp ); + VectorAdd( temp, vec1, temp ); + VectorSubtract( temp, vec2, temp ); + VectorCopy( temp, tess.xyz[tess.numVertexes] ); + tess.texCoords[tess.numVertexes][0][0] = 0; + tess.texCoords[tess.numVertexes][0][1] = 1; + tess.vertexColors[tess.numVertexes][0] = 255; + tess.vertexColors[tess.numVertexes][1] = 255; + tess.vertexColors[tess.numVertexes][2] = 255; + tess.numVertexes++; + + VectorCopy( origin, temp ); + VectorAdd( temp, vec1, temp ); + VectorAdd( temp, vec2, temp ); + VectorCopy( temp, tess.xyz[tess.numVertexes] ); + tess.texCoords[tess.numVertexes][0][0] = 1; + tess.texCoords[tess.numVertexes][0][1] = 1; + tess.vertexColors[tess.numVertexes][0] = 255; + tess.vertexColors[tess.numVertexes][1] = 255; + tess.vertexColors[tess.numVertexes][2] = 255; + tess.numVertexes++; + + VectorCopy( origin, temp ); + VectorSubtract( temp, vec1, temp ); + VectorAdd( temp, vec2, temp ); + VectorCopy( temp, tess.xyz[tess.numVertexes] ); + tess.texCoords[tess.numVertexes][0][0] = 1; + tess.texCoords[tess.numVertexes][0][1] = 0; + tess.vertexColors[tess.numVertexes][0] = 255; + tess.vertexColors[tess.numVertexes][1] = 255; + tess.vertexColors[tess.numVertexes][2] = 255; + tess.numVertexes++; + + tess.indexes[tess.numIndexes++] = 0; + tess.indexes[tess.numIndexes++] = 1; + tess.indexes[tess.numIndexes++] = 2; + tess.indexes[tess.numIndexes++] = 0; + tess.indexes[tess.numIndexes++] = 2; + tess.indexes[tess.numIndexes++] = 3; + + RB_EndSurface(); + + // back to normal depth range + qglDepthRange( 0.0, 1.0 ); +} + + + + +/* +================ +RB_StageIteratorSky + +All of the visible sky triangles are in tess + +Other things could be stuck in here, like birds in the sky, etc +================ +*/ +void RB_StageIteratorSky( void ) { + if ( r_fastsky->integer ) { + return; + } + + // go through all the polygons and project them onto + // the sky box to see which blocks on each side need + // to be drawn + RB_ClipSkyPolygons( &tess ); + + // r_showsky will let all the sky blocks be drawn in + // front of everything to allow developers to see how + // much sky is getting sucked in + if ( r_showsky->integer ) { + qglDepthRange( 0.0, 0.0 ); + } else { + qglDepthRange( 1.0, 1.0 ); + } + + // draw the outer skybox + if ( tess.shader->sky.outerbox[0] && tess.shader->sky.outerbox[0] != tr.defaultImage ) { + qglColor3f( tr.identityLight, tr.identityLight, tr.identityLight ); + + qglPushMatrix (); + GL_State( 0 ); + qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]); + + DrawSkyBox( tess.shader ); + + qglPopMatrix(); + } + + // generate the vertexes for all the clouds, which will be drawn + // by the generic shader routine + R_BuildCloudData( &tess ); + + RB_StageIteratorGeneric(); + + // draw the inner skybox + + + // back to normal depth range + qglDepthRange( 0.0, 1.0 ); + + // note that sky was drawn so we will draw a sun later + backEnd.skyRenderedThisView = qtrue; +} + diff --git a/src/renderergl1/tr_subs.c b/src/renderergl1/tr_subs.c new file mode 100644 index 00000000..6f490128 --- /dev/null +++ b/src/renderergl1/tr_subs.c @@ -0,0 +1,48 @@ +/* +=========================================================================== +Copyright (C) 2010 James Canete (use.less01@gmail.com) + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_subs.c - common function replacements for modular renderer + +#include "tr_local.h" + +void QDECL Com_Printf( const char *msg, ... ) +{ + va_list argptr; + char text[1024]; + + va_start(argptr, msg); + Q_vsnprintf(text, sizeof(text), msg, argptr); + va_end(argptr); + + ri.Printf(PRINT_ALL, "%s", text); +} + +void QDECL Com_Error( int level, const char *error, ... ) +{ + va_list argptr; + char text[1024]; + + va_start(argptr, error); + Q_vsnprintf(text, sizeof(text), error, argptr); + va_end(argptr); + + ri.Error(level, "%s", text); +} diff --git a/src/renderergl1/tr_surface.c b/src/renderergl1/tr_surface.c new file mode 100644 index 00000000..7a836386 --- /dev/null +++ b/src/renderergl1/tr_surface.c @@ -0,0 +1,1245 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_surf.c +#include "tr_local.h" +#if idppc_altivec && !defined(MACOS_X) +#include +#endif + +/* + + THIS ENTIRE FILE IS BACK END + +backEnd.currentEntity will be valid. + +Tess_Begin has already been called for the surface's shader. + +The modelview matrix will be set. + +It is safe to actually issue drawing commands here if you don't want to +use the shader system. +*/ + + +//============================================================================ + + +/* +============== +RB_CheckOverflow +============== +*/ +void RB_CheckOverflow( int verts, int indexes ) { + if (tess.numVertexes + verts < SHADER_MAX_VERTEXES + && tess.numIndexes + indexes < SHADER_MAX_INDEXES) { + return; + } + + RB_EndSurface(); + + if ( verts >= SHADER_MAX_VERTEXES ) { + ri.Error(ERR_DROP, "RB_CheckOverflow: verts > MAX (%d > %d)", verts, SHADER_MAX_VERTEXES ); + } + if ( indexes >= SHADER_MAX_INDEXES ) { + ri.Error(ERR_DROP, "RB_CheckOverflow: indices > MAX (%d > %d)", indexes, SHADER_MAX_INDEXES ); + } + + RB_BeginSurface(tess.shader, tess.fogNum ); +} + + +/* +============== +RB_AddQuadStampExt +============== +*/ +void RB_AddQuadStampExt( vec3_t origin, vec3_t left, vec3_t up, byte *color, float s1, float t1, float s2, float t2 ) { + vec3_t normal; + int ndx; + + RB_CHECKOVERFLOW( 4, 6 ); + + ndx = tess.numVertexes; + + // triangle indexes for a simple quad + tess.indexes[ tess.numIndexes ] = ndx; + tess.indexes[ tess.numIndexes + 1 ] = ndx + 1; + tess.indexes[ tess.numIndexes + 2 ] = ndx + 3; + + tess.indexes[ tess.numIndexes + 3 ] = ndx + 3; + tess.indexes[ tess.numIndexes + 4 ] = ndx + 1; + tess.indexes[ tess.numIndexes + 5 ] = ndx + 2; + + tess.xyz[ndx][0] = origin[0] + left[0] + up[0]; + tess.xyz[ndx][1] = origin[1] + left[1] + up[1]; + tess.xyz[ndx][2] = origin[2] + left[2] + up[2]; + + tess.xyz[ndx+1][0] = origin[0] - left[0] + up[0]; + tess.xyz[ndx+1][1] = origin[1] - left[1] + up[1]; + tess.xyz[ndx+1][2] = origin[2] - left[2] + up[2]; + + tess.xyz[ndx+2][0] = origin[0] - left[0] - up[0]; + tess.xyz[ndx+2][1] = origin[1] - left[1] - up[1]; + tess.xyz[ndx+2][2] = origin[2] - left[2] - up[2]; + + tess.xyz[ndx+3][0] = origin[0] + left[0] - up[0]; + tess.xyz[ndx+3][1] = origin[1] + left[1] - up[1]; + tess.xyz[ndx+3][2] = origin[2] + left[2] - up[2]; + + + // constant normal all the way around + VectorSubtract( vec3_origin, backEnd.viewParms.or.axis[0], normal ); + + tess.normal[ndx][0] = tess.normal[ndx+1][0] = tess.normal[ndx+2][0] = tess.normal[ndx+3][0] = normal[0]; + tess.normal[ndx][1] = tess.normal[ndx+1][1] = tess.normal[ndx+2][1] = tess.normal[ndx+3][1] = normal[1]; + tess.normal[ndx][2] = tess.normal[ndx+1][2] = tess.normal[ndx+2][2] = tess.normal[ndx+3][2] = normal[2]; + + // standard square texture coordinates + tess.texCoords[ndx][0][0] = tess.texCoords[ndx][1][0] = s1; + tess.texCoords[ndx][0][1] = tess.texCoords[ndx][1][1] = t1; + + tess.texCoords[ndx+1][0][0] = tess.texCoords[ndx+1][1][0] = s2; + tess.texCoords[ndx+1][0][1] = tess.texCoords[ndx+1][1][1] = t1; + + tess.texCoords[ndx+2][0][0] = tess.texCoords[ndx+2][1][0] = s2; + tess.texCoords[ndx+2][0][1] = tess.texCoords[ndx+2][1][1] = t2; + + tess.texCoords[ndx+3][0][0] = tess.texCoords[ndx+3][1][0] = s1; + tess.texCoords[ndx+3][0][1] = tess.texCoords[ndx+3][1][1] = t2; + + // constant color all the way around + // should this be identity and let the shader specify from entity? + * ( unsigned int * ) &tess.vertexColors[ndx] = + * ( unsigned int * ) &tess.vertexColors[ndx+1] = + * ( unsigned int * ) &tess.vertexColors[ndx+2] = + * ( unsigned int * ) &tess.vertexColors[ndx+3] = + * ( unsigned int * )color; + + + tess.numVertexes += 4; + tess.numIndexes += 6; +} + +/* +============== +RB_AddQuadStamp +============== +*/ +void RB_AddQuadStamp( vec3_t origin, vec3_t left, vec3_t up, byte *color ) { + RB_AddQuadStampExt( origin, left, up, color, 0, 0, 1, 1 ); +} + +/* +============== +RB_SurfaceSprite +============== +*/ +static void RB_SurfaceSprite( void ) { + vec3_t left, up; + float radius; + + // calculate the xyz locations for the four corners + radius = backEnd.currentEntity->e.radius; + if ( backEnd.currentEntity->e.rotation == 0 ) { + VectorScale( backEnd.viewParms.or.axis[1], radius, left ); + VectorScale( backEnd.viewParms.or.axis[2], radius, up ); + } else { + float s, c; + float ang; + + ang = M_PI * backEnd.currentEntity->e.rotation / 180; + s = sin( ang ); + c = cos( ang ); + + VectorScale( backEnd.viewParms.or.axis[1], c * radius, left ); + VectorMA( left, -s * radius, backEnd.viewParms.or.axis[2], left ); + + VectorScale( backEnd.viewParms.or.axis[2], c * radius, up ); + VectorMA( up, s * radius, backEnd.viewParms.or.axis[1], up ); + } + if ( backEnd.viewParms.isMirror ) { + VectorSubtract( vec3_origin, left, left ); + } + + RB_AddQuadStamp( backEnd.currentEntity->e.origin, left, up, backEnd.currentEntity->e.shaderRGBA ); +} + + +/* +============= +RB_SurfacePolychain +============= +*/ +static void RB_SurfacePolychain( srfPoly_t *p ) { + int i; + int numv; + + RB_CHECKOVERFLOW( p->numVerts, 3*(p->numVerts - 2) ); + + // fan triangles into the tess array + numv = tess.numVertexes; + for ( i = 0; i < p->numVerts; i++ ) { + VectorCopy( p->verts[i].xyz, tess.xyz[numv] ); + tess.texCoords[numv][0][0] = p->verts[i].st[0]; + tess.texCoords[numv][0][1] = p->verts[i].st[1]; + *(int *)&tess.vertexColors[numv] = *(int *)p->verts[ i ].modulate; + + numv++; + } + + // generate fan indexes into the tess array + for ( i = 0; i < p->numVerts-2; i++ ) { + tess.indexes[tess.numIndexes + 0] = tess.numVertexes; + tess.indexes[tess.numIndexes + 1] = tess.numVertexes + i + 1; + tess.indexes[tess.numIndexes + 2] = tess.numVertexes + i + 2; + tess.numIndexes += 3; + } + + tess.numVertexes = numv; +} + + +/* +============= +RB_SurfaceTriangles +============= +*/ +static void RB_SurfaceTriangles( srfTriangles_t *srf ) { + int i; + drawVert_t *dv; + float *xyz, *normal, *texCoords; + byte *color; + int dlightBits; + qboolean needsNormal; + + dlightBits = srf->dlightBits; + tess.dlightBits |= dlightBits; + + RB_CHECKOVERFLOW( srf->numVerts, srf->numIndexes ); + + for ( i = 0 ; i < srf->numIndexes ; i += 3 ) { + tess.indexes[ tess.numIndexes + i + 0 ] = tess.numVertexes + srf->indexes[ i + 0 ]; + tess.indexes[ tess.numIndexes + i + 1 ] = tess.numVertexes + srf->indexes[ i + 1 ]; + tess.indexes[ tess.numIndexes + i + 2 ] = tess.numVertexes + srf->indexes[ i + 2 ]; + } + tess.numIndexes += srf->numIndexes; + + dv = srf->verts; + xyz = tess.xyz[ tess.numVertexes ]; + normal = tess.normal[ tess.numVertexes ]; + texCoords = tess.texCoords[ tess.numVertexes ][0]; + color = tess.vertexColors[ tess.numVertexes ]; + needsNormal = tess.shader->needsNormal; + + for ( i = 0 ; i < srf->numVerts ; i++, dv++, xyz += 4, normal += 4, texCoords += 4, color += 4 ) { + xyz[0] = dv->xyz[0]; + xyz[1] = dv->xyz[1]; + xyz[2] = dv->xyz[2]; + + if ( needsNormal ) { + normal[0] = dv->normal[0]; + normal[1] = dv->normal[1]; + normal[2] = dv->normal[2]; + } + + texCoords[0] = dv->st[0]; + texCoords[1] = dv->st[1]; + + texCoords[2] = dv->lightmap[0]; + texCoords[3] = dv->lightmap[1]; + + *(int *)color = *(int *)dv->color; + } + + for ( i = 0 ; i < srf->numVerts ; i++ ) { + tess.vertexDlightBits[ tess.numVertexes + i] = dlightBits; + } + + tess.numVertexes += srf->numVerts; +} + + + +/* +============== +RB_SurfaceBeam +============== +*/ +static void RB_SurfaceBeam( void ) +{ +#define NUM_BEAM_SEGS 6 + refEntity_t *e; + int i; + vec3_t perpvec; + vec3_t direction, normalized_direction; + vec3_t start_points[NUM_BEAM_SEGS], end_points[NUM_BEAM_SEGS]; + vec3_t oldorigin, origin; + + e = &backEnd.currentEntity->e; + + oldorigin[0] = e->oldorigin[0]; + oldorigin[1] = e->oldorigin[1]; + oldorigin[2] = e->oldorigin[2]; + + origin[0] = e->origin[0]; + origin[1] = e->origin[1]; + origin[2] = e->origin[2]; + + normalized_direction[0] = direction[0] = oldorigin[0] - origin[0]; + normalized_direction[1] = direction[1] = oldorigin[1] - origin[1]; + normalized_direction[2] = direction[2] = oldorigin[2] - origin[2]; + + if ( VectorNormalize( normalized_direction ) == 0 ) + return; + + PerpendicularVector( perpvec, normalized_direction ); + + VectorScale( perpvec, 4, perpvec ); + + for ( i = 0; i < NUM_BEAM_SEGS ; i++ ) + { + RotatePointAroundVector( start_points[i], normalized_direction, perpvec, (360.0/NUM_BEAM_SEGS)*i ); +// VectorAdd( start_points[i], origin, start_points[i] ); + VectorAdd( start_points[i], direction, end_points[i] ); + } + + GL_Bind( tr.whiteImage ); + + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + + qglColor3f( 1, 0, 0 ); + + qglBegin( GL_TRIANGLE_STRIP ); + for ( i = 0; i <= NUM_BEAM_SEGS; i++ ) { + qglVertex3fv( start_points[ i % NUM_BEAM_SEGS] ); + qglVertex3fv( end_points[ i % NUM_BEAM_SEGS] ); + } + qglEnd(); +} + +//================================================================================ + +static void DoRailCore( const vec3_t start, const vec3_t end, const vec3_t up, float len, float spanWidth ) +{ + float spanWidth2; + int vbase; + float t = len / 256.0f; + + vbase = tess.numVertexes; + + spanWidth2 = -spanWidth; + + // FIXME: use quad stamp? + VectorMA( start, spanWidth, up, tess.xyz[tess.numVertexes] ); + tess.texCoords[tess.numVertexes][0][0] = 0; + tess.texCoords[tess.numVertexes][0][1] = 0; + tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0] * 0.25; + tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1] * 0.25; + tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2] * 0.25; + tess.numVertexes++; + + VectorMA( start, spanWidth2, up, tess.xyz[tess.numVertexes] ); + tess.texCoords[tess.numVertexes][0][0] = 0; + tess.texCoords[tess.numVertexes][0][1] = 1; + tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0]; + tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1]; + tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2]; + tess.numVertexes++; + + VectorMA( end, spanWidth, up, tess.xyz[tess.numVertexes] ); + + tess.texCoords[tess.numVertexes][0][0] = t; + tess.texCoords[tess.numVertexes][0][1] = 0; + tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0]; + tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1]; + tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2]; + tess.numVertexes++; + + VectorMA( end, spanWidth2, up, tess.xyz[tess.numVertexes] ); + tess.texCoords[tess.numVertexes][0][0] = t; + tess.texCoords[tess.numVertexes][0][1] = 1; + tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0]; + tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1]; + tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2]; + tess.numVertexes++; + + tess.indexes[tess.numIndexes++] = vbase; + tess.indexes[tess.numIndexes++] = vbase + 1; + tess.indexes[tess.numIndexes++] = vbase + 2; + + tess.indexes[tess.numIndexes++] = vbase + 2; + tess.indexes[tess.numIndexes++] = vbase + 1; + tess.indexes[tess.numIndexes++] = vbase + 3; +} + +static void DoRailDiscs( int numSegs, const vec3_t start, const vec3_t dir, const vec3_t right, const vec3_t up ) +{ + int i; + vec3_t pos[4]; + vec3_t v; + int spanWidth = r_railWidth->integer; + float c, s; + float scale; + + if ( numSegs > 1 ) + numSegs--; + if ( !numSegs ) + return; + + scale = 0.25; + + for ( i = 0; i < 4; i++ ) + { + c = cos( DEG2RAD( 45 + i * 90 ) ); + s = sin( DEG2RAD( 45 + i * 90 ) ); + v[0] = ( right[0] * c + up[0] * s ) * scale * spanWidth; + v[1] = ( right[1] * c + up[1] * s ) * scale * spanWidth; + v[2] = ( right[2] * c + up[2] * s ) * scale * spanWidth; + VectorAdd( start, v, pos[i] ); + + if ( numSegs > 1 ) + { + // offset by 1 segment if we're doing a long distance shot + VectorAdd( pos[i], dir, pos[i] ); + } + } + + for ( i = 0; i < numSegs; i++ ) + { + int j; + + RB_CHECKOVERFLOW( 4, 6 ); + + for ( j = 0; j < 4; j++ ) + { + VectorCopy( pos[j], tess.xyz[tess.numVertexes] ); + tess.texCoords[tess.numVertexes][0][0] = ( j < 2 ); + tess.texCoords[tess.numVertexes][0][1] = ( j && j != 3 ); + tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0]; + tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1]; + tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2]; + tess.numVertexes++; + + VectorAdd( pos[j], dir, pos[j] ); + } + + tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 0; + tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 1; + tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 3; + tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 3; + tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 1; + tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 2; + } +} + +/* +** RB_SurfaceRailRinges +*/ +static void RB_SurfaceRailRings( void ) { + refEntity_t *e; + int numSegs; + int len; + vec3_t vec; + vec3_t right, up; + vec3_t start, end; + + e = &backEnd.currentEntity->e; + + VectorCopy( e->oldorigin, start ); + VectorCopy( e->origin, end ); + + // compute variables + VectorSubtract( end, start, vec ); + len = VectorNormalize( vec ); + MakeNormalVectors( vec, right, up ); + numSegs = ( len ) / r_railSegmentLength->value; + if ( numSegs <= 0 ) { + numSegs = 1; + } + + VectorScale( vec, r_railSegmentLength->value, vec ); + + DoRailDiscs( numSegs, start, vec, right, up ); +} + +/* +** RB_SurfaceRailCore +*/ +static void RB_SurfaceRailCore( void ) { + refEntity_t *e; + int len; + vec3_t right; + vec3_t vec; + vec3_t start, end; + vec3_t v1, v2; + + e = &backEnd.currentEntity->e; + + VectorCopy( e->oldorigin, start ); + VectorCopy( e->origin, end ); + + VectorSubtract( end, start, vec ); + len = VectorNormalize( vec ); + + // compute side vector + VectorSubtract( start, backEnd.viewParms.or.origin, v1 ); + VectorNormalize( v1 ); + VectorSubtract( end, backEnd.viewParms.or.origin, v2 ); + VectorNormalize( v2 ); + CrossProduct( v1, v2, right ); + VectorNormalize( right ); + + DoRailCore( start, end, right, len, r_railCoreWidth->integer ); +} + +/* +** RB_SurfaceLightningBolt +*/ +static void RB_SurfaceLightningBolt( void ) { + refEntity_t *e; + int len; + vec3_t right; + vec3_t vec; + vec3_t start, end; + vec3_t v1, v2; + int i; + + e = &backEnd.currentEntity->e; + + VectorCopy( e->oldorigin, end ); + VectorCopy( e->origin, start ); + + // compute variables + VectorSubtract( end, start, vec ); + len = VectorNormalize( vec ); + + // compute side vector + VectorSubtract( start, backEnd.viewParms.or.origin, v1 ); + VectorNormalize( v1 ); + VectorSubtract( end, backEnd.viewParms.or.origin, v2 ); + VectorNormalize( v2 ); + CrossProduct( v1, v2, right ); + VectorNormalize( right ); + + for ( i = 0 ; i < 4 ; i++ ) { + vec3_t temp; + + DoRailCore( start, end, right, len, 8 ); + RotatePointAroundVector( temp, vec, right, 45 ); + VectorCopy( temp, right ); + } +} + +/* +** VectorArrayNormalize +* +* The inputs to this routing seem to always be close to length = 1.0 (about 0.6 to 2.0) +* This means that we don't have to worry about zero length or enormously long vectors. +*/ +static void VectorArrayNormalize(vec4_t *normals, unsigned int count) +{ +// assert(count); + +#if idppc + { + register float half = 0.5; + register float one = 1.0; + float *components = (float *)normals; + + // Vanilla PPC code, but since PPC has a reciprocal square root estimate instruction, + // runs *much* faster than calling sqrt(). We'll use a single Newton-Raphson + // refinement step to get a little more precision. This seems to yeild results + // that are correct to 3 decimal places and usually correct to at least 4 (sometimes 5). + // (That is, for the given input range of about 0.6 to 2.0). + do { + float x, y, z; + float B, y0, y1; + + x = components[0]; + y = components[1]; + z = components[2]; + components += 4; + B = x*x + y*y + z*z; + +#ifdef __GNUC__ + asm("frsqrte %0,%1" : "=f" (y0) : "f" (B)); +#else + y0 = __frsqrte(B); +#endif + y1 = y0 + half*y0*(one - B*y0*y0); + + x = x * y1; + y = y * y1; + components[-4] = x; + z = z * y1; + components[-3] = y; + components[-2] = z; + } while(count--); + } +#else // No assembly version for this architecture, or C_ONLY defined + // given the input, it's safe to call VectorNormalizeFast + while (count--) { + VectorNormalizeFast(normals[0]); + normals++; + } +#endif + +} + + + +/* +** LerpMeshVertexes +*/ +#if idppc_altivec +static void LerpMeshVertexes_altivec(md3Surface_t *surf, float backlerp) +{ + short *oldXyz, *newXyz, *oldNormals, *newNormals; + float *outXyz, *outNormal; + float oldXyzScale QALIGN(16); + float newXyzScale QALIGN(16); + float oldNormalScale QALIGN(16); + float newNormalScale QALIGN(16); + int vertNum; + unsigned lat, lng; + int numVerts; + + outXyz = tess.xyz[tess.numVertexes]; + outNormal = tess.normal[tess.numVertexes]; + + newXyz = (short *)((byte *)surf + surf->ofsXyzNormals) + + (backEnd.currentEntity->e.frame * surf->numVerts * 4); + newNormals = newXyz + 3; + + newXyzScale = MD3_XYZ_SCALE * (1.0 - backlerp); + newNormalScale = 1.0 - backlerp; + + numVerts = surf->numVerts; + + if ( backlerp == 0 ) { + vector signed short newNormalsVec0; + vector signed short newNormalsVec1; + vector signed int newNormalsIntVec; + vector float newNormalsFloatVec; + vector float newXyzScaleVec; + vector unsigned char newNormalsLoadPermute; + vector unsigned char newNormalsStorePermute; + vector float zero; + + newNormalsStorePermute = vec_lvsl(0,(float *)&newXyzScaleVec); + newXyzScaleVec = *(vector float *)&newXyzScale; + newXyzScaleVec = vec_perm(newXyzScaleVec,newXyzScaleVec,newNormalsStorePermute); + newXyzScaleVec = vec_splat(newXyzScaleVec,0); + newNormalsLoadPermute = vec_lvsl(0,newXyz); + newNormalsStorePermute = vec_lvsr(0,outXyz); + zero = (vector float)vec_splat_s8(0); + // + // just copy the vertexes + // + for (vertNum=0 ; vertNum < numVerts ; vertNum++, + newXyz += 4, newNormals += 4, + outXyz += 4, outNormal += 4) + { + newNormalsLoadPermute = vec_lvsl(0,newXyz); + newNormalsStorePermute = vec_lvsr(0,outXyz); + newNormalsVec0 = vec_ld(0,newXyz); + newNormalsVec1 = vec_ld(16,newXyz); + newNormalsVec0 = vec_perm(newNormalsVec0,newNormalsVec1,newNormalsLoadPermute); + newNormalsIntVec = vec_unpackh(newNormalsVec0); + newNormalsFloatVec = vec_ctf(newNormalsIntVec,0); + newNormalsFloatVec = vec_madd(newNormalsFloatVec,newXyzScaleVec,zero); + newNormalsFloatVec = vec_perm(newNormalsFloatVec,newNormalsFloatVec,newNormalsStorePermute); + //outXyz[0] = newXyz[0] * newXyzScale; + //outXyz[1] = newXyz[1] * newXyzScale; + //outXyz[2] = newXyz[2] * newXyzScale; + + lat = ( newNormals[0] >> 8 ) & 0xff; + lng = ( newNormals[0] & 0xff ); + lat *= (FUNCTABLE_SIZE/256); + lng *= (FUNCTABLE_SIZE/256); + + // decode X as cos( lat ) * sin( long ) + // decode Y as sin( lat ) * sin( long ) + // decode Z as cos( long ) + + outNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; + outNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; + outNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; + + vec_ste(newNormalsFloatVec,0,outXyz); + vec_ste(newNormalsFloatVec,4,outXyz); + vec_ste(newNormalsFloatVec,8,outXyz); + } + } else { + // + // interpolate and copy the vertex and normal + // + oldXyz = (short *)((byte *)surf + surf->ofsXyzNormals) + + (backEnd.currentEntity->e.oldframe * surf->numVerts * 4); + oldNormals = oldXyz + 3; + + oldXyzScale = MD3_XYZ_SCALE * backlerp; + oldNormalScale = backlerp; + + for (vertNum=0 ; vertNum < numVerts ; vertNum++, + oldXyz += 4, newXyz += 4, oldNormals += 4, newNormals += 4, + outXyz += 4, outNormal += 4) + { + vec3_t uncompressedOldNormal, uncompressedNewNormal; + + // interpolate the xyz + outXyz[0] = oldXyz[0] * oldXyzScale + newXyz[0] * newXyzScale; + outXyz[1] = oldXyz[1] * oldXyzScale + newXyz[1] * newXyzScale; + outXyz[2] = oldXyz[2] * oldXyzScale + newXyz[2] * newXyzScale; + + // FIXME: interpolate lat/long instead? + lat = ( newNormals[0] >> 8 ) & 0xff; + lng = ( newNormals[0] & 0xff ); + lat *= 4; + lng *= 4; + uncompressedNewNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; + uncompressedNewNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; + uncompressedNewNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; + + lat = ( oldNormals[0] >> 8 ) & 0xff; + lng = ( oldNormals[0] & 0xff ); + lat *= 4; + lng *= 4; + + uncompressedOldNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; + uncompressedOldNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; + uncompressedOldNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; + + outNormal[0] = uncompressedOldNormal[0] * oldNormalScale + uncompressedNewNormal[0] * newNormalScale; + outNormal[1] = uncompressedOldNormal[1] * oldNormalScale + uncompressedNewNormal[1] * newNormalScale; + outNormal[2] = uncompressedOldNormal[2] * oldNormalScale + uncompressedNewNormal[2] * newNormalScale; + +// VectorNormalize (outNormal); + } + VectorArrayNormalize((vec4_t *)tess.normal[tess.numVertexes], numVerts); + } +} +#endif + +static void LerpMeshVertexes_scalar(md3Surface_t *surf, float backlerp) +{ + short *oldXyz, *newXyz, *oldNormals, *newNormals; + float *outXyz, *outNormal; + float oldXyzScale, newXyzScale; + float oldNormalScale, newNormalScale; + int vertNum; + unsigned lat, lng; + int numVerts; + + outXyz = tess.xyz[tess.numVertexes]; + outNormal = tess.normal[tess.numVertexes]; + + newXyz = (short *)((byte *)surf + surf->ofsXyzNormals) + + (backEnd.currentEntity->e.frame * surf->numVerts * 4); + newNormals = newXyz + 3; + + newXyzScale = MD3_XYZ_SCALE * (1.0 - backlerp); + newNormalScale = 1.0 - backlerp; + + numVerts = surf->numVerts; + + if ( backlerp == 0 ) { + // + // just copy the vertexes + // + for (vertNum=0 ; vertNum < numVerts ; vertNum++, + newXyz += 4, newNormals += 4, + outXyz += 4, outNormal += 4) + { + + outXyz[0] = newXyz[0] * newXyzScale; + outXyz[1] = newXyz[1] * newXyzScale; + outXyz[2] = newXyz[2] * newXyzScale; + + lat = ( newNormals[0] >> 8 ) & 0xff; + lng = ( newNormals[0] & 0xff ); + lat *= (FUNCTABLE_SIZE/256); + lng *= (FUNCTABLE_SIZE/256); + + // decode X as cos( lat ) * sin( long ) + // decode Y as sin( lat ) * sin( long ) + // decode Z as cos( long ) + + outNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; + outNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; + outNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; + } + } else { + // + // interpolate and copy the vertex and normal + // + oldXyz = (short *)((byte *)surf + surf->ofsXyzNormals) + + (backEnd.currentEntity->e.oldframe * surf->numVerts * 4); + oldNormals = oldXyz + 3; + + oldXyzScale = MD3_XYZ_SCALE * backlerp; + oldNormalScale = backlerp; + + for (vertNum=0 ; vertNum < numVerts ; vertNum++, + oldXyz += 4, newXyz += 4, oldNormals += 4, newNormals += 4, + outXyz += 4, outNormal += 4) + { + vec3_t uncompressedOldNormal, uncompressedNewNormal; + + // interpolate the xyz + outXyz[0] = oldXyz[0] * oldXyzScale + newXyz[0] * newXyzScale; + outXyz[1] = oldXyz[1] * oldXyzScale + newXyz[1] * newXyzScale; + outXyz[2] = oldXyz[2] * oldXyzScale + newXyz[2] * newXyzScale; + + // FIXME: interpolate lat/long instead? + lat = ( newNormals[0] >> 8 ) & 0xff; + lng = ( newNormals[0] & 0xff ); + lat *= 4; + lng *= 4; + uncompressedNewNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; + uncompressedNewNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; + uncompressedNewNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; + + lat = ( oldNormals[0] >> 8 ) & 0xff; + lng = ( oldNormals[0] & 0xff ); + lat *= 4; + lng *= 4; + + uncompressedOldNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; + uncompressedOldNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; + uncompressedOldNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; + + outNormal[0] = uncompressedOldNormal[0] * oldNormalScale + uncompressedNewNormal[0] * newNormalScale; + outNormal[1] = uncompressedOldNormal[1] * oldNormalScale + uncompressedNewNormal[1] * newNormalScale; + outNormal[2] = uncompressedOldNormal[2] * oldNormalScale + uncompressedNewNormal[2] * newNormalScale; + +// VectorNormalize (outNormal); + } + VectorArrayNormalize((vec4_t *)tess.normal[tess.numVertexes], numVerts); + } +} + +static void LerpMeshVertexes(md3Surface_t *surf, float backlerp) +{ +#if idppc_altivec + if (com_altivec->integer) { + // must be in a seperate function or G3 systems will crash. + LerpMeshVertexes_altivec( surf, backlerp ); + return; + } +#endif // idppc_altivec + LerpMeshVertexes_scalar( surf, backlerp ); +} + + +/* +============= +RB_SurfaceMesh +============= +*/ +static void RB_SurfaceMesh(md3Surface_t *surface) { + int j; + float backlerp; + int *triangles; + float *texCoords; + int indexes; + int Bob, Doug; + int numVerts; + + if ( backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame ) { + backlerp = 0; + } else { + backlerp = backEnd.currentEntity->e.backlerp; + } + + RB_CHECKOVERFLOW( surface->numVerts, surface->numTriangles*3 ); + + LerpMeshVertexes (surface, backlerp); + + triangles = (int *) ((byte *)surface + surface->ofsTriangles); + indexes = surface->numTriangles * 3; + Bob = tess.numIndexes; + Doug = tess.numVertexes; + for (j = 0 ; j < indexes ; j++) { + tess.indexes[Bob + j] = Doug + triangles[j]; + } + tess.numIndexes += indexes; + + texCoords = (float *) ((byte *)surface + surface->ofsSt); + + numVerts = surface->numVerts; + for ( j = 0; j < numVerts; j++ ) { + tess.texCoords[Doug + j][0][0] = texCoords[j*2+0]; + tess.texCoords[Doug + j][0][1] = texCoords[j*2+1]; + // FIXME: fill in lightmapST for completeness? + } + + tess.numVertexes += surface->numVerts; + +} + + +/* +============== +RB_SurfaceFace +============== +*/ +static void RB_SurfaceFace( srfSurfaceFace_t *surf ) { + int i; + unsigned *indices, *tessIndexes; + float *v; + float *normal; + int ndx; + int Bob; + int numPoints; + int dlightBits; + + RB_CHECKOVERFLOW( surf->numPoints, surf->numIndices ); + + dlightBits = surf->dlightBits; + tess.dlightBits |= dlightBits; + + indices = ( unsigned * ) ( ( ( char * ) surf ) + surf->ofsIndices ); + + Bob = tess.numVertexes; + tessIndexes = tess.indexes + tess.numIndexes; + for ( i = surf->numIndices-1 ; i >= 0 ; i-- ) { + tessIndexes[i] = indices[i] + Bob; + } + + tess.numIndexes += surf->numIndices; + + numPoints = surf->numPoints; + + if ( tess.shader->needsNormal ) { + normal = surf->plane.normal; + for ( i = 0, ndx = tess.numVertexes; i < numPoints; i++, ndx++ ) { + VectorCopy( normal, tess.normal[ndx] ); + } + } + + for ( i = 0, v = surf->points[0], ndx = tess.numVertexes; i < numPoints; i++, v += VERTEXSIZE, ndx++ ) { + VectorCopy( v, tess.xyz[ndx]); + tess.texCoords[ndx][0][0] = v[3]; + tess.texCoords[ndx][0][1] = v[4]; + tess.texCoords[ndx][1][0] = v[5]; + tess.texCoords[ndx][1][1] = v[6]; + * ( unsigned int * ) &tess.vertexColors[ndx] = * ( unsigned int * ) &v[7]; + tess.vertexDlightBits[ndx] = dlightBits; + } + + + tess.numVertexes += surf->numPoints; +} + + +static float LodErrorForVolume( vec3_t local, float radius ) { + vec3_t world; + float d; + + // never let it go negative + if ( r_lodCurveError->value < 0 ) { + return 0; + } + + world[0] = local[0] * backEnd.or.axis[0][0] + local[1] * backEnd.or.axis[1][0] + + local[2] * backEnd.or.axis[2][0] + backEnd.or.origin[0]; + world[1] = local[0] * backEnd.or.axis[0][1] + local[1] * backEnd.or.axis[1][1] + + local[2] * backEnd.or.axis[2][1] + backEnd.or.origin[1]; + world[2] = local[0] * backEnd.or.axis[0][2] + local[1] * backEnd.or.axis[1][2] + + local[2] * backEnd.or.axis[2][2] + backEnd.or.origin[2]; + + VectorSubtract( world, backEnd.viewParms.or.origin, world ); + d = DotProduct( world, backEnd.viewParms.or.axis[0] ); + + if ( d < 0 ) { + d = -d; + } + d -= radius; + if ( d < 1 ) { + d = 1; + } + + return r_lodCurveError->value / d; +} + +/* +============= +RB_SurfaceGrid + +Just copy the grid of points and triangulate +============= +*/ +static void RB_SurfaceGrid( srfGridMesh_t *cv ) { + int i, j; + float *xyz; + float *texCoords; + float *normal; + unsigned char *color; + drawVert_t *dv; + int rows, irows, vrows; + int used; + int widthTable[MAX_GRID_SIZE]; + int heightTable[MAX_GRID_SIZE]; + float lodError; + int lodWidth, lodHeight; + int numVertexes; + int dlightBits; + int *vDlightBits; + qboolean needsNormal; + + dlightBits = cv->dlightBits; + tess.dlightBits |= dlightBits; + + // determine the allowable discrepance + lodError = LodErrorForVolume( cv->lodOrigin, cv->lodRadius ); + + // determine which rows and columns of the subdivision + // we are actually going to use + widthTable[0] = 0; + lodWidth = 1; + for ( i = 1 ; i < cv->width-1 ; i++ ) { + if ( cv->widthLodError[i] <= lodError ) { + widthTable[lodWidth] = i; + lodWidth++; + } + } + widthTable[lodWidth] = cv->width-1; + lodWidth++; + + heightTable[0] = 0; + lodHeight = 1; + for ( i = 1 ; i < cv->height-1 ; i++ ) { + if ( cv->heightLodError[i] <= lodError ) { + heightTable[lodHeight] = i; + lodHeight++; + } + } + heightTable[lodHeight] = cv->height-1; + lodHeight++; + + + // very large grids may have more points or indexes than can be fit + // in the tess structure, so we may have to issue it in multiple passes + + used = 0; + while ( used < lodHeight - 1 ) { + // see how many rows of both verts and indexes we can add without overflowing + do { + vrows = ( SHADER_MAX_VERTEXES - tess.numVertexes ) / lodWidth; + irows = ( SHADER_MAX_INDEXES - tess.numIndexes ) / ( lodWidth * 6 ); + + // if we don't have enough space for at least one strip, flush the buffer + if ( vrows < 2 || irows < 1 ) { + RB_EndSurface(); + RB_BeginSurface(tess.shader, tess.fogNum ); + } else { + break; + } + } while ( 1 ); + + rows = irows; + if ( vrows < irows + 1 ) { + rows = vrows - 1; + } + if ( used + rows > lodHeight ) { + rows = lodHeight - used; + } + + numVertexes = tess.numVertexes; + + xyz = tess.xyz[numVertexes]; + normal = tess.normal[numVertexes]; + texCoords = tess.texCoords[numVertexes][0]; + color = ( unsigned char * ) &tess.vertexColors[numVertexes]; + vDlightBits = &tess.vertexDlightBits[numVertexes]; + needsNormal = tess.shader->needsNormal; + + for ( i = 0 ; i < rows ; i++ ) { + for ( j = 0 ; j < lodWidth ; j++ ) { + dv = cv->verts + heightTable[ used + i ] * cv->width + + widthTable[ j ]; + + xyz[0] = dv->xyz[0]; + xyz[1] = dv->xyz[1]; + xyz[2] = dv->xyz[2]; + texCoords[0] = dv->st[0]; + texCoords[1] = dv->st[1]; + texCoords[2] = dv->lightmap[0]; + texCoords[3] = dv->lightmap[1]; + if ( needsNormal ) { + normal[0] = dv->normal[0]; + normal[1] = dv->normal[1]; + normal[2] = dv->normal[2]; + } + * ( unsigned int * ) color = * ( unsigned int * ) dv->color; + *vDlightBits++ = dlightBits; + xyz += 4; + normal += 4; + texCoords += 4; + color += 4; + } + } + + + // add the indexes + { + int numIndexes; + int w, h; + + h = rows - 1; + w = lodWidth - 1; + numIndexes = tess.numIndexes; + for (i = 0 ; i < h ; i++) { + for (j = 0 ; j < w ; j++) { + int v1, v2, v3, v4; + + // vertex order to be reckognized as tristrips + v1 = numVertexes + i*lodWidth + j + 1; + v2 = v1 - 1; + v3 = v2 + lodWidth; + v4 = v3 + 1; + + tess.indexes[numIndexes] = v2; + tess.indexes[numIndexes+1] = v3; + tess.indexes[numIndexes+2] = v1; + + tess.indexes[numIndexes+3] = v1; + tess.indexes[numIndexes+4] = v3; + tess.indexes[numIndexes+5] = v4; + numIndexes += 6; + } + } + + tess.numIndexes = numIndexes; + } + + tess.numVertexes += rows * lodWidth; + + used += rows - 1; + } +} + + +/* +=========================================================================== + +NULL MODEL + +=========================================================================== +*/ + +/* +=================== +RB_SurfaceAxis + +Draws x/y/z lines from the origin for orientation debugging +=================== +*/ +static void RB_SurfaceAxis( void ) { + GL_Bind( tr.whiteImage ); + qglLineWidth( 3 ); + qglBegin( GL_LINES ); + qglColor3f( 1,0,0 ); + qglVertex3f( 0,0,0 ); + qglVertex3f( 16,0,0 ); + qglColor3f( 0,1,0 ); + qglVertex3f( 0,0,0 ); + qglVertex3f( 0,16,0 ); + qglColor3f( 0,0,1 ); + qglVertex3f( 0,0,0 ); + qglVertex3f( 0,0,16 ); + qglEnd(); + qglLineWidth( 1 ); +} + +//=========================================================================== + +/* +==================== +RB_SurfaceEntity + +Entities that have a single procedurally generated surface +==================== +*/ +static void RB_SurfaceEntity( surfaceType_t *surfType ) { + switch( backEnd.currentEntity->e.reType ) { + case RT_SPRITE: + RB_SurfaceSprite(); + break; + case RT_BEAM: + RB_SurfaceBeam(); + break; + case RT_RAIL_CORE: + RB_SurfaceRailCore(); + break; + case RT_RAIL_RINGS: + RB_SurfaceRailRings(); + break; + case RT_LIGHTNING: + RB_SurfaceLightningBolt(); + break; + default: + RB_SurfaceAxis(); + break; + } + return; +} + +static void RB_SurfaceBad( surfaceType_t *surfType ) { + ri.Printf( PRINT_ALL, "Bad surface tesselated.\n" ); +} + +static void RB_SurfaceFlare(srfFlare_t *surf) +{ + if (r_flares->integer) + RB_AddFlare(surf, tess.fogNum, surf->origin, surf->color, surf->normal); +} + +static void RB_SurfaceDisplayList( srfDisplayList_t *surf ) { + // all apropriate state must be set in RB_BeginSurface + // this isn't implemented yet... + qglCallList( surf->listNum ); +} + +static void RB_SurfaceSkip( void *surf ) { +} + + +void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])( void *) = { + (void(*)(void*))RB_SurfaceBad, // SF_BAD, + (void(*)(void*))RB_SurfaceSkip, // SF_SKIP, + (void(*)(void*))RB_SurfaceFace, // SF_FACE, + (void(*)(void*))RB_SurfaceGrid, // SF_GRID, + (void(*)(void*))RB_SurfaceTriangles, // SF_TRIANGLES, + (void(*)(void*))RB_SurfacePolychain, // SF_POLY, + (void(*)(void*))RB_SurfaceMesh, // SF_MD3, + (void(*)(void*))RB_SurfaceAnim, // SF_MD4, +#ifdef RAVENMD4 + (void(*)(void*))RB_MDRSurfaceAnim, // SF_MDR, +#endif + (void(*)(void*))RB_IQMSurfaceAnim, // SF_IQM, + (void(*)(void*))RB_SurfaceFlare, // SF_FLARE, + (void(*)(void*))RB_SurfaceEntity, // SF_ENTITY + (void(*)(void*))RB_SurfaceDisplayList // SF_DISPLAY_LIST +}; diff --git a/src/renderergl1/tr_world.c b/src/renderergl1/tr_world.c new file mode 100644 index 00000000..de9715a8 --- /dev/null +++ b/src/renderergl1/tr_world.c @@ -0,0 +1,669 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +#include "tr_local.h" + + + +/* +================= +R_CullTriSurf + +Returns true if the grid is completely culled away. +Also sets the clipped hint bit in tess +================= +*/ +static qboolean R_CullTriSurf( srfTriangles_t *cv ) { + int boxCull; + + boxCull = R_CullLocalBox( cv->bounds ); + + if ( boxCull == CULL_OUT ) { + return qtrue; + } + return qfalse; +} + +/* +================= +R_CullGrid + +Returns true if the grid is completely culled away. +Also sets the clipped hint bit in tess +================= +*/ +static qboolean R_CullGrid( srfGridMesh_t *cv ) { + int boxCull; + int sphereCull; + + if ( r_nocurves->integer ) { + return qtrue; + } + + if ( tr.currentEntityNum != REFENTITYNUM_WORLD ) { + sphereCull = R_CullLocalPointAndRadius( cv->localOrigin, cv->meshRadius ); + } else { + sphereCull = R_CullPointAndRadius( cv->localOrigin, cv->meshRadius ); + } + + // check for trivial reject + if ( sphereCull == CULL_OUT ) + { + tr.pc.c_sphere_cull_patch_out++; + return qtrue; + } + // check bounding box if necessary + else if ( sphereCull == CULL_CLIP ) + { + tr.pc.c_sphere_cull_patch_clip++; + + boxCull = R_CullLocalBox( cv->meshBounds ); + + if ( boxCull == CULL_OUT ) + { + tr.pc.c_box_cull_patch_out++; + return qtrue; + } + else if ( boxCull == CULL_IN ) + { + tr.pc.c_box_cull_patch_in++; + } + else + { + tr.pc.c_box_cull_patch_clip++; + } + } + else + { + tr.pc.c_sphere_cull_patch_in++; + } + + return qfalse; +} + + +/* +================ +R_CullSurface + +Tries to back face cull surfaces before they are lighted or +added to the sorting list. + +This will also allow mirrors on both sides of a model without recursion. +================ +*/ +static qboolean R_CullSurface( surfaceType_t *surface, shader_t *shader ) { + srfSurfaceFace_t *sface; + float d; + + if ( r_nocull->integer ) { + return qfalse; + } + + if ( *surface == SF_GRID ) { + return R_CullGrid( (srfGridMesh_t *)surface ); + } + + if ( *surface == SF_TRIANGLES ) { + return R_CullTriSurf( (srfTriangles_t *)surface ); + } + + if ( *surface != SF_FACE ) { + return qfalse; + } + + if ( shader->cullType == CT_TWO_SIDED ) { + return qfalse; + } + + // face culling + if ( !r_facePlaneCull->integer ) { + return qfalse; + } + + sface = ( srfSurfaceFace_t * ) surface; + d = DotProduct (tr.or.viewOrigin, sface->plane.normal); + + // don't cull exactly on the plane, because there are levels of rounding + // through the BSP, ICD, and hardware that may cause pixel gaps if an + // epsilon isn't allowed here + if ( shader->cullType == CT_FRONT_SIDED ) { + if ( d < sface->plane.dist - 8 ) { + return qtrue; + } + } else { + if ( d > sface->plane.dist + 8 ) { + return qtrue; + } + } + + return qfalse; +} + + +static int R_DlightFace( srfSurfaceFace_t *face, int dlightBits ) { + float d; + int i; + dlight_t *dl; + + for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { + if ( ! ( dlightBits & ( 1 << i ) ) ) { + continue; + } + dl = &tr.refdef.dlights[i]; + d = DotProduct( dl->origin, face->plane.normal ) - face->plane.dist; + if ( d < -dl->radius || d > dl->radius ) { + // dlight doesn't reach the plane + dlightBits &= ~( 1 << i ); + } + } + + if ( !dlightBits ) { + tr.pc.c_dlightSurfacesCulled++; + } + + face->dlightBits = dlightBits; + return dlightBits; +} + +static int R_DlightGrid( srfGridMesh_t *grid, int dlightBits ) { + int i; + dlight_t *dl; + + for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { + if ( ! ( dlightBits & ( 1 << i ) ) ) { + continue; + } + dl = &tr.refdef.dlights[i]; + if ( dl->origin[0] - dl->radius > grid->meshBounds[1][0] + || dl->origin[0] + dl->radius < grid->meshBounds[0][0] + || dl->origin[1] - dl->radius > grid->meshBounds[1][1] + || dl->origin[1] + dl->radius < grid->meshBounds[0][1] + || dl->origin[2] - dl->radius > grid->meshBounds[1][2] + || dl->origin[2] + dl->radius < grid->meshBounds[0][2] ) { + // dlight doesn't reach the bounds + dlightBits &= ~( 1 << i ); + } + } + + if ( !dlightBits ) { + tr.pc.c_dlightSurfacesCulled++; + } + + grid->dlightBits = dlightBits; + return dlightBits; +} + + +static int R_DlightTrisurf( srfTriangles_t *surf, int dlightBits ) { + // FIXME: more dlight culling to trisurfs... + surf->dlightBits = dlightBits; + return dlightBits; +#if 0 + int i; + dlight_t *dl; + + for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { + if ( ! ( dlightBits & ( 1 << i ) ) ) { + continue; + } + dl = &tr.refdef.dlights[i]; + if ( dl->origin[0] - dl->radius > grid->meshBounds[1][0] + || dl->origin[0] + dl->radius < grid->meshBounds[0][0] + || dl->origin[1] - dl->radius > grid->meshBounds[1][1] + || dl->origin[1] + dl->radius < grid->meshBounds[0][1] + || dl->origin[2] - dl->radius > grid->meshBounds[1][2] + || dl->origin[2] + dl->radius < grid->meshBounds[0][2] ) { + // dlight doesn't reach the bounds + dlightBits &= ~( 1 << i ); + } + } + + if ( !dlightBits ) { + tr.pc.c_dlightSurfacesCulled++; + } + + grid->dlightBits = dlightBits; + return dlightBits; +#endif +} + +/* +==================== +R_DlightSurface + +The given surface is going to be drawn, and it touches a leaf +that is touched by one or more dlights, so try to throw out +more dlights if possible. +==================== +*/ +static int R_DlightSurface( msurface_t *surf, int dlightBits ) { + if ( *surf->data == SF_FACE ) { + dlightBits = R_DlightFace( (srfSurfaceFace_t *)surf->data, dlightBits ); + } else if ( *surf->data == SF_GRID ) { + dlightBits = R_DlightGrid( (srfGridMesh_t *)surf->data, dlightBits ); + } else if ( *surf->data == SF_TRIANGLES ) { + dlightBits = R_DlightTrisurf( (srfTriangles_t *)surf->data, dlightBits ); + } else { + dlightBits = 0; + } + + if ( dlightBits ) { + tr.pc.c_dlightSurfaces++; + } + + return dlightBits; +} + + + +/* +====================== +R_AddWorldSurface +====================== +*/ +static void R_AddWorldSurface( msurface_t *surf, int dlightBits ) { + if ( surf->viewCount == tr.viewCount ) { + return; // already in this view + } + + surf->viewCount = tr.viewCount; + // FIXME: bmodel fog? + + // try to cull before dlighting or adding + if ( R_CullSurface( surf->data, surf->shader ) ) { + return; + } + + // check for dlighting + if ( dlightBits ) { + dlightBits = R_DlightSurface( surf, dlightBits ); + dlightBits = ( dlightBits != 0 ); + } + + R_AddDrawSurf( surf->data, surf->shader, surf->fogIndex, dlightBits ); +} + +/* +============================================================= + + BRUSH MODELS + +============================================================= +*/ + +/* +================= +R_AddBrushModelSurfaces +================= +*/ +void R_AddBrushModelSurfaces ( trRefEntity_t *ent ) { + bmodel_t *bmodel; + int clip; + model_t *pModel; + int i; + + pModel = R_GetModelByHandle( ent->e.hModel ); + + bmodel = pModel->bmodel; + + clip = R_CullLocalBox( bmodel->bounds ); + if ( clip == CULL_OUT ) { + return; + } + + R_SetupEntityLighting( &tr.refdef, ent ); + R_DlightBmodel( bmodel ); + + for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) { + R_AddWorldSurface( bmodel->firstSurface + i, tr.currentEntity->needDlights ); + } +} + + +/* +============================================================= + + WORLD MODEL + +============================================================= +*/ + + +/* +================ +R_RecursiveWorldNode +================ +*/ +static void R_RecursiveWorldNode( mnode_t *node, int planeBits, int dlightBits ) { + + do { + int newDlights[2]; + + // if the node wasn't marked as potentially visible, exit + if (node->visframe != tr.visCount) { + return; + } + + // if the bounding volume is outside the frustum, nothing + // inside can be visible OPTIMIZE: don't do this all the way to leafs? + + if ( !r_nocull->integer ) { + int r; + + if ( planeBits & 1 ) { + r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[0]); + if (r == 2) { + return; // culled + } + if ( r == 1 ) { + planeBits &= ~1; // all descendants will also be in front + } + } + + if ( planeBits & 2 ) { + r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[1]); + if (r == 2) { + return; // culled + } + if ( r == 1 ) { + planeBits &= ~2; // all descendants will also be in front + } + } + + if ( planeBits & 4 ) { + r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[2]); + if (r == 2) { + return; // culled + } + if ( r == 1 ) { + planeBits &= ~4; // all descendants will also be in front + } + } + + if ( planeBits & 8 ) { + r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[3]); + if (r == 2) { + return; // culled + } + if ( r == 1 ) { + planeBits &= ~8; // all descendants will also be in front + } + } + + } + + if ( node->contents != -1 ) { + break; + } + + // node is just a decision point, so go down both sides + // since we don't care about sort orders, just go positive to negative + + // determine which dlights are needed + newDlights[0] = 0; + newDlights[1] = 0; + if ( dlightBits ) { + int i; + + for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { + dlight_t *dl; + float dist; + + if ( dlightBits & ( 1 << i ) ) { + dl = &tr.refdef.dlights[i]; + dist = DotProduct( dl->origin, node->plane->normal ) - node->plane->dist; + + if ( dist > -dl->radius ) { + newDlights[0] |= ( 1 << i ); + } + if ( dist < dl->radius ) { + newDlights[1] |= ( 1 << i ); + } + } + } + } + + // recurse down the children, front side first + R_RecursiveWorldNode (node->children[0], planeBits, newDlights[0] ); + + // tail recurse + node = node->children[1]; + dlightBits = newDlights[1]; + } while ( 1 ); + + { + // leaf node, so add mark surfaces + int c; + msurface_t *surf, **mark; + + tr.pc.c_leafs++; + + // add to z buffer bounds + if ( node->mins[0] < tr.viewParms.visBounds[0][0] ) { + tr.viewParms.visBounds[0][0] = node->mins[0]; + } + if ( node->mins[1] < tr.viewParms.visBounds[0][1] ) { + tr.viewParms.visBounds[0][1] = node->mins[1]; + } + if ( node->mins[2] < tr.viewParms.visBounds[0][2] ) { + tr.viewParms.visBounds[0][2] = node->mins[2]; + } + + if ( node->maxs[0] > tr.viewParms.visBounds[1][0] ) { + tr.viewParms.visBounds[1][0] = node->maxs[0]; + } + if ( node->maxs[1] > tr.viewParms.visBounds[1][1] ) { + tr.viewParms.visBounds[1][1] = node->maxs[1]; + } + if ( node->maxs[2] > tr.viewParms.visBounds[1][2] ) { + tr.viewParms.visBounds[1][2] = node->maxs[2]; + } + + // add the individual surfaces + mark = node->firstmarksurface; + c = node->nummarksurfaces; + while (c--) { + // the surface may have already been added if it + // spans multiple leafs + surf = *mark; + R_AddWorldSurface( surf, dlightBits ); + mark++; + } + } + +} + + +/* +=============== +R_PointInLeaf +=============== +*/ +static mnode_t *R_PointInLeaf( const vec3_t p ) { + mnode_t *node; + float d; + cplane_t *plane; + + if ( !tr.world ) { + ri.Error (ERR_DROP, "R_PointInLeaf: bad model"); + } + + node = tr.world->nodes; + while( 1 ) { + if (node->contents != -1) { + break; + } + plane = node->plane; + d = DotProduct (p,plane->normal) - plane->dist; + if (d > 0) { + node = node->children[0]; + } else { + node = node->children[1]; + } + } + + return node; +} + +/* +============== +R_ClusterPVS +============== +*/ +static const byte *R_ClusterPVS (int cluster) { + if (!tr.world || !tr.world->vis || cluster < 0 || cluster >= tr.world->numClusters ) { + return tr.world->novis; + } + + return tr.world->vis + cluster * tr.world->clusterBytes; +} + +/* +================= +R_inPVS +================= +*/ +qboolean R_inPVS( const vec3_t p1, const vec3_t p2 ) { + mnode_t *leaf; + byte *vis; + + leaf = R_PointInLeaf( p1 ); + vis = ri.CM_ClusterPVS( leaf->cluster ); // why not R_ClusterPVS ?? + leaf = R_PointInLeaf( p2 ); + + if ( !(vis[leaf->cluster>>3] & (1<<(leaf->cluster&7))) ) { + return qfalse; + } + return qtrue; +} + +/* +=============== +R_MarkLeaves + +Mark the leaves and nodes that are in the PVS for the current +cluster +=============== +*/ +static void R_MarkLeaves (void) { + const byte *vis; + mnode_t *leaf, *parent; + int i; + int cluster; + + // lockpvs lets designers walk around to determine the + // extent of the current pvs + if ( r_lockpvs->integer ) { + return; + } + + // current viewcluster + leaf = R_PointInLeaf( tr.viewParms.pvsOrigin ); + cluster = leaf->cluster; + + // if the cluster is the same and the area visibility matrix + // hasn't changed, we don't need to mark everything again + + // if r_showcluster was just turned on, remark everything + if ( tr.viewCluster == cluster && !tr.refdef.areamaskModified + && !r_showcluster->modified ) { + return; + } + + if ( r_showcluster->modified || r_showcluster->integer ) { + r_showcluster->modified = qfalse; + if ( r_showcluster->integer ) { + ri.Printf( PRINT_ALL, "cluster:%i area:%i\n", cluster, leaf->area ); + } + } + + tr.visCount++; + tr.viewCluster = cluster; + + if ( r_novis->integer || tr.viewCluster == -1 ) { + for (i=0 ; inumnodes ; i++) { + if (tr.world->nodes[i].contents != CONTENTS_SOLID) { + tr.world->nodes[i].visframe = tr.visCount; + } + } + return; + } + + vis = R_ClusterPVS (tr.viewCluster); + + for (i=0,leaf=tr.world->nodes ; inumnodes ; i++, leaf++) { + cluster = leaf->cluster; + if ( cluster < 0 || cluster >= tr.world->numClusters ) { + continue; + } + + // check general pvs + if ( !(vis[cluster>>3] & (1<<(cluster&7))) ) { + continue; + } + + // check for door connection + if ( (tr.refdef.areamask[leaf->area>>3] & (1<<(leaf->area&7)) ) ) { + continue; // not visible + } + + parent = leaf; + do { + if (parent->visframe == tr.visCount) + break; + parent->visframe = tr.visCount; + parent = parent->parent; + } while (parent); + } +} + + +/* +============= +R_AddWorldSurfaces +============= +*/ +void R_AddWorldSurfaces (void) { + if ( !r_drawworld->integer ) { + return; + } + + if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { + return; + } + + tr.currentEntityNum = REFENTITYNUM_WORLD; + tr.shiftedEntityNum = tr.currentEntityNum << QSORT_REFENTITYNUM_SHIFT; + + // determine which leaves are in the PVS / areamask + R_MarkLeaves (); + + // clear out the visible min/max + ClearBounds( tr.viewParms.visBounds[0], tr.viewParms.visBounds[1] ); + + // perform frustum culling and add all the potentially visible surfaces + if ( tr.refdef.num_dlights > 32 ) { + tr.refdef.num_dlights = 32 ; + } + R_RecursiveWorldNode( tr.world->nodes, 15, ( 1 << tr.refdef.num_dlights ) - 1 ); +} diff --git a/src/renderergl2/glsl/bokeh_fp.glsl b/src/renderergl2/glsl/bokeh_fp.glsl new file mode 100644 index 00000000..d08816ae --- /dev/null +++ b/src/renderergl2/glsl/bokeh_fp.glsl @@ -0,0 +1,70 @@ +uniform sampler2D u_TextureMap; + +uniform vec4 u_Color; + +uniform vec2 u_InvTexRes; +varying vec2 var_TexCoords; + +void main() +{ + vec4 color; + vec2 tc; + +#if 0 + float c[7] = float[7](1.0, 0.9659258263, 0.8660254038, 0.7071067812, 0.5, 0.2588190451, 0.0); + + tc = var_TexCoords + u_InvTexRes * vec2( c[0], c[6]); color = texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( c[1], c[5]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( c[2], c[4]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( c[3], c[3]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( c[4], c[2]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( c[5], c[1]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( c[6], c[0]); color += texture2D(u_TextureMap, tc); + + tc = var_TexCoords + u_InvTexRes * vec2( c[1], -c[5]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( c[2], -c[4]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( c[3], -c[3]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( c[4], -c[2]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( c[5], -c[1]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( c[6], -c[0]); color += texture2D(u_TextureMap, tc); + + tc = var_TexCoords + u_InvTexRes * vec2( -c[0], c[6]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( -c[1], c[5]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( -c[2], c[4]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( -c[3], c[3]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( -c[4], c[2]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( -c[5], c[1]); color += texture2D(u_TextureMap, tc); + + tc = var_TexCoords + u_InvTexRes * vec2( -c[1], -c[5]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( -c[2], -c[4]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( -c[3], -c[3]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( -c[4], -c[2]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( -c[5], -c[1]); color += texture2D(u_TextureMap, tc); + + gl_FragColor = color * 0.04166667 * u_Color; +#endif + + float c[5] = float[5](1.0, 0.9238795325, 0.7071067812, 0.3826834324, 0.0); + + tc = var_TexCoords + u_InvTexRes * vec2( c[0], c[4]); color = texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( c[1], c[3]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( c[2], c[2]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( c[3], c[1]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( c[4], c[0]); color += texture2D(u_TextureMap, tc); + + tc = var_TexCoords + u_InvTexRes * vec2( c[1], -c[3]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( c[2], -c[2]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( c[3], -c[1]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( c[4], -c[0]); color += texture2D(u_TextureMap, tc); + + tc = var_TexCoords + u_InvTexRes * vec2( -c[0], c[4]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( -c[1], c[3]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( -c[2], c[2]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( -c[3], c[1]); color += texture2D(u_TextureMap, tc); + + tc = var_TexCoords + u_InvTexRes * vec2( -c[1], -c[3]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( -c[2], -c[2]); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( -c[3], -c[1]); color += texture2D(u_TextureMap, tc); + + gl_FragColor = color * 0.0625 * u_Color; +} diff --git a/src/renderergl2/glsl/bokeh_vp.glsl b/src/renderergl2/glsl/bokeh_vp.glsl new file mode 100644 index 00000000..5ca41600 --- /dev/null +++ b/src/renderergl2/glsl/bokeh_vp.glsl @@ -0,0 +1,13 @@ +attribute vec4 attr_Position; +attribute vec4 attr_TexCoord0; + +uniform mat4 u_ModelViewProjectionMatrix; + +varying vec2 var_TexCoords; + + +void main() +{ + gl_Position = u_ModelViewProjectionMatrix * attr_Position; + var_TexCoords = attr_TexCoord0.st; +} diff --git a/src/renderergl2/glsl/calclevels4x_fp.glsl b/src/renderergl2/glsl/calclevels4x_fp.glsl new file mode 100644 index 00000000..c8cf06c7 --- /dev/null +++ b/src/renderergl2/glsl/calclevels4x_fp.glsl @@ -0,0 +1,55 @@ +uniform sampler2D u_TextureMap; + +uniform vec4 u_Color; + +uniform vec2 u_InvTexRes; +varying vec2 var_TexCoords; + +const vec3 LUMINANCE_VECTOR = vec3(0.2125, 0.7154, 0.0721); //vec3(0.299, 0.587, 0.114); + +vec3 GetValues(vec2 offset, vec3 current) +{ + vec3 minAvgMax; + vec2 tc = var_TexCoords + u_InvTexRes * offset; minAvgMax = texture2D(u_TextureMap, tc).rgb; + +#ifdef FIRST_PASS + float lumi = max(dot(LUMINANCE_VECTOR, minAvgMax), 0.000001); + float loglumi = clamp(log2(lumi), -10.0, 10.0); + minAvgMax = vec3(loglumi * 0.05 + 0.5); +#endif + + return vec3(min(current.x, minAvgMax.x), current.y + minAvgMax.y, max(current.z, minAvgMax.z)); +} + +void main() +{ + vec3 current = vec3(1.0, 0.0, 0.0); + +#ifdef FIRST_PASS + current = GetValues(vec2( 0.0, 0.0), current); +#else + current = GetValues(vec2(-1.5, -1.5), current); + current = GetValues(vec2(-0.5, -1.5), current); + current = GetValues(vec2( 0.5, -1.5), current); + current = GetValues(vec2( 1.5, -1.5), current); + + current = GetValues(vec2(-1.5, -0.5), current); + current = GetValues(vec2(-0.5, -0.5), current); + current = GetValues(vec2( 0.5, -0.5), current); + current = GetValues(vec2( 1.5, -0.5), current); + + current = GetValues(vec2(-1.5, 0.5), current); + current = GetValues(vec2(-0.5, 0.5), current); + current = GetValues(vec2( 0.5, 0.5), current); + current = GetValues(vec2( 1.5, 0.5), current); + + current = GetValues(vec2(-1.5, 1.5), current); + current = GetValues(vec2(-0.5, 1.5), current); + current = GetValues(vec2( 0.5, 1.5), current); + current = GetValues(vec2( 1.5, 1.5), current); + + current.y *= 0.0625; +#endif + + gl_FragColor = vec4(current, 1.0f); +} diff --git a/src/renderergl2/glsl/calclevels4x_vp.glsl b/src/renderergl2/glsl/calclevels4x_vp.glsl new file mode 100644 index 00000000..5ca41600 --- /dev/null +++ b/src/renderergl2/glsl/calclevels4x_vp.glsl @@ -0,0 +1,13 @@ +attribute vec4 attr_Position; +attribute vec4 attr_TexCoord0; + +uniform mat4 u_ModelViewProjectionMatrix; + +varying vec2 var_TexCoords; + + +void main() +{ + gl_Position = u_ModelViewProjectionMatrix * attr_Position; + var_TexCoords = attr_TexCoord0.st; +} diff --git a/src/renderergl2/glsl/depthblur_fp.glsl b/src/renderergl2/glsl/depthblur_fp.glsl new file mode 100644 index 00000000..93895b4e --- /dev/null +++ b/src/renderergl2/glsl/depthblur_fp.glsl @@ -0,0 +1,58 @@ +uniform sampler2D u_ScreenImageMap; +uniform sampler2D u_ScreenDepthMap; + +uniform vec4 u_ViewInfo; // zfar / znear, zfar +varying vec2 var_ScreenTex; + +//float gauss[5] = float[5](0.30, 0.23, 0.097, 0.024, 0.0033); +float gauss[4] = float[4](0.40, 0.24, 0.054, 0.0044); +//float gauss[3] = float[3](0.60, 0.19, 0.0066); +#define GAUSS_SIZE 4 + +float getLinearDepth(sampler2D depthMap, const vec2 tex, const float zFarDivZNear) +{ + float sampleZDivW = texture2D(depthMap, tex).r; + return 1.0 / mix(zFarDivZNear, 1.0, sampleZDivW); +} + +vec4 depthGaussian1D(sampler2D imageMap, sampler2D depthMap, vec2 tex, float zFarDivZNear, float zFar) +{ + float scale = 1.0 / 256.0; + +#if defined(USE_HORIZONTAL_BLUR) + vec2 direction = vec2(1.0, 0.0) * scale; +#else // if defined(USE_VERTICAL_BLUR) + vec2 direction = vec2(0.0, 1.0) * scale; +#endif + + float depthCenter = zFar * getLinearDepth(depthMap, tex, zFarDivZNear); + vec2 centerSlope = vec2(dFdx(depthCenter), dFdy(depthCenter)) / vec2(dFdx(tex.x), dFdy(tex.y)); + + vec4 result = texture2D(imageMap, tex) * gauss[0]; + float total = gauss[0]; + + int i, j; + for (i = 0; i < 2; i++) + { + for (j = 1; j < GAUSS_SIZE; j++) + { + vec2 offset = direction * j; + float depthSample = zFar * getLinearDepth(depthMap, tex + offset, zFarDivZNear); + float depthExpected = depthCenter + dot(centerSlope, offset); + if(abs(depthSample - depthExpected) < 5.0) + { + result += texture2D(imageMap, tex + offset) * gauss[j]; + total += gauss[j]; + } + } + + direction = -direction; + } + + return result / total; +} + +void main() +{ + gl_FragColor = depthGaussian1D(u_ScreenImageMap, u_ScreenDepthMap, var_ScreenTex, u_ViewInfo.x, u_ViewInfo.y); +} diff --git a/src/renderergl2/glsl/depthblur_vp.glsl b/src/renderergl2/glsl/depthblur_vp.glsl new file mode 100644 index 00000000..9c46a79f --- /dev/null +++ b/src/renderergl2/glsl/depthblur_vp.glsl @@ -0,0 +1,12 @@ +attribute vec4 attr_Position; +attribute vec4 attr_TexCoord0; + +varying vec2 var_ScreenTex; + +void main() +{ + gl_Position = attr_Position; + var_ScreenTex = attr_TexCoord0.xy; + //vec2 screenCoords = gl_Position.xy / gl_Position.w; + //var_ScreenTex = screenCoords * 0.5 + 0.5; +} diff --git a/src/renderergl2/glsl/dlight_fp.glsl b/src/renderergl2/glsl/dlight_fp.glsl new file mode 100644 index 00000000..8ffca5b9 --- /dev/null +++ b/src/renderergl2/glsl/dlight_fp.glsl @@ -0,0 +1,12 @@ +uniform sampler2D u_DiffuseMap; + +varying vec2 var_Tex1; +varying vec4 var_Color; + + +void main() +{ + vec4 color = texture2D(u_DiffuseMap, var_Tex1); + + gl_FragColor = color * var_Color; +} diff --git a/src/renderergl2/glsl/dlight_vp.glsl b/src/renderergl2/glsl/dlight_vp.glsl new file mode 100644 index 00000000..d9fd71d0 --- /dev/null +++ b/src/renderergl2/glsl/dlight_vp.glsl @@ -0,0 +1,92 @@ +attribute vec4 attr_Position; +attribute vec4 attr_TexCoord0; +attribute vec3 attr_Normal; + +uniform vec4 u_DlightInfo; + +#if defined(USE_DEFORM_VERTEXES) +uniform int u_DeformGen; +uniform float u_DeformParams[5]; +uniform float u_Time; +#endif + +uniform vec4 u_Color; +uniform mat4 u_ModelViewProjectionMatrix; + +varying vec2 var_Tex1; +varying vec4 var_Color; + +#if defined(USE_DEFORM_VERTEXES) +vec3 DeformPosition(const vec3 pos, const vec3 normal, const vec2 st) +{ + if (u_DeformGen == 0) + { + return pos; + } + + float base = u_DeformParams[0]; + float amplitude = u_DeformParams[1]; + float phase = u_DeformParams[2]; + float frequency = u_DeformParams[3]; + float spread = u_DeformParams[4]; + + if (u_DeformGen == DGEN_BULGE) + { + phase *= M_PI * 0.25 * st.x; + } + else // if (u_DeformGen <= DGEN_WAVE_INVERSE_SAWTOOTH) + { + phase += dot(pos.xyz, vec3(spread)); + } + + float value = phase + (u_Time * frequency); + float func; + + if (u_DeformGen == DGEN_WAVE_SIN) + { + func = sin(value * 2.0 * M_PI); + } + else if (u_DeformGen == DGEN_WAVE_SQUARE) + { + func = sign(sin(value * 2.0 * M_PI)); + } + else if (u_DeformGen == DGEN_WAVE_TRIANGLE) + { + func = abs(fract(value + 0.75) - 0.5) * 4.0 - 1.0; + } + else if (u_DeformGen == DGEN_WAVE_SAWTOOTH) + { + func = fract(value); + } + else if (u_DeformGen == DGEN_WAVE_INVERSE_SAWTOOTH) + { + func = (1.0 - fract(value)); + } + else if (u_DeformGen == DGEN_BULGE) + { + func = sin(value); + } + + return pos + normal * (base + func * amplitude); +} +#endif + +void main() +{ + vec4 position = attr_Position; + vec3 normal = attr_Normal; + +#if defined(USE_DEFORM_VERTEXES) + position.xyz = DeformPosition(position.xyz, normal, attr_TexCoord0.st); +#endif + + gl_Position = u_ModelViewProjectionMatrix * position; + + vec3 dist = u_DlightInfo.xyz - position.xyz; + + var_Tex1 = dist.xy * u_DlightInfo.a + vec2(0.5); + float dlightmod = step(0.0, dot(dist, normal)); + dlightmod *= clamp(2.0 * (1.0 - abs(dist.z) * u_DlightInfo.a), 0.0, 1.0); + + var_Color = u_Color * dlightmod; +} diff --git a/src/renderergl2/glsl/down4x_fp.glsl b/src/renderergl2/glsl/down4x_fp.glsl new file mode 100644 index 00000000..0f88fb2e --- /dev/null +++ b/src/renderergl2/glsl/down4x_fp.glsl @@ -0,0 +1,34 @@ +uniform sampler2D u_TextureMap; + +uniform vec2 u_InvTexRes; +varying vec2 var_TexCoords; + +void main() +{ + vec4 color; + vec2 tc; + + tc = var_TexCoords + u_InvTexRes * vec2(-1.5, -1.5); color = texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2(-0.5, -1.5); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( 0.5, -1.5); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( 1.5, -1.5); color += texture2D(u_TextureMap, tc); + + tc = var_TexCoords + u_InvTexRes * vec2(-1.5, -0.5); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2(-0.5, -0.5); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( 0.5, -0.5); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( 1.5, -0.5); color += texture2D(u_TextureMap, tc); + + tc = var_TexCoords + u_InvTexRes * vec2(-1.5, 0.5); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2(-0.5, 0.5); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( 0.5, 0.5); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( 1.5, 0.5); color += texture2D(u_TextureMap, tc); + + tc = var_TexCoords + u_InvTexRes * vec2(-1.5, 1.5); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2(-0.5, 1.5); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( 0.5, 1.5); color += texture2D(u_TextureMap, tc); + tc = var_TexCoords + u_InvTexRes * vec2( 1.5, 1.5); color += texture2D(u_TextureMap, tc); + + color *= 0.0625; + + gl_FragColor = color; +} diff --git a/src/renderergl2/glsl/down4x_vp.glsl b/src/renderergl2/glsl/down4x_vp.glsl new file mode 100644 index 00000000..5ca41600 --- /dev/null +++ b/src/renderergl2/glsl/down4x_vp.glsl @@ -0,0 +1,13 @@ +attribute vec4 attr_Position; +attribute vec4 attr_TexCoord0; + +uniform mat4 u_ModelViewProjectionMatrix; + +varying vec2 var_TexCoords; + + +void main() +{ + gl_Position = u_ModelViewProjectionMatrix * attr_Position; + var_TexCoords = attr_TexCoord0.st; +} diff --git a/src/renderergl2/glsl/fogpass_fp.glsl b/src/renderergl2/glsl/fogpass_fp.glsl new file mode 100644 index 00000000..91884304 --- /dev/null +++ b/src/renderergl2/glsl/fogpass_fp.glsl @@ -0,0 +1,9 @@ +uniform vec4 u_Color; + +varying float var_Scale; + +void main() +{ + gl_FragColor = u_Color; + gl_FragColor.a *= sqrt(clamp(var_Scale, 0.0, 1.0)); +} diff --git a/src/renderergl2/glsl/fogpass_vp.glsl b/src/renderergl2/glsl/fogpass_vp.glsl new file mode 100644 index 00000000..f18bc707 --- /dev/null +++ b/src/renderergl2/glsl/fogpass_vp.glsl @@ -0,0 +1,117 @@ +attribute vec4 attr_Position; +attribute vec3 attr_Normal; +attribute vec4 attr_TexCoord0; + +//#if defined(USE_VERTEX_ANIMATION) +attribute vec4 attr_Position2; +attribute vec3 attr_Normal2; +//#endif + +uniform vec4 u_FogDistance; +uniform vec4 u_FogDepth; +uniform float u_FogEyeT; + +//#if defined(USE_DEFORM_VERTEXES) +uniform int u_DeformGen; +uniform float u_DeformParams[5]; +//#endif + +uniform float u_Time; +uniform mat4 u_ModelViewProjectionMatrix; + +//#if defined(USE_VERTEX_ANIMATION) +uniform float u_VertexLerp; +//#endif + +varying float var_Scale; + +#if defined(USE_DEFORM_VERTEXES) +vec3 DeformPosition(const vec3 pos, const vec3 normal, const vec2 st) +{ + if (u_DeformGen == 0) + { + return pos; + } + + float base = u_DeformParams[0]; + float amplitude = u_DeformParams[1]; + float phase = u_DeformParams[2]; + float frequency = u_DeformParams[3]; + float spread = u_DeformParams[4]; + + if (u_DeformGen == DGEN_BULGE) + { + phase *= M_PI * 0.25 * st.x; + } + else // if (u_DeformGen <= DGEN_WAVE_INVERSE_SAWTOOTH) + { + phase += dot(pos.xyz, vec3(spread)); + } + + float value = phase + (u_Time * frequency); + float func; + + if (u_DeformGen == DGEN_WAVE_SIN) + { + func = sin(value * 2.0 * M_PI); + } + else if (u_DeformGen == DGEN_WAVE_SQUARE) + { + func = sign(sin(value * 2.0 * M_PI)); + } + else if (u_DeformGen == DGEN_WAVE_TRIANGLE) + { + func = abs(fract(value + 0.75) - 0.5) * 4.0 - 1.0; + } + else if (u_DeformGen == DGEN_WAVE_SAWTOOTH) + { + func = fract(value); + } + else if (u_DeformGen == DGEN_WAVE_INVERSE_SAWTOOTH) + { + func = (1.0 - fract(value)); + } + else if (u_DeformGen == DGEN_BULGE) + { + func = sin(value); + } + + return pos + normal * (base + func * amplitude); +} +#endif + +float CalcFog(vec4 position) +{ + float s = dot(position, u_FogDistance) * 8.0; + float t = dot(position, u_FogDepth); + + if (t < 1.0) + { + t = step(step(0.0, -u_FogEyeT), t); + } + else + { + t /= t - min(u_FogEyeT, 0.0); + } + + return s * t; +} + +void main() +{ +#if defined(USE_VERTEX_ANIMATION) + vec4 position = mix(attr_Position, attr_Position2, u_VertexLerp); + vec3 normal = normalize(mix(attr_Normal, attr_Normal2, u_VertexLerp)); +#else + vec4 position = attr_Position; + vec3 normal = attr_Normal; +#endif + +#if defined(USE_DEFORM_VERTEXES) + position.xyz = DeformPosition(position.xyz, normal, attr_TexCoord0.st); +#endif + + gl_Position = u_ModelViewProjectionMatrix * position; + + var_Scale = CalcFog(position); +} diff --git a/src/renderergl2/glsl/generic_fp.glsl b/src/renderergl2/glsl/generic_fp.glsl new file mode 100644 index 00000000..dea52e06 --- /dev/null +++ b/src/renderergl2/glsl/generic_fp.glsl @@ -0,0 +1,43 @@ +uniform sampler2D u_DiffuseMap; + +#if defined(USE_LIGHTMAP) +uniform sampler2D u_LightMap; + +uniform int u_Texture1Env; +#endif + +varying vec2 var_DiffuseTex; + +#if defined(USE_LIGHTMAP) +varying vec2 var_LightTex; +#endif + +varying vec4 var_Color; + + +void main() +{ + vec4 color = texture2D(u_DiffuseMap, var_DiffuseTex); +#if defined(USE_LIGHTMAP) + vec4 color2 = texture2D(u_LightMap, var_LightTex); + #if defined(RGBE_LIGHTMAP) + color2.rgb *= exp2(color2.a * 255.0 - 128.0); + color2.a = 1.0; + #endif + + if (u_Texture1Env == TEXENV_MODULATE) + { + color *= color2; + } + else if (u_Texture1Env == TEXENV_ADD) + { + color += color2; + } + else if (u_Texture1Env == TEXENV_REPLACE) + { + color = color2; + } +#endif + + gl_FragColor = color * var_Color; +} diff --git a/src/renderergl2/glsl/generic_vp.glsl b/src/renderergl2/glsl/generic_vp.glsl new file mode 100644 index 00000000..67360b1b --- /dev/null +++ b/src/renderergl2/glsl/generic_vp.glsl @@ -0,0 +1,251 @@ +attribute vec4 attr_Position; +attribute vec3 attr_Normal; + +#if defined(USE_VERTEX_ANIMATION) +attribute vec4 attr_Position2; +attribute vec3 attr_Normal2; +#endif + +attribute vec4 attr_Color; +attribute vec4 attr_TexCoord0; + +#if defined(USE_LIGHTMAP) || defined(USE_TCGEN) +attribute vec4 attr_TexCoord1; +#endif + +uniform vec4 u_DiffuseTexMatrix; +uniform vec4 u_DiffuseTexOffTurb; + +#if defined(USE_TCGEN) || defined(USE_RGBAGEN) +uniform vec3 u_ViewOrigin; +#endif + +#if defined(USE_TCGEN) +uniform int u_TCGen0; +uniform vec3 u_TCGen0Vector0; +uniform vec3 u_TCGen0Vector1; +#endif + +#if defined(USE_FOG) +uniform vec4 u_FogDistance; +uniform vec4 u_FogDepth; +uniform float u_FogEyeT; +uniform vec4 u_FogColorMask; +#endif + +#if defined(USE_DEFORM_VERTEXES) +uniform int u_DeformGen; +uniform float u_DeformParams[5]; +uniform float u_Time; +#endif + +uniform mat4 u_ModelViewProjectionMatrix; +uniform vec4 u_BaseColor; +uniform vec4 u_VertColor; + +#if defined(USE_RGBAGEN) +uniform int u_ColorGen; +uniform int u_AlphaGen; +uniform vec3 u_AmbientLight; +uniform vec3 u_DirectedLight; +uniform vec4 u_LightOrigin; +uniform float u_PortalRange; +#endif + +#if defined(USE_VERTEX_ANIMATION) +uniform float u_VertexLerp; +#endif + +varying vec2 var_DiffuseTex; +#if defined(USE_LIGHTMAP) +varying vec2 var_LightTex; +#endif +varying vec4 var_Color; + +#if defined(USE_DEFORM_VERTEXES) +vec3 DeformPosition(const vec3 pos, const vec3 normal, const vec2 st) +{ + float base = u_DeformParams[0]; + float amplitude = u_DeformParams[1]; + float phase = u_DeformParams[2]; + float frequency = u_DeformParams[3]; + float spread = u_DeformParams[4]; + + if (u_DeformGen == DGEN_BULGE) + { + phase *= M_PI * 0.25 * st.x; + } + else // if (u_DeformGen <= DGEN_WAVE_INVERSE_SAWTOOTH) + { + phase += dot(pos.xyz, vec3(spread)); + } + + float value = phase + (u_Time * frequency); + float func; + + if (u_DeformGen == DGEN_WAVE_SIN) + { + func = sin(value * 2.0 * M_PI); + } + else if (u_DeformGen == DGEN_WAVE_SQUARE) + { + func = sign(sin(value * 2.0 * M_PI)); + } + else if (u_DeformGen == DGEN_WAVE_TRIANGLE) + { + func = abs(fract(value + 0.75) - 0.5) * 4.0 - 1.0; + } + else if (u_DeformGen == DGEN_WAVE_SAWTOOTH) + { + func = fract(value); + } + else if (u_DeformGen == DGEN_WAVE_INVERSE_SAWTOOTH) + { + func = (1.0 - fract(value)); + } + else if (u_DeformGen == DGEN_BULGE) + { + func = sin(value); + } + + return pos + normal * (base + func * amplitude); +} +#endif + +#if defined(USE_TCGEN) +vec2 GenTexCoords(int TCGen, vec3 position, vec3 normal, vec3 TCGenVector0, vec3 TCGenVector1) +{ + vec2 tex = attr_TexCoord0.st; + + if (TCGen == TCGEN_LIGHTMAP) + { + tex = attr_TexCoord1.st; + } + else if (TCGen == TCGEN_ENVIRONMENT_MAPPED) + { + vec3 viewer = normalize(u_ViewOrigin - position); + tex = -reflect(viewer, normal).yz * vec2(0.5, -0.5) + 0.5; + } + else if (TCGen == TCGEN_VECTOR) + { + tex = vec2(dot(position, TCGenVector0), dot(position, TCGenVector1)); + } + + return tex; +} +#endif + +#if defined(USE_TCMOD) +vec2 ModTexCoords(vec2 st, vec3 position, vec4 texMatrix, vec4 offTurb) +{ + float amplitude = offTurb.z; + float phase = offTurb.w; + vec2 st2 = vec2(dot(st, texMatrix.xz), dot(st, texMatrix.yw)) + offTurb.xy; + + vec3 offsetPos = position / 1024.0; + offsetPos.x += offsetPos.z; + + vec2 texOffset = sin((offsetPos.xy + vec2(phase)) * 2.0 * M_PI); + + return st2 + texOffset * amplitude; +} +#endif + +#if defined(USE_RGBAGEN) +vec4 CalcColor(vec3 position, vec3 normal) +{ + vec4 color = u_VertColor * attr_Color + u_BaseColor; + + if (u_ColorGen == CGEN_LIGHTING_DIFFUSE) + { + float incoming = clamp(dot(normal, u_LightOrigin.xyz), 0.0, 1.0); + + color.rgb = clamp(u_DirectedLight * incoming + u_AmbientLight, 0.0, 1.0); + } + + vec3 toView = u_ViewOrigin - position; + vec3 viewer = normalize(u_ViewOrigin - position); + + if (u_AlphaGen == AGEN_LIGHTING_SPECULAR) + { + vec3 lightDir = normalize(vec3(-960.0, -1980.0, 96.0) - position.xyz); + vec3 halfangle = normalize(lightDir + viewer); + + color.a = pow(max(dot(normal, halfangle), 0.0), 8.0); + } + else if (u_AlphaGen == AGEN_PORTAL) + { + float alpha = length(toView) / u_PortalRange; + + color.a = clamp(alpha, 0.0, 1.0); + } + else if (u_AlphaGen == AGEN_FRESNEL) + { + color.a = 0.10 + 0.90 * pow(1.0 - dot(normal, viewer), 5); + } + + return color; +} +#endif + +#if defined(USE_FOG) +float CalcFog(vec4 position) +{ + float s = dot(position, u_FogDistance) * 8.0; + float t = dot(position, u_FogDepth); + + if (t < 1.0) + { + t = step(step(0.0, -u_FogEyeT), t); + } + else + { + t /= t - min(u_FogEyeT, 0.0); + } + + return s * t; +} +#endif + +void main() +{ +#if defined(USE_VERTEX_ANIMATION) + vec4 position = mix(attr_Position, attr_Position2, u_VertexLerp); + vec3 normal = normalize(mix(attr_Normal, attr_Normal2, u_VertexLerp)); +#else + vec4 position = attr_Position; + vec3 normal = attr_Normal; +#endif + +#if defined(USE_DEFORM_VERTEXES) + position.xyz = DeformPosition(position.xyz, normal, attr_TexCoord0.st); +#endif + + gl_Position = u_ModelViewProjectionMatrix * position; + +#if defined(USE_TCGEN) + vec2 tex = GenTexCoords(u_TCGen0, position.xyz, normal, u_TCGen0Vector0, u_TCGen0Vector1); +#else + vec2 tex = attr_TexCoord0.st; +#endif + +#if defined(USE_TCMOD) + var_DiffuseTex = ModTexCoords(tex, position.xyz, u_DiffuseTexMatrix, u_DiffuseTexOffTurb); +#else + var_DiffuseTex = tex; +#endif + +#if defined(USE_LIGHTMAP) + var_LightTex = attr_TexCoord1.st; +#endif + +#if defined(USE_RGBAGEN) + var_Color = CalcColor(position.xyz, normal); +#else + var_Color = u_VertColor * attr_Color + u_BaseColor; +#endif + +#if defined(USE_FOG) + var_Color *= vec4(1.0) - u_FogColorMask * sqrt(clamp(CalcFog(position), 0.0, 1.0)); +#endif +} diff --git a/src/renderergl2/glsl/lightall_fp.glsl b/src/renderergl2/glsl/lightall_fp.glsl new file mode 100644 index 00000000..ec9bb2dc --- /dev/null +++ b/src/renderergl2/glsl/lightall_fp.glsl @@ -0,0 +1,383 @@ +uniform sampler2D u_DiffuseMap; + +#if defined(USE_LIGHTMAP) +uniform sampler2D u_LightMap; +#endif + +#if defined(USE_NORMALMAP) +uniform sampler2D u_NormalMap; +#endif + +#if defined(USE_DELUXEMAP) +uniform sampler2D u_DeluxeMap; +#endif + +#if defined(USE_SPECULARMAP) +uniform sampler2D u_SpecularMap; +#endif + +#if defined(USE_SHADOWMAP) +uniform sampler2D u_ShadowMap; +#endif + +uniform vec3 u_ViewOrigin; + +#if defined(USE_TCGEN) +uniform int u_TCGen0; +#endif + +#if defined(USE_LIGHT_VECTOR) +uniform vec3 u_DirectedLight; +uniform vec3 u_AmbientLight; +uniform float u_LightRadius; +#endif + +#if defined(USE_LIGHT) +uniform vec2 u_MaterialInfo; +#endif + +varying vec2 var_DiffuseTex; +#if defined(USE_LIGHTMAP) +varying vec2 var_LightTex; +#endif +varying vec4 var_Color; + +#if defined(USE_NORMALMAP) && !defined(USE_VERT_TANGENT_SPACE) +varying vec3 var_Position; +#endif + +#if defined(USE_TCGEN) || defined(USE_NORMALMAP) || (defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)) +varying vec3 var_SampleToView; +#endif + +#if !defined(USE_FAST_LIGHT) +varying vec3 var_Normal; +#endif + +#if defined(USE_VERT_TANGENT_SPACE) +varying vec3 var_Tangent; +varying vec3 var_Bitangent; +#endif + +varying vec3 var_VertLight; + +#if defined(USE_LIGHT) && !defined(USE_DELUXEMAP) +varying vec3 var_WorldLight; +#endif + +#define EPSILON 0.00000001 + +#if defined(USE_PARALLAXMAP) +float SampleHeight(sampler2D normalMap, vec2 t) +{ + #if defined(SWIZZLE_NORMALMAP) + return texture2D(normalMap, t).r; + #else + return texture2D(normalMap, t).a; + #endif +} + +float RayIntersectDisplaceMap(vec2 dp, vec2 ds, sampler2D normalMap) +{ + const int linearSearchSteps = 16; + const int binarySearchSteps = 6; + + float depthStep = 1.0 / float(linearSearchSteps); + + // current size of search window + float size = depthStep; + + // current depth position + float depth = 0.0; + + // best match found (starts with last position 1.0) + float bestDepth = 1.0; + + // search front to back for first point inside object + for(int i = 0; i < linearSearchSteps - 1; ++i) + { + depth += size; + + float t = 1.0 - SampleHeight(normalMap, dp + ds * depth); + + if(bestDepth > 0.996) // if no depth found yet + if(depth >= t) + bestDepth = depth; // store best depth + } + + depth = bestDepth; + + // recurse around first point (depth) for closest match + for(int i = 0; i < binarySearchSteps; ++i) + { + size *= 0.5; + + float t = 1.0 - SampleHeight(normalMap, dp + ds * depth); + + if(depth >= t) + { + bestDepth = depth; + depth -= 2.0 * size; + } + + depth += size; + } + + return bestDepth; +} +#endif + +float CalcDiffuse(vec3 N, vec3 L, vec3 E, float NE, float NL, float fzero, float shininess) +{ + #if defined(USE_OREN_NAYAR) || defined(USE_TRIACE_OREN_NAYAR) + float gamma = dot(E, L) - NE * NL; + float B = 2.22222 + 0.1 * shininess; + + #if defined(USE_OREN_NAYAR) + float A = 1.0 - 1.0 / (2.0 + 0.33 * shininess); + gamma = clamp(gamma, 0.0, 1.0); + #endif + + #if defined(USE_TRIACE_OREN_NAYAR) + float A = 1.0 - 1.0 / (2.0 + 0.65 * shininess); + + if (gamma >= 0.0) + #endif + { + B *= max(max(NL, NE), EPSILON); + } + + return (A + gamma / B) * (1.0 - fzero); + #else + return 1.0 - fzero; + #endif +} + +#if defined(USE_SPECULARMAP) +float CalcSpecular(float NH, float NL, float NE, float EH, float fzero, float shininess) +{ + #if defined(USE_BLINN) || defined(USE_TRIACE) || defined(USE_TORRANCE_SPARROW) + float blinn = pow(NH, shininess); + #endif + + #if defined(USE_BLINN) + return blinn; + #endif + + #if defined(USE_COOK_TORRANCE) || defined (USE_TRIACE) || defined (USE_TORRANCE_SPARROW) + float fresnel = fzero + (1.0 - fzero) * pow(1.0 - EH, 5); + #endif + + #if defined(USE_COOK_TORRANCE) || defined(USE_TORRANCE_SPARROW) + float geo = 2.0 * NH * min(NE, NL); + geo /= max(EH, geo); + #endif + + #if defined(USE_COOK_TORRANCE) + float m_sq = 2.0 / max(shininess, EPSILON); + float NH_sq = NH * NH; + float m_NH_sq = m_sq * NH_sq; + float beckmann = exp((NH_sq - 1.0) / max(m_NH_sq, EPSILON)) / max(4.0 * m_NH_sq * NH_sq, EPSILON); + + return fresnel * geo * beckmann / max(NE, EPSILON); + #endif + + #if defined(USE_TRIACE) + float scale = 0.1248582 * shininess + 0.2691817; + + return fresnel * scale * blinn / max(max(NL, NE), EPSILON); + #endif + + #if defined(USE_TORRANCE_SPARROW) + float scale = 0.125 * shininess + 1.0; + + return fresnel * geo * scale * blinn / max(NE, EPSILON); + #endif +} +#endif + +void main() +{ +#if !defined(USE_FAST_LIGHT) && (defined(USE_LIGHT) || defined(USE_NORMALMAP)) + vec3 surfNormal = normalize(var_Normal); +#endif + +#if defined(USE_DELUXEMAP) + vec3 worldLight = 2.0 * texture2D(u_DeluxeMap, var_LightTex).xyz - vec3(1.0); + //worldLight += var_WorldLight * 0.0001; +#elif defined(USE_LIGHT) + vec3 worldLight = var_WorldLight; +#endif + +#if defined(USE_LIGHTMAP) + vec4 lightSample = texture2D(u_LightMap, var_LightTex).rgba; + #if defined(RGBE_LIGHTMAP) + lightSample.rgb *= exp2(lightSample.a * 255.0 - 128.0); + #endif + vec3 directedLight = lightSample.rgb; +#elif defined(USE_LIGHT_VECTOR) && !defined(USE_FAST_LIGHT) + #if defined(USE_INVSQRLIGHT) + float intensity = 1.0 / dot(worldLight, worldLight); + #else + float intensity = clamp((1.0 - dot(worldLight, worldLight) / (u_LightRadius * u_LightRadius)) * 1.07, 0.0, 1.0); + #endif + + vec3 directedLight = u_DirectedLight * intensity; + vec3 ambientLight = u_AmbientLight; + + #if defined(USE_SHADOWMAP) + vec2 shadowTex = gl_FragCoord.xy * r_FBufScale; + directedLight *= texture2D(u_ShadowMap, shadowTex).r; + #endif +#elif defined(USE_LIGHT_VERTEX) && !defined(USE_FAST_LIGHT) + vec3 directedLight = var_VertLight; +#endif + +#if defined(USE_TCGEN) || defined(USE_NORMALMAP) || (defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)) + vec3 SampleToView = normalize(var_SampleToView); +#endif + vec2 tex = var_DiffuseTex; + + float ambientDiff = 1.0; + +#if defined(USE_NORMALMAP) + #if defined(USE_VERT_TANGENT_SPACE) + vec3 tangent = var_Tangent; + vec3 bitangent = var_Bitangent; + #else + vec3 q0 = dFdx(var_Position); + vec3 q1 = dFdy(var_Position); + vec2 st0 = dFdx(tex); + vec2 st1 = dFdy(tex); + float dir = sign(st1.t * st0.s - st0.t * st1.s); + + vec3 tangent = normalize( q0 * st1.t - q1 * st0.t) * dir; + vec3 bitangent = -normalize( q0 * st1.s - q1 * st0.s) * dir; + #endif + + mat3 tangentToWorld = mat3(tangent, bitangent, var_Normal); + + #if defined(USE_PARALLAXMAP) + vec3 offsetDir = normalize(SampleToView * tangentToWorld); + #if 0 + float height = SampleHeight(u_NormalMap, tex); + float pdist = 0.05 * height - (0.05 / 2.0); + #else + offsetDir.xy *= -0.05 / offsetDir.z; + float pdist = RayIntersectDisplaceMap(tex, offsetDir.xy, u_NormalMap); + #endif + tex += offsetDir.xy * pdist; + #endif + #if defined(SWIZZLE_NORMALMAP) + vec3 normal = 2.0 * texture2D(u_NormalMap, tex).agb - 1.0; + #else + vec3 normal = 2.0 * texture2D(u_NormalMap, tex).rgb - 1.0; + #endif + normal.z = sqrt(clamp(1.0 - dot(normal.xy, normal.xy), 0.0, 1.0)); + vec3 worldNormal = tangentToWorld * normal; + #if defined(r_normalAmbient) + ambientDiff = 0.781341 * normal.z + 0.218659; + #endif +#elif defined(USE_LIGHT) && !defined(USE_FAST_LIGHT) + vec3 worldNormal = surfNormal; +#endif + +#if (defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)) || (defined(USE_TCGEN) && defined(USE_NORMALMAP)) + worldNormal = normalize(worldNormal); +#endif + +#if defined(USE_TCGEN) && defined(USE_NORMALMAP) + if (u_TCGen0 == TCGEN_ENVIRONMENT_MAPPED) + { + tex = -reflect(normalize(SampleToView), worldNormal).yz * vec2(0.5, -0.5) + 0.5; + } +#endif + + vec4 diffuse = texture2D(u_DiffuseMap, tex); + +#if defined(USE_LIGHT) && defined(USE_FAST_LIGHT) + #if defined(USE_LIGHTMAP) + diffuse.rgb *= directedLight; + #endif +#elif defined(USE_LIGHT) + worldLight = normalize(worldLight); + + float surfNL = clamp(dot(surfNormal, worldLight), 0.0, 1.0); + + #if defined(USE_LIGHTMAP) || defined(USE_LIGHT_VERTEX) + #if defined(USE_STANDARD_DELUXEMAP) + // Standard deluxe mapping treats the light sample as fully directed + // and doesn't compensate for light angle attenuation. + vec3 ambientLight = vec3(0.0); + #else + // Separate the light sample into directed and ambient parts. + // + // ambientMax - if the cosine of the angle between the surface + // normal and the light is below this value, the light + // is fully ambient. + // directedMax - if the cosine of the angle between the surface + // normal and the light is above this value, the light + // is fully directed. + const float ambientMax = 0.25; + const float directedMax = 0.5; + + float directedScale = clamp((surfNL - ambientMax) / (directedMax - ambientMax), 0.0, 1.0); + + // Scale the directed portion to compensate for the baked-in + // light angle attenuation. + directedScale /= max(surfNL, ambientMax); + + #if defined(r_normalAmbient) + directedScale *= 1.0 - r_normalAmbient; + #endif + + // Recover any unused light as ambient + vec3 ambientLight = directedLight; + directedLight *= directedScale; + ambientLight -= directedLight * surfNL; + #endif + #endif + + float NL = clamp(dot(worldNormal, worldLight), 0.0, 1.0); + float NE = clamp(dot(worldNormal, SampleToView), 0.0, 1.0); + + float fzero = u_MaterialInfo.x; + float shininess = u_MaterialInfo.y; + + #if defined(USE_SPECULARMAP) + vec4 specular = texture2D(u_SpecularMap, tex); + //specular.rgb = clamp(specular.rgb - diffuse.rgb, 0.0, 1.0); + shininess *= specular.a; + #endif + + float directedDiff = NL * CalcDiffuse(worldNormal, worldLight, SampleToView, NE, NL, fzero, shininess); + diffuse.rgb *= directedLight * directedDiff + ambientDiff * ambientLight; + + #if defined(USE_SPECULARMAP) + vec3 halfAngle = normalize(worldLight + SampleToView); + + float EH = clamp(dot(SampleToView, halfAngle), 0.0, 1.0); + float NH = clamp(dot(worldNormal, halfAngle), 0.0, 1.0); + + float directedSpec = NL * CalcSpecular(NH, NL, NE, EH, fzero, shininess); + + #if defined(r_normalAmbient) + vec3 ambientHalf = normalize(surfNormal + SampleToView); + float ambientSpec = max(dot(ambientHalf, worldNormal) + 0.5, 0.0); + ambientSpec *= ambientSpec * 0.44; + ambientSpec = pow(ambientSpec, shininess) * fzero; + specular.rgb *= directedSpec * directedLight + ambientSpec * ambientLight; + #else + specular.rgb *= directedSpec * directedLight; + #endif + #endif +#endif + + gl_FragColor = diffuse; + +#if defined(USE_SPECULARMAP) && defined(USE_LIGHT) && !defined(USE_FAST_LIGHT) + gl_FragColor.rgb += specular.rgb; +#endif + + gl_FragColor *= var_Color; +} diff --git a/src/renderergl2/glsl/lightall_vp.glsl b/src/renderergl2/glsl/lightall_vp.glsl new file mode 100644 index 00000000..05a41f4d --- /dev/null +++ b/src/renderergl2/glsl/lightall_vp.glsl @@ -0,0 +1,219 @@ +attribute vec4 attr_TexCoord0; +#if defined(USE_LIGHTMAP) +attribute vec4 attr_TexCoord1; +#endif +attribute vec4 attr_Color; + +attribute vec4 attr_Position; +attribute vec3 attr_Normal; + +#if defined(USE_VERT_TANGENT_SPACE) +attribute vec3 attr_Tangent; +attribute vec3 attr_Bitangent; +#endif + +#if defined(USE_VERTEX_ANIMATION) +attribute vec4 attr_Position2; +attribute vec3 attr_Normal2; + #if defined(USE_VERT_TANGENT_SPACE) +attribute vec3 attr_Tangent2; +attribute vec3 attr_Bitangent2; + #endif +#endif + +#if defined(USE_LIGHT) && !defined(USE_LIGHT_VECTOR) +attribute vec3 attr_LightDirection; +#endif + +#if defined(USE_TCGEN) || defined(USE_NORMALMAP) || defined(USE_LIGHT) && !defined(USE_FAST_LIGHT) +uniform vec3 u_ViewOrigin; +#endif + +#if defined(USE_TCGEN) +uniform int u_TCGen0; +#endif + +#if defined(USE_TCMOD) +uniform vec4 u_DiffuseTexMatrix; +uniform vec4 u_DiffuseTexOffTurb; +#endif + +uniform mat4 u_ModelViewProjectionMatrix; +uniform vec4 u_BaseColor; +uniform vec4 u_VertColor; + +#if defined(USE_MODELMATRIX) +uniform mat4 u_ModelMatrix; +#endif + +#if defined(USE_VERTEX_ANIMATION) +uniform float u_VertexLerp; +#endif + +#if defined(USE_LIGHT_VECTOR) +uniform vec4 u_LightOrigin; + #if defined(USE_FAST_LIGHT) +uniform vec3 u_DirectedLight; +uniform vec3 u_AmbientLight; +uniform float u_LightRadius; + #endif +#endif + +varying vec2 var_DiffuseTex; + +#if defined(USE_LIGHTMAP) +varying vec2 var_LightTex; +#endif + +#if defined(USE_TCGEN) || defined(USE_NORMALMAP) || (defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)) +varying vec3 var_SampleToView; +#endif + +varying vec4 var_Color; + +#if defined(USE_NORMALMAP) && !defined(USE_VERT_TANGENT_SPACE) +varying vec3 var_Position; +#endif + + +#if !defined(USE_FAST_LIGHT) +varying vec3 var_Normal; + #if defined(USE_VERT_TANGENT_SPACE) +varying vec3 var_Tangent; +varying vec3 var_Bitangent; + #endif +#endif + +#if defined(USE_LIGHT_VERTEX) && !defined(USE_FAST_LIGHT) +varying vec3 var_VertLight; +#endif + +#if defined(USE_LIGHT) && !defined(USE_DELUXEMAP) && !defined(USE_FAST_LIGHT) +varying vec3 var_WorldLight; +#endif + +#if defined(USE_TCMOD) +vec2 ModTexCoords(vec2 st, vec3 position, vec4 texMatrix, vec4 offTurb) +{ + float amplitude = offTurb.z; + float phase = offTurb.w; + vec2 st2 = vec2(dot(st, texMatrix.xz), dot(st, texMatrix.yw)) + offTurb.xy; + + vec3 offsetPos = position / 1024.0; + offsetPos.x += offsetPos.z; + + vec2 texOffset = sin((offsetPos.xy + vec2(phase)) * 2.0 * M_PI); + + return st2 + texOffset * amplitude; +} +#endif + + +void main() +{ +#if defined(USE_VERTEX_ANIMATION) + vec4 position = mix(attr_Position, attr_Position2, u_VertexLerp); + vec3 normal = normalize(mix(attr_Normal, attr_Normal2, u_VertexLerp)); + #if defined(USE_VERT_TANGENT_SPACE) + vec3 tangent = normalize(mix(attr_Tangent, attr_Tangent2, u_VertexLerp)); + vec3 bitangent = normalize(mix(attr_Bitangent, attr_Bitangent2, u_VertexLerp)); + #endif +#else + vec4 position = attr_Position; + vec3 normal = attr_Normal; + #if defined(USE_VERT_TANGENT_SPACE) + vec3 tangent = attr_Tangent; + vec3 bitangent = attr_Bitangent; + #endif +#endif + + gl_Position = u_ModelViewProjectionMatrix * position; + +#if (defined(USE_LIGHTMAP) || defined(USE_LIGHT_VERTEX)) && !defined(USE_DELUXEMAP) && !defined(USE_FAST_LIGHT) + vec3 worldLight = attr_LightDirection; +#endif + +#if defined(USE_MODELMATRIX) + position = u_ModelMatrix * position; + normal = (u_ModelMatrix * vec4(normal, 0.0)).xyz; + #if defined(USE_VERT_TANGENT_SPACE) + tangent = (u_ModelMatrix * vec4(tangent, 0.0)).xyz; + bitangent = (u_ModelMatrix * vec4(bitangent, 0.0)).xyz; + #endif + + #if defined(USE_LIGHTMAP) && !defined(USE_DELUXEMAP) && !defined(USE_FAST_LIGHT) + worldLight = (u_ModelMatrix * vec4(worldLight, 0.0)).xyz; + #endif +#endif + +#if defined(USE_NORMALMAP) && !defined(USE_VERT_TANGENT_SPACE) + var_Position = position.xyz; +#endif + +#if defined(USE_TCGEN) || defined(USE_NORMALMAP) || defined(USE_LIGHT) && !defined(USE_FAST_LIGHT) + vec3 SampleToView = u_ViewOrigin - position.xyz; +#endif + +#if defined(USE_TCGEN) || defined(USE_NORMALMAP) || (defined(USE_LIGHT) && !defined(USE_FAST_LIGHT)) + var_SampleToView = SampleToView; +#endif + + vec2 tex; + +#if defined(USE_TCGEN) + if (u_TCGen0 == TCGEN_ENVIRONMENT_MAPPED) + { + tex = -reflect(normalize(SampleToView), normal).yz * vec2(0.5, -0.5) + 0.5; + } + else +#endif + { + tex = attr_TexCoord0.st; + } + +#if defined(USE_TCMOD) + var_DiffuseTex = ModTexCoords(tex, position.xyz, u_DiffuseTexMatrix, u_DiffuseTexOffTurb); +#else + var_DiffuseTex = tex; +#endif + +#if defined(USE_LIGHTMAP) + var_LightTex = attr_TexCoord1.st; +#endif + +#if !defined(USE_FAST_LIGHT) + var_Normal = normal; + #if defined(USE_VERT_TANGENT_SPACE) + var_Tangent = tangent; + var_Bitangent = bitangent; + #endif +#endif + +#if defined(USE_LIGHT) && !defined(USE_DELUXEMAP) + #if defined(USE_LIGHT_VECTOR) + vec3 worldLight = u_LightOrigin.xyz - (position.xyz * u_LightOrigin.w); + #endif + #if !defined(USE_FAST_LIGHT) + var_WorldLight = worldLight; + #endif +#endif + +#if defined(USE_LIGHT_VERTEX) && !defined(USE_FAST_LIGHT) + var_VertLight = u_VertColor.rgb * attr_Color.rgb; + var_Color.rgb = vec3(1.0); + var_Color.a = u_VertColor.a * attr_Color.a + u_BaseColor.a; +#else + var_Color = u_VertColor * attr_Color + u_BaseColor; +#endif + +#if defined(USE_LIGHT_VECTOR) && defined(USE_FAST_LIGHT) + #if defined(USE_INVSQRLIGHT) + float intensity = 1.0 / dot(worldLight, worldLight); + #else + float intensity = clamp((1.0 - dot(worldLight, worldLight) / (u_LightRadius * u_LightRadius)) * 1.07, 0.0, 1.0); + #endif + float NL = clamp(dot(normal, normalize(worldLight)), 0.0, 1.0); + + var_Color.rgb *= u_DirectedLight * intensity * NL + u_AmbientLight; +#endif +} diff --git a/src/renderergl2/glsl/pshadow_fp.glsl b/src/renderergl2/glsl/pshadow_fp.glsl new file mode 100644 index 00000000..b152971a --- /dev/null +++ b/src/renderergl2/glsl/pshadow_fp.glsl @@ -0,0 +1,98 @@ +uniform sampler2D u_ShadowMap; + +uniform vec3 u_LightForward; +uniform vec3 u_LightUp; +uniform vec3 u_LightRight; +uniform vec4 u_LightOrigin; +uniform float u_LightRadius; +varying vec3 var_Position; +varying vec3 var_Normal; + +float sampleDistMap(sampler2D texMap, vec2 uv, float scale) +{ + vec3 distv = texture2D(texMap, uv).xyz; + return dot(distv, vec3(1.0 / (256.0 * 256.0), 1.0 / 256.0, 1.0)) * scale; +} + +void main() +{ + vec3 lightToPos = var_Position - u_LightOrigin.xyz; + vec2 st = vec2(-dot(u_LightRight, lightToPos), dot(u_LightUp, lightToPos)); + + float fade = length(st); + +#if defined(USE_DISCARD) + if (fade >= 1.0) + { + discard; + } +#endif + + fade = clamp(8.0 - fade * 8.0, 0.0, 1.0); + + st = st * 0.5 + vec2(0.5); + +#if defined(USE_SOLID_PSHADOWS) + float intensity = max(sign(u_LightRadius - length(lightToPos)), 0.0); +#else + float intensity = clamp((1.0 - dot(lightToPos, lightToPos) / (u_LightRadius * u_LightRadius)) * 2.0, 0.0, 1.0); +#endif + + float lightDist = length(lightToPos); + float dist; + +#if defined(USE_DISCARD) + if (dot(u_LightForward, lightToPos) <= 0.0) + { + discard; + } + + if (dot(var_Normal, lightToPos) > 0.0) + { + discard; + } +#else + intensity *= max(sign(dot(u_LightForward, lightToPos)), 0.0); + intensity *= max(sign(-dot(var_Normal, lightToPos)), 0.0); +#endif + + intensity *= fade; +#if defined(USE_PCF) + float part; + + dist = sampleDistMap(u_ShadowMap, st + vec2(-1.0/512.0, -1.0/512.0), u_LightRadius); + part = max(sign(lightDist - dist), 0.0); + + dist = sampleDistMap(u_ShadowMap, st + vec2( 1.0/512.0, -1.0/512.0), u_LightRadius); + part += max(sign(lightDist - dist), 0.0); + + dist = sampleDistMap(u_ShadowMap, st + vec2(-1.0/512.0, 1.0/512.0), u_LightRadius); + part += max(sign(lightDist - dist), 0.0); + + dist = sampleDistMap(u_ShadowMap, st + vec2( 1.0/512.0, 1.0/512.0), u_LightRadius); + part += max(sign(lightDist - dist), 0.0); + + #if defined(USE_DISCARD) + if (part <= 0.0) + { + discard; + } + #endif + + intensity *= part * 0.25; +#else + dist = sampleDistMap(u_ShadowMap, st, u_LightRadius); + + #if defined(USE_DISCARD) + if (lightDist - dist <= 0.0) + { + discard; + } + #endif + + intensity *= max(sign(lightDist - dist), 0.0); +#endif + + gl_FragColor.rgb = vec3(0); + gl_FragColor.a = clamp(intensity, 0.0, 0.75); +} diff --git a/src/renderergl2/glsl/pshadow_vp.glsl b/src/renderergl2/glsl/pshadow_vp.glsl new file mode 100644 index 00000000..0e0e3b3d --- /dev/null +++ b/src/renderergl2/glsl/pshadow_vp.glsl @@ -0,0 +1,17 @@ +attribute vec4 attr_Position; +attribute vec3 attr_Normal; + +uniform mat4 u_ModelViewProjectionMatrix; +varying vec3 var_Position; +varying vec3 var_Normal; + + +void main() +{ + vec4 position = attr_Position; + + gl_Position = u_ModelViewProjectionMatrix * position; + + var_Position = position.xyz; + var_Normal = attr_Normal; +} diff --git a/src/renderergl2/glsl/shadowfill_fp.glsl b/src/renderergl2/glsl/shadowfill_fp.glsl new file mode 100644 index 00000000..150f3d12 --- /dev/null +++ b/src/renderergl2/glsl/shadowfill_fp.glsl @@ -0,0 +1,41 @@ +uniform vec4 u_LightOrigin; +uniform float u_LightRadius; + +varying vec3 var_Position; + +void main() +{ +#if defined(USE_DEPTH) + float depth = length(u_LightOrigin.xyz - var_Position) / u_LightRadius; + #if 0 + // 32 bit precision + const vec4 bitSh = vec4( 256 * 256 * 256, 256 * 256, 256, 1); + const vec4 bitMsk = vec4( 0, 1.0 / 256.0, 1.0 / 256.0, 1.0 / 256.0); + + vec4 comp; + comp = depth * bitSh; + comp.xyz = fract(comp.xyz); + comp -= comp.xxyz * bitMsk; + gl_FragColor = comp; + #endif + + #if 1 + // 24 bit precision + const vec3 bitSh = vec3( 256 * 256, 256, 1); + const vec3 bitMsk = vec3( 0, 1.0 / 256.0, 1.0 / 256.0); + + vec3 comp; + comp = depth * bitSh; + comp.xy = fract(comp.xy); + comp -= comp.xxy * bitMsk; + gl_FragColor = vec4(comp, 1.0); + #endif + + #if 0 + // 8 bit precision + gl_FragColor = vec4(depth, depth, depth, 1); + #endif +#else + gl_FragColor = vec4(0, 0, 0, 1); +#endif +} diff --git a/src/renderergl2/glsl/shadowfill_vp.glsl b/src/renderergl2/glsl/shadowfill_vp.glsl new file mode 100644 index 00000000..10802eca --- /dev/null +++ b/src/renderergl2/glsl/shadowfill_vp.glsl @@ -0,0 +1,89 @@ +attribute vec4 attr_Position; +attribute vec3 attr_Normal; +attribute vec4 attr_TexCoord0; + +//#if defined(USE_VERTEX_ANIMATION) +attribute vec4 attr_Position2; +attribute vec3 attr_Normal2; +//#endif + +//#if defined(USE_DEFORM_VERTEXES) +uniform int u_DeformGen; +uniform float u_DeformParams[5]; +//#endif + +uniform float u_Time; +uniform mat4 u_ModelViewProjectionMatrix; + +uniform mat4 u_ModelMatrix; + +//#if defined(USE_VERTEX_ANIMATION) +uniform float u_VertexLerp; +//#endif + +varying vec3 var_Position; + +vec3 DeformPosition(const vec3 pos, const vec3 normal, const vec2 st) +{ + if (u_DeformGen == 0) + { + return pos; + } + + float base = u_DeformParams[0]; + float amplitude = u_DeformParams[1]; + float phase = u_DeformParams[2]; + float frequency = u_DeformParams[3]; + float spread = u_DeformParams[4]; + + if (u_DeformGen == DGEN_BULGE) + { + phase *= M_PI * 0.25 * st.x; + } + else // if (u_DeformGen <= DGEN_WAVE_INVERSE_SAWTOOTH) + { + phase += dot(pos.xyz, vec3(spread)); + } + + float value = phase + (u_Time * frequency); + float func; + + if (u_DeformGen == DGEN_WAVE_SIN) + { + func = sin(value * 2.0 * M_PI); + } + else if (u_DeformGen == DGEN_WAVE_SQUARE) + { + func = sign(sin(value * 2.0 * M_PI)); + } + else if (u_DeformGen == DGEN_WAVE_TRIANGLE) + { + func = abs(fract(value + 0.75) - 0.5) * 4.0 - 1.0; + } + else if (u_DeformGen == DGEN_WAVE_SAWTOOTH) + { + func = fract(value); + } + else if (u_DeformGen == DGEN_WAVE_INVERSE_SAWTOOTH) + { + func = (1.0 - fract(value)); + } + else if (u_DeformGen == DGEN_BULGE) + { + func = sin(value); + } + + return pos + normal * (base + func * amplitude); +} + +void main() +{ + vec4 position = mix(attr_Position, attr_Position2, u_VertexLerp); + vec3 normal = normalize(mix(attr_Normal, attr_Normal2, u_VertexLerp)); + + position.xyz = DeformPosition(position.xyz, normal, attr_TexCoord0.st); + + gl_Position = u_ModelViewProjectionMatrix * position; + + var_Position = (u_ModelMatrix * position).xyz; +} diff --git a/src/renderergl2/glsl/shadowmask_fp.glsl b/src/renderergl2/glsl/shadowmask_fp.glsl new file mode 100644 index 00000000..b3a698c8 --- /dev/null +++ b/src/renderergl2/glsl/shadowmask_fp.glsl @@ -0,0 +1,127 @@ +uniform sampler2D u_ScreenDepthMap; + +uniform sampler2D u_ShadowMap; +#if defined(USE_SHADOW_CASCADE) +uniform sampler2D u_ShadowMap2; +uniform sampler2D u_ShadowMap3; +#endif + +uniform mat4 u_ShadowMvp; +#if defined(USE_SHADOW_CASCADE) +uniform mat4 u_ShadowMvp2; +uniform mat4 u_ShadowMvp3; +#endif + +uniform vec3 u_ViewOrigin; +uniform vec4 u_ViewInfo; // zfar / znear, zfar + +varying vec2 var_DepthTex; +varying vec3 var_ViewDir; + +// Input: It uses texture coords as the random number seed. +// Output: Random number: [0,1), that is between 0.0 and 0.999999... inclusive. +// Author: Michael Pohoreski +// Copyright: Copyleft 2012 :-) +// Source: http://stackoverflow.com/questions/5149544/can-i-generate-a-random-number-inside-a-pixel-shader + +float random( const vec2 p ) +{ + // We need irrationals for pseudo randomness. + // Most (all?) known transcendental numbers will (generally) work. + const vec2 r = vec2( + 23.1406926327792690, // e^pi (Gelfond's constant) + 2.6651441426902251); // 2^sqrt(2) (Gelfond-Schneider constant) + //return fract( cos( mod( 123456789., 1e-7 + 256. * dot(p,r) ) ) ); + return mod( 123456789., 1e-7 + 256. * dot(p,r) ); +} + +float PCF(const sampler2D shadowmap, const vec2 st, const float dist) +{ + float mult; + float scale = 2.0 / r_shadowMapSize; + +#if defined(USE_SHADOW_FILTER) + float r = random(var_DepthTex.xy); + float sinr = sin(r) * scale; + float cosr = cos(r) * scale; + mat2 rmat = mat2(cosr, sinr, -sinr, cosr); + + mult = step(dist, texture2D(shadowmap, st + rmat * vec2(-0.7055767, 0.196515)).r); + mult += step(dist, texture2D(shadowmap, st + rmat * vec2(0.3524343, -0.7791386)).r); + mult += step(dist, texture2D(shadowmap, st + rmat * vec2(0.2391056, 0.9189604)).r); + #if defined(USE_SHADOW_FILTER2) + mult += step(dist, texture2D(shadowmap, st + rmat * vec2(-0.07580382, -0.09224417)).r); + mult += step(dist, texture2D(shadowmap, st + rmat * vec2(0.5784913, -0.002528916)).r); + mult += step(dist, texture2D(shadowmap, st + rmat * vec2(0.192888, 0.4064181)).r); + mult += step(dist, texture2D(shadowmap, st + rmat * vec2(-0.6335801, -0.5247476)).r); + mult += step(dist, texture2D(shadowmap, st + rmat * vec2(-0.5579782, 0.7491854)).r); + mult += step(dist, texture2D(shadowmap, st + rmat * vec2(0.7320465, 0.6317794)).r); + + mult *= 0.11111; + #else + mult *= 0.33333; + #endif +#else + mult = step(dist, texture2D(shadowmap, st).r); +#endif + + return mult; +} + +float getLinearDepth(sampler2D depthMap, vec2 tex, float zFarDivZNear) +{ + float sampleZDivW = texture2D(depthMap, tex).r; + return 1.0 / mix(zFarDivZNear, 1.0, sampleZDivW); +} + +void main() +{ + float result; + + float depth = getLinearDepth(u_ScreenDepthMap, var_DepthTex, u_ViewInfo.x); + float sampleZ = u_ViewInfo.y * depth; + + vec4 biasPos = vec4(u_ViewOrigin + var_ViewDir * depth * 0.99, 1.0); + + vec4 shadowpos = u_ShadowMvp * biasPos; + +#if defined(USE_SHADOW_CASCADE) + const float fadeTo = 0.5; + result = fadeTo; +#else + result = 0.0; +#endif + + if (all(lessThanEqual(abs(shadowpos.xyz), vec3(abs(shadowpos.w))))) + { + shadowpos.xyz = shadowpos.xyz / shadowpos.w * 0.5 + 0.5; + result = PCF(u_ShadowMap, shadowpos.xy, shadowpos.z); + } +#if defined(USE_SHADOW_CASCADE) + else + { + shadowpos = u_ShadowMvp2 * biasPos; + + if (all(lessThanEqual(abs(shadowpos.xyz), vec3(abs(shadowpos.w))))) + { + shadowpos.xyz = shadowpos.xyz / shadowpos.w * 0.5 + 0.5; + result = PCF(u_ShadowMap2, shadowpos.xy, shadowpos.z); + } + else + { + shadowpos = u_ShadowMvp3 * biasPos; + + if (all(lessThanEqual(abs(shadowpos.xyz), vec3(abs(shadowpos.w))))) + { + shadowpos.xyz = shadowpos.xyz / shadowpos.w * 0.5 + 0.5; + result = PCF(u_ShadowMap3, shadowpos.xy, shadowpos.z); + + float fade = clamp(sampleZ / r_shadowCascadeZFar * 10.0 - 9.0, 0.0, 1.0); + result = mix(result, fadeTo, fade); + } + } + } +#endif + + gl_FragColor = vec4(vec3(result), 1.0); +} diff --git a/src/renderergl2/glsl/shadowmask_vp.glsl b/src/renderergl2/glsl/shadowmask_vp.glsl new file mode 100644 index 00000000..13166a24 --- /dev/null +++ b/src/renderergl2/glsl/shadowmask_vp.glsl @@ -0,0 +1,18 @@ +attribute vec4 attr_Position; +attribute vec4 attr_TexCoord0; + +uniform vec3 u_ViewForward; +uniform vec3 u_ViewLeft; +uniform vec3 u_ViewUp; +uniform vec4 u_ViewInfo; // zfar / znear + +varying vec2 var_DepthTex; +varying vec3 var_ViewDir; + +void main() +{ + gl_Position = attr_Position; + vec2 screenCoords = gl_Position.xy / gl_Position.w; + var_DepthTex = attr_TexCoord0.xy; + var_ViewDir = u_ViewForward + u_ViewLeft * -screenCoords.x + u_ViewUp * screenCoords.y; +} diff --git a/src/renderergl2/glsl/ssao_fp.glsl b/src/renderergl2/glsl/ssao_fp.glsl new file mode 100644 index 00000000..6263284c --- /dev/null +++ b/src/renderergl2/glsl/ssao_fp.glsl @@ -0,0 +1,86 @@ +uniform sampler2D u_ScreenDepthMap; + +uniform vec4 u_ViewInfo; // zfar / znear, zfar + +varying vec2 var_ScreenTex; + +vec2 poissonDisc[9] = vec2[9]( +vec2(-0.7055767, 0.196515), vec2(0.3524343, -0.7791386), +vec2(0.2391056, 0.9189604), vec2(-0.07580382, -0.09224417), +vec2(0.5784913, -0.002528916), vec2(0.192888, 0.4064181), +vec2(-0.6335801, -0.5247476), vec2(-0.5579782, 0.7491854), +vec2(0.7320465, 0.6317794) +); + +// Input: It uses texture coords as the random number seed. +// Output: Random number: [0,1), that is between 0.0 and 0.999999... inclusive. +// Author: Michael Pohoreski +// Copyright: Copyleft 2012 :-) +// Source: http://stackoverflow.com/questions/5149544/can-i-generate-a-random-number-inside-a-pixel-shader + +float random( const vec2 p ) +{ + // We need irrationals for pseudo randomness. + // Most (all?) known transcendental numbers will (generally) work. + const vec2 r = vec2( + 23.1406926327792690, // e^pi (Gelfond's constant) + 2.6651441426902251); // 2^sqrt(2) (Gelfond-Schneider constant) + //return fract( cos( mod( 123456789., 1e-7 + 256. * dot(p,r) ) ) ); + return mod( 123456789., 1e-7 + 256. * dot(p,r) ); +} + +mat2 randomRotation( const vec2 p ) +{ + float r = random(p); + float sinr = sin(r); + float cosr = cos(r); + return mat2(cosr, sinr, -sinr, cosr); +} + +float getLinearDepth(sampler2D depthMap, const vec2 tex, const float zFarDivZNear) +{ + float sampleZDivW = texture2D(depthMap, tex).r; + return 1.0 / mix(zFarDivZNear, 1.0, sampleZDivW); +} + +float ambientOcclusion(sampler2D depthMap, const vec2 tex, const float zFarDivZNear, const float zFar) +{ + float result = 0; + + float sampleZ = zFar * getLinearDepth(depthMap, tex, zFarDivZNear); + + vec2 expectedSlope = vec2(dFdx(sampleZ), dFdy(sampleZ)) / vec2(dFdx(tex.x), dFdy(tex.y)); + + if (length(expectedSlope) > 5000.0) + return 1.0; + + vec2 offsetScale = vec2(3.0 / sampleZ); + + mat2 rmat = randomRotation(tex); + + int i; + for (i = 0; i < 3; i++) + { + vec2 offset = rmat * poissonDisc[i] * offsetScale; + float sampleZ2 = zFar * getLinearDepth(depthMap, tex + offset, zFarDivZNear); + + if (abs(sampleZ - sampleZ2) > 20.0) + result += 1.0; + else + { + float expectedZ = sampleZ + dot(expectedSlope, offset); + result += step(expectedZ - 1.0, sampleZ2); + } + } + + result *= 0.33333; + + return result; +} + +void main() +{ + float result = ambientOcclusion(u_ScreenDepthMap, var_ScreenTex, u_ViewInfo.x, u_ViewInfo.y); + + gl_FragColor = vec4(vec3(result), 1.0); +} diff --git a/src/renderergl2/glsl/ssao_vp.glsl b/src/renderergl2/glsl/ssao_vp.glsl new file mode 100644 index 00000000..9c46a79f --- /dev/null +++ b/src/renderergl2/glsl/ssao_vp.glsl @@ -0,0 +1,12 @@ +attribute vec4 attr_Position; +attribute vec4 attr_TexCoord0; + +varying vec2 var_ScreenTex; + +void main() +{ + gl_Position = attr_Position; + var_ScreenTex = attr_TexCoord0.xy; + //vec2 screenCoords = gl_Position.xy / gl_Position.w; + //var_ScreenTex = screenCoords * 0.5 + 0.5; +} diff --git a/src/renderergl2/glsl/texturecolor_fp.glsl b/src/renderergl2/glsl/texturecolor_fp.glsl new file mode 100644 index 00000000..5646b511 --- /dev/null +++ b/src/renderergl2/glsl/texturecolor_fp.glsl @@ -0,0 +1,12 @@ +#version 120 + +uniform sampler2D u_DiffuseMap; +uniform vec4 u_Color; + +varying vec2 var_Tex1; + + +void main() +{ + gl_FragColor = texture2D(u_DiffuseMap, var_Tex1) * u_Color; +} diff --git a/src/renderergl2/glsl/texturecolor_vp.glsl b/src/renderergl2/glsl/texturecolor_vp.glsl new file mode 100644 index 00000000..ae26a18e --- /dev/null +++ b/src/renderergl2/glsl/texturecolor_vp.glsl @@ -0,0 +1,15 @@ +#version 120 + +attribute vec4 attr_Position; +attribute vec4 attr_TexCoord0; + +uniform mat4 u_ModelViewProjectionMatrix; + +varying vec2 var_Tex1; + + +void main() +{ + gl_Position = u_ModelViewProjectionMatrix * attr_Position; + var_Tex1 = attr_TexCoord0.st; +} diff --git a/src/renderergl2/glsl/tonemap_fp.glsl b/src/renderergl2/glsl/tonemap_fp.glsl new file mode 100644 index 00000000..9b18de8a --- /dev/null +++ b/src/renderergl2/glsl/tonemap_fp.glsl @@ -0,0 +1,48 @@ +uniform sampler2D u_TextureMap; +uniform sampler2D u_LevelsMap; + +uniform vec4 u_Color; + +uniform vec2 u_AutoExposureMinMax; +uniform vec3 u_ToneMinAvgMaxLinear; + +varying vec2 var_TexCoords; + +const vec3 LUMINANCE_VECTOR = vec3(0.2125, 0.7154, 0.0721); //vec3(0.299, 0.587, 0.114); + +vec3 FilmicTonemap(vec3 x) +{ + const float SS = 0.22; // Shoulder Strength + const float LS = 0.30; // Linear Strength + const float LA = 0.10; // Linear Angle + const float TS = 0.20; // Toe Strength + const float TAN = 0.01; // Toe Angle Numerator + const float TAD = 0.30; // Toe Angle Denominator + + vec3 SSxx = SS * x * x; + vec3 LSx = LS * x; + vec3 LALSx = LSx * LA; + + return ((SSxx + LALSx + TS * TAN) / (SSxx + LSx + TS * TAD)) - TAN / TAD; + + //return ((x*(SS*x+LA*LS)+TS*TAN)/(x*(SS*x+LS)+TS*TAD)) - TAN/TAD; + +} + +void main() +{ + vec4 color = texture2D(u_TextureMap, var_TexCoords) * u_Color; + vec3 minAvgMax = texture2D(u_LevelsMap, var_TexCoords).rgb; + vec3 logMinAvgMaxLum = clamp(minAvgMax * 20.0 - 10.0, -u_AutoExposureMinMax.y, -u_AutoExposureMinMax.x); + + float avgLum = exp2(logMinAvgMaxLum.y); + //float maxLum = exp2(logMinAvgMaxLum.z); + + color.rgb *= u_ToneMinAvgMaxLinear.y / avgLum; + color.rgb = max(vec3(0.0), color.rgb - vec3(u_ToneMinAvgMaxLinear.x)); + + vec3 fWhite = 1.0 / FilmicTonemap(vec3(u_ToneMinAvgMaxLinear.z - u_ToneMinAvgMaxLinear.x)); + color.rgb = FilmicTonemap(color.rgb) * fWhite; + + gl_FragColor = clamp(color, 0.0, 1.0); +} diff --git a/src/renderergl2/glsl/tonemap_vp.glsl b/src/renderergl2/glsl/tonemap_vp.glsl new file mode 100644 index 00000000..5ca41600 --- /dev/null +++ b/src/renderergl2/glsl/tonemap_vp.glsl @@ -0,0 +1,13 @@ +attribute vec4 attr_Position; +attribute vec4 attr_TexCoord0; + +uniform mat4 u_ModelViewProjectionMatrix; + +varying vec2 var_TexCoords; + + +void main() +{ + gl_Position = u_ModelViewProjectionMatrix * attr_Position; + var_TexCoords = attr_TexCoord0.st; +} diff --git a/src/renderergl2/qgl.h b/src/renderergl2/qgl.h new file mode 100644 index 00000000..6013a87c --- /dev/null +++ b/src/renderergl2/qgl.h @@ -0,0 +1,755 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +/* +** QGL.H +*/ + +#ifndef __QGL_H__ +#define __QGL_H__ + +#ifdef USE_LOCAL_HEADERS +# include "SDL_opengl.h" +#else +# include +#endif + +extern void (APIENTRYP qglActiveTextureARB) (GLenum texture); +extern void (APIENTRYP qglClientActiveTextureARB) (GLenum texture); +extern void (APIENTRYP qglMultiTexCoord2fARB) (GLenum target, GLfloat s, GLfloat t); + +extern void (APIENTRYP qglLockArraysEXT) (GLint first, GLsizei count); +extern void (APIENTRYP qglUnlockArraysEXT) (void); + +// GL_EXT_draw_range_elements +extern void (APIENTRY * qglDrawRangeElementsEXT) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); + +// GL_EXT_multi_draw_arrays +extern void (APIENTRY * qglMultiDrawArraysEXT) (GLenum, GLint *, GLsizei *, GLsizei); +extern void (APIENTRY * qglMultiDrawElementsEXT) (GLenum, const GLsizei *, GLenum, const GLvoid **, GLsizei); + +// GL_ARB_shading_language_100 +#ifndef GL_ARB_shading_language_100 +#define GL_ARB_shading_language_100 +#define GL_SHADING_LANGUAGE_VERSION_ARB 0x8B8C +#endif + +// GL_ARB_vertex_program +extern void (APIENTRY * qglVertexAttrib4fARB) (GLuint, GLfloat, GLfloat, GLfloat, GLfloat); +extern void (APIENTRY * qglVertexAttrib4fvARB) (GLuint, const GLfloat *); +extern void (APIENTRY * qglVertexAttribPointerARB) (GLuint index, GLint size, GLenum type, GLboolean normalized, + GLsizei stride, const GLvoid * pointer); +extern void (APIENTRY * qglEnableVertexAttribArrayARB) (GLuint index); +extern void (APIENTRY * qglDisableVertexAttribArrayARB) (GLuint index); + +// GL_ARB_vertex_buffer_object +extern void (APIENTRY * qglBindBufferARB) (GLenum target, GLuint buffer); +extern void (APIENTRY * qglDeleteBuffersARB) (GLsizei n, const GLuint * buffers); +extern void (APIENTRY * qglGenBuffersARB) (GLsizei n, GLuint * buffers); +extern GLboolean(APIENTRY * qglIsBufferARB) (GLuint buffer); +extern void (APIENTRY * qglBufferDataARB) (GLenum target, GLsizeiptrARB size, const GLvoid * data, GLenum usage); +extern void (APIENTRY * qglBufferSubDataARB) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid * data); +extern void (APIENTRY * qglGetBufferSubDataARB) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid * data); +extern void (APIENTRY * qglGetBufferParameterivARB) (GLenum target, GLenum pname, GLint * params); +extern void (APIENTRY * qglGetBufferPointervARB) (GLenum target, GLenum pname, GLvoid * *params); + +// GL_ARB_shader_objects +extern void (APIENTRY * qglDeleteObjectARB) (GLhandleARB obj); +extern GLhandleARB(APIENTRY * qglGetHandleARB) (GLenum pname); +extern void (APIENTRY * qglDetachObjectARB) (GLhandleARB containerObj, GLhandleARB attachedObj); +extern GLhandleARB(APIENTRY * qglCreateShaderObjectARB) (GLenum shaderType); +extern void (APIENTRY * qglShaderSourceARB) (GLhandleARB shaderObj, GLsizei count, const GLcharARB * *string, + const GLint * length); +extern void (APIENTRY * qglCompileShaderARB) (GLhandleARB shaderObj); +extern GLhandleARB(APIENTRY * qglCreateProgramObjectARB) (void); +extern void (APIENTRY * qglAttachObjectARB) (GLhandleARB containerObj, GLhandleARB obj); +extern void (APIENTRY * qglLinkProgramARB) (GLhandleARB programObj); +extern void (APIENTRY * qglUseProgramObjectARB) (GLhandleARB programObj); +extern void (APIENTRY * qglValidateProgramARB) (GLhandleARB programObj); +extern void (APIENTRY * qglUniform1fARB) (GLint location, GLfloat v0); +extern void (APIENTRY * qglUniform2fARB) (GLint location, GLfloat v0, GLfloat v1); +extern void (APIENTRY * qglUniform3fARB) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +extern void (APIENTRY * qglUniform4fARB) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +extern void (APIENTRY * qglUniform1iARB) (GLint location, GLint v0); +extern void (APIENTRY * qglUniform2iARB) (GLint location, GLint v0, GLint v1); +extern void (APIENTRY * qglUniform3iARB) (GLint location, GLint v0, GLint v1, GLint v2); +extern void (APIENTRY * qglUniform4iARB) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +extern void (APIENTRY * qglUniform1fvARB) (GLint location, GLsizei count, const GLfloat * value); +extern void (APIENTRY * qglUniform2fvARB) (GLint location, GLsizei count, const GLfloat * value); +extern void (APIENTRY * qglUniform3fvARB) (GLint location, GLsizei count, const GLfloat * value); +extern void (APIENTRY * qglUniform4fvARB) (GLint location, GLsizei count, const GLfloat * value); +extern void (APIENTRY * qglUniform2ivARB) (GLint location, GLsizei count, const GLint * value); +extern void (APIENTRY * qglUniform3ivARB) (GLint location, GLsizei count, const GLint * value); +extern void (APIENTRY * qglUniform4ivARB) (GLint location, GLsizei count, const GLint * value); +extern void (APIENTRY * qglUniformMatrix2fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +extern void (APIENTRY * qglUniformMatrix3fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +extern void (APIENTRY * qglUniformMatrix4fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +extern void (APIENTRY * qglGetObjectParameterfvARB) (GLhandleARB obj, GLenum pname, GLfloat * params); +extern void (APIENTRY * qglGetObjectParameterivARB) (GLhandleARB obj, GLenum pname, GLint * params); +extern void (APIENTRY * qglGetInfoLogARB) (GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog); +extern void (APIENTRY * qglGetAttachedObjectsARB) (GLhandleARB containerObj, GLsizei maxCount, GLsizei * count, + GLhandleARB * obj); +extern GLint(APIENTRY * qglGetUniformLocationARB) (GLhandleARB programObj, const GLcharARB * name); +extern void (APIENTRY * qglGetActiveUniformARB) (GLhandleARB programObj, GLuint index, GLsizei maxIndex, GLsizei * length, + GLint * size, GLenum * type, GLcharARB * name); +extern void (APIENTRY * qglGetUniformfvARB) (GLhandleARB programObj, GLint location, GLfloat * params); +extern void (APIENTRY * qglGetUniformivARB) (GLhandleARB programObj, GLint location, GLint * params); +extern void (APIENTRY * qglGetShaderSourceARB) (GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * source); + +// GL_ARB_vertex_shader +extern void (APIENTRY * qglBindAttribLocationARB) (GLhandleARB programObj, GLuint index, const GLcharARB * name); +extern void (APIENTRY * qglGetActiveAttribARB) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei * length, + GLint * size, GLenum * type, GLcharARB * name); +extern GLint(APIENTRY * qglGetAttribLocationARB) (GLhandleARB programObj, const GLcharARB * name); + +// GL_ARB_texture_compression +extern void (APIENTRY * qglCompressedTexImage3DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, + GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); +extern void (APIENTRY * qglCompressedTexImage2DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, + GLint border, GLsizei imageSize, const GLvoid *data); +extern void (APIENTRY * qglCompressedTexImage1DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, + GLsizei imageSize, const GLvoid *data); +extern void (APIENTRY * qglCompressedTexSubImage3DARB)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data); +extern void (APIENTRY * qglCompressedTexSubImage2DARB)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, + GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); +extern void (APIENTRY * qglCompressedTexSubImage1DARB)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, + GLsizei imageSize, const GLvoid *data); +extern void (APIENTRY * qglGetCompressedTexImageARB)(GLenum target, GLint lod, + GLvoid *img); + +// GL_NVX_gpu_memory_info +#ifndef GL_NVX_gpu_memory_info +#define GL_NVX_gpu_memory_info +#define GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX 0x9047 +#define GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX 0x9048 +#define GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX 0x9049 +#define GL_GPU_MEMORY_INFO_EVICTION_COUNT_NVX 0x904A +#define GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX 0x904B +#endif + +// GL_ATI_meminfo +#ifndef GL_ATI_meminfo +#define GL_ATI_meminfo +#define GL_VBO_FREE_MEMORY_ATI 0x87FB +#define GL_TEXTURE_FREE_MEMORY_ATI 0x87FC +#define GL_RENDERBUFFER_FREE_MEMORY_ATI 0x87FD +#endif + +// GL_ARB_texture_float +#ifndef GL_ARB_texture_float +#define GL_ARB_texture_float +#define GL_TEXTURE_RED_TYPE_ARB 0x8C10 +#define GL_TEXTURE_GREEN_TYPE_ARB 0x8C11 +#define GL_TEXTURE_BLUE_TYPE_ARB 0x8C12 +#define GL_TEXTURE_ALPHA_TYPE_ARB 0x8C13 +#define GL_TEXTURE_LUMINANCE_TYPE_ARB 0x8C14 +#define GL_TEXTURE_INTENSITY_TYPE_ARB 0x8C15 +#define GL_TEXTURE_DEPTH_TYPE_ARB 0x8C16 +#define GL_UNSIGNED_NORMALIZED_ARB 0x8C17 +#define GL_RGBA32F_ARB 0x8814 +#define GL_RGB32F_ARB 0x8815 +#define GL_ALPHA32F_ARB 0x8816 +#define GL_INTENSITY32F_ARB 0x8817 +#define GL_LUMINANCE32F_ARB 0x8818 +#define GL_LUMINANCE_ALPHA32F_ARB 0x8819 +#define GL_RGBA16F_ARB 0x881A +#define GL_RGB16F_ARB 0x881B +#define GL_ALPHA16F_ARB 0x881C +#define GL_INTENSITY16F_ARB 0x881D +#define GL_LUMINANCE16F_ARB 0x881E +#define GL_LUMINANCE_ALPHA16F_ARB 0x881F +#endif + +#ifndef GL_ARB_half_float_pixel +#define GL_ARB_half_float_pixel +#define GL_HALF_FLOAT_ARB 0x140B +#endif + +// GL_EXT_framebuffer_object +extern GLboolean (APIENTRY * qglIsRenderbufferEXT)(GLuint renderbuffer); +extern void (APIENTRY * qglBindRenderbufferEXT)(GLenum target, GLuint renderbuffer); +extern void (APIENTRY * qglDeleteRenderbuffersEXT)(GLsizei n, const GLuint *renderbuffers); +extern void (APIENTRY * qglGenRenderbuffersEXT)(GLsizei n, GLuint *renderbuffers); +extern void (APIENTRY * qglRenderbufferStorageEXT)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); +extern void (APIENTRY * qglGetRenderbufferParameterivEXT)(GLenum target, GLenum pname, GLint *params); +extern GLboolean (APIENTRY * qglIsFramebufferEXT)(GLuint framebuffer); +extern void (APIENTRY * qglBindFramebufferEXT)(GLenum target, GLuint framebuffer); +extern void (APIENTRY * qglDeleteFramebuffersEXT)(GLsizei n, const GLuint *framebuffers); +extern void (APIENTRY * qglGenFramebuffersEXT)(GLsizei n, GLuint *framebuffers); +extern GLenum (APIENTRY * qglCheckFramebufferStatusEXT)(GLenum target); +extern void (APIENTRY * qglFramebufferTexture1DEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, + GLint level); +extern void (APIENTRY * qglFramebufferTexture2DEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, + GLint level); +extern void (APIENTRY * qglFramebufferTexture3DEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, + GLint level, GLint zoffset); +extern void (APIENTRY * qglFramebufferRenderbufferEXT)(GLenum target, GLenum attachment, GLenum renderbuffertarget, + GLuint renderbuffer); +extern void (APIENTRY * qglGetFramebufferAttachmentParameterivEXT)(GLenum target, GLenum attachment, GLenum pname, GLint *params); +extern void (APIENTRY * qglGenerateMipmapEXT)(GLenum target); + +#ifndef GL_EXT_framebuffer_object +#define GL_EXT_framebuffer_object +#define GL_FRAMEBUFFER_EXT 0x8D40 +#define GL_RENDERBUFFER_EXT 0x8D41 +#define GL_STENCIL_INDEX1_EXT 0x8D46 +#define GL_STENCIL_INDEX4_EXT 0x8D47 +#define GL_STENCIL_INDEX8_EXT 0x8D48 +#define GL_STENCIL_INDEX16_EXT 0x8D49 +#define GL_RENDERBUFFER_WIDTH_EXT 0x8D42 +#define GL_RENDERBUFFER_HEIGHT_EXT 0x8D43 +#define GL_RENDERBUFFER_INTERNAL_FORMAT_EXT 0x8D44 +#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50 +#define GL_RENDERBUFFER_GREEN_SIZE_EXT 0x8D51 +#define GL_RENDERBUFFER_BLUE_SIZE_EXT 0x8D52 +#define GL_RENDERBUFFER_ALPHA_SIZE_EXT 0x8D53 +#define GL_RENDERBUFFER_DEPTH_SIZE_EXT 0x8D54 +#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE_EXT 0x8CD0 +#define GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME_EXT 0x8CD1 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL_EXT 0x8CD2 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE_EXT 0x8CD3 +#define GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_3D_ZOFFSET_EXT 0x8CD4 +#define GL_COLOR_ATTACHMENT0_EXT 0x8CE0 +#define GL_COLOR_ATTACHMENT1_EXT 0x8CE1 +#define GL_COLOR_ATTACHMENT2_EXT 0x8CE2 +#define GL_COLOR_ATTACHMENT3_EXT 0x8CE3 +#define GL_COLOR_ATTACHMENT4_EXT 0x8CE4 +#define GL_COLOR_ATTACHMENT5_EXT 0x8CE5 +#define GL_COLOR_ATTACHMENT6_EXT 0x8CE6 +#define GL_COLOR_ATTACHMENT7_EXT 0x8CE7 +#define GL_COLOR_ATTACHMENT8_EXT 0x8CE8 +#define GL_COLOR_ATTACHMENT9_EXT 0x8CE9 +#define GL_COLOR_ATTACHMENT10_EXT 0x8CEA +#define GL_COLOR_ATTACHMENT11_EXT 0x8CEB +#define GL_COLOR_ATTACHMENT12_EXT 0x8CEC +#define GL_COLOR_ATTACHMENT13_EXT 0x8CED +#define GL_COLOR_ATTACHMENT14_EXT 0x8CEE +#define GL_COLOR_ATTACHMENT15_EXT 0x8CEF +#define GL_DEPTH_ATTACHMENT_EXT 0x8D00 +#define GL_STENCIL_ATTACHMENT_EXT 0x8D20 +#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD +#define GL_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_RENDERBUFFER_BINDING_EXT 0x8CA7 +#define GL_MAX_COLOR_ATTACHMENTS_EXT 0x8CDF +#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8 +#define GL_INVALID_FRAMEBUFFER_OPERATION_EXT 0x0506 +#endif + +// GL_EXT_packed_depth_stencil +#ifndef GL_EXT_packed_depth_stencil +#define GL_EXT_packed_depth_stencil +#define GL_DEPTH_STENCIL_EXT 0x84F9 +#define GL_UNSIGNED_INT_24_8_EXT 0x84FA +#define GL_DEPTH24_STENCIL8_EXT 0x88F0 +#define GL_TEXTURE_STENCIL_SIZE_EXT 0x88F1 +#endif + +// GL_ARB_occlusion_query +extern void (APIENTRY * qglGenQueriesARB)(GLsizei n, GLuint *ids); +extern void (APIENTRY * qglDeleteQueriesARB)(GLsizei n, const GLuint *ids); +extern GLboolean (APIENTRY * qglIsQueryARB)(GLuint id); +extern void (APIENTRY * qglBeginQueryARB)(GLenum target, GLuint id); +extern void (APIENTRY * qglEndQueryARB)(GLenum target); +extern void (APIENTRY * qglGetQueryivARB)(GLenum target, GLenum pname, GLint *params); +extern void (APIENTRY * qglGetQueryObjectivARB)(GLuint id, GLenum pname, GLint *params); +extern void (APIENTRY * qglGetQueryObjectuivARB)(GLuint id, GLenum pname, GLuint *params); + +#ifndef GL_ARB_occlusion_query +#define GL_ARB_occlusion_query +#define GL_SAMPLES_PASSED_ARB 0x8914 +#define GL_QUERY_COUNTER_BITS_ARB 0x8864 +#define GL_CURRENT_QUERY_ARB 0x8865 +#define GL_QUERY_RESULT_ARB 0x8866 +#define GL_QUERY_RESULT_AVAILABLE_ARB 0x8867 +#endif + +// GL_EXT_framebuffer_blit +extern void (APIENTRY * qglBlitFramebufferEXT)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter); + +#ifndef GL_EXT_framebuffer_blit +#define GL_EXT_framebuffer_blit +#define GL_READ_FRAMEBUFFER_EXT 0x8CA8 +#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9 +#define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6 +#define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA +#endif + +// GL_EXT_framebuffer_multisample +extern void (APIENTRY * qglRenderbufferStorageMultisampleEXT)(GLenum target, GLsizei samples, + GLenum internalformat, GLsizei width, GLsizei height); + +#ifndef GL_EXT_framebuffer_multisample +#define GL_EXT_framebuffer_multisample +#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB +#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56 +#define GL_MAX_SAMPLES_EXT 0x8D57 +#endif + +#ifndef GL_EXT_texture_sRGB +#define GL_EXT_texture_sRGB +#define GL_SRGB_EXT 0x8C40 +#define GL_SRGB8_EXT 0x8C41 +#define GL_SRGB_ALPHA_EXT 0x8C42 +#define GL_SRGB8_ALPHA8_EXT 0x8C43 +#define GL_SLUMINANCE_ALPHA_EXT 0x8C44 +#define GL_SLUMINANCE8_ALPHA8_EXT 0x8C45 +#define GL_SLUMINANCE_EXT 0x8C46 +#define GL_SLUMINANCE8_EXT 0x8C47 +#define GL_COMPRESSED_SRGB_EXT 0x8C48 +#define GL_COMPRESSED_SRGB_ALPHA_EXT 0x8C49 +#define GL_COMPRESSED_SLUMINANCE_EXT 0x8C4A +#define GL_COMPRESSED_SLUMINANCE_ALPHA_EXT 0x8C4B +#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT 0x8C4C +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT 0x8C4D +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT 0x8C4E +#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT 0x8C4F +#endif + +#ifndef GL_EXT_framebuffer_sRGB +#define GL_EXT_framebuffer_sRGB +#define GL_FRAMEBUFFER_SRGB_EXT 0x8DB9 +#endif + +#ifndef GL_EXT_texture_compression_latc +#define GL_EXT_texture_compression_latc +#define GL_COMPRESSED_LUMINANCE_LATC1_EXT 0x8C70 +#define GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT 0x8C71 +#define GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72 +#define GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT 0x8C73 +#endif + +#ifndef GL_ARB_texture_compression_bptc +#define GL_ARB_texture_compression_bptc +#define GL_COMPRESSED_RGBA_BPTC_UNORM_ARB 0x8E8C +#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB 0x8E8D +#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB 0x8E8E +#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB 0x8E8F +#endif + +// GL_ARB_draw_buffers +extern void (APIENTRY * qglDrawBuffersARB)(GLsizei n, const GLenum *bufs); +#ifndef GL_ARB_draw_buffers +#define GL_ARB_draw_buffers +#define GL_MAX_DRAW_BUFFERS_ARB 0x8824 +#define GL_DRAW_BUFFER0_ARB 0x8825 +#define GL_DRAW_BUFFER1_ARB 0x8826 +#define GL_DRAW_BUFFER2_ARB 0x8827 +#define GL_DRAW_BUFFER3_ARB 0x8828 +#define GL_DRAW_BUFFER4_ARB 0x8829 +#define GL_DRAW_BUFFER5_ARB 0x882A +#define GL_DRAW_BUFFER6_ARB 0x882B +#define GL_DRAW_BUFFER7_ARB 0x882C +#define GL_DRAW_BUFFER8_ARB 0x882D +#define GL_DRAW_BUFFER9_ARB 0x882E +#define GL_DRAW_BUFFER10_ARB 0x882F +#define GL_DRAW_BUFFER11_ARB 0x8830 +#define GL_DRAW_BUFFER12_ARB 0x8831 +#define GL_DRAW_BUFFER13_ARB 0x8832 +#define GL_DRAW_BUFFER14_ARB 0x8833 +#define GL_DRAW_BUFFER15_ARB 0x8834 +#endif + +#ifndef GL_ARB_depth_clamp +#define GL_ARB_depth_clamp +#define GL_DEPTH_CLAMP 0x864F +#endif + +#if defined(WIN32) +// WGL_ARB_create_context +#ifndef WGL_ARB_create_context +#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093 +#define WGL_CONTEXT_FLAGS_ARB 0x2094 +#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126 +#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001 +#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002 +#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001 +#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002 +#define ERROR_INVALID_VERSION_ARB 0x2095 +#define ERROR_INVALID_PROFILE_ARB 0x2096 +#endif + +extern HGLRC(APIENTRY * qwglCreateContextAttribsARB) (HDC hdC, HGLRC hShareContext, const int *attribList); +#endif + +#if 0 //defined(__linux__) +// GLX_ARB_create_context +#ifndef GLX_ARB_create_context +#define GLX_CONTEXT_DEBUG_BIT_ARB 0x00000001 +#define GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002 +#define GLX_CONTEXT_MAJOR_VERSION_ARB 0x2091 +#define GLX_CONTEXT_MINOR_VERSION_ARB 0x2092 +#define GLX_CONTEXT_FLAGS_ARB 0x2094 +#endif + +extern GLXContext (APIENTRY * qglXCreateContextAttribsARB) (Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list); +#endif + +//=========================================================================== + +#define qglAccum glAccum +#define qglAlphaFunc glAlphaFunc +#define qglAreTexturesResident glAreTexturesResident +#define qglArrayElement glArrayElement +#define qglBegin glBegin +#define qglBindTexture glBindTexture +#define qglBitmap glBitmap +#define qglBlendFunc glBlendFunc +#define qglCallList glCallList +#define qglCallLists glCallLists +#define qglClear glClear +#define qglClearAccum glClearAccum +#define qglClearColor glClearColor +#define qglClearDepth glClearDepth +#define qglClearIndex glClearIndex +#define qglClearStencil glClearStencil +#define qglClipPlane glClipPlane +#define qglColor3b glColor3b +#define qglColor3bv glColor3bv +#define qglColor3d glColor3d +#define qglColor3dv glColor3dv +#define qglColor3f glColor3f +#define qglColor3fv glColor3fv +#define qglColor3i glColor3i +#define qglColor3iv glColor3iv +#define qglColor3s glColor3s +#define qglColor3sv glColor3sv +#define qglColor3ub glColor3ub +#define qglColor3ubv glColor3ubv +#define qglColor3ui glColor3ui +#define qglColor3uiv glColor3uiv +#define qglColor3us glColor3us +#define qglColor3usv glColor3usv +#define qglColor4b glColor4b +#define qglColor4bv glColor4bv +#define qglColor4d glColor4d +#define qglColor4dv glColor4dv +#define qglColor4f glColor4f +#define qglColor4fv glColor4fv +#define qglColor4i glColor4i +#define qglColor4iv glColor4iv +#define qglColor4s glColor4s +#define qglColor4sv glColor4sv +#define qglColor4ub glColor4ub +#define qglColor4ubv glColor4ubv +#define qglColor4ui glColor4ui +#define qglColor4uiv glColor4uiv +#define qglColor4us glColor4us +#define qglColor4usv glColor4usv +#define qglColorMask glColorMask +#define qglColorMaterial glColorMaterial +#define qglColorPointer glColorPointer +#define qglCopyPixels glCopyPixels +#define qglCopyTexImage1D glCopyTexImage1D +#define qglCopyTexImage2D glCopyTexImage2D +#define qglCopyTexSubImage1D glCopyTexSubImage1D +#define qglCopyTexSubImage2D glCopyTexSubImage2D +#define qglCullFace glCullFace +#define qglDeleteLists glDeleteLists +#define qglDeleteTextures glDeleteTextures +#define qglDepthFunc glDepthFunc +#define qglDepthMask glDepthMask +#define qglDepthRange glDepthRange +#define qglDisable glDisable +#define qglDisableClientState glDisableClientState +#define qglDrawArrays glDrawArrays +#define qglDrawBuffer glDrawBuffer +#define qglDrawElements glDrawElements +#define qglDrawPixels glDrawPixels +#define qglEdgeFlag glEdgeFlag +#define qglEdgeFlagPointer glEdgeFlagPointer +#define qglEdgeFlagv glEdgeFlagv +#define qglEnable glEnable +#define qglEnableClientState glEnableClientState +#define qglEnd glEnd +#define qglEndList glEndList +#define qglEvalCoord1d glEvalCoord1d +#define qglEvalCoord1dv glEvalCoord1dv +#define qglEvalCoord1f glEvalCoord1f +#define qglEvalCoord1fv glEvalCoord1fv +#define qglEvalCoord2d glEvalCoord2d +#define qglEvalCoord2dv glEvalCoord2dv +#define qglEvalCoord2f glEvalCoord2f +#define qglEvalCoord2fv glEvalCoord2fv +#define qglEvalMesh1 glEvalMesh1 +#define qglEvalMesh2 glEvalMesh2 +#define qglEvalPoint1 glEvalPoint1 +#define qglEvalPoint2 glEvalPoint2 +#define qglFeedbackBuffer glFeedbackBuffer +#define qglFinish glFinish +#define qglFlush glFlush +#define qglFogf glFogf +#define qglFogfv glFogfv +#define qglFogi glFogi +#define qglFogiv glFogiv +#define qglFrontFace glFrontFace +#define qglFrustum glFrustum +#define qglGenLists glGenLists +#define qglGenTextures glGenTextures +#define qglGetBooleanv glGetBooleanv +#define qglGetClipPlane glGetClipPlane +#define qglGetDoublev glGetDoublev +#define qglGetError glGetError +#define qglGetFloatv glGetFloatv +#define qglGetIntegerv glGetIntegerv +#define qglGetLightfv glGetLightfv +#define qglGetLightiv glGetLightiv +#define qglGetMapdv glGetMapdv +#define qglGetMapfv glGetMapfv +#define qglGetMapiv glGetMapiv +#define qglGetMaterialfv glGetMaterialfv +#define qglGetMaterialiv glGetMaterialiv +#define qglGetPixelMapfv glGetPixelMapfv +#define qglGetPixelMapuiv glGetPixelMapuiv +#define qglGetPixelMapusv glGetPixelMapusv +#define qglGetPointerv glGetPointerv +#define qglGetPolygonStipple glGetPolygonStipple +#define qglGetString glGetString +#define qglGetTexGendv glGetTexGendv +#define qglGetTexGenfv glGetTexGenfv +#define qglGetTexGeniv glGetTexGeniv +#define qglGetTexImage glGetTexImage +#define qglGetTexLevelParameterfv glGetTexLevelParameterfv +#define qglGetTexLevelParameteriv glGetTexLevelParameteriv +#define qglGetTexParameterfv glGetTexParameterfv +#define qglGetTexParameteriv glGetTexParameteriv +#define qglHint glHint +#define qglIndexMask glIndexMask +#define qglIndexPointer glIndexPointer +#define qglIndexd glIndexd +#define qglIndexdv glIndexdv +#define qglIndexf glIndexf +#define qglIndexfv glIndexfv +#define qglIndexi glIndexi +#define qglIndexiv glIndexiv +#define qglIndexs glIndexs +#define qglIndexsv glIndexsv +#define qglIndexub glIndexub +#define qglIndexubv glIndexubv +#define qglInitNames glInitNames +#define qglInterleavedArrays glInterleavedArrays +#define qglIsEnabled glIsEnabled +#define qglIsList glIsList +#define qglIsTexture glIsTexture +#define qglLightModelf glLightModelf +#define qglLightModelfv glLightModelfv +#define qglLightModeli glLightModeli +#define qglLightModeliv glLightModeliv +#define qglLightf glLightf +#define qglLightfv glLightfv +#define qglLighti glLighti +#define qglLightiv glLightiv +#define qglLineStipple glLineStipple +#define qglLineWidth glLineWidth +#define qglListBase glListBase +#define qglLoadIdentity glLoadIdentity +#define qglLoadMatrixd glLoadMatrixd +#define qglLoadMatrixf glLoadMatrixf +#define qglLoadName glLoadName +#define qglLogicOp glLogicOp +#define qglMap1d glMap1d +#define qglMap1f glMap1f +#define qglMap2d glMap2d +#define qglMap2f glMap2f +#define qglMapGrid1d glMapGrid1d +#define qglMapGrid1f glMapGrid1f +#define qglMapGrid2d glMapGrid2d +#define qglMapGrid2f glMapGrid2f +#define qglMaterialf glMaterialf +#define qglMaterialfv glMaterialfv +#define qglMateriali glMateriali +#define qglMaterialiv glMaterialiv +#define qglMatrixMode glMatrixMode +#define qglMultMatrixd glMultMatrixd +#define qglMultMatrixf glMultMatrixf +#define qglNewList glNewList +#define qglNormal3b glNormal3b +#define qglNormal3bv glNormal3bv +#define qglNormal3d glNormal3d +#define qglNormal3dv glNormal3dv +#define qglNormal3f glNormal3f +#define qglNormal3fv glNormal3fv +#define qglNormal3i glNormal3i +#define qglNormal3iv glNormal3iv +#define qglNormal3s glNormal3s +#define qglNormal3sv glNormal3sv +#define qglNormalPointer glNormalPointer +#define qglOrtho glOrtho +#define qglPassThrough glPassThrough +#define qglPixelMapfv glPixelMapfv +#define qglPixelMapuiv glPixelMapuiv +#define qglPixelMapusv glPixelMapusv +#define qglPixelStoref glPixelStoref +#define qglPixelStorei glPixelStorei +#define qglPixelTransferf glPixelTransferf +#define qglPixelTransferi glPixelTransferi +#define qglPixelZoom glPixelZoom +#define qglPointSize glPointSize +#define qglPolygonMode glPolygonMode +#define qglPolygonOffset glPolygonOffset +#define qglPolygonStipple glPolygonStipple +#define qglPopAttrib glPopAttrib +#define qglPopClientAttrib glPopClientAttrib +#define qglPopMatrix glPopMatrix +#define qglPopName glPopName +#define qglPrioritizeTextures glPrioritizeTextures +#define qglPushAttrib glPushAttrib +#define qglPushClientAttrib glPushClientAttrib +#define qglPushMatrix glPushMatrix +#define qglPushName glPushName +#define qglRasterPos2d glRasterPos2d +#define qglRasterPos2dv glRasterPos2dv +#define qglRasterPos2f glRasterPos2f +#define qglRasterPos2fv glRasterPos2fv +#define qglRasterPos2i glRasterPos2i +#define qglRasterPos2iv glRasterPos2iv +#define qglRasterPos2s glRasterPos2s +#define qglRasterPos2sv glRasterPos2sv +#define qglRasterPos3d glRasterPos3d +#define qglRasterPos3dv glRasterPos3dv +#define qglRasterPos3f glRasterPos3f +#define qglRasterPos3fv glRasterPos3fv +#define qglRasterPos3i glRasterPos3i +#define qglRasterPos3iv glRasterPos3iv +#define qglRasterPos3s glRasterPos3s +#define qglRasterPos3sv glRasterPos3sv +#define qglRasterPos4d glRasterPos4d +#define qglRasterPos4dv glRasterPos4dv +#define qglRasterPos4f glRasterPos4f +#define qglRasterPos4fv glRasterPos4fv +#define qglRasterPos4i glRasterPos4i +#define qglRasterPos4iv glRasterPos4iv +#define qglRasterPos4s glRasterPos4s +#define qglRasterPos4sv glRasterPos4sv +#define qglReadBuffer glReadBuffer +#define qglReadPixels glReadPixels +#define qglRectd glRectd +#define qglRectdv glRectdv +#define qglRectf glRectf +#define qglRectfv glRectfv +#define qglRecti glRecti +#define qglRectiv glRectiv +#define qglRects glRects +#define qglRectsv glRectsv +#define qglRenderMode glRenderMode +#define qglRotated glRotated +#define qglRotatef glRotatef +#define qglScaled glScaled +#define qglScalef glScalef +#define qglScissor glScissor +#define qglSelectBuffer glSelectBuffer +#define qglShadeModel glShadeModel +#define qglStencilFunc glStencilFunc +#define qglStencilMask glStencilMask +#define qglStencilOp glStencilOp +#define qglTexCoord1d glTexCoord1d +#define qglTexCoord1dv glTexCoord1dv +#define qglTexCoord1f glTexCoord1f +#define qglTexCoord1fv glTexCoord1fv +#define qglTexCoord1i glTexCoord1i +#define qglTexCoord1iv glTexCoord1iv +#define qglTexCoord1s glTexCoord1s +#define qglTexCoord1sv glTexCoord1sv +#define qglTexCoord2d glTexCoord2d +#define qglTexCoord2dv glTexCoord2dv +#define qglTexCoord2f glTexCoord2f +#define qglTexCoord2fv glTexCoord2fv +#define qglTexCoord2i glTexCoord2i +#define qglTexCoord2iv glTexCoord2iv +#define qglTexCoord2s glTexCoord2s +#define qglTexCoord2sv glTexCoord2sv +#define qglTexCoord3d glTexCoord3d +#define qglTexCoord3dv glTexCoord3dv +#define qglTexCoord3f glTexCoord3f +#define qglTexCoord3fv glTexCoord3fv +#define qglTexCoord3i glTexCoord3i +#define qglTexCoord3iv glTexCoord3iv +#define qglTexCoord3s glTexCoord3s +#define qglTexCoord3sv glTexCoord3sv +#define qglTexCoord4d glTexCoord4d +#define qglTexCoord4dv glTexCoord4dv +#define qglTexCoord4f glTexCoord4f +#define qglTexCoord4fv glTexCoord4fv +#define qglTexCoord4i glTexCoord4i +#define qglTexCoord4iv glTexCoord4iv +#define qglTexCoord4s glTexCoord4s +#define qglTexCoord4sv glTexCoord4sv +#define qglTexCoordPointer glTexCoordPointer +#define qglTexEnvf glTexEnvf +#define qglTexEnvfv glTexEnvfv +#define qglTexEnvi glTexEnvi +#define qglTexEnviv glTexEnviv +#define qglTexGend glTexGend +#define qglTexGendv glTexGendv +#define qglTexGenf glTexGenf +#define qglTexGenfv glTexGenfv +#define qglTexGeni glTexGeni +#define qglTexGeniv glTexGeniv +#define qglTexImage1D glTexImage1D +#define qglTexImage2D glTexImage2D +#define qglTexParameterf glTexParameterf +#define qglTexParameterfv glTexParameterfv +#define qglTexParameteri glTexParameteri +#define qglTexParameteriv glTexParameteriv +#define qglTexSubImage1D glTexSubImage1D +#define qglTexSubImage2D glTexSubImage2D +#define qglTranslated glTranslated +#define qglTranslatef glTranslatef +#define qglVertex2d glVertex2d +#define qglVertex2dv glVertex2dv +#define qglVertex2f glVertex2f +#define qglVertex2fv glVertex2fv +#define qglVertex2i glVertex2i +#define qglVertex2iv glVertex2iv +#define qglVertex2s glVertex2s +#define qglVertex2sv glVertex2sv +#define qglVertex3d glVertex3d +#define qglVertex3dv glVertex3dv +#define qglVertex3f glVertex3f +#define qglVertex3fv glVertex3fv +#define qglVertex3i glVertex3i +#define qglVertex3iv glVertex3iv +#define qglVertex3s glVertex3s +#define qglVertex3sv glVertex3sv +#define qglVertex4d glVertex4d +#define qglVertex4dv glVertex4dv +#define qglVertex4f glVertex4f +#define qglVertex4fv glVertex4fv +#define qglVertex4i glVertex4i +#define qglVertex4iv glVertex4iv +#define qglVertex4s glVertex4s +#define qglVertex4sv glVertex4sv +#define qglVertexPointer glVertexPointer +#define qglViewport glViewport + +#endif diff --git a/src/renderergl2/tr_animation.c b/src/renderergl2/tr_animation.c new file mode 100644 index 00000000..794111c6 --- /dev/null +++ b/src/renderergl2/tr_animation.c @@ -0,0 +1,658 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "tr_local.h" + +/* + +All bones should be an identity orientation to display the mesh exactly +as it is specified. + +For all other frames, the bones represent the transformation from the +orientation of the bone in the base frame to the orientation in this +frame. + +*/ + +/* +============== +R_AddAnimSurfaces +============== +*/ +void R_AddAnimSurfaces( trRefEntity_t *ent ) { + md4Header_t *header; + md4Surface_t *surface; + md4LOD_t *lod; + shader_t *shader; + int i; + + header = (md4Header_t *) tr.currentModel->modelData; + lod = (md4LOD_t *)( (byte *)header + header->ofsLODs ); + + surface = (md4Surface_t *)( (byte *)lod + lod->ofsSurfaces ); + for ( i = 0 ; i < lod->numSurfaces ; i++ ) { + shader = R_GetShaderByHandle( surface->shaderIndex ); + R_AddDrawSurf( (void *)surface, shader, 0 /*fogNum*/, qfalse, qfalse ); + surface = (md4Surface_t *)( (byte *)surface + surface->ofsEnd ); + } +} + +/* +============== +RB_SurfaceAnim +============== +*/ +void RB_SurfaceAnim( md4Surface_t *surface ) { + int i, j, k; + float frontlerp, backlerp; + int *triangles; + int indexes; + int baseIndex, baseVertex; + int numVerts; + md4Vertex_t *v; + md4Bone_t bones[MD4_MAX_BONES]; + md4Bone_t *bonePtr, *bone; + md4Header_t *header; + md4Frame_t *frame; + md4Frame_t *oldFrame; + int frameSize; + + + if ( backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame ) { + backlerp = 0; + frontlerp = 1; + } else { + backlerp = backEnd.currentEntity->e.backlerp; + frontlerp = 1.0f - backlerp; + } + header = (md4Header_t *)((byte *)surface + surface->ofsHeader); + + frameSize = (size_t)( &((md4Frame_t *)0)->bones[ header->numBones ] ); + + frame = (md4Frame_t *)((byte *)header + header->ofsFrames + + backEnd.currentEntity->e.frame * frameSize ); + oldFrame = (md4Frame_t *)((byte *)header + header->ofsFrames + + backEnd.currentEntity->e.oldframe * frameSize ); + + RB_CheckOverflow( surface->numVerts, surface->numTriangles * 3 ); + + triangles = (int *) ((byte *)surface + surface->ofsTriangles); + indexes = surface->numTriangles * 3; + baseIndex = tess.numIndexes; + baseVertex = tess.numVertexes; + for (j = 0 ; j < indexes ; j++) { + tess.indexes[baseIndex + j] = baseIndex + triangles[j]; + } + tess.numIndexes += indexes; + + // + // lerp all the needed bones + // + if ( !backlerp ) { + // no lerping needed + bonePtr = frame->bones; + } else { + bonePtr = bones; + for ( i = 0 ; i < header->numBones*12 ; i++ ) { + ((float *)bonePtr)[i] = frontlerp * ((float *)frame->bones)[i] + + backlerp * ((float *)oldFrame->bones)[i]; + } + } + + // + // deform the vertexes by the lerped bones + // + numVerts = surface->numVerts; + // FIXME + // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left + // in for reference. + //v = (md4Vertex_t *) ((byte *)surface + surface->ofsVerts + 12); + v = (md4Vertex_t *) ((byte *)surface + surface->ofsVerts); + for ( j = 0; j < numVerts; j++ ) { + vec3_t tempVert, tempNormal; + md4Weight_t *w; + + VectorClear( tempVert ); + VectorClear( tempNormal ); + w = v->weights; + for ( k = 0 ; k < v->numWeights ; k++, w++ ) { + bone = bonePtr + w->boneIndex; + + tempVert[0] += w->boneWeight * ( DotProduct( bone->matrix[0], w->offset ) + bone->matrix[0][3] ); + tempVert[1] += w->boneWeight * ( DotProduct( bone->matrix[1], w->offset ) + bone->matrix[1][3] ); + tempVert[2] += w->boneWeight * ( DotProduct( bone->matrix[2], w->offset ) + bone->matrix[2][3] ); + + tempNormal[0] += w->boneWeight * DotProduct( bone->matrix[0], v->normal ); + tempNormal[1] += w->boneWeight * DotProduct( bone->matrix[1], v->normal ); + tempNormal[2] += w->boneWeight * DotProduct( bone->matrix[2], v->normal ); + } + + tess.xyz[baseVertex + j][0] = tempVert[0]; + tess.xyz[baseVertex + j][1] = tempVert[1]; + tess.xyz[baseVertex + j][2] = tempVert[2]; + + tess.normal[baseVertex + j][0] = tempNormal[0]; + tess.normal[baseVertex + j][1] = tempNormal[1]; + tess.normal[baseVertex + j][2] = tempNormal[2]; + + tess.texCoords[baseVertex + j][0][0] = v->texCoords[0]; + tess.texCoords[baseVertex + j][0][1] = v->texCoords[1]; + + // FIXME + // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left + // in for reference. + //v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights] + 12 ); + v = (md4Vertex_t *)&v->weights[v->numWeights]; + } + + tess.numVertexes += surface->numVerts; +} + + +#ifdef RAVENMD4 + +// copied and adapted from tr_mesh.c + +/* +============= +R_MDRCullModel +============= +*/ + +static int R_MDRCullModel( mdrHeader_t *header, trRefEntity_t *ent ) { + vec3_t bounds[2]; + mdrFrame_t *oldFrame, *newFrame; + int i, frameSize; + + frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] ); + + // compute frame pointers + newFrame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.frame); + oldFrame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.oldframe); + + // cull bounding sphere ONLY if this is not an upscaled entity + if ( !ent->e.nonNormalizedAxes ) + { + if ( ent->e.frame == ent->e.oldframe ) + { + switch ( R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ) ) + { + // Ummm... yeah yeah I know we don't really have an md3 here.. but we pretend + // we do. After all, the purpose of md4s are not that different, are they? + + case CULL_OUT: + tr.pc.c_sphere_cull_md3_out++; + return CULL_OUT; + + case CULL_IN: + tr.pc.c_sphere_cull_md3_in++; + return CULL_IN; + + case CULL_CLIP: + tr.pc.c_sphere_cull_md3_clip++; + break; + } + } + else + { + int sphereCull, sphereCullB; + + sphereCull = R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ); + if ( newFrame == oldFrame ) { + sphereCullB = sphereCull; + } else { + sphereCullB = R_CullLocalPointAndRadius( oldFrame->localOrigin, oldFrame->radius ); + } + + if ( sphereCull == sphereCullB ) + { + if ( sphereCull == CULL_OUT ) + { + tr.pc.c_sphere_cull_md3_out++; + return CULL_OUT; + } + else if ( sphereCull == CULL_IN ) + { + tr.pc.c_sphere_cull_md3_in++; + return CULL_IN; + } + else + { + tr.pc.c_sphere_cull_md3_clip++; + } + } + } + } + + // calculate a bounding box in the current coordinate system + for (i = 0 ; i < 3 ; i++) { + bounds[0][i] = oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i]; + bounds[1][i] = oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i]; + } + + switch ( R_CullLocalBox( bounds ) ) + { + case CULL_IN: + tr.pc.c_box_cull_md3_in++; + return CULL_IN; + case CULL_CLIP: + tr.pc.c_box_cull_md3_clip++; + return CULL_CLIP; + case CULL_OUT: + default: + tr.pc.c_box_cull_md3_out++; + return CULL_OUT; + } +} + +/* +================= +R_MDRComputeFogNum + +================= +*/ + +int R_MDRComputeFogNum( mdrHeader_t *header, trRefEntity_t *ent ) { + int i, j; + fog_t *fog; + mdrFrame_t *mdrFrame; + vec3_t localOrigin; + int frameSize; + + if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { + return 0; + } + + frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] ); + + // FIXME: non-normalized axis issues + mdrFrame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.frame); + VectorAdd( ent->e.origin, mdrFrame->localOrigin, localOrigin ); + for ( i = 1 ; i < tr.world->numfogs ; i++ ) { + fog = &tr.world->fogs[i]; + for ( j = 0 ; j < 3 ; j++ ) { + if ( localOrigin[j] - mdrFrame->radius >= fog->bounds[1][j] ) { + break; + } + if ( localOrigin[j] + mdrFrame->radius <= fog->bounds[0][j] ) { + break; + } + } + if ( j == 3 ) { + return i; + } + } + + return 0; +} + + +/* +============== +R_MDRAddAnimSurfaces +============== +*/ + +// much stuff in there is just copied from R_AddMd3Surfaces in tr_mesh.c + +void R_MDRAddAnimSurfaces( trRefEntity_t *ent ) { + mdrHeader_t *header; + mdrSurface_t *surface; + mdrLOD_t *lod; + shader_t *shader; + skin_t *skin; + int i, j; + int lodnum = 0; + int fogNum = 0; + int cull; + qboolean personalModel; + + header = (mdrHeader_t *) tr.currentModel->modelData; + + personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal; + + if ( ent->e.renderfx & RF_WRAP_FRAMES ) + { + ent->e.frame %= header->numFrames; + ent->e.oldframe %= header->numFrames; + } + + // + // Validate the frames so there is no chance of a crash. + // This will write directly into the entity structure, so + // when the surfaces are rendered, they don't need to be + // range checked again. + // + if ((ent->e.frame >= header->numFrames) + || (ent->e.frame < 0) + || (ent->e.oldframe >= header->numFrames) + || (ent->e.oldframe < 0) ) + { + ri.Printf( PRINT_DEVELOPER, "R_MDRAddAnimSurfaces: no such frame %d to %d for '%s'\n", + ent->e.oldframe, ent->e.frame, tr.currentModel->name ); + ent->e.frame = 0; + ent->e.oldframe = 0; + } + + // + // cull the entire model if merged bounding box of both frames + // is outside the view frustum. + // + cull = R_MDRCullModel (header, ent); + if ( cull == CULL_OUT ) { + return; + } + + // figure out the current LOD of the model we're rendering, and set the lod pointer respectively. + lodnum = R_ComputeLOD(ent); + // check whether this model has as that many LODs at all. If not, try the closest thing we got. + if(header->numLODs <= 0) + return; + if(header->numLODs <= lodnum) + lodnum = header->numLODs - 1; + + lod = (mdrLOD_t *)( (byte *)header + header->ofsLODs); + for(i = 0; i < lodnum; i++) + { + lod = (mdrLOD_t *) ((byte *) lod + lod->ofsEnd); + } + + // set up lighting + if ( !personalModel || r_shadows->integer > 1 ) + { + R_SetupEntityLighting( &tr.refdef, ent ); + } + + // fogNum? + fogNum = R_MDRComputeFogNum( header, ent ); + + surface = (mdrSurface_t *)( (byte *)lod + lod->ofsSurfaces ); + + for ( i = 0 ; i < lod->numSurfaces ; i++ ) + { + + if(ent->e.customShader) + shader = R_GetShaderByHandle(ent->e.customShader); + else if(ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins) + { + skin = R_GetSkinByHandle(ent->e.customSkin); + shader = tr.defaultShader; + + for(j = 0; j < skin->numSurfaces; j++) + { + if (!strcmp(skin->surfaces[j]->name, surface->name)) + { + shader = skin->surfaces[j]->shader; + break; + } + } + } + else if(surface->shaderIndex > 0) + shader = R_GetShaderByHandle( surface->shaderIndex ); + else + shader = tr.defaultShader; + + // we will add shadows even if the main object isn't visible in the view + + // stencil shadows can't do personal models unless I polyhedron clip + if ( !personalModel + && r_shadows->integer == 2 + && fogNum == 0 + && !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) + && shader->sort == SS_OPAQUE ) + { + R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, qfalse, qfalse ); + } + + // projection shadows work fine with personal models + if ( r_shadows->integer == 3 + && fogNum == 0 + && (ent->e.renderfx & RF_SHADOW_PLANE ) + && shader->sort == SS_OPAQUE ) + { + R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, qfalse, qfalse ); + } + + if (!personalModel) + R_AddDrawSurf( (void *)surface, shader, fogNum, qfalse, qfalse ); + + surface = (mdrSurface_t *)( (byte *)surface + surface->ofsEnd ); + } +} + +/* +============== +RB_MDRSurfaceAnim +============== +*/ +void RB_MDRSurfaceAnim( md4Surface_t *surface ) +{ + int i, j, k; + float frontlerp, backlerp; + int *triangles; + int indexes; + int baseIndex, baseVertex; + int numVerts; + mdrVertex_t *v; + mdrHeader_t *header; + mdrFrame_t *frame; + mdrFrame_t *oldFrame; + mdrBone_t bones[MD4_MAX_BONES], *bonePtr, *bone; + + int frameSize; + + // don't lerp if lerping off, or this is the only frame, or the last frame... + // + if (backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame) + { + backlerp = 0; // if backlerp is 0, lerping is off and frontlerp is never used + frontlerp = 1; + } + else + { + backlerp = backEnd.currentEntity->e.backlerp; + frontlerp = 1.0f - backlerp; + } + + header = (mdrHeader_t *)((byte *)surface + surface->ofsHeader); + + frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] ); + + frame = (mdrFrame_t *)((byte *)header + header->ofsFrames + + backEnd.currentEntity->e.frame * frameSize ); + oldFrame = (mdrFrame_t *)((byte *)header + header->ofsFrames + + backEnd.currentEntity->e.oldframe * frameSize ); + + RB_CheckOverflow( surface->numVerts, surface->numTriangles ); + + triangles = (int *) ((byte *)surface + surface->ofsTriangles); + indexes = surface->numTriangles * 3; + baseIndex = tess.numIndexes; + baseVertex = tess.numVertexes; + + // Set up all triangles. + for (j = 0 ; j < indexes ; j++) + { + tess.indexes[baseIndex + j] = baseVertex + triangles[j]; + } + tess.numIndexes += indexes; + + // + // lerp all the needed bones + // + if ( !backlerp ) + { + // no lerping needed + bonePtr = frame->bones; + } + else + { + bonePtr = bones; + + for ( i = 0 ; i < header->numBones*12 ; i++ ) + { + ((float *)bonePtr)[i] = frontlerp * ((float *)frame->bones)[i] + backlerp * ((float *)oldFrame->bones)[i]; + } + } + + // + // deform the vertexes by the lerped bones + // + numVerts = surface->numVerts; + v = (mdrVertex_t *) ((byte *)surface + surface->ofsVerts); + for ( j = 0; j < numVerts; j++ ) + { + vec3_t tempVert, tempNormal; + mdrWeight_t *w; + + VectorClear( tempVert ); + VectorClear( tempNormal ); + w = v->weights; + for ( k = 0 ; k < v->numWeights ; k++, w++ ) + { + bone = bonePtr + w->boneIndex; + + tempVert[0] += w->boneWeight * ( DotProduct( bone->matrix[0], w->offset ) + bone->matrix[0][3] ); + tempVert[1] += w->boneWeight * ( DotProduct( bone->matrix[1], w->offset ) + bone->matrix[1][3] ); + tempVert[2] += w->boneWeight * ( DotProduct( bone->matrix[2], w->offset ) + bone->matrix[2][3] ); + + tempNormal[0] += w->boneWeight * DotProduct( bone->matrix[0], v->normal ); + tempNormal[1] += w->boneWeight * DotProduct( bone->matrix[1], v->normal ); + tempNormal[2] += w->boneWeight * DotProduct( bone->matrix[2], v->normal ); + } + + tess.xyz[baseVertex + j][0] = tempVert[0]; + tess.xyz[baseVertex + j][1] = tempVert[1]; + tess.xyz[baseVertex + j][2] = tempVert[2]; + + tess.normal[baseVertex + j][0] = tempNormal[0]; + tess.normal[baseVertex + j][1] = tempNormal[1]; + tess.normal[baseVertex + j][2] = tempNormal[2]; + + tess.texCoords[baseVertex + j][0][0] = v->texCoords[0]; + tess.texCoords[baseVertex + j][0][1] = v->texCoords[1]; + + v = (mdrVertex_t *)&v->weights[v->numWeights]; + } + + tess.numVertexes += surface->numVerts; +} + + +#define MC_MASK_X ((1<<(MC_BITS_X))-1) +#define MC_MASK_Y ((1<<(MC_BITS_Y))-1) +#define MC_MASK_Z ((1<<(MC_BITS_Z))-1) +#define MC_MASK_VECT ((1<<(MC_BITS_VECT))-1) + +#define MC_SCALE_VECT (1.0f/(float)((1<<(MC_BITS_VECT-1))-2)) + +#define MC_POS_X (0) +#define MC_SHIFT_X (0) + +#define MC_POS_Y ((((MC_BITS_X))/8)) +#define MC_SHIFT_Y ((((MC_BITS_X)%8))) + +#define MC_POS_Z ((((MC_BITS_X+MC_BITS_Y))/8)) +#define MC_SHIFT_Z ((((MC_BITS_X+MC_BITS_Y)%8))) + +#define MC_POS_V11 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z))/8)) +#define MC_SHIFT_V11 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z)%8))) + +#define MC_POS_V12 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT))/8)) +#define MC_SHIFT_V12 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT)%8))) + +#define MC_POS_V13 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*2))/8)) +#define MC_SHIFT_V13 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*2)%8))) + +#define MC_POS_V21 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*3))/8)) +#define MC_SHIFT_V21 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*3)%8))) + +#define MC_POS_V22 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*4))/8)) +#define MC_SHIFT_V22 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*4)%8))) + +#define MC_POS_V23 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*5))/8)) +#define MC_SHIFT_V23 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*5)%8))) + +#define MC_POS_V31 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*6))/8)) +#define MC_SHIFT_V31 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*6)%8))) + +#define MC_POS_V32 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*7))/8)) +#define MC_SHIFT_V32 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*7)%8))) + +#define MC_POS_V33 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*8))/8)) +#define MC_SHIFT_V33 ((((MC_BITS_X+MC_BITS_Y+MC_BITS_Z+MC_BITS_VECT*8)%8))) + +void MC_UnCompress(float mat[3][4],const unsigned char * comp) +{ + int val; + + val=(int)((unsigned short *)(comp))[0]; + val-=1<<(MC_BITS_X-1); + mat[0][3]=((float)(val))*MC_SCALE_X; + + val=(int)((unsigned short *)(comp))[1]; + val-=1<<(MC_BITS_Y-1); + mat[1][3]=((float)(val))*MC_SCALE_Y; + + val=(int)((unsigned short *)(comp))[2]; + val-=1<<(MC_BITS_Z-1); + mat[2][3]=((float)(val))*MC_SCALE_Z; + + val=(int)((unsigned short *)(comp))[3]; + val-=1<<(MC_BITS_VECT-1); + mat[0][0]=((float)(val))*MC_SCALE_VECT; + + val=(int)((unsigned short *)(comp))[4]; + val-=1<<(MC_BITS_VECT-1); + mat[0][1]=((float)(val))*MC_SCALE_VECT; + + val=(int)((unsigned short *)(comp))[5]; + val-=1<<(MC_BITS_VECT-1); + mat[0][2]=((float)(val))*MC_SCALE_VECT; + + + val=(int)((unsigned short *)(comp))[6]; + val-=1<<(MC_BITS_VECT-1); + mat[1][0]=((float)(val))*MC_SCALE_VECT; + + val=(int)((unsigned short *)(comp))[7]; + val-=1<<(MC_BITS_VECT-1); + mat[1][1]=((float)(val))*MC_SCALE_VECT; + + val=(int)((unsigned short *)(comp))[8]; + val-=1<<(MC_BITS_VECT-1); + mat[1][2]=((float)(val))*MC_SCALE_VECT; + + + val=(int)((unsigned short *)(comp))[9]; + val-=1<<(MC_BITS_VECT-1); + mat[2][0]=((float)(val))*MC_SCALE_VECT; + + val=(int)((unsigned short *)(comp))[10]; + val-=1<<(MC_BITS_VECT-1); + mat[2][1]=((float)(val))*MC_SCALE_VECT; + + val=(int)((unsigned short *)(comp))[11]; + val-=1<<(MC_BITS_VECT-1); + mat[2][2]=((float)(val))*MC_SCALE_VECT; +} +#endif diff --git a/src/renderergl2/tr_backend.c b/src/renderergl2/tr_backend.c new file mode 100644 index 00000000..fd61ee76 --- /dev/null +++ b/src/renderergl2/tr_backend.c @@ -0,0 +1,1843 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +#include "tr_local.h" + +backEndData_t *backEndData; +backEndState_t backEnd; + + +static float s_flipMatrix[16] = { + // convert from our coordinate system (looking down X) + // to OpenGL's coordinate system (looking down -Z) + 0, 0, -1, 0, + -1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 0, 1 +}; + + +/* +** GL_Bind2 +*/ +void GL_Bind2( image_t *image, GLenum type ) { + int texnum; + + if ( !image ) { + ri.Printf( PRINT_WARNING, "GL_Bind2: NULL image\n" ); + texnum = tr.defaultImage->texnum; + } else { + texnum = image->texnum; + } + + if ( r_nobind->integer && tr.dlightImage ) { // performance evaluation option + texnum = tr.dlightImage->texnum; + } + + if ( glState.currenttextures[glState.currenttmu] != texnum ) { + image->frameUsed = tr.frameCount; + glState.currenttextures[glState.currenttmu] = texnum; + qglBindTexture (type, texnum); + } +} + +/* +** GL_Bind2 +*/ +void GL_Bind( image_t *image ) +{ + GL_Bind2( image, GL_TEXTURE_2D ); +} + +/* +** GL_BindCubemap +*/ +void GL_BindCubemap( image_t *image ) +{ + GL_Bind2( image, GL_TEXTURE_CUBE_MAP ); +} + +/* +** GL_SelectTexture +*/ +void GL_SelectTexture( int unit ) +{ + if ( glState.currenttmu == unit ) + { + return; + } + + if (!(unit >= 0 && unit <= 31)) + ri.Error( ERR_DROP, "GL_SelectTexture: unit = %i", unit ); + + qglActiveTextureARB( GL_TEXTURE0_ARB + unit ); + + glState.currenttmu = unit; +} + + +/* +** GL_BindMultitexture +*/ +void GL_BindMultitexture( image_t *image0, GLuint env0, image_t *image1, GLuint env1 ) { + int texnum0, texnum1; + + texnum0 = image0->texnum; + texnum1 = image1->texnum; + + if ( r_nobind->integer && tr.dlightImage ) { // performance evaluation option + texnum0 = texnum1 = tr.dlightImage->texnum; + } + + if ( glState.currenttextures[1] != texnum1 ) { + GL_SelectTexture( 1 ); + image1->frameUsed = tr.frameCount; + glState.currenttextures[1] = texnum1; + qglBindTexture( GL_TEXTURE_2D, texnum1 ); + } + if ( glState.currenttextures[0] != texnum0 ) { + GL_SelectTexture( 0 ); + image0->frameUsed = tr.frameCount; + glState.currenttextures[0] = texnum0; + qglBindTexture( GL_TEXTURE_2D, texnum0 ); + } +} + +/* +** GL_BindToTMU +*/ +void GL_BindToTMU( image_t *image, int tmu ) +{ + int texnum; + int oldtmu = glState.currenttmu; + + if (!image) + texnum = 0; + else + texnum = image->texnum; + + if ( glState.currenttextures[tmu] != texnum ) { + GL_SelectTexture( tmu ); + if (image) + image->frameUsed = tr.frameCount; + glState.currenttextures[tmu] = texnum; + qglBindTexture( GL_TEXTURE_2D, texnum ); + GL_SelectTexture( oldtmu ); + } +} + + +/* +** GL_Cull +*/ +void GL_Cull( int cullType ) { + if ( glState.faceCulling == cullType ) { + return; + } + + glState.faceCulling = cullType; + + if ( cullType == CT_TWO_SIDED ) + { + qglDisable( GL_CULL_FACE ); + } + else + { + qboolean cullFront; + qglEnable( GL_CULL_FACE ); + + cullFront = (cullType == CT_FRONT_SIDED); + if ( backEnd.viewParms.isMirror ) + { + cullFront = !cullFront; + } + + if ( backEnd.currentEntity && backEnd.currentEntity->mirrored ) + { + cullFront = !cullFront; + } + + qglCullFace( cullFront ? GL_FRONT : GL_BACK ); + } +} + +/* +** GL_TexEnv +*/ +void GL_TexEnv( int env ) +{ + if ( env == glState.texEnv[glState.currenttmu] ) + { + return; + } + + glState.texEnv[glState.currenttmu] = env; + + + switch ( env ) + { + case GL_MODULATE: + qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); + break; + case GL_REPLACE: + qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE ); + break; + case GL_DECAL: + qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL ); + break; + case GL_ADD: + qglTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ADD ); + break; + default: + ri.Error( ERR_DROP, "GL_TexEnv: invalid env '%d' passed", env ); + break; + } +} + +/* +** GL_State +** +** This routine is responsible for setting the most commonly changed state +** in Q3. +*/ +void GL_State( unsigned long stateBits ) +{ + unsigned long diff = stateBits ^ glState.glStateBits; + + if ( !diff ) + { + return; + } + + // + // check depthFunc bits + // + if ( diff & GLS_DEPTHFUNC_BITS ) + { + if ( stateBits & GLS_DEPTHFUNC_EQUAL ) + { + qglDepthFunc( GL_EQUAL ); + } + else if ( stateBits & GLS_DEPTHFUNC_GREATER) + { + qglDepthFunc( GL_GREATER ); + } + else + { + qglDepthFunc( GL_LEQUAL ); + } + } + + // + // check blend bits + // + if ( diff & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) + { + GLenum srcFactor, dstFactor; + + if ( stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) + { + switch ( stateBits & GLS_SRCBLEND_BITS ) + { + case GLS_SRCBLEND_ZERO: + srcFactor = GL_ZERO; + break; + case GLS_SRCBLEND_ONE: + srcFactor = GL_ONE; + break; + case GLS_SRCBLEND_DST_COLOR: + srcFactor = GL_DST_COLOR; + break; + case GLS_SRCBLEND_ONE_MINUS_DST_COLOR: + srcFactor = GL_ONE_MINUS_DST_COLOR; + break; + case GLS_SRCBLEND_SRC_ALPHA: + srcFactor = GL_SRC_ALPHA; + break; + case GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA: + srcFactor = GL_ONE_MINUS_SRC_ALPHA; + break; + case GLS_SRCBLEND_DST_ALPHA: + srcFactor = GL_DST_ALPHA; + break; + case GLS_SRCBLEND_ONE_MINUS_DST_ALPHA: + srcFactor = GL_ONE_MINUS_DST_ALPHA; + break; + case GLS_SRCBLEND_ALPHA_SATURATE: + srcFactor = GL_SRC_ALPHA_SATURATE; + break; + default: + srcFactor = GL_ONE; // to get warning to shut up + ri.Error( ERR_DROP, "GL_State: invalid src blend state bits" ); + break; + } + + switch ( stateBits & GLS_DSTBLEND_BITS ) + { + case GLS_DSTBLEND_ZERO: + dstFactor = GL_ZERO; + break; + case GLS_DSTBLEND_ONE: + dstFactor = GL_ONE; + break; + case GLS_DSTBLEND_SRC_COLOR: + dstFactor = GL_SRC_COLOR; + break; + case GLS_DSTBLEND_ONE_MINUS_SRC_COLOR: + dstFactor = GL_ONE_MINUS_SRC_COLOR; + break; + case GLS_DSTBLEND_SRC_ALPHA: + dstFactor = GL_SRC_ALPHA; + break; + case GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA: + dstFactor = GL_ONE_MINUS_SRC_ALPHA; + break; + case GLS_DSTBLEND_DST_ALPHA: + dstFactor = GL_DST_ALPHA; + break; + case GLS_DSTBLEND_ONE_MINUS_DST_ALPHA: + dstFactor = GL_ONE_MINUS_DST_ALPHA; + break; + default: + dstFactor = GL_ONE; // to get warning to shut up + ri.Error( ERR_DROP, "GL_State: invalid dst blend state bits" ); + break; + } + + qglEnable( GL_BLEND ); + qglBlendFunc( srcFactor, dstFactor ); + } + else + { + qglDisable( GL_BLEND ); + } + } + + // + // check depthmask + // + if ( diff & GLS_DEPTHMASK_TRUE ) + { + if ( stateBits & GLS_DEPTHMASK_TRUE ) + { + qglDepthMask( GL_TRUE ); + } + else + { + qglDepthMask( GL_FALSE ); + } + } + + // + // fill/line mode + // + if ( diff & GLS_POLYMODE_LINE ) + { + if ( stateBits & GLS_POLYMODE_LINE ) + { + qglPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); + } + else + { + qglPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); + } + } + + // + // depthtest + // + if ( diff & GLS_DEPTHTEST_DISABLE ) + { + if ( stateBits & GLS_DEPTHTEST_DISABLE ) + { + qglDisable( GL_DEPTH_TEST ); + } + else + { + qglEnable( GL_DEPTH_TEST ); + } + } + + // + // alpha test + // + if ( diff & GLS_ATEST_BITS ) + { + switch ( stateBits & GLS_ATEST_BITS ) + { + case 0: + qglDisable( GL_ALPHA_TEST ); + break; + case GLS_ATEST_GT_0: + qglEnable( GL_ALPHA_TEST ); + qglAlphaFunc( GL_GREATER, 0.0f ); + break; + case GLS_ATEST_LT_80: + qglEnable( GL_ALPHA_TEST ); + qglAlphaFunc( GL_LESS, 0.5f ); + break; + case GLS_ATEST_GE_80: + qglEnable( GL_ALPHA_TEST ); + qglAlphaFunc( GL_GEQUAL, 0.5f ); + break; + default: + assert( 0 ); + break; + } + } + + glState.glStateBits = stateBits; +} + + +void GL_SetProjectionMatrix(matrix_t matrix) +{ + Matrix16Copy(matrix, glState.projection); + Matrix16Multiply(glState.projection, glState.modelview, glState.modelviewProjection); +} + + +void GL_SetModelviewMatrix(matrix_t matrix) +{ + Matrix16Copy(matrix, glState.modelview); + Matrix16Multiply(glState.projection, glState.modelview, glState.modelviewProjection); +} + + +/* +================ +RB_Hyperspace + +A player has predicted a teleport, but hasn't arrived yet +================ +*/ +static void RB_Hyperspace( void ) { + float c; + + if ( !backEnd.isHyperspace ) { + // do initialization shit + } + + c = ( backEnd.refdef.time & 255 ) / 255.0f; + qglClearColor( c, c, c, 1 ); + qglClear( GL_COLOR_BUFFER_BIT ); + + backEnd.isHyperspace = qtrue; +} + + +static void SetViewportAndScissor( void ) { + GL_SetProjectionMatrix( backEnd.viewParms.projectionMatrix ); + + // set the window clipping + qglViewport( backEnd.viewParms.viewportX, backEnd.viewParms.viewportY, + backEnd.viewParms.viewportWidth, backEnd.viewParms.viewportHeight ); + qglScissor( backEnd.viewParms.viewportX, backEnd.viewParms.viewportY, + backEnd.viewParms.viewportWidth, backEnd.viewParms.viewportHeight ); +} + +/* +================= +RB_BeginDrawingView + +Any mirrored or portaled views have already been drawn, so prepare +to actually render the visible surfaces for this view +================= +*/ +void RB_BeginDrawingView (void) { + int clearBits = 0; + + // sync with gl if needed + if ( r_finish->integer == 1 && !glState.finishCalled ) { + qglFinish (); + glState.finishCalled = qtrue; + } + if ( r_finish->integer == 0 ) { + glState.finishCalled = qtrue; + } + + // we will need to change the projection matrix before drawing + // 2D images again + backEnd.projection2D = qfalse; + + if (glRefConfig.framebufferObject) + { + // FIXME: HUGE HACK: render to the screen fbo if we've already postprocessed the frame and aren't drawing more world + // drawing more world check is in case of double renders, such as skyportals + if (backEnd.viewParms.targetFbo == NULL) + { + if (!tr.renderFbo || (backEnd.framePostProcessed && (backEnd.refdef.rdflags & RDF_NOWORLDMODEL))) + { + FBO_Bind(tr.screenScratchFbo); + } + else + { + FBO_Bind(tr.renderFbo); + } + } + else + { + FBO_Bind(backEnd.viewParms.targetFbo); + } + } + + // + // set the modelview matrix for the viewer + // + SetViewportAndScissor(); + + // ensures that depth writes are enabled for the depth clear + GL_State( GLS_DEFAULT ); + // clear relevant buffers + clearBits = GL_DEPTH_BUFFER_BIT; + + if ( r_measureOverdraw->integer || r_shadows->integer == 2 ) + { + clearBits |= GL_STENCIL_BUFFER_BIT; + } + if ( r_fastsky->integer && !( backEnd.refdef.rdflags & RDF_NOWORLDMODEL ) ) + { + clearBits |= GL_COLOR_BUFFER_BIT; // FIXME: only if sky shaders have been used +#ifdef _DEBUG + qglClearColor( 0.8f, 0.7f, 0.4f, 1.0f ); // FIXME: get color of sky +#else + qglClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); // FIXME: get color of sky +#endif + } + + // clear to white for shadow maps + if (backEnd.viewParms.flags & VPF_SHADOWMAP) + { + clearBits |= GL_COLOR_BUFFER_BIT; + qglClearColor( 1.0f, 1.0f, 1.0f, 1.0f ); + } + + qglClear( clearBits ); + + if ( ( backEnd.refdef.rdflags & RDF_HYPERSPACE ) ) + { + RB_Hyperspace(); + return; + } + else + { + backEnd.isHyperspace = qfalse; + } + + glState.faceCulling = -1; // force face culling to set next time + + // we will only draw a sun if there was sky rendered in this view + backEnd.skyRenderedThisView = qfalse; + + // clip to the plane of the portal + if ( backEnd.viewParms.isPortal ) { +#if 0 + float plane[4]; + double plane2[4]; + + plane[0] = backEnd.viewParms.portalPlane.normal[0]; + plane[1] = backEnd.viewParms.portalPlane.normal[1]; + plane[2] = backEnd.viewParms.portalPlane.normal[2]; + plane[3] = backEnd.viewParms.portalPlane.dist; + + plane2[0] = DotProduct (backEnd.viewParms.or.axis[0], plane); + plane2[1] = DotProduct (backEnd.viewParms.or.axis[1], plane); + plane2[2] = DotProduct (backEnd.viewParms.or.axis[2], plane); + plane2[3] = DotProduct (plane, backEnd.viewParms.or.origin) - plane[3]; +#endif + GL_SetModelviewMatrix( s_flipMatrix ); + } +} + + +#define MAC_EVENT_PUMP_MSEC 5 + +/* +================== +RB_RenderDrawSurfList +================== +*/ +void RB_RenderDrawSurfList( drawSurf_t *drawSurfs, int numDrawSurfs ) { + shader_t *shader, *oldShader; + int fogNum, oldFogNum; + int entityNum, oldEntityNum; + int dlighted, oldDlighted; + int pshadowed, oldPshadowed; + qboolean depthRange, oldDepthRange, isCrosshair, wasCrosshair; + int i; + drawSurf_t *drawSurf; + int oldSort; + float originalTime; + FBO_t* fbo = NULL; + qboolean inQuery = qfalse; + + float depth[2]; + + + // save original time for entity shader offsets + originalTime = backEnd.refdef.floatTime; + + fbo = glState.currentFBO; + + // draw everything + oldEntityNum = -1; + backEnd.currentEntity = &tr.worldEntity; + oldShader = NULL; + oldFogNum = -1; + oldDepthRange = qfalse; + wasCrosshair = qfalse; + oldDlighted = qfalse; + oldPshadowed = qfalse; + oldSort = -1; + depthRange = qfalse; + + depth[0] = 0.f; + depth[1] = 1.f; + + backEnd.pc.c_surfaces += numDrawSurfs; + + for (i = 0, drawSurf = drawSurfs ; i < numDrawSurfs ; i++, drawSurf++) { + if ( drawSurf->sort == oldSort ) { + if (backEnd.depthFill && shader && shader->sort != SS_OPAQUE) + continue; + + // fast path, same as previous sort + rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface ); + continue; + } + oldSort = drawSurf->sort; + R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted, &pshadowed ); + + // + // change the tess parameters if needed + // a "entityMergable" shader is a shader that can have surfaces from seperate + // entities merged into a single batch, like smoke and blood puff sprites + if (shader != oldShader || fogNum != oldFogNum || dlighted != oldDlighted || pshadowed != oldPshadowed + || ( entityNum != oldEntityNum && !shader->entityMergable ) ) { + if (oldShader != NULL) { + RB_EndSurface(); + } + RB_BeginSurface( shader, fogNum ); + backEnd.pc.c_surfBatches++; + oldShader = shader; + oldFogNum = fogNum; + oldDlighted = dlighted; + oldPshadowed = pshadowed; + } + + if (backEnd.depthFill && shader && shader->sort != SS_OPAQUE) + continue; + + // + // change the modelview matrix if needed + // + if ( entityNum != oldEntityNum ) { + qboolean sunflare = qfalse; + depthRange = isCrosshair = qfalse; + + if ( entityNum != REFENTITYNUM_WORLD ) { + backEnd.currentEntity = &backEnd.refdef.entities[entityNum]; + backEnd.refdef.floatTime = originalTime - backEnd.currentEntity->e.shaderTime; + // we have to reset the shaderTime as well otherwise image animations start + // from the wrong frame + tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; + + // set up the transformation matrix + R_RotateForEntity( backEnd.currentEntity, &backEnd.viewParms, &backEnd.or ); + + // set up the dynamic lighting if needed + if ( backEnd.currentEntity->needDlights ) { + R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.or ); + } + + if(backEnd.currentEntity->e.renderfx & RF_DEPTHHACK) + { + // hack the depth range to prevent view model from poking into walls + depthRange = qtrue; + + if(backEnd.currentEntity->e.renderfx & RF_CROSSHAIR) + isCrosshair = qtrue; + } + } else { + backEnd.currentEntity = &tr.worldEntity; + backEnd.refdef.floatTime = originalTime; + backEnd.or = backEnd.viewParms.world; + // we have to reset the shaderTime as well otherwise image animations on + // the world (like water) continue with the wrong frame + tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; + R_TransformDlights( backEnd.refdef.num_dlights, backEnd.refdef.dlights, &backEnd.or ); + } + + GL_SetModelviewMatrix( backEnd.or.modelMatrix ); + + // + // change depthrange. Also change projection matrix so first person weapon does not look like coming + // out of the screen. + // + if (oldDepthRange != depthRange || wasCrosshair != isCrosshair) + { + if (depthRange) + { + if(backEnd.viewParms.stereoFrame != STEREO_CENTER) + { + if(isCrosshair) + { + if(oldDepthRange) + { + // was not a crosshair but now is, change back proj matrix + GL_SetProjectionMatrix( backEnd.viewParms.projectionMatrix ); + } + } + else + { + viewParms_t temp = backEnd.viewParms; + + R_SetupProjection(&temp, r_znear->value, 0, qfalse); + + GL_SetProjectionMatrix( temp.projectionMatrix ); + } + } + + if(!oldDepthRange) + { + depth[0] = 0; + depth[1] = 0.3f; + qglDepthRange (depth[0], depth[1]); + } + } + else + { + if(!wasCrosshair && backEnd.viewParms.stereoFrame != STEREO_CENTER) + { + GL_SetProjectionMatrix( backEnd.viewParms.projectionMatrix ); + } + + if (!sunflare) + qglDepthRange (0, 1); + + depth[0] = 0; + depth[1] = 1; + } + + oldDepthRange = depthRange; + wasCrosshair = isCrosshair; + } + + oldEntityNum = entityNum; + } + + // add the triangles for this surface + rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface ); + } + + backEnd.refdef.floatTime = originalTime; + + // draw the contents of the last shader batch + if (oldShader != NULL) { + RB_EndSurface(); + } + + if (inQuery) { + inQuery = qfalse; + qglEndQueryARB(GL_SAMPLES_PASSED_ARB); + } + + if (glRefConfig.framebufferObject) + FBO_Bind(fbo); + + // go back to the world modelview matrix + + GL_SetModelviewMatrix( backEnd.viewParms.world.modelMatrix ); + + qglDepthRange (0, 1); +} + + +/* +============================================================================ + +RENDER BACK END FUNCTIONS + +============================================================================ +*/ + +/* +================ +RB_SetGL2D + +================ +*/ +void RB_SetGL2D (void) { + matrix_t matrix; + int width, height; + + if (backEnd.projection2D && backEnd.last2DFBO == glState.currentFBO) + return; + + backEnd.projection2D = qtrue; + backEnd.last2DFBO = glState.currentFBO; + + if (glState.currentFBO) + { + width = glState.currentFBO->width; + height = glState.currentFBO->height; + } + else + { + width = glConfig.vidWidth; + height = glConfig.vidHeight; + } + + // set 2D virtual screen size + qglViewport( 0, 0, width, height ); + qglScissor( 0, 0, width, height ); + + Matrix16Ortho(0, width, height, 0, 0, 1, matrix); + GL_SetProjectionMatrix(matrix); + Matrix16Identity(matrix); + GL_SetModelviewMatrix(matrix); + + GL_State( GLS_DEPTHTEST_DISABLE | + GLS_SRCBLEND_SRC_ALPHA | + GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); + + qglDisable( GL_CULL_FACE ); + qglDisable( GL_CLIP_PLANE0 ); + + // set time for 2D shaders + backEnd.refdef.time = ri.Milliseconds(); + backEnd.refdef.floatTime = backEnd.refdef.time * 0.001f; + + // reset color scaling + backEnd.refdef.colorScale = 1.0f; +} + + +/* +============= +RE_StretchRaw + +FIXME: not exactly backend +Stretches a raw 32 bit power of 2 bitmap image over the given screen rectangle. +Used for cinematics. +============= +*/ +void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty) { + int i, j; + int start, end; + shaderProgram_t *sp = &tr.textureColorShader; + vec4_t color; + + if ( !tr.registered ) { + return; + } + R_IssuePendingRenderCommands(); + + // we definately want to sync every frame for the cinematics + qglFinish(); + + start = 0; + if ( r_speeds->integer ) { + start = ri.Milliseconds(); + } + + // make sure rows and cols are powers of 2 + for ( i = 0 ; ( 1 << i ) < cols ; i++ ) { + } + for ( j = 0 ; ( 1 << j ) < rows ; j++ ) { + } + if ( ( 1 << i ) != cols || ( 1 << j ) != rows) { + ri.Error (ERR_DROP, "Draw_StretchRaw: size not a power of 2: %i by %i", cols, rows); + } + + GL_Bind( tr.scratchImage[client] ); + + // if the scratchImage isn't in the format we want, specify it as a new texture + if ( cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height ) { + tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols; + tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows; + qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + } else { + if (dirty) { + // otherwise, just subimage upload it so that drivers can tell we are going to be changing + // it and don't try and do a texture compression + qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data ); + } + } + + if ( r_speeds->integer ) { + end = ri.Milliseconds(); + ri.Printf( PRINT_ALL, "qglTexSubImage2D %i, %i: %i msec\n", cols, rows, end - start ); + } + + // FIXME: HUGE hack + if (glRefConfig.framebufferObject) + { + if (!tr.renderFbo || backEnd.framePostProcessed) + { + FBO_Bind(tr.screenScratchFbo); + } + else + { + FBO_Bind(tr.renderFbo); + } + } + + RB_SetGL2D(); + + tess.numIndexes = 0; + tess.numVertexes = 0; + tess.firstIndex = 0; + tess.minIndex = 0; + tess.maxIndex = 0; + + tess.xyz[tess.numVertexes][0] = x; + tess.xyz[tess.numVertexes][1] = y; + tess.xyz[tess.numVertexes][2] = 0; + tess.xyz[tess.numVertexes][3] = 1; + tess.texCoords[tess.numVertexes][0][0] = 0.5f / cols; + tess.texCoords[tess.numVertexes][0][1] = 0.5f / rows; + tess.texCoords[tess.numVertexes][1][0] = 0; + tess.texCoords[tess.numVertexes][1][1] = 1; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = x + w; + tess.xyz[tess.numVertexes][1] = y; + tess.xyz[tess.numVertexes][2] = 0; + tess.xyz[tess.numVertexes][3] = 1; + tess.texCoords[tess.numVertexes][0][0] = (cols - 0.5f) / cols; + tess.texCoords[tess.numVertexes][0][1] = 0.5f / rows; + tess.texCoords[tess.numVertexes][1][0] = 0; + tess.texCoords[tess.numVertexes][1][1] = 1; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = x + w; + tess.xyz[tess.numVertexes][1] = y + h; + tess.xyz[tess.numVertexes][2] = 0; + tess.xyz[tess.numVertexes][3] = 1; + tess.texCoords[tess.numVertexes][0][0] = (cols - 0.5f) / cols; + tess.texCoords[tess.numVertexes][0][1] = (rows - 0.5f) / rows; + tess.texCoords[tess.numVertexes][1][0] = 0; + tess.texCoords[tess.numVertexes][1][1] = 1; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = x; + tess.xyz[tess.numVertexes][1] = y + h; + tess.xyz[tess.numVertexes][2] = 0; + tess.xyz[tess.numVertexes][3] = 1; + tess.texCoords[tess.numVertexes][0][0] = 0.5f / cols; + tess.texCoords[tess.numVertexes][0][1] = (rows - 0.5f) / rows; + tess.texCoords[tess.numVertexes][1][0] = 0; + tess.texCoords[tess.numVertexes][1][1] = 1; + tess.numVertexes++; + + tess.indexes[tess.numIndexes++] = 0; + tess.indexes[tess.numIndexes++] = 1; + tess.indexes[tess.numIndexes++] = 2; + tess.indexes[tess.numIndexes++] = 0; + tess.indexes[tess.numIndexes++] = 2; + tess.indexes[tess.numIndexes++] = 3; + tess.minIndex = 0; + tess.maxIndex = 3; + + // FIXME: A lot of this can probably be removed for speed, and refactored into a more convenient function + RB_UpdateVBOs(ATTR_POSITION | ATTR_TEXCOORD); + + sp = &tr.textureColorShader; + + GLSL_VertexAttribsState(ATTR_POSITION | ATTR_TEXCOORD); + + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + VectorSet4(color, 1, 1, 1, 1); + GLSL_SetUniformVec4(sp, TEXTURECOLOR_UNIFORM_COLOR, color); + + R_DrawElementsVBO(tess.numIndexes, tess.firstIndex, tess.minIndex, tess.maxIndex); + + //R_BindNullVBO(); + //R_BindNullIBO(); + + tess.numIndexes = 0; + tess.numVertexes = 0; + tess.firstIndex = 0; + tess.minIndex = 0; + tess.maxIndex = 0; +} + +void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty) { + + GL_Bind( tr.scratchImage[client] ); + + // if the scratchImage isn't in the format we want, specify it as a new texture + if ( cols != tr.scratchImage[client]->width || rows != tr.scratchImage[client]->height ) { + tr.scratchImage[client]->width = tr.scratchImage[client]->uploadWidth = cols; + tr.scratchImage[client]->height = tr.scratchImage[client]->uploadHeight = rows; + qglTexImage2D( GL_TEXTURE_2D, 0, GL_RGB8, cols, rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, data ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); + } else { + if (dirty) { + // otherwise, just subimage upload it so that drivers can tell we are going to be changing + // it and don't try and do a texture compression + qglTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, cols, rows, GL_RGBA, GL_UNSIGNED_BYTE, data ); + } + } +} + + +/* +============= +RB_SetColor + +============= +*/ +const void *RB_SetColor( const void *data ) { + const setColorCommand_t *cmd; + + cmd = (const setColorCommand_t *)data; + + backEnd.color2D[0] = cmd->color[0] * 255; + backEnd.color2D[1] = cmd->color[1] * 255; + backEnd.color2D[2] = cmd->color[2] * 255; + backEnd.color2D[3] = cmd->color[3] * 255; + + return (const void *)(cmd + 1); +} + +/* +============= +RB_StretchPic +============= +*/ +const void *RB_StretchPic ( const void *data ) { + const stretchPicCommand_t *cmd; + shader_t *shader; + int numVerts, numIndexes; + + cmd = (const stretchPicCommand_t *)data; + + // FIXME: HUGE hack + if (glRefConfig.framebufferObject) + { + if (!tr.renderFbo || backEnd.framePostProcessed) + { + FBO_Bind(tr.screenScratchFbo); + } + else + { + FBO_Bind(tr.renderFbo); + } + } + + RB_SetGL2D(); + + shader = cmd->shader; + if ( shader != tess.shader ) { + if ( tess.numIndexes ) { + RB_EndSurface(); + } + backEnd.currentEntity = &backEnd.entity2D; + RB_BeginSurface( shader, 0 ); + } + + RB_CHECKOVERFLOW( 4, 6 ); + numVerts = tess.numVertexes; + numIndexes = tess.numIndexes; + + tess.numVertexes += 4; + tess.numIndexes += 6; + + tess.indexes[ numIndexes ] = numVerts + 3; + tess.indexes[ numIndexes + 1 ] = numVerts + 0; + tess.indexes[ numIndexes + 2 ] = numVerts + 2; + tess.indexes[ numIndexes + 3 ] = numVerts + 2; + tess.indexes[ numIndexes + 4 ] = numVerts + 0; + tess.indexes[ numIndexes + 5 ] = numVerts + 1; + + { + vec4_t color; + + VectorScale4(backEnd.color2D, 1.0f / 255.0f, color); + + VectorCopy4(color, tess.vertexColors[ numVerts ]); + VectorCopy4(color, tess.vertexColors[ numVerts + 1]); + VectorCopy4(color, tess.vertexColors[ numVerts + 2]); + VectorCopy4(color, tess.vertexColors[ numVerts + 3 ]); + } + + tess.xyz[ numVerts ][0] = cmd->x; + tess.xyz[ numVerts ][1] = cmd->y; + tess.xyz[ numVerts ][2] = 0; + + tess.texCoords[ numVerts ][0][0] = cmd->s1; + tess.texCoords[ numVerts ][0][1] = cmd->t1; + + tess.xyz[ numVerts + 1 ][0] = cmd->x + cmd->w; + tess.xyz[ numVerts + 1 ][1] = cmd->y; + tess.xyz[ numVerts + 1 ][2] = 0; + + tess.texCoords[ numVerts + 1 ][0][0] = cmd->s2; + tess.texCoords[ numVerts + 1 ][0][1] = cmd->t1; + + tess.xyz[ numVerts + 2 ][0] = cmd->x + cmd->w; + tess.xyz[ numVerts + 2 ][1] = cmd->y + cmd->h; + tess.xyz[ numVerts + 2 ][2] = 0; + + tess.texCoords[ numVerts + 2 ][0][0] = cmd->s2; + tess.texCoords[ numVerts + 2 ][0][1] = cmd->t2; + + tess.xyz[ numVerts + 3 ][0] = cmd->x; + tess.xyz[ numVerts + 3 ][1] = cmd->y + cmd->h; + tess.xyz[ numVerts + 3 ][2] = 0; + + tess.texCoords[ numVerts + 3 ][0][0] = cmd->s1; + tess.texCoords[ numVerts + 3 ][0][1] = cmd->t2; + + return (const void *)(cmd + 1); +} + + +/* +============= +RB_DrawSurfs + +============= +*/ +const void *RB_DrawSurfs( const void *data ) { + const drawSurfsCommand_t *cmd; + + // finish any 2D drawing if needed + if ( tess.numIndexes ) { + RB_EndSurface(); + } + + cmd = (const drawSurfsCommand_t *)data; + + backEnd.refdef = cmd->refdef; + backEnd.viewParms = cmd->viewParms; + + // clear the z buffer, set the modelview, etc + RB_BeginDrawingView (); + + if (glRefConfig.framebufferObject && (backEnd.viewParms.flags & VPF_DEPTHCLAMP) && glRefConfig.depthClamp) + { + qglEnable(GL_DEPTH_CLAMP); + } + + if (glRefConfig.framebufferObject && !(backEnd.refdef.rdflags & RDF_NOWORLDMODEL) && (r_depthPrepass->integer || (backEnd.viewParms.flags & VPF_DEPTHSHADOW))) + { + FBO_t *oldFbo = glState.currentFBO; + + backEnd.depthFill = qtrue; + qglColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + RB_RenderDrawSurfList( cmd->drawSurfs, cmd->numDrawSurfs ); + qglColorMask(!backEnd.colorMask[0], !backEnd.colorMask[1], !backEnd.colorMask[2], !backEnd.colorMask[3]); + backEnd.depthFill = qfalse; + + if (tr.msaaResolveFbo) + { + // If we're using multisampling, resolve the depth first + FBO_FastBlit(tr.renderFbo, NULL, tr.msaaResolveFbo, NULL, GL_DEPTH_BUFFER_BIT, GL_NEAREST); + } + else if (tr.renderFbo == NULL) + { + // If we're rendering directly to the screen, copy the depth to a texture + GL_BindToTMU(tr.renderDepthImage, 0); + qglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, 0, 0, glConfig.vidWidth, glConfig.vidHeight, 0); + } + + if (r_ssao->integer) + { + // need the depth in a texture we can do GL_LINEAR sampling on, so copy it to an HDR image + FBO_BlitFromTexture(tr.renderDepthImage, NULL, NULL, tr.hdrDepthFbo, NULL, NULL, NULL, 0); + } + + if (backEnd.viewParms.flags & VPF_USESUNLIGHT) + { + vec4_t quadVerts[4]; + vec2_t texCoords[4]; + vec4_t box; + + FBO_Bind(tr.screenShadowFbo); + + box[0] = (backEnd.refdef.x ) * tr.screenShadowFbo->width / (float)glConfig.vidWidth; + box[1] = (backEnd.refdef.y ) * tr.screenShadowFbo->height / (float)glConfig.vidHeight; + box[2] = (backEnd.refdef.width ) * tr.screenShadowFbo->width / (float)glConfig.vidWidth; + box[3] = (backEnd.refdef.height) * tr.screenShadowFbo->height / (float)glConfig.vidHeight; + + qglViewport(box[0], box[1], box[2], box[3]); + qglScissor(box[0], box[1], box[2], box[3]); + + box[0] = (backEnd.refdef.x ) / (float)glConfig.vidWidth; + box[1] = (backEnd.refdef.y ) / (float)glConfig.vidHeight; + box[2] = (backEnd.refdef.x + backEnd.refdef.width ) / (float)glConfig.vidWidth; + box[3] = (backEnd.refdef.y + backEnd.refdef.height) / (float)glConfig.vidHeight; + + texCoords[0][0] = box[0]; texCoords[0][1] = box[3]; + texCoords[1][0] = box[2]; texCoords[1][1] = box[3]; + texCoords[2][0] = box[2]; texCoords[2][1] = box[1]; + texCoords[3][0] = box[0]; texCoords[3][1] = box[1]; + + box[0] = -1.0f; + box[1] = -1.0f; + box[2] = 1.0f; + box[3] = 1.0f; + + VectorSet4(quadVerts[0], box[0], box[3], 0, 1); + VectorSet4(quadVerts[1], box[2], box[3], 0, 1); + VectorSet4(quadVerts[2], box[2], box[1], 0, 1); + VectorSet4(quadVerts[3], box[0], box[1], 0, 1); + + GL_State( GLS_DEPTHTEST_DISABLE ); + + GLSL_BindProgram(&tr.shadowmaskShader); + + GL_BindToTMU(tr.renderDepthImage, TB_COLORMAP); + GL_BindToTMU(tr.sunShadowDepthImage[0], TB_SHADOWMAP); + GL_BindToTMU(tr.sunShadowDepthImage[1], TB_SHADOWMAP2); + GL_BindToTMU(tr.sunShadowDepthImage[2], TB_SHADOWMAP3); + + GLSL_SetUniformMatrix16(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMVP, backEnd.refdef.sunShadowMvp[0]); + GLSL_SetUniformMatrix16(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMVP2, backEnd.refdef.sunShadowMvp[1]); + GLSL_SetUniformMatrix16(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMVP3, backEnd.refdef.sunShadowMvp[2]); + + GLSL_SetUniformVec3(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWORIGIN, backEnd.refdef.vieworg); + { + vec4_t viewInfo; + vec3_t viewVector; + + float zmax = backEnd.viewParms.zFar; + float ymax = zmax * tan(backEnd.viewParms.fovY * M_PI / 360.0f); + float xmax = zmax * tan(backEnd.viewParms.fovX * M_PI / 360.0f); + + float zmin = r_znear->value; + + VectorScale(backEnd.refdef.viewaxis[0], zmax, viewVector); + GLSL_SetUniformVec3(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWFORWARD, viewVector); + VectorScale(backEnd.refdef.viewaxis[1], xmax, viewVector); + GLSL_SetUniformVec3(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWLEFT, viewVector); + VectorScale(backEnd.refdef.viewaxis[2], ymax, viewVector); + GLSL_SetUniformVec3(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWUP, viewVector); + + VectorSet4(viewInfo, zmax / zmin, zmax, 0.0, 0.0); + + GLSL_SetUniformVec4(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWINFO, viewInfo); + } + + + RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes); + } + + if (r_ssao->integer) + { + vec4_t quadVerts[4]; + vec2_t texCoords[4]; + + FBO_Bind(tr.quarterFbo[0]); + + qglViewport(0, 0, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height); + qglScissor(0, 0, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height); + + VectorSet4(quadVerts[0], -1, 1, 0, 1); + VectorSet4(quadVerts[1], 1, 1, 0, 1); + VectorSet4(quadVerts[2], 1, -1, 0, 1); + VectorSet4(quadVerts[3], -1, -1, 0, 1); + + texCoords[0][0] = 0; texCoords[0][1] = 1; + texCoords[1][0] = 1; texCoords[1][1] = 1; + texCoords[2][0] = 1; texCoords[2][1] = 0; + texCoords[3][0] = 0; texCoords[3][1] = 0; + + GL_State( GLS_DEPTHTEST_DISABLE ); + + GLSL_BindProgram(&tr.ssaoShader); + + GL_BindToTMU(tr.hdrDepthImage, TB_COLORMAP); + + { + vec4_t viewInfo; + + float zmax = backEnd.viewParms.zFar; + float zmin = r_znear->value; + + VectorSet4(viewInfo, zmax / zmin, zmax, 0.0, 0.0); + + GLSL_SetUniformVec4(&tr.ssaoShader, SSAO_UNIFORM_VIEWINFO, viewInfo); + } + + RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes); + + + FBO_Bind(tr.quarterFbo[1]); + + qglViewport(0, 0, tr.quarterFbo[1]->width, tr.quarterFbo[1]->height); + qglScissor(0, 0, tr.quarterFbo[1]->width, tr.quarterFbo[1]->height); + + GLSL_BindProgram(&tr.depthBlurShader[0]); + + GL_BindToTMU(tr.quarterImage[0], TB_COLORMAP); + GL_BindToTMU(tr.hdrDepthImage, TB_LIGHTMAP); + + { + vec4_t viewInfo; + + float zmax = backEnd.viewParms.zFar; + float zmin = r_znear->value; + + VectorSet4(viewInfo, zmax / zmin, zmax, 0.0, 0.0); + + GLSL_SetUniformVec4(&tr.depthBlurShader[0], DEPTHBLUR_UNIFORM_VIEWINFO, viewInfo); + } + + RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes); + + + FBO_Bind(tr.screenSsaoFbo); + + qglViewport(0, 0, tr.screenSsaoFbo->width, tr.screenSsaoFbo->height); + qglScissor(0, 0, tr.screenSsaoFbo->width, tr.screenSsaoFbo->height); + + GLSL_BindProgram(&tr.depthBlurShader[1]); + + GL_BindToTMU(tr.quarterImage[1], TB_COLORMAP); + GL_BindToTMU(tr.hdrDepthImage, TB_LIGHTMAP); + + { + vec4_t viewInfo; + + float zmax = backEnd.viewParms.zFar; + float zmin = r_znear->value; + + VectorSet4(viewInfo, zmax / zmin, zmax, 0.0, 0.0); + + GLSL_SetUniformVec4(&tr.depthBlurShader[1], DEPTHBLUR_UNIFORM_VIEWINFO, viewInfo); + } + + + RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes); + } + + // reset viewport and scissor + FBO_Bind(oldFbo); + SetViewportAndScissor(); + } + + if (glRefConfig.framebufferObject && (backEnd.viewParms.flags & VPF_DEPTHCLAMP) && glRefConfig.depthClamp) + { + qglDisable(GL_DEPTH_CLAMP); + } + + if (!(backEnd.viewParms.flags & VPF_DEPTHSHADOW)) + { + RB_RenderDrawSurfList( cmd->drawSurfs, cmd->numDrawSurfs ); + + if (r_drawSun->integer) + { + RB_DrawSun(0.1, tr.sunShader); + } + + if (r_drawSunRays->integer) + { + FBO_t *oldFbo = glState.currentFBO; + FBO_Bind(tr.sunRaysFbo); + + qglClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); + qglClear( GL_COLOR_BUFFER_BIT ); + + if (glRefConfig.occlusionQuery) + { + tr.sunFlareQueryActive[tr.sunFlareQueryIndex] = qtrue; + qglBeginQueryARB(GL_SAMPLES_PASSED_ARB, tr.sunFlareQuery[tr.sunFlareQueryIndex]); + } + + RB_DrawSun(0.3, tr.sunFlareShader); + + if (glRefConfig.occlusionQuery) + { + qglEndQueryARB(GL_SAMPLES_PASSED_ARB); + } + + FBO_Bind(oldFbo); + } + + // darken down any stencil shadows + RB_ShadowFinish(); + + // add light flares on lights that aren't obscured + RB_RenderFlares(); + } + + //if (glRefConfig.framebufferObject) + //FBO_Bind(NULL); + + return (const void *)(cmd + 1); +} + + +/* +============= +RB_DrawBuffer + +============= +*/ +const void *RB_DrawBuffer( const void *data ) { + const drawBufferCommand_t *cmd; + + cmd = (const drawBufferCommand_t *)data; + + // finish any 2D drawing if needed + if(tess.numIndexes) + RB_EndSurface(); + + if (glRefConfig.framebufferObject) + FBO_Bind(NULL); + + qglDrawBuffer( cmd->buffer ); + + // clear screen for debugging + if ( r_clear->integer ) { + qglClearColor( 1, 0, 0.5, 1 ); + qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + } + + return (const void *)(cmd + 1); +} + +/* +=============== +RB_ShowImages + +Draw all the images to the screen, on top of whatever +was there. This is used to test for texture thrashing. + +Also called by RE_EndRegistration +=============== +*/ +void RB_ShowImages( void ) { + int i; + image_t *image; + float x, y, w, h; + int start, end; + + RB_SetGL2D(); + + qglClear( GL_COLOR_BUFFER_BIT ); + + qglFinish(); + + start = ri.Milliseconds(); + + for ( i=0 ; iinteger == 2 ) { + w *= image->uploadWidth / 512.0f; + h *= image->uploadHeight / 512.0f; + } + + { + vec4_t quadVerts[4]; + + GL_Bind(image); + + VectorSet4(quadVerts[0], x, y, 0, 1); + VectorSet4(quadVerts[1], x + w, y, 0, 1); + VectorSet4(quadVerts[2], x + w, y + h, 0, 1); + VectorSet4(quadVerts[3], x, y + h, 0, 1); + + RB_InstantQuad(quadVerts); + } + } + + qglFinish(); + + end = ri.Milliseconds(); + ri.Printf( PRINT_ALL, "%i msec to draw all images\n", end - start ); + +} + +/* +============= +RB_ColorMask + +============= +*/ +const void *RB_ColorMask(const void *data) +{ + const colorMaskCommand_t *cmd = data; + + // finish any 2D drawing if needed + if(tess.numIndexes) + RB_EndSurface(); + + if (glRefConfig.framebufferObject) + { + // reverse color mask, so 0 0 0 0 is the default + backEnd.colorMask[0] = !cmd->rgba[0]; + backEnd.colorMask[1] = !cmd->rgba[1]; + backEnd.colorMask[2] = !cmd->rgba[2]; + backEnd.colorMask[3] = !cmd->rgba[3]; + } + + qglColorMask(cmd->rgba[0], cmd->rgba[1], cmd->rgba[2], cmd->rgba[3]); + + return (const void *)(cmd + 1); +} + +/* +============= +RB_ClearDepth + +============= +*/ +const void *RB_ClearDepth(const void *data) +{ + const clearDepthCommand_t *cmd = data; + + // finish any 2D drawing if needed + if(tess.numIndexes) + RB_EndSurface(); + + // texture swapping test + if (r_showImages->integer) + RB_ShowImages(); + + if (glRefConfig.framebufferObject) + { + if (!tr.renderFbo || backEnd.framePostProcessed) + { + FBO_Bind(tr.screenScratchFbo); + } + else + { + FBO_Bind(tr.renderFbo); + } + } + + qglClear(GL_DEPTH_BUFFER_BIT); + + // if we're doing MSAA, clear the depth texture for the resolve buffer + if (tr.msaaResolveFbo) + { + FBO_Bind(tr.msaaResolveFbo); + qglClear(GL_DEPTH_BUFFER_BIT); + } + + + return (const void *)(cmd + 1); +} + +/* +============= +RB_SwapBuffers + +============= +*/ +const void *RB_SwapBuffers( const void *data ) { + const swapBuffersCommand_t *cmd; + + // finish any 2D drawing if needed + if ( tess.numIndexes ) { + RB_EndSurface(); + } + + // texture swapping test + if ( r_showImages->integer ) { + RB_ShowImages(); + } + + cmd = (const swapBuffersCommand_t *)data; + + // we measure overdraw by reading back the stencil buffer and + // counting up the number of increments that have happened + if ( r_measureOverdraw->integer ) { + int i; + long sum = 0; + unsigned char *stencilReadback; + + stencilReadback = ri.Hunk_AllocateTempMemory( glConfig.vidWidth * glConfig.vidHeight ); + qglReadPixels( 0, 0, glConfig.vidWidth, glConfig.vidHeight, GL_STENCIL_INDEX, GL_UNSIGNED_BYTE, stencilReadback ); + + for ( i = 0; i < glConfig.vidWidth * glConfig.vidHeight; i++ ) { + sum += stencilReadback[i]; + } + + backEnd.pc.c_overDraw += sum; + ri.Hunk_FreeTempMemory( stencilReadback ); + } + + if (glRefConfig.framebufferObject) + { + if (!backEnd.framePostProcessed) + { + if (tr.msaaResolveFbo && r_hdr->integer) + { + // Resolving an RGB16F MSAA FBO to the screen messes with the brightness, so resolve to an RGB16F FBO first + FBO_FastBlit(tr.renderFbo, NULL, tr.msaaResolveFbo, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST); + FBO_FastBlit(tr.msaaResolveFbo, NULL, tr.screenScratchFbo, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST); + } + else if (tr.renderFbo) + { + FBO_FastBlit(tr.renderFbo, NULL, tr.screenScratchFbo, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST); + } + } + + if (tr.screenScratchFbo) + { + vec4_t color; + + color[0] = + color[1] = + color[2] = pow(2, tr.overbrightBits); //exp2(tr.overbrightBits); + color[3] = 1.0f; + + // turn off colormask when copying final image + if (backEnd.colorMask[0] || backEnd.colorMask[1] || backEnd.colorMask[2] || backEnd.colorMask[3]) + qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + FBO_Blit(tr.screenScratchFbo, NULL, NULL, NULL, NULL, NULL, color, 0); + + if (backEnd.colorMask[0] || backEnd.colorMask[1] || backEnd.colorMask[2] || backEnd.colorMask[3]) + qglColorMask(!backEnd.colorMask[0], !backEnd.colorMask[1], !backEnd.colorMask[2], !backEnd.colorMask[3]); + } + } + + if ( !glState.finishCalled ) { + qglFinish(); + } + + GLimp_LogComment( "***************** RB_SwapBuffers *****************\n\n\n" ); + + GLimp_EndFrame(); + + backEnd.framePostProcessed = qfalse; + backEnd.projection2D = qfalse; + + return (const void *)(cmd + 1); +} + +/* +============= +RB_CapShadowMap + +============= +*/ +const void *RB_CapShadowMap(const void *data) +{ + const capShadowmapCommand_t *cmd = data; + + // finish any 2D drawing if needed + if(tess.numIndexes) + RB_EndSurface(); + + if (cmd->map != -1) + { + GL_SelectTexture(0); + if (cmd->cubeSide != -1) + { + GL_BindCubemap(tr.shadowCubemaps[cmd->map]); + qglCopyTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + cmd->cubeSide, 0, GL_RGBA8, backEnd.refdef.x, glConfig.vidHeight - ( backEnd.refdef.y + PSHADOW_MAP_SIZE ), PSHADOW_MAP_SIZE, PSHADOW_MAP_SIZE, 0); + } + else + { + GL_Bind(tr.pshadowMaps[cmd->map]); + qglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, backEnd.refdef.x, glConfig.vidHeight - ( backEnd.refdef.y + PSHADOW_MAP_SIZE ), PSHADOW_MAP_SIZE, PSHADOW_MAP_SIZE, 0); + } + } + + return (const void *)(cmd + 1); +} + + + +/* +============= +RB_PostProcess + +============= +*/ +const void *RB_PostProcess(const void *data) +{ + const postProcessCommand_t *cmd = data; + FBO_t *srcFbo; + qboolean autoExposure; + + // finish any 2D drawing if needed + if(tess.numIndexes) + RB_EndSurface(); + + if (!glRefConfig.framebufferObject || !r_postProcess->integer) + { + // do nothing + return (const void *)(cmd + 1); + } + + srcFbo = tr.renderFbo; + if (tr.msaaResolveFbo) + { + // Resolve the MSAA before anything else + FBO_FastBlit(tr.renderFbo, NULL, tr.msaaResolveFbo, NULL, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST); + srcFbo = tr.msaaResolveFbo; + } + + if (r_ssao->integer) + { + FBO_BlitFromTexture(tr.screenSsaoImage, NULL, NULL, srcFbo, NULL, NULL, NULL, GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO); + } + + if (srcFbo) + { + if (r_hdr->integer && (r_toneMap->integer || r_forceToneMap->integer)) + { + autoExposure = r_autoExposure->integer || r_forceAutoExposure->integer; + RB_ToneMap(srcFbo, autoExposure); + } + else if (r_cameraExposure->value == 0.0f) + { + FBO_FastBlit(srcFbo, NULL, tr.screenScratchFbo, NULL, GL_COLOR_BUFFER_BIT, GL_NEAREST); + } + else + { + vec4_t color; + + color[0] = + color[1] = + color[2] = pow(2, r_cameraExposure->value); //exp2(r_cameraExposure->value); + color[3] = 1.0f; + + FBO_Blit(srcFbo, NULL, NULL, tr.screenScratchFbo, NULL, NULL, color, 0); + } + } + + if (r_drawSunRays->integer) + RB_SunRays(); + + if (1) + RB_BokehBlur(backEnd.refdef.blurFactor); + else + RB_GaussianBlur(backEnd.refdef.blurFactor); + + if (0) + { + vec4i_t dstBox; + VectorSet4(dstBox, 0, 0, 128, 128); + FBO_BlitFromTexture(tr.sunShadowDepthImage[0], NULL, NULL, tr.screenScratchFbo, dstBox, NULL, NULL, 0); + VectorSet4(dstBox, 128, 0, 128, 128); + FBO_BlitFromTexture(tr.sunShadowDepthImage[1], NULL, NULL, tr.screenScratchFbo, dstBox, NULL, NULL, 0); + VectorSet4(dstBox, 256, 0, 128, 128); + FBO_BlitFromTexture(tr.sunShadowDepthImage[2], NULL, NULL, tr.screenScratchFbo, dstBox, NULL, NULL, 0); + } + + if (0) + { + vec4i_t dstBox; + VectorSet4(dstBox, 256, glConfig.vidHeight - 256, 256, 256); + FBO_BlitFromTexture(tr.renderDepthImage, NULL, NULL, tr.screenScratchFbo, dstBox, NULL, NULL, 0); + VectorSet4(dstBox, 512, glConfig.vidHeight - 256, 256, 256); + FBO_BlitFromTexture(tr.screenShadowImage, NULL, NULL, tr.screenScratchFbo, dstBox, NULL, NULL, 0); + } + + if (0) + { + vec4i_t dstBox; + VectorSet4(dstBox, 256, glConfig.vidHeight - 256, 256, 256); + FBO_BlitFromTexture(tr.sunRaysImage, NULL, NULL, tr.screenScratchFbo, dstBox, NULL, NULL, 0); + } + + backEnd.framePostProcessed = qtrue; + + return (const void *)(cmd + 1); +} + +/* +==================== +RB_ExecuteRenderCommands +==================== +*/ +void RB_ExecuteRenderCommands( const void *data ) { + int t1, t2; + + t1 = ri.Milliseconds (); + + while ( 1 ) { + data = PADP(data, sizeof(void *)); + + switch ( *(const int *)data ) { + case RC_SET_COLOR: + data = RB_SetColor( data ); + break; + case RC_STRETCH_PIC: + data = RB_StretchPic( data ); + break; + case RC_DRAW_SURFS: + data = RB_DrawSurfs( data ); + break; + case RC_DRAW_BUFFER: + data = RB_DrawBuffer( data ); + break; + case RC_SWAP_BUFFERS: + data = RB_SwapBuffers( data ); + break; + case RC_SCREENSHOT: + data = RB_TakeScreenshotCmd( data ); + break; + case RC_VIDEOFRAME: + data = RB_TakeVideoFrameCmd( data ); + break; + case RC_COLORMASK: + data = RB_ColorMask(data); + break; + case RC_CLEARDEPTH: + data = RB_ClearDepth(data); + break; + case RC_CAPSHADOWMAP: + data = RB_CapShadowMap(data); + break; + case RC_POSTPROCESS: + data = RB_PostProcess(data); + break; + case RC_END_OF_LIST: + default: + // finish any 2D drawing if needed + if(tess.numIndexes) + RB_EndSurface(); + + // stop rendering + t2 = ri.Milliseconds (); + backEnd.pc.msec = t2 - t1; + return; + } + } + +} diff --git a/src/renderergl2/tr_bsp.c b/src/renderergl2/tr_bsp.c new file mode 100644 index 00000000..c14245b8 --- /dev/null +++ b/src/renderergl2/tr_bsp.c @@ -0,0 +1,3370 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_map.c + +#include "tr_local.h" + +/* + +Loads and prepares a map file for scene rendering. + +A single entry point: + +void RE_LoadWorldMap( const char *name ); + +*/ + +static world_t s_worldData; +static byte *fileBase; + +int c_subdivisions; +int c_gridVerts; + +//=============================================================================== + +static void HSVtoRGB( float h, float s, float v, float rgb[3] ) +{ + int i; + float f; + float p, q, t; + + h *= 5; + + i = floor( h ); + f = h - i; + + p = v * ( 1 - s ); + q = v * ( 1 - s * f ); + t = v * ( 1 - s * ( 1 - f ) ); + + switch ( i ) + { + case 0: + rgb[0] = v; + rgb[1] = t; + rgb[2] = p; + break; + case 1: + rgb[0] = q; + rgb[1] = v; + rgb[2] = p; + break; + case 2: + rgb[0] = p; + rgb[1] = v; + rgb[2] = t; + break; + case 3: + rgb[0] = p; + rgb[1] = q; + rgb[2] = v; + break; + case 4: + rgb[0] = t; + rgb[1] = p; + rgb[2] = v; + break; + case 5: + rgb[0] = v; + rgb[1] = p; + rgb[2] = q; + break; + } +} + +/* +=============== +R_ColorShiftLightingBytes + +=============== +*/ +static void R_ColorShiftLightingBytes( byte in[4], byte out[4] ) { + int shift, r, g, b; + + // shift the color data based on overbright range + shift = r_mapOverBrightBits->integer - tr.overbrightBits; + + // shift the data based on overbright range + r = in[0] << shift; + g = in[1] << shift; + b = in[2] << shift; + + // normalize by color instead of saturating to white + if ( ( r | g | b ) > 255 ) { + int max; + + max = r > g ? r : g; + max = max > b ? max : b; + r = r * 255 / max; + g = g * 255 / max; + b = b * 255 / max; + } + + out[0] = r; + out[1] = g; + out[2] = b; + out[3] = in[3]; +} + + +/* +=============== +R_ColorShiftLightingBytes + +=============== +*/ +static void R_ColorShiftLightingFloats(float in[4], float out[4], float scale ) +{ + scale *= pow(2.0f, r_mapOverBrightBits->integer - tr.overbrightBits); + + out[0] = in[0] * scale; + out[1] = in[1] * scale; + out[2] = in[2] * scale; + out[3] = in[3]; +} + + +void ColorToRGBE(const vec3_t color, unsigned char rgbe[4]) +{ + vec3_t sample; + float maxComponent; + int e; + + VectorCopy(color, sample); + + maxComponent = sample[0]; + if(sample[1] > maxComponent) + maxComponent = sample[1]; + if(sample[2] > maxComponent) + maxComponent = sample[2]; + + if(maxComponent < 1e-32) + { + rgbe[0] = 0; + rgbe[1] = 0; + rgbe[2] = 0; + rgbe[3] = 0; + } + else + { +#if 0 + maxComponent = frexp(maxComponent, &e) * 255.0 / maxComponent; + rgbe[0] = (unsigned char) (sample[0] * maxComponent); + rgbe[1] = (unsigned char) (sample[1] * maxComponent); + rgbe[2] = (unsigned char) (sample[2] * maxComponent); + rgbe[3] = (unsigned char) (e + 128); +#else + e = ceil(log(maxComponent) / log(2.0f));//ceil(log2(maxComponent)); + VectorScale(sample, 1.0 / pow(2.0f, e)/*exp2(e)*/, sample); + + rgbe[0] = (unsigned char) (sample[0] * 255); + rgbe[1] = (unsigned char) (sample[1] * 255); + rgbe[2] = (unsigned char) (sample[2] * 255); + rgbe[3] = (unsigned char) (e + 128); +#endif + } +} + + +void ColorToRGBA16F(const vec3_t color, unsigned short rgba16f[4]) +{ + rgba16f[0] = FloatToHalf(color[0]); + rgba16f[1] = FloatToHalf(color[1]); + rgba16f[2] = FloatToHalf(color[2]); + rgba16f[3] = FloatToHalf(1.0f); +} + + +/* +=============== +R_LoadLightmaps + +=============== +*/ +#define DEFAULT_LIGHTMAP_SIZE 128 +#define MAX_LIGHTMAP_PAGES 2 +static void R_LoadLightmaps( lump_t *l, lump_t *surfs ) { + byte *buf, *buf_p; + dsurface_t *surf; + int len; + byte *image; + int i, j, numLightmaps, textureInternalFormat = 0; + float maxIntensity = 0; + double sumIntensity = 0; + + len = l->filelen; + if ( !len ) { + return; + } + buf = fileBase + l->fileofs; + + // we are about to upload textures + R_IssuePendingRenderCommands(); + + tr.lightmapSize = DEFAULT_LIGHTMAP_SIZE; + numLightmaps = len / (tr.lightmapSize * tr.lightmapSize * 3); + + // check for deluxe mapping + if (numLightmaps <= 1) + { + tr.worldDeluxeMapping = qfalse; + } + else + { + tr.worldDeluxeMapping = qtrue; + for( i = 0, surf = (dsurface_t *)(fileBase + surfs->fileofs); + i < surfs->filelen / sizeof(dsurface_t); i++, surf++ ) { + int lightmapNum = LittleLong( surf->lightmapNum ); + + if ( lightmapNum >= 0 && (lightmapNum & 1) != 0 ) { + tr.worldDeluxeMapping = qfalse; + break; + } + } + } + + image = ri.Malloc(tr.lightmapSize * tr.lightmapSize * 4 * 2); + + if (tr.worldDeluxeMapping) + numLightmaps >>= 1; + + if(numLightmaps == 1) + { + //FIXME: HACK: maps with only one lightmap turn up fullbright for some reason. + //this avoids this, but isn't the correct solution. + numLightmaps++; + } + else if (r_mergeLightmaps->integer && numLightmaps >= 1024 ) + { + // FIXME: fat light maps don't support more than 1024 light maps + ri.Printf(PRINT_WARNING, "WARNING: number of lightmaps > 1024\n"); + numLightmaps = 1024; + } + + // use fat lightmaps of an appropriate size + if (r_mergeLightmaps->integer) + { + tr.fatLightmapSize = 512; + tr.fatLightmapStep = tr.fatLightmapSize / tr.lightmapSize; + + // at most MAX_LIGHTMAP_PAGES + while (tr.fatLightmapStep * tr.fatLightmapStep * MAX_LIGHTMAP_PAGES < numLightmaps && tr.fatLightmapSize != glConfig.maxTextureSize ) + { + tr.fatLightmapSize <<= 1; + tr.fatLightmapStep = tr.fatLightmapSize / tr.lightmapSize; + } + + tr.numLightmaps = numLightmaps / (tr.fatLightmapStep * tr.fatLightmapStep); + + if (numLightmaps % (tr.fatLightmapStep * tr.fatLightmapStep) != 0) + tr.numLightmaps++; + } + else + { + tr.numLightmaps = numLightmaps; + } + + tr.lightmaps = ri.Hunk_Alloc( tr.numLightmaps * sizeof(image_t *), h_low ); + + if (tr.worldDeluxeMapping) + { + tr.deluxemaps = ri.Hunk_Alloc( tr.numLightmaps * sizeof(image_t *), h_low ); + } + + if (r_hdr->integer && glRefConfig.textureFloat && glRefConfig.halfFloatPixel) + textureInternalFormat = GL_RGBA16F_ARB; + + if (r_mergeLightmaps->integer) + { + for (i = 0; i < tr.numLightmaps; i++) + { + tr.lightmaps[i] = R_CreateImage(va("_fatlightmap%d", i), NULL, tr.fatLightmapSize, tr.fatLightmapSize, IMGTYPE_COLORALPHA, IMGFLAG_NOLIGHTSCALE | IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, textureInternalFormat ); + + if (tr.worldDeluxeMapping) + { + tr.deluxemaps[i] = R_CreateImage(va("_fatdeluxemap%d", i), NULL, tr.fatLightmapSize, tr.fatLightmapSize, IMGTYPE_DELUXE, IMGFLAG_NOLIGHTSCALE | IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, 0 ); + } + } + } + + for(i = 0; i < numLightmaps; i++) + { + int xoff = 0, yoff = 0; + int lightmapnum = i; + // expand the 24 bit on-disk to 32 bit + + if (r_mergeLightmaps->integer) + { + int lightmaponpage = i % (tr.fatLightmapStep * tr.fatLightmapStep); + xoff = (lightmaponpage % tr.fatLightmapStep) * tr.lightmapSize; + yoff = (lightmaponpage / tr.fatLightmapStep) * tr.lightmapSize; + + lightmapnum /= (tr.fatLightmapStep * tr.fatLightmapStep); + } + + // if (tr.worldLightmapping) + { + char filename[MAX_QPATH]; + byte *hdrLightmap = NULL; + float lightScale = 1.0f; + int size = 0; + + // look for hdr lightmaps + if (r_hdr->integer) + { + Com_sprintf( filename, sizeof( filename ), "maps/%s/lm_%04d.hdr", s_worldData.baseName, i * (tr.worldDeluxeMapping ? 2 : 1) ); + //ri.Printf(PRINT_ALL, "looking for %s\n", filename); + + size = ri.FS_ReadFile(filename, (void **)&hdrLightmap); + } + + if (hdrLightmap) + { + byte *p = hdrLightmap; + //ri.Printf(PRINT_ALL, "found!\n"); + + /* FIXME: don't just skip over this header and actually parse it */ + while (size && !(*p == '\n' && *(p+1) == '\n')) + { + size--; + p++; + } + + if (!size) + ri.Error(ERR_DROP, "Bad header for %s!\n", filename); + + size -= 2; + p += 2; + + while (size && !(*p == '\n')) + { + size--; + p++; + } + + size--; + p++; + + buf_p = (byte *)p; + +#if 0 // HDRFILE_RGBE + if (size != tr.lightmapSize * tr.lightmapSize * 4) + ri.Error(ERR_DROP, "Bad size for %s (%i)!\n", filename, size); +#else // HDRFILE_FLOAT + if (size != tr.lightmapSize * tr.lightmapSize * 12) + ri.Error(ERR_DROP, "Bad size for %s (%i)!\n", filename, size); +#endif + } + else + { + if (tr.worldDeluxeMapping) + buf_p = buf + (i * 2) * tr.lightmapSize * tr.lightmapSize * 3; + else + buf_p = buf + i * tr.lightmapSize * tr.lightmapSize * 3; + } + + lightScale = pow(2, r_mapOverBrightBits->integer - tr.overbrightBits - 8); //exp2(r_mapOverBrightBits->integer - tr.overbrightBits - 8); + + for ( j = 0 ; j < tr.lightmapSize * tr.lightmapSize; j++ ) + { + if (r_hdr->integer) + { + float color[3]; + + if (hdrLightmap) + { +#if 0 // HDRFILE_RGBE + float exponent = exp2(buf_p[j*4+3] - 128); + + color[0] = buf_p[j*4+0] * exponent; + color[1] = buf_p[j*4+1] * exponent; + color[2] = buf_p[j*4+2] * exponent; +#else // HDRFILE_FLOAT + memcpy(color, &buf_p[j*12], 12); + + color[0] = LittleFloat(color[0]); + color[1] = LittleFloat(color[1]); + color[2] = LittleFloat(color[2]); +#endif + } + else + { + //hack: convert LDR lightmap to HDR one + color[0] = (buf_p[j*3+0] + 1.0f); + color[1] = (buf_p[j*3+1] + 1.0f); + color[2] = (buf_p[j*3+2] + 1.0f); + + // if under an arbitrary value (say 12) grey it out + // this prevents weird splotches in dimly lit areas + if (color[0] + color[1] + color[2] < 12.0f) + { + float avg = (color[0] + color[1] + color[2]) * 0.3333f; + color[0] = avg; + color[1] = avg; + color[2] = avg; + } + } + + VectorScale(color, lightScale, color); + + if (glRefConfig.textureFloat && glRefConfig.halfFloatPixel) + ColorToRGBA16F(color, (unsigned short *)(&image[j*8])); + else + ColorToRGBE(color, &image[j*4]); + } + else + { + if ( r_lightmap->integer == 2 ) + { // color code by intensity as development tool (FIXME: check range) + float r = buf_p[j*3+0]; + float g = buf_p[j*3+1]; + float b = buf_p[j*3+2]; + float intensity; + float out[3] = {0.0, 0.0, 0.0}; + + intensity = 0.33f * r + 0.685f * g + 0.063f * b; + + if ( intensity > 255 ) + intensity = 1.0f; + else + intensity /= 255.0f; + + if ( intensity > maxIntensity ) + maxIntensity = intensity; + + HSVtoRGB( intensity, 1.00, 0.50, out ); + + image[j*4+0] = out[0] * 255; + image[j*4+1] = out[1] * 255; + image[j*4+2] = out[2] * 255; + image[j*4+3] = 255; + + sumIntensity += intensity; + } + else + { + R_ColorShiftLightingBytes( &buf_p[j*3], &image[j*4] ); + image[j*4+3] = 255; + } + } + } + + if (r_mergeLightmaps->integer) + R_UpdateSubImage(tr.lightmaps[lightmapnum], image, xoff, yoff, tr.lightmapSize, tr.lightmapSize); + else + tr.lightmaps[i] = R_CreateImage(va("*lightmap%d", i), image, tr.lightmapSize, tr.lightmapSize, IMGTYPE_COLORALPHA, IMGFLAG_NOLIGHTSCALE | IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, textureInternalFormat ); + + if (hdrLightmap) + ri.FS_FreeFile(hdrLightmap); + } + + if (tr.worldDeluxeMapping) + { + buf_p = buf + (i * 2 + 1) * tr.lightmapSize * tr.lightmapSize * 3; + + for ( j = 0 ; j < tr.lightmapSize * tr.lightmapSize; j++ ) { + image[j*4+0] = buf_p[j*3+0]; + image[j*4+1] = buf_p[j*3+1]; + image[j*4+2] = buf_p[j*3+2]; + + // make 0,0,0 into 127,127,127 + if ((image[j*4+0] == 0) && (image[j*4+0] == 0) && (image[j*4+2] == 0)) + { + image[j*4+0] = + image[j*4+1] = + image[j*4+2] = 127; + } + + image[j*4+3] = 255; + } + + if (r_mergeLightmaps->integer) + { + R_UpdateSubImage(tr.deluxemaps[lightmapnum], image, xoff, yoff, tr.lightmapSize, tr.lightmapSize ); + } + else + { + tr.deluxemaps[i] = R_CreateImage(va("*deluxemap%d", i), image, tr.lightmapSize, tr.lightmapSize, IMGTYPE_DELUXE, IMGFLAG_NOLIGHTSCALE | IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, 0 ); + } + } + } + + if ( r_lightmap->integer == 2 ) { + ri.Printf( PRINT_ALL, "Brightest lightmap value: %d\n", ( int ) ( maxIntensity * 255 ) ); + } + + ri.Free(image); +} + + +static float FatPackU(float input, int lightmapnum) +{ + if (lightmapnum < 0) + return input; + + if (tr.worldDeluxeMapping) + lightmapnum >>= 1; + + lightmapnum %= (tr.fatLightmapStep * tr.fatLightmapStep); + + if(tr.fatLightmapSize > 0) + { + int x = lightmapnum % tr.fatLightmapStep; + + return (input / ((float)tr.fatLightmapStep)) + ((1.0 / ((float)tr.fatLightmapStep)) * (float)x); + } + + return input; +} + +static float FatPackV(float input, int lightmapnum) +{ + if (lightmapnum < 0) + return input; + + if (tr.worldDeluxeMapping) + lightmapnum >>= 1; + + lightmapnum %= (tr.fatLightmapStep * tr.fatLightmapStep); + + if(tr.fatLightmapSize > 0) + { + int y = lightmapnum / tr.fatLightmapStep; + + return (input / ((float)tr.fatLightmapStep)) + ((1.0 / ((float)tr.fatLightmapStep)) * (float)y); + } + + return input; +} + + +static int FatLightmap(int lightmapnum) +{ + if (lightmapnum < 0) + return lightmapnum; + + if (tr.worldDeluxeMapping) + lightmapnum >>= 1; + + if (tr.fatLightmapSize > 0) + { + return lightmapnum / (tr.fatLightmapStep * tr.fatLightmapStep); + } + + return lightmapnum; +} + +/* +================= +RE_SetWorldVisData + +This is called by the clipmodel subsystem so we can share the 1.8 megs of +space in big maps... +================= +*/ +void RE_SetWorldVisData( const byte *vis ) { + tr.externalVisData = vis; +} + + +/* +================= +R_LoadVisibility +================= +*/ +static void R_LoadVisibility( lump_t *l ) { + int len; + byte *buf; + + len = ( s_worldData.numClusters + 63 ) & ~63; + s_worldData.novis = ri.Hunk_Alloc( len, h_low ); + Com_Memset( s_worldData.novis, 0xff, len ); + + len = l->filelen; + if ( !len ) { + return; + } + buf = fileBase + l->fileofs; + + s_worldData.numClusters = LittleLong( ((int *)buf)[0] ); + s_worldData.clusterBytes = LittleLong( ((int *)buf)[1] ); + + // CM_Load should have given us the vis data to share, so + // we don't need to allocate another copy + if ( tr.externalVisData ) { + s_worldData.vis = tr.externalVisData; + } else { + byte *dest; + + dest = ri.Hunk_Alloc( len - 8, h_low ); + Com_Memcpy( dest, buf + 8, len - 8 ); + s_worldData.vis = dest; + } +} + +//=============================================================================== + + +/* +=============== +ShaderForShaderNum +=============== +*/ +static shader_t *ShaderForShaderNum( int shaderNum, int lightmapNum ) { + shader_t *shader; + dshader_t *dsh; + + int _shaderNum = LittleLong( shaderNum ); + if ( _shaderNum < 0 || _shaderNum >= s_worldData.numShaders ) { + ri.Error( ERR_DROP, "ShaderForShaderNum: bad num %i", _shaderNum ); + } + dsh = &s_worldData.shaders[ _shaderNum ]; + + if ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) { + lightmapNum = LIGHTMAP_BY_VERTEX; + } + + if ( r_fullbright->integer ) { + lightmapNum = LIGHTMAP_WHITEIMAGE; + } + + shader = R_FindShader( dsh->shader, lightmapNum, qtrue ); + + // if the shader had errors, just use default shader + if ( shader->defaultShader ) { + return tr.defaultShader; + } + + return shader; +} + +/* +=============== +ParseFace +=============== +*/ +static void ParseFace( dsurface_t *ds, drawVert_t *verts, float *hdrVertColors, msurface_t *surf, int *indexes ) { + int i, j; + srfSurfaceFace_t *cv; + srfTriangle_t *tri; + int numVerts, numTriangles, badTriangles; + int realLightmapNum; + + realLightmapNum = LittleLong( ds->lightmapNum ); + + // get fog volume + surf->fogIndex = LittleLong( ds->fogNum ) + 1; + + // get shader value + surf->shader = ShaderForShaderNum( ds->shaderNum, FatLightmap(realLightmapNum) ); + if ( r_singleShader->integer && !surf->shader->isSky ) { + surf->shader = tr.defaultShader; + } + + numVerts = LittleLong(ds->numVerts); + if (numVerts > MAX_FACE_POINTS) { + ri.Printf( PRINT_WARNING, "WARNING: MAX_FACE_POINTS exceeded: %i\n", numVerts); + numVerts = MAX_FACE_POINTS; + surf->shader = tr.defaultShader; + } + + numTriangles = LittleLong(ds->numIndexes) / 3; + + //cv = ri.Hunk_Alloc(sizeof(*cv), h_low); + cv = (void *)surf->data; + cv->surfaceType = SF_FACE; + + cv->numTriangles = numTriangles; + cv->triangles = ri.Hunk_Alloc(numTriangles * sizeof(cv->triangles[0]), h_low); + + cv->numVerts = numVerts; + cv->verts = ri.Hunk_Alloc(numVerts * sizeof(cv->verts[0]), h_low); + + // copy vertexes + surf->cullinfo.type = CULLINFO_PLANE | CULLINFO_BOX; + ClearBounds(surf->cullinfo.bounds[0], surf->cullinfo.bounds[1]); + verts += LittleLong(ds->firstVert); + for(i = 0; i < numVerts; i++) + { + vec4_t color; + + for(j = 0; j < 3; j++) + { + cv->verts[i].xyz[j] = LittleFloat(verts[i].xyz[j]); + cv->verts[i].normal[j] = LittleFloat(verts[i].normal[j]); + } + AddPointToBounds(cv->verts[i].xyz, surf->cullinfo.bounds[0], surf->cullinfo.bounds[1]); + for(j = 0; j < 2; j++) + { + cv->verts[i].st[j] = LittleFloat(verts[i].st[j]); + //cv->verts[i].lightmap[j] = LittleFloat(verts[i].lightmap[j]); + } + cv->verts[i].lightmap[0] = FatPackU(LittleFloat(verts[i].lightmap[0]), realLightmapNum); + cv->verts[i].lightmap[1] = FatPackV(LittleFloat(verts[i].lightmap[1]), realLightmapNum); + + if (hdrVertColors) + { + color[0] = hdrVertColors[(ds->firstVert + i) * 3 ]; + color[1] = hdrVertColors[(ds->firstVert + i) * 3 + 1]; + color[2] = hdrVertColors[(ds->firstVert + i) * 3 + 2]; + } + else + { + //hack: convert LDR vertex colors to HDR + if (r_hdr->integer) + { + color[0] = verts[i].color[0] + 1.0f; + color[1] = verts[i].color[1] + 1.0f; + color[2] = verts[i].color[2] + 1.0f; + } + else + { + color[0] = verts[i].color[0]; + color[1] = verts[i].color[1]; + color[2] = verts[i].color[2]; + } + + } + color[3] = verts[i].color[3] / 255.0f; + + R_ColorShiftLightingFloats( color, cv->verts[i].vertexColors, 1.0f / 255.0f ); + } + + // copy triangles + badTriangles = 0; + indexes += LittleLong(ds->firstIndex); + for(i = 0, tri = cv->triangles; i < numTriangles; i++, tri++) + { + for(j = 0; j < 3; j++) + { + tri->indexes[j] = LittleLong(indexes[i * 3 + j]); + + if(tri->indexes[j] < 0 || tri->indexes[j] >= numVerts) + { + ri.Error(ERR_DROP, "Bad index in face surface"); + } + } + + if ((tri->indexes[0] == tri->indexes[1]) || (tri->indexes[1] == tri->indexes[2]) || (tri->indexes[0] == tri->indexes[2])) + { + tri--; + badTriangles++; + } + } + + if (badTriangles) + { + ri.Printf(PRINT_WARNING, "Face has bad triangles, originally shader %s %d tris %d verts, now %d tris\n", surf->shader->name, numTriangles, numVerts, numTriangles - badTriangles); + cv->numTriangles -= badTriangles; + } + + // take the plane information from the lightmap vector + for ( i = 0 ; i < 3 ; i++ ) { + cv->plane.normal[i] = LittleFloat( ds->lightmapVecs[2][i] ); + } + cv->plane.dist = DotProduct( cv->verts[0].xyz, cv->plane.normal ); + SetPlaneSignbits( &cv->plane ); + cv->plane.type = PlaneTypeForNormal( cv->plane.normal ); + surf->cullinfo.plane = cv->plane; + + surf->data = (surfaceType_t *)cv; + +#ifdef USE_VERT_TANGENT_SPACE + // Tr3B - calc tangent spaces + { + srfVert_t *dv[3]; + + for(i = 0, tri = cv->triangles; i < numTriangles; i++, tri++) + { + dv[0] = &cv->verts[tri->indexes[0]]; + dv[1] = &cv->verts[tri->indexes[1]]; + dv[2] = &cv->verts[tri->indexes[2]]; + + R_CalcTangentVectors(dv); + } + } +#endif +} + + +/* +=============== +ParseMesh +=============== +*/ +static void ParseMesh ( dsurface_t *ds, drawVert_t *verts, float *hdrVertColors, msurface_t *surf ) { + srfGridMesh_t *grid; + int i, j; + int width, height, numPoints; + srfVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE]; + vec3_t bounds[2]; + vec3_t tmpVec; + static surfaceType_t skipData = SF_SKIP; + int realLightmapNum; + + realLightmapNum = LittleLong( ds->lightmapNum ); + + // get fog volume + surf->fogIndex = LittleLong( ds->fogNum ) + 1; + + // get shader value + surf->shader = ShaderForShaderNum( ds->shaderNum, FatLightmap(realLightmapNum) ); + if ( r_singleShader->integer && !surf->shader->isSky ) { + surf->shader = tr.defaultShader; + } + + // we may have a nodraw surface, because they might still need to + // be around for movement clipping + if ( s_worldData.shaders[ LittleLong( ds->shaderNum ) ].surfaceFlags & SURF_NODRAW ) { + surf->data = &skipData; + return; + } + + width = LittleLong( ds->patchWidth ); + height = LittleLong( ds->patchHeight ); + + if(width < 0 || width > MAX_PATCH_SIZE || height < 0 || height > MAX_PATCH_SIZE) + ri.Error(ERR_DROP, "ParseMesh: bad size"); + + verts += LittleLong( ds->firstVert ); + numPoints = width * height; + for(i = 0; i < numPoints; i++) + { + vec4_t color; + + for(j = 0; j < 3; j++) + { + points[i].xyz[j] = LittleFloat(verts[i].xyz[j]); + points[i].normal[j] = LittleFloat(verts[i].normal[j]); + } + + for(j = 0; j < 2; j++) + { + points[i].st[j] = LittleFloat(verts[i].st[j]); + //points[i].lightmap[j] = LittleFloat(verts[i].lightmap[j]); + } + points[i].lightmap[0] = FatPackU(LittleFloat(verts[i].lightmap[0]), realLightmapNum); + points[i].lightmap[1] = FatPackV(LittleFloat(verts[i].lightmap[1]), realLightmapNum); + + if (hdrVertColors) + { + color[0] = hdrVertColors[(ds->firstVert + i) * 3 ]; + color[1] = hdrVertColors[(ds->firstVert + i) * 3 + 1]; + color[2] = hdrVertColors[(ds->firstVert + i) * 3 + 2]; + } + else + { + //hack: convert LDR vertex colors to HDR + if (r_hdr->integer) + { + color[0] = verts[i].color[0] + 1.0f; + color[1] = verts[i].color[1] + 1.0f; + color[2] = verts[i].color[2] + 1.0f; + } + else + { + color[0] = verts[i].color[0]; + color[1] = verts[i].color[1]; + color[2] = verts[i].color[2]; + } + } + color[3] = verts[i].color[3] / 255.0f; + + R_ColorShiftLightingFloats( color, points[i].vertexColors, 1.0f / 255.0f ); + } + + // pre-tesseleate + grid = R_SubdividePatchToGrid( width, height, points ); + surf->data = (surfaceType_t *)grid; + + // copy the level of detail origin, which is the center + // of the group of all curves that must subdivide the same + // to avoid cracking + for ( i = 0 ; i < 3 ; i++ ) { + bounds[0][i] = LittleFloat( ds->lightmapVecs[0][i] ); + bounds[1][i] = LittleFloat( ds->lightmapVecs[1][i] ); + } + VectorAdd( bounds[0], bounds[1], bounds[1] ); + VectorScale( bounds[1], 0.5f, grid->lodOrigin ); + VectorSubtract( bounds[0], grid->lodOrigin, tmpVec ); + grid->lodRadius = VectorLength( tmpVec ); +} + +/* +=============== +ParseTriSurf +=============== +*/ +static void ParseTriSurf( dsurface_t *ds, drawVert_t *verts, float *hdrVertColors, msurface_t *surf, int *indexes ) { + srfTriangles_t *cv; + srfTriangle_t *tri; + int i, j; + int numVerts, numTriangles, badTriangles; + + // get fog volume + surf->fogIndex = LittleLong( ds->fogNum ) + 1; + + // get shader + surf->shader = ShaderForShaderNum( ds->shaderNum, LIGHTMAP_BY_VERTEX ); + if ( r_singleShader->integer && !surf->shader->isSky ) { + surf->shader = tr.defaultShader; + } + + numVerts = LittleLong(ds->numVerts); + numTriangles = LittleLong(ds->numIndexes) / 3; + + //cv = ri.Hunk_Alloc(sizeof(*cv), h_low); + cv = (void *)surf->data; + cv->surfaceType = SF_TRIANGLES; + + cv->numTriangles = numTriangles; + cv->triangles = ri.Hunk_Alloc(numTriangles * sizeof(cv->triangles[0]), h_low); + + cv->numVerts = numVerts; + cv->verts = ri.Hunk_Alloc(numVerts * sizeof(cv->verts[0]), h_low); + + surf->data = (surfaceType_t *) cv; + + // copy vertexes + surf->cullinfo.type = CULLINFO_BOX; + ClearBounds(surf->cullinfo.bounds[0], surf->cullinfo.bounds[1]); + verts += LittleLong(ds->firstVert); + for(i = 0; i < numVerts; i++) + { + vec4_t color; + + for(j = 0; j < 3; j++) + { + cv->verts[i].xyz[j] = LittleFloat(verts[i].xyz[j]); + cv->verts[i].normal[j] = LittleFloat(verts[i].normal[j]); + } + + AddPointToBounds( cv->verts[i].xyz, surf->cullinfo.bounds[0], surf->cullinfo.bounds[1] ); + + for(j = 0; j < 2; j++) + { + cv->verts[i].st[j] = LittleFloat(verts[i].st[j]); + cv->verts[i].lightmap[j] = LittleFloat(verts[i].lightmap[j]); + } + + if (hdrVertColors) + { + color[0] = hdrVertColors[(ds->firstVert + i) * 3 ]; + color[1] = hdrVertColors[(ds->firstVert + i) * 3 + 1]; + color[2] = hdrVertColors[(ds->firstVert + i) * 3 + 2]; + } + else + { + //hack: convert LDR vertex colors to HDR + if (r_hdr->integer) + { + color[0] = verts[i].color[0] + 1.0f; + color[1] = verts[i].color[1] + 1.0f; + color[2] = verts[i].color[2] + 1.0f; + } + else + { + color[0] = verts[i].color[0]; + color[1] = verts[i].color[1]; + color[2] = verts[i].color[2]; + } + } + color[3] = verts[i].color[3] / 255.0f; + + R_ColorShiftLightingFloats( color, cv->verts[i].vertexColors, 1.0f / 255.0f ); + } + + // copy triangles + badTriangles = 0; + indexes += LittleLong(ds->firstIndex); + for(i = 0, tri = cv->triangles; i < numTriangles; i++, tri++) + { + for(j = 0; j < 3; j++) + { + tri->indexes[j] = LittleLong(indexes[i * 3 + j]); + + if(tri->indexes[j] < 0 || tri->indexes[j] >= numVerts) + { + ri.Error(ERR_DROP, "Bad index in face surface"); + } + } + + if ((tri->indexes[0] == tri->indexes[1]) || (tri->indexes[1] == tri->indexes[2]) || (tri->indexes[0] == tri->indexes[2])) + { + tri--; + badTriangles++; + } + } + + if (badTriangles) + { + ri.Printf(PRINT_WARNING, "Trisurf has bad triangles, originally shader %s %d tris %d verts, now %d tris\n", surf->shader->name, numTriangles, numVerts, numTriangles - badTriangles); + cv->numTriangles -= badTriangles; + } + +#ifdef USE_VERT_TANGENT_SPACE + // Tr3B - calc tangent spaces + { + srfVert_t *dv[3]; + + for(i = 0, tri = cv->triangles; i < numTriangles; i++, tri++) + { + dv[0] = &cv->verts[tri->indexes[0]]; + dv[1] = &cv->verts[tri->indexes[1]]; + dv[2] = &cv->verts[tri->indexes[2]]; + + R_CalcTangentVectors(dv); + } + } +#endif +} + +/* +=============== +ParseFlare +=============== +*/ +static void ParseFlare( dsurface_t *ds, drawVert_t *verts, msurface_t *surf, int *indexes ) { + srfFlare_t *flare; + int i; + + // get fog volume + surf->fogIndex = LittleLong( ds->fogNum ) + 1; + + // get shader + surf->shader = ShaderForShaderNum( ds->shaderNum, LIGHTMAP_BY_VERTEX ); + if ( r_singleShader->integer && !surf->shader->isSky ) { + surf->shader = tr.defaultShader; + } + + //flare = ri.Hunk_Alloc( sizeof( *flare ), h_low ); + flare = (void *)surf->data; + flare->surfaceType = SF_FLARE; + + surf->data = (surfaceType_t *)flare; + + for ( i = 0 ; i < 3 ; i++ ) { + flare->origin[i] = LittleFloat( ds->lightmapOrigin[i] ); + flare->color[i] = LittleFloat( ds->lightmapVecs[0][i] ); + flare->normal[i] = LittleFloat( ds->lightmapVecs[2][i] ); + } +} + + +/* +================= +R_MergedWidthPoints + +returns true if there are grid points merged on a width edge +================= +*/ +int R_MergedWidthPoints(srfGridMesh_t *grid, int offset) { + int i, j; + + for (i = 1; i < grid->width-1; i++) { + for (j = i + 1; j < grid->width-1; j++) { + if ( fabs(grid->verts[i + offset].xyz[0] - grid->verts[j + offset].xyz[0]) > .1) continue; + if ( fabs(grid->verts[i + offset].xyz[1] - grid->verts[j + offset].xyz[1]) > .1) continue; + if ( fabs(grid->verts[i + offset].xyz[2] - grid->verts[j + offset].xyz[2]) > .1) continue; + return qtrue; + } + } + return qfalse; +} + +/* +================= +R_MergedHeightPoints + +returns true if there are grid points merged on a height edge +================= +*/ +int R_MergedHeightPoints(srfGridMesh_t *grid, int offset) { + int i, j; + + for (i = 1; i < grid->height-1; i++) { + for (j = i + 1; j < grid->height-1; j++) { + if ( fabs(grid->verts[grid->width * i + offset].xyz[0] - grid->verts[grid->width * j + offset].xyz[0]) > .1) continue; + if ( fabs(grid->verts[grid->width * i + offset].xyz[1] - grid->verts[grid->width * j + offset].xyz[1]) > .1) continue; + if ( fabs(grid->verts[grid->width * i + offset].xyz[2] - grid->verts[grid->width * j + offset].xyz[2]) > .1) continue; + return qtrue; + } + } + return qfalse; +} + +/* +================= +R_FixSharedVertexLodError_r + +NOTE: never sync LoD through grid edges with merged points! + +FIXME: write generalized version that also avoids cracks between a patch and one that meets half way? +================= +*/ +void R_FixSharedVertexLodError_r( int start, srfGridMesh_t *grid1 ) { + int j, k, l, m, n, offset1, offset2, touch; + srfGridMesh_t *grid2; + + for ( j = start; j < s_worldData.numsurfaces; j++ ) { + // + grid2 = (srfGridMesh_t *) s_worldData.surfaces[j].data; + // if this surface is not a grid + if ( grid2->surfaceType != SF_GRID ) continue; + // if the LOD errors are already fixed for this patch + if ( grid2->lodFixed == 2 ) continue; + // grids in the same LOD group should have the exact same lod radius + if ( grid1->lodRadius != grid2->lodRadius ) continue; + // grids in the same LOD group should have the exact same lod origin + if ( grid1->lodOrigin[0] != grid2->lodOrigin[0] ) continue; + if ( grid1->lodOrigin[1] != grid2->lodOrigin[1] ) continue; + if ( grid1->lodOrigin[2] != grid2->lodOrigin[2] ) continue; + // + touch = qfalse; + for (n = 0; n < 2; n++) { + // + if (n) offset1 = (grid1->height-1) * grid1->width; + else offset1 = 0; + if (R_MergedWidthPoints(grid1, offset1)) continue; + for (k = 1; k < grid1->width-1; k++) { + for (m = 0; m < 2; m++) { + + if (m) offset2 = (grid2->height-1) * grid2->width; + else offset2 = 0; + if (R_MergedWidthPoints(grid2, offset2)) continue; + for ( l = 1; l < grid2->width-1; l++) { + // + if ( fabs(grid1->verts[k + offset1].xyz[0] - grid2->verts[l + offset2].xyz[0]) > .1) continue; + if ( fabs(grid1->verts[k + offset1].xyz[1] - grid2->verts[l + offset2].xyz[1]) > .1) continue; + if ( fabs(grid1->verts[k + offset1].xyz[2] - grid2->verts[l + offset2].xyz[2]) > .1) continue; + // ok the points are equal and should have the same lod error + grid2->widthLodError[l] = grid1->widthLodError[k]; + touch = qtrue; + } + } + for (m = 0; m < 2; m++) { + + if (m) offset2 = grid2->width-1; + else offset2 = 0; + if (R_MergedHeightPoints(grid2, offset2)) continue; + for ( l = 1; l < grid2->height-1; l++) { + // + if ( fabs(grid1->verts[k + offset1].xyz[0] - grid2->verts[grid2->width * l + offset2].xyz[0]) > .1) continue; + if ( fabs(grid1->verts[k + offset1].xyz[1] - grid2->verts[grid2->width * l + offset2].xyz[1]) > .1) continue; + if ( fabs(grid1->verts[k + offset1].xyz[2] - grid2->verts[grid2->width * l + offset2].xyz[2]) > .1) continue; + // ok the points are equal and should have the same lod error + grid2->heightLodError[l] = grid1->widthLodError[k]; + touch = qtrue; + } + } + } + } + for (n = 0; n < 2; n++) { + // + if (n) offset1 = grid1->width-1; + else offset1 = 0; + if (R_MergedHeightPoints(grid1, offset1)) continue; + for (k = 1; k < grid1->height-1; k++) { + for (m = 0; m < 2; m++) { + + if (m) offset2 = (grid2->height-1) * grid2->width; + else offset2 = 0; + if (R_MergedWidthPoints(grid2, offset2)) continue; + for ( l = 1; l < grid2->width-1; l++) { + // + if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[0] - grid2->verts[l + offset2].xyz[0]) > .1) continue; + if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[1] - grid2->verts[l + offset2].xyz[1]) > .1) continue; + if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[2] - grid2->verts[l + offset2].xyz[2]) > .1) continue; + // ok the points are equal and should have the same lod error + grid2->widthLodError[l] = grid1->heightLodError[k]; + touch = qtrue; + } + } + for (m = 0; m < 2; m++) { + + if (m) offset2 = grid2->width-1; + else offset2 = 0; + if (R_MergedHeightPoints(grid2, offset2)) continue; + for ( l = 1; l < grid2->height-1; l++) { + // + if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[0] - grid2->verts[grid2->width * l + offset2].xyz[0]) > .1) continue; + if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[1] - grid2->verts[grid2->width * l + offset2].xyz[1]) > .1) continue; + if ( fabs(grid1->verts[grid1->width * k + offset1].xyz[2] - grid2->verts[grid2->width * l + offset2].xyz[2]) > .1) continue; + // ok the points are equal and should have the same lod error + grid2->heightLodError[l] = grid1->heightLodError[k]; + touch = qtrue; + } + } + } + } + if (touch) { + grid2->lodFixed = 2; + R_FixSharedVertexLodError_r ( start, grid2 ); + //NOTE: this would be correct but makes things really slow + //grid2->lodFixed = 1; + } + } +} + +/* +================= +R_FixSharedVertexLodError + +This function assumes that all patches in one group are nicely stitched together for the highest LoD. +If this is not the case this function will still do its job but won't fix the highest LoD cracks. +================= +*/ +void R_FixSharedVertexLodError( void ) { + int i; + srfGridMesh_t *grid1; + + for ( i = 0; i < s_worldData.numsurfaces; i++ ) { + // + grid1 = (srfGridMesh_t *) s_worldData.surfaces[i].data; + // if this surface is not a grid + if ( grid1->surfaceType != SF_GRID ) + continue; + // + if ( grid1->lodFixed ) + continue; + // + grid1->lodFixed = 2; + // recursively fix other patches in the same LOD group + R_FixSharedVertexLodError_r( i + 1, grid1); + } +} + + +/* +=============== +R_StitchPatches +=============== +*/ +int R_StitchPatches( int grid1num, int grid2num ) { + float *v1, *v2; + srfGridMesh_t *grid1, *grid2; + int k, l, m, n, offset1, offset2, row, column; + + grid1 = (srfGridMesh_t *) s_worldData.surfaces[grid1num].data; + grid2 = (srfGridMesh_t *) s_worldData.surfaces[grid2num].data; + for (n = 0; n < 2; n++) { + // + if (n) offset1 = (grid1->height-1) * grid1->width; + else offset1 = 0; + if (R_MergedWidthPoints(grid1, offset1)) + continue; + for (k = 0; k < grid1->width-2; k += 2) { + + for (m = 0; m < 2; m++) { + + if ( grid2->width >= MAX_GRID_SIZE ) + break; + if (m) offset2 = (grid2->height-1) * grid2->width; + else offset2 = 0; + for ( l = 0; l < grid2->width-1; l++) { + // + v1 = grid1->verts[k + offset1].xyz; + v2 = grid2->verts[l + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + + v1 = grid1->verts[k + 2 + offset1].xyz; + v2 = grid2->verts[l + 1 + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + // + v1 = grid2->verts[l + offset2].xyz; + v2 = grid2->verts[l + 1 + offset2].xyz; + if ( fabs(v1[0] - v2[0]) < .01 && + fabs(v1[1] - v2[1]) < .01 && + fabs(v1[2] - v2[2]) < .01) + continue; + // + //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); + // insert column into grid2 right after after column l + if (m) row = grid2->height-1; + else row = 0; + grid2 = R_GridInsertColumn( grid2, l+1, row, + grid1->verts[k + 1 + offset1].xyz, grid1->widthLodError[k+1]); + grid2->lodStitched = qfalse; + s_worldData.surfaces[grid2num].data = (void *) grid2; + return qtrue; + } + } + for (m = 0; m < 2; m++) { + + if (grid2->height >= MAX_GRID_SIZE) + break; + if (m) offset2 = grid2->width-1; + else offset2 = 0; + for ( l = 0; l < grid2->height-1; l++) { + // + v1 = grid1->verts[k + offset1].xyz; + v2 = grid2->verts[grid2->width * l + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + + v1 = grid1->verts[k + 2 + offset1].xyz; + v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + // + v1 = grid2->verts[grid2->width * l + offset2].xyz; + v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) < .01 && + fabs(v1[1] - v2[1]) < .01 && + fabs(v1[2] - v2[2]) < .01) + continue; + // + //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); + // insert row into grid2 right after after row l + if (m) column = grid2->width-1; + else column = 0; + grid2 = R_GridInsertRow( grid2, l+1, column, + grid1->verts[k + 1 + offset1].xyz, grid1->widthLodError[k+1]); + grid2->lodStitched = qfalse; + s_worldData.surfaces[grid2num].data = (void *) grid2; + return qtrue; + } + } + } + } + for (n = 0; n < 2; n++) { + // + if (n) offset1 = grid1->width-1; + else offset1 = 0; + if (R_MergedHeightPoints(grid1, offset1)) + continue; + for (k = 0; k < grid1->height-2; k += 2) { + for (m = 0; m < 2; m++) { + + if ( grid2->width >= MAX_GRID_SIZE ) + break; + if (m) offset2 = (grid2->height-1) * grid2->width; + else offset2 = 0; + for ( l = 0; l < grid2->width-1; l++) { + // + v1 = grid1->verts[grid1->width * k + offset1].xyz; + v2 = grid2->verts[l + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + + v1 = grid1->verts[grid1->width * (k + 2) + offset1].xyz; + v2 = grid2->verts[l + 1 + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + // + v1 = grid2->verts[l + offset2].xyz; + v2 = grid2->verts[(l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) < .01 && + fabs(v1[1] - v2[1]) < .01 && + fabs(v1[2] - v2[2]) < .01) + continue; + // + //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); + // insert column into grid2 right after after column l + if (m) row = grid2->height-1; + else row = 0; + grid2 = R_GridInsertColumn( grid2, l+1, row, + grid1->verts[grid1->width * (k + 1) + offset1].xyz, grid1->heightLodError[k+1]); + grid2->lodStitched = qfalse; + s_worldData.surfaces[grid2num].data = (void *) grid2; + return qtrue; + } + } + for (m = 0; m < 2; m++) { + + if (grid2->height >= MAX_GRID_SIZE) + break; + if (m) offset2 = grid2->width-1; + else offset2 = 0; + for ( l = 0; l < grid2->height-1; l++) { + // + v1 = grid1->verts[grid1->width * k + offset1].xyz; + v2 = grid2->verts[grid2->width * l + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + + v1 = grid1->verts[grid1->width * (k + 2) + offset1].xyz; + v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + // + v1 = grid2->verts[grid2->width * l + offset2].xyz; + v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) < .01 && + fabs(v1[1] - v2[1]) < .01 && + fabs(v1[2] - v2[2]) < .01) + continue; + // + //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); + // insert row into grid2 right after after row l + if (m) column = grid2->width-1; + else column = 0; + grid2 = R_GridInsertRow( grid2, l+1, column, + grid1->verts[grid1->width * (k + 1) + offset1].xyz, grid1->heightLodError[k+1]); + grid2->lodStitched = qfalse; + s_worldData.surfaces[grid2num].data = (void *) grid2; + return qtrue; + } + } + } + } + for (n = 0; n < 2; n++) { + // + if (n) offset1 = (grid1->height-1) * grid1->width; + else offset1 = 0; + if (R_MergedWidthPoints(grid1, offset1)) + continue; + for (k = grid1->width-1; k > 1; k -= 2) { + + for (m = 0; m < 2; m++) { + + if ( grid2->width >= MAX_GRID_SIZE ) + break; + if (m) offset2 = (grid2->height-1) * grid2->width; + else offset2 = 0; + for ( l = 0; l < grid2->width-1; l++) { + // + v1 = grid1->verts[k + offset1].xyz; + v2 = grid2->verts[l + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + + v1 = grid1->verts[k - 2 + offset1].xyz; + v2 = grid2->verts[l + 1 + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + // + v1 = grid2->verts[l + offset2].xyz; + v2 = grid2->verts[(l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) < .01 && + fabs(v1[1] - v2[1]) < .01 && + fabs(v1[2] - v2[2]) < .01) + continue; + // + //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); + // insert column into grid2 right after after column l + if (m) row = grid2->height-1; + else row = 0; + grid2 = R_GridInsertColumn( grid2, l+1, row, + grid1->verts[k - 1 + offset1].xyz, grid1->widthLodError[k+1]); + grid2->lodStitched = qfalse; + s_worldData.surfaces[grid2num].data = (void *) grid2; + return qtrue; + } + } + for (m = 0; m < 2; m++) { + + if (grid2->height >= MAX_GRID_SIZE) + break; + if (m) offset2 = grid2->width-1; + else offset2 = 0; + for ( l = 0; l < grid2->height-1; l++) { + // + v1 = grid1->verts[k + offset1].xyz; + v2 = grid2->verts[grid2->width * l + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + + v1 = grid1->verts[k - 2 + offset1].xyz; + v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + // + v1 = grid2->verts[grid2->width * l + offset2].xyz; + v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) < .01 && + fabs(v1[1] - v2[1]) < .01 && + fabs(v1[2] - v2[2]) < .01) + continue; + // + //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); + // insert row into grid2 right after after row l + if (m) column = grid2->width-1; + else column = 0; + grid2 = R_GridInsertRow( grid2, l+1, column, + grid1->verts[k - 1 + offset1].xyz, grid1->widthLodError[k+1]); + if (!grid2) + break; + grid2->lodStitched = qfalse; + s_worldData.surfaces[grid2num].data = (void *) grid2; + return qtrue; + } + } + } + } + for (n = 0; n < 2; n++) { + // + if (n) offset1 = grid1->width-1; + else offset1 = 0; + if (R_MergedHeightPoints(grid1, offset1)) + continue; + for (k = grid1->height-1; k > 1; k -= 2) { + for (m = 0; m < 2; m++) { + + if ( grid2->width >= MAX_GRID_SIZE ) + break; + if (m) offset2 = (grid2->height-1) * grid2->width; + else offset2 = 0; + for ( l = 0; l < grid2->width-1; l++) { + // + v1 = grid1->verts[grid1->width * k + offset1].xyz; + v2 = grid2->verts[l + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + + v1 = grid1->verts[grid1->width * (k - 2) + offset1].xyz; + v2 = grid2->verts[l + 1 + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + // + v1 = grid2->verts[l + offset2].xyz; + v2 = grid2->verts[(l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) < .01 && + fabs(v1[1] - v2[1]) < .01 && + fabs(v1[2] - v2[2]) < .01) + continue; + // + //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); + // insert column into grid2 right after after column l + if (m) row = grid2->height-1; + else row = 0; + grid2 = R_GridInsertColumn( grid2, l+1, row, + grid1->verts[grid1->width * (k - 1) + offset1].xyz, grid1->heightLodError[k+1]); + grid2->lodStitched = qfalse; + s_worldData.surfaces[grid2num].data = (void *) grid2; + return qtrue; + } + } + for (m = 0; m < 2; m++) { + + if (grid2->height >= MAX_GRID_SIZE) + break; + if (m) offset2 = grid2->width-1; + else offset2 = 0; + for ( l = 0; l < grid2->height-1; l++) { + // + v1 = grid1->verts[grid1->width * k + offset1].xyz; + v2 = grid2->verts[grid2->width * l + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + + v1 = grid1->verts[grid1->width * (k - 2) + offset1].xyz; + v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) > .1) + continue; + if ( fabs(v1[1] - v2[1]) > .1) + continue; + if ( fabs(v1[2] - v2[2]) > .1) + continue; + // + v1 = grid2->verts[grid2->width * l + offset2].xyz; + v2 = grid2->verts[grid2->width * (l + 1) + offset2].xyz; + if ( fabs(v1[0] - v2[0]) < .01 && + fabs(v1[1] - v2[1]) < .01 && + fabs(v1[2] - v2[2]) < .01) + continue; + // + //ri.Printf( PRINT_ALL, "found highest LoD crack between two patches\n" ); + // insert row into grid2 right after after row l + if (m) column = grid2->width-1; + else column = 0; + grid2 = R_GridInsertRow( grid2, l+1, column, + grid1->verts[grid1->width * (k - 1) + offset1].xyz, grid1->heightLodError[k+1]); + grid2->lodStitched = qfalse; + s_worldData.surfaces[grid2num].data = (void *) grid2; + return qtrue; + } + } + } + } + return qfalse; +} + +/* +=============== +R_TryStitchPatch + +This function will try to stitch patches in the same LoD group together for the highest LoD. + +Only single missing vertice cracks will be fixed. + +Vertices will be joined at the patch side a crack is first found, at the other side +of the patch (on the same row or column) the vertices will not be joined and cracks +might still appear at that side. +=============== +*/ +int R_TryStitchingPatch( int grid1num ) { + int j, numstitches; + srfGridMesh_t *grid1, *grid2; + + numstitches = 0; + grid1 = (srfGridMesh_t *) s_worldData.surfaces[grid1num].data; + for ( j = 0; j < s_worldData.numsurfaces; j++ ) { + // + grid2 = (srfGridMesh_t *) s_worldData.surfaces[j].data; + // if this surface is not a grid + if ( grid2->surfaceType != SF_GRID ) continue; + // grids in the same LOD group should have the exact same lod radius + if ( grid1->lodRadius != grid2->lodRadius ) continue; + // grids in the same LOD group should have the exact same lod origin + if ( grid1->lodOrigin[0] != grid2->lodOrigin[0] ) continue; + if ( grid1->lodOrigin[1] != grid2->lodOrigin[1] ) continue; + if ( grid1->lodOrigin[2] != grid2->lodOrigin[2] ) continue; + // + while (R_StitchPatches(grid1num, j)) + { + numstitches++; + } + } + return numstitches; +} + +/* +=============== +R_StitchAllPatches +=============== +*/ +void R_StitchAllPatches( void ) { + int i, stitched, numstitches; + srfGridMesh_t *grid1; + + numstitches = 0; + do + { + stitched = qfalse; + for ( i = 0; i < s_worldData.numsurfaces; i++ ) { + // + grid1 = (srfGridMesh_t *) s_worldData.surfaces[i].data; + // if this surface is not a grid + if ( grid1->surfaceType != SF_GRID ) + continue; + // + if ( grid1->lodStitched ) + continue; + // + grid1->lodStitched = qtrue; + stitched = qtrue; + // + numstitches += R_TryStitchingPatch( i ); + } + } + while (stitched); + ri.Printf( PRINT_ALL, "stitched %d LoD cracks\n", numstitches ); +} + +/* +=============== +R_MovePatchSurfacesToHunk +=============== +*/ +void R_MovePatchSurfacesToHunk(void) { + int i, size; + srfGridMesh_t *grid, *hunkgrid; + + for ( i = 0; i < s_worldData.numsurfaces; i++ ) { + // + grid = (srfGridMesh_t *) s_worldData.surfaces[i].data; + // if this surface is not a grid + if ( grid->surfaceType != SF_GRID ) + continue; + // + size = sizeof(*grid); + hunkgrid = ri.Hunk_Alloc(size, h_low); + Com_Memcpy(hunkgrid, grid, size); + + hunkgrid->widthLodError = ri.Hunk_Alloc( grid->width * 4, h_low ); + Com_Memcpy( hunkgrid->widthLodError, grid->widthLodError, grid->width * 4 ); + + hunkgrid->heightLodError = ri.Hunk_Alloc( grid->height * 4, h_low ); + Com_Memcpy( hunkgrid->heightLodError, grid->heightLodError, grid->height * 4 ); + + hunkgrid->numTriangles = grid->numTriangles; + hunkgrid->triangles = ri.Hunk_Alloc(grid->numTriangles * sizeof(srfTriangle_t), h_low); + Com_Memcpy(hunkgrid->triangles, grid->triangles, grid->numTriangles * sizeof(srfTriangle_t)); + + hunkgrid->numVerts = grid->numVerts; + hunkgrid->verts = ri.Hunk_Alloc(grid->numVerts * sizeof(srfVert_t), h_low); + Com_Memcpy(hunkgrid->verts, grid->verts, grid->numVerts * sizeof(srfVert_t)); + + R_FreeSurfaceGridMesh( grid ); + + s_worldData.surfaces[i].data = (void *) hunkgrid; + } +} + + +/* +================= +BSPSurfaceCompare +compare function for qsort() +================= +*/ +static int BSPSurfaceCompare(const void *a, const void *b) +{ + msurface_t *aa, *bb; + + aa = *(msurface_t **) a; + bb = *(msurface_t **) b; + + // shader first + if(aa->shader->sortedIndex < bb->shader->sortedIndex) + return -1; + + else if(aa->shader->sortedIndex > bb->shader->sortedIndex) + return 1; + + // by fogIndex + if(aa->fogIndex < bb->fogIndex) + return -1; + + else if(aa->fogIndex > bb->fogIndex) + return 1; + + return 0; +} + + +static void CopyVert(const srfVert_t * in, srfVert_t * out) +{ + int j; + + for(j = 0; j < 3; j++) + { + out->xyz[j] = in->xyz[j]; +#ifdef USE_VERT_TANGENT_SPACE + out->tangent[j] = in->tangent[j]; + out->bitangent[j] = in->bitangent[j]; +#endif + out->normal[j] = in->normal[j]; + out->lightdir[j] = in->lightdir[j]; + } + + for(j = 0; j < 2; j++) + { + out->st[j] = in->st[j]; + out->lightmap[j] = in->lightmap[j]; + } + + for(j = 0; j < 4; j++) + { + out->vertexColors[j] = in->vertexColors[j]; + } +} + + +/* +=============== +R_CreateWorldVBO +=============== +*/ +static void R_CreateWorldVBO(void) +{ + int i, j, k; + + int numVerts; + srfVert_t *verts; + + int numTriangles; + srfTriangle_t *triangles; + + int numSurfaces; + msurface_t *surface; + msurface_t **surfacesSorted; + + int startTime, endTime; + + startTime = ri.Milliseconds(); + + numVerts = 0; + numTriangles = 0; + numSurfaces = 0; + for(k = 0, surface = &s_worldData.surfaces[0]; k < s_worldData.numsurfaces /* s_worldData.numWorldSurfaces */; k++, surface++) + { + if(*surface->data == SF_FACE) + { + srfSurfaceFace_t *face = (srfSurfaceFace_t *) surface->data; + + if(face->numVerts) + numVerts += face->numVerts; + + if(face->numTriangles) + numTriangles += face->numTriangles; + + numSurfaces++; + } + else if(*surface->data == SF_GRID) + { + srfGridMesh_t *grid = (srfGridMesh_t *) surface->data; + + if(grid->numVerts) + numVerts += grid->numVerts; + + if(grid->numTriangles) + numTriangles += grid->numTriangles; + + numSurfaces++; + } + else if(*surface->data == SF_TRIANGLES) + { + srfTriangles_t *tri = (srfTriangles_t *) surface->data; + + if(tri->numVerts) + numVerts += tri->numVerts; + + if(tri->numTriangles) + numTriangles += tri->numTriangles; + + numSurfaces++; + } + } + + if(!numVerts || !numTriangles) + return; + + ri.Printf(PRINT_ALL, "...calculating world VBO ( %i verts %i tris )\n", numVerts, numTriangles); + + // create arrays + + verts = ri.Hunk_AllocateTempMemory(numVerts * sizeof(srfVert_t)); + + triangles = ri.Hunk_AllocateTempMemory(numTriangles * sizeof(srfTriangle_t)); + + // presort surfaces + surfacesSorted = ri.Malloc(numSurfaces * sizeof(*surfacesSorted)); + + j = 0; + for(k = 0, surface = &s_worldData.surfaces[0]; k < s_worldData.numsurfaces; k++, surface++) + { + if(*surface->data == SF_FACE || *surface->data == SF_GRID || *surface->data == SF_TRIANGLES) + { + surfacesSorted[j++] = surface; + } + } + + qsort(surfacesSorted, numSurfaces, sizeof(*surfacesSorted), BSPSurfaceCompare); + + // set up triangle indices + numVerts = 0; + numTriangles = 0; + for(k = 0, surface = surfacesSorted[k]; k < numSurfaces; k++, surface = surfacesSorted[k]) + { + if(*surface->data == SF_FACE) + { + srfSurfaceFace_t *srf = (srfSurfaceFace_t *) surface->data; + + srf->firstIndex = numTriangles * 3; + + if(srf->numTriangles) + { + srfTriangle_t *tri; + + srf->minIndex = numVerts + srf->triangles->indexes[0]; + srf->maxIndex = numVerts + srf->triangles->indexes[0]; + + for(i = 0, tri = srf->triangles; i < srf->numTriangles; i++, tri++) + { + for(j = 0; j < 3; j++) + { + triangles[numTriangles + i].indexes[j] = numVerts + tri->indexes[j]; + srf->minIndex = MIN(srf->minIndex, numVerts + tri->indexes[j]); + srf->maxIndex = MAX(srf->maxIndex, numVerts + tri->indexes[j]); + } + } + + numTriangles += srf->numTriangles; + } + + if(srf->numVerts) + numVerts += srf->numVerts; + } + else if(*surface->data == SF_GRID) + { + srfGridMesh_t *srf = (srfGridMesh_t *) surface->data; + + srf->firstIndex = numTriangles * 3; + + if(srf->numTriangles) + { + srfTriangle_t *tri; + + srf->minIndex = numVerts + srf->triangles->indexes[0]; + srf->maxIndex = numVerts + srf->triangles->indexes[0]; + + for(i = 0, tri = srf->triangles; i < srf->numTriangles; i++, tri++) + { + for(j = 0; j < 3; j++) + { + triangles[numTriangles + i].indexes[j] = numVerts + tri->indexes[j]; + srf->minIndex = MIN(srf->minIndex, numVerts + tri->indexes[j]); + srf->maxIndex = MAX(srf->maxIndex, numVerts + tri->indexes[j]); + } + } + + numTriangles += srf->numTriangles; + } + + if(srf->numVerts) + numVerts += srf->numVerts; + } + else if(*surface->data == SF_TRIANGLES) + { + srfTriangles_t *srf = (srfTriangles_t *) surface->data; + + srf->firstIndex = numTriangles * 3; + + if(srf->numTriangles) + { + srfTriangle_t *tri; + + srf->minIndex = numVerts + srf->triangles->indexes[0]; + srf->maxIndex = numVerts + srf->triangles->indexes[0]; + + for(i = 0, tri = srf->triangles; i < srf->numTriangles; i++, tri++) + { + for(j = 0; j < 3; j++) + { + triangles[numTriangles + i].indexes[j] = numVerts + tri->indexes[j]; + srf->minIndex = MIN(srf->minIndex, numVerts + tri->indexes[j]); + srf->maxIndex = MAX(srf->maxIndex, numVerts + tri->indexes[j]); + } + } + + numTriangles += srf->numTriangles; + } + + if(srf->numVerts) + numVerts += srf->numVerts; + } + } + + // build vertices + numVerts = 0; + for(k = 0, surface = surfacesSorted[k]; k < numSurfaces; k++, surface = surfacesSorted[k]) + { + if(*surface->data == SF_FACE) + { + srfSurfaceFace_t *srf = (srfSurfaceFace_t *) surface->data; + + srf->firstVert = numVerts; + + if(srf->numVerts) + { + for(i = 0; i < srf->numVerts; i++) + { + CopyVert(&srf->verts[i], &verts[numVerts + i]); + } + + numVerts += srf->numVerts; + } + } + else if(*surface->data == SF_GRID) + { + srfGridMesh_t *srf = (srfGridMesh_t *) surface->data; + + srf->firstVert = numVerts; + + if(srf->numVerts) + { + for(i = 0; i < srf->numVerts; i++) + { + CopyVert(&srf->verts[i], &verts[numVerts + i]); + } + + numVerts += srf->numVerts; + } + } + else if(*surface->data == SF_TRIANGLES) + { + srfTriangles_t *srf = (srfTriangles_t *) surface->data; + + srf->firstVert = numVerts; + + if(srf->numVerts) + { + for(i = 0; i < srf->numVerts; i++) + { + CopyVert(&srf->verts[i], &verts[numVerts + i]); + } + + numVerts += srf->numVerts; + } + } + } + +#ifdef USE_VERT_TANGENT_SPACE + s_worldData.vbo = R_CreateVBO2(va("staticBspModel0_VBO %i", 0), numVerts, verts, + ATTR_POSITION | ATTR_TEXCOORD | ATTR_LIGHTCOORD | ATTR_TANGENT | ATTR_BITANGENT | + ATTR_NORMAL | ATTR_COLOR | ATTR_LIGHTDIRECTION, VBO_USAGE_STATIC); +#else + s_worldData.vbo = R_CreateVBO2(va("staticBspModel0_VBO %i", 0), numVerts, verts, + ATTR_POSITION | ATTR_TEXCOORD | ATTR_LIGHTCOORD | + ATTR_NORMAL | ATTR_COLOR | ATTR_LIGHTDIRECTION, VBO_USAGE_STATIC); +#endif + + s_worldData.ibo = R_CreateIBO2(va("staticBspModel0_IBO %i", 0), numTriangles, triangles, VBO_USAGE_STATIC); + + endTime = ri.Milliseconds(); + ri.Printf(PRINT_ALL, "world VBO calculation time = %5.2f seconds\n", (endTime - startTime) / 1000.0); + + // point triangle surfaces to world VBO + for(k = 0, surface = surfacesSorted[k]; k < numSurfaces; k++, surface = surfacesSorted[k]) + { + if(*surface->data == SF_FACE) + { + srfSurfaceFace_t *srf = (srfSurfaceFace_t *) surface->data; + + if( srf->numVerts && srf->numTriangles) + { + srf->vbo = s_worldData.vbo; + srf->ibo = s_worldData.ibo; + } + } + else if(*surface->data == SF_GRID) + { + srfGridMesh_t *srf = (srfGridMesh_t *) surface->data; + + if( srf->numVerts && srf->numTriangles) + { + srf->vbo = s_worldData.vbo; + srf->ibo = s_worldData.ibo; + } + } + else if(*surface->data == SF_TRIANGLES) + { + srfTriangles_t *srf = (srfTriangles_t *) surface->data; + + if( srf->numVerts && srf->numTriangles) + { + srf->vbo = s_worldData.vbo; + srf->ibo = s_worldData.ibo; + } + } + } + + + startTime = ri.Milliseconds(); + + ri.Free(surfacesSorted); + + ri.Hunk_FreeTempMemory(triangles); + ri.Hunk_FreeTempMemory(verts); +} + +/* +=============== +R_LoadSurfaces +=============== +*/ +static void R_LoadSurfaces( lump_t *surfs, lump_t *verts, lump_t *indexLump ) { + dsurface_t *in; + msurface_t *out; + drawVert_t *dv; + int *indexes; + int count; + int numFaces, numMeshes, numTriSurfs, numFlares; + int i; + float *hdrVertColors = NULL; + + numFaces = 0; + numMeshes = 0; + numTriSurfs = 0; + numFlares = 0; + + in = (void *)(fileBase + surfs->fileofs); + if (surfs->filelen % sizeof(*in)) + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + count = surfs->filelen / sizeof(*in); + + dv = (void *)(fileBase + verts->fileofs); + if (verts->filelen % sizeof(*dv)) + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + + indexes = (void *)(fileBase + indexLump->fileofs); + if ( indexLump->filelen % sizeof(*indexes)) + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + + out = ri.Hunk_Alloc ( count * sizeof(*out), h_low ); + + s_worldData.surfaces = out; + s_worldData.numsurfaces = count; + s_worldData.surfacesViewCount = ri.Hunk_Alloc ( count * sizeof(*s_worldData.surfacesViewCount), h_low ); + s_worldData.surfacesDlightBits = ri.Hunk_Alloc ( count * sizeof(*s_worldData.surfacesDlightBits), h_low ); + s_worldData.surfacesPshadowBits = ri.Hunk_Alloc ( count * sizeof(*s_worldData.surfacesPshadowBits), h_low ); + + // load hdr vertex colors + if (r_hdr->integer) + { + char filename[MAX_QPATH]; + int size; + + Com_sprintf( filename, sizeof( filename ), "maps/%s/vertlight.raw", s_worldData.baseName); + //ri.Printf(PRINT_ALL, "looking for %s\n", filename); + + size = ri.FS_ReadFile(filename, (void **)&hdrVertColors); + + if (hdrVertColors) + { + //ri.Printf(PRINT_ALL, "Found!\n"); + if (size != sizeof(float) * 3 * (verts->filelen / sizeof(*dv))) + ri.Error(ERR_DROP, "Bad size for %s (%i, expected %i)!\n", filename, size, (int)((sizeof(float)) * 3 * (verts->filelen / sizeof(*dv)))); + } + } + + + // Two passes, allocate surfaces first, then load them full of data + // This ensures surfaces are close together to reduce L2 cache misses when using VBOs, + // which don't actually use the verts and tris + in = (void *)(fileBase + surfs->fileofs); + out = s_worldData.surfaces; + for ( i = 0 ; i < count ; i++, in++, out++ ) { + switch ( LittleLong( in->surfaceType ) ) { + case MST_PATCH: + // FIXME: do this + break; + case MST_TRIANGLE_SOUP: + out->data = ri.Hunk_Alloc( sizeof(srfTriangles_t), h_low); + break; + case MST_PLANAR: + out->data = ri.Hunk_Alloc( sizeof(srfSurfaceFace_t), h_low); + break; + case MST_FLARE: + out->data = ri.Hunk_Alloc( sizeof(srfFlare_t), h_low); + break; + default: + break; + } + } + + in = (void *)(fileBase + surfs->fileofs); + out = s_worldData.surfaces; + for ( i = 0 ; i < count ; i++, in++, out++ ) { + switch ( LittleLong( in->surfaceType ) ) { + case MST_PATCH: + ParseMesh ( in, dv, hdrVertColors, out ); + { + srfGridMesh_t *surface = (srfGridMesh_t *)out->data; + + out->cullinfo.type = CULLINFO_BOX | CULLINFO_SPHERE; + VectorCopy(surface->meshBounds[0], out->cullinfo.bounds[0]); + VectorCopy(surface->meshBounds[1], out->cullinfo.bounds[1]); + VectorCopy(surface->localOrigin, out->cullinfo.localOrigin); + out->cullinfo.radius = surface->meshRadius; + } + numMeshes++; + break; + case MST_TRIANGLE_SOUP: + ParseTriSurf( in, dv, hdrVertColors, out, indexes ); + numTriSurfs++; + break; + case MST_PLANAR: + ParseFace( in, dv, hdrVertColors, out, indexes ); + numFaces++; + break; + case MST_FLARE: + ParseFlare( in, dv, out, indexes ); + { + out->cullinfo.type = CULLINFO_NONE; + } + numFlares++; + break; + default: + ri.Error( ERR_DROP, "Bad surfaceType" ); + } + } + + if (hdrVertColors) + { + ri.FS_FreeFile(hdrVertColors); + } + +#ifdef PATCH_STITCHING + R_StitchAllPatches(); +#endif + + R_FixSharedVertexLodError(); + +#ifdef PATCH_STITCHING + R_MovePatchSurfacesToHunk(); +#endif + + ri.Printf( PRINT_ALL, "...loaded %d faces, %i meshes, %i trisurfs, %i flares\n", + numFaces, numMeshes, numTriSurfs, numFlares ); +} + + + +/* +================= +R_LoadSubmodels +================= +*/ +static void R_LoadSubmodels( lump_t *l ) { + dmodel_t *in; + bmodel_t *out; + int i, j, count; + + in = (void *)(fileBase + l->fileofs); + if (l->filelen % sizeof(*in)) + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + count = l->filelen / sizeof(*in); + + s_worldData.numBModels = count; + s_worldData.bmodels = out = ri.Hunk_Alloc( count * sizeof(*out), h_low ); + + for ( i=0 ; itype = MOD_BRUSH; + model->bmodel = out; + Com_sprintf( model->name, sizeof( model->name ), "*%d", i ); + + for (j=0 ; j<3 ; j++) { + out->bounds[0][j] = LittleFloat (in->mins[j]); + out->bounds[1][j] = LittleFloat (in->maxs[j]); + } + + out->firstSurface = LittleLong( in->firstSurface ); + out->numSurfaces = LittleLong( in->numSurfaces ); + + if(i == 0) + { + // Tr3B: add this for limiting VBO surface creation + s_worldData.numWorldSurfaces = out->numSurfaces; + } + } +} + + + +//================================================================== + +/* +================= +R_SetParent +================= +*/ +static void R_SetParent (mnode_t *node, mnode_t *parent) +{ + node->parent = parent; + if (node->contents != -1) + return; + R_SetParent (node->children[0], node); + R_SetParent (node->children[1], node); +} + +/* +================= +R_LoadNodesAndLeafs +================= +*/ +static void R_LoadNodesAndLeafs (lump_t *nodeLump, lump_t *leafLump) { + int i, j, p; + dnode_t *in; + dleaf_t *inLeaf; + mnode_t *out; + int numNodes, numLeafs; + + in = (void *)(fileBase + nodeLump->fileofs); + if (nodeLump->filelen % sizeof(dnode_t) || + leafLump->filelen % sizeof(dleaf_t) ) { + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + } + numNodes = nodeLump->filelen / sizeof(dnode_t); + numLeafs = leafLump->filelen / sizeof(dleaf_t); + + out = ri.Hunk_Alloc ( (numNodes + numLeafs) * sizeof(*out), h_low); + + s_worldData.nodes = out; + s_worldData.numnodes = numNodes + numLeafs; + s_worldData.numDecisionNodes = numNodes; + + // load nodes + for ( i=0 ; imins[j] = LittleLong (in->mins[j]); + out->maxs[j] = LittleLong (in->maxs[j]); + } + + p = LittleLong(in->planeNum); + out->plane = s_worldData.planes + p; + + out->contents = CONTENTS_NODE; // differentiate from leafs + + for (j=0 ; j<2 ; j++) + { + p = LittleLong (in->children[j]); + if (p >= 0) + out->children[j] = s_worldData.nodes + p; + else + out->children[j] = s_worldData.nodes + numNodes + (-1 - p); + } + } + + // load leafs + inLeaf = (void *)(fileBase + leafLump->fileofs); + for ( i=0 ; imins[j] = LittleLong (inLeaf->mins[j]); + out->maxs[j] = LittleLong (inLeaf->maxs[j]); + } + + out->cluster = LittleLong(inLeaf->cluster); + out->area = LittleLong(inLeaf->area); + + if ( out->cluster >= s_worldData.numClusters ) { + s_worldData.numClusters = out->cluster + 1; + } + + out->firstmarksurface = LittleLong(inLeaf->firstLeafSurface); + out->nummarksurfaces = LittleLong(inLeaf->numLeafSurfaces); + } + + // chain decendants + R_SetParent (s_worldData.nodes, NULL); +} + +//============================================================================= + +/* +================= +R_LoadShaders +================= +*/ +static void R_LoadShaders( lump_t *l ) { + int i, count; + dshader_t *in, *out; + + in = (void *)(fileBase + l->fileofs); + if (l->filelen % sizeof(*in)) + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + count = l->filelen / sizeof(*in); + out = ri.Hunk_Alloc ( count*sizeof(*out), h_low ); + + s_worldData.shaders = out; + s_worldData.numShaders = count; + + Com_Memcpy( out, in, count*sizeof(*out) ); + + for ( i=0 ; ifileofs); + if (l->filelen % sizeof(*in)) + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + count = l->filelen / sizeof(*in); + out = ri.Hunk_Alloc ( count*sizeof(*out), h_low); + + s_worldData.marksurfaces = out; + s_worldData.nummarksurfaces = count; + + for ( i=0 ; ifileofs); + if (l->filelen % sizeof(*in)) + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + count = l->filelen / sizeof(*in); + out = ri.Hunk_Alloc ( count*2*sizeof(*out), h_low); + + s_worldData.planes = out; + s_worldData.numplanes = count; + + for ( i=0 ; inormal[j] = LittleFloat (in->normal[j]); + if (out->normal[j] < 0) { + bits |= 1<dist = LittleFloat (in->dist); + out->type = PlaneTypeForNormal( out->normal ); + out->signbits = bits; + } +} + +/* +================= +R_LoadFogs + +================= +*/ +static void R_LoadFogs( lump_t *l, lump_t *brushesLump, lump_t *sidesLump ) { + int i; + fog_t *out; + dfog_t *fogs; + dbrush_t *brushes, *brush; + dbrushside_t *sides; + int count, brushesCount, sidesCount; + int sideNum; + int planeNum; + shader_t *shader; + float d; + int firstSide; + + fogs = (void *)(fileBase + l->fileofs); + if (l->filelen % sizeof(*fogs)) { + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + } + count = l->filelen / sizeof(*fogs); + + // create fog strucutres for them + s_worldData.numfogs = count + 1; + s_worldData.fogs = ri.Hunk_Alloc ( s_worldData.numfogs*sizeof(*out), h_low); + out = s_worldData.fogs + 1; + + if ( !count ) { + return; + } + + brushes = (void *)(fileBase + brushesLump->fileofs); + if (brushesLump->filelen % sizeof(*brushes)) { + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + } + brushesCount = brushesLump->filelen / sizeof(*brushes); + + sides = (void *)(fileBase + sidesLump->fileofs); + if (sidesLump->filelen % sizeof(*sides)) { + ri.Error (ERR_DROP, "LoadMap: funny lump size in %s",s_worldData.name); + } + sidesCount = sidesLump->filelen / sizeof(*sides); + + for ( i=0 ; ioriginalBrushNumber = LittleLong( fogs->brushNum ); + + if ( (unsigned)out->originalBrushNumber >= brushesCount ) { + ri.Error( ERR_DROP, "fog brushNumber out of range" ); + } + brush = brushes + out->originalBrushNumber; + + firstSide = LittleLong( brush->firstSide ); + + if ( (unsigned)firstSide > sidesCount - 6 ) { + ri.Error( ERR_DROP, "fog brush sideNumber out of range" ); + } + + // brushes are always sorted with the axial sides first + sideNum = firstSide + 0; + planeNum = LittleLong( sides[ sideNum ].planeNum ); + out->bounds[0][0] = -s_worldData.planes[ planeNum ].dist; + + sideNum = firstSide + 1; + planeNum = LittleLong( sides[ sideNum ].planeNum ); + out->bounds[1][0] = s_worldData.planes[ planeNum ].dist; + + sideNum = firstSide + 2; + planeNum = LittleLong( sides[ sideNum ].planeNum ); + out->bounds[0][1] = -s_worldData.planes[ planeNum ].dist; + + sideNum = firstSide + 3; + planeNum = LittleLong( sides[ sideNum ].planeNum ); + out->bounds[1][1] = s_worldData.planes[ planeNum ].dist; + + sideNum = firstSide + 4; + planeNum = LittleLong( sides[ sideNum ].planeNum ); + out->bounds[0][2] = -s_worldData.planes[ planeNum ].dist; + + sideNum = firstSide + 5; + planeNum = LittleLong( sides[ sideNum ].planeNum ); + out->bounds[1][2] = s_worldData.planes[ planeNum ].dist; + + // get information from the shader for fog parameters + shader = R_FindShader( fogs->shader, LIGHTMAP_NONE, qtrue ); + + out->parms = shader->fogParms; + + out->colorInt = ColorBytes4 ( shader->fogParms.color[0] * tr.identityLight, + shader->fogParms.color[1] * tr.identityLight, + shader->fogParms.color[2] * tr.identityLight, 1.0 ); + + d = shader->fogParms.depthForOpaque < 1 ? 1 : shader->fogParms.depthForOpaque; + out->tcScale = 1.0f / ( d * 8 ); + + // set the gradient vector + sideNum = LittleLong( fogs->visibleSide ); + + if ( sideNum == -1 ) { + out->hasSurface = qfalse; + } else { + out->hasSurface = qtrue; + planeNum = LittleLong( sides[ firstSide + sideNum ].planeNum ); + VectorSubtract( vec3_origin, s_worldData.planes[ planeNum ].normal, out->surface ); + out->surface[3] = -s_worldData.planes[ planeNum ].dist; + } + + out++; + } + +} + + +/* +================ +R_LoadLightGrid + +================ +*/ +void R_LoadLightGrid( lump_t *l ) { + int i; + vec3_t maxs; + int numGridPoints; + world_t *w; + float *wMins, *wMaxs; + + w = &s_worldData; + + w->lightGridInverseSize[0] = 1.0f / w->lightGridSize[0]; + w->lightGridInverseSize[1] = 1.0f / w->lightGridSize[1]; + w->lightGridInverseSize[2] = 1.0f / w->lightGridSize[2]; + + wMins = w->bmodels[0].bounds[0]; + wMaxs = w->bmodels[0].bounds[1]; + + for ( i = 0 ; i < 3 ; i++ ) { + w->lightGridOrigin[i] = w->lightGridSize[i] * ceil( wMins[i] / w->lightGridSize[i] ); + maxs[i] = w->lightGridSize[i] * floor( wMaxs[i] / w->lightGridSize[i] ); + w->lightGridBounds[i] = (maxs[i] - w->lightGridOrigin[i])/w->lightGridSize[i] + 1; + } + + numGridPoints = w->lightGridBounds[0] * w->lightGridBounds[1] * w->lightGridBounds[2]; + + if ( l->filelen != numGridPoints * 8 ) { + ri.Printf( PRINT_WARNING, "WARNING: light grid mismatch\n" ); + w->lightGridData = NULL; + return; + } + + w->lightGridData = ri.Hunk_Alloc( l->filelen, h_low ); + Com_Memcpy( w->lightGridData, (void *)(fileBase + l->fileofs), l->filelen ); + + // deal with overbright bits + for ( i = 0 ; i < numGridPoints ; i++ ) { + R_ColorShiftLightingBytes( &w->lightGridData[i*8], &w->lightGridData[i*8] ); + R_ColorShiftLightingBytes( &w->lightGridData[i*8+3], &w->lightGridData[i*8+3] ); + } + + // load hdr lightgrid + if (r_hdr->integer) + { + char filename[MAX_QPATH]; + float *hdrLightGrid; + int size; + + Com_sprintf( filename, sizeof( filename ), "maps/%s/lightgrid.raw", s_worldData.baseName); + //ri.Printf(PRINT_ALL, "looking for %s\n", filename); + + size = ri.FS_ReadFile(filename, (void **)&hdrLightGrid); + + if (hdrLightGrid) + { + float lightScale = pow(2, r_mapOverBrightBits->integer - tr.overbrightBits); + + //ri.Printf(PRINT_ALL, "found!\n"); + + if (size != sizeof(float) * 6 * numGridPoints) + { + ri.Error(ERR_DROP, "Bad size for %s (%i, expected %i)!\n", filename, size, (int)(sizeof(float)) * 6 * numGridPoints); + } + + w->hdrLightGrid = ri.Hunk_Alloc(size, h_low); + + for (i = 0; i < numGridPoints ; i++) + { + w->hdrLightGrid[i * 6 ] = hdrLightGrid[i * 6 ] * lightScale; + w->hdrLightGrid[i * 6 + 1] = hdrLightGrid[i * 6 + 1] * lightScale; + w->hdrLightGrid[i * 6 + 2] = hdrLightGrid[i * 6 + 2] * lightScale; + w->hdrLightGrid[i * 6 + 3] = hdrLightGrid[i * 6 + 3] * lightScale; + w->hdrLightGrid[i * 6 + 4] = hdrLightGrid[i * 6 + 4] * lightScale; + w->hdrLightGrid[i * 6 + 5] = hdrLightGrid[i * 6 + 5] * lightScale; + } + } + + if (hdrLightGrid) + ri.FS_FreeFile(hdrLightGrid); + } +} + +/* +================ +R_LoadEntities +================ +*/ +void R_LoadEntities( lump_t *l ) { + char *p, *token, *s; + char keyname[MAX_TOKEN_CHARS]; + char value[MAX_TOKEN_CHARS]; + world_t *w; + + w = &s_worldData; + w->lightGridSize[0] = 64; + w->lightGridSize[1] = 64; + w->lightGridSize[2] = 128; + + p = (char *)(fileBase + l->fileofs); + + // store for reference by the cgame + w->entityString = ri.Hunk_Alloc( l->filelen + 1, h_low ); + strcpy( w->entityString, p ); + w->entityParsePoint = w->entityString; + + token = COM_ParseExt( &p, qtrue ); + if (!*token || *token != '{') { + return; + } + + // only parse the world spawn + while ( 1 ) { + // parse key + token = COM_ParseExt( &p, qtrue ); + + if ( !*token || *token == '}' ) { + break; + } + Q_strncpyz(keyname, token, sizeof(keyname)); + + // parse value + token = COM_ParseExt( &p, qtrue ); + + if ( !*token || *token == '}' ) { + break; + } + Q_strncpyz(value, token, sizeof(value)); + + // check for remapping of shaders for vertex lighting + s = "vertexremapshader"; + if (!Q_strncmp(keyname, s, strlen(s)) ) { + s = strchr(value, ';'); + if (!s) { + ri.Printf( PRINT_WARNING, "WARNING: no semi colon in vertexshaderremap '%s'\n", value ); + break; + } + *s++ = 0; + if (r_vertexLight->integer) { + R_RemapShader(value, s, "0"); + } + continue; + } + // check for remapping of shaders + s = "remapshader"; + if (!Q_strncmp(keyname, s, strlen(s)) ) { + s = strchr(value, ';'); + if (!s) { + ri.Printf( PRINT_WARNING, "WARNING: no semi colon in shaderremap '%s'\n", value ); + break; + } + *s++ = 0; + R_RemapShader(value, s, "0"); + continue; + } + // check for a different grid size + if (!Q_stricmp(keyname, "gridsize")) { + sscanf(value, "%f %f %f", &w->lightGridSize[0], &w->lightGridSize[1], &w->lightGridSize[2] ); + continue; + } + + // check for auto exposure + if (!Q_stricmp(keyname, "autoExposureMinMax")) { + sscanf(value, "%f %f", &tr.autoExposureMinMax[0], &tr.autoExposureMinMax[1]); + continue; + } + } +} + +/* +================= +R_GetEntityToken +================= +*/ +qboolean R_GetEntityToken( char *buffer, int size ) { + const char *s; + + s = COM_Parse( &s_worldData.entityParsePoint ); + Q_strncpyz( buffer, s, size ); + if ( !s_worldData.entityParsePoint || !s[0] ) { + s_worldData.entityParsePoint = s_worldData.entityString; + return qfalse; + } else { + return qtrue; + } +} + + +/* +================= +R_MergeLeafSurfaces + +Merges surfaces that share a common leaf +================= +*/ +void R_MergeLeafSurfaces(void) +{ + int i, j, k; + int numWorldSurfaces; + int mergedSurfIndex; + int numMergedSurfaces; + int numUnmergedSurfaces; + IBO_t *ibo; + + msurface_t *mergedSurf; + + glIndex_t *iboIndexes, *outIboIndexes; + int numIboIndexes; + + int startTime, endTime; + + startTime = ri.Milliseconds(); + + numWorldSurfaces = s_worldData.numWorldSurfaces; + + // use viewcount to keep track of mergers + for (i = 0; i < numWorldSurfaces; i++) + { + s_worldData.surfacesViewCount[i] = -1; + } + + // create ibo + ibo = tr.ibos[tr.numIBOs++] = ri.Hunk_Alloc(sizeof(*ibo), h_low); + memset(ibo, 0, sizeof(*ibo)); + Q_strncpyz(ibo->name, "staticWorldMesh_IBO_mergedSurfs", sizeof(ibo->name)); + + // allocate more than we need + iboIndexes = outIboIndexes = ri.Malloc(s_worldData.ibo->indexesSize); + + // mark matching surfaces + for (i = 0; i < s_worldData.numnodes - s_worldData.numDecisionNodes; i++) + { + mnode_t *leaf = s_worldData.nodes + s_worldData.numDecisionNodes + i; + + for (j = 0; j < leaf->nummarksurfaces; j++) + { + msurface_t *surf1; + shader_t *shader1; + int fogIndex1; + int surfNum1; + + surfNum1 = *(s_worldData.marksurfaces + leaf->firstmarksurface + j); + + if (s_worldData.surfacesViewCount[surfNum1] != -1) + continue; + + surf1 = s_worldData.surfaces + surfNum1; + + if ((*surf1->data != SF_GRID) && (*surf1->data != SF_TRIANGLES) && (*surf1->data != SF_FACE)) + continue; + + shader1 = surf1->shader; + + if(shader1->isSky) + continue; + + if(shader1->isPortal) + continue; + + if(ShaderRequiresCPUDeforms(shader1)) + continue; + + fogIndex1 = surf1->fogIndex; + + s_worldData.surfacesViewCount[surfNum1] = surfNum1; + + for (k = j + 1; k < leaf->nummarksurfaces; k++) + { + msurface_t *surf2; + shader_t *shader2; + int fogIndex2; + int surfNum2; + + surfNum2 = *(s_worldData.marksurfaces + leaf->firstmarksurface + k); + + if (s_worldData.surfacesViewCount[surfNum2] != -1) + continue; + + surf2 = s_worldData.surfaces + surfNum2; + + if ((*surf2->data != SF_GRID) && (*surf2->data != SF_TRIANGLES) && (*surf2->data != SF_FACE)) + continue; + + shader2 = surf2->shader; + + if (shader1 != shader2) + continue; + + fogIndex2 = surf2->fogIndex; + + if (fogIndex1 != fogIndex2) + continue; + + s_worldData.surfacesViewCount[surfNum2] = surfNum1; + } + } + } + + // don't add surfaces that don't merge to any others to the merged list + for (i = 0; i < numWorldSurfaces; i++) + { + qboolean merges = qfalse; + + if (s_worldData.surfacesViewCount[i] != i) + continue; + + for (j = 0; j < numWorldSurfaces; j++) + { + if (j == i) + continue; + + if (s_worldData.surfacesViewCount[j] == i) + { + merges = qtrue; + break; + } + } + + if (!merges) + s_worldData.surfacesViewCount[i] = -1; + } + + // count merged/unmerged surfaces + numMergedSurfaces = 0; + numUnmergedSurfaces = 0; + for (i = 0; i < numWorldSurfaces; i++) + { + if (s_worldData.surfacesViewCount[i] == i) + { + numMergedSurfaces++; + } + else if (s_worldData.surfacesViewCount[i] == -1) + { + numUnmergedSurfaces++; + } + } + + // Allocate merged surfaces + s_worldData.mergedSurfaces = ri.Hunk_Alloc(sizeof(*s_worldData.mergedSurfaces) * numMergedSurfaces, h_low); + s_worldData.mergedSurfacesViewCount = ri.Hunk_Alloc(sizeof(*s_worldData.mergedSurfacesViewCount) * numMergedSurfaces, h_low); + s_worldData.mergedSurfacesDlightBits = ri.Hunk_Alloc(sizeof(*s_worldData.mergedSurfacesDlightBits) * numMergedSurfaces, h_low); + s_worldData.mergedSurfacesPshadowBits = ri.Hunk_Alloc(sizeof(*s_worldData.mergedSurfacesPshadowBits) * numMergedSurfaces, h_low); + s_worldData.numMergedSurfaces = numMergedSurfaces; + + // view surfaces are like mark surfaces, except negative ones represent merged surfaces + // -1 represents 0, -2 represents 1, and so on + s_worldData.viewSurfaces = ri.Hunk_Alloc(sizeof(*s_worldData.viewSurfaces) * s_worldData.nummarksurfaces, h_low); + + // copy view surfaces into mark surfaces + for (i = 0; i < s_worldData.nummarksurfaces; i++) + { + s_worldData.viewSurfaces[i] = s_worldData.marksurfaces[i]; + } + + // actually merge surfaces + numIboIndexes = 0; + mergedSurfIndex = 0; + mergedSurf = s_worldData.mergedSurfaces; + for (i = 0; i < numWorldSurfaces; i++) + { + msurface_t *surf1; + + vec3_t bounds[2]; + + int numSurfsToMerge; + int numTriangles; + int numVerts; + int firstIndex; + + srfVBOMesh_t *vboSurf; + + if (s_worldData.surfacesViewCount[i] != i) + continue; + + surf1 = s_worldData.surfaces + i; + + // count verts, indexes, and surfaces + numSurfsToMerge = 0; + numTriangles = 0; + numVerts = 0; + for (j = 0; j < numWorldSurfaces; j++) + { + msurface_t *surf2; + + if (s_worldData.surfacesViewCount[j] != i) + continue; + + surf2 = s_worldData.surfaces + j; + + switch(*surf2->data) + { + case SF_FACE: + { + srfSurfaceFace_t *face; + + face = (srfSurfaceFace_t *) surf2->data; + numTriangles += face->numTriangles; + numVerts += face->numVerts; + } + break; + + case SF_GRID: + { + srfGridMesh_t *grid; + + grid = (srfGridMesh_t *) surf2->data; + numTriangles += grid->numTriangles; + numVerts += grid->numVerts; + } + break; + + case SF_TRIANGLES: + { + srfTriangles_t *tris; + + tris = (srfTriangles_t *) surf2->data; + numTriangles += tris->numTriangles; + numVerts += tris->numVerts; + } + break; + + default: + break; + } + + numSurfsToMerge++; + } + + if (numVerts == 0 || numTriangles == 0 || numSurfsToMerge < 2) + { + continue; + } + + // Merge surfaces (indexes) and calculate bounds + ClearBounds(bounds[0], bounds[1]); + firstIndex = numIboIndexes; + for (j = 0; j < numWorldSurfaces; j++) + { + msurface_t *surf2; + + if (s_worldData.surfacesViewCount[j] != i) + continue; + + surf2 = s_worldData.surfaces + j; + + AddPointToBounds(surf2->cullinfo.bounds[0], bounds[0], bounds[1]); + AddPointToBounds(surf2->cullinfo.bounds[1], bounds[0], bounds[1]); + + switch(*surf2->data) + { + case SF_FACE: + { + srfSurfaceFace_t *face; + + face = (srfSurfaceFace_t *) surf2->data; + + for (k = 0; k < face->numTriangles; k++) + { + *outIboIndexes++ = face->triangles[k].indexes[0] + face->firstVert; + *outIboIndexes++ = face->triangles[k].indexes[1] + face->firstVert; + *outIboIndexes++ = face->triangles[k].indexes[2] + face->firstVert; + numIboIndexes += 3; + } + } + break; + + case SF_GRID: + { + srfGridMesh_t *grid; + + grid = (srfGridMesh_t *) surf2->data; + + for (k = 0; k < grid->numTriangles; k++) + { + *outIboIndexes++ = grid->triangles[k].indexes[0] + grid->firstVert; + *outIboIndexes++ = grid->triangles[k].indexes[1] + grid->firstVert; + *outIboIndexes++ = grid->triangles[k].indexes[2] + grid->firstVert; + numIboIndexes += 3; + } + } + break; + + case SF_TRIANGLES: + { + srfTriangles_t *tris; + + tris = (srfTriangles_t *) surf2->data; + + for (k = 0; k < tris->numTriangles; k++) + { + *outIboIndexes++ = tris->triangles[k].indexes[0] + tris->firstVert; + *outIboIndexes++ = tris->triangles[k].indexes[1] + tris->firstVert; + *outIboIndexes++ = tris->triangles[k].indexes[2] + tris->firstVert; + numIboIndexes += 3; + } + } + break; + + // never happens, but silences a compile warning + default: + break; + } + } + + vboSurf = ri.Hunk_Alloc(sizeof(*vboSurf), h_low); + memset(vboSurf, 0, sizeof(*vboSurf)); + vboSurf->surfaceType = SF_VBO_MESH; + + vboSurf->vbo = s_worldData.vbo; + vboSurf->ibo = ibo; + + vboSurf->numIndexes = numTriangles * 3; + vboSurf->numVerts = numVerts; + vboSurf->firstIndex = firstIndex; + + vboSurf->minIndex = *(iboIndexes + firstIndex); + vboSurf->maxIndex = *(iboIndexes + firstIndex); + + for (j = 1; j < numTriangles * 3; j++) + { + vboSurf->minIndex = MIN(vboSurf->minIndex, *(iboIndexes + firstIndex + j)); + vboSurf->maxIndex = MAX(vboSurf->maxIndex, *(iboIndexes + firstIndex + j)); + } + + vboSurf->shader = surf1->shader; + vboSurf->fogIndex = surf1->fogIndex; + + VectorCopy(bounds[0], vboSurf->bounds[0]); + VectorCopy(bounds[1], vboSurf->bounds[1]); + + VectorCopy(bounds[0], mergedSurf->cullinfo.bounds[0]); + VectorCopy(bounds[1], mergedSurf->cullinfo.bounds[1]); + + mergedSurf->cullinfo.type = CULLINFO_BOX; + mergedSurf->data = (surfaceType_t *)vboSurf; + mergedSurf->fogIndex = surf1->fogIndex; + mergedSurf->shader = surf1->shader; + + // redirect view surfaces to this surf + for (j = 0; j < numWorldSurfaces; j++) + { + if (s_worldData.surfacesViewCount[j] != i) + continue; + + for (k = 0; k < s_worldData.nummarksurfaces; k++) + { + int *mark = s_worldData.marksurfaces + k; + int *view = s_worldData.viewSurfaces + k; + + if (*mark == j) + *view = -(mergedSurfIndex + 1); + } + } + + mergedSurfIndex++; + mergedSurf++; + } + + // finish up the ibo + R_IssuePendingRenderCommands(); + + qglGenBuffersARB(1, &ibo->indexesVBO); + + R_BindIBO(ibo); + + qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, numIboIndexes * sizeof(*iboIndexes), iboIndexes, GL_STATIC_DRAW_ARB); + + R_BindNullIBO(); + + GL_CheckErrors(); + + ri.Free(iboIndexes); + + endTime = ri.Milliseconds(); + + ri.Printf(PRINT_ALL, "Processed %d surfaces into %d merged, %d unmerged in %5.2f seconds\n", + numWorldSurfaces, numMergedSurfaces, numUnmergedSurfaces, (endTime - startTime) / 1000.0f); + + // reset viewcounts + for (i = 0; i < numWorldSurfaces; i++) + { + s_worldData.surfacesViewCount[i] = -1; + } +} + + +void R_CalcVertexLightDirs( void ) +{ + int i, k; + msurface_t *surface; + + for(k = 0, surface = &s_worldData.surfaces[0]; k < s_worldData.numsurfaces /* s_worldData.numWorldSurfaces */; k++, surface++) + { + if(*surface->data == SF_FACE) + { + srfSurfaceFace_t *srf = (srfSurfaceFace_t *) surface->data; + + if(srf->numVerts) + { + for(i = 0; i < srf->numVerts; i++) + { + R_LightDirForPoint( srf->verts[i].xyz, srf->verts[i].lightdir, srf->verts[i].normal, &s_worldData ); + } + } + } + else if(*surface->data == SF_GRID) + { + srfGridMesh_t *srf = (srfGridMesh_t *) surface->data; + + if(srf->numVerts) + { + for(i = 0; i < srf->numVerts; i++) + { + R_LightDirForPoint( srf->verts[i].xyz, srf->verts[i].lightdir, srf->verts[i].normal, &s_worldData ); + } + } + } + else if(*surface->data == SF_TRIANGLES) + { + srfTriangles_t *srf = (srfTriangles_t *) surface->data; + + if(srf->numVerts) + { + for(i = 0; i < srf->numVerts; i++) + { + R_LightDirForPoint( srf->verts[i].xyz, srf->verts[i].lightdir, srf->verts[i].normal, &s_worldData ); + } + } + } + } +} + + +/* +================= +RE_LoadWorldMap + +Called directly from cgame +================= +*/ +void RE_LoadWorldMap( const char *name ) { + int i; + dheader_t *header; + union { + byte *b; + void *v; + } buffer; + byte *startMarker; + + if ( tr.worldMapLoaded ) { + ri.Error( ERR_DROP, "ERROR: attempted to redundantly load world map" ); + } + + // set default map light scale + tr.mapLightScale = 1.0f; + + // set default sun direction to be used if it isn't + // overridden by a shader + tr.sunDirection[0] = 0.45f; + tr.sunDirection[1] = 0.3f; + tr.sunDirection[2] = 0.9f; + + VectorNormalize( tr.sunDirection ); + + // set default autoexposure settings + tr.autoExposureMinMax[0] = -2.0f; + tr.autoExposureMinMax[1] = 2.0f; + + // set default tone mapping settings + tr.toneMinAvgMaxLevel[0] = -8.0f; + tr.toneMinAvgMaxLevel[1] = -2.0f; + tr.toneMinAvgMaxLevel[2] = 0.0f; + + tr.worldMapLoaded = qtrue; + + // load it + ri.FS_ReadFile( name, &buffer.v ); + if ( !buffer.b ) { + ri.Error (ERR_DROP, "RE_LoadWorldMap: %s not found", name); + } + + // clear tr.world so if the level fails to load, the next + // try will not look at the partially loaded version + tr.world = NULL; + + Com_Memset( &s_worldData, 0, sizeof( s_worldData ) ); + Q_strncpyz( s_worldData.name, name, sizeof( s_worldData.name ) ); + + Q_strncpyz( s_worldData.baseName, COM_SkipPath( s_worldData.name ), sizeof( s_worldData.name ) ); + COM_StripExtension(s_worldData.baseName, s_worldData.baseName, sizeof(s_worldData.baseName)); + + startMarker = ri.Hunk_Alloc(0, h_low); + c_gridVerts = 0; + + header = (dheader_t *)buffer.b; + fileBase = (byte *)header; + + i = LittleLong (header->version); + if ( i != BSP_VERSION ) { + ri.Error (ERR_DROP, "RE_LoadWorldMap: %s has wrong version number (%i should be %i)", + name, i, BSP_VERSION); + } + + // swap all the lumps + for (i=0 ; ilumps[LUMP_ENTITIES] ); + R_LoadShaders( &header->lumps[LUMP_SHADERS] ); + R_LoadLightmaps( &header->lumps[LUMP_LIGHTMAPS], &header->lumps[LUMP_SURFACES] ); + R_LoadPlanes (&header->lumps[LUMP_PLANES]); + R_LoadFogs( &header->lumps[LUMP_FOGS], &header->lumps[LUMP_BRUSHES], &header->lumps[LUMP_BRUSHSIDES] ); + R_LoadSurfaces( &header->lumps[LUMP_SURFACES], &header->lumps[LUMP_DRAWVERTS], &header->lumps[LUMP_DRAWINDEXES] ); + R_LoadMarksurfaces (&header->lumps[LUMP_LEAFSURFACES]); + R_LoadNodesAndLeafs (&header->lumps[LUMP_NODES], &header->lumps[LUMP_LEAFS]); + R_LoadSubmodels (&header->lumps[LUMP_MODELS]); + R_LoadVisibility( &header->lumps[LUMP_VISIBILITY] ); + R_LoadLightGrid( &header->lumps[LUMP_LIGHTGRID] ); + + // determine vertex light directions + R_CalcVertexLightDirs(); + + // create static VBOS from the world + R_CreateWorldVBO(); + if (r_mergeLeafSurfaces->integer) + { + R_MergeLeafSurfaces(); + } + + s_worldData.dataSize = (byte *)ri.Hunk_Alloc(0, h_low) - startMarker; + + // only set tr.world now that we know the entire level has loaded properly + tr.world = &s_worldData; + + // make sure the VBO glState entries are safe + R_BindNullVBO(); + R_BindNullIBO(); + + ri.FS_FreeFile( buffer.v ); +} diff --git a/src/renderergl2/tr_cmds.c b/src/renderergl2/tr_cmds.c new file mode 100644 index 00000000..622b401a --- /dev/null +++ b/src/renderergl2/tr_cmds.c @@ -0,0 +1,668 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +#include "tr_local.h" + +volatile renderCommandList_t *renderCommandList; + +/* +===================== +R_PerformanceCounters +===================== +*/ +void R_PerformanceCounters( void ) { + if ( !r_speeds->integer ) { + // clear the counters even if we aren't printing + Com_Memset( &tr.pc, 0, sizeof( tr.pc ) ); + Com_Memset( &backEnd.pc, 0, sizeof( backEnd.pc ) ); + return; + } + + if (r_speeds->integer == 1) { + ri.Printf (PRINT_ALL, "%i/%i/%i shaders/batches/surfs %i leafs %i verts %i/%i tris %.2f mtex %.2f dc\n", + backEnd.pc.c_shaders, backEnd.pc.c_surfBatches, backEnd.pc.c_surfaces, tr.pc.c_leafs, backEnd.pc.c_vertexes, + backEnd.pc.c_indexes/3, backEnd.pc.c_totalIndexes/3, + R_SumOfUsedImages()/(1000000.0f), backEnd.pc.c_overDraw / (float)(glConfig.vidWidth * glConfig.vidHeight) ); + } else if (r_speeds->integer == 2) { + ri.Printf (PRINT_ALL, "(patch) %i sin %i sclip %i sout %i bin %i bclip %i bout\n", + tr.pc.c_sphere_cull_patch_in, tr.pc.c_sphere_cull_patch_clip, tr.pc.c_sphere_cull_patch_out, + tr.pc.c_box_cull_patch_in, tr.pc.c_box_cull_patch_clip, tr.pc.c_box_cull_patch_out ); + ri.Printf (PRINT_ALL, "(md3) %i sin %i sclip %i sout %i bin %i bclip %i bout\n", + tr.pc.c_sphere_cull_md3_in, tr.pc.c_sphere_cull_md3_clip, tr.pc.c_sphere_cull_md3_out, + tr.pc.c_box_cull_md3_in, tr.pc.c_box_cull_md3_clip, tr.pc.c_box_cull_md3_out ); + } else if (r_speeds->integer == 3) { + ri.Printf (PRINT_ALL, "viewcluster: %i\n", tr.viewCluster ); + } else if (r_speeds->integer == 4) { + if ( backEnd.pc.c_dlightVertexes ) { + ri.Printf (PRINT_ALL, "dlight srf:%i culled:%i verts:%i tris:%i\n", + tr.pc.c_dlightSurfaces, tr.pc.c_dlightSurfacesCulled, + backEnd.pc.c_dlightVertexes, backEnd.pc.c_dlightIndexes / 3 ); + } + } + else if (r_speeds->integer == 5 ) + { + ri.Printf( PRINT_ALL, "zFar: %.0f\n", tr.viewParms.zFar ); + } + else if (r_speeds->integer == 6 ) + { + ri.Printf( PRINT_ALL, "flare adds:%i tests:%i renders:%i\n", + backEnd.pc.c_flareAdds, backEnd.pc.c_flareTests, backEnd.pc.c_flareRenders ); + } + else if (r_speeds->integer == 7 ) + { + ri.Printf( PRINT_ALL, "VBO draws: static %i dynamic %i\nMultidraws: %i merged %i\n", + backEnd.pc.c_staticVboDraws, backEnd.pc.c_dynamicVboDraws, backEnd.pc.c_multidraws, backEnd.pc.c_multidrawsMerged ); + ri.Printf( PRINT_ALL, "GLSL binds: %i draws: gen %i light %i fog %i dlight %i\n", + backEnd.pc.c_glslShaderBinds, backEnd.pc.c_genericDraws, backEnd.pc.c_lightallDraws, backEnd.pc.c_fogDraws, backEnd.pc.c_dlightDraws); + } + + Com_Memset( &tr.pc, 0, sizeof( tr.pc ) ); + Com_Memset( &backEnd.pc, 0, sizeof( backEnd.pc ) ); +} + + +/* +==================== +R_IssueRenderCommands +==================== +*/ +void R_IssueRenderCommands( qboolean runPerformanceCounters ) { + renderCommandList_t *cmdList; + + cmdList = &backEndData->commands; + assert(cmdList); + // add an end-of-list command + *(int *)(cmdList->cmds + cmdList->used) = RC_END_OF_LIST; + + // clear it out, in case this is a sync and not a buffer flip + cmdList->used = 0; + + if ( runPerformanceCounters ) { + R_PerformanceCounters(); + } + + // actually start the commands going + if ( !r_skipBackEnd->integer ) { + // let it start on the new batch + RB_ExecuteRenderCommands( cmdList->cmds ); + } +} + + +/* +==================== +R_IssuePendingRenderCommands + +Issue any pending commands and wait for them to complete. +==================== +*/ +void R_IssuePendingRenderCommands( void ) { + if ( !tr.registered ) { + return; + } + R_IssueRenderCommands( qfalse ); +} + +/* +============ +R_GetCommandBuffer + +make sure there is enough command space +============ +*/ +void *R_GetCommandBuffer( int bytes ) { + renderCommandList_t *cmdList; + + cmdList = &backEndData->commands; + bytes = PAD(bytes, sizeof(void *)); + + // always leave room for the end of list command + if ( cmdList->used + bytes + 4 > MAX_RENDER_COMMANDS ) { + if ( bytes > MAX_RENDER_COMMANDS - 4 ) { + ri.Error( ERR_FATAL, "R_GetCommandBuffer: bad size %i", bytes ); + } + // if we run out of room, just start dropping commands + return NULL; + } + + cmdList->used += bytes; + + return cmdList->cmds + cmdList->used - bytes; +} + + +/* +============= +R_AddDrawSurfCmd + +============= +*/ +void R_AddDrawSurfCmd( drawSurf_t *drawSurfs, int numDrawSurfs ) { + drawSurfsCommand_t *cmd; + + cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + if ( !cmd ) { + return; + } + cmd->commandId = RC_DRAW_SURFS; + + cmd->drawSurfs = drawSurfs; + cmd->numDrawSurfs = numDrawSurfs; + + cmd->refdef = tr.refdef; + cmd->viewParms = tr.viewParms; +} + + +/* +============= +R_AddCapShadowmapCmd + +============= +*/ +void R_AddCapShadowmapCmd( int map, int cubeSide ) { + capShadowmapCommand_t *cmd; + + cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + if ( !cmd ) { + return; + } + cmd->commandId = RC_CAPSHADOWMAP; + + cmd->map = map; + cmd->cubeSide = cubeSide; +} + + +/* +============= +R_PostProcessingCmd + +============= +*/ +void R_AddPostProcessCmd( ) { + postProcessCommand_t *cmd; + + cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + if ( !cmd ) { + return; + } + cmd->commandId = RC_POSTPROCESS; + + cmd->refdef = tr.refdef; + cmd->viewParms = tr.viewParms; +} + +/* +============= +RE_SetColor + +Passing NULL will set the color to white +============= +*/ +void RE_SetColor( const float *rgba ) { + setColorCommand_t *cmd; + + if ( !tr.registered ) { + return; + } + cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + if ( !cmd ) { + return; + } + cmd->commandId = RC_SET_COLOR; + if ( !rgba ) { + static float colorWhite[4] = { 1, 1, 1, 1 }; + + rgba = colorWhite; + } + + cmd->color[0] = rgba[0]; + cmd->color[1] = rgba[1]; + cmd->color[2] = rgba[2]; + cmd->color[3] = rgba[3]; +} + +/* +============= +R_ClipRegion +============= +*/ +static qboolean R_ClipRegion ( float *x, float *y, float *w, float *h, + float *s1, float *t1, float *s2, float *t2 ) { + float left, top, right, bottom; + float _s1, _t1, _s2, _t2; + float clipLeft, clipTop, clipRight, clipBottom; + + if (tr.clipRegion[2] <= tr.clipRegion[0] || + tr.clipRegion[3] <= tr.clipRegion[1] ) { + return qfalse; + } + + left = *x; + top = *y; + right = *x + *w; + bottom = *y + *h; + + _s1 = *s1; + _t1 = *t1; + _s2 = *s2; + _t2 = *t2; + + clipLeft = tr.clipRegion[0]; + clipTop = tr.clipRegion[1]; + clipRight = tr.clipRegion[2]; + clipBottom = tr.clipRegion[3]; + + // Completely clipped away + if ( right <= clipLeft || left >= clipRight || + bottom <= clipTop || top >= clipBottom ) { + return qtrue; + } + + // Clip left edge + if ( left < clipLeft ) { + float f = ( clipLeft - left ) / ( right - left ); + *s1 = ( f * ( _s2 - _s1 ) ) + _s1; + *x = clipLeft; + *w -= ( clipLeft - left ); + } + + // Clip right edge + if ( right > clipRight ) { + float f = ( clipRight - right ) / ( left - right ); + *s2 = ( f * ( _s1 - _s2 ) ) + _s2; + *w = clipRight - *x; + } + + // Clip top edge + if ( top < clipTop ) { + float f = ( clipTop - top ) / ( bottom - top ); + *t1 = ( f * ( _t2 - _t1 ) ) + _t1; + *y = clipTop; + *h -= ( clipTop - top ); + } + + // Clip bottom edge + if ( bottom > clipBottom ) { + float f = ( clipBottom - bottom ) / ( top - bottom ); + *t2 = ( f * ( _t1 - _t2 ) ) + _t2; + *h = clipBottom - *y; + } + + return qfalse; +} + +/* +============= +RE_SetClipRegion +============= +*/ +void RE_SetClipRegion( const float *region ) { + if ( region == NULL ) { + Com_Memset( tr.clipRegion, 0, sizeof( vec4_t ) ); + } else { + Vector4Copy( region, tr.clipRegion ); + } +} + +/* +============= +RE_StretchPic +============= +*/ +void RE_StretchPic ( float x, float y, float w, float h, + float s1, float t1, float s2, float t2, qhandle_t hShader ) { + stretchPicCommand_t *cmd; + + if (!tr.registered) { + return; + } + if (R_ClipRegion(&x, &y, &w, &h, &s1, &t1, &s2, &t2)) { + return; + } + cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + if ( !cmd ) { + return; + } + cmd->commandId = RC_STRETCH_PIC; + cmd->shader = R_GetShaderByHandle( hShader ); + cmd->x = x; + cmd->y = y; + cmd->w = w; + cmd->h = h; + cmd->s1 = s1; + cmd->t1 = t1; + cmd->s2 = s2; + cmd->t2 = t2; +} + +#define MODE_RED_CYAN 1 +#define MODE_RED_BLUE 2 +#define MODE_RED_GREEN 3 +#define MODE_GREEN_MAGENTA 4 +#define MODE_MAX MODE_GREEN_MAGENTA + +void R_SetColorMode(GLboolean *rgba, stereoFrame_t stereoFrame, int colormode) +{ + rgba[0] = rgba[1] = rgba[2] = rgba[3] = GL_TRUE; + + if(colormode > MODE_MAX) + { + if(stereoFrame == STEREO_LEFT) + stereoFrame = STEREO_RIGHT; + else if(stereoFrame == STEREO_RIGHT) + stereoFrame = STEREO_LEFT; + + colormode -= MODE_MAX; + } + + if(colormode == MODE_GREEN_MAGENTA) + { + if(stereoFrame == STEREO_LEFT) + rgba[0] = rgba[2] = GL_FALSE; + else if(stereoFrame == STEREO_RIGHT) + rgba[1] = GL_FALSE; + } + else + { + if(stereoFrame == STEREO_LEFT) + rgba[1] = rgba[2] = GL_FALSE; + else if(stereoFrame == STEREO_RIGHT) + { + rgba[0] = GL_FALSE; + + if(colormode == MODE_RED_BLUE) + rgba[1] = GL_FALSE; + else if(colormode == MODE_RED_GREEN) + rgba[2] = GL_FALSE; + } + } +} + + +/* +==================== +RE_BeginFrame + +If running in stereo, RE_BeginFrame will be called twice +for each RE_EndFrame +==================== +*/ +void RE_BeginFrame( stereoFrame_t stereoFrame ) { + drawBufferCommand_t *cmd = NULL; + colorMaskCommand_t *colcmd = NULL; + + if ( !tr.registered ) { + return; + } + glState.finishCalled = qfalse; + + tr.frameCount++; + tr.frameSceneNum = 0; + + // + // do overdraw measurement + // + if ( r_measureOverdraw->integer ) + { + if ( glConfig.stencilBits < 4 ) + { + ri.Printf( PRINT_ALL, "Warning: not enough stencil bits to measure overdraw: %d\n", glConfig.stencilBits ); + ri.Cvar_Set( "r_measureOverdraw", "0" ); + r_measureOverdraw->modified = qfalse; + } + else if ( r_shadows->integer == 2 ) + { + ri.Printf( PRINT_ALL, "Warning: stencil shadows and overdraw measurement are mutually exclusive\n" ); + ri.Cvar_Set( "r_measureOverdraw", "0" ); + r_measureOverdraw->modified = qfalse; + } + else + { + R_IssuePendingRenderCommands(); + qglEnable( GL_STENCIL_TEST ); + qglStencilMask( ~0U ); + qglClearStencil( 0U ); + qglStencilFunc( GL_ALWAYS, 0U, ~0U ); + qglStencilOp( GL_KEEP, GL_INCR, GL_INCR ); + } + r_measureOverdraw->modified = qfalse; + } + else + { + // this is only reached if it was on and is now off + if ( r_measureOverdraw->modified ) { + R_IssuePendingRenderCommands(); + qglDisable( GL_STENCIL_TEST ); + } + r_measureOverdraw->modified = qfalse; + } + + // + // texturemode stuff + // + if ( r_textureMode->modified ) { + R_IssuePendingRenderCommands(); + GL_TextureMode( r_textureMode->string ); + r_textureMode->modified = qfalse; + } + + // + // gamma stuff + // + if ( r_gamma->modified ) { + r_gamma->modified = qfalse; + + R_IssuePendingRenderCommands(); + R_SetColorMappings(); + } + + // check for errors + if ( !r_ignoreGLErrors->integer ) + { + int err; + + R_IssuePendingRenderCommands(); + if ((err = qglGetError()) != GL_NO_ERROR) + ri.Error(ERR_FATAL, "RE_BeginFrame() - glGetError() failed (0x%x)!", err); + } + + if (glConfig.stereoEnabled) { + if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) ) + return; + + cmd->commandId = RC_DRAW_BUFFER; + + if ( stereoFrame == STEREO_LEFT ) { + cmd->buffer = (int)GL_BACK_LEFT; + } else if ( stereoFrame == STEREO_RIGHT ) { + cmd->buffer = (int)GL_BACK_RIGHT; + } else { + ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame ); + } + } + else + { + if(r_anaglyphMode->integer) + { + if(r_anaglyphMode->modified) + { + // clear both, front and backbuffer. + qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + backEnd.colorMask[0] = GL_FALSE; + backEnd.colorMask[1] = GL_FALSE; + backEnd.colorMask[2] = GL_FALSE; + backEnd.colorMask[3] = GL_FALSE; + qglClearColor(0.0f, 0.0f, 0.0f, 1.0f); + + if (glRefConfig.framebufferObject) + { + // clear all framebuffers + if (tr.msaaResolveFbo) + { + FBO_Bind(tr.msaaResolveFbo); + qglClear(GL_COLOR_BUFFER_BIT); + } + + if (tr.renderFbo) + { + FBO_Bind(tr.renderFbo); + qglClear(GL_COLOR_BUFFER_BIT); + } + + if (tr.screenScratchFbo) + { + FBO_Bind(tr.screenScratchFbo); + qglClear(GL_COLOR_BUFFER_BIT); + } + + FBO_Bind(NULL); + } + + qglDrawBuffer(GL_FRONT); + qglClear(GL_COLOR_BUFFER_BIT); + qglDrawBuffer(GL_BACK); + qglClear(GL_COLOR_BUFFER_BIT); + + r_anaglyphMode->modified = qfalse; + } + + if(stereoFrame == STEREO_LEFT) + { + if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) ) + return; + + if( !(colcmd = R_GetCommandBuffer(sizeof(*colcmd))) ) + return; + } + else if(stereoFrame == STEREO_RIGHT) + { + clearDepthCommand_t *cldcmd; + + if( !(cldcmd = R_GetCommandBuffer(sizeof(*cldcmd))) ) + return; + + cldcmd->commandId = RC_CLEARDEPTH; + + if( !(colcmd = R_GetCommandBuffer(sizeof(*colcmd))) ) + return; + } + else + ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is enabled, but stereoFrame was %i", stereoFrame ); + + R_SetColorMode(colcmd->rgba, stereoFrame, r_anaglyphMode->integer); + colcmd->commandId = RC_COLORMASK; + } + else + { + if(stereoFrame != STEREO_CENTER) + ri.Error( ERR_FATAL, "RE_BeginFrame: Stereo is disabled, but stereoFrame was %i", stereoFrame ); + + if( !(cmd = R_GetCommandBuffer(sizeof(*cmd))) ) + return; + } + + if(cmd) + { + cmd->commandId = RC_DRAW_BUFFER; + + if(r_anaglyphMode->modified) + { + qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + backEnd.colorMask[0] = 0; + backEnd.colorMask[1] = 0; + backEnd.colorMask[2] = 0; + backEnd.colorMask[3] = 0; + r_anaglyphMode->modified = qfalse; + } + + if (!Q_stricmp(r_drawBuffer->string, "GL_FRONT")) + cmd->buffer = (int)GL_FRONT; + else + cmd->buffer = (int)GL_BACK; + } + } + + tr.refdef.stereoFrame = stereoFrame; +} + + +/* +============= +RE_EndFrame + +Returns the number of msec spent in the back end +============= +*/ +void RE_EndFrame( int *frontEndMsec, int *backEndMsec ) { + swapBuffersCommand_t *cmd; + + if ( !tr.registered ) { + return; + } + cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + if ( !cmd ) { + return; + } + cmd->commandId = RC_SWAP_BUFFERS; + + R_IssueRenderCommands( qtrue ); + + R_InitNextFrame(); + + if ( frontEndMsec ) { + *frontEndMsec = tr.frontEndMsec; + } + tr.frontEndMsec = 0; + if ( backEndMsec ) { + *backEndMsec = backEnd.pc.msec; + } + backEnd.pc.msec = 0; +} + +/* +============= +RE_TakeVideoFrame +============= +*/ +void RE_TakeVideoFrame( int width, int height, + byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg ) +{ + videoFrameCommand_t *cmd; + + if( !tr.registered ) { + return; + } + + cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + if( !cmd ) { + return; + } + + cmd->commandId = RC_VIDEOFRAME; + + cmd->width = width; + cmd->height = height; + cmd->captureBuffer = captureBuffer; + cmd->encodeBuffer = encodeBuffer; + cmd->motionJpeg = motionJpeg; +} diff --git a/src/renderergl2/tr_curve.c b/src/renderergl2/tr_curve.c new file mode 100644 index 00000000..3d439257 --- /dev/null +++ b/src/renderergl2/tr_curve.c @@ -0,0 +1,806 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "tr_local.h" + +/* + +This file does all of the processing necessary to turn a raw grid of points +read from the map file into a srfGridMesh_t ready for rendering. + +The level of detail solution is direction independent, based only on subdivided +distance from the true curve. + +Only a single entry point: + +srfGridMesh_t *R_SubdividePatchToGrid( int width, int height, + srfVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) { + +*/ + + +/* +============ +LerpDrawVert +============ +*/ +static void LerpDrawVert( srfVert_t *a, srfVert_t *b, srfVert_t *out ) { + out->xyz[0] = 0.5f * (a->xyz[0] + b->xyz[0]); + out->xyz[1] = 0.5f * (a->xyz[1] + b->xyz[1]); + out->xyz[2] = 0.5f * (a->xyz[2] + b->xyz[2]); + + out->st[0] = 0.5f * (a->st[0] + b->st[0]); + out->st[1] = 0.5f * (a->st[1] + b->st[1]); + + out->lightmap[0] = 0.5f * (a->lightmap[0] + b->lightmap[0]); + out->lightmap[1] = 0.5f * (a->lightmap[1] + b->lightmap[1]); + + out->vertexColors[0] = 0.5f * (a->vertexColors[0] + b->vertexColors[0]); + out->vertexColors[1] = 0.5f * (a->vertexColors[1] + b->vertexColors[1]); + out->vertexColors[2] = 0.5f * (a->vertexColors[2] + b->vertexColors[2]); + out->vertexColors[3] = 0.5f * (a->vertexColors[3] + b->vertexColors[3]); +} + +/* +============ +Transpose +============ +*/ +static void Transpose( int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { + int i, j; + srfVert_t temp; + + if ( width > height ) { + for ( i = 0 ; i < height ; i++ ) { + for ( j = i + 1 ; j < width ; j++ ) { + if ( j < height ) { + // swap the value + temp = ctrl[j][i]; + ctrl[j][i] = ctrl[i][j]; + ctrl[i][j] = temp; + } else { + // just copy + ctrl[j][i] = ctrl[i][j]; + } + } + } + } else { + for ( i = 0 ; i < width ; i++ ) { + for ( j = i + 1 ; j < height ; j++ ) { + if ( j < width ) { + // swap the value + temp = ctrl[i][j]; + ctrl[i][j] = ctrl[j][i]; + ctrl[j][i] = temp; + } else { + // just copy + ctrl[i][j] = ctrl[j][i]; + } + } + } + } + +} + + +/* +================= +MakeMeshNormals + +Handles all the complicated wrapping and degenerate cases +================= +*/ +static void MakeMeshNormals( int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { + int i, j, k, dist; + vec3_t normal; + vec3_t sum; + int count = 0; + vec3_t base; + vec3_t delta; + int x, y; + srfVert_t *dv; + vec3_t around[8], temp; + qboolean good[8]; + qboolean wrapWidth, wrapHeight; + float len; +static int neighbors[8][2] = { + {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1} + }; + + wrapWidth = qfalse; + for ( i = 0 ; i < height ; i++ ) { + VectorSubtract( ctrl[i][0].xyz, ctrl[i][width-1].xyz, delta ); + len = VectorLengthSquared( delta ); + if ( len > 1.0 ) { + break; + } + } + if ( i == height ) { + wrapWidth = qtrue; + } + + wrapHeight = qfalse; + for ( i = 0 ; i < width ; i++ ) { + VectorSubtract( ctrl[0][i].xyz, ctrl[height-1][i].xyz, delta ); + len = VectorLengthSquared( delta ); + if ( len > 1.0 ) { + break; + } + } + if ( i == width) { + wrapHeight = qtrue; + } + + + for ( i = 0 ; i < width ; i++ ) { + for ( j = 0 ; j < height ; j++ ) { + count = 0; + dv = &ctrl[j][i]; + VectorCopy( dv->xyz, base ); + for ( k = 0 ; k < 8 ; k++ ) { + VectorClear( around[k] ); + good[k] = qfalse; + + for ( dist = 1 ; dist <= 3 ; dist++ ) { + x = i + neighbors[k][0] * dist; + y = j + neighbors[k][1] * dist; + if ( wrapWidth ) { + if ( x < 0 ) { + x = width - 1 + x; + } else if ( x >= width ) { + x = 1 + x - width; + } + } + if ( wrapHeight ) { + if ( y < 0 ) { + y = height - 1 + y; + } else if ( y >= height ) { + y = 1 + y - height; + } + } + + if ( x < 0 || x >= width || y < 0 || y >= height ) { + break; // edge of patch + } + VectorSubtract( ctrl[y][x].xyz, base, temp ); + if ( VectorNormalize2( temp, temp ) == 0 ) { + continue; // degenerate edge, get more dist + } else { + good[k] = qtrue; + VectorCopy( temp, around[k] ); + break; // good edge + } + } + } + + VectorClear( sum ); + for ( k = 0 ; k < 8 ; k++ ) { + if ( !good[k] || !good[(k+1)&7] ) { + continue; // didn't get two points + } + CrossProduct( around[(k+1)&7], around[k], normal ); + if ( VectorNormalize2( normal, normal ) == 0 ) { + continue; + } + VectorAdd( normal, sum, sum ); + count++; + } + //if ( count == 0 ) { + // printf("bad normal\n"); + //} + VectorNormalize2( sum, dv->normal ); + } + } +} + +#ifdef USE_VERT_TANGENT_SPACE +static void MakeMeshTangentVectors(int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], int numTriangles, + srfTriangle_t triangles[(MAX_GRID_SIZE-1)*(MAX_GRID_SIZE-1)*2]) +{ + int i, j; + srfVert_t *dv[3]; + static srfVert_t ctrl2[MAX_GRID_SIZE * MAX_GRID_SIZE]; + srfTriangle_t *tri; + + // FIXME: use more elegant way + for(i = 0; i < width; i++) + { + for(j = 0; j < height; j++) + { + dv[0] = &ctrl2[j * width + i]; + *dv[0] = ctrl[j][i]; + } + } + + for(i = 0, tri = triangles; i < numTriangles; i++, tri++) + { + dv[0] = &ctrl2[tri->indexes[0]]; + dv[1] = &ctrl2[tri->indexes[1]]; + dv[2] = &ctrl2[tri->indexes[2]]; + + R_CalcTangentVectors(dv); + } + +#if 0 + for(i = 0; i < (width * height); i++) + { + dv0 = &ctrl2[i]; + + VectorNormalize(dv0->normal); +#if 0 + VectorNormalize(dv0->tangent); + VectorNormalize(dv0->bitangent); +#else + d = DotProduct(dv0->tangent, dv0->normal); + VectorMA(dv0->tangent, -d, dv0->normal, dv0->tangent); + VectorNormalize(dv0->tangent); + + d = DotProduct(dv0->bitangent, dv0->normal); + VectorMA(dv0->bitangent, -d, dv0->normal, dv0->bitangent); + VectorNormalize(dv0->bitangent); +#endif + } +#endif + + +#if 0 + // do another extra smoothing for normals to avoid flat shading + for(i = 0; i < (width * height); i++) + { + for(j = 0; j < (width * height); j++) + { + if(R_CompareVert(&ctrl2[i], &ctrl2[j], qfalse)) + { + VectorAdd(ctrl2[i].normal, ctrl2[j].normal, ctrl2[i].normal); + } + } + + VectorNormalize(ctrl2[i].normal); + } +#endif + + for(i = 0; i < width; i++) + { + for(j = 0; j < height; j++) + { + dv[0] = &ctrl2[j * width + i]; + dv[1] = &ctrl[j][i]; + + VectorCopy(dv[0]->tangent, dv[1]->tangent); + VectorCopy(dv[0]->bitangent, dv[1]->bitangent); + } + } +} +#endif + + +static int MakeMeshTriangles(int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], + srfTriangle_t triangles[(MAX_GRID_SIZE-1)*(MAX_GRID_SIZE-1)*2]) +{ + int i, j; + int numTriangles; + int w, h; + srfVert_t *dv; + static srfVert_t ctrl2[MAX_GRID_SIZE * MAX_GRID_SIZE]; + + h = height - 1; + w = width - 1; + numTriangles = 0; + for(i = 0; i < h; i++) + { + for(j = 0; j < w; j++) + { + int v1, v2, v3, v4; + + // vertex order to be reckognized as tristrips + v1 = i * width + j + 1; + v2 = v1 - 1; + v3 = v2 + width; + v4 = v3 + 1; + + triangles[numTriangles].indexes[0] = v2; + triangles[numTriangles].indexes[1] = v3; + triangles[numTriangles].indexes[2] = v1; + numTriangles++; + + triangles[numTriangles].indexes[0] = v1; + triangles[numTriangles].indexes[1] = v3; + triangles[numTriangles].indexes[2] = v4; + numTriangles++; + } + } + + R_CalcSurfaceTriangleNeighbors(numTriangles, triangles); + + // FIXME: use more elegant way + for(i = 0; i < width; i++) + { + for(j = 0; j < height; j++) + { + dv = &ctrl2[j * width + i]; + *dv = ctrl[j][i]; + } + } + + R_CalcSurfaceTrianglePlanes(numTriangles, triangles, ctrl2); + + return numTriangles; +} + + +/* +============ +InvertCtrl +============ +*/ +static void InvertCtrl( int width, int height, srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { + int i, j; + srfVert_t temp; + + for ( i = 0 ; i < height ; i++ ) { + for ( j = 0 ; j < width/2 ; j++ ) { + temp = ctrl[i][j]; + ctrl[i][j] = ctrl[i][width-1-j]; + ctrl[i][width-1-j] = temp; + } + } +} + + +/* +================= +InvertErrorTable +================= +*/ +static void InvertErrorTable( float errorTable[2][MAX_GRID_SIZE], int width, int height ) { + int i; + float copy[2][MAX_GRID_SIZE]; + + Com_Memcpy( copy, errorTable, sizeof( copy ) ); + + for ( i = 0 ; i < width ; i++ ) { + errorTable[1][i] = copy[0][i]; //[width-1-i]; + } + + for ( i = 0 ; i < height ; i++ ) { + errorTable[0][i] = copy[1][height-1-i]; + } + +} + +/* +================== +PutPointsOnCurve +================== +*/ +static void PutPointsOnCurve( srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], + int width, int height ) { + int i, j; + srfVert_t prev, next; + + for ( i = 0 ; i < width ; i++ ) { + for ( j = 1 ; j < height ; j += 2 ) { + LerpDrawVert( &ctrl[j][i], &ctrl[j+1][i], &prev ); + LerpDrawVert( &ctrl[j][i], &ctrl[j-1][i], &next ); + LerpDrawVert( &prev, &next, &ctrl[j][i] ); + } + } + + + for ( j = 0 ; j < height ; j++ ) { + for ( i = 1 ; i < width ; i += 2 ) { + LerpDrawVert( &ctrl[j][i], &ctrl[j][i+1], &prev ); + LerpDrawVert( &ctrl[j][i], &ctrl[j][i-1], &next ); + LerpDrawVert( &prev, &next, &ctrl[j][i] ); + } + } +} + +/* +================= +R_CreateSurfaceGridMesh +================= +*/ +srfGridMesh_t *R_CreateSurfaceGridMesh(int width, int height, + srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], float errorTable[2][MAX_GRID_SIZE], + int numTriangles, srfTriangle_t triangles[(MAX_GRID_SIZE-1)*(MAX_GRID_SIZE-1)*2]) { + int i, j, size; + srfVert_t *vert; + vec3_t tmpVec; + srfGridMesh_t *grid; + + // copy the results out to a grid + size = (width * height - 1) * sizeof( srfVert_t ) + sizeof( *grid ); + +#ifdef PATCH_STITCHING + grid = /*ri.Hunk_Alloc*/ ri.Malloc( size ); + Com_Memset(grid, 0, size); + + grid->widthLodError = /*ri.Hunk_Alloc*/ ri.Malloc( width * 4 ); + Com_Memcpy( grid->widthLodError, errorTable[0], width * 4 ); + + grid->heightLodError = /*ri.Hunk_Alloc*/ ri.Malloc( height * 4 ); + Com_Memcpy( grid->heightLodError, errorTable[1], height * 4 ); + + grid->numTriangles = numTriangles; + grid->triangles = ri.Malloc(grid->numTriangles * sizeof(srfTriangle_t)); + Com_Memcpy(grid->triangles, triangles, numTriangles * sizeof(srfTriangle_t)); + + grid->numVerts = (width * height); + grid->verts = ri.Malloc(grid->numVerts * sizeof(srfVert_t)); +#else + grid = ri.Hunk_Alloc( size ); + Com_Memset(grid, 0, size); + + grid->widthLodError = ri.Hunk_Alloc( width * 4 ); + Com_Memcpy( grid->widthLodError, errorTable[0], width * 4 ); + + grid->heightLodError = ri.Hunk_Alloc( height * 4 ); + Com_Memcpy( grid->heightLodError, errorTable[1], height * 4 ); + + grid->numTriangles = numTriangles; + grid->triangles = ri.Hunk_Alloc(grid->numTriangles * sizeof(srfTriangle_t), h_low); + Com_Memcpy(grid->triangles, triangles, numTriangles * sizeof(srfTriangle_t)); + + grid->numVerts = (width * height); + grid->verts = ri.Hunk_Alloc(grid->numVerts * sizeof(srfVert_t), h_low); +#endif + + grid->width = width; + grid->height = height; + grid->surfaceType = SF_GRID; + ClearBounds( grid->meshBounds[0], grid->meshBounds[1] ); + for ( i = 0 ; i < width ; i++ ) { + for ( j = 0 ; j < height ; j++ ) { + vert = &grid->verts[j*width+i]; + *vert = ctrl[j][i]; + AddPointToBounds( vert->xyz, grid->meshBounds[0], grid->meshBounds[1] ); + } + } + + // compute local origin and bounds + VectorAdd( grid->meshBounds[0], grid->meshBounds[1], grid->localOrigin ); + VectorScale( grid->localOrigin, 0.5f, grid->localOrigin ); + VectorSubtract( grid->meshBounds[0], grid->localOrigin, tmpVec ); + grid->meshRadius = VectorLength( tmpVec ); + + VectorCopy( grid->localOrigin, grid->lodOrigin ); + grid->lodRadius = grid->meshRadius; + // + return grid; +} + +/* +================= +R_FreeSurfaceGridMesh +================= +*/ +void R_FreeSurfaceGridMesh( srfGridMesh_t *grid ) { + ri.Free(grid->widthLodError); + ri.Free(grid->heightLodError); + ri.Free(grid->triangles); + ri.Free(grid->verts); + ri.Free(grid); +} + +/* +================= +R_SubdividePatchToGrid +================= +*/ +srfGridMesh_t *R_SubdividePatchToGrid( int width, int height, + srfVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) { + int i, j, k, l; + srfVert_t_cleared( prev ); + srfVert_t_cleared( next ); + srfVert_t_cleared( mid ); + float len, maxLen; + int dir; + int t; + srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; + float errorTable[2][MAX_GRID_SIZE]; + int numTriangles; + static srfTriangle_t triangles[(MAX_GRID_SIZE-1)*(MAX_GRID_SIZE-1)*2]; + int consecutiveComplete; + + for ( i = 0 ; i < width ; i++ ) { + for ( j = 0 ; j < height ; j++ ) { + ctrl[j][i] = points[j*width+i]; + } + } + + for ( dir = 0 ; dir < 2 ; dir++ ) { + + for ( j = 0 ; j < MAX_GRID_SIZE ; j++ ) { + errorTable[dir][j] = 0; + } + + consecutiveComplete = 0; + + // horizontal subdivisions + for ( j = 0 ; ; j = (j + 2) % (width - 1) ) { + // check subdivided midpoints against control points + + // FIXME: also check midpoints of adjacent patches against the control points + // this would basically stitch all patches in the same LOD group together. + + maxLen = 0; + for ( i = 0 ; i < height ; i++ ) { + vec3_t midxyz; + vec3_t midxyz2; + vec3_t dir; + vec3_t projected; + float d; + + // calculate the point on the curve + for ( l = 0 ; l < 3 ; l++ ) { + midxyz[l] = (ctrl[i][j].xyz[l] + ctrl[i][j+1].xyz[l] * 2 + + ctrl[i][j+2].xyz[l] ) * 0.25f; + } + + // see how far off the line it is + // using dist-from-line will not account for internal + // texture warping, but it gives a lot less polygons than + // dist-from-midpoint + VectorSubtract( midxyz, ctrl[i][j].xyz, midxyz ); + VectorSubtract( ctrl[i][j+2].xyz, ctrl[i][j].xyz, dir ); + VectorNormalize( dir ); + + d = DotProduct( midxyz, dir ); + VectorScale( dir, d, projected ); + VectorSubtract( midxyz, projected, midxyz2); + len = VectorLengthSquared( midxyz2 ); // we will do the sqrt later + if ( len > maxLen ) { + maxLen = len; + } + } + + maxLen = sqrt(maxLen); + + // if all the points are on the lines, remove the entire columns + if ( maxLen < 0.1f ) { + errorTable[dir][j+1] = 999; + // if we go over the whole grid twice without adding any columns, stop + if (++consecutiveComplete >= width) + break; + continue; + } + + // see if we want to insert subdivided columns + if ( width + 2 > MAX_GRID_SIZE ) { + errorTable[dir][j+1] = 1.0f/maxLen; + break; // can't subdivide any more + } + + if ( maxLen <= r_subdivisions->value ) { + errorTable[dir][j+1] = 1.0f/maxLen; + // if we go over the whole grid twice without adding any columns, stop + if (++consecutiveComplete >= width) + break; + continue; // didn't need subdivision + } + + errorTable[dir][j+2] = 1.0f/maxLen; + + consecutiveComplete = 0; + + // insert two columns and replace the peak + width += 2; + for ( i = 0 ; i < height ; i++ ) { + LerpDrawVert( &ctrl[i][j], &ctrl[i][j+1], &prev ); + LerpDrawVert( &ctrl[i][j+1], &ctrl[i][j+2], &next ); + LerpDrawVert( &prev, &next, &mid ); + + for ( k = width - 1 ; k > j + 3 ; k-- ) { + ctrl[i][k] = ctrl[i][k-2]; + } + ctrl[i][j + 1] = prev; + ctrl[i][j + 2] = mid; + ctrl[i][j + 3] = next; + } + + // skip the new one, we'll get it on the next pass + j += 2; + } + + Transpose( width, height, ctrl ); + t = width; + width = height; + height = t; + } + + + // put all the aproximating points on the curve + PutPointsOnCurve( ctrl, width, height ); + + // cull out any rows or columns that are colinear + for ( i = 1 ; i < width-1 ; i++ ) { + if ( errorTable[0][i] != 999 ) { + continue; + } + for ( j = i+1 ; j < width ; j++ ) { + for ( k = 0 ; k < height ; k++ ) { + ctrl[k][j-1] = ctrl[k][j]; + } + errorTable[0][j-1] = errorTable[0][j]; + } + width--; + } + + for ( i = 1 ; i < height-1 ; i++ ) { + if ( errorTable[1][i] != 999 ) { + continue; + } + for ( j = i+1 ; j < height ; j++ ) { + for ( k = 0 ; k < width ; k++ ) { + ctrl[j-1][k] = ctrl[j][k]; + } + errorTable[1][j-1] = errorTable[1][j]; + } + height--; + } + +#if 1 + // flip for longest tristrips as an optimization + // the results should be visually identical with or + // without this step + if ( height > width ) { + Transpose( width, height, ctrl ); + InvertErrorTable( errorTable, width, height ); + t = width; + width = height; + height = t; + InvertCtrl( width, height, ctrl ); + } +#endif + + // calculate triangles + numTriangles = MakeMeshTriangles(width, height, ctrl, triangles); + + // calculate normals + MakeMeshNormals( width, height, ctrl ); +#ifdef USE_VERT_TANGENT_SPACE + MakeMeshTangentVectors(width, height, ctrl, numTriangles, triangles); +#endif + + return R_CreateSurfaceGridMesh(width, height, ctrl, errorTable, numTriangles, triangles); +} + +/* +=============== +R_GridInsertColumn +=============== +*/ +srfGridMesh_t *R_GridInsertColumn( srfGridMesh_t *grid, int column, int row, vec3_t point, float loderror ) { + int i, j; + int width, height, oldwidth; + srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; + float errorTable[2][MAX_GRID_SIZE]; + float lodRadius; + vec3_t lodOrigin; + int numTriangles; + static srfTriangle_t triangles[(MAX_GRID_SIZE-1)*(MAX_GRID_SIZE-1)*2]; + + oldwidth = 0; + width = grid->width + 1; + if (width > MAX_GRID_SIZE) + return NULL; + height = grid->height; + for (i = 0; i < width; i++) { + if (i == column) { + //insert new column + for (j = 0; j < grid->height; j++) { + LerpDrawVert( &grid->verts[j * grid->width + i-1], &grid->verts[j * grid->width + i], &ctrl[j][i] ); + if (j == row) + VectorCopy(point, ctrl[j][i].xyz); + } + errorTable[0][i] = loderror; + continue; + } + errorTable[0][i] = grid->widthLodError[oldwidth]; + for (j = 0; j < grid->height; j++) { + ctrl[j][i] = grid->verts[j * grid->width + oldwidth]; + } + oldwidth++; + } + for (j = 0; j < grid->height; j++) { + errorTable[1][j] = grid->heightLodError[j]; + } + // put all the aproximating points on the curve + //PutPointsOnCurve( ctrl, width, height ); + + // calculate triangles + numTriangles = MakeMeshTriangles(width, height, ctrl, triangles); + + // calculate normals + MakeMeshNormals( width, height, ctrl ); + + VectorCopy(grid->lodOrigin, lodOrigin); + lodRadius = grid->lodRadius; + // free the old grid + R_FreeSurfaceGridMesh(grid); + // create a new grid + grid = R_CreateSurfaceGridMesh(width, height, ctrl, errorTable, numTriangles, triangles); + grid->lodRadius = lodRadius; + VectorCopy(lodOrigin, grid->lodOrigin); + return grid; +} + +/* +=============== +R_GridInsertRow +=============== +*/ +srfGridMesh_t *R_GridInsertRow( srfGridMesh_t *grid, int row, int column, vec3_t point, float loderror ) { + int i, j; + int width, height, oldheight; + srfVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; + float errorTable[2][MAX_GRID_SIZE]; + float lodRadius; + vec3_t lodOrigin; + int numTriangles; + static srfTriangle_t triangles[(MAX_GRID_SIZE-1)*(MAX_GRID_SIZE-1)*2]; + + oldheight = 0; + width = grid->width; + height = grid->height + 1; + if (height > MAX_GRID_SIZE) + return NULL; + for (i = 0; i < height; i++) { + if (i == row) { + //insert new row + for (j = 0; j < grid->width; j++) { + LerpDrawVert( &grid->verts[(i-1) * grid->width + j], &grid->verts[i * grid->width + j], &ctrl[i][j] ); + if (j == column) + VectorCopy(point, ctrl[i][j].xyz); + } + errorTable[1][i] = loderror; + continue; + } + errorTable[1][i] = grid->heightLodError[oldheight]; + for (j = 0; j < grid->width; j++) { + ctrl[i][j] = grid->verts[oldheight * grid->width + j]; + } + oldheight++; + } + for (j = 0; j < grid->width; j++) { + errorTable[0][j] = grid->widthLodError[j]; + } + // put all the aproximating points on the curve + //PutPointsOnCurve( ctrl, width, height ); + + // calculate triangles + numTriangles = MakeMeshTriangles(width, height, ctrl, triangles); + + // calculate normals + MakeMeshNormals( width, height, ctrl ); + + VectorCopy(grid->lodOrigin, lodOrigin); + lodRadius = grid->lodRadius; + // free the old grid + R_FreeSurfaceGridMesh(grid); + // create a new grid + grid = R_CreateSurfaceGridMesh(width, height, ctrl, errorTable, numTriangles, triangles); + grid->lodRadius = lodRadius; + VectorCopy(lodOrigin, grid->lodOrigin); + return grid; +} diff --git a/src/renderergl2/tr_extensions.c b/src/renderergl2/tr_extensions.c new file mode 100644 index 00000000..0b70d48b --- /dev/null +++ b/src/renderergl2/tr_extensions.c @@ -0,0 +1,667 @@ +/* +=========================================================================== +Copyright (C) 2011 James Canete (use.less01@gmail.com) + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_extensions.c - extensions needed by the renderer not in sdl_glimp.c + +#ifdef USE_LOCAL_HEADERS +# include "SDL.h" +#else +# include +#endif + +#include "tr_local.h" + +// GL_EXT_draw_range_elements +void (APIENTRY * qglDrawRangeElementsEXT) (GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid *indices); + +// GL_EXT_multi_draw_arrays +void (APIENTRY * qglMultiDrawArraysEXT) (GLenum mode, GLint *first, GLsizei *count, GLsizei primcount); +void (APIENTRY * qglMultiDrawElementsEXT) (GLenum mode, const GLsizei *count, GLenum type, const GLvoid **indices, GLsizei primcount); + +// GL_ARB_vertex_shader +void (APIENTRY * qglBindAttribLocationARB) (GLhandleARB programObj, GLuint index, const GLcharARB * name); +void (APIENTRY * qglGetActiveAttribARB) (GLhandleARB programObj, GLuint index, GLsizei maxLength, GLsizei * length, + GLint * size, GLenum * type, GLcharARB * name); +GLint(APIENTRY * qglGetAttribLocationARB) (GLhandleARB programObj, const GLcharARB * name); + +// GL_ARB_vertex_program +void (APIENTRY * qglVertexAttrib4fARB) (GLuint, GLfloat, GLfloat, GLfloat, GLfloat); +void (APIENTRY * qglVertexAttrib4fvARB) (GLuint, const GLfloat *); +void (APIENTRY * qglVertexAttribPointerARB) (GLuint index, GLint size, GLenum type, GLboolean normalized, + GLsizei stride, const GLvoid * pointer); +void (APIENTRY * qglEnableVertexAttribArrayARB) (GLuint index); +void (APIENTRY * qglDisableVertexAttribArrayARB) (GLuint index); + +// GL_ARB_vertex_buffer_object +void (APIENTRY * qglBindBufferARB) (GLenum target, GLuint buffer); +void (APIENTRY * qglDeleteBuffersARB) (GLsizei n, const GLuint * buffers); +void (APIENTRY * qglGenBuffersARB) (GLsizei n, GLuint * buffers); + +GLboolean(APIENTRY * qglIsBufferARB) (GLuint buffer); +void (APIENTRY * qglBufferDataARB) (GLenum target, GLsizeiptrARB size, const GLvoid * data, GLenum usage); +void (APIENTRY * qglBufferSubDataARB) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, const GLvoid * data); +void (APIENTRY * qglGetBufferSubDataARB) (GLenum target, GLintptrARB offset, GLsizeiptrARB size, GLvoid * data); + +void (APIENTRY * qglGetBufferParameterivARB) (GLenum target, GLenum pname, GLint * params); +void (APIENTRY * qglGetBufferPointervARB) (GLenum target, GLenum pname, GLvoid * *params); + +// GL_ARB_shader_objects +void (APIENTRY * qglDeleteObjectARB) (GLhandleARB obj); + +GLhandleARB(APIENTRY * qglGetHandleARB) (GLenum pname); +void (APIENTRY * qglDetachObjectARB) (GLhandleARB containerObj, GLhandleARB attachedObj); + +GLhandleARB(APIENTRY * qglCreateShaderObjectARB) (GLenum shaderType); +void (APIENTRY * qglShaderSourceARB) (GLhandleARB shaderObj, GLsizei count, const GLcharARB * *string, + const GLint * length); +void (APIENTRY * qglCompileShaderARB) (GLhandleARB shaderObj); + +GLhandleARB(APIENTRY * qglCreateProgramObjectARB) (void); +void (APIENTRY * qglAttachObjectARB) (GLhandleARB containerObj, GLhandleARB obj); +void (APIENTRY * qglLinkProgramARB) (GLhandleARB programObj); +void (APIENTRY * qglUseProgramObjectARB) (GLhandleARB programObj); +void (APIENTRY * qglValidateProgramARB) (GLhandleARB programObj); +void (APIENTRY * qglUniform1fARB) (GLint location, GLfloat v0); +void (APIENTRY * qglUniform2fARB) (GLint location, GLfloat v0, GLfloat v1); +void (APIENTRY * qglUniform3fARB) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2); +void (APIENTRY * qglUniform4fARB) (GLint location, GLfloat v0, GLfloat v1, GLfloat v2, GLfloat v3); +void (APIENTRY * qglUniform1iARB) (GLint location, GLint v0); +void (APIENTRY * qglUniform2iARB) (GLint location, GLint v0, GLint v1); +void (APIENTRY * qglUniform3iARB) (GLint location, GLint v0, GLint v1, GLint v2); +void (APIENTRY * qglUniform4iARB) (GLint location, GLint v0, GLint v1, GLint v2, GLint v3); +void (APIENTRY * qglUniform1fvARB) (GLint location, GLsizei count, const GLfloat * value); +void (APIENTRY * qglUniform2fvARB) (GLint location, GLsizei count, const GLfloat * value); +void (APIENTRY * qglUniform3fvARB) (GLint location, GLsizei count, const GLfloat * value); +void (APIENTRY * qglUniform4fvARB) (GLint location, GLsizei count, const GLfloat * value); +void (APIENTRY * qglUniform2ivARB) (GLint location, GLsizei count, const GLint * value); +void (APIENTRY * qglUniform3ivARB) (GLint location, GLsizei count, const GLint * value); +void (APIENTRY * qglUniform4ivARB) (GLint location, GLsizei count, const GLint * value); +void (APIENTRY * qglUniformMatrix2fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +void (APIENTRY * qglUniformMatrix3fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +void (APIENTRY * qglUniformMatrix4fvARB) (GLint location, GLsizei count, GLboolean transpose, const GLfloat * value); +void (APIENTRY * qglGetObjectParameterfvARB) (GLhandleARB obj, GLenum pname, GLfloat * params); +void (APIENTRY * qglGetObjectParameterivARB) (GLhandleARB obj, GLenum pname, GLint * params); +void (APIENTRY * qglGetInfoLogARB) (GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog); +void (APIENTRY * qglGetAttachedObjectsARB) (GLhandleARB containerObj, GLsizei maxCount, GLsizei * count, + GLhandleARB * obj); +GLint(APIENTRY * qglGetUniformLocationARB) (GLhandleARB programObj, const GLcharARB * name); +void (APIENTRY * qglGetActiveUniformARB) (GLhandleARB programObj, GLuint index, GLsizei maxIndex, GLsizei * length, + GLint * size, GLenum * type, GLcharARB * name); +void (APIENTRY * qglGetUniformfvARB) (GLhandleARB programObj, GLint location, GLfloat * params); +void (APIENTRY * qglGetUniformivARB) (GLhandleARB programObj, GLint location, GLint * params); +void (APIENTRY * qglGetShaderSourceARB) (GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * source); + +// GL_ARB_texture_compression +void (APIENTRY * qglCompressedTexImage3DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, + GLsizei depth, GLint border, GLsizei imageSize, const GLvoid *data); +void (APIENTRY * qglCompressedTexImage2DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, + GLint border, GLsizei imageSize, const GLvoid *data); +void (APIENTRY * qglCompressedTexImage1DARB)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLint border, + GLsizei imageSize, const GLvoid *data); +void (APIENTRY * qglCompressedTexSubImage3DARB)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, + GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data); +void (APIENTRY * qglCompressedTexSubImage2DARB)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, + GLsizei height, GLenum format, GLsizei imageSize, const GLvoid *data); +void (APIENTRY * qglCompressedTexSubImage1DARB)(GLenum target, GLint level, GLint xoffset, GLsizei width, GLenum format, + GLsizei imageSize, const GLvoid *data); +void (APIENTRY * qglGetCompressedTexImageARB)(GLenum target, GLint lod, + GLvoid *img); + +// GL_EXT_framebuffer_object +GLboolean (APIENTRY * qglIsRenderbufferEXT)(GLuint renderbuffer); +void (APIENTRY * qglBindRenderbufferEXT)(GLenum target, GLuint renderbuffer); +void (APIENTRY * qglDeleteRenderbuffersEXT)(GLsizei n, const GLuint *renderbuffers); +void (APIENTRY * qglGenRenderbuffersEXT)(GLsizei n, GLuint *renderbuffers); + +void (APIENTRY * qglRenderbufferStorageEXT)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height); + +void (APIENTRY * qglGetRenderbufferParameterivEXT)(GLenum target, GLenum pname, GLint *params); + +GLboolean (APIENTRY * qglIsFramebufferEXT)(GLuint framebuffer); +void (APIENTRY * qglBindFramebufferEXT)(GLenum target, GLuint framebuffer); +void (APIENTRY * qglDeleteFramebuffersEXT)(GLsizei n, const GLuint *framebuffers); +void (APIENTRY * qglGenFramebuffersEXT)(GLsizei n, GLuint *framebuffers); + +GLenum (APIENTRY * qglCheckFramebufferStatusEXT)(GLenum target); + +void (APIENTRY * qglFramebufferTexture1DEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, + GLint level); +void (APIENTRY * qglFramebufferTexture2DEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, + GLint level); +void (APIENTRY * qglFramebufferTexture3DEXT)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, + GLint level, GLint zoffset); + +void (APIENTRY * qglFramebufferRenderbufferEXT)(GLenum target, GLenum attachment, GLenum renderbuffertarget, + GLuint renderbuffer); + +void (APIENTRY * qglGetFramebufferAttachmentParameterivEXT)(GLenum target, GLenum attachment, GLenum pname, GLint *params); + +void (APIENTRY * qglGenerateMipmapEXT)(GLenum target); + +// GL_ARB_occlusion_query +void (APIENTRY * qglGenQueriesARB)(GLsizei n, GLuint *ids); +void (APIENTRY * qglDeleteQueriesARB)(GLsizei n, const GLuint *ids); +GLboolean (APIENTRY * qglIsQueryARB)(GLuint id); +void (APIENTRY * qglBeginQueryARB)(GLenum target, GLuint id); +void (APIENTRY * qglEndQueryARB)(GLenum target); +void (APIENTRY * qglGetQueryivARB)(GLenum target, GLenum pname, GLint *params); +void (APIENTRY * qglGetQueryObjectivARB)(GLuint id, GLenum pname, GLint *params); +void (APIENTRY * qglGetQueryObjectuivARB)(GLuint id, GLenum pname, GLuint *params); + +// GL_EXT_framebuffer_blit +void (APIENTRY * qglBlitFramebufferEXT)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, + GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, + GLbitfield mask, GLenum filter); + +// GL_EXT_framebuffer_multisample +void (APIENTRY * qglRenderbufferStorageMultisampleEXT)(GLenum target, GLsizei samples, + GLenum internalformat, GLsizei width, GLsizei height); + +// GL_ARB_draw_buffers +void (APIENTRY * qglDrawBuffersARB)(GLsizei n, const GLenum *bufs); + +static qboolean GLimp_HaveExtension(const char *ext) +{ + const char *ptr = Q_stristr( glConfig.extensions_string, ext ); + if (ptr == NULL) + return qfalse; + ptr += strlen(ext); + return ((*ptr == ' ') || (*ptr == '\0')); // verify it's complete string. +} + +void GLimp_InitExtraExtensions() +{ + char *extension; + const char* result[3] = { "...ignoring %s\n", "...using %s\n", "...%s not found\n" }; + + // GL_EXT_draw_range_elements + extension = "GL_EXT_draw_range_elements"; + glRefConfig.drawRangeElements = qfalse; + qglMultiDrawArraysEXT = NULL; + qglMultiDrawElementsEXT = NULL; + if( GLimp_HaveExtension( extension ) ) + { + qglDrawRangeElementsEXT = (void *) SDL_GL_GetProcAddress("glDrawRangeElementsEXT"); + + if ( r_ext_draw_range_elements->integer) + glRefConfig.drawRangeElements = qtrue; + + ri.Printf(PRINT_ALL, result[glRefConfig.drawRangeElements], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); + } + + // GL_EXT_multi_draw_arrays + extension = "GL_EXT_multi_draw_arrays"; + glRefConfig.multiDrawArrays = qfalse; + qglMultiDrawArraysEXT = NULL; + qglMultiDrawElementsEXT = NULL; + if( GLimp_HaveExtension( extension ) ) + { + qglMultiDrawArraysEXT = (PFNGLMULTIDRAWARRAYSEXTPROC) SDL_GL_GetProcAddress("glMultiDrawArraysEXT"); + qglMultiDrawElementsEXT = (PFNGLMULTIDRAWELEMENTSEXTPROC) SDL_GL_GetProcAddress("glMultiDrawElementsEXT"); + + if ( r_ext_multi_draw_arrays->integer ) + glRefConfig.multiDrawArrays = qtrue; + + ri.Printf(PRINT_ALL, result[glRefConfig.multiDrawArrays], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); + } + + // GL_ARB_vertex_program + //glRefConfig.vertexProgram = qfalse; + extension = "GL_ARB_vertex_program"; + qglVertexAttrib4fARB = NULL; + qglVertexAttrib4fvARB = NULL; + qglVertexAttribPointerARB = NULL; + qglEnableVertexAttribArrayARB = NULL; + qglDisableVertexAttribArrayARB = NULL; + if( GLimp_HaveExtension( extension ) ) + { + qglVertexAttrib4fARB = (PFNGLVERTEXATTRIB4FARBPROC) SDL_GL_GetProcAddress("glVertexAttrib4fARB"); + qglVertexAttrib4fvARB = (PFNGLVERTEXATTRIB4FVARBPROC) SDL_GL_GetProcAddress("glVertexAttrib4fvARB"); + qglVertexAttribPointerARB = (PFNGLVERTEXATTRIBPOINTERARBPROC) SDL_GL_GetProcAddress("glVertexAttribPointerARB"); + qglEnableVertexAttribArrayARB = + (PFNGLENABLEVERTEXATTRIBARRAYARBPROC) SDL_GL_GetProcAddress("glEnableVertexAttribArrayARB"); + qglDisableVertexAttribArrayARB = + (PFNGLDISABLEVERTEXATTRIBARRAYARBPROC) SDL_GL_GetProcAddress("glDisableVertexAttribArrayARB"); + + ri.Printf(PRINT_ALL, result[1], extension); + //glRefConfig.vertexProgram = qtrue; + } + else + { + ri.Error(ERR_FATAL, result[2], extension); + } + + // GL_ARB_vertex_buffer_object + //glRefConfig.vertexBufferObject = qfalse; + extension = "GL_ARB_vertex_buffer_object"; + qglBindBufferARB = NULL; + qglDeleteBuffersARB = NULL; + qglGenBuffersARB = NULL; + qglIsBufferARB = NULL; + qglBufferDataARB = NULL; + qglBufferSubDataARB = NULL; + qglGetBufferSubDataARB = NULL; + qglGetBufferParameterivARB = NULL; + qglGetBufferPointervARB = NULL; + if( GLimp_HaveExtension( extension ) ) + { + qglBindBufferARB = (PFNGLBINDBUFFERARBPROC) SDL_GL_GetProcAddress("glBindBufferARB"); + qglDeleteBuffersARB = (PFNGLDELETEBUFFERSARBPROC) SDL_GL_GetProcAddress("glDeleteBuffersARB"); + qglGenBuffersARB = (PFNGLGENBUFFERSARBPROC) SDL_GL_GetProcAddress("glGenBuffersARB"); + qglIsBufferARB = (PFNGLISBUFFERARBPROC) SDL_GL_GetProcAddress("glIsBufferARB"); + qglBufferDataARB = (PFNGLBUFFERDATAARBPROC) SDL_GL_GetProcAddress("glBufferDataARB"); + qglBufferSubDataARB = (PFNGLBUFFERSUBDATAARBPROC) SDL_GL_GetProcAddress("glBufferSubDataARB"); + qglGetBufferSubDataARB = (PFNGLGETBUFFERSUBDATAARBPROC) SDL_GL_GetProcAddress("glGetBufferSubDataARB"); + qglGetBufferParameterivARB = (PFNGLGETBUFFERPARAMETERIVARBPROC) SDL_GL_GetProcAddress("glGetBufferParameterivARB"); + qglGetBufferPointervARB = (PFNGLGETBUFFERPOINTERVARBPROC) SDL_GL_GetProcAddress("glGetBufferPointervARB"); + ri.Printf(PRINT_ALL, result[1], extension); + //glRefConfig.vertexBufferObject = qtrue; + } + else + { + ri.Error(ERR_FATAL, result[2], extension); + } + + // GL_ARB_shader_objects + extension = "GL_ARB_shader_objects"; + //glRefConfig.shaderObjects = qfalse; + qglDeleteObjectARB = NULL; + qglGetHandleARB = NULL; + qglDetachObjectARB = NULL; + qglCreateShaderObjectARB = NULL; + qglShaderSourceARB = NULL; + qglCompileShaderARB = NULL; + qglCreateProgramObjectARB = NULL; + qglAttachObjectARB = NULL; + qglLinkProgramARB = NULL; + qglUseProgramObjectARB = NULL; + qglValidateProgramARB = NULL; + qglUniform1fARB = NULL; + qglUniform2fARB = NULL; + qglUniform3fARB = NULL; + qglUniform4fARB = NULL; + qglUniform1iARB = NULL; + qglUniform2iARB = NULL; + qglUniform3iARB = NULL; + qglUniform4iARB = NULL; + qglUniform1fvARB = NULL; + qglUniform2fvARB = NULL; + qglUniform3fvARB = NULL; + qglUniform4fvARB = NULL; + qglUniform2ivARB = NULL; + qglUniform3ivARB = NULL; + qglUniform4ivARB = NULL; + qglUniformMatrix2fvARB = NULL; + qglUniformMatrix3fvARB = NULL; + qglUniformMatrix4fvARB = NULL; + qglGetObjectParameterfvARB = NULL; + qglGetObjectParameterivARB = NULL; + qglGetInfoLogARB = NULL; + qglGetAttachedObjectsARB = NULL; + qglGetUniformLocationARB = NULL; + qglGetActiveUniformARB = NULL; + qglGetUniformfvARB = NULL; + qglGetUniformivARB = NULL; + qglGetShaderSourceARB = NULL; + if( GLimp_HaveExtension( extension ) ) + { + qglDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC) SDL_GL_GetProcAddress("glDeleteObjectARB"); + qglGetHandleARB = (PFNGLGETHANDLEARBPROC) SDL_GL_GetProcAddress("glGetHandleARB"); + qglDetachObjectARB = (PFNGLDETACHOBJECTARBPROC) SDL_GL_GetProcAddress("glDetachObjectARB"); + qglCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) SDL_GL_GetProcAddress("glCreateShaderObjectARB"); + qglShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) SDL_GL_GetProcAddress("glShaderSourceARB"); + qglCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) SDL_GL_GetProcAddress("glCompileShaderARB"); + qglCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glCreateProgramObjectARB"); + qglAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) SDL_GL_GetProcAddress("glAttachObjectARB"); + qglLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) SDL_GL_GetProcAddress("glLinkProgramARB"); + qglUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glUseProgramObjectARB"); + qglValidateProgramARB = (PFNGLVALIDATEPROGRAMARBPROC) SDL_GL_GetProcAddress("glValidateProgramARB"); + qglUniform1fARB = (PFNGLUNIFORM1FARBPROC) SDL_GL_GetProcAddress("glUniform1fARB"); + qglUniform2fARB = (PFNGLUNIFORM2FARBPROC) SDL_GL_GetProcAddress("glUniform2fARB"); + qglUniform3fARB = (PFNGLUNIFORM3FARBPROC) SDL_GL_GetProcAddress("glUniform3fARB"); + qglUniform4fARB = (PFNGLUNIFORM4FARBPROC) SDL_GL_GetProcAddress("glUniform4fARB"); + qglUniform1iARB = (PFNGLUNIFORM1IARBPROC) SDL_GL_GetProcAddress("glUniform1iARB"); + qglUniform2iARB = (PFNGLUNIFORM2IARBPROC) SDL_GL_GetProcAddress("glUniform2iARB"); + qglUniform3iARB = (PFNGLUNIFORM3IARBPROC) SDL_GL_GetProcAddress("glUniform3iARB"); + qglUniform4iARB = (PFNGLUNIFORM4IARBPROC) SDL_GL_GetProcAddress("glUniform4iARB"); + qglUniform1fvARB = (PFNGLUNIFORM1FVARBPROC) SDL_GL_GetProcAddress("glUniform1fvARB"); + qglUniform2fvARB = (PFNGLUNIFORM2FVARBPROC) SDL_GL_GetProcAddress("glUniform2fvARB"); + qglUniform3fvARB = (PFNGLUNIFORM3FVARBPROC) SDL_GL_GetProcAddress("glUniform3fvARB"); + qglUniform4fvARB = (PFNGLUNIFORM4FVARBPROC) SDL_GL_GetProcAddress("glUniform4fvARB"); + qglUniform2ivARB = (PFNGLUNIFORM2IVARBPROC) SDL_GL_GetProcAddress("glUniform2ivARB"); + qglUniform3ivARB = (PFNGLUNIFORM3IVARBPROC) SDL_GL_GetProcAddress("glUniform3ivARB"); + qglUniform4ivARB = (PFNGLUNIFORM4IVARBPROC) SDL_GL_GetProcAddress("glUniform4ivARB"); + qglUniformMatrix2fvARB = (PFNGLUNIFORMMATRIX2FVARBPROC) SDL_GL_GetProcAddress("glUniformMatrix2fvARB"); + qglUniformMatrix3fvARB = (PFNGLUNIFORMMATRIX3FVARBPROC) SDL_GL_GetProcAddress("glUniformMatrix3fvARB"); + qglUniformMatrix4fvARB = (PFNGLUNIFORMMATRIX4FVARBPROC) SDL_GL_GetProcAddress("glUniformMatrix4fvARB"); + qglGetObjectParameterfvARB = (PFNGLGETOBJECTPARAMETERFVARBPROC) SDL_GL_GetProcAddress("glGetObjectParameterfvARB"); + qglGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) SDL_GL_GetProcAddress("glGetObjectParameterivARB"); + qglGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) SDL_GL_GetProcAddress("glGetInfoLogARB"); + qglGetAttachedObjectsARB = (PFNGLGETATTACHEDOBJECTSARBPROC) SDL_GL_GetProcAddress("glGetAttachedObjectsARB"); + qglGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONARBPROC) SDL_GL_GetProcAddress("glGetUniformLocationARB"); + qglGetActiveUniformARB = (PFNGLGETACTIVEUNIFORMARBPROC) SDL_GL_GetProcAddress("glGetActiveUniformARB"); + qglGetUniformfvARB = (PFNGLGETUNIFORMFVARBPROC) SDL_GL_GetProcAddress("glGetUniformfvARB"); + qglGetUniformivARB = (PFNGLGETUNIFORMIVARBPROC) SDL_GL_GetProcAddress("glGetUniformivARB"); + qglGetShaderSourceARB = (PFNGLGETSHADERSOURCEARBPROC) SDL_GL_GetProcAddress("glGetShaderSourceARB"); + ri.Printf(PRINT_ALL, result[1], extension); + //glRefConfig.shaderObjects = qtrue; + } + else + { + ri.Error(ERR_FATAL, result[2], extension); + } + + // GL_ARB_vertex_shader + //glRefConfig.vertexShader = qfalse; + extension = "GL_ARB_vertex_shader"; + qglBindAttribLocationARB = NULL; + qglGetActiveAttribARB = NULL; + qglGetAttribLocationARB = NULL; + if( GLimp_HaveExtension( extension ) ) + { + //int reservedComponents; + + //qglGetIntegerv(GL_MAX_VERTEX_UNIFORM_COMPONENTS_ARB, &glConfig.maxVertexUniforms); + //qglGetIntegerv(GL_MAX_VARYING_FLOATS_ARB, &glConfig.maxVaryingFloats); + //qglGetIntegerv(GL_MAX_VERTEX_ATTRIBS_ARB, &glConfig.maxVertexAttribs); + + //reservedComponents = 16 * 10; // approximation how many uniforms we have besides the bone matrices + +#if 0 + if(glConfig.driverType == GLDRV_MESA) + { + // HACK + // restrict to number of vertex uniforms to 512 because of: + // xreal.x86_64: nv50_program.c:4181: nv50_program_validate_data: Assertion `p->param_nr <= 512' failed + + glConfig.maxVertexUniforms = Q_bound(0, glConfig.maxVertexUniforms, 512); + } +#endif + + //glConfig.maxVertexSkinningBones = (int) Q_bound(0.0, (Q_max(glConfig.maxVertexUniforms - reservedComponents, 0) / 16), MAX_BONES); + //glConfig.vboVertexSkinningAvailable = r_vboVertexSkinning->integer && ((glConfig.maxVertexSkinningBones >= 12) ? qtrue : qfalse); + + qglBindAttribLocationARB = (PFNGLBINDATTRIBLOCATIONARBPROC) SDL_GL_GetProcAddress("glBindAttribLocationARB"); + qglGetActiveAttribARB = (PFNGLGETACTIVEATTRIBARBPROC) SDL_GL_GetProcAddress("glGetActiveAttribARB"); + qglGetAttribLocationARB = (PFNGLGETATTRIBLOCATIONARBPROC) SDL_GL_GetProcAddress("glGetAttribLocationARB"); + ri.Printf(PRINT_ALL, result[1], extension); + //glRefConfig.vertexShader = qtrue; + } + else + { + ri.Error(ERR_FATAL, result[2], extension); + } + + // GL_ARB_shading_language_100 + extension = "GL_ARB_shading_language_100"; + glRefConfig.textureFloat = qfalse; + if( GLimp_HaveExtension( extension ) ) + { + char version[256]; + + Q_strncpyz( version, (char *) qglGetString (GL_SHADING_LANGUAGE_VERSION_ARB), sizeof( version ) ); + + sscanf(version, "%d.%d", &glRefConfig.glslMajorVersion, &glRefConfig.glslMinorVersion); + + ri.Printf(PRINT_ALL, "...using GLSL version %s\n", version); + } + else + { + ri.Error(ERR_FATAL, result[2], extension); + } + + glRefConfig.memInfo = MI_NONE; + + if( GLimp_HaveExtension( "GL_NVX_gpu_memory_info" ) ) + { + glRefConfig.memInfo = MI_NVX; + } + else if( GLimp_HaveExtension( "GL_ATI_meminfo" ) ) + { + glRefConfig.memInfo = MI_ATI; + } + + extension = "GL_ARB_texture_non_power_of_two"; + glRefConfig.textureNonPowerOfTwo = qfalse; + if( GLimp_HaveExtension( extension ) ) + { + if(1) //(r_ext_texture_non_power_of_two->integer) + { + glRefConfig.textureNonPowerOfTwo = qtrue; + } + + ri.Printf(PRINT_ALL, result[glRefConfig.textureNonPowerOfTwo], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); + } + + // GL_ARB_texture_float + extension = "GL_ARB_texture_float"; + glRefConfig.textureFloat = qfalse; + if( GLimp_HaveExtension( extension ) ) + { + if( r_ext_texture_float->integer ) + { + glRefConfig.textureFloat = qtrue; + } + + ri.Printf(PRINT_ALL, result[glRefConfig.textureFloat], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); + } + + // GL_ARB_half_float_pixel + extension = "GL_ARB_half_float_pixel"; + glRefConfig.halfFloatPixel = qfalse; + if( GLimp_HaveExtension( extension ) ) + { + if( r_arb_half_float_pixel->integer ) + glRefConfig.halfFloatPixel = qtrue; + + ri.Printf(PRINT_ALL, result[glRefConfig.halfFloatPixel], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); + } + + // GL_EXT_framebuffer_object + extension = "GL_EXT_framebuffer_object"; + glRefConfig.framebufferObject = qfalse; + if( GLimp_HaveExtension( extension ) ) + { + glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE_EXT, &glRefConfig.maxRenderbufferSize); + glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS_EXT, &glRefConfig.maxColorAttachments); + + qglIsRenderbufferEXT = (PFNGLISRENDERBUFFEREXTPROC) SDL_GL_GetProcAddress("glIsRenderbufferEXT"); + qglBindRenderbufferEXT = (PFNGLBINDRENDERBUFFEREXTPROC) SDL_GL_GetProcAddress("glBindRenderbufferEXT"); + qglDeleteRenderbuffersEXT = (PFNGLDELETERENDERBUFFERSEXTPROC) SDL_GL_GetProcAddress("glDeleteRenderbuffersEXT"); + qglGenRenderbuffersEXT = (PFNGLGENRENDERBUFFERSEXTPROC) SDL_GL_GetProcAddress("glGenRenderbuffersEXT"); + qglRenderbufferStorageEXT = (PFNGLRENDERBUFFERSTORAGEEXTPROC) SDL_GL_GetProcAddress("glRenderbufferStorageEXT"); + qglGetRenderbufferParameterivEXT = (PFNGLGETRENDERBUFFERPARAMETERIVEXTPROC) SDL_GL_GetProcAddress("glGetRenderbufferParameterivEXT"); + qglIsFramebufferEXT = (PFNGLISFRAMEBUFFEREXTPROC) SDL_GL_GetProcAddress("glIsFramebufferEXT"); + qglBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC) SDL_GL_GetProcAddress("glBindFramebufferEXT"); + qglDeleteFramebuffersEXT = (PFNGLDELETEFRAMEBUFFERSEXTPROC) SDL_GL_GetProcAddress("glDeleteFramebuffersEXT"); + qglGenFramebuffersEXT = (PFNGLGENFRAMEBUFFERSEXTPROC) SDL_GL_GetProcAddress("glGenFramebuffersEXT"); + qglCheckFramebufferStatusEXT = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) SDL_GL_GetProcAddress("glCheckFramebufferStatusEXT"); + qglFramebufferTexture1DEXT = (PFNGLFRAMEBUFFERTEXTURE1DEXTPROC) SDL_GL_GetProcAddress("glFramebufferTexture1DEXT"); + qglFramebufferTexture2DEXT = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) SDL_GL_GetProcAddress("glFramebufferTexture2DEXT"); + qglFramebufferTexture3DEXT = (PFNGLFRAMEBUFFERTEXTURE3DEXTPROC) SDL_GL_GetProcAddress("glFramebufferTexture3DEXT"); + qglFramebufferRenderbufferEXT = (PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) SDL_GL_GetProcAddress("glFramebufferRenderbufferEXT"); + qglGetFramebufferAttachmentParameterivEXT = (PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVEXTPROC) SDL_GL_GetProcAddress("glGetFramebufferAttachmentParameterivEXT"); + qglGenerateMipmapEXT = (PFNGLGENERATEMIPMAPEXTPROC) SDL_GL_GetProcAddress("glGenerateMipmapEXT"); + + if(r_ext_framebuffer_object->value) + glRefConfig.framebufferObject = qtrue; + + ri.Printf(PRINT_ALL, result[glRefConfig.framebufferObject], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); + } + + // GL_EXT_packed_depth_stencil + extension = "GL_EXT_packed_depth_stencil"; + glRefConfig.packedDepthStencil = qfalse; + if( GLimp_HaveExtension(extension)) + { + glRefConfig.packedDepthStencil = qtrue; + ri.Printf(PRINT_ALL, result[glRefConfig.packedDepthStencil], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); + } + + // GL_ARB_occlusion_query + extension = "GL_ARB_occlusion_query"; + glRefConfig.occlusionQuery = qfalse; + if (GLimp_HaveExtension(extension)) + { + qglGenQueriesARB = (PFNGLGENQUERIESARBPROC) SDL_GL_GetProcAddress("glGenQueriesARB"); + qglDeleteQueriesARB = (PFNGLDELETEQUERIESARBPROC) SDL_GL_GetProcAddress("glDeleteQueriesARB"); + qglIsQueryARB = (PFNGLISQUERYARBPROC) SDL_GL_GetProcAddress("glIsQueryARB"); + qglBeginQueryARB = (PFNGLBEGINQUERYARBPROC) SDL_GL_GetProcAddress("glBeginQueryARB"); + qglEndQueryARB = (PFNGLENDQUERYARBPROC) SDL_GL_GetProcAddress("glEndQueryARB"); + qglGetQueryivARB = (PFNGLGETQUERYIVARBPROC) SDL_GL_GetProcAddress("glGetQueryivARB"); + qglGetQueryObjectivARB = (PFNGLGETQUERYOBJECTIVARBPROC) SDL_GL_GetProcAddress("glGetQueryObjectivARB"); + qglGetQueryObjectuivARB = (PFNGLGETQUERYOBJECTUIVARBPROC) SDL_GL_GetProcAddress("glGetQueryObjectuivARB"); + glRefConfig.occlusionQuery = qtrue; + ri.Printf(PRINT_ALL, result[glRefConfig.occlusionQuery], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); + } + + // GL_EXT_framebuffer_blit + extension = "GL_EXT_framebuffer_blit"; + glRefConfig.framebufferBlit = qfalse; + if (GLimp_HaveExtension(extension)) + { + qglBlitFramebufferEXT = (void *)SDL_GL_GetProcAddress("glBlitFramebufferEXT"); + glRefConfig.framebufferBlit = qtrue; + ri.Printf(PRINT_ALL, result[glRefConfig.framebufferBlit], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); + } + + // GL_EXT_framebuffer_multisample + extension = "GL_EXT_framebuffer_multisample"; + glRefConfig.framebufferMultisample = qfalse; + if (GLimp_HaveExtension(extension)) + { + qglRenderbufferStorageMultisampleEXT = (void *)SDL_GL_GetProcAddress("glRenderbufferStorageMultisampleEXT"); + glRefConfig.framebufferMultisample = qtrue; + ri.Printf(PRINT_ALL, result[glRefConfig.framebufferMultisample], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); + } + + // GL_EXT_texture_sRGB + extension = "GL_EXT_texture_sRGB"; + glRefConfig.texture_srgb = qfalse; + if (GLimp_HaveExtension(extension)) + { + if (r_srgb->integer) + glRefConfig.texture_srgb = qtrue; + + ri.Printf(PRINT_ALL, result[glRefConfig.texture_srgb], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); + } + + glRefConfig.textureCompression = TCR_NONE; + + // GL_EXT_texture_compression_latc + extension = "GL_EXT_texture_compression_latc"; + if (GLimp_HaveExtension(extension)) + { + if (r_ext_compressed_textures->integer) + glRefConfig.textureCompression |= TCR_LATC; + + ri.Printf(PRINT_ALL, result[r_ext_compressed_textures->integer ? 1 : 0], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); + } + + // GL_ARB_texture_compression_bptc + extension = "GL_ARB_texture_compression_bptc"; + if (GLimp_HaveExtension(extension)) + { + if (r_ext_compressed_textures->integer >= 2) + glRefConfig.textureCompression |= TCR_BPTC; + + ri.Printf(PRINT_ALL, result[(r_ext_compressed_textures->integer >= 2) ? 1 : 0], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); + } + + // GL_ARB_draw_buffers + extension = "GL_ARB_draw_buffers"; + qglDrawBuffersARB = NULL; + if( GLimp_HaveExtension( extension ) ) + { + qglDrawBuffersARB = (void *) SDL_GL_GetProcAddress("glDrawBuffersARB"); + + ri.Printf(PRINT_ALL, result[1], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); + } + + // GL_ARB_depth_clamp + extension = "GL_ARB_depth_clamp"; + glRefConfig.depthClamp = qfalse; + if( GLimp_HaveExtension( extension ) ) + { + glRefConfig.depthClamp = qtrue; + ri.Printf(PRINT_ALL, result[1], extension); + } + else + { + ri.Printf(PRINT_ALL, result[2], extension); + } +} diff --git a/src/renderergl2/tr_extramath.c b/src/renderergl2/tr_extramath.c new file mode 100644 index 00000000..e989c7e9 --- /dev/null +++ b/src/renderergl2/tr_extramath.c @@ -0,0 +1,233 @@ +/* +=========================================================================== +Copyright (C) 2010 James Canete (use.less01@gmail.com) + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_extramath.c - extra math needed by the renderer not in qmath.c + +#include "tr_local.h" + +// Some matrix helper functions +// FIXME: do these already exist in ioq3 and I don't know about them? + +void Matrix16Zero( matrix_t out ) +{ + out[ 0] = 0.0f; out[ 4] = 0.0f; out[ 8] = 0.0f; out[12] = 0.0f; + out[ 1] = 0.0f; out[ 5] = 0.0f; out[ 9] = 0.0f; out[13] = 0.0f; + out[ 2] = 0.0f; out[ 6] = 0.0f; out[10] = 0.0f; out[14] = 0.0f; + out[ 3] = 0.0f; out[ 7] = 0.0f; out[11] = 0.0f; out[15] = 0.0f; +} + +void Matrix16Identity( matrix_t out ) +{ + out[ 0] = 1.0f; out[ 4] = 0.0f; out[ 8] = 0.0f; out[12] = 0.0f; + out[ 1] = 0.0f; out[ 5] = 1.0f; out[ 9] = 0.0f; out[13] = 0.0f; + out[ 2] = 0.0f; out[ 6] = 0.0f; out[10] = 1.0f; out[14] = 0.0f; + out[ 3] = 0.0f; out[ 7] = 0.0f; out[11] = 0.0f; out[15] = 1.0f; +} + +void Matrix16Copy( const matrix_t in, matrix_t out ) +{ + out[ 0] = in[ 0]; out[ 4] = in[ 4]; out[ 8] = in[ 8]; out[12] = in[12]; + out[ 1] = in[ 1]; out[ 5] = in[ 5]; out[ 9] = in[ 9]; out[13] = in[13]; + out[ 2] = in[ 2]; out[ 6] = in[ 6]; out[10] = in[10]; out[14] = in[14]; + out[ 3] = in[ 3]; out[ 7] = in[ 7]; out[11] = in[11]; out[15] = in[15]; +} + +void Matrix16Multiply( const matrix_t in1, const matrix_t in2, matrix_t out ) +{ + out[ 0] = in1[ 0] * in2[ 0] + in1[ 4] * in2[ 1] + in1[ 8] * in2[ 2] + in1[12] * in2[ 3]; + out[ 1] = in1[ 1] * in2[ 0] + in1[ 5] * in2[ 1] + in1[ 9] * in2[ 2] + in1[13] * in2[ 3]; + out[ 2] = in1[ 2] * in2[ 0] + in1[ 6] * in2[ 1] + in1[10] * in2[ 2] + in1[14] * in2[ 3]; + out[ 3] = in1[ 3] * in2[ 0] + in1[ 7] * in2[ 1] + in1[11] * in2[ 2] + in1[15] * in2[ 3]; + + out[ 4] = in1[ 0] * in2[ 4] + in1[ 4] * in2[ 5] + in1[ 8] * in2[ 6] + in1[12] * in2[ 7]; + out[ 5] = in1[ 1] * in2[ 4] + in1[ 5] * in2[ 5] + in1[ 9] * in2[ 6] + in1[13] * in2[ 7]; + out[ 6] = in1[ 2] * in2[ 4] + in1[ 6] * in2[ 5] + in1[10] * in2[ 6] + in1[14] * in2[ 7]; + out[ 7] = in1[ 3] * in2[ 4] + in1[ 7] * in2[ 5] + in1[11] * in2[ 6] + in1[15] * in2[ 7]; + + out[ 8] = in1[ 0] * in2[ 8] + in1[ 4] * in2[ 9] + in1[ 8] * in2[10] + in1[12] * in2[11]; + out[ 9] = in1[ 1] * in2[ 8] + in1[ 5] * in2[ 9] + in1[ 9] * in2[10] + in1[13] * in2[11]; + out[10] = in1[ 2] * in2[ 8] + in1[ 6] * in2[ 9] + in1[10] * in2[10] + in1[14] * in2[11]; + out[11] = in1[ 3] * in2[ 8] + in1[ 7] * in2[ 9] + in1[11] * in2[10] + in1[15] * in2[11]; + + out[12] = in1[ 0] * in2[12] + in1[ 4] * in2[13] + in1[ 8] * in2[14] + in1[12] * in2[15]; + out[13] = in1[ 1] * in2[12] + in1[ 5] * in2[13] + in1[ 9] * in2[14] + in1[13] * in2[15]; + out[14] = in1[ 2] * in2[12] + in1[ 6] * in2[13] + in1[10] * in2[14] + in1[14] * in2[15]; + out[15] = in1[ 3] * in2[12] + in1[ 7] * in2[13] + in1[11] * in2[14] + in1[15] * in2[15]; +} + +void Matrix16Transform( const matrix_t in1, const vec4_t in2, vec4_t out ) +{ + out[ 0] = in1[ 0] * in2[ 0] + in1[ 4] * in2[ 1] + in1[ 8] * in2[ 2] + in1[12] * in2[ 3]; + out[ 1] = in1[ 1] * in2[ 0] + in1[ 5] * in2[ 1] + in1[ 9] * in2[ 2] + in1[13] * in2[ 3]; + out[ 2] = in1[ 2] * in2[ 0] + in1[ 6] * in2[ 1] + in1[10] * in2[ 2] + in1[14] * in2[ 3]; + out[ 3] = in1[ 3] * in2[ 0] + in1[ 7] * in2[ 1] + in1[11] * in2[ 2] + in1[15] * in2[ 3]; +} + +qboolean Matrix16Compare( const matrix_t a, const matrix_t b ) +{ + return !(a[ 0] != b[ 0] || a[ 4] != b[ 4] || a[ 8] != b[ 8] || a[12] != b[12] || + a[ 1] != b[ 1] || a[ 5] != b[ 5] || a[ 9] != b[ 9] || a[13] != b[13] || + a[ 2] != b[ 2] || a[ 6] != b[ 6] || a[10] != b[10] || a[14] != b[14] || + a[ 3] != b[ 3] || a[ 7] != b[ 7] || a[11] != b[11] || a[15] != b[15]); +} + +void Matrix16Dump( const matrix_t in ) +{ + ri.Printf(PRINT_ALL, "%3.5f %3.5f %3.5f %3.5f\n", in[ 0], in[ 4], in[ 8], in[12]); + ri.Printf(PRINT_ALL, "%3.5f %3.5f %3.5f %3.5f\n", in[ 1], in[ 5], in[ 9], in[13]); + ri.Printf(PRINT_ALL, "%3.5f %3.5f %3.5f %3.5f\n", in[ 2], in[ 6], in[10], in[14]); + ri.Printf(PRINT_ALL, "%3.5f %3.5f %3.5f %3.5f\n", in[ 3], in[ 7], in[11], in[15]); +} + +void Matrix16Translation( vec3_t vec, matrix_t out ) +{ + out[ 0] = 1.0f; out[ 4] = 0.0f; out[ 8] = 0.0f; out[12] = vec[0]; + out[ 1] = 0.0f; out[ 5] = 1.0f; out[ 9] = 0.0f; out[13] = vec[1]; + out[ 2] = 0.0f; out[ 6] = 0.0f; out[10] = 1.0f; out[14] = vec[2]; + out[ 3] = 0.0f; out[ 7] = 0.0f; out[11] = 0.0f; out[15] = 1.0f; +} + +void Matrix16Ortho( float left, float right, float bottom, float top, float znear, float zfar, matrix_t out ) +{ + out[ 0] = 2.0f / (right - left); out[ 4] = 0.0f; out[ 8] = 0.0f; out[12] = -(right + left) / (right - left); + out[ 1] = 0.0f; out[ 5] = 2.0f / (top - bottom); out[ 9] = 0.0f; out[13] = -(top + bottom) / (top - bottom); + out[ 2] = 0.0f; out[ 6] = 0.0f; out[10] = 2.0f / (zfar - znear); out[14] = -(zfar + znear) / (zfar - znear); + out[ 3] = 0.0f; out[ 7] = 0.0f; out[11] = 0.0f; out[15] = 1.0f; +} + +void Matrix16View(vec3_t axes[3], vec3_t origin, matrix_t out) +{ + out[0] = axes[0][0]; + out[1] = axes[1][0]; + out[2] = axes[2][0]; + out[3] = 0; + + out[4] = axes[0][1]; + out[5] = axes[1][1]; + out[6] = axes[2][1]; + out[7] = 0; + + out[8] = axes[0][2]; + out[9] = axes[1][2]; + out[10] = axes[2][2]; + out[11] = 0; + + out[12] = -DotProduct(origin, axes[0]); + out[13] = -DotProduct(origin, axes[1]); + out[14] = -DotProduct(origin, axes[2]); + out[15] = 1; +} + +void Matrix16SimpleInverse( const matrix_t in, matrix_t out) +{ + vec3_t v; + float invSqrLen; + + VectorCopy(in + 0, v); + invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); + out[ 0] = v[0]; out[ 4] = v[1]; out[ 8] = v[2]; out[12] = -DotProduct(v, &in[12]); + + VectorCopy(in + 4, v); + invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); + out[ 1] = v[0]; out[ 5] = v[1]; out[ 9] = v[2]; out[13] = -DotProduct(v, &in[12]); + + VectorCopy(in + 8, v); + invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); + out[ 2] = v[0]; out[ 6] = v[1]; out[10] = v[2]; out[14] = -DotProduct(v, &in[12]); + + out[ 3] = 0.0f; out[ 7] = 0.0f; out[11] = 0.0f; out[15] = 1.0f; +} + +qboolean SpheresIntersect(vec3_t origin1, float radius1, vec3_t origin2, float radius2) +{ + float radiusSum = radius1 + radius2; + vec3_t diff; + + VectorSubtract(origin1, origin2, diff); + + if (DotProduct(diff, diff) <= radiusSum * radiusSum) + { + return qtrue; + } + + return qfalse; +} + +void BoundingSphereOfSpheres(vec3_t origin1, float radius1, vec3_t origin2, float radius2, vec3_t origin3, float *radius3) +{ + vec3_t diff; + + VectorScale(origin1, 0.5f, origin3); + VectorMA(origin3, 0.5f, origin2, origin3); + + VectorSubtract(origin1, origin2, diff); + *radius3 = VectorLength(diff) * 0.5f + MAX(radius1, radius2); +} + +int NextPowerOfTwo(int in) +{ + int out; + + for (out = 1; out < in; out <<= 1) + ; + + return out; +} + +unsigned short FloatToHalf(float in) +{ + unsigned short out; + + union + { + float f; + unsigned int i; + } f32; + + int sign, inExponent, inFraction; + int outExponent, outFraction; + + f32.f = in; + + sign = (f32.i & 0x80000000) >> 31; + inExponent = (f32.i & 0x7F800000) >> 23; + inFraction = f32.i & 0x007FFFFF; + + outExponent = CLAMP(inExponent - 127, -15, 16) + 15; + + outFraction = 0; + if (outExponent == 0x1F) + { + if (inExponent == 0xFF && inFraction != 0) + outFraction = 0x3FF; + } + else if (outExponent == 0x00) + { + if (inExponent == 0x00 && inFraction != 0) + outFraction = 0x3FF; + } + else + outFraction = inFraction >> 13; + + out = (sign << 15) | (outExponent << 10) | outFraction; + + return out; +} diff --git a/src/renderergl2/tr_extramath.h b/src/renderergl2/tr_extramath.h new file mode 100644 index 00000000..b4ca6f3c --- /dev/null +++ b/src/renderergl2/tr_extramath.h @@ -0,0 +1,101 @@ +/* +=========================================================================== +Copyright (C) 2010 James Canete (use.less01@gmail.com) + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_extramath.h + +#ifndef __TR_EXTRAMATH_H__ +#define __TR_EXTRAMATH_H__ + +typedef vec_t matrix_t[16]; +typedef int vec2i_t[2]; +typedef int vec3i_t[3]; +typedef int vec4i_t[4]; + +void Matrix16Zero( matrix_t out ); +void Matrix16Identity( matrix_t out ); +void Matrix16Copy( const matrix_t in, matrix_t out ); +void Matrix16Multiply( const matrix_t in1, const matrix_t in2, matrix_t out ); +void Matrix16Transform( const matrix_t in1, const vec4_t in2, vec4_t out ); +qboolean Matrix16Compare(const matrix_t a, const matrix_t b); +void Matrix16Dump( const matrix_t in ); +void Matrix16Translation( vec3_t vec, matrix_t out ); +void Matrix16Ortho( float left, float right, float bottom, float top, float znear, float zfar, matrix_t out ); +void Matrix16View(vec3_t axes[3], vec3_t origin, matrix_t out); +void Matrix16SimpleInverse( const matrix_t in, matrix_t out); + +#define VectorCopy2(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1]) + +#define VectorCopy4(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3]) +#define VectorSet4(v,x,y,z,w) ((v)[0]=(x),(v)[1]=(y),(v)[2]=(z),(v)[3]=(w)) +#define DotProduct4(a,b) ((a)[0]*(b)[0] + (a)[1]*(b)[1] + (a)[2]*(b)[2] + (a)[3]*(b)[3]) +#define VectorScale4(a,b,c) ((c)[0]=(a)[0]*(b),(c)[1]=(a)[1]*(b),(c)[2]=(a)[2]*(b),(c)[3]=(a)[3]*(b)) + +#define VectorCopy5(a,b) ((b)[0]=(a)[0],(b)[1]=(a)[1],(b)[2]=(a)[2],(b)[3]=(a)[3],(b)[4]=(a)[4]) + +#define OffsetByteToFloat(a) ((float)(a) * 1.0f/127.5f - 1.0f) +#define FloatToOffsetByte(a) (byte)(((a) + 1.0f) * 127.5f) +#define ByteToFloat(a) ((float)(a) * 1.0f/255.0f) +#define FloatToByte(a) (byte)((a) * 255.0f) + +#define RGBtosRGB(a) (((a) < 0.0031308f) ? (12.92f * (a)) : (1.055f * pow((a), 0.41666f) - 0.055f)) +#define sRGBtoRGB(a) (((a) <= 0.04045f) ? ((a) / 12.92f) : (pow((((a) + 0.055f) / 1.055f), 2.4)) ) + +static ID_INLINE int VectorCompare4(const vec4_t v1, const vec4_t v2) +{ + if(v1[0] != v2[0] || v1[1] != v2[1] || v1[2] != v2[2] || v1[3] != v2[3]) + { + return 0; + } + return 1; +} + +static ID_INLINE int VectorCompare5(const vec5_t v1, const vec5_t v2) +{ + if(v1[0] != v2[0] || v1[1] != v2[1] || v1[2] != v2[2] || v1[3] != v2[3] || v1[4] != v2[4]) + { + return 0; + } + return 1; +} + +qboolean SpheresIntersect(vec3_t origin1, float radius1, vec3_t origin2, float radius2); +void BoundingSphereOfSpheres(vec3_t origin1, float radius1, vec3_t origin2, float radius2, vec3_t origin3, float *radius3); + +#ifndef SGN +#define SGN(x) (((x) >= 0) ? !!(x) : -1) +#endif + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef CLAMP +#define CLAMP(a,b,c) MIN(MAX((a),(b)),(c)) +#endif + +int NextPowerOfTwo(int in); +unsigned short FloatToHalf(float in); + +#endif diff --git a/src/renderergl2/tr_extratypes.h b/src/renderergl2/tr_extratypes.h new file mode 100644 index 00000000..b84cd5e4 --- /dev/null +++ b/src/renderergl2/tr_extratypes.h @@ -0,0 +1,43 @@ +/* +=========================================================================== +Copyright (C) 2009-2011 Andrei Drexler, Richard Allen, James Canete + +This file is part of Reaction source code. + +Reaction source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Reaction source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Reaction source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#ifndef __TR_EXTRATYPES_H__ +#define __TR_EXTRATYPES_H__ + +// tr_extratypes.h, for mods that want to extend tr_types.h without losing compatibility with original VMs + +// extra renderfx flags start at 0x0400 +#define RF_SUNFLARE 0x0400 + +// extra refdef flags start at 0x0008 +#define RDF_NOFOG 0x0008 // don't apply fog +#define RDF_EXTRA 0x0010 // Makro - refdefex_t to follow after refdef_t +#define RDF_SUNLIGHT 0x0020 // SmileTheory - render sunlight and shadows + +typedef struct { + float blurFactor; + float sunDir[3]; + float sunCol[3]; + float sunAmbCol[3]; +} refdefex_t; + +#endif diff --git a/src/renderergl2/tr_fbo.c b/src/renderergl2/tr_fbo.c new file mode 100644 index 00000000..0ad08b20 --- /dev/null +++ b/src/renderergl2/tr_fbo.c @@ -0,0 +1,861 @@ +/* +=========================================================================== +Copyright (C) 2006 Kirk Barnes +Copyright (C) 2006-2008 Robert Beckebans + +This file is part of XreaL source code. + +XreaL source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +XreaL source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_fbo.c +#include "tr_local.h" + +/* +============= +R_CheckFBO +============= +*/ +qboolean R_CheckFBO(const FBO_t * fbo) +{ + int code; + int id; + + qglGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &id); + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->frameBuffer); + + code = qglCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + + if(code == GL_FRAMEBUFFER_COMPLETE_EXT) + { + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id); + return qtrue; + } + + // an error occured + switch (code) + { + case GL_FRAMEBUFFER_COMPLETE_EXT: + break; + + case GL_FRAMEBUFFER_UNSUPPORTED_EXT: + ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Unsupported framebuffer format\n", fbo->name); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: + ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete attachment\n", fbo->name); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: + ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, missing attachment\n", fbo->name); + break; + + //case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT: + // ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, duplicate attachment\n", fbo->name); + // break; + + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: + ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, attached images must have same dimensions\n", + fbo->name); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: + ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, attached images must have same format\n", + fbo->name); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: + ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, missing draw buffer\n", fbo->name); + break; + + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: + ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) Framebuffer incomplete, missing read buffer\n", fbo->name); + break; + + default: + ri.Printf(PRINT_WARNING, "R_CheckFBO: (%s) unknown error 0x%X\n", fbo->name, code); + //ri.Error(ERR_FATAL, "R_CheckFBO: (%s) unknown error 0x%X", fbo->name, code); + //assert(0); + break; + } + + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id); + + return qfalse; +} + +/* +============ +FBO_Create +============ +*/ +FBO_t *FBO_Create(const char *name, int width, int height) +{ + FBO_t *fbo; + + if(strlen(name) >= MAX_QPATH) + { + ri.Error(ERR_DROP, "FBO_Create: \"%s\" is too long\n", name); + } + + if(width <= 0 || width > glRefConfig.maxRenderbufferSize) + { + ri.Error(ERR_DROP, "FBO_Create: bad width %i", width); + } + + if(height <= 0 || height > glRefConfig.maxRenderbufferSize) + { + ri.Error(ERR_DROP, "FBO_Create: bad height %i", height); + } + + if(tr.numFBOs == MAX_FBOS) + { + ri.Error(ERR_DROP, "FBO_Create: MAX_FBOS hit"); + } + + fbo = tr.fbos[tr.numFBOs] = ri.Hunk_Alloc(sizeof(*fbo), h_low); + Q_strncpyz(fbo->name, name, sizeof(fbo->name)); + fbo->index = tr.numFBOs++; + fbo->width = width; + fbo->height = height; + + qglGenFramebuffersEXT(1, &fbo->frameBuffer); + + return fbo; +} + +void FBO_CreateBuffer(FBO_t *fbo, int format, int index, int multisample) +{ + uint32_t *pRenderBuffer; + GLenum attachment; + qboolean absent; + + switch(format) + { + case GL_RGB: + case GL_RGBA: + case GL_RGB8: + case GL_RGBA8: + case GL_RGB16F_ARB: + case GL_RGBA16F_ARB: + case GL_RGB32F_ARB: + case GL_RGBA32F_ARB: + fbo->colorFormat = format; + pRenderBuffer = &fbo->colorBuffers[index]; + attachment = GL_COLOR_ATTACHMENT0_EXT + index; + break; + + case GL_DEPTH_COMPONENT: + case GL_DEPTH_COMPONENT16_ARB: + case GL_DEPTH_COMPONENT24_ARB: + case GL_DEPTH_COMPONENT32_ARB: + fbo->depthFormat = format; + pRenderBuffer = &fbo->depthBuffer; + attachment = GL_DEPTH_ATTACHMENT_EXT; + break; + + case GL_STENCIL_INDEX: + case GL_STENCIL_INDEX1_EXT: + case GL_STENCIL_INDEX4_EXT: + case GL_STENCIL_INDEX8_EXT: + case GL_STENCIL_INDEX16_EXT: + fbo->stencilFormat = format; + pRenderBuffer = &fbo->stencilBuffer; + attachment = GL_STENCIL_ATTACHMENT_EXT; + break; + + case GL_DEPTH_STENCIL_EXT: + case GL_DEPTH24_STENCIL8_EXT: + fbo->packedDepthStencilFormat = format; + pRenderBuffer = &fbo->packedDepthStencilBuffer; + attachment = 0; // special for stencil and depth + break; + + default: + ri.Printf(PRINT_WARNING, "FBO_CreateBuffer: invalid format %d\n", format); + return; + } + + absent = *pRenderBuffer == 0; + if (absent) + qglGenRenderbuffersEXT(1, pRenderBuffer); + + qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, *pRenderBuffer); + if (multisample && glRefConfig.framebufferMultisample) + { + qglRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER_EXT, multisample, format, fbo->width, fbo->height); + } + else + { + qglRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, format, fbo->width, fbo->height); + } + + if(absent) + { + if (attachment == 0) + { + qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, *pRenderBuffer); + qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, *pRenderBuffer); + } + else + qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attachment, GL_RENDERBUFFER_EXT, *pRenderBuffer); + } +} + + +/* +================= +R_AttachFBOTexture1D +================= +*/ +void R_AttachFBOTexture1D(int texId, int index) +{ + if(index < 0 || index >= glRefConfig.maxColorAttachments) + { + ri.Printf(PRINT_WARNING, "R_AttachFBOTexture1D: invalid attachment index %i\n", index); + return; + } + + qglFramebufferTexture1DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, GL_TEXTURE_1D, texId, 0); +} + +/* +================= +R_AttachFBOTexture2D +================= +*/ +void R_AttachFBOTexture2D(int target, int texId, int index) +{ + if(target != GL_TEXTURE_2D && (target < GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB || target > GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB)) + { + ri.Printf(PRINT_WARNING, "R_AttachFBOTexture2D: invalid target %i\n", target); + return; + } + + if(index < 0 || index >= glRefConfig.maxColorAttachments) + { + ri.Printf(PRINT_WARNING, "R_AttachFBOTexture2D: invalid attachment index %i\n", index); + return; + } + + qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, target, texId, 0); +} + +/* +================= +R_AttachFBOTexture3D +================= +*/ +void R_AttachFBOTexture3D(int texId, int index, int zOffset) +{ + if(index < 0 || index >= glRefConfig.maxColorAttachments) + { + ri.Printf(PRINT_WARNING, "R_AttachFBOTexture3D: invalid attachment index %i\n", index); + return; + } + + qglFramebufferTexture3DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT + index, GL_TEXTURE_3D_EXT, texId, 0, zOffset); +} + +/* +================= +R_AttachFBOTextureDepth +================= +*/ +void R_AttachFBOTextureDepth(int texId) +{ + qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, texId, 0); +} + +/* +================= +R_AttachFBOTexturePackedDepthStencil +================= +*/ +void R_AttachFBOTexturePackedDepthStencil(int texId) +{ + qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, texId, 0); + qglFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_STENCIL_ATTACHMENT_EXT, GL_TEXTURE_2D, texId, 0); +} + +void FBO_AttachTextureImage(image_t *img, int index) +{ + if (!glState.currentFBO) + { + ri.Printf(PRINT_WARNING, "FBO: attempted to attach a texture image with no FBO bound!\n"); + return; + } + + R_AttachFBOTexture2D(GL_TEXTURE_2D, img->texnum, index); + glState.currentFBO->colorImage[index] = img; +} + +/* +============ +FBO_Bind +============ +*/ +void FBO_Bind(FBO_t * fbo) +{ + if (glState.currentFBO == fbo) + return; + + if (r_logFile->integer) + { + // don't just call LogComment, or we will get a call to va() every frame! + if (fbo) + GLimp_LogComment(va("--- FBO_Bind( %s ) ---\n", fbo->name)); + else + GLimp_LogComment("--- FBO_Bind ( NULL ) ---\n"); + } + + if (!fbo) + { + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + //qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0); + glState.currentFBO = NULL; + + return; + } + + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo->frameBuffer); + + /* + if(fbo->colorBuffers[0]) + { + qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo->colorBuffers[0]); + } + */ + + /* + if(fbo->depthBuffer) + { + qglBindRenderbufferEXT(GL_RENDERBUFFER_EXT, fbo->depthBuffer); + qglFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, fbo->depthBuffer); + } + */ + + glState.currentFBO = fbo; +} + +/* +============ +FBO_Init +============ +*/ +void FBO_Init(void) +{ + int i; + // int width, height, hdrFormat, multisample; + int hdrFormat, multisample; + + ri.Printf(PRINT_ALL, "------- FBO_Init -------\n"); + + if(!glRefConfig.framebufferObject) + return; + + tr.numFBOs = 0; + + GL_CheckErrors(); + + R_IssuePendingRenderCommands(); + +/* if(glRefConfig.textureNonPowerOfTwo) + { + width = glConfig.vidWidth; + height = glConfig.vidHeight; + } + else + { + width = NextPowerOfTwo(glConfig.vidWidth); + height = NextPowerOfTwo(glConfig.vidHeight); + } */ + + hdrFormat = GL_RGBA8; + if (r_hdr->integer && glRefConfig.framebufferObject && glRefConfig.textureFloat) + { + hdrFormat = GL_RGB16F_ARB; + } + + qglGetIntegerv(GL_MAX_SAMPLES_EXT, &multisample); + + if (r_ext_framebuffer_multisample->integer < multisample) + { + multisample = r_ext_framebuffer_multisample->integer; + } + + if (multisample < 2 || !glRefConfig.framebufferBlit) + multisample = 0; + + if (multisample != r_ext_framebuffer_multisample->integer) + { + ri.Cvar_SetValue("r_ext_framebuffer_multisample", (float)multisample); + } + + // only create a render FBO if we need to resolve MSAA or do HDR + // otherwise just render straight to the screen (tr.renderFbo = NULL) + if (multisample && glRefConfig.framebufferMultisample) + { + tr.renderFbo = FBO_Create("_render", tr.renderDepthImage->width, tr.renderDepthImage->height); + FBO_Bind(tr.renderFbo); + + FBO_CreateBuffer(tr.renderFbo, hdrFormat, 0, multisample); + FBO_CreateBuffer(tr.renderFbo, GL_DEPTH_COMPONENT24_ARB, 0, multisample); + + R_CheckFBO(tr.renderFbo); + + + tr.msaaResolveFbo = FBO_Create("_msaaResolve", tr.renderDepthImage->width, tr.renderDepthImage->height); + FBO_Bind(tr.msaaResolveFbo); + + //FBO_CreateBuffer(tr.msaaResolveFbo, hdrFormat, 0, 0); + FBO_AttachTextureImage(tr.renderImage, 0); + + //FBO_CreateBuffer(tr.msaaResolveFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0); + R_AttachFBOTextureDepth(tr.renderDepthImage->texnum); + + R_CheckFBO(tr.msaaResolveFbo); + } + else if (r_hdr->integer) + { + tr.renderFbo = FBO_Create("_render", tr.renderDepthImage->width, tr.renderDepthImage->height); + FBO_Bind(tr.renderFbo); + + //FBO_CreateBuffer(tr.renderFbo, hdrFormat, 0, 0); + FBO_AttachTextureImage(tr.renderImage, 0); + + //FBO_CreateBuffer(tr.renderFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0); + R_AttachFBOTextureDepth(tr.renderDepthImage->texnum); + + R_CheckFBO(tr.renderFbo); + } + + // clear render buffer + // this fixes the corrupt screen bug with r_hdr 1 on older hardware + if (tr.renderFbo) + { + FBO_Bind(tr.renderFbo); + qglClearColor( 1, 0, 0.5, 1 ); + qglClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); + FBO_Bind(NULL); + } + + if (r_drawSunRays->integer) + { + tr.sunRaysFbo = FBO_Create("_sunRays", tr.renderDepthImage->width, tr.renderDepthImage->height); + FBO_Bind(tr.sunRaysFbo); + + FBO_AttachTextureImage(tr.sunRaysImage, 0); + + R_AttachFBOTextureDepth(tr.renderDepthImage->texnum); + + R_CheckFBO(tr.sunRaysFbo); + } + + // FIXME: Don't use separate color/depth buffers for a shadow buffer + for( i = 0; i < MAX_DRAWN_PSHADOWS; i++) + { + tr.pshadowFbos[i] = FBO_Create(va("_shadowmap%d", i), tr.pshadowMaps[i]->width, tr.pshadowMaps[i]->height); + FBO_Bind(tr.pshadowFbos[i]); + + //FBO_CreateBuffer(tr.pshadowFbos[i], GL_RGBA8, 0, 0); + FBO_AttachTextureImage(tr.pshadowMaps[i], 0); + + FBO_CreateBuffer(tr.pshadowFbos[i], GL_DEPTH_COMPONENT24_ARB, 0, 0); + //R_AttachFBOTextureDepth(tr.textureDepthImage->texnum); + + R_CheckFBO(tr.pshadowFbos[i]); + } + + for ( i = 0; i < 3; i++) + { + tr.sunShadowFbo[i] = FBO_Create("_sunshadowmap", tr.sunShadowDepthImage[i]->width, tr.sunShadowDepthImage[i]->height); + FBO_Bind(tr.sunShadowFbo[i]); + + //FBO_CreateBuffer(tr.sunShadowFbo[i], GL_RGBA8, 0, 0); + //FBO_AttachTextureImage(tr.sunShadowImage, 0); + qglDrawBuffer(GL_NONE); + qglReadBuffer(GL_NONE); + + //FBO_CreateBuffer(tr.sunShadowFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0); + R_AttachFBOTextureDepth(tr.sunShadowDepthImage[i]->texnum); + + R_CheckFBO(tr.sunShadowFbo[i]); + } + + for (i = 0; i < 2; i++) + { + tr.textureScratchFbo[i] = FBO_Create(va("_texturescratch%d", i), tr.textureScratchImage[i]->width, tr.textureScratchImage[i]->height); + FBO_Bind(tr.textureScratchFbo[i]); + + //FBO_CreateBuffer(tr.textureScratchFbo[i], GL_RGBA8, 0, 0); + FBO_AttachTextureImage(tr.textureScratchImage[i], 0); + + R_CheckFBO(tr.textureScratchFbo[i]); + } + + { + tr.calcLevelsFbo = FBO_Create("_calclevels", tr.calcLevelsImage->width, tr.calcLevelsImage->height); + FBO_Bind(tr.calcLevelsFbo); + + //FBO_CreateBuffer(tr.calcLevelsFbo, hdrFormat, 0, 0); + FBO_AttachTextureImage(tr.calcLevelsImage, 0); + + R_CheckFBO(tr.calcLevelsFbo); + } + + { + tr.targetLevelsFbo = FBO_Create("_targetlevels", tr.targetLevelsImage->width, tr.targetLevelsImage->height); + FBO_Bind(tr.targetLevelsFbo); + + //FBO_CreateBuffer(tr.targetLevelsFbo, hdrFormat, 0, 0); + FBO_AttachTextureImage(tr.targetLevelsImage, 0); + + R_CheckFBO(tr.targetLevelsFbo); + } + + if (r_softOverbright->integer) + { + //tr.screenScratchFbo = FBO_Create("_screenscratch", width, height); + tr.screenScratchFbo = FBO_Create("_screenscratch", tr.screenScratchImage->width, tr.screenScratchImage->height); + FBO_Bind(tr.screenScratchFbo); + + //FBO_CreateBuffer(tr.screenScratchFbo, format, 0, 0); + FBO_AttachTextureImage(tr.screenScratchImage, 0); + + // FIXME: hack: share zbuffer between render fbo and pre-screen fbo + //FBO_CreateBuffer(tr.screenScratchFbo, GL_DEPTH_COMPONENT24_ARB, 0, 0); + R_AttachFBOTextureDepth(tr.renderDepthImage->texnum); + + R_CheckFBO(tr.screenScratchFbo); + } + + for (i = 0; i < 2; i++) + { + tr.quarterFbo[i] = FBO_Create(va("_quarter%d", i), tr.quarterImage[i]->width, tr.quarterImage[i]->height); + FBO_Bind(tr.quarterFbo[i]); + + //FBO_CreateBuffer(tr.quarterFbo[i], hdrFormat, 0, 0); + FBO_AttachTextureImage(tr.quarterImage[i], 0); + + R_CheckFBO(tr.quarterFbo[i]); + } + + { + tr.screenShadowFbo = FBO_Create("_screenshadow", tr.screenShadowImage->width, tr.screenShadowImage->height); + FBO_Bind(tr.screenShadowFbo); + + FBO_AttachTextureImage(tr.screenShadowImage, 0); + + R_CheckFBO(tr.screenShadowFbo); + } + + if (r_ssao->integer) + { + tr.hdrDepthFbo = FBO_Create("_hdrDepth", tr.hdrDepthImage->width, tr.hdrDepthImage->height); + FBO_Bind(tr.hdrDepthFbo); + + FBO_AttachTextureImage(tr.hdrDepthImage, 0); + + R_CheckFBO(tr.hdrDepthFbo); + + tr.screenSsaoFbo = FBO_Create("_screenssao", tr.screenSsaoImage->width, tr.screenSsaoImage->height); + FBO_Bind(tr.screenSsaoFbo); + + FBO_AttachTextureImage(tr.screenSsaoImage, 0); + + R_CheckFBO(tr.screenSsaoFbo); + } + + GL_CheckErrors(); + + FBO_Bind(NULL); +} + +/* +============ +FBO_Shutdown +============ +*/ +void FBO_Shutdown(void) +{ + int i, j; + FBO_t *fbo; + + ri.Printf(PRINT_ALL, "------- FBO_Shutdown -------\n"); + + if(!glRefConfig.framebufferObject) + return; + + FBO_Bind(NULL); + + for(i = 0; i < tr.numFBOs; i++) + { + fbo = tr.fbos[i]; + + for(j = 0; j < glRefConfig.maxColorAttachments; j++) + { + if(fbo->colorBuffers[j]) + qglDeleteRenderbuffersEXT(1, &fbo->colorBuffers[j]); + } + + if(fbo->depthBuffer) + qglDeleteRenderbuffersEXT(1, &fbo->depthBuffer); + + if(fbo->stencilBuffer) + qglDeleteRenderbuffersEXT(1, &fbo->stencilBuffer); + + if(fbo->frameBuffer) + qglDeleteFramebuffersEXT(1, &fbo->frameBuffer); + } +} + +/* +============ +R_FBOList_f +============ +*/ +void R_FBOList_f(void) +{ + int i; + FBO_t *fbo; + + if(!glRefConfig.framebufferObject) + { + ri.Printf(PRINT_ALL, "GL_EXT_framebuffer_object is not available.\n"); + return; + } + + ri.Printf(PRINT_ALL, " size name\n"); + ri.Printf(PRINT_ALL, "----------------------------------------------------------\n"); + + for(i = 0; i < tr.numFBOs; i++) + { + fbo = tr.fbos[i]; + + ri.Printf(PRINT_ALL, " %4i: %4i %4i %s\n", i, fbo->width, fbo->height, fbo->name); + } + + ri.Printf(PRINT_ALL, " %i FBOs\n", tr.numFBOs); +} + +// FIXME +extern void RB_SetGL2D (void); + +void FBO_BlitFromTexture(struct image_s *src, vec4i_t inSrcBox, vec2_t inSrcTexScale, FBO_t *dst, vec4i_t inDstBox, struct shaderProgram_s *shaderProgram, vec4_t inColor, int blend) +{ + vec4i_t dstBox, srcBox; + vec2_t srcTexScale; + vec4_t color; + vec4_t quadVerts[4]; + vec2_t texCoords[4]; + vec2_t invTexRes; + FBO_t *oldFbo = glState.currentFBO; + matrix_t projection; + int width, height; + + if (!src) + return; + + if (inSrcBox) + { + VectorSet4(srcBox, inSrcBox[0], inSrcBox[1], inSrcBox[0] + inSrcBox[2], inSrcBox[1] + inSrcBox[3]); + } + else + { + VectorSet4(srcBox, 0, 0, src->width, src->height); + } + + // framebuffers are 0 bottom, Y up. + if (inDstBox) + { + if (dst) + { + dstBox[0] = inDstBox[0]; + dstBox[1] = dst->height - inDstBox[1] - inDstBox[3]; + dstBox[2] = inDstBox[0] + inDstBox[2]; + dstBox[3] = dst->height - inDstBox[1]; + } + else + { + dstBox[0] = inDstBox[0]; + dstBox[1] = glConfig.vidHeight - inDstBox[1] - inDstBox[3]; + dstBox[2] = inDstBox[0] + inDstBox[2]; + dstBox[3] = glConfig.vidHeight - inDstBox[1]; + } + } + else if (dst) + { + VectorSet4(dstBox, 0, dst->height, dst->width, 0); + } + else + { + VectorSet4(dstBox, 0, glConfig.vidHeight, glConfig.vidWidth, 0); + } + + if (inSrcTexScale) + { + VectorCopy2(inSrcTexScale, srcTexScale); + } + else + { + srcTexScale[0] = srcTexScale[1] = 1.0f; + } + + if (inColor) + { + VectorCopy4(inColor, color); + } + else + { + color[0] = color[1] = color[2] = color[3] = 1.0f; + } + + if (!shaderProgram) + { + shaderProgram = &tr.textureColorShader; + } + + FBO_Bind(dst); + + if (glState.currentFBO) + { + width = glState.currentFBO->width; + height = glState.currentFBO->height; + } + else + { + width = glConfig.vidWidth; + height = glConfig.vidHeight; + } + + qglViewport( 0, 0, width, height ); + qglScissor( 0, 0, width, height ); + + Matrix16Ortho(0, width, height, 0, 0, 1, projection); + + qglDisable( GL_CULL_FACE ); + + GL_BindToTMU(src, TB_COLORMAP); + + VectorSet4(quadVerts[0], dstBox[0], dstBox[1], 0, 1); + VectorSet4(quadVerts[1], dstBox[2], dstBox[1], 0, 1); + VectorSet4(quadVerts[2], dstBox[2], dstBox[3], 0, 1); + VectorSet4(quadVerts[3], dstBox[0], dstBox[3], 0, 1); + + texCoords[0][0] = srcBox[0] / (float)src->width; texCoords[0][1] = 1.0f - srcBox[1] / (float)src->height; + texCoords[1][0] = srcBox[2] / (float)src->width; texCoords[1][1] = 1.0f - srcBox[1] / (float)src->height; + texCoords[2][0] = srcBox[2] / (float)src->width; texCoords[2][1] = 1.0f - srcBox[3] / (float)src->height; + texCoords[3][0] = srcBox[0] / (float)src->width; texCoords[3][1] = 1.0f - srcBox[3] / (float)src->height; + + invTexRes[0] = 1.0f / src->width * srcTexScale[0]; + invTexRes[1] = 1.0f / src->height * srcTexScale[1]; + + GL_State( blend ); + + GLSL_BindProgram(shaderProgram); + + GLSL_SetUniformMatrix16(shaderProgram, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, projection); + GLSL_SetUniformVec4(shaderProgram, TEXTURECOLOR_UNIFORM_COLOR, color); + GLSL_SetUniformVec2(shaderProgram, TEXTURECOLOR_UNIFORM_INVTEXRES, invTexRes); + GLSL_SetUniformVec2(shaderProgram, TEXTURECOLOR_UNIFORM_AUTOEXPOSUREMINMAX, tr.refdef.autoExposureMinMax); + GLSL_SetUniformVec3(shaderProgram, TEXTURECOLOR_UNIFORM_TONEMINAVGMAXLINEAR, tr.refdef.toneMinAvgMaxLinear); + + RB_InstantQuad2(quadVerts, texCoords); //, color, shaderProgram, invTexRes); + + FBO_Bind(oldFbo); +} + +void FBO_Blit(FBO_t *src, vec4i_t inSrcBox, vec2_t srcTexScale, FBO_t *dst, vec4i_t dstBox, struct shaderProgram_s *shaderProgram, vec4_t color, int blend) +{ + vec4i_t srcBox; + + if (!src) + return; + + // framebuffers are 0 bottom, Y up. + if (inSrcBox) + { + srcBox[0] = inSrcBox[0]; + srcBox[1] = src->height - inSrcBox[1] - inSrcBox[3]; + srcBox[2] = inSrcBox[2]; + srcBox[3] = inSrcBox[3]; + } + else + { + VectorSet4(srcBox, 0, src->height, src->width, -src->height); + } + + FBO_BlitFromTexture(src->colorImage[0], srcBox, srcTexScale, dst, dstBox, shaderProgram, color, blend | GLS_DEPTHTEST_DISABLE); +} + +void FBO_FastBlit(FBO_t *src, vec4i_t srcBox, FBO_t *dst, vec4i_t dstBox, int buffers, int filter) +{ + vec4i_t srcBoxFinal, dstBoxFinal; + GLuint srcFb, dstFb; + + if (!glRefConfig.framebufferBlit) + { + FBO_Blit(src, srcBox, NULL, dst, dstBox, NULL, NULL, 0); + return; + } + + // get to a neutral state first + //FBO_Bind(NULL); + + srcFb = src ? src->frameBuffer : 0; + dstFb = dst ? dst->frameBuffer : 0; + + if (!srcBox) + { + if (src) + { + VectorSet4(srcBoxFinal, 0, 0, src->width, src->height); + } + else + { + VectorSet4(srcBoxFinal, 0, 0, glConfig.vidWidth, glConfig.vidHeight); + } + } + else + { + VectorSet4(srcBoxFinal, srcBox[0], srcBox[1], srcBox[0] + srcBox[2], srcBox[1] + srcBox[3]); + } + + if (!dstBox) + { + if (dst) + { + VectorSet4(dstBoxFinal, 0, 0, dst->width, dst->height); + } + else + { + VectorSet4(dstBoxFinal, 0, 0, glConfig.vidWidth, glConfig.vidHeight); + } + } + else + { + VectorSet4(dstBoxFinal, dstBox[0], dstBox[1], dstBox[0] + dstBox[2], dstBox[1] + dstBox[3]); + } + + qglBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, srcFb); + qglBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, dstFb); + qglBlitFramebufferEXT(srcBoxFinal[0], srcBoxFinal[1], srcBoxFinal[2], srcBoxFinal[3], + dstBoxFinal[0], dstBoxFinal[1], dstBoxFinal[2], dstBoxFinal[3], + buffers, filter); + + qglBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); + glState.currentFBO = NULL; +} diff --git a/src/renderergl2/tr_fbo.h b/src/renderergl2/tr_fbo.h new file mode 100644 index 00000000..f0366251 --- /dev/null +++ b/src/renderergl2/tr_fbo.h @@ -0,0 +1,64 @@ +/* +=========================================================================== +Copyright (C) 2010 James Canete (use.less01@gmail.com) + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_fbo.h + +#ifndef __TR_FBO_H__ +#define __TR_FBO_H__ + +struct image_s; +struct shaderProgram_s; + +typedef struct FBO_s +{ + char name[MAX_QPATH]; + + int index; + + uint32_t frameBuffer; + + uint32_t colorBuffers[16]; + int colorFormat; + struct image_s *colorImage[16]; + + uint32_t depthBuffer; + int depthFormat; + + uint32_t stencilBuffer; + int stencilFormat; + + uint32_t packedDepthStencilBuffer; + int packedDepthStencilFormat; + + int width; + int height; +} FBO_t; + +void FBO_Bind(FBO_t *fbo); +void FBO_Init(void); +void FBO_Shutdown(void); + +void FBO_BlitFromTexture(struct image_s *src, vec4i_t srcBox, vec2_t srcTexScale, FBO_t *dst, vec4i_t dstBox, struct shaderProgram_s *shaderProgram, vec4_t color, int blend); +void FBO_Blit(FBO_t *src, vec4i_t srcBox, vec2_t srcTexScale, FBO_t *dst, vec4i_t dstBox, struct shaderProgram_s *shaderProgram, vec4_t color, int blend); +void FBO_FastBlit(FBO_t *src, vec4i_t srcBox, FBO_t *dst, vec4i_t dstBox, int buffers, int filter); + + +#endif diff --git a/src/renderergl2/tr_flares.c b/src/renderergl2/tr_flares.c new file mode 100644 index 00000000..8e6c321f --- /dev/null +++ b/src/renderergl2/tr_flares.c @@ -0,0 +1,532 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_flares.c + +#include "tr_local.h" + +/* +============================================================================= + +LIGHT FLARES + +A light flare is an effect that takes place inside the eye when bright light +sources are visible. The size of the flare reletive to the screen is nearly +constant, irrespective of distance, but the intensity should be proportional to the +projected area of the light source. + +A surface that has been flagged as having a light flare will calculate the depth +buffer value that its midpoint should have when the surface is added. + +After all opaque surfaces have been rendered, the depth buffer is read back for +each flare in view. If the point has not been obscured by a closer surface, the +flare should be drawn. + +Surfaces that have a repeated texture should never be flagged as flaring, because +there will only be a single flare added at the midpoint of the polygon. + +To prevent abrupt popping, the intensity of the flare is interpolated up and +down as it changes visibility. This involves scene to scene state, unlike almost +all other aspects of the renderer, and is complicated by the fact that a single +frame may have multiple scenes. + +RB_RenderFlares() will be called once per view (twice in a mirrored scene, potentially +up to five or more times in a frame with 3D status bar icons). + +============================================================================= +*/ + + +// flare states maintain visibility over multiple frames for fading +// layers: view, mirror, menu +typedef struct flare_s { + struct flare_s *next; // for active chain + + int addedFrame; + + qboolean inPortal; // true if in a portal view of the scene + int frameSceneNum; + void *surface; + int fogNum; + + int fadeTime; + + qboolean visible; // state of last test + float drawIntensity; // may be non 0 even if !visible due to fading + + int windowX, windowY; + float eyeZ; + + vec3_t origin; + vec3_t color; +} flare_t; + +#define MAX_FLARES 128 + +flare_t r_flareStructs[MAX_FLARES]; +flare_t *r_activeFlares, *r_inactiveFlares; + +int flareCoeff; + +/* +================== +R_ClearFlares +================== +*/ +void R_ClearFlares( void ) { + int i; + + Com_Memset( r_flareStructs, 0, sizeof( r_flareStructs ) ); + r_activeFlares = NULL; + r_inactiveFlares = NULL; + + for ( i = 0 ; i < MAX_FLARES ; i++ ) { + r_flareStructs[i].next = r_inactiveFlares; + r_inactiveFlares = &r_flareStructs[i]; + } +} + + +/* +================== +RB_AddFlare + +This is called at surface tesselation time +================== +*/ +void RB_AddFlare( void *surface, int fogNum, vec3_t point, vec3_t color, vec3_t normal ) { + int i; + flare_t *f; + vec3_t local; + float d = 1; + vec4_t eye, clip, normalized, window; + + backEnd.pc.c_flareAdds++; + + if(normal && (normal[0] || normal[1] || normal[2])) + { + VectorSubtract( backEnd.viewParms.or.origin, point, local ); + VectorNormalizeFast(local); + d = DotProduct(local, normal); + + // If the viewer is behind the flare don't add it. + if(d < 0) + return; + } + + // if the point is off the screen, don't bother adding it + // calculate screen coordinates and depth + R_TransformModelToClip( point, backEnd.or.modelMatrix, + backEnd.viewParms.projectionMatrix, eye, clip ); + + // check to see if the point is completely off screen + for ( i = 0 ; i < 3 ; i++ ) { + if ( clip[i] >= clip[3] || clip[i] <= -clip[3] ) { + return; + } + } + + R_TransformClipToWindow( clip, &backEnd.viewParms, normalized, window ); + + if ( window[0] < 0 || window[0] >= backEnd.viewParms.viewportWidth + || window[1] < 0 || window[1] >= backEnd.viewParms.viewportHeight ) { + return; // shouldn't happen, since we check the clip[] above, except for FP rounding + } + + // see if a flare with a matching surface, scene, and view exists + for ( f = r_activeFlares ; f ; f = f->next ) { + if ( f->surface == surface && f->frameSceneNum == backEnd.viewParms.frameSceneNum + && f->inPortal == backEnd.viewParms.isPortal ) { + break; + } + } + + // allocate a new one + if (!f ) { + if ( !r_inactiveFlares ) { + // the list is completely full + return; + } + f = r_inactiveFlares; + r_inactiveFlares = r_inactiveFlares->next; + f->next = r_activeFlares; + r_activeFlares = f; + + f->surface = surface; + f->frameSceneNum = backEnd.viewParms.frameSceneNum; + f->inPortal = backEnd.viewParms.isPortal; + f->addedFrame = -1; + } + + if ( f->addedFrame != backEnd.viewParms.frameCount - 1 ) { + f->visible = qfalse; + f->fadeTime = backEnd.refdef.time - 2000; + } + + f->addedFrame = backEnd.viewParms.frameCount; + f->fogNum = fogNum; + + VectorCopy(point, f->origin); + VectorCopy( color, f->color ); + + // fade the intensity of the flare down as the + // light surface turns away from the viewer + VectorScale( f->color, d, f->color ); + + // save info needed to test + f->windowX = backEnd.viewParms.viewportX + window[0]; + f->windowY = backEnd.viewParms.viewportY + window[1]; + + f->eyeZ = eye[2]; +} + +/* +================== +RB_AddDlightFlares +================== +*/ +void RB_AddDlightFlares( void ) { + dlight_t *l; + int i, j, k; + fog_t *fog = NULL; + + if ( !r_flares->integer ) { + return; + } + + l = backEnd.refdef.dlights; + + if(tr.world) + fog = tr.world->fogs; + + for (i=0 ; inumfogs ; j++ ) { + fog = &tr.world->fogs[j]; + for ( k = 0 ; k < 3 ; k++ ) { + if ( l->origin[k] < fog->bounds[0][k] || l->origin[k] > fog->bounds[1][k] ) { + break; + } + } + if ( k == 3 ) { + break; + } + } + if ( j == tr.world->numfogs ) { + j = 0; + } + } + else + j = 0; + + RB_AddFlare( (void *)l, j, l->origin, l->color, NULL ); + } +} + +/* +=============================================================================== + +FLARE BACK END + +=============================================================================== +*/ + +/* +================== +RB_TestFlare +================== +*/ +void RB_TestFlare( flare_t *f ) { + float depth; + qboolean visible; + float fade; + float screenZ; + + backEnd.pc.c_flareTests++; + + // doing a readpixels is as good as doing a glFinish(), so + // don't bother with another sync + glState.finishCalled = qfalse; + + // read back the z buffer contents + qglReadPixels( f->windowX, f->windowY, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &depth ); + + screenZ = backEnd.viewParms.projectionMatrix[14] / + ( ( 2*depth - 1 ) * backEnd.viewParms.projectionMatrix[11] - backEnd.viewParms.projectionMatrix[10] ); + + visible = ( -f->eyeZ - -screenZ ) < 24; + + if ( visible ) { + if ( !f->visible ) { + f->visible = qtrue; + f->fadeTime = backEnd.refdef.time - 1; + } + fade = ( ( backEnd.refdef.time - f->fadeTime ) /1000.0f ) * r_flareFade->value; + } else { + if ( f->visible ) { + f->visible = qfalse; + f->fadeTime = backEnd.refdef.time - 1; + } + fade = 1.0f - ( ( backEnd.refdef.time - f->fadeTime ) / 1000.0f ) * r_flareFade->value; + } + + if ( fade < 0 ) { + fade = 0; + } + if ( fade > 1 ) { + fade = 1; + } + + f->drawIntensity = fade; +} + + +/* +================== +RB_RenderFlare +================== +*/ +void RB_RenderFlare( flare_t *f ) { + float size; + vec3_t color; + int iColor[3]; + float distance, intensity, factor; + byte fogFactors[3] = {255, 255, 255}; + + backEnd.pc.c_flareRenders++; + + // We don't want too big values anyways when dividing by distance. + if(f->eyeZ > -1.0f) + distance = 1.0f; + else + distance = -f->eyeZ; + + // calculate the flare size.. + size = backEnd.viewParms.viewportWidth * ( r_flareSize->value/640.0f + 8 / distance ); + +/* + * This is an alternative to intensity scaling. It changes the size of the flare on screen instead + * with growing distance. See in the description at the top why this is not the way to go. + // size will change ~ 1/r. + size = backEnd.viewParms.viewportWidth * (r_flareSize->value / (distance * -2.0f)); +*/ + +/* + * As flare sizes stay nearly constant with increasing distance we must decrease the intensity + * to achieve a reasonable visual result. The intensity is ~ (size^2 / distance^2) which can be + * got by considering the ratio of + * (flaresurface on screen) : (Surface of sphere defined by flare origin and distance from flare) + * An important requirement is: + * intensity <= 1 for all distances. + * + * The formula used here to compute the intensity is as follows: + * intensity = flareCoeff * size^2 / (distance + size*sqrt(flareCoeff))^2 + * As you can see, the intensity will have a max. of 1 when the distance is 0. + * The coefficient flareCoeff will determine the falloff speed with increasing distance. + */ + + factor = distance + size * sqrt(flareCoeff); + + intensity = flareCoeff * size * size / (factor * factor); + + VectorScale(f->color, f->drawIntensity * intensity, color); + +// Calculations for fogging + if(tr.world && f->fogNum < tr.world->numfogs) + { + tess.numVertexes = 1; + VectorCopy(f->origin, tess.xyz[0]); + tess.fogNum = f->fogNum; + + RB_CalcModulateColorsByFog(fogFactors); + + // We don't need to render the flare if colors are 0 anyways. + if(!(fogFactors[0] || fogFactors[1] || fogFactors[2])) + return; + } + + iColor[0] = color[0] * fogFactors[0]; + iColor[1] = color[1] * fogFactors[1]; + iColor[2] = color[2] * fogFactors[2]; + + RB_BeginSurface( tr.flareShader, f->fogNum ); + + // FIXME: use quadstamp? + tess.xyz[tess.numVertexes][0] = f->windowX - size; + tess.xyz[tess.numVertexes][1] = f->windowY - size; + tess.texCoords[tess.numVertexes][0][0] = 0; + tess.texCoords[tess.numVertexes][0][1] = 0; + tess.vertexColors[tess.numVertexes][0] = iColor[0] / 255.0f; + tess.vertexColors[tess.numVertexes][1] = iColor[1] / 255.0f; + tess.vertexColors[tess.numVertexes][2] = iColor[2] / 255.0f; + tess.vertexColors[tess.numVertexes][3] = 1.0f; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = f->windowX - size; + tess.xyz[tess.numVertexes][1] = f->windowY + size; + tess.texCoords[tess.numVertexes][0][0] = 0; + tess.texCoords[tess.numVertexes][0][1] = 1; + tess.vertexColors[tess.numVertexes][0] = iColor[0] / 255.0f; + tess.vertexColors[tess.numVertexes][1] = iColor[1] / 255.0f; + tess.vertexColors[tess.numVertexes][2] = iColor[2] / 255.0f; + tess.vertexColors[tess.numVertexes][3] = 1.0f; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = f->windowX + size; + tess.xyz[tess.numVertexes][1] = f->windowY + size; + tess.texCoords[tess.numVertexes][0][0] = 1; + tess.texCoords[tess.numVertexes][0][1] = 1; + tess.vertexColors[tess.numVertexes][0] = iColor[0] / 255.0f; + tess.vertexColors[tess.numVertexes][1] = iColor[1] / 255.0f; + tess.vertexColors[tess.numVertexes][2] = iColor[2] / 255.0f; + tess.vertexColors[tess.numVertexes][3] = 1.0f; + tess.numVertexes++; + + tess.xyz[tess.numVertexes][0] = f->windowX + size; + tess.xyz[tess.numVertexes][1] = f->windowY - size; + tess.texCoords[tess.numVertexes][0][0] = 1; + tess.texCoords[tess.numVertexes][0][1] = 0; + tess.vertexColors[tess.numVertexes][0] = iColor[0] / 255.0f; + tess.vertexColors[tess.numVertexes][1] = iColor[1] / 255.0f; + tess.vertexColors[tess.numVertexes][2] = iColor[2] / 255.0f; + tess.vertexColors[tess.numVertexes][3] = 1.0f; + tess.numVertexes++; + + tess.indexes[tess.numIndexes++] = 0; + tess.indexes[tess.numIndexes++] = 1; + tess.indexes[tess.numIndexes++] = 2; + tess.indexes[tess.numIndexes++] = 0; + tess.indexes[tess.numIndexes++] = 2; + tess.indexes[tess.numIndexes++] = 3; + + RB_EndSurface(); +} + +/* +================== +RB_RenderFlares + +Because flares are simulating an occular effect, they should be drawn after +everything (all views) in the entire frame has been drawn. + +Because of the way portals use the depth buffer to mark off areas, the +needed information would be lost after each view, so we are forced to draw +flares after each view. + +The resulting artifact is that flares in mirrors or portals don't dim properly +when occluded by something in the main view, and portal flares that should +extend past the portal edge will be overwritten. +================== +*/ +void RB_RenderFlares (void) { + flare_t *f; + flare_t **prev; + qboolean draw; + matrix_t oldmodelview, oldprojection, matrix; + + if ( !r_flares->integer ) { + return; + } + + if(r_flareCoeff->modified) + { + if(r_flareCoeff->value == 0.0f) + flareCoeff = atof(FLARE_STDCOEFF); + else + flareCoeff = r_flareCoeff->value; + + r_flareCoeff->modified = qfalse; + } + + // Reset currentEntity to world so that any previously referenced entities + // don't have influence on the rendering of these flares (i.e. RF_ renderer flags). + backEnd.currentEntity = &tr.worldEntity; + backEnd.or = backEnd.viewParms.world; + +// RB_AddDlightFlares(); + + // perform z buffer readback on each flare in this view + draw = qfalse; + prev = &r_activeFlares; + while ( ( f = *prev ) != NULL ) { + // throw out any flares that weren't added last frame + if ( f->addedFrame < backEnd.viewParms.frameCount - 1 ) { + *prev = f->next; + f->next = r_inactiveFlares; + r_inactiveFlares = f; + continue; + } + + // don't draw any here that aren't from this scene / portal + f->drawIntensity = 0; + if ( f->frameSceneNum == backEnd.viewParms.frameSceneNum + && f->inPortal == backEnd.viewParms.isPortal ) { + RB_TestFlare( f ); + if ( f->drawIntensity ) { + draw = qtrue; + } else { + // this flare has completely faded out, so remove it from the chain + *prev = f->next; + f->next = r_inactiveFlares; + r_inactiveFlares = f; + continue; + } + } + + prev = &f->next; + } + + if ( !draw ) { + return; // none visible + } + + if ( backEnd.viewParms.isPortal ) { + qglDisable (GL_CLIP_PLANE0); + } + + Matrix16Copy(glState.projection, oldprojection); + Matrix16Copy(glState.modelview, oldmodelview); + Matrix16Identity(matrix); + GL_SetModelviewMatrix(matrix); + Matrix16Ortho( backEnd.viewParms.viewportX, backEnd.viewParms.viewportX + backEnd.viewParms.viewportWidth, + backEnd.viewParms.viewportY, backEnd.viewParms.viewportY + backEnd.viewParms.viewportHeight, + -99999, 99999, matrix ); + GL_SetProjectionMatrix(matrix); + + for ( f = r_activeFlares ; f ; f = f->next ) { + if ( f->frameSceneNum == backEnd.viewParms.frameSceneNum + && f->inPortal == backEnd.viewParms.isPortal + && f->drawIntensity ) { + RB_RenderFlare( f ); + } + } + + GL_SetProjectionMatrix(oldprojection); + GL_SetModelviewMatrix(oldmodelview); +} + + + + + diff --git a/src/renderergl2/tr_font.c b/src/renderergl2/tr_font.c new file mode 100644 index 00000000..21226709 --- /dev/null +++ b/src/renderergl2/tr_font.c @@ -0,0 +1,554 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_font.c +// +// +// The font system uses FreeType 2.x to render TrueType fonts for use within the game. +// As of this writing ( Nov, 2000 ) Team Arena uses these fonts for all of the ui and +// about 90% of the cgame presentation. A few areas of the CGAME were left uses the old +// fonts since the code is shared with standard Q3A. +// +// If you include this font rendering code in a commercial product you MUST include the +// following somewhere with your product, see www.freetype.org for specifics or changes. +// The Freetype code also uses some hinting techniques that MIGHT infringe on patents +// held by apple so be aware of that also. +// +// As of Q3A 1.25+ and Team Arena, we are shipping the game with the font rendering code +// disabled. This removes any potential patent issues and it keeps us from having to +// distribute an actual TrueTrype font which is 1. expensive to do and 2. seems to require +// an act of god to accomplish. +// +// What we did was pre-render the fonts using FreeType ( which is why we leave the FreeType +// credit in the credits ) and then saved off the glyph data and then hand touched up the +// font bitmaps so they scale a bit better in GL. +// +// There are limitations in the way fonts are saved and reloaded in that it is based on +// point size and not name. So if you pre-render Helvetica in 18 point and Impact in 18 point +// you will end up with a single 18 point data file and image set. Typically you will want to +// choose 3 sizes to best approximate the scaling you will be doing in the ui scripting system +// +// In the UI Scripting code, a scale of 1.0 is equal to a 48 point font. In Team Arena, we +// use three or four scales, most of them exactly equaling the specific rendered size. We +// rendered three sizes in Team Arena, 12, 16, and 20. +// +// To generate new font data you need to go through the following steps. +// 1. delete the fontImage_x_xx.tga files and fontImage_xx.dat files from the fonts path. +// 2. in a ui script, specificy a font, smallFont, and bigFont keyword with font name and +// point size. the original TrueType fonts must exist in fonts at this point. +// 3. run the game, you should see things normally. +// 4. Exit the game and there will be three dat files and at least three tga files. The +// tga's are in 256x256 pages so if it takes three images to render a 24 point font you +// will end up with fontImage_0_24.tga through fontImage_2_24.tga +// 5. In future runs of the game, the system looks for these images and data files when a s +// specific point sized font is rendered and loads them for use. +// 6. Because of the original beta nature of the FreeType code you will probably want to hand +// touch the font bitmaps. +// +// Currently a define in the project turns on or off the FreeType code which is currently +// defined out. To pre-render new fonts you need enable the define ( BUILD_FREETYPE ) and +// uncheck the exclude from build check box in the FreeType2 area of the Renderer project. + + +#include "tr_local.h" +#include "../qcommon/qcommon.h" + +#ifdef BUILD_FREETYPE +#include +#include FT_ERRORS_H +#include FT_SYSTEM_H +#include FT_IMAGE_H +#include FT_FREETYPE_H +#include FT_OUTLINE_H + +#define _FLOOR(x) ((x) & -64) +#define _CEIL(x) (((x)+63) & -64) +#define _TRUNC(x) ((x) >> 6) + +FT_Library ftLibrary = NULL; +#endif + +#define MAX_FONTS 6 +static int registeredFontCount = 0; +static fontInfo_t registeredFont[MAX_FONTS]; + +#ifdef BUILD_FREETYPE +void R_GetGlyphInfo(FT_GlyphSlot glyph, int *left, int *right, int *width, int *top, int *bottom, int *height, int *pitch) { + *left = _FLOOR( glyph->metrics.horiBearingX ); + *right = _CEIL( glyph->metrics.horiBearingX + glyph->metrics.width ); + *width = _TRUNC(*right - *left); + + *top = _CEIL( glyph->metrics.horiBearingY ); + *bottom = _FLOOR( glyph->metrics.horiBearingY - glyph->metrics.height ); + *height = _TRUNC( *top - *bottom ); + *pitch = ( qtrue ? (*width+3) & -4 : (*width+7) >> 3 ); +} + + +FT_Bitmap *R_RenderGlyph(FT_GlyphSlot glyph, glyphInfo_t* glyphOut) { + FT_Bitmap *bit2; + int left, right, width, top, bottom, height, pitch, size; + + R_GetGlyphInfo(glyph, &left, &right, &width, &top, &bottom, &height, &pitch); + + if ( glyph->format == ft_glyph_format_outline ) { + size = pitch*height; + + bit2 = ri.Malloc(sizeof(FT_Bitmap)); + + bit2->width = width; + bit2->rows = height; + bit2->pitch = pitch; + bit2->pixel_mode = ft_pixel_mode_grays; + //bit2->pixel_mode = ft_pixel_mode_mono; + bit2->buffer = ri.Malloc(pitch*height); + bit2->num_grays = 256; + + Com_Memset( bit2->buffer, 0, size ); + + FT_Outline_Translate( &glyph->outline, -left, -bottom ); + + FT_Outline_Get_Bitmap( ftLibrary, &glyph->outline, bit2 ); + + glyphOut->height = height; + glyphOut->pitch = pitch; + glyphOut->top = (glyph->metrics.horiBearingY >> 6) + 1; + glyphOut->bottom = bottom; + + return bit2; + } else { + ri.Printf(PRINT_ALL, "Non-outline fonts are not supported\n"); + } + return NULL; +} + +void WriteTGA (char *filename, byte *data, int width, int height) { + byte *buffer; + int i, c; + int row; + unsigned char *flip; + unsigned char *src, *dst; + + buffer = ri.Malloc(width*height*4 + 18); + Com_Memset (buffer, 0, 18); + buffer[2] = 2; // uncompressed type + buffer[12] = width&255; + buffer[13] = width>>8; + buffer[14] = height&255; + buffer[15] = height>>8; + buffer[16] = 32; // pixel size + + // swap rgb to bgr + c = 18 + width * height * 4; + for (i=18 ; iglyph, &glyph); + if (bitmap) { + glyph.xSkip = (face->glyph->metrics.horiAdvance >> 6) + 1; + } else { + return &glyph; + } + + if (glyph.height > *maxHeight) { + *maxHeight = glyph.height; + } + + if (calcHeight) { + ri.Free(bitmap->buffer); + ri.Free(bitmap); + return &glyph; + } + +/* + // need to convert to power of 2 sizes so we do not get + // any scaling from the gl upload + for (scaled_width = 1 ; scaled_width < glyph.pitch ; scaled_width<<=1) + ; + for (scaled_height = 1 ; scaled_height < glyph.height ; scaled_height<<=1) + ; +*/ + + scaled_width = glyph.pitch; + scaled_height = glyph.height; + + // we need to make sure we fit + if (*xOut + scaled_width + 1 >= 255) { + *xOut = 0; + *yOut += *maxHeight + 1; + } + + if (*yOut + *maxHeight + 1 >= 255) { + *yOut = -1; + *xOut = -1; + ri.Free(bitmap->buffer); + ri.Free(bitmap); + return &glyph; + } + + + src = bitmap->buffer; + dst = imageOut + (*yOut * 256) + *xOut; + + if (bitmap->pixel_mode == ft_pixel_mode_mono) { + for (i = 0; i < glyph.height; i++) { + int j; + unsigned char *_src = src; + unsigned char *_dst = dst; + unsigned char mask = 0x80; + unsigned char val = *_src; + for (j = 0; j < glyph.pitch; j++) { + if (mask == 0x80) { + val = *_src++; + } + if (val & mask) { + *_dst = 0xff; + } + mask >>= 1; + + if ( mask == 0 ) { + mask = 0x80; + } + _dst++; + } + + src += glyph.pitch; + dst += 256; + } + } else { + for (i = 0; i < glyph.height; i++) { + Com_Memcpy(dst, src, glyph.pitch); + src += glyph.pitch; + dst += 256; + } + } + + // we now have an 8 bit per pixel grey scale bitmap + // that is width wide and pf->ftSize->metrics.y_ppem tall + + glyph.imageHeight = scaled_height; + glyph.imageWidth = scaled_width; + glyph.s = (float)*xOut / 256; + glyph.t = (float)*yOut / 256; + glyph.s2 = glyph.s + (float)scaled_width / 256; + glyph.t2 = glyph.t + (float)scaled_height / 256; + + *xOut += scaled_width + 1; + + ri.Free(bitmap->buffer); + ri.Free(bitmap); + } + + return &glyph; +} +#endif + +static int fdOffset; +static byte *fdFile; + +int readInt( void ) { + int i = fdFile[fdOffset]+(fdFile[fdOffset+1]<<8)+(fdFile[fdOffset+2]<<16)+(fdFile[fdOffset+3]<<24); + fdOffset += 4; + return i; +} + +typedef union { + byte fred[4]; + float ffred; +} poor; + +float readFloat( void ) { + poor me; +#if defined Q3_BIG_ENDIAN + me.fred[0] = fdFile[fdOffset+3]; + me.fred[1] = fdFile[fdOffset+2]; + me.fred[2] = fdFile[fdOffset+1]; + me.fred[3] = fdFile[fdOffset+0]; +#elif defined Q3_LITTLE_ENDIAN + me.fred[0] = fdFile[fdOffset+0]; + me.fred[1] = fdFile[fdOffset+1]; + me.fred[2] = fdFile[fdOffset+2]; + me.fred[3] = fdFile[fdOffset+3]; +#endif + fdOffset += 4; + return me.ffred; +} + +void RE_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font) { +#ifdef BUILD_FREETYPE + FT_Face face; + int j, k, xOut, yOut, lastStart, imageNumber; + int scaledSize, newSize, maxHeight, left; + unsigned char *out, *imageBuff; + glyphInfo_t *glyph; + image_t *image; + qhandle_t h; + float max; + float dpi = 72; + float glyphScale; +#endif + void *faceData; + int i, len; + char name[1024]; + + if (!fontName) { + ri.Printf(PRINT_ALL, "RE_RegisterFont: called with empty name\n"); + return; + } + + if (pointSize <= 0) { + pointSize = 12; + } + + R_IssuePendingRenderCommands(); + + if (registeredFontCount >= MAX_FONTS) { + ri.Printf(PRINT_WARNING, "RE_RegisterFont: Too many fonts registered already.\n"); + return; + } + + Com_sprintf(name, sizeof(name), "fonts/fontImage_%i.dat",pointSize); + for (i = 0; i < registeredFontCount; i++) { + if (Q_stricmp(name, registeredFont[i].name) == 0) { + Com_Memcpy(font, ®isteredFont[i], sizeof(fontInfo_t)); + return; + } + } + + len = ri.FS_ReadFile(name, NULL); + if (len == sizeof(fontInfo_t)) { + ri.FS_ReadFile(name, &faceData); + fdOffset = 0; + fdFile = faceData; + for(i=0; iglyphs[i].height = readInt(); + font->glyphs[i].top = readInt(); + font->glyphs[i].bottom = readInt(); + font->glyphs[i].pitch = readInt(); + font->glyphs[i].xSkip = readInt(); + font->glyphs[i].imageWidth = readInt(); + font->glyphs[i].imageHeight = readInt(); + font->glyphs[i].s = readFloat(); + font->glyphs[i].t = readFloat(); + font->glyphs[i].s2 = readFloat(); + font->glyphs[i].t2 = readFloat(); + font->glyphs[i].glyph = readInt(); + Q_strncpyz(font->glyphs[i].shaderName, (const char *)&fdFile[fdOffset], sizeof(font->glyphs[i].shaderName)); + fdOffset += sizeof(font->glyphs[i].shaderName); + } + font->glyphScale = readFloat(); + Com_Memcpy(font->name, &fdFile[fdOffset], MAX_QPATH); + +// Com_Memcpy(font, faceData, sizeof(fontInfo_t)); + Q_strncpyz(font->name, name, sizeof(font->name)); + for (i = GLYPH_START; i < GLYPH_END; i++) { + font->glyphs[i].glyph = RE_RegisterShaderNoMip(font->glyphs[i].shaderName); + } + Com_Memcpy(®isteredFont[registeredFontCount++], font, sizeof(fontInfo_t)); + return; + } + +#ifndef BUILD_FREETYPE + ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType code not available\n"); +#else + if (ftLibrary == NULL) { + ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType not initialized.\n"); + return; + } + + len = ri.FS_ReadFile(fontName, &faceData); + if (len <= 0) { + ri.Printf(PRINT_WARNING, "RE_RegisterFont: Unable to read font file '%s'\n", fontName); + return; + } + + // allocate on the stack first in case we fail + if (FT_New_Memory_Face( ftLibrary, faceData, len, 0, &face )) { + ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType, unable to allocate new face.\n"); + return; + } + + + if (FT_Set_Char_Size( face, pointSize << 6, pointSize << 6, dpi, dpi)) { + ri.Printf(PRINT_WARNING, "RE_RegisterFont: FreeType, unable to set face char size.\n"); + return; + } + + //*font = ®isteredFonts[registeredFontCount++]; + + // make a 256x256 image buffer, once it is full, register it, clean it and keep going + // until all glyphs are rendered + + out = ri.Malloc(1024*1024); + if (out == NULL) { + ri.Printf(PRINT_WARNING, "RE_RegisterFont: ri.Malloc failure during output image creation.\n"); + return; + } + Com_Memset(out, 0, 1024*1024); + + maxHeight = 0; + + for (i = GLYPH_START; i < GLYPH_END; i++) { + RE_ConstructGlyphInfo(out, &xOut, &yOut, &maxHeight, face, (unsigned char)i, qtrue); + } + + xOut = 0; + yOut = 0; + i = GLYPH_START; + lastStart = i; + imageNumber = 0; + + while ( i <= GLYPH_END ) { + + glyph = RE_ConstructGlyphInfo(out, &xOut, &yOut, &maxHeight, face, (unsigned char)i, qfalse); + + if (xOut == -1 || yOut == -1 || i == GLYPH_END) { + // ran out of room + // we need to create an image from the bitmap, set all the handles in the glyphs to this point + // + + scaledSize = 256*256; + newSize = scaledSize * 4; + imageBuff = ri.Malloc(newSize); + left = 0; + max = 0; + for ( k = 0; k < (scaledSize) ; k++ ) { + if (max < out[k]) { + max = out[k]; + } + } + + if (max > 0) { + max = 255/max; + } + + for ( k = 0; k < (scaledSize) ; k++ ) { + imageBuff[left++] = 255; + imageBuff[left++] = 255; + imageBuff[left++] = 255; + + imageBuff[left++] = ((float)out[k] * max); + } + + Com_sprintf (name, sizeof(name), "fonts/fontImage_%i_%i.tga", imageNumber++, pointSize); + if (r_saveFontData->integer) { + WriteTGA(name, imageBuff, 256, 256); + } + + //Com_sprintf (name, sizeof(name), "fonts/fontImage_%i_%i", imageNumber++, pointSize); + image = R_CreateImage(name, imageBuff, 256, 256, IMGTYPE_COLORALPHA, IMGFLAG_CLAMPTOEDGE, 0 ); + h = RE_RegisterShaderFromImage(name, LIGHTMAP_2D, image, qfalse); + for (j = lastStart; j < i; j++) { + font->glyphs[j].glyph = h; + Q_strncpyz(font->glyphs[j].shaderName, name, sizeof(font->glyphs[j].shaderName)); + } + lastStart = i; + Com_Memset(out, 0, 1024*1024); + xOut = 0; + yOut = 0; + ri.Free(imageBuff); + i++; + } else { + Com_Memcpy(&font->glyphs[i], glyph, sizeof(glyphInfo_t)); + i++; + } + } + + // change the scale to be relative to 1 based on 72 dpi ( so dpi of 144 means a scale of .5 ) + glyphScale = 72.0f / dpi; + + // we also need to adjust the scale based on point size relative to 48 points as the ui scaling is based on a 48 point font + glyphScale *= 48.0f / pointSize; + + registeredFont[registeredFontCount].glyphScale = glyphScale; + font->glyphScale = glyphScale; + Com_Memcpy(®isteredFont[registeredFontCount++], font, sizeof(fontInfo_t)); + + if (r_saveFontData->integer) { + ri.FS_WriteFile(va("fonts/fontImage_%i.dat", pointSize), font, sizeof(fontInfo_t)); + } + + ri.Free(out); + + ri.FS_FreeFile(faceData); +#endif +} + + + +void R_InitFreeType(void) { +#ifdef BUILD_FREETYPE + if (FT_Init_FreeType( &ftLibrary )) { + ri.Printf(PRINT_WARNING, "R_InitFreeType: Unable to initialize FreeType.\n"); + } +#endif + registeredFontCount = 0; +} + + +void R_DoneFreeType(void) { +#ifdef BUILD_FREETYPE + if (ftLibrary) { + FT_Done_FreeType( ftLibrary ); + ftLibrary = NULL; + } +#endif + registeredFontCount = 0; +} + diff --git a/src/renderergl2/tr_glsl.c b/src/renderergl2/tr_glsl.c new file mode 100644 index 00000000..ccfea909 --- /dev/null +++ b/src/renderergl2/tr_glsl.c @@ -0,0 +1,1932 @@ +/* +=========================================================================== +Copyright (C) 2006-2009 Robert Beckebans + +This file is part of XreaL source code. + +XreaL source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +XreaL source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_glsl.c +#include "tr_local.h" + +void GLSL_BindNullProgram(void); + +extern const char *fallbackShader_bokeh_vp; +extern const char *fallbackShader_bokeh_fp; +extern const char *fallbackShader_calclevels4x_vp; +extern const char *fallbackShader_calclevels4x_fp; +extern const char *fallbackShader_depthblur_vp; +extern const char *fallbackShader_depthblur_fp; +extern const char *fallbackShader_dlight_vp; +extern const char *fallbackShader_dlight_fp; +extern const char *fallbackShader_down4x_vp; +extern const char *fallbackShader_down4x_fp; +extern const char *fallbackShader_fogpass_vp; +extern const char *fallbackShader_fogpass_fp; +extern const char *fallbackShader_generic_vp; +extern const char *fallbackShader_generic_fp; +extern const char *fallbackShader_lightall_vp; +extern const char *fallbackShader_lightall_fp; +extern const char *fallbackShader_pshadow_vp; +extern const char *fallbackShader_pshadow_fp; +extern const char *fallbackShader_shadowfill_vp; +extern const char *fallbackShader_shadowfill_fp; +extern const char *fallbackShader_shadowmask_vp; +extern const char *fallbackShader_shadowmask_fp; +extern const char *fallbackShader_ssao_vp; +extern const char *fallbackShader_ssao_fp; +extern const char *fallbackShader_texturecolor_vp; +extern const char *fallbackShader_texturecolor_fp; +extern const char *fallbackShader_tonemap_vp; +extern const char *fallbackShader_tonemap_fp; + +static void GLSL_PrintInfoLog(GLhandleARB object, qboolean developerOnly) +{ + char *msg; + static char msgPart[1024]; + int maxLength = 0; + int i; + int printLevel = developerOnly ? PRINT_DEVELOPER : PRINT_ALL; + + qglGetObjectParameterivARB(object, GL_OBJECT_INFO_LOG_LENGTH_ARB, &maxLength); + + if (maxLength <= 0) + { + ri.Printf(printLevel, "No compile log.\n"); + return; + } + + ri.Printf(printLevel, "compile log:\n"); + + if (maxLength < 1023) + { + qglGetInfoLogARB(object, maxLength, &maxLength, msgPart); + + msgPart[maxLength + 1] = '\0'; + + ri.Printf(printLevel, "%s\n", msgPart); + } + else + { + msg = ri.Malloc(maxLength); + + qglGetInfoLogARB(object, maxLength, &maxLength, msg); + + for(i = 0; i < maxLength; i += 1024) + { + Q_strncpyz(msgPart, msg + i, sizeof(msgPart)); + + ri.Printf(printLevel, "%s\n", msgPart); + } + + ri.Free(msg); + } +} + +static void GLSL_PrintShaderSource(GLhandleARB object) +{ + char *msg; + static char msgPart[1024]; + int maxLength = 0; + int i; + + qglGetObjectParameterivARB(object, GL_OBJECT_SHADER_SOURCE_LENGTH_ARB, &maxLength); + + msg = ri.Malloc(maxLength); + + qglGetShaderSourceARB(object, maxLength, &maxLength, msg); + + for(i = 0; i < maxLength; i += 1024) + { + Q_strncpyz(msgPart, msg + i, sizeof(msgPart)); + ri.Printf(PRINT_ALL, "%s\n", msgPart); + } + + ri.Free(msg); +} + +static void GLSL_GetShaderHeader( GLenum shaderType, const GLcharARB *extra, char *dest, int size ) +{ + float fbufWidthScale, fbufHeightScale; + + dest[0] = '\0'; + + // HACK: abuse the GLSL preprocessor to turn GLSL 1.20 shaders into 1.30 ones + if(glRefConfig.glslMajorVersion > 1 || (glRefConfig.glslMajorVersion == 1 && glRefConfig.glslMinorVersion >= 30)) + { + Q_strcat(dest, size, "#version 130\n"); + + if(shaderType == GL_VERTEX_SHADER_ARB) + { + Q_strcat(dest, size, "#define attribute in\n"); + Q_strcat(dest, size, "#define varying out\n"); + } + else + { + Q_strcat(dest, size, "#define varying in\n"); + + Q_strcat(dest, size, "out vec4 out_Color;\n"); + Q_strcat(dest, size, "#define gl_FragColor out_Color\n"); + } + } + else + { + Q_strcat(dest, size, "#version 120\n"); + } + + // HACK: add some macros to avoid extra uniforms and save speed and code maintenance + //Q_strcat(dest, size, + // va("#ifndef r_SpecularExponent\n#define r_SpecularExponent %f\n#endif\n", r_specularExponent->value)); + //Q_strcat(dest, size, + // va("#ifndef r_SpecularScale\n#define r_SpecularScale %f\n#endif\n", r_specularScale->value)); + //Q_strcat(dest, size, + // va("#ifndef r_NormalScale\n#define r_NormalScale %f\n#endif\n", r_normalScale->value)); + + + Q_strcat(dest, size, "#ifndef M_PI\n#define M_PI 3.14159265358979323846f\n#endif\n"); + + //Q_strcat(dest, size, va("#ifndef MAX_SHADOWMAPS\n#define MAX_SHADOWMAPS %i\n#endif\n", MAX_SHADOWMAPS)); + + Q_strcat(dest, size, + va("#ifndef deformGen_t\n" + "#define deformGen_t\n" + "#define DGEN_WAVE_SIN %i\n" + "#define DGEN_WAVE_SQUARE %i\n" + "#define DGEN_WAVE_TRIANGLE %i\n" + "#define DGEN_WAVE_SAWTOOTH %i\n" + "#define DGEN_WAVE_INVERSE_SAWTOOTH %i\n" + "#define DGEN_BULGE %i\n" + "#define DGEN_MOVE %i\n" + "#endif\n", + DGEN_WAVE_SIN, + DGEN_WAVE_SQUARE, + DGEN_WAVE_TRIANGLE, + DGEN_WAVE_SAWTOOTH, + DGEN_WAVE_INVERSE_SAWTOOTH, + DGEN_BULGE, + DGEN_MOVE)); + + Q_strcat(dest, size, + va("#ifndef tcGen_t\n" + "#define tcGen_t\n" + "#define TCGEN_LIGHTMAP %i\n" + "#define TCGEN_TEXTURE %i\n" + "#define TCGEN_ENVIRONMENT_MAPPED %i\n" + "#define TCGEN_FOG %i\n" + "#define TCGEN_VECTOR %i\n" + "#endif\n", + TCGEN_LIGHTMAP, + TCGEN_TEXTURE, + TCGEN_ENVIRONMENT_MAPPED, + TCGEN_FOG, + TCGEN_VECTOR)); + + Q_strcat(dest, size, + va("#ifndef colorGen_t\n" + "#define colorGen_t\n" + "#define CGEN_LIGHTING_DIFFUSE %i\n" + "#endif\n", + CGEN_LIGHTING_DIFFUSE)); + + Q_strcat(dest, size, + va("#ifndef alphaGen_t\n" + "#define alphaGen_t\n" + "#define AGEN_LIGHTING_SPECULAR %i\n" + "#define AGEN_PORTAL %i\n" + "#define AGEN_FRESNEL %i\n" + "#endif\n", + AGEN_LIGHTING_SPECULAR, + AGEN_PORTAL, + AGEN_FRESNEL)); + + Q_strcat(dest, size, + va("#ifndef texenv_t\n" + "#define texenv_t\n" + "#define TEXENV_MODULATE %i\n" + "#define TEXENV_ADD %i\n" + "#define TEXENV_REPLACE %i\n" + "#endif\n", + GL_MODULATE, + GL_ADD, + GL_REPLACE)); + + fbufWidthScale = 1.0f / ((float)glConfig.vidWidth); + fbufHeightScale = 1.0f / ((float)glConfig.vidHeight); + Q_strcat(dest, size, + va("#ifndef r_FBufScale\n#define r_FBufScale vec2(%f, %f)\n#endif\n", fbufWidthScale, fbufHeightScale)); + + if (extra) + { + Q_strcat(dest, size, extra); + } + + // OK we added a lot of stuff but if we do something bad in the GLSL shaders then we want the proper line + // so we have to reset the line counting + Q_strcat(dest, size, "#line 0\n"); +} + +static int GLSL_CompileGPUShader(GLhandleARB program, GLhandleARB *prevShader, const GLcharARB *buffer, int size, GLenum shaderType) +{ + GLint compiled; + GLhandleARB shader; + + shader = qglCreateShaderObjectARB(shaderType); + + qglShaderSourceARB(shader, 1, (const GLcharARB **)&buffer, &size); + + // compile shader + qglCompileShaderARB(shader); + + // check if shader compiled + qglGetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &compiled); + if(!compiled) + { + GLSL_PrintShaderSource(shader); + GLSL_PrintInfoLog(shader, qfalse); + ri.Error(ERR_DROP, "Couldn't compile shader"); + return 0; + } + + //GLSL_PrintInfoLog(shader, qtrue); + //GLSL_PrintShaderSource(shader); + + if (*prevShader) + { + qglDetachObjectARB(program, *prevShader); + qglDeleteObjectARB(*prevShader); + } + + // attach shader to program + qglAttachObjectARB(program, shader); + + *prevShader = shader; + + return 1; +} + +static int GLSL_LoadGPUShaderText(const char *name, const char *fallback, + GLenum shaderType, char *dest, int destSize) +{ + char filename[MAX_QPATH]; + GLcharARB *buffer = NULL; + const GLcharARB *shaderText = NULL; + int size; + int result; + + if(shaderType == GL_VERTEX_SHADER_ARB) + { + Com_sprintf(filename, sizeof(filename), "glsl/%s_vp.glsl", name); + } + else + { + Com_sprintf(filename, sizeof(filename), "glsl/%s_fp.glsl", name); + } + + ri.Printf(PRINT_DEVELOPER, "...loading '%s'\n", filename); + size = ri.FS_ReadFile(filename, (void **)&buffer); + if(!buffer) + { + if (fallback) + { + ri.Printf(PRINT_DEVELOPER, "couldn't load, using fallback\n"); + shaderText = fallback; + size = strlen(shaderText); + } + else + { + ri.Printf(PRINT_DEVELOPER, "couldn't load!\n"); + return 0; + } + } + else + { + shaderText = buffer; + } + + if (size > destSize) + { + result = 0; + } + else + { + Q_strncpyz(dest, shaderText, size + 1); + result = 1; + } + + if (buffer) + { + ri.FS_FreeFile(buffer); + } + + return result; +} + +static void GLSL_LinkProgram(GLhandleARB program) +{ + GLint linked; + + qglLinkProgramARB(program); + + qglGetObjectParameterivARB(program, GL_OBJECT_LINK_STATUS_ARB, &linked); + if(!linked) + { + GLSL_PrintInfoLog(program, qfalse); + ri.Error(ERR_DROP, "\nshaders failed to link"); + } +} + +static void GLSL_ValidateProgram(GLhandleARB program) +{ + GLint validated; + + qglValidateProgramARB(program); + + qglGetObjectParameterivARB(program, GL_OBJECT_VALIDATE_STATUS_ARB, &validated); + if(!validated) + { + GLSL_PrintInfoLog(program, qfalse); + ri.Error(ERR_DROP, "\nshaders failed to validate"); + } +} + +static void GLSL_ShowProgramUniforms(GLhandleARB program) +{ + int i, count, size; + GLenum type; + char uniformName[1000]; + + // install the executables in the program object as part of current state. + qglUseProgramObjectARB(program); + + // check for GL Errors + + // query the number of active uniforms + qglGetObjectParameterivARB(program, GL_OBJECT_ACTIVE_UNIFORMS_ARB, &count); + + // Loop over each of the active uniforms, and set their value + for(i = 0; i < count; i++) + { + qglGetActiveUniformARB(program, i, sizeof(uniformName), NULL, &size, &type, uniformName); + + ri.Printf(PRINT_DEVELOPER, "active uniform: '%s'\n", uniformName); + } + + qglUseProgramObjectARB(0); +} + +static int GLSL_InitGPUShader2(shaderProgram_t * program, const char *name, int attribs, const char *vpCode, const char *fpCode, int numUniforms) +{ + ri.Printf(PRINT_DEVELOPER, "------- GPU shader -------\n"); + + if(strlen(name) >= MAX_QPATH) + { + ri.Error(ERR_DROP, "GLSL_InitGPUShader2: \"%s\" is too long", name); + } + + Q_strncpyz(program->name, name, sizeof(program->name)); + + program->program = qglCreateProgramObjectARB(); + program->attribs = attribs; + + if (!(GLSL_CompileGPUShader(program->program, &program->vertexShader, vpCode, strlen(vpCode), GL_VERTEX_SHADER_ARB))) + { + ri.Printf(PRINT_ALL, "GLSL_InitGPUShader2: Unable to load \"%s\" as GL_VERTEX_SHADER_ARB\n", name); + qglDeleteObjectARB(program->program); + return 0; + } + + if(fpCode) + { + if(!(GLSL_CompileGPUShader(program->program, &program->fragmentShader, fpCode, strlen(fpCode), GL_FRAGMENT_SHADER_ARB))) + { + ri.Printf(PRINT_ALL, "GLSL_InitGPUShader2: Unable to load \"%s\" as GL_FRAGMENT_SHADER_ARB\n", name); + qglDeleteObjectARB(program->program); + return 0; + } + } + + if(attribs & ATTR_POSITION) + qglBindAttribLocationARB(program->program, ATTR_INDEX_POSITION, "attr_Position"); + + if(attribs & ATTR_TEXCOORD) + qglBindAttribLocationARB(program->program, ATTR_INDEX_TEXCOORD0, "attr_TexCoord0"); + + if(attribs & ATTR_LIGHTCOORD) + qglBindAttribLocationARB(program->program, ATTR_INDEX_TEXCOORD1, "attr_TexCoord1"); + +// if(attribs & ATTR_TEXCOORD2) +// qglBindAttribLocationARB(program->program, ATTR_INDEX_TEXCOORD2, "attr_TexCoord2"); + +// if(attribs & ATTR_TEXCOORD3) +// qglBindAttribLocationARB(program->program, ATTR_INDEX_TEXCOORD3, "attr_TexCoord3"); + +#ifdef USE_VERT_TANGENT_SPACE + if(attribs & ATTR_TANGENT) + qglBindAttribLocationARB(program->program, ATTR_INDEX_TANGENT, "attr_Tangent"); + + if(attribs & ATTR_BITANGENT) + qglBindAttribLocationARB(program->program, ATTR_INDEX_BITANGENT, "attr_Bitangent"); +#endif + + if(attribs & ATTR_NORMAL) + qglBindAttribLocationARB(program->program, ATTR_INDEX_NORMAL, "attr_Normal"); + + if(attribs & ATTR_COLOR) + qglBindAttribLocationARB(program->program, ATTR_INDEX_COLOR, "attr_Color"); + + if(attribs & ATTR_PAINTCOLOR) + qglBindAttribLocationARB(program->program, ATTR_INDEX_PAINTCOLOR, "attr_PaintColor"); + + if(attribs & ATTR_LIGHTDIRECTION) + qglBindAttribLocationARB(program->program, ATTR_INDEX_LIGHTDIRECTION, "attr_LightDirection"); + + if(attribs & ATTR_POSITION2) + qglBindAttribLocationARB(program->program, ATTR_INDEX_POSITION2, "attr_Position2"); + + if(attribs & ATTR_NORMAL2) + qglBindAttribLocationARB(program->program, ATTR_INDEX_NORMAL2, "attr_Normal2"); + +#ifdef USE_VERT_TANGENT_SPACE + if(attribs & ATTR_TANGENT2) + qglBindAttribLocationARB(program->program, ATTR_INDEX_TANGENT2, "attr_Tangent2"); + + if(attribs & ATTR_BITANGENT2) + qglBindAttribLocationARB(program->program, ATTR_INDEX_BITANGENT2, "attr_Bitangent2"); +#endif + + GLSL_LinkProgram(program->program); + + program->numUniforms = numUniforms; + + { + int i, size; + + size = sizeof(*program->uniforms) * numUniforms; + program->uniforms = ri.Malloc(size); + for (i = 0; i < numUniforms; i++) + { + program->uniforms[i] = -1; + } + + size = sizeof(*program->uniformTypes) * numUniforms; + program->uniformTypes = ri.Malloc(size); + memset(program->uniformTypes, 0, size); + + size = sizeof(*program->uniformBufferOffsets) * numUniforms; + program->uniformBufferOffsets = ri.Malloc(size); + memset(program->uniformBufferOffsets, 0, size); + } + + return 1; +} + +static int GLSL_InitGPUShader(shaderProgram_t * program, const char *name, + int attribs, qboolean fragmentShader, const GLcharARB *extra, qboolean addHeader, + const char *fallback_vp, const char *fallback_fp, int numUniforms) +{ + char vpCode[32000]; + char fpCode[32000]; + char *postHeader; + int size; + int result; + + size = sizeof(vpCode); + if (addHeader) + { + GLSL_GetShaderHeader(GL_VERTEX_SHADER_ARB, extra, vpCode, size); + postHeader = &vpCode[strlen(vpCode)]; + size -= strlen(vpCode); + } + else + { + postHeader = &vpCode[0]; + } + + if (!GLSL_LoadGPUShaderText(name, fallback_vp, GL_VERTEX_SHADER_ARB, postHeader, size)) + { + return 0; + } + + if (fragmentShader) + { + size = sizeof(fpCode); + if (addHeader) + { + GLSL_GetShaderHeader(GL_FRAGMENT_SHADER_ARB, extra, fpCode, size); + postHeader = &fpCode[strlen(fpCode)]; + size -= strlen(fpCode); + } + else + { + postHeader = &fpCode[0]; + } + + if (!GLSL_LoadGPUShaderText(name, fallback_fp, GL_FRAGMENT_SHADER_ARB, postHeader, size)) + { + return 0; + } + } + + result = GLSL_InitGPUShader2(program, name, attribs, vpCode, fragmentShader ? fpCode : NULL, numUniforms); + + return result; +} + +// intentionally deceiving the user here, not actually setting the names but getting their indexes. +void GLSL_AddUniform(shaderProgram_t *program, int uniformNum, const char *name, int type) +{ + GLint *uniforms = program->uniforms; + + uniforms[uniformNum] = qglGetUniformLocationARB(program->program, name); + program->uniformTypes[uniformNum] = type; +} + +void GLSL_EndUniforms(shaderProgram_t *program) +{ + if (program->numUniforms) + { + int i, size; + + size = 0; + for (i = 0; i < program->numUniforms; i++) + { + if (program->uniforms[i] != -1) + { + program->uniformBufferOffsets[i] = size; + + switch(program->uniformTypes[i]) + { + case GLSL_INT: + size += sizeof(GLint); + break; + case GLSL_FLOAT: + size += sizeof(GLfloat); + break; + case GLSL_FLOAT5: + size += sizeof(vec_t) * 5; + break; + case GLSL_VEC2: + size += sizeof(vec_t) * 2; + break; + case GLSL_VEC3: + size += sizeof(vec_t) * 3; + break; + case GLSL_VEC4: + size += sizeof(vec_t) * 4; + break; + case GLSL_MAT16: + size += sizeof(vec_t) * 16; + break; + default: + break; + } + } + } + + program->uniformBuffer = ri.Malloc(size); + + } +} + +void GLSL_FinishGPUShader(shaderProgram_t *program) +{ + GLSL_ValidateProgram(program->program); + GLSL_ShowProgramUniforms(program->program); + GL_CheckErrors(); +} + +void GLSL_SetUniformInt(shaderProgram_t *program, int uniformNum, GLint value) +{ + GLint *uniforms = program->uniforms; + GLint *compare = (GLint *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); + + if (uniforms[uniformNum] == -1) + return; + + if (program->uniformTypes[uniformNum] != GLSL_INT) + { + ri.Printf( PRINT_WARNING, "GLSL_SetUniformInt: wrong type for uniform %i in program %s\n", uniformNum, program->name); + return; + } + + if (value == *compare) + { + return; + } + + *compare = value; + + qglUniform1iARB(uniforms[uniformNum], value); +} + +void GLSL_SetUniformFloat(shaderProgram_t *program, int uniformNum, GLfloat value) +{ + GLint *uniforms = program->uniforms; + GLfloat *compare = (GLfloat *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); + + if (uniforms[uniformNum] == -1) + return; + + if (program->uniformTypes[uniformNum] != GLSL_FLOAT) + { + ri.Printf( PRINT_WARNING, "GLSL_SetUniformFloat: wrong type for uniform %i in program %s\n", uniformNum, program->name); + return; + } + + if (value == *compare) + { + return; + } + + *compare = value; + + qglUniform1fARB(uniforms[uniformNum], value); +} + +void GLSL_SetUniformVec2(shaderProgram_t *program, int uniformNum, const vec2_t v) +{ + GLint *uniforms = program->uniforms; + vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); + + if (uniforms[uniformNum] == -1) + return; + + if (program->uniformTypes[uniformNum] != GLSL_VEC2) + { + ri.Printf( PRINT_WARNING, "GLSL_SetUniformVec2: wrong type for uniform %i in program %s\n", uniformNum, program->name); + return; + } + + if (v[0] == compare[0] && v[1] == compare[1]) + { + return; + } + + compare[0] = v[0]; + compare[1] = v[1]; + + qglUniform2fARB(uniforms[uniformNum], v[0], v[1]); +} + +void GLSL_SetUniformVec3(shaderProgram_t *program, int uniformNum, const vec3_t v) +{ + GLint *uniforms = program->uniforms; + vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); + + if (uniforms[uniformNum] == -1) + return; + + if (program->uniformTypes[uniformNum] != GLSL_VEC3) + { + ri.Printf( PRINT_WARNING, "GLSL_SetUniformVec3: wrong type for uniform %i in program %s\n", uniformNum, program->name); + return; + } + + if (VectorCompare(v, compare)) + { + return; + } + + VectorCopy(v, compare); + + qglUniform3fARB(uniforms[uniformNum], v[0], v[1], v[2]); +} + +void GLSL_SetUniformVec4(shaderProgram_t *program, int uniformNum, const vec4_t v) +{ + GLint *uniforms = program->uniforms; + vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); + + if (uniforms[uniformNum] == -1) + return; + + if (program->uniformTypes[uniformNum] != GLSL_VEC4) + { + ri.Printf( PRINT_WARNING, "GLSL_SetUniformVec4: wrong type for uniform %i in program %s\n", uniformNum, program->name); + return; + } + + if (VectorCompare4(v, compare)) + { + return; + } + + VectorCopy4(v, compare); + + qglUniform4fARB(uniforms[uniformNum], v[0], v[1], v[2], v[3]); +} + +void GLSL_SetUniformFloat5(shaderProgram_t *program, int uniformNum, const vec5_t v) +{ + GLint *uniforms = program->uniforms; + vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); + + if (uniforms[uniformNum] == -1) + return; + + if (program->uniformTypes[uniformNum] != GLSL_FLOAT5) + { + ri.Printf( PRINT_WARNING, "GLSL_SetUniformFloat5: wrong type for uniform %i in program %s\n", uniformNum, program->name); + return; + } + + if (VectorCompare5(v, compare)) + { + return; + } + + VectorCopy5(v, compare); + + qglUniform1fvARB(uniforms[uniformNum], 5, v); +} + +void GLSL_SetUniformMatrix16(shaderProgram_t *program, int uniformNum, const matrix_t matrix) +{ + GLint *uniforms = program->uniforms; + vec_t *compare = (float *)(program->uniformBuffer + program->uniformBufferOffsets[uniformNum]); + + if (uniforms[uniformNum] == -1) + return; + + if (program->uniformTypes[uniformNum] != GLSL_MAT16) + { + ri.Printf( PRINT_WARNING, "GLSL_SetUniformMatrix16: wrong type for uniform %i in program %s\n", uniformNum, program->name); + return; + } + + if (Matrix16Compare(matrix, compare)) + { + return; + } + + Matrix16Copy(matrix, compare); + + qglUniformMatrix4fvARB(uniforms[uniformNum], 1, GL_FALSE, matrix); +} + +void GLSL_DeleteGPUShader(shaderProgram_t *program) +{ + if(program->program) + { + if (program->vertexShader) + { + qglDetachObjectARB(program->program, program->vertexShader); + qglDeleteObjectARB(program->vertexShader); + } + + if (program->fragmentShader) + { + qglDetachObjectARB(program->program, program->fragmentShader); + qglDeleteObjectARB(program->fragmentShader); + } + + qglDeleteObjectARB(program->program); + + if (program->uniforms) + { + ri.Free(program->uniforms); + } + + if (program->uniformTypes) + { + ri.Free(program->uniformTypes); + } + + if (program->uniformBuffer) + { + ri.Free(program->uniformBuffer); + } + + if (program->uniformBufferOffsets) + { + ri.Free(program->uniformBufferOffsets); + } + + Com_Memset(program, 0, sizeof(*program)); + } +} + +void GLSL_InitGPUShaders(void) +{ + int startTime, endTime; + int i; + char extradefines[1024]; + int attribs; + int numGenShaders = 0, numLightShaders = 0, numEtcShaders = 0; + + ri.Printf(PRINT_ALL, "------- GLSL_InitGPUShaders -------\n"); + + R_IssuePendingRenderCommands(); + + startTime = ri.Milliseconds(); + + for (i = 0; i < GENERICDEF_COUNT; i++) + { + attribs = ATTR_POSITION | ATTR_TEXCOORD | ATTR_LIGHTCOORD | ATTR_NORMAL | ATTR_COLOR; + extradefines[0] = '\0'; + + if (i & GENERICDEF_USE_DEFORM_VERTEXES) + Q_strcat(extradefines, 1024, "#define USE_DEFORM_VERTEXES\n"); + + if (i & GENERICDEF_USE_TCGEN_AND_TCMOD) + { + Q_strcat(extradefines, 1024, "#define USE_TCGEN\n"); + Q_strcat(extradefines, 1024, "#define USE_TCMOD\n"); + } + + if (i & GENERICDEF_USE_VERTEX_ANIMATION) + { + Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n"); + attribs |= ATTR_POSITION2 | ATTR_NORMAL2; + } + + if (i & GENERICDEF_USE_FOG) + Q_strcat(extradefines, 1024, "#define USE_FOG\n"); + + if (i & GENERICDEF_USE_RGBAGEN) + Q_strcat(extradefines, 1024, "#define USE_RGBAGEN\n"); + + if (i & GENERICDEF_USE_LIGHTMAP) + Q_strcat(extradefines, 1024, "#define USE_LIGHTMAP\n"); + + if (r_hdr->integer && !(glRefConfig.textureFloat && glRefConfig.halfFloatPixel)) + Q_strcat(extradefines, 1024, "#define RGBE_LIGHTMAP\n"); + + if (!GLSL_InitGPUShader(&tr.genericShader[i], "generic", attribs, qtrue, extradefines, qtrue, fallbackShader_generic_vp, fallbackShader_generic_fp, GENERIC_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load generic shader!"); + } + + // There's actually no need to filter these out, since they'll + // redirect to -1 if nonexistent, but it's more understandable this way. + + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); + + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_BASECOLOR, "u_BaseColor", GLSL_VEC4); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_VERTCOLOR, "u_VertColor", GLSL_VEC4); + + if (i & GENERICDEF_USE_RGBAGEN) + { + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_COLORGEN, "u_ColorGen", GLSL_INT); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_ALPHAGEN, "u_AlphaGen", GLSL_INT); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_AMBIENTLIGHT, "u_AmbientLight", GLSL_VEC3); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_DIRECTEDLIGHT, "u_DirectedLight", GLSL_VEC3); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_LIGHTORIGIN, "u_LightOrigin", GLSL_VEC4); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_PORTALRANGE, "u_PortalRange", GLSL_FLOAT); + } + + if (i & GENERICDEF_USE_TCGEN_AND_TCMOD) + { + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_TCGEN0, "u_TCGen0", GLSL_INT); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_TCGEN0VECTOR0, "u_TCGen0Vector0", GLSL_VEC3); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_TCGEN0VECTOR1, "u_TCGen0Vector1", GLSL_VEC3); + } + + if (i & GENERICDEF_USE_FOG) + { + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_FOGCOLORMASK, "u_FogColorMask", GLSL_VEC4); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_FOGDISTANCE, "u_FogDistance", GLSL_VEC4); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_FOGDEPTH, "u_FogDepth", GLSL_VEC4); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_FOGEYET, "u_FogEyeT", GLSL_FLOAT); + } + + if (i & GENERICDEF_USE_DEFORM_VERTEXES) + { + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_DEFORMGEN, "u_DeformGen", GLSL_INT); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_DEFORMPARAMS, "u_DeformParams", GLSL_FLOAT5); + } + + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_TIME, "u_Time", GLSL_FLOAT); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_VIEWORIGIN, "u_ViewOrigin", GLSL_VEC3); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_DIFFUSETEXMATRIX, "u_DiffuseTexMatrix", GLSL_VEC4); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_DIFFUSETEXOFFTURB,"u_DiffuseTexOffTurb",GLSL_VEC4); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_TEXTURE1ENV, "u_Texture1Env", GLSL_INT); + + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_DIFFUSEMAP, "u_DiffuseMap", GLSL_INT); + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_LIGHTMAP, "u_LightMap", GLSL_INT); + + + if (i & GENERICDEF_USE_VERTEX_ANIMATION) + { + GLSL_AddUniform(&tr.genericShader[i], GENERIC_UNIFORM_VERTEXLERP, "u_VertexLerp", GLSL_FLOAT); + } + + GLSL_EndUniforms(&tr.genericShader[i]); + + qglUseProgramObjectARB(tr.genericShader[i].program); + GLSL_SetUniformInt(&tr.genericShader[i], GENERIC_UNIFORM_DIFFUSEMAP, TB_DIFFUSEMAP); + GLSL_SetUniformInt(&tr.genericShader[i], GENERIC_UNIFORM_LIGHTMAP, TB_LIGHTMAP); + qglUseProgramObjectARB(0); + + GLSL_FinishGPUShader(&tr.genericShader[i]); + + numGenShaders++; + } + + + attribs = ATTR_POSITION | ATTR_TEXCOORD; + + if (!GLSL_InitGPUShader(&tr.textureColorShader, "texturecolor", attribs, qtrue, NULL, qfalse, fallbackShader_texturecolor_vp, fallbackShader_texturecolor_fp, TEXTURECOLOR_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load texturecolor shader!"); + } + + GLSL_AddUniform(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_COLOR, "u_Color", GLSL_VEC4); + GLSL_AddUniform(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_TEXTUREMAP, "u_DiffuseMap", GLSL_INT); + + GLSL_EndUniforms(&tr.textureColorShader); + + qglUseProgramObjectARB(tr.textureColorShader.program); + GLSL_SetUniformInt(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_TEXTUREMAP, TB_DIFFUSEMAP); + qglUseProgramObjectARB(0); + + GLSL_FinishGPUShader(&tr.textureColorShader); + + numEtcShaders++; + + for (i = 0; i < FOGDEF_COUNT; i++) + { + attribs = ATTR_POSITION | ATTR_POSITION2 | ATTR_NORMAL | ATTR_NORMAL2 | ATTR_TEXCOORD; + extradefines[0] = '\0'; + + if (i & FOGDEF_USE_DEFORM_VERTEXES) + Q_strcat(extradefines, 1024, "#define USE_DEFORM_VERTEXES\n"); + + if (i & FOGDEF_USE_VERTEX_ANIMATION) + Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n"); + + if (!GLSL_InitGPUShader(&tr.fogShader[i], "fogpass", attribs, qtrue, extradefines, qtrue, fallbackShader_fogpass_vp, fallbackShader_fogpass_fp, FOGPASS_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load fogpass shader!"); + } + + GLSL_AddUniform(&tr.fogShader[i], FOGPASS_UNIFORM_FOGDISTANCE, "u_FogDistance", GLSL_VEC4); + GLSL_AddUniform(&tr.fogShader[i], FOGPASS_UNIFORM_FOGDEPTH, "u_FogDepth", GLSL_VEC4); + GLSL_AddUniform(&tr.fogShader[i], FOGPASS_UNIFORM_FOGEYET, "u_FogEyeT", GLSL_FLOAT); + GLSL_AddUniform(&tr.fogShader[i], FOGPASS_UNIFORM_DEFORMGEN, "u_DeformGen", GLSL_INT); + GLSL_AddUniform(&tr.fogShader[i], FOGPASS_UNIFORM_DEFORMPARAMS, "u_DeformParams", GLSL_FLOAT5); + GLSL_AddUniform(&tr.fogShader[i], FOGPASS_UNIFORM_TIME, "u_Time", GLSL_FLOAT); + GLSL_AddUniform(&tr.fogShader[i], FOGPASS_UNIFORM_COLOR, "u_Color", GLSL_VEC4); + GLSL_AddUniform(&tr.fogShader[i], FOGPASS_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.fogShader[i], FOGPASS_UNIFORM_VERTEXLERP, "u_VertexLerp", GLSL_FLOAT); + + GLSL_EndUniforms(&tr.fogShader[i]); + GLSL_FinishGPUShader(&tr.fogShader[i]); + + numEtcShaders++; + } + + + for (i = 0; i < DLIGHTDEF_COUNT; i++) + { + attribs = ATTR_POSITION | ATTR_NORMAL | ATTR_TEXCOORD; + extradefines[0] = '\0'; + + if (i & DLIGHTDEF_USE_DEFORM_VERTEXES) + { + Q_strcat(extradefines, 1024, "#define USE_DEFORM_VERTEXES\n"); + } + + if (!GLSL_InitGPUShader(&tr.dlightShader[i], "dlight", attribs, qtrue, extradefines, qtrue, fallbackShader_dlight_vp, fallbackShader_dlight_fp, DLIGHT_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load dlight shader!"); + } + + GLSL_AddUniform(&tr.dlightShader[i], DLIGHT_UNIFORM_DLIGHTINFO, "u_DlightInfo", GLSL_VEC4); + GLSL_AddUniform(&tr.dlightShader[i], DLIGHT_UNIFORM_DEFORMGEN, "u_DeformGen", GLSL_INT); + GLSL_AddUniform(&tr.dlightShader[i], DLIGHT_UNIFORM_DEFORMPARAMS, "u_DeformParams", GLSL_FLOAT5); + GLSL_AddUniform(&tr.dlightShader[i], DLIGHT_UNIFORM_TIME, "u_Time", GLSL_FLOAT); + GLSL_AddUniform(&tr.dlightShader[i], DLIGHT_UNIFORM_COLOR, "u_Color", GLSL_VEC4); + GLSL_AddUniform(&tr.dlightShader[i], DLIGHT_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); + + GLSL_EndUniforms(&tr.dlightShader[i]); + + qglUseProgramObjectARB(tr.dlightShader[i].program); + GLSL_SetUniformInt(&tr.dlightShader[i], DLIGHT_UNIFORM_DIFFUSEMAP, TB_DIFFUSEMAP); + qglUseProgramObjectARB(0); + + GLSL_FinishGPUShader(&tr.dlightShader[i]); + + numEtcShaders++; + } + + + for (i = 0; i < LIGHTDEF_COUNT; i++) + { + // skip impossible combos + if ((i & LIGHTDEF_USE_NORMALMAP) && !r_normalMapping->integer) + continue; + + if ((i & LIGHTDEF_USE_PARALLAXMAP) && !r_parallaxMapping->integer) + continue; + + if ((i & LIGHTDEF_USE_SPECULARMAP) && !r_specularMapping->integer) + continue; + + if ((i & LIGHTDEF_USE_DELUXEMAP) && !r_deluxeMapping->integer) + continue; + + if (!((i & LIGHTDEF_LIGHTTYPE_MASK) == LIGHTDEF_USE_LIGHTMAP) && (i & LIGHTDEF_USE_DELUXEMAP)) + continue; + + if (!(i & LIGHTDEF_USE_NORMALMAP) && (i & LIGHTDEF_USE_PARALLAXMAP)) + continue; + + if (!((i & LIGHTDEF_LIGHTTYPE_MASK) == LIGHTDEF_USE_LIGHT_VECTOR)) + { + if (i & LIGHTDEF_USE_SHADOWMAP) + continue; + } + + attribs = ATTR_POSITION | ATTR_TEXCOORD | ATTR_COLOR | ATTR_NORMAL; + + extradefines[0] = '\0'; + + if (r_normalAmbient->value > 0.003f) + Q_strcat(extradefines, 1024, va("#define r_normalAmbient %f\n", r_normalAmbient->value)); + + if (r_dlightMode->integer >= 2) + Q_strcat(extradefines, 1024, "#define USE_SHADOWMAP\n"); + + if (1) + { + Q_strcat(extradefines, 1024, "#define SWIZZLE_NORMALMAP\n"); + } + + if (r_hdr->integer && !(glRefConfig.textureFloat && glRefConfig.halfFloatPixel)) + Q_strcat(extradefines, 1024, "#define RGBE_LIGHTMAP\n"); + + if (i & LIGHTDEF_LIGHTTYPE_MASK) + { + Q_strcat(extradefines, 1024, "#define USE_LIGHT\n"); + + if (r_normalMapping->integer == 0 && r_specularMapping->integer == 0) + Q_strcat(extradefines, 1024, "#define USE_FAST_LIGHT\n"); + + switch (i & LIGHTDEF_LIGHTTYPE_MASK) + { + case LIGHTDEF_USE_LIGHTMAP: + Q_strcat(extradefines, 1024, "#define USE_LIGHTMAP\n"); + attribs |= ATTR_LIGHTCOORD | ATTR_LIGHTDIRECTION; + break; + case LIGHTDEF_USE_LIGHT_VECTOR: + Q_strcat(extradefines, 1024, "#define USE_LIGHT_VECTOR\n"); + break; + case LIGHTDEF_USE_LIGHT_VERTEX: + Q_strcat(extradefines, 1024, "#define USE_LIGHT_VERTEX\n"); + attribs |= ATTR_LIGHTDIRECTION; + break; + default: + break; + } + } + + if ((i & LIGHTDEF_USE_NORMALMAP) && r_normalMapping->integer) + { + Q_strcat(extradefines, 1024, "#define USE_NORMALMAP\n"); + + if (r_normalMapping->integer == 2) + Q_strcat(extradefines, 1024, "#define USE_OREN_NAYAR\n"); + + if (r_normalMapping->integer == 3) + Q_strcat(extradefines, 1024, "#define USE_TRIACE_OREN_NAYAR\n"); + +#ifdef USE_VERT_TANGENT_SPACE + Q_strcat(extradefines, 1024, "#define USE_VERT_TANGENT_SPACE\n"); + attribs |= ATTR_TANGENT | ATTR_BITANGENT; +#endif + } + + if ((i & LIGHTDEF_USE_SPECULARMAP) && r_specularMapping->integer) + { + Q_strcat(extradefines, 1024, "#define USE_SPECULARMAP\n"); + + switch (r_specularMapping->integer) + { + case 1: + default: + Q_strcat(extradefines, 1024, "#define USE_TRIACE\n"); + break; + + case 2: + Q_strcat(extradefines, 1024, "#define USE_BLINN\n"); + break; + + case 3: + Q_strcat(extradefines, 1024, "#define USE_COOK_TORRANCE\n"); + break; + + case 4: + Q_strcat(extradefines, 1024, "#define USE_TORRANCE_SPARROW\n"); + break; + } + } + + if ((i & LIGHTDEF_USE_DELUXEMAP) && r_deluxeMapping->integer) + Q_strcat(extradefines, 1024, "#define USE_DELUXEMAP\n"); + + if ((i & LIGHTDEF_USE_PARALLAXMAP) && !(i & LIGHTDEF_ENTITY) && r_parallaxMapping->integer) + Q_strcat(extradefines, 1024, "#define USE_PARALLAXMAP\n"); + + if (i & LIGHTDEF_USE_SHADOWMAP) + Q_strcat(extradefines, 1024, "#define USE_SHADOWMAP\n"); + + if (i & LIGHTDEF_USE_TCGEN_AND_TCMOD) + { + Q_strcat(extradefines, 1024, "#define USE_TCGEN\n"); + Q_strcat(extradefines, 1024, "#define USE_TCMOD\n"); + } + + if (i & LIGHTDEF_ENTITY) + { + Q_strcat(extradefines, 1024, "#define USE_VERTEX_ANIMATION\n#define USE_MODELMATRIX\n"); + attribs |= ATTR_POSITION2 | ATTR_NORMAL2; + +#ifdef USE_VERT_TANGENT_SPACE + if (i & LIGHTDEF_USE_NORMALMAP && r_normalMapping->integer) + { + attribs |= ATTR_TANGENT2 | ATTR_BITANGENT2; + } +#endif + } + + if (!GLSL_InitGPUShader(&tr.lightallShader[i], "lightall", attribs, qtrue, extradefines, qtrue, fallbackShader_lightall_vp, fallbackShader_lightall_fp, GENERIC_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load lightall shader!"); + } + + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_MODELMATRIX, "u_ModelMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_DIFFUSETEXMATRIX, "u_DiffuseTexMatrix", GLSL_VEC4); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_DIFFUSETEXOFFTURB, "u_DiffuseTexOffTurb", GLSL_VEC4); + //GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_NORMALTEXMATRIX, "u_NormalTexMatrix", GLSL_MAT16); + //GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_SPECULARTEXMATRIX, "u_SpecularTexMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_VIEWORIGIN, "u_ViewOrigin", GLSL_VEC3); + + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_TCGEN0, "u_TCGen0", GLSL_INT); + + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_DIFFUSEMAP, "u_DiffuseMap", GLSL_INT); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_LIGHTMAP, "u_LightMap", GLSL_INT); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_NORMALMAP, "u_NormalMap", GLSL_INT); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_DELUXEMAP, "u_DeluxeMap", GLSL_INT); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_SPECULARMAP, "u_SpecularMap", GLSL_INT); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_SHADOWMAP, "u_ShadowMap", GLSL_INT); + + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_AMBIENTLIGHT, "u_AmbientLight", GLSL_VEC3); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_DIRECTEDLIGHT, "u_DirectedLight", GLSL_VEC3); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_LIGHTORIGIN, "u_LightOrigin", GLSL_VEC4); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_LIGHTRADIUS, "u_LightRadius", GLSL_FLOAT); + + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_MATERIALINFO, "u_MaterialInfo", GLSL_VEC2); + + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_BASECOLOR, "u_BaseColor", GLSL_VEC4); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_VERTCOLOR, "u_VertColor", GLSL_VEC4); + GLSL_AddUniform(&tr.lightallShader[i], GENERIC_UNIFORM_VERTEXLERP, "u_VertexLerp", GLSL_FLOAT); + + GLSL_EndUniforms(&tr.lightallShader[i]); + + qglUseProgramObjectARB(tr.lightallShader[i].program); + GLSL_SetUniformInt(&tr.lightallShader[i], GENERIC_UNIFORM_DIFFUSEMAP, TB_DIFFUSEMAP); + GLSL_SetUniformInt(&tr.lightallShader[i], GENERIC_UNIFORM_LIGHTMAP, TB_LIGHTMAP); + GLSL_SetUniformInt(&tr.lightallShader[i], GENERIC_UNIFORM_NORMALMAP, TB_NORMALMAP); + GLSL_SetUniformInt(&tr.lightallShader[i], GENERIC_UNIFORM_DELUXEMAP, TB_DELUXEMAP); + GLSL_SetUniformInt(&tr.lightallShader[i], GENERIC_UNIFORM_SPECULARMAP, TB_SPECULARMAP); + GLSL_SetUniformInt(&tr.lightallShader[i], GENERIC_UNIFORM_SHADOWMAP, TB_SHADOWMAP); + qglUseProgramObjectARB(0); + + GLSL_FinishGPUShader(&tr.lightallShader[i]); + + numLightShaders++; + } + + attribs = ATTR_POSITION | ATTR_POSITION2 | ATTR_NORMAL | ATTR_NORMAL2 | ATTR_TEXCOORD; + + extradefines[0] = '\0'; + + if (!GLSL_InitGPUShader(&tr.shadowmapShader, "shadowfill", attribs, qtrue, extradefines, qtrue, fallbackShader_shadowfill_vp, fallbackShader_shadowfill_fp, GENERIC_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load shadowfill shader!"); + } + + GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_DEFORMGEN, "u_DeformGen", GLSL_INT); + GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_DEFORMPARAMS, "u_DeformParams", GLSL_FLOAT5); + GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_TIME, "u_Time", GLSL_FLOAT); + GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_MODELMATRIX, "u_ModelMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_VERTEXLERP, "u_VertexLerp", GLSL_FLOAT); + + GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_LIGHTORIGIN, "u_LightOrigin", GLSL_VEC4); + GLSL_AddUniform(&tr.shadowmapShader, GENERIC_UNIFORM_LIGHTRADIUS, "u_LightRadius", GLSL_FLOAT); + + GLSL_EndUniforms(&tr.shadowmapShader); + GLSL_FinishGPUShader(&tr.shadowmapShader); + + numEtcShaders++; + + attribs = ATTR_POSITION | ATTR_NORMAL; + extradefines[0] = '\0'; + + Q_strcat(extradefines, 1024, "#define USE_PCF\n#define USE_DISCARD\n"); + + if (!GLSL_InitGPUShader(&tr.pshadowShader, "pshadow", attribs, qtrue, extradefines, qtrue, fallbackShader_pshadow_vp, fallbackShader_pshadow_fp, PSHADOW_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load pshadow shader!"); + } + + GLSL_AddUniform(&tr.pshadowShader, PSHADOW_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.pshadowShader, PSHADOW_UNIFORM_LIGHTFORWARD, "u_LightForward", GLSL_VEC3); + GLSL_AddUniform(&tr.pshadowShader, PSHADOW_UNIFORM_LIGHTUP, "u_LightUp", GLSL_VEC3); + GLSL_AddUniform(&tr.pshadowShader, PSHADOW_UNIFORM_LIGHTRIGHT, "u_LightRight", GLSL_VEC3); + GLSL_AddUniform(&tr.pshadowShader, PSHADOW_UNIFORM_LIGHTORIGIN, "u_LightOrigin", GLSL_VEC4); + GLSL_AddUniform(&tr.pshadowShader, PSHADOW_UNIFORM_LIGHTRADIUS, "u_LightRadius", GLSL_FLOAT); + + GLSL_EndUniforms(&tr.pshadowShader); + + qglUseProgramObjectARB(tr.pshadowShader.program); + GLSL_SetUniformInt(&tr.pshadowShader, PSHADOW_UNIFORM_SHADOWMAP, TB_DIFFUSEMAP); + qglUseProgramObjectARB(0); + + GLSL_FinishGPUShader(&tr.pshadowShader); + + numEtcShaders++; + + + attribs = ATTR_POSITION | ATTR_TEXCOORD; + extradefines[0] = '\0'; + + if (!GLSL_InitGPUShader(&tr.down4xShader, "down4x", attribs, qtrue, extradefines, qtrue, fallbackShader_down4x_vp, fallbackShader_down4x_fp, TEXTURECOLOR_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load down4x shader!"); + } + + GLSL_AddUniform(&tr.down4xShader, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.down4xShader, TEXTURECOLOR_UNIFORM_INVTEXRES, "u_InvTexRes", GLSL_VEC2); + + GLSL_AddUniform(&tr.down4xShader, TEXTURECOLOR_UNIFORM_TEXTUREMAP, "u_TextureMap", GLSL_INT); + + GLSL_EndUniforms(&tr.down4xShader); + + qglUseProgramObjectARB(tr.down4xShader.program); + GLSL_SetUniformInt(&tr.down4xShader, TEXTURECOLOR_UNIFORM_TEXTUREMAP, TB_DIFFUSEMAP); + qglUseProgramObjectARB(0); + + GLSL_FinishGPUShader(&tr.down4xShader); + + numEtcShaders++; + + + attribs = ATTR_POSITION | ATTR_TEXCOORD; + extradefines[0] = '\0'; + + if (!GLSL_InitGPUShader(&tr.bokehShader, "bokeh", attribs, qtrue, extradefines, qtrue, fallbackShader_bokeh_vp, fallbackShader_bokeh_fp, TEXTURECOLOR_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load bokeh shader!"); + } + + GLSL_AddUniform(&tr.bokehShader, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.bokehShader, TEXTURECOLOR_UNIFORM_INVTEXRES, "u_InvTexRes", GLSL_VEC2); + GLSL_AddUniform(&tr.bokehShader, TEXTURECOLOR_UNIFORM_COLOR, "u_Color", GLSL_VEC4); + + GLSL_AddUniform(&tr.bokehShader, TEXTURECOLOR_UNIFORM_TEXTUREMAP, "u_TextureMap", GLSL_INT); + + GLSL_EndUniforms(&tr.bokehShader); + + qglUseProgramObjectARB(tr.bokehShader.program); + GLSL_SetUniformInt(&tr.bokehShader, TEXTURECOLOR_UNIFORM_TEXTUREMAP, TB_DIFFUSEMAP); + qglUseProgramObjectARB(0); + + GLSL_FinishGPUShader(&tr.bokehShader); + + numEtcShaders++; + + + attribs = ATTR_POSITION | ATTR_TEXCOORD; + extradefines[0] = '\0'; + + if (!GLSL_InitGPUShader(&tr.tonemapShader, "tonemap", attribs, qtrue, extradefines, qtrue, fallbackShader_tonemap_vp, fallbackShader_tonemap_fp, TEXTURECOLOR_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load tonemap shader!"); + } + + GLSL_AddUniform(&tr.tonemapShader, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.tonemapShader, TEXTURECOLOR_UNIFORM_INVTEXRES, "u_InvTexRes", GLSL_VEC2); + GLSL_AddUniform(&tr.tonemapShader, TEXTURECOLOR_UNIFORM_COLOR, "u_Color", GLSL_VEC4); + GLSL_AddUniform(&tr.tonemapShader, TEXTURECOLOR_UNIFORM_AUTOEXPOSUREMINMAX, "u_AutoExposureMinMax", GLSL_VEC2); + GLSL_AddUniform(&tr.tonemapShader, TEXTURECOLOR_UNIFORM_TONEMINAVGMAXLINEAR, "u_ToneMinAvgMaxLinear", GLSL_VEC3); + GLSL_AddUniform(&tr.tonemapShader, TEXTURECOLOR_UNIFORM_TEXTUREMAP, "u_TextureMap", GLSL_INT); + GLSL_AddUniform(&tr.tonemapShader, TEXTURECOLOR_UNIFORM_LEVELSMAP, "u_LevelsMap", GLSL_INT); + + GLSL_EndUniforms(&tr.tonemapShader); + + qglUseProgramObjectARB(tr.tonemapShader.program); + GLSL_SetUniformInt(&tr.tonemapShader, TEXTURECOLOR_UNIFORM_TEXTUREMAP, TB_COLORMAP); + GLSL_SetUniformInt(&tr.tonemapShader, TEXTURECOLOR_UNIFORM_LEVELSMAP, TB_LEVELSMAP); + qglUseProgramObjectARB(0); + + GLSL_FinishGPUShader(&tr.tonemapShader); + + numEtcShaders++; + + + for (i = 0; i < 2; i++) + { + attribs = ATTR_POSITION | ATTR_TEXCOORD; + extradefines[0] = '\0'; + + if (!i) + Q_strcat(extradefines, 1024, "#define FIRST_PASS\n"); + + if (!GLSL_InitGPUShader(&tr.calclevels4xShader[i], "calclevels4x", attribs, qtrue, extradefines, qtrue, fallbackShader_calclevels4x_vp, fallbackShader_calclevels4x_fp, TEXTURECOLOR_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load calclevels4x shader!"); + } + + GLSL_AddUniform(&tr.calclevels4xShader[i], TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, "u_ModelViewProjectionMatrix", GLSL_MAT16); + GLSL_AddUniform(&tr.calclevels4xShader[i], TEXTURECOLOR_UNIFORM_INVTEXRES, "u_InvTexRes", GLSL_VEC2); + GLSL_AddUniform(&tr.calclevels4xShader[i], TEXTURECOLOR_UNIFORM_COLOR, "u_Color", GLSL_VEC4); + + GLSL_AddUniform(&tr.calclevels4xShader[i], TEXTURECOLOR_UNIFORM_TEXTUREMAP, "u_TextureMap", GLSL_INT); + + GLSL_EndUniforms(&tr.calclevels4xShader[i]); + + qglUseProgramObjectARB(tr.calclevels4xShader[i].program); + GLSL_SetUniformInt(&tr.calclevels4xShader[i], TEXTURECOLOR_UNIFORM_TEXTUREMAP, TB_DIFFUSEMAP); + qglUseProgramObjectARB(0); + + GLSL_FinishGPUShader(&tr.calclevels4xShader[i]); + + numEtcShaders++; + } + + + attribs = ATTR_POSITION | ATTR_TEXCOORD; + extradefines[0] = '\0'; + + if (r_shadowFilter->integer >= 1) + Q_strcat(extradefines, 1024, "#define USE_SHADOW_FILTER\n"); + + if (r_shadowFilter->integer >= 2) + Q_strcat(extradefines, 1024, "#define USE_SHADOW_FILTER2\n"); + + Q_strcat(extradefines, 1024, "#define USE_SHADOW_CASCADE\n"); + + Q_strcat(extradefines, 1024, va("#define r_shadowMapSize %d\n", r_shadowMapSize->integer)); + Q_strcat(extradefines, 1024, va("#define r_shadowCascadeZFar %f\n", r_shadowCascadeZFar->value)); + + + if (!GLSL_InitGPUShader(&tr.shadowmaskShader, "shadowmask", attribs, qtrue, extradefines, qtrue, fallbackShader_shadowmask_vp, fallbackShader_shadowmask_fp, SHADOWMASK_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load shadowmask shader!"); + } + + GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMVP, "u_ShadowMvp", GLSL_MAT16); + GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMVP2, "u_ShadowMvp2", GLSL_MAT16); + GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMVP3, "u_ShadowMvp3", GLSL_MAT16); + GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWORIGIN, "u_ViewOrigin", GLSL_VEC3); + GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWINFO, "u_ViewInfo", GLSL_VEC4); + GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWFORWARD,"u_ViewForward", GLSL_VEC3); + GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWLEFT, "u_ViewLeft", GLSL_VEC3); + GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_VIEWUP, "u_ViewUp", GLSL_VEC3); + + GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SCREENDEPTHMAP, "u_ScreenDepthMap", GLSL_INT); + GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMAP, "u_ShadowMap", GLSL_INT); + GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMAP2, "u_ShadowMap2", GLSL_INT); + GLSL_AddUniform(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMAP3, "u_ShadowMap3", GLSL_INT); + + GLSL_EndUniforms(&tr.shadowmaskShader); + + qglUseProgramObjectARB(tr.shadowmaskShader.program); + GLSL_SetUniformInt(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SCREENDEPTHMAP, TB_COLORMAP); + GLSL_SetUniformInt(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMAP, TB_SHADOWMAP); + GLSL_SetUniformInt(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMAP2, TB_SHADOWMAP2); + GLSL_SetUniformInt(&tr.shadowmaskShader, SHADOWMASK_UNIFORM_SHADOWMAP3, TB_SHADOWMAP3); + qglUseProgramObjectARB(0); + + GLSL_FinishGPUShader(&tr.shadowmaskShader); + + numEtcShaders++; + + + attribs = ATTR_POSITION | ATTR_TEXCOORD; + extradefines[0] = '\0'; + + if (!GLSL_InitGPUShader(&tr.ssaoShader, "ssao", attribs, qtrue, extradefines, qtrue, fallbackShader_ssao_vp, fallbackShader_ssao_fp, SSAO_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load ssao shader!"); + } + + GLSL_AddUniform(&tr.ssaoShader, SSAO_UNIFORM_VIEWINFO, "u_ViewInfo", GLSL_VEC4); + + GLSL_AddUniform(&tr.ssaoShader, SSAO_UNIFORM_SCREENDEPTHMAP, "u_ScreenDepthMap", GLSL_INT); + + GLSL_EndUniforms(&tr.ssaoShader); + + qglUseProgramObjectARB(tr.ssaoShader.program); + GLSL_SetUniformInt(&tr.ssaoShader, SSAO_UNIFORM_SCREENDEPTHMAP, TB_COLORMAP); + qglUseProgramObjectARB(0); + + GLSL_FinishGPUShader(&tr.ssaoShader); + + numEtcShaders++; + + + for (i = 0; i < 2; i++) + { + attribs = ATTR_POSITION | ATTR_TEXCOORD; + extradefines[0] = '\0'; + + if (i & 1) + Q_strcat(extradefines, 1024, "#define USE_VERTICAL_BLUR\n"); + else + Q_strcat(extradefines, 1024, "#define USE_HORIZONTAL_BLUR\n"); + + + if (!GLSL_InitGPUShader(&tr.depthBlurShader[i], "depthBlur", attribs, qtrue, extradefines, qtrue, fallbackShader_depthblur_vp, fallbackShader_depthblur_fp, DEPTHBLUR_UNIFORM_COUNT)) + { + ri.Error(ERR_FATAL, "Could not load depthBlur shader!"); + } + + GLSL_AddUniform(&tr.depthBlurShader[i], DEPTHBLUR_UNIFORM_VIEWINFO, "u_ViewInfo", GLSL_VEC4); + + GLSL_AddUniform(&tr.depthBlurShader[i], DEPTHBLUR_UNIFORM_SCREENIMAGEMAP, "u_ScreenImageMap", GLSL_INT); + GLSL_AddUniform(&tr.depthBlurShader[i], DEPTHBLUR_UNIFORM_SCREENDEPTHMAP, "u_ScreenDepthMap", GLSL_INT); + + GLSL_EndUniforms(&tr.depthBlurShader[i]); + + qglUseProgramObjectARB(tr.depthBlurShader[i].program); + GLSL_SetUniformInt(&tr.depthBlurShader[i], DEPTHBLUR_UNIFORM_SCREENIMAGEMAP, TB_COLORMAP); + GLSL_SetUniformInt(&tr.depthBlurShader[i], DEPTHBLUR_UNIFORM_SCREENDEPTHMAP, TB_LIGHTMAP); + qglUseProgramObjectARB(0); + + GLSL_FinishGPUShader(&tr.depthBlurShader[i]); + + numEtcShaders++; + } + + + endTime = ri.Milliseconds(); + + ri.Printf(PRINT_ALL, "loaded %i GLSL shaders (%i gen %i light %i etc) in %5.2f seconds\n", + numGenShaders + numLightShaders + numEtcShaders, numGenShaders, numLightShaders, + numEtcShaders, (endTime - startTime) / 1000.0); +} + +void GLSL_ShutdownGPUShaders(void) +{ + int i; + + ri.Printf(PRINT_ALL, "------- GLSL_ShutdownGPUShaders -------\n"); + + qglDisableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD0); + qglDisableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD1); + qglDisableVertexAttribArrayARB(ATTR_INDEX_POSITION); + qglDisableVertexAttribArrayARB(ATTR_INDEX_POSITION2); + qglDisableVertexAttribArrayARB(ATTR_INDEX_NORMAL); +#ifdef USE_VERT_TANGENT_SPACE + qglDisableVertexAttribArrayARB(ATTR_INDEX_TANGENT); + qglDisableVertexAttribArrayARB(ATTR_INDEX_BITANGENT); +#endif + qglDisableVertexAttribArrayARB(ATTR_INDEX_NORMAL2); +#ifdef USE_VERT_TANGENT_SPACE + qglDisableVertexAttribArrayARB(ATTR_INDEX_TANGENT2); + qglDisableVertexAttribArrayARB(ATTR_INDEX_BITANGENT2); +#endif + qglDisableVertexAttribArrayARB(ATTR_INDEX_COLOR); + qglDisableVertexAttribArrayARB(ATTR_INDEX_LIGHTDIRECTION); + GLSL_BindNullProgram(); + + for ( i = 0; i < GENERICDEF_COUNT; i++) + GLSL_DeleteGPUShader(&tr.genericShader[i]); + + GLSL_DeleteGPUShader(&tr.textureColorShader); + + for ( i = 0; i < FOGDEF_COUNT; i++) + GLSL_DeleteGPUShader(&tr.fogShader[i]); + + for ( i = 0; i < DLIGHTDEF_COUNT; i++) + GLSL_DeleteGPUShader(&tr.dlightShader[i]); + + for ( i = 0; i < LIGHTDEF_COUNT; i++) + GLSL_DeleteGPUShader(&tr.lightallShader[i]); + + GLSL_DeleteGPUShader(&tr.shadowmapShader); + GLSL_DeleteGPUShader(&tr.pshadowShader); + GLSL_DeleteGPUShader(&tr.down4xShader); + + for ( i = 0; i < 2; i++) + GLSL_DeleteGPUShader(&tr.calclevels4xShader[i]); + + glState.currentProgram = 0; + qglUseProgramObjectARB(0); +} + + +void GLSL_BindProgram(shaderProgram_t * program) +{ + if(!program) + { + GLSL_BindNullProgram(); + return; + } + + if(r_logFile->integer) + { + // don't just call LogComment, or we will get a call to va() every frame! + GLimp_LogComment(va("--- GL_BindProgram( %s ) ---\n", program->name)); + } + + if(glState.currentProgram != program) + { + qglUseProgramObjectARB(program->program); + glState.currentProgram = program; + backEnd.pc.c_glslShaderBinds++; + } +} + + +void GLSL_BindNullProgram(void) +{ + if(r_logFile->integer) + { + GLimp_LogComment("--- GL_BindNullProgram ---\n"); + } + + if(glState.currentProgram) + { + qglUseProgramObjectARB(0); + glState.currentProgram = NULL; + } +} + + +void GLSL_VertexAttribsState(uint32_t stateBits) +{ + uint32_t diff; + + GLSL_VertexAttribPointers(stateBits); + + diff = stateBits ^ glState.vertexAttribsState; + if(!diff) + { + return; + } + + if(diff & ATTR_POSITION) + { + if(stateBits & ATTR_POSITION) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_POSITION )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_POSITION); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_POSITION )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_POSITION); + } + } + + if(diff & ATTR_TEXCOORD) + { + if(stateBits & ATTR_TEXCOORD) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_TEXCOORD )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD0); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_TEXCOORD )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD0); + } + } + + if(diff & ATTR_LIGHTCOORD) + { + if(stateBits & ATTR_LIGHTCOORD) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_LIGHTCOORD )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD1); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_LIGHTCOORD )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_TEXCOORD1); + } + } + + if(diff & ATTR_NORMAL) + { + if(stateBits & ATTR_NORMAL) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_NORMAL )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_NORMAL); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_NORMAL )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_NORMAL); + } + } + +#ifdef USE_VERT_TANGENT_SPACE + if(diff & ATTR_TANGENT) + { + if(stateBits & ATTR_TANGENT) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_TANGENT )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_TANGENT); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_TANGENT )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_TANGENT); + } + } + + if(diff & ATTR_BITANGENT) + { + if(stateBits & ATTR_BITANGENT) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_BITANGENT )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_BITANGENT); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_BITANGENT )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_BITANGENT); + } + } +#endif + + if(diff & ATTR_COLOR) + { + if(stateBits & ATTR_COLOR) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_COLOR )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_COLOR); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_COLOR )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_COLOR); + } + } + + if(diff & ATTR_LIGHTDIRECTION) + { + if(stateBits & ATTR_LIGHTDIRECTION) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_LIGHTDIRECTION )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_LIGHTDIRECTION); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_LIGHTDIRECTION )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_LIGHTDIRECTION); + } + } + + if(diff & ATTR_POSITION2) + { + if(stateBits & ATTR_POSITION2) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_POSITION2 )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_POSITION2); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_POSITION2 )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_POSITION2); + } + } + + if(diff & ATTR_NORMAL2) + { + if(stateBits & ATTR_NORMAL2) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_NORMAL2 )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_NORMAL2); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_NORMAL2 )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_NORMAL2); + } + } + +#ifdef USE_VERT_TANGENT_SPACE + if(diff & ATTR_TANGENT2) + { + if(stateBits & ATTR_TANGENT2) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_TANGENT2 )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_TANGENT2); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_TANGENT2 )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_TANGENT2); + } + } + + if(diff & ATTR_BITANGENT2) + { + if(stateBits & ATTR_BITANGENT2) + { + GLimp_LogComment("qglEnableVertexAttribArrayARB( ATTR_INDEX_BITANGENT2 )\n"); + qglEnableVertexAttribArrayARB(ATTR_INDEX_BITANGENT2); + } + else + { + GLimp_LogComment("qglDisableVertexAttribArrayARB( ATTR_INDEX_BITANGENT2 )\n"); + qglDisableVertexAttribArrayARB(ATTR_INDEX_BITANGENT2); + } + } +#endif + + glState.vertexAttribsState = stateBits; +} + +void GLSL_VertexAttribPointers(uint32_t attribBits) +{ + if(!glState.currentVBO) + { + ri.Error(ERR_FATAL, "GL_VertexAttribPointers: no VBO bound"); + return; + } + + // don't just call LogComment, or we will get a call to va() every frame! + GLimp_LogComment(va("--- GL_VertexAttribPointers( %s ) ---\n", glState.currentVBO->name)); + + if((attribBits & ATTR_POSITION) && !(glState.vertexAttribPointersSet & ATTR_POSITION)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_POSITION )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_POSITION, 3, GL_FLOAT, 0, glState.currentVBO->stride_xyz, BUFFER_OFFSET(glState.currentVBO->ofs_xyz + glState.vertexAttribsNewFrame * glState.currentVBO->size_xyz)); + glState.vertexAttribPointersSet |= ATTR_POSITION; + } + + if((attribBits & ATTR_TEXCOORD) && !(glState.vertexAttribPointersSet & ATTR_TEXCOORD)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_TEXCOORD )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_TEXCOORD0, 2, GL_FLOAT, 0, glState.currentVBO->stride_st, BUFFER_OFFSET(glState.currentVBO->ofs_st)); + glState.vertexAttribPointersSet |= ATTR_TEXCOORD; + } + + if((attribBits & ATTR_LIGHTCOORD) && !(glState.vertexAttribPointersSet & ATTR_LIGHTCOORD)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_LIGHTCOORD )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_TEXCOORD1, 2, GL_FLOAT, 0, glState.currentVBO->stride_lightmap, BUFFER_OFFSET(glState.currentVBO->ofs_lightmap)); + glState.vertexAttribPointersSet |= ATTR_LIGHTCOORD; + } + + if((attribBits & ATTR_NORMAL) && !(glState.vertexAttribPointersSet & ATTR_NORMAL)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_NORMAL )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_NORMAL, 3, GL_FLOAT, 0, glState.currentVBO->stride_normal, BUFFER_OFFSET(glState.currentVBO->ofs_normal + glState.vertexAttribsNewFrame * glState.currentVBO->size_normal)); + glState.vertexAttribPointersSet |= ATTR_NORMAL; + } + +#ifdef USE_VERT_TANGENT_SPACE + if((attribBits & ATTR_TANGENT) && !(glState.vertexAttribPointersSet & ATTR_TANGENT)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_TANGENT )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_TANGENT, 3, GL_FLOAT, 0, glState.currentVBO->stride_tangent, BUFFER_OFFSET(glState.currentVBO->ofs_tangent + glState.vertexAttribsNewFrame * glState.currentVBO->size_normal)); // FIXME + glState.vertexAttribPointersSet |= ATTR_TANGENT; + } + + if((attribBits & ATTR_BITANGENT) && !(glState.vertexAttribPointersSet & ATTR_BITANGENT)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_BITANGENT )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_BITANGENT, 3, GL_FLOAT, 0, glState.currentVBO->stride_bitangent, BUFFER_OFFSET(glState.currentVBO->ofs_bitangent + glState.vertexAttribsNewFrame * glState.currentVBO->size_normal)); // FIXME + glState.vertexAttribPointersSet |= ATTR_BITANGENT; + } +#endif + + if((attribBits & ATTR_COLOR) && !(glState.vertexAttribPointersSet & ATTR_COLOR)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_COLOR )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_COLOR, 4, GL_FLOAT, 0, glState.currentVBO->stride_vertexcolor, BUFFER_OFFSET(glState.currentVBO->ofs_vertexcolor)); + glState.vertexAttribPointersSet |= ATTR_COLOR; + } + + if((attribBits & ATTR_LIGHTDIRECTION) && !(glState.vertexAttribPointersSet & ATTR_LIGHTDIRECTION)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_LIGHTDIRECTION )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_LIGHTDIRECTION, 3, GL_FLOAT, 0, glState.currentVBO->stride_lightdir, BUFFER_OFFSET(glState.currentVBO->ofs_lightdir)); + glState.vertexAttribPointersSet |= ATTR_LIGHTDIRECTION; + } + + if((attribBits & ATTR_POSITION2) && !(glState.vertexAttribPointersSet & ATTR_POSITION2)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_POSITION2 )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_POSITION2, 3, GL_FLOAT, 0, glState.currentVBO->stride_xyz, BUFFER_OFFSET(glState.currentVBO->ofs_xyz + glState.vertexAttribsOldFrame * glState.currentVBO->size_xyz)); + glState.vertexAttribPointersSet |= ATTR_POSITION2; + } + + if((attribBits & ATTR_NORMAL2) && !(glState.vertexAttribPointersSet & ATTR_NORMAL2)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_NORMAL2 )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_NORMAL2, 3, GL_FLOAT, 0, glState.currentVBO->stride_normal, BUFFER_OFFSET(glState.currentVBO->ofs_normal + glState.vertexAttribsOldFrame * glState.currentVBO->size_normal)); + glState.vertexAttribPointersSet |= ATTR_NORMAL2; + } + +#ifdef USE_VERT_TANGENT_SPACE + if((attribBits & ATTR_TANGENT2) && !(glState.vertexAttribPointersSet & ATTR_TANGENT2)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_TANGENT2 )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_TANGENT2, 3, GL_FLOAT, 0, glState.currentVBO->stride_tangent, BUFFER_OFFSET(glState.currentVBO->ofs_tangent + glState.vertexAttribsOldFrame * glState.currentVBO->size_normal)); // FIXME + glState.vertexAttribPointersSet |= ATTR_TANGENT2; + } + + if((attribBits & ATTR_BITANGENT2) && !(glState.vertexAttribPointersSet & ATTR_BITANGENT2)) + { + GLimp_LogComment("qglVertexAttribPointerARB( ATTR_INDEX_BITANGENT2 )\n"); + + qglVertexAttribPointerARB(ATTR_INDEX_BITANGENT2, 3, GL_FLOAT, 0, glState.currentVBO->stride_bitangent, BUFFER_OFFSET(glState.currentVBO->ofs_bitangent + glState.vertexAttribsOldFrame * glState.currentVBO->size_normal)); // FIXME + glState.vertexAttribPointersSet |= ATTR_BITANGENT2; + } +#endif + +} + +shaderProgram_t *GLSL_GetGenericShaderProgram(int stage) +{ + shaderStage_t *pStage = tess.xstages[stage]; + int shaderAttribs = 0; + + if (tess.fogNum && pStage->adjustColorsForFog) + { + shaderAttribs |= GENERICDEF_USE_FOG; + } + + if (pStage->bundle[1].image[0] && tess.shader->multitextureEnv) + { + shaderAttribs |= GENERICDEF_USE_LIGHTMAP; + } + + switch (pStage->rgbGen) + { + case CGEN_LIGHTING_DIFFUSE: + shaderAttribs |= GENERICDEF_USE_RGBAGEN; + break; + default: + break; + } + + switch (pStage->alphaGen) + { + case AGEN_LIGHTING_SPECULAR: + case AGEN_PORTAL: + case AGEN_FRESNEL: + shaderAttribs |= GENERICDEF_USE_RGBAGEN; + break; + default: + break; + } + + if (pStage->bundle[0].tcGen != TCGEN_TEXTURE) + { + shaderAttribs |= GENERICDEF_USE_TCGEN_AND_TCMOD; + } + + if (tess.shader->numDeforms && !ShaderRequiresCPUDeforms(tess.shader)) + { + shaderAttribs |= GENERICDEF_USE_DEFORM_VERTEXES; + } + + if (glState.vertexAttribsInterpolation > 0.0f && backEnd.currentEntity && backEnd.currentEntity != &tr.worldEntity) + { + shaderAttribs |= GENERICDEF_USE_VERTEX_ANIMATION; + } + + if (pStage->bundle[0].numTexMods) + { + shaderAttribs |= GENERICDEF_USE_TCGEN_AND_TCMOD; + } + + return &tr.genericShader[shaderAttribs]; +} diff --git a/src/renderergl2/tr_image.c b/src/renderergl2/tr_image.c new file mode 100644 index 00000000..8a1f574f --- /dev/null +++ b/src/renderergl2/tr_image.c @@ -0,0 +1,3462 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_image.c +#include "tr_local.h" + +static byte s_intensitytable[256]; +static unsigned char s_gammatable[256]; + +int gl_filter_min = GL_LINEAR_MIPMAP_NEAREST; +int gl_filter_max = GL_LINEAR; + +#define FILE_HASH_SIZE 1024 +static image_t* hashTable[FILE_HASH_SIZE]; + +/* +** R_GammaCorrect +*/ +void R_GammaCorrect( byte *buffer, int bufSize ) { + int i; + + for ( i = 0; i < bufSize; i++ ) { + buffer[i] = s_gammatable[buffer[i]]; + } +} + +typedef struct { + char *name; + int minimize, maximize; +} textureMode_t; + +textureMode_t modes[] = { + {"GL_NEAREST", GL_NEAREST, GL_NEAREST}, + {"GL_LINEAR", GL_LINEAR, GL_LINEAR}, + {"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST}, + {"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR}, + {"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST}, + {"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR} +}; + +/* +================ +return a hash value for the filename +================ +*/ +static long generateHashValue( const char *fname ) { + int i; + long hash; + char letter; + + hash = 0; + i = 0; + while (fname[i] != '\0') { + letter = tolower(fname[i]); + if (letter =='.') break; // don't include extension + if (letter =='\\') letter = '/'; // damn path names + hash+=(long)(letter)*(i+119); + i++; + } + hash &= (FILE_HASH_SIZE-1); + return hash; +} + +/* +=============== +GL_TextureMode +=============== +*/ +void GL_TextureMode( const char *string ) { + int i; + image_t *glt; + + for ( i=0 ; i< 6 ; i++ ) { + if ( !Q_stricmp( modes[i].name, string ) ) { + break; + } + } + + // hack to prevent trilinear from being set on voodoo, + // because their driver freaks... + if ( i == 5 && glConfig.hardwareType == GLHW_3DFX_2D3D ) { + ri.Printf( PRINT_ALL, "Refusing to set trilinear on a voodoo.\n" ); + i = 3; + } + + + if ( i == 6 ) { + ri.Printf (PRINT_ALL, "bad filter name\n"); + return; + } + + gl_filter_min = modes[i].minimize; + gl_filter_max = modes[i].maximize; + + // change all the existing mipmap texture objects + for ( i = 0 ; i < tr.numImages ; i++ ) { + glt = tr.images[ i ]; + if ( glt->flags & IMGFLAG_MIPMAP ) { + GL_Bind (glt); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + } +} + +/* +=============== +R_SumOfUsedImages +=============== +*/ +int R_SumOfUsedImages( void ) { + int total; + int i; + + total = 0; + for ( i = 0; i < tr.numImages; i++ ) { + if ( tr.images[i]->frameUsed == tr.frameCount ) { + total += tr.images[i]->uploadWidth * tr.images[i]->uploadHeight; + } + } + + return total; +} + +/* +=============== +R_ImageList_f +=============== +*/ +void R_ImageList_f( void ) { +#if 1 + int i; + int estTotalSize = 0; + + ri.Printf(PRINT_ALL, "\n -w-- -h-- type -size- --name-------\n"); + + for ( i = 0 ; i < tr.numImages ; i++ ) + { + image_t *image = tr.images[i]; + char *format = "???? "; + char *sizeSuffix; + int estSize; + int displaySize; + + estSize = image->uploadHeight * image->uploadWidth; + + switch(image->internalFormat) + { + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: + format = "sDXT1"; + // 64 bits per 16 pixels, so 4 bits per pixel + estSize /= 2; + break; + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: + format = "sDXT5"; + // 128 bits per 16 pixels, so 1 byte per pixel + break; + case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB: + format = "sBPTC"; + // 128 bits per 16 pixels, so 1 byte per pixel + break; + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: + format = "LATC "; + // 128 bits per 16 pixels, so 1 byte per pixel + break; + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + format = "DXT1 "; + // 64 bits per 16 pixels, so 4 bits per pixel + estSize /= 2; + break; + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + format = "DXT5 "; + // 128 bits per 16 pixels, so 1 byte per pixel + break; + case GL_COMPRESSED_RGBA_BPTC_UNORM_ARB: + format = "BPTC "; + // 128 bits per 16 pixels, so 1 byte per pixel + break; + case GL_RGB4_S3TC: + format = "S3TC "; + // same as DXT1? + estSize /= 2; + break; + case GL_RGBA4: + case GL_RGBA8: + case GL_RGBA: + format = "RGBA "; + // 4 bytes per pixel + estSize *= 4; + break; + case GL_LUMINANCE8: + case GL_LUMINANCE16: + case GL_LUMINANCE: + format = "L "; + // 1 byte per pixel? + break; + case GL_RGB5: + case GL_RGB8: + case GL_RGB: + format = "RGB "; + // 3 bytes per pixel? + estSize *= 3; + break; + case GL_LUMINANCE8_ALPHA8: + case GL_LUMINANCE16_ALPHA16: + case GL_LUMINANCE_ALPHA: + format = "LA "; + // 2 bytes per pixel? + estSize *= 2; + break; + case GL_SRGB_EXT: + case GL_SRGB8_EXT: + format = "sRGB "; + // 3 bytes per pixel? + estSize *= 3; + break; + case GL_SRGB_ALPHA_EXT: + case GL_SRGB8_ALPHA8_EXT: + format = "sRGBA"; + // 4 bytes per pixel? + estSize *= 4; + break; + case GL_SLUMINANCE_EXT: + case GL_SLUMINANCE8_EXT: + format = "sL "; + // 1 byte per pixel? + break; + case GL_SLUMINANCE_ALPHA_EXT: + case GL_SLUMINANCE8_ALPHA8_EXT: + format = "sLA "; + // 2 byte per pixel? + estSize *= 2; + break; + } + + // mipmap adds about 50% + if (image->flags & IMGFLAG_MIPMAP) + estSize += estSize / 2; + + sizeSuffix = "b "; + displaySize = estSize; + + if (displaySize > 1024) + { + displaySize /= 1024; + sizeSuffix = "kb"; + } + + if (displaySize > 1024) + { + displaySize /= 1024; + sizeSuffix = "Mb"; + } + + if (displaySize > 1024) + { + displaySize /= 1024; + sizeSuffix = "Gb"; + } + + ri.Printf(PRINT_ALL, "%4i: %4ix%4i %s %4i%s %s\n", i, image->uploadWidth, image->uploadHeight, format, displaySize, sizeSuffix, image->imgName); + estTotalSize += estSize; + } + + ri.Printf (PRINT_ALL, " ---------\n"); + ri.Printf (PRINT_ALL, " approx %i bytes\n", estTotalSize); + ri.Printf (PRINT_ALL, " %i total images\n\n", tr.numImages ); +#else + int i; + image_t *image; + int texels; + const char *yesno[] = { + "no ", "yes" + }; + + ri.Printf (PRINT_ALL, "\n -w-- -h-- -mm- -TMU- -if-- wrap --name-------\n"); + texels = 0; + + for ( i = 0 ; i < tr.numImages ; i++ ) { + image = tr.images[ i ]; + + texels += image->uploadWidth*image->uploadHeight; + ri.Printf (PRINT_ALL, "%4i: %4i %4i %s %d ", + i, image->uploadWidth, image->uploadHeight, yesno[(image->flags & IMGFLAG_MIPMAP) ? 1 : 0], image->TMU ); + switch ( image->internalFormat ) { + case 1: + ri.Printf( PRINT_ALL, "I " ); + break; + case 2: + ri.Printf( PRINT_ALL, "IA " ); + break; + case 3: + ri.Printf( PRINT_ALL, "RGB " ); + break; + case 4: + ri.Printf( PRINT_ALL, "RGBA " ); + break; + case GL_RGBA8: + ri.Printf( PRINT_ALL, "RGBA8" ); + break; + case GL_RGB8: + ri.Printf( PRINT_ALL, "RGB8" ); + break; + case GL_RGB4_S3TC: + ri.Printf( PRINT_ALL, "S3TC " ); + break; + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + ri.Printf( PRINT_ALL, "DXT1 " ); + break; + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + ri.Printf( PRINT_ALL, "DXT5 " ); + break; + case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT: + ri.Printf( PRINT_ALL, "LATC " ); + break; + case GL_RGBA4: + ri.Printf( PRINT_ALL, "RGBA4" ); + break; + case GL_RGB5: + ri.Printf( PRINT_ALL, "RGB5 " ); + break; + case GL_SRGB_EXT: + ri.Printf( PRINT_ALL, "sRGB " ); + break; + case GL_SRGB8_EXT: + ri.Printf( PRINT_ALL, "sRGB8" ); + break; + case GL_SRGB_ALPHA_EXT: + case GL_SRGB8_ALPHA8_EXT: + ri.Printf( PRINT_ALL, "sRGBA" ); + break; + /* + case GL_SLUMINANCE_EXT: + break; + case GL_SLUMINANCE8_EXT: + break; + case GL_SLUMINANCE_ALPHA_EXT: + break; + case GL_SLUMINANCE8_ALPHA8_EXT: + break; + */ + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT: + ri.Printf( PRINT_ALL, "sDXT1" ); + break; + case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT: + ri.Printf( PRINT_ALL, "sDXT5" ); + break; + case GL_COMPRESSED_RGBA_BPTC_UNORM_ARB: + ri.Printf( PRINT_ALL, "BPTC " ); + break; + case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB: + ri.Printf( PRINT_ALL, "sBPTC" ); + break; + default: + ri.Printf( PRINT_ALL, "???? " ); + } + + if (image->flags & IMGFLAG_CLAMPTOEDGE) + ri.Printf( PRINT_ALL, "clmp " ); + else + ri.Printf( PRINT_ALL, "rept " ); + + ri.Printf( PRINT_ALL, " %s\n", image->imgName ); + } + ri.Printf (PRINT_ALL, " ---------\n"); + ri.Printf (PRINT_ALL, " %i total texels (not including mipmaps)\n", texels); + ri.Printf (PRINT_ALL, " %i total images\n\n", tr.numImages ); +#endif +} + +//======================================================================= + +/* +================ +ResampleTexture + +Used to resample images in a more general than quartering fashion. + +This will only be filtered properly if the resampled size +is greater than half the original size. + +If a larger shrinking is needed, use the mipmap function +before or after. +================ +*/ +static void ResampleTexture( byte *in, int inwidth, int inheight, byte *out, + int outwidth, int outheight ) { + int i, j; + byte *inrow, *inrow2; + int frac, fracstep; + int p1[2048], p2[2048]; + byte *pix1, *pix2, *pix3, *pix4; + + if (outwidth>2048) + ri.Error(ERR_DROP, "ResampleTexture: max width"); + + fracstep = inwidth*0x10000/outwidth; + + frac = fracstep>>2; + for ( i=0 ; i>16); + frac += fracstep; + } + frac = 3*(fracstep>>2); + for ( i=0 ; i>16); + frac += fracstep; + } + + for (i=0 ; i> 1; + for (j=0 ; j>2; + *out++ = (pix1[1] + pix2[1] + pix3[1] + pix4[1])>>2; + *out++ = (pix1[2] + pix2[2] + pix3[2] + pix4[2])>>2; + *out++ = (pix1[3] + pix2[3] + pix3[3] + pix4[3])>>2; + } + } +} + +static void RGBAtoYCoCgA(const byte *in, byte *out, int width, int height) +{ + int x, y; + + for (y = 0; y < height; y++) + { + const byte *inbyte = in + y * width * 4; + byte *outbyte = out + y * width * 4; + + for (x = 0; x < width; x++) + { + byte r, g, b, a, rb2; + + r = *inbyte++; + g = *inbyte++; + b = *inbyte++; + a = *inbyte++; + rb2 = (r + b) >> 1; + + *outbyte++ = (g + rb2) >> 1; // Y = R/4 + G/2 + B/4 + *outbyte++ = (r - b + 256) >> 1; // Co = R/2 - B/2 + *outbyte++ = (g - rb2 + 256) >> 1; // Cg = -R/4 + G/2 - B/4 + *outbyte++ = a; + } + } +} + +static void YCoCgAtoRGBA(const byte *in, byte *out, int width, int height) +{ + int x, y; + + for (y = 0; y < height; y++) + { + const byte *inbyte = in + y * width * 4; + byte *outbyte = out + y * width * 4; + + for (x = 0; x < width; x++) + { + byte _Y, Co, Cg, a; + + _Y = *inbyte++; + Co = *inbyte++; + Cg = *inbyte++; + a = *inbyte++; + + *outbyte++ = CLAMP(_Y + Co - Cg, 0, 255); // R = Y + Co - Cg + *outbyte++ = CLAMP(_Y + Cg - 128, 0, 255); // G = Y + Cg + *outbyte++ = CLAMP(_Y - Co - Cg + 256, 0, 255); // B = Y - Co - Cg + *outbyte++ = a; + } + } +} + + +// uses a sobel filter to change a texture to a normal map +static void RGBAtoNormal(const byte *in, byte *out, int width, int height, qboolean clampToEdge) +{ + int x, y, max; + + // convert to heightmap, storing in alpha + // same as converting to Y in YCoCg + max = 1; + for (y = 0; y < height; y++) + { + const byte *inbyte = in + y * width * 4; + byte *outbyte = out + y * width * 4 + 3; + + for (x = 0; x < width; x++) + { + *outbyte = (inbyte[0] >> 2) + (inbyte[1] >> 1) + (inbyte[2] >> 2); + max = MAX(max, *outbyte); + outbyte += 4; + inbyte += 4; + } + } + + // level out heights + if (max < 255) + { + for (y = 0; y < height; y++) + { + byte *outbyte = out + y * width * 4 + 3; + + for (x = 0; x < width; x++) + { + *outbyte = *outbyte + (255 - max); + outbyte += 4; + } + } + } + + + // now run sobel filter over height values to generate X and Y + // then normalize + for (y = 0; y < height; y++) + { + byte *outbyte = out + y * width * 4; + + for (x = 0; x < width; x++) + { + // 0 1 2 + // 3 4 5 + // 6 7 8 + + byte s[9]; + int x2, y2, i; + vec3_t normal; + + i = 0; + for (y2 = -1; y2 <= 1; y2++) + { + int src_y = y + y2; + + if (clampToEdge) + { + src_y = CLAMP(src_y, 0, height - 1); + } + else + { + src_y = (src_y + height) % height; + } + + + for (x2 = -1; x2 <= 1; x2++) + { + int src_x = x + x2; + + if (clampToEdge) + { + src_x = CLAMP(src_x, 0, height - 1); + } + else + { + src_x = (src_x + height) % height; + } + + s[i++] = *(out + (src_y * width + src_x) * 4 + 3); + } + } + + normal[0] = s[0] - s[2] + + 2 * s[3] - 2 * s[5] + + s[6] - s[8]; + + normal[1] = s[0] + 2 * s[1] + s[2] + + - s[6] - 2 * s[7] - s[8]; + + normal[2] = s[4] * 4; + + if (!VectorNormalize2(normal, normal)) + { + VectorSet(normal, 0, 0, 1); + } + + *outbyte++ = FloatToOffsetByte(normal[0]); + *outbyte++ = FloatToOffsetByte(normal[1]); + *outbyte++ = FloatToOffsetByte(normal[2]); + outbyte++; + } + } +} + +#define COPYSAMPLE(a,b) *(unsigned int *)(a) = *(unsigned int *)(b) + +// based on Fast Curve Based Interpolation +// from Fast Artifacts-Free Image Interpolation (http://www.andreagiachetti.it/icbi/) +// assumes data has a 2 pixel thick border of clamped or wrapped data +// expects data to be a grid with even (0, 0), (2, 0), (0, 2), (2, 2) etc pixels filled +// only performs FCBI on specified component +static void DoFCBI(byte *in, byte *out, int width, int height, int component) +{ + int x, y; + byte *outbyte, *inbyte; + + // copy in to out + for (y = 2; y < height - 2; y += 2) + { + inbyte = in + (y * width + 2) * 4 + component; + outbyte = out + (y * width + 2) * 4 + component; + + for (x = 2; x < width - 2; x += 2) + { + *outbyte = *inbyte; + outbyte += 8; + inbyte += 8; + } + } + + for (y = 3; y < height - 3; y += 2) + { + // diagonals + // + // NWp - northwest interpolated pixel + // NEp - northeast interpolated pixel + // NWd - northwest first derivative + // NEd - northeast first derivative + // NWdd - northwest second derivative + // NEdd - northeast second derivative + // + // Uses these samples: + // + // 0 + // - - a - b - - + // - - - - - - - + // c - d - e - f + // 0 - - - - - - - + // g - h - i - j + // - - - - - - - + // - - k - l - - + // + // x+2 uses these samples: + // + // 0 + // - - - - a - b - - + // - - - - - - - - - + // - - c - d - e - f + // 0 - - - - - - - - - + // - - g - h - i - j + // - - - - - - - - - + // - - - - k - l - - + // + // so we can reuse 8 of them on next iteration + // + // a=b, c=d, d=e, e=f, g=h, h=i, i=j, k=l + // + // only b, f, j, and l need to be sampled on next iteration + + byte sa, sb, sc, sd, se, sf, sg, sh, si, sj, sk, sl; + byte *line1, *line2, *line3, *line4; + + x = 3; + + // optimization one + // SAMPLE2(sa, x-1, y-3); + //SAMPLE2(sc, x-3, y-1); SAMPLE2(sd, x-1, y-1); SAMPLE2(se, x+1, y-1); + //SAMPLE2(sg, x-3, y+1); SAMPLE2(sh, x-1, y+1); SAMPLE2(si, x+1, y+1); + // SAMPLE2(sk, x-1, y+3); + + // optimization two + line1 = in + ((y - 3) * width + (x - 1)) * 4 + component; + line2 = in + ((y - 1) * width + (x - 3)) * 4 + component; + line3 = in + ((y + 1) * width + (x - 3)) * 4 + component; + line4 = in + ((y + 3) * width + (x - 1)) * 4 + component; + + // COPYSAMPLE(sa, line1); line1 += 8; + //COPYSAMPLE(sc, line2); line2 += 8; COPYSAMPLE(sd, line2); line2 += 8; COPYSAMPLE(se, line2); line2 += 8; + //COPYSAMPLE(sg, line3); line3 += 8; COPYSAMPLE(sh, line3); line3 += 8; COPYSAMPLE(si, line3); line3 += 8; + // COPYSAMPLE(sk, line4); line4 += 8; + + sa = *line1; line1 += 8; + sc = *line2; line2 += 8; sd = *line2; line2 += 8; se = *line2; line2 += 8; + sg = *line3; line3 += 8; sh = *line3; line3 += 8; si = *line3; line3 += 8; + sk = *line4; line4 += 8; + + outbyte = out + (y * width + x) * 4 + component; + + for ( ; x < width - 3; x += 2) + { + int NWd, NEd, NWp, NEp; + + // original + // SAMPLE2(sa, x-1, y-3); SAMPLE2(sb, x+1, y-3); + //SAMPLE2(sc, x-3, y-1); SAMPLE2(sd, x-1, y-1); SAMPLE2(se, x+1, y-1); SAMPLE2(sf, x+3, y-1); + //SAMPLE2(sg, x-3, y+1); SAMPLE2(sh, x-1, y+1); SAMPLE2(si, x+1, y+1); SAMPLE2(sj, x+3, y+1); + // SAMPLE2(sk, x-1, y+3); SAMPLE2(sl, x+1, y+3); + + // optimization one + //SAMPLE2(sb, x+1, y-3); + //SAMPLE2(sf, x+3, y-1); + //SAMPLE2(sj, x+3, y+1); + //SAMPLE2(sl, x+1, y+3); + + // optimization two + //COPYSAMPLE(sb, line1); line1 += 8; + //COPYSAMPLE(sf, line2); line2 += 8; + //COPYSAMPLE(sj, line3); line3 += 8; + //COPYSAMPLE(sl, line4); line4 += 8; + + sb = *line1; line1 += 8; + sf = *line2; line2 += 8; + sj = *line3; line3 += 8; + sl = *line4; line4 += 8; + + NWp = sd + si; + NEp = se + sh; + NWd = abs(sd - si); + NEd = abs(se - sh); + + if (NWd > 100 || NEd > 100 || abs(NWp-NEp) > 200) + { + if (NWd < NEd) + *outbyte = NWp >> 1; + else + *outbyte = NEp >> 1; + } + else + { + int NWdd, NEdd; + + //NEdd = abs(sg + sd + sb - 3 * (se + sh) + sk + si + sf); + //NWdd = abs(sa + se + sj - 3 * (sd + si) + sc + sh + sl); + NEdd = abs(sg + sb - 3 * NEp + sk + sf + NWp); + NWdd = abs(sa + sj - 3 * NWp + sc + sl + NEp); + + if (NWdd > NEdd) + *outbyte = NWp >> 1; + else + *outbyte = NEp >> 1; + } + + outbyte += 8; + + // COPYSAMPLE(sa, sb); + //COPYSAMPLE(sc, sd); COPYSAMPLE(sd, se); COPYSAMPLE(se, sf); + //COPYSAMPLE(sg, sh); COPYSAMPLE(sh, si); COPYSAMPLE(si, sj); + // COPYSAMPLE(sk, sl); + + sa = sb; + sc = sd; sd = se; se = sf; + sg = sh; sh = si; si = sj; + sk = sl; + } + } + + // hack: copy out to in again + for (y = 3; y < height - 3; y += 2) + { + inbyte = out + (y * width + 3) * 4 + component; + outbyte = in + (y * width + 3) * 4 + component; + + for (x = 3; x < width - 3; x += 2) + { + *outbyte = *inbyte; + outbyte += 8; + inbyte += 8; + } + } + + for (y = 2; y < height - 3; y++) + { + // horizontal & vertical + // + // hp - horizontally interpolated pixel + // vp - vertically interpolated pixel + // hd - horizontal first derivative + // vd - vertical first derivative + // hdd - horizontal second derivative + // vdd - vertical second derivative + // Uses these samples: + // + // 0 + // - a - b - + // c - d - e + // 0 - f - g - + // h - i - j + // - k - l - + // + // x+2 uses these samples: + // + // 0 + // - - - a - b - + // - - c - d - e + // 0 - - - f - g - + // - - h - i - j + // - - - k - l - + // + // so we can reuse 7 of them on next iteration + // + // a=b, c=d, d=e, f=g, h=i, i=j, k=l + // + // only b, e, g, j, and l need to be sampled on next iteration + + byte sa, sb, sc, sd, se, sf, sg, sh, si, sj, sk, sl; + byte *line1, *line2, *line3, *line4, *line5; + + //x = (y + 1) % 2; + x = (y + 1) % 2 + 2; + + // optimization one + // SAMPLE2(sa, x-1, y-2); + //SAMPLE2(sc, x-2, y-1); SAMPLE2(sd, x, y-1); + // SAMPLE2(sf, x-1, y ); + //SAMPLE2(sh, x-2, y+1); SAMPLE2(si, x, y+1); + // SAMPLE2(sk, x-1, y+2); + + line1 = in + ((y - 2) * width + (x - 1)) * 4 + component; + line2 = in + ((y - 1) * width + (x - 2)) * 4 + component; + line3 = in + ((y ) * width + (x - 1)) * 4 + component; + line4 = in + ((y + 1) * width + (x - 2)) * 4 + component; + line5 = in + ((y + 2) * width + (x - 1)) * 4 + component; + + // COPYSAMPLE(sa, line1); line1 += 8; + //COPYSAMPLE(sc, line2); line2 += 8; COPYSAMPLE(sd, line2); line2 += 8; + // COPYSAMPLE(sf, line3); line3 += 8; + //COPYSAMPLE(sh, line4); line4 += 8; COPYSAMPLE(si, line4); line4 += 8; + // COPYSAMPLE(sk, line5); line5 += 8; + + sa = *line1; line1 += 8; + sc = *line2; line2 += 8; sd = *line2; line2 += 8; + sf = *line3; line3 += 8; + sh = *line4; line4 += 8; si = *line4; line4 += 8; + sk = *line5; line5 += 8; + + outbyte = out + (y * width + x) * 4 + component; + + for ( ; x < width - 3; x+=2) + { + int hd, vd, hp, vp; + + // SAMPLE2(sa, x-1, y-2); SAMPLE2(sb, x+1, y-2); + //SAMPLE2(sc, x-2, y-1); SAMPLE2(sd, x, y-1); SAMPLE2(se, x+2, y-1); + // SAMPLE2(sf, x-1, y ); SAMPLE2(sg, x+1, y ); + //SAMPLE2(sh, x-2, y+1); SAMPLE2(si, x, y+1); SAMPLE2(sj, x+2, y+1); + // SAMPLE2(sk, x-1, y+2); SAMPLE2(sl, x+1, y+2); + + // optimization one + //SAMPLE2(sb, x+1, y-2); + //SAMPLE2(se, x+2, y-1); + //SAMPLE2(sg, x+1, y ); + //SAMPLE2(sj, x+2, y+1); + //SAMPLE2(sl, x+1, y+2); + + //COPYSAMPLE(sb, line1); line1 += 8; + //COPYSAMPLE(se, line2); line2 += 8; + //COPYSAMPLE(sg, line3); line3 += 8; + //COPYSAMPLE(sj, line4); line4 += 8; + //COPYSAMPLE(sl, line5); line5 += 8; + + sb = *line1; line1 += 8; + se = *line2; line2 += 8; + sg = *line3; line3 += 8; + sj = *line4; line4 += 8; + sl = *line5; line5 += 8; + + hp = sf + sg; + vp = sd + si; + hd = abs(sf - sg); + vd = abs(sd - si); + + if (hd > 100 || vd > 100 || abs(hp-vp) > 200) + { + if (hd < vd) + *outbyte = hp >> 1; + else + *outbyte = vp >> 1; + } + else + { + int hdd, vdd; + + //hdd = abs(sc[i] + sd[i] + se[i] - 3 * (sf[i] + sg[i]) + sh[i] + si[i] + sj[i]); + //vdd = abs(sa[i] + sf[i] + sk[i] - 3 * (sd[i] + si[i]) + sb[i] + sg[i] + sl[i]); + + hdd = abs(sc + se - 3 * hp + sh + sj + vp); + vdd = abs(sa + sk - 3 * vp + sb + sl + hp); + + if (hdd > vdd) + *outbyte = hp >> 1; + else + *outbyte = vp >> 1; + } + + outbyte += 8; + + // COPYSAMPLE(sa, sb); + //COPYSAMPLE(sc, sd); COPYSAMPLE(sd, se); + // COPYSAMPLE(sf, sg); + //COPYSAMPLE(sh, si); COPYSAMPLE(si, sj); + // COPYSAMPLE(sk, sl); + sa = sb; + sc = sd; sd = se; + sf = sg; + sh = si; si = sj; + sk = sl; + } + } +} + +// Similar to FCBI, but throws out the second order derivatives for speed +static void DoFCBIQuick(byte *in, byte *out, int width, int height, int component) +{ + int x, y; + byte *outbyte, *inbyte; + + // copy in to out + for (y = 2; y < height - 2; y += 2) + { + inbyte = in + (y * width + 2) * 4 + component; + outbyte = out + (y * width + 2) * 4 + component; + + for (x = 2; x < width - 2; x += 2) + { + *outbyte = *inbyte; + outbyte += 8; + inbyte += 8; + } + } + + for (y = 3; y < height - 4; y += 2) + { + byte sd, se, sh, si; + byte *line2, *line3; + + x = 3; + + line2 = in + ((y - 1) * width + (x - 1)) * 4 + component; + line3 = in + ((y + 1) * width + (x - 1)) * 4 + component; + + sd = *line2; line2 += 8; + sh = *line3; line3 += 8; + + outbyte = out + (y * width + x) * 4 + component; + + for ( ; x < width - 4; x += 2) + { + int NWd, NEd, NWp, NEp; + + se = *line2; line2 += 8; + si = *line3; line3 += 8; + + NWp = sd + si; + NEp = se + sh; + NWd = abs(sd - si); + NEd = abs(se - sh); + + if (NWd < NEd) + *outbyte = NWp >> 1; + else + *outbyte = NEp >> 1; + + outbyte += 8; + + sd = se; + sh = si; + } + } + + // hack: copy out to in again + for (y = 3; y < height - 3; y += 2) + { + inbyte = out + (y * width + 3) * 4 + component; + outbyte = in + (y * width + 3) * 4 + component; + + for (x = 3; x < width - 3; x += 2) + { + *outbyte = *inbyte; + outbyte += 8; + inbyte += 8; + } + } + + for (y = 2; y < height - 3; y++) + { + byte sd, sf, sg, si; + byte *line2, *line3, *line4; + + x = (y + 1) % 2 + 2; + + line2 = in + ((y - 1) * width + (x )) * 4 + component; + line3 = in + ((y ) * width + (x - 1)) * 4 + component; + line4 = in + ((y + 1) * width + (x )) * 4 + component; + + outbyte = out + (y * width + x) * 4 + component; + + sf = *line3; line3 += 8; + + for ( ; x < width - 3; x+=2) + { + int hd, vd, hp, vp; + + sd = *line2; line2 += 8; + sg = *line3; line3 += 8; + si = *line4; line4 += 8; + + hp = sf + sg; + vp = sd + si; + hd = abs(sf - sg); + vd = abs(sd - si); + + if (hd < vd) + *outbyte = hp >> 1; + else + *outbyte = vp >> 1; + + outbyte += 8; + + sf = sg; + } + } +} + +// Similar to DoFCBIQuick, but just takes the average instead of checking derivatives +// as well, this operates on all four components +static void DoLinear(byte *in, byte *out, int width, int height) +{ + int x, y, i; + byte *outbyte, *inbyte; + + // copy in to out + for (y = 2; y < height - 2; y += 2) + { + x = 2; + + inbyte = in + (y * width + x) * 4; + outbyte = out + (y * width + x) * 4; + + for ( ; x < width - 2; x += 2) + { + COPYSAMPLE(outbyte, inbyte); + outbyte += 8; + inbyte += 8; + } + } + + for (y = 1; y < height - 1; y += 2) + { + byte sd[4], se[4], sh[4], si[4]; + byte *line2, *line3; + + x = 1; + + line2 = in + ((y - 1) * width + (x - 1)) * 4; + line3 = in + ((y + 1) * width + (x - 1)) * 4; + + COPYSAMPLE(sd, line2); line2 += 8; + COPYSAMPLE(sh, line3); line3 += 8; + + outbyte = out + (y * width + x) * 4; + + for ( ; x < width - 1; x += 2) + { + COPYSAMPLE(se, line2); line2 += 8; + COPYSAMPLE(si, line3); line3 += 8; + + for (i = 0; i < 4; i++) + { + *outbyte++ = (sd[i] + si[i] + se[i] + sh[i]) >> 2; + } + + outbyte += 4; + + COPYSAMPLE(sd, se); + COPYSAMPLE(sh, si); + } + } + + // hack: copy out to in again + for (y = 1; y < height - 1; y += 2) + { + x = 1; + + inbyte = out + (y * width + x) * 4; + outbyte = in + (y * width + x) * 4; + + for ( ; x < width - 1; x += 2) + { + COPYSAMPLE(outbyte, inbyte); + outbyte += 8; + inbyte += 8; + } + } + + for (y = 1; y < height - 1; y++) + { + byte sd[4], sf[4], sg[4], si[4]; + byte *line2, *line3, *line4; + + x = y % 2 + 1; + + line2 = in + ((y - 1) * width + (x )) * 4; + line3 = in + ((y ) * width + (x - 1)) * 4; + line4 = in + ((y + 1) * width + (x )) * 4; + + COPYSAMPLE(sf, line3); line3 += 8; + + outbyte = out + (y * width + x) * 4; + + for ( ; x < width - 1; x += 2) + { + COPYSAMPLE(sd, line2); line2 += 8; + COPYSAMPLE(sg, line3); line3 += 8; + COPYSAMPLE(si, line4); line4 += 8; + + for (i = 0; i < 4; i++) + { + *outbyte++ = (sf[i] + sg[i] + sd[i] + si[i]) >> 2; + } + + outbyte += 4; + + COPYSAMPLE(sf, sg); + } + } +} + + +static void ExpandHalfTextureToGrid( byte *data, int width, int height) +{ + int x, y; + + for (y = height / 2; y > 0; y--) + { + byte *outbyte = data + ((y * 2 - 1) * (width) - 2) * 4; + byte *inbyte = data + (y * (width / 2) - 1) * 4; + + for (x = width / 2; x > 0; x--) + { + COPYSAMPLE(outbyte, inbyte); + + outbyte -= 8; + inbyte -= 4; + } + } +} + +static void FillInNormalizedZ(const byte *in, byte *out, int width, int height) +{ + int x, y; + + for (y = 0; y < height; y++) + { + const byte *inbyte = in + y * width * 4; + byte *outbyte = out + y * width * 4; + + for (x = 0; x < width; x++) + { + byte nx, ny, nz, h; + float fnx, fny, fll, fnz; + + nx = *inbyte++; + ny = *inbyte++; + inbyte++; + h = *inbyte++; + + fnx = OffsetByteToFloat(nx); + fny = OffsetByteToFloat(ny); + fll = 1.0f - fnx * fnx - fny * fny; + if (fll >= 0.0f) + fnz = (float)sqrt(fll); + else + fnz = 0.0f; + + nz = FloatToOffsetByte(fnz); + + *outbyte++ = nx; + *outbyte++ = ny; + *outbyte++ = nz; + *outbyte++ = h; + } + } +} + + +// size must be even +#define WORKBLOCK_SIZE 128 +#define WORKBLOCK_BORDER 4 +#define WORKBLOCK_REALSIZE (WORKBLOCK_SIZE + WORKBLOCK_BORDER * 2) + +// assumes that data has already been expanded into a 2x2 grid +static void FCBIByBlock(byte *data, int width, int height, qboolean clampToEdge, qboolean normalized) +{ + byte workdata[WORKBLOCK_REALSIZE * WORKBLOCK_REALSIZE * 4]; + byte outdata[WORKBLOCK_REALSIZE * WORKBLOCK_REALSIZE * 4]; + byte *inbyte, *outbyte; + int x, y; + int srcx, srcy; + + ExpandHalfTextureToGrid(data, width, height); + + for (y = 0; y < height; y += WORKBLOCK_SIZE) + { + for (x = 0; x < width; x += WORKBLOCK_SIZE) + { + int x2, y2; + int workwidth, workheight, fullworkwidth, fullworkheight; + + workwidth = MIN(WORKBLOCK_SIZE, width - x); + workheight = MIN(WORKBLOCK_SIZE, height - y); + + fullworkwidth = workwidth + WORKBLOCK_BORDER * 2; + fullworkheight = workheight + WORKBLOCK_BORDER * 2; + + //memset(workdata, 0, WORKBLOCK_REALSIZE * WORKBLOCK_REALSIZE * 4); + + // fill in work block + for (y2 = 0; y2 < fullworkheight; y2 += 2) + { + srcy = y + y2 - WORKBLOCK_BORDER; + + if (clampToEdge) + { + srcy = CLAMP(srcy, 0, height - 2); + } + else + { + srcy = (srcy + height) % height; + } + + outbyte = workdata + y2 * fullworkwidth * 4; + inbyte = data + srcy * width * 4; + + for (x2 = 0; x2 < fullworkwidth; x2 += 2) + { + srcx = x + x2 - WORKBLOCK_BORDER; + + if (clampToEdge) + { + srcx = CLAMP(srcx, 0, width - 2); + } + else + { + srcx = (srcx + width) % width; + } + + COPYSAMPLE(outbyte, inbyte + srcx * 4); + outbyte += 8; + } + } + + // submit work block + DoLinear(workdata, outdata, fullworkwidth, fullworkheight); + + if (!normalized) + { + switch (r_imageUpsampleType->integer) + { + case 0: + break; + case 1: + DoFCBIQuick(workdata, outdata, fullworkwidth, fullworkheight, 0); + break; + case 2: + default: + DoFCBI(workdata, outdata, fullworkwidth, fullworkheight, 0); + break; + } + } + else + { + switch (r_imageUpsampleType->integer) + { + case 0: + break; + case 1: + DoFCBIQuick(workdata, outdata, fullworkwidth, fullworkheight, 0); + DoFCBIQuick(workdata, outdata, fullworkwidth, fullworkheight, 1); + break; + case 2: + default: + DoFCBI(workdata, outdata, fullworkwidth, fullworkheight, 0); + DoFCBI(workdata, outdata, fullworkwidth, fullworkheight, 1); + break; + } + } + + // copy back work block + for (y2 = 0; y2 < workheight; y2++) + { + inbyte = outdata + ((y2 + WORKBLOCK_BORDER) * fullworkwidth + WORKBLOCK_BORDER) * 4; + outbyte = data + ((y + y2) * width + x) * 4; + for (x2 = 0; x2 < workwidth; x2++) + { + COPYSAMPLE(outbyte, inbyte); + outbyte += 4; + inbyte += 4; + } + } + } + } +} +#undef COPYSAMPLE + +/* +================ +R_LightScaleTexture + +Scale up the pixel values in a texture to increase the +lighting range +================ +*/ +void R_LightScaleTexture (byte *in, int inwidth, int inheight, qboolean only_gamma ) +{ + if ( only_gamma ) + { + if ( !glConfig.deviceSupportsGamma ) + { + int i, c; + byte *p; + + p = in; + + c = inwidth*inheight; + for (i=0 ; i> 1; + outHeight = inHeight >> 1; + temp = ri.Hunk_AllocateTempMemory( outWidth * outHeight * 4 ); + + inWidthMask = inWidth - 1; + inHeightMask = inHeight - 1; + + for ( i = 0 ; i < outHeight ; i++ ) { + for ( j = 0 ; j < outWidth ; j++ ) { + outpix = (byte *) ( temp + i * outWidth + j ); + for ( k = 0 ; k < 4 ; k++ ) { + total = + 1 * (&in[ 4*(((i*2-1)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask)) ])[k] + + 2 * (&in[ 4*(((i*2-1)&inHeightMask)*inWidth + ((j*2 )&inWidthMask)) ])[k] + + 2 * (&in[ 4*(((i*2-1)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask)) ])[k] + + 1 * (&in[ 4*(((i*2-1)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask)) ])[k] + + + 2 * (&in[ 4*(((i*2 )&inHeightMask)*inWidth + ((j*2-1)&inWidthMask)) ])[k] + + 4 * (&in[ 4*(((i*2 )&inHeightMask)*inWidth + ((j*2 )&inWidthMask)) ])[k] + + 4 * (&in[ 4*(((i*2 )&inHeightMask)*inWidth + ((j*2+1)&inWidthMask)) ])[k] + + 2 * (&in[ 4*(((i*2 )&inHeightMask)*inWidth + ((j*2+2)&inWidthMask)) ])[k] + + + 2 * (&in[ 4*(((i*2+1)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask)) ])[k] + + 4 * (&in[ 4*(((i*2+1)&inHeightMask)*inWidth + ((j*2 )&inWidthMask)) ])[k] + + 4 * (&in[ 4*(((i*2+1)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask)) ])[k] + + 2 * (&in[ 4*(((i*2+1)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask)) ])[k] + + + 1 * (&in[ 4*(((i*2+2)&inHeightMask)*inWidth + ((j*2-1)&inWidthMask)) ])[k] + + 2 * (&in[ 4*(((i*2+2)&inHeightMask)*inWidth + ((j*2 )&inWidthMask)) ])[k] + + 2 * (&in[ 4*(((i*2+2)&inHeightMask)*inWidth + ((j*2+1)&inWidthMask)) ])[k] + + 1 * (&in[ 4*(((i*2+2)&inHeightMask)*inWidth + ((j*2+2)&inWidthMask)) ])[k]; + outpix[k] = total / 36; + } + } + } + + Com_Memcpy( in, temp, outWidth * outHeight * 4 ); + ri.Hunk_FreeTempMemory( temp ); +} + + +static void R_MipMapsRGB( byte *in, int inWidth, int inHeight) +{ + int i, j, k; + int outWidth, outHeight; + byte *temp; + + outWidth = inWidth >> 1; + outHeight = inHeight >> 1; + temp = ri.Hunk_AllocateTempMemory( outWidth * outHeight * 4 ); + + for ( i = 0 ; i < outHeight ; i++ ) { + byte *outbyte = temp + ( i * outWidth ) * 4; + byte *inbyte1 = in + ( i * 2 * inWidth ) * 4; + byte *inbyte2 = in + ( (i * 2 + 1) * inWidth ) * 4; + for ( j = 0 ; j < outWidth ; j++ ) { + for ( k = 0 ; k < 3 ; k++ ) { + float total, current; + + current = ByteToFloat(inbyte1[0]); total = sRGBtoRGB(current); + current = ByteToFloat(inbyte1[4]); total += sRGBtoRGB(current); + current = ByteToFloat(inbyte2[0]); total += sRGBtoRGB(current); + current = ByteToFloat(inbyte2[4]); total += sRGBtoRGB(current); + + total *= 0.25f; + + inbyte1++; + inbyte2++; + + current = RGBtosRGB(total); + *outbyte++ = FloatToByte(current); + } + *outbyte++ = (inbyte1[0] + inbyte1[4] + inbyte2[0] + inbyte2[4]) >> 2; + inbyte1 += 5; + inbyte2 += 5; + } + } + + Com_Memcpy( in, temp, outWidth * outHeight * 4 ); + ri.Hunk_FreeTempMemory( temp ); +} + +/* +================ +R_MipMap + +Operates in place, quartering the size of the texture +================ +*/ +static void R_MipMap (byte *in, int width, int height) { + int i, j; + byte *out; + int row; + + if ( !r_simpleMipMaps->integer ) { + R_MipMap2( in, width, height ); + return; + } + + if ( width == 1 && height == 1 ) { + return; + } + + row = width * 4; + out = in; + width >>= 1; + height >>= 1; + + if ( width == 0 || height == 0 ) { + width += height; // get largest + for (i=0 ; i>1; + out[1] = ( in[1] + in[5] )>>1; + out[2] = ( in[2] + in[6] )>>1; + out[3] = ( in[3] + in[7] )>>1; + } + return; + } + + for (i=0 ; i>2; + out[1] = (in[1] + in[5] + in[row+1] + in[row+5])>>2; + out[2] = (in[2] + in[6] + in[row+2] + in[row+6])>>2; + out[3] = (in[3] + in[7] + in[row+3] + in[row+7])>>2; + } + } +} + + +static void R_MipMapLuminanceAlpha (const byte *in, byte *out, int width, int height) +{ + int i, j, row; + + if ( width == 1 && height == 1 ) { + return; + } + + row = width * 4; + width >>= 1; + height >>= 1; + + if ( width == 0 || height == 0 ) { + width += height; // get largest + for (i=0 ; i> 1; + out[3] = (in[3] + in[7]) >> 1; + } + return; + } + + for (i=0 ; i> 2; + out[3] = (in[3] + in[7] + in[row+3] + in[row+7]) >> 2; + } + } + +} + + +static void R_MipMapNormalHeight (const byte *in, byte *out, int width, int height, qboolean swizzle) +{ + int i, j; + int row; + int sx = swizzle ? 3 : 0; + int sa = swizzle ? 0 : 3; + + if ( width == 1 && height == 1 ) { + return; + } + + row = width * 4; + width >>= 1; + height >>= 1; + + for (i=0 ; i> 9; + data[1] = ( data[1] * inverseAlpha + premult[1] ) >> 9; + data[2] = ( data[2] * inverseAlpha + premult[2] ) >> 9; + } +} + +byte mipBlendColors[16][4] = { + {0,0,0,0}, + {255,0,0,128}, + {0,255,0,128}, + {0,0,255,128}, + {255,0,0,128}, + {0,255,0,128}, + {0,0,255,128}, + {255,0,0,128}, + {0,255,0,128}, + {0,0,255,128}, + {255,0,0,128}, + {0,255,0,128}, + {0,0,255,128}, + {255,0,0,128}, + {0,255,0,128}, + {0,0,255,128}, +}; + +static void RawImage_SwizzleRA( byte *data, int width, int height ) +{ + int i; + byte *ptr = data, swap; + + for (i=0; iinteger && scaled_width > width ) + scaled_width >>= 1; + if ( r_roundImagesDown->integer && scaled_height > height ) + scaled_height >>= 1; + + if ( picmip && data && resampledBuffer && r_imageUpsample->integer && + scaled_width < r_imageUpsampleMaxSize->integer && scaled_height < r_imageUpsampleMaxSize->integer) + { + int finalwidth, finalheight; + //int startTime, endTime; + + //startTime = ri.Milliseconds(); + + finalwidth = scaled_width << r_imageUpsample->integer; + finalheight = scaled_height << r_imageUpsample->integer; + + while ( finalwidth > r_imageUpsampleMaxSize->integer + || finalheight > r_imageUpsampleMaxSize->integer ) { + finalwidth >>= 1; + finalheight >>= 1; + } + + while ( finalwidth > glConfig.maxTextureSize + || finalheight > glConfig.maxTextureSize ) { + finalwidth >>= 1; + finalheight >>= 1; + } + + *resampledBuffer = ri.Hunk_AllocateTempMemory( finalwidth * finalheight * 4 ); + + if (scaled_width != width || scaled_height != height) + { + ResampleTexture (*data, width, height, *resampledBuffer, scaled_width, scaled_height); + } + else + { + byte *inbyte, *outbyte; + int i; + + inbyte = *data; + outbyte = *resampledBuffer; + + for (i = width * height * 4; i > 0; i--) + { + *outbyte++ = *inbyte++; + } + } + + if (type == IMGTYPE_COLORALPHA) + RGBAtoYCoCgA(*resampledBuffer, *resampledBuffer, scaled_width, scaled_height); + + while (scaled_width < finalwidth || scaled_height < finalheight) + { + scaled_width <<= 1; + scaled_height <<= 1; + + FCBIByBlock(*resampledBuffer, scaled_width, scaled_height, clampToEdge, (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT)); + } + + if (type == IMGTYPE_COLORALPHA) + { + YCoCgAtoRGBA(*resampledBuffer, *resampledBuffer, scaled_width, scaled_height); + } + else if (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT) + { + FillInNormalizedZ(*resampledBuffer, *resampledBuffer, scaled_width, scaled_height); + } + + + //endTime = ri.Milliseconds(); + + //ri.Printf(PRINT_ALL, "upsampled %dx%d to %dx%d in %dms\n", width, height, scaled_width, scaled_height, endTime - startTime); + + *data = *resampledBuffer; + width = scaled_width; + height = scaled_height; + } + else if ( scaled_width != width || scaled_height != height ) { + if (data && resampledBuffer) + { + *resampledBuffer = ri.Hunk_AllocateTempMemory( scaled_width * scaled_height * 4 ); + ResampleTexture (*data, width, height, *resampledBuffer, scaled_width, scaled_height); + *data = *resampledBuffer; + } + width = scaled_width; + height = scaled_height; + } + + // + // perform optional picmip operation + // + if ( picmip ) { + scaled_width >>= r_picmip->integer; + scaled_height >>= r_picmip->integer; + } + + // + // clamp to minimum size + // + if (scaled_width < 1) { + scaled_width = 1; + } + if (scaled_height < 1) { + scaled_height = 1; + } + + // + // clamp to the current upper OpenGL limit + // scale both axis down equally so we don't have to + // deal with a half mip resampling + // + while ( scaled_width > glConfig.maxTextureSize + || scaled_height > glConfig.maxTextureSize ) { + scaled_width >>= 1; + scaled_height >>= 1; + } + + *inout_width = width; + *inout_height = height; + *inout_scaled_width = scaled_width; + *inout_scaled_height = scaled_height; +} + + +static qboolean RawImage_HasAlpha(const byte *scan, int numPixels) +{ + int i; + + if (!scan) + return qtrue; + + for ( i = 0; i < numPixels; i++ ) + { + if ( scan[i*4 + 3] != 255 ) + { + return qtrue; + } + } + + return qfalse; +} + +static GLenum RawImage_GetFormat(const byte *data, int numPixels, qboolean lightMap, imgType_t type, imgFlags_t flags) +{ + int samples = 3; + GLenum internalFormat = GL_RGB; + qboolean forceNoCompression = (flags & IMGFLAG_NO_COMPRESSION); + qboolean normalmap = (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT); + + if(normalmap) + { + if ((!RawImage_HasAlpha(data, numPixels) || (type == IMGTYPE_NORMAL)) && !forceNoCompression && (glRefConfig.textureCompression & TCR_LATC)) + { + internalFormat = GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT; + } + else + { + if ( !forceNoCompression && glConfig.textureCompression == TC_S3TC_ARB ) + { + internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + } + else if ( r_texturebits->integer == 16 ) + { + internalFormat = GL_RGBA4; + } + else if ( r_texturebits->integer == 32 ) + { + internalFormat = GL_RGBA8; + } + else + { + internalFormat = GL_RGBA; + } + } + } + else if(lightMap) + { + samples = 4; + if(r_greyscale->integer) + internalFormat = GL_LUMINANCE; + else + internalFormat = GL_RGBA; + } + else + { + if (RawImage_HasAlpha(data, numPixels)) + { + samples = 4; + } + + // select proper internal format + if ( samples == 3 ) + { + if(r_greyscale->integer) + { + if(r_texturebits->integer == 16) + internalFormat = GL_LUMINANCE8; + else if(r_texturebits->integer == 32) + internalFormat = GL_LUMINANCE16; + else + internalFormat = GL_LUMINANCE; + } + else + { + if ( !forceNoCompression && (glRefConfig.textureCompression & TCR_BPTC) ) + { + internalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + } + else if ( !forceNoCompression && glConfig.textureCompression == TC_S3TC_ARB ) + { + internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + } + else if ( !forceNoCompression && glConfig.textureCompression == TC_S3TC ) + { + internalFormat = GL_RGB4_S3TC; + } + else if ( r_texturebits->integer == 16 ) + { + internalFormat = GL_RGB5; + } + else if ( r_texturebits->integer == 32 ) + { + internalFormat = GL_RGB8; + } + else + { + internalFormat = GL_RGB; + } + } + } + else if ( samples == 4 ) + { + if(r_greyscale->integer) + { + if(r_texturebits->integer == 16) + internalFormat = GL_LUMINANCE8_ALPHA8; + else if(r_texturebits->integer == 32) + internalFormat = GL_LUMINANCE16_ALPHA16; + else + internalFormat = GL_LUMINANCE_ALPHA; + } + else + { + if ( !forceNoCompression && (glRefConfig.textureCompression & TCR_BPTC) ) + { + internalFormat = GL_COMPRESSED_RGBA_BPTC_UNORM_ARB; + } + else if ( !forceNoCompression && glConfig.textureCompression == TC_S3TC_ARB ) + { + internalFormat = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + } + else if ( r_texturebits->integer == 16 ) + { + internalFormat = GL_RGBA4; + } + else if ( r_texturebits->integer == 32 ) + { + internalFormat = GL_RGBA8; + } + else + { + internalFormat = GL_RGBA; + } + } + } + + if (glRefConfig.texture_srgb && (flags & IMGFLAG_SRGB)) + { + switch(internalFormat) + { + case GL_RGB: + internalFormat = GL_SRGB_EXT; + break; + + case GL_RGB4: + case GL_RGB5: + case GL_RGB8: + internalFormat = GL_SRGB8_EXT; + break; + + case GL_RGBA: + internalFormat = GL_SRGB_ALPHA_EXT; + break; + + case GL_RGBA4: + case GL_RGBA8: + internalFormat = GL_SRGB8_ALPHA8_EXT; + break; + + case GL_LUMINANCE: + internalFormat = GL_SLUMINANCE_EXT; + break; + + case GL_LUMINANCE8: + case GL_LUMINANCE16: + internalFormat = GL_SLUMINANCE8_EXT; + break; + + case GL_LUMINANCE_ALPHA: + internalFormat = GL_SLUMINANCE_ALPHA_EXT; + break; + + case GL_LUMINANCE8_ALPHA8: + case GL_LUMINANCE16_ALPHA16: + internalFormat = GL_SLUMINANCE8_ALPHA8_EXT; + break; + + case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT: + internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT; + break; + + case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT: + internalFormat = GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT; + break; + + case GL_COMPRESSED_RGBA_BPTC_UNORM_ARB: + internalFormat = GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB; + break; + } + } + } + + return internalFormat; +} + + +static void RawImage_UploadTexture( byte *data, int x, int y, int width, int height, GLenum internalFormat, imgType_t type, imgFlags_t flags, qboolean subtexture ) +{ + int dataFormat, dataType; + + switch(internalFormat) + { + case GL_DEPTH_COMPONENT: + case GL_DEPTH_COMPONENT16_ARB: + case GL_DEPTH_COMPONENT24_ARB: + case GL_DEPTH_COMPONENT32_ARB: + dataFormat = GL_DEPTH_COMPONENT; + dataType = GL_UNSIGNED_BYTE; + break; + case GL_RGBA16F_ARB: + dataFormat = GL_RGBA; + dataType = GL_HALF_FLOAT_ARB; + break; + default: + dataFormat = GL_RGBA; + dataType = GL_UNSIGNED_BYTE; + break; + } + + if ( subtexture ) + qglTexSubImage2D( GL_TEXTURE_2D, 0, x, y, width, height, dataFormat, dataType, data ); + else + qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, width, height, 0, dataFormat, dataType, data ); + + if (flags & IMGFLAG_MIPMAP) + { + int miplevel; + + miplevel = 0; + while (width > 1 || height > 1) + { + if (data) + { + if (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT) + { + if (internalFormat == GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT) + { + R_MipMapLuminanceAlpha( data, data, width, height ); + } + else + { + R_MipMapNormalHeight( data, data, width, height, qtrue); + } + } + else if (flags & IMGFLAG_SRGB) + { + R_MipMapsRGB( data, width, height ); + } + else + { + R_MipMap( data, width, height ); + } + } + + width >>= 1; + height >>= 1; + if (width < 1) + width = 1; + if (height < 1) + height = 1; + miplevel++; + + if ( data && r_colorMipLevels->integer ) + R_BlendOverTexture( (byte *)data, width * height, mipBlendColors[miplevel] ); + + if ( subtexture ) + { + x >>= 1; + y >>= 1; + qglTexSubImage2D( GL_TEXTURE_2D, miplevel, x, y, width, height, dataFormat, dataType, data ); + } + else + { + qglTexImage2D (GL_TEXTURE_2D, miplevel, internalFormat, width, height, 0, dataFormat, dataType, data ); + } + } + } +} + + +/* +=============== +Upload32 + +=============== +*/ +extern qboolean charSet; +static void Upload32( byte *data, int width, int height, imgType_t type, imgFlags_t flags, + qboolean lightMap, GLenum internalFormat, int *pUploadWidth, int *pUploadHeight) +{ + byte *scaledBuffer = NULL; + byte *resampledBuffer = NULL; + int scaled_width, scaled_height; + int i, c; + byte *scan; + + RawImage_ScaleToPower2(&data, &width, &height, &scaled_width, &scaled_height, type, flags, &resampledBuffer); + + scaledBuffer = ri.Hunk_AllocateTempMemory( sizeof( unsigned ) * scaled_width * scaled_height ); + + // + // scan the texture for each channel's max values + // and verify if the alpha channel is being used or not + // + c = width*height; + scan = data; + + if( r_greyscale->integer ) + { + for ( i = 0; i < c; i++ ) + { + byte luma = LUMA(scan[i*4], scan[i*4 + 1], scan[i*4 + 2]); + scan[i*4] = luma; + scan[i*4 + 1] = luma; + scan[i*4 + 2] = luma; + } + } + else if( r_greyscale->value ) + { + for ( i = 0; i < c; i++ ) + { + float luma = LUMA(scan[i*4], scan[i*4 + 1], scan[i*4 + 2]); + scan[i*4] = LERP(scan[i*4], luma, r_greyscale->value); + scan[i*4 + 1] = LERP(scan[i*4 + 1], luma, r_greyscale->value); + scan[i*4 + 2] = LERP(scan[i*4 + 2], luma, r_greyscale->value); + } + } + + // Convert to RGB if sRGB textures aren't supported in hardware + if (!glRefConfig.texture_srgb && (flags & IMGFLAG_SRGB)) + { + byte *in = data; + int c = width * height; + while (c--) + { + for (i = 0; i < 3; i++) + { + float x = ByteToFloat(in[i]); + x = sRGBtoRGB(x); + in[i] = FloatToByte(x); + } + in += 4; + } + + // FIXME: Probably should mark the image as non-sRGB as well + flags &= ~IMGFLAG_SRGB; + } + + // normals are always swizzled + if (type == IMGTYPE_NORMAL || type == IMGTYPE_NORMALHEIGHT) + { + RawImage_SwizzleRA(data, width, height); + } + + // LATC2 is only used for normals + if (internalFormat == GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT) + { + byte *in = data; + int c = width * height; + while (c--) + { + in[0] = in[1]; + in[2] = in[1]; + in += 4; + } + } + + // copy or resample data as appropriate for first MIP level + if ( ( scaled_width == width ) && + ( scaled_height == height ) ) { + if (!(flags & IMGFLAG_MIPMAP)) + { + RawImage_UploadTexture( data, 0, 0, scaled_width, scaled_height, internalFormat, type, flags, qfalse ); + //qglTexImage2D (GL_TEXTURE_2D, 0, internalFormat, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + *pUploadWidth = scaled_width; + *pUploadHeight = scaled_height; + + goto done; + } + Com_Memcpy (scaledBuffer, data, width*height*4); + } + else + { + // use the normal mip-mapping function to go down from here + while ( width > scaled_width || height > scaled_height ) { + + if (flags & IMGFLAG_SRGB) + { + R_MipMapsRGB( (byte *)data, width, height ); + } + else + { + R_MipMap( (byte *)data, width, height ); + } + + width >>= 1; + height >>= 1; + if ( width < 1 ) { + width = 1; + } + if ( height < 1 ) { + height = 1; + } + } + Com_Memcpy( scaledBuffer, data, width * height * 4 ); + } + + if (!(flags & IMGFLAG_NOLIGHTSCALE)) + R_LightScaleTexture (scaledBuffer, scaled_width, scaled_height, !(flags & IMGFLAG_MIPMAP) ); + + *pUploadWidth = scaled_width; + *pUploadHeight = scaled_height; + + RawImage_UploadTexture(scaledBuffer, 0, 0, scaled_width, scaled_height, internalFormat, type, flags, qfalse); + +done: + + if (flags & IMGFLAG_MIPMAP) + { + if ( textureFilterAnisotropic ) + qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, + (GLint)Com_Clamp( 1, maxAnisotropy, r_ext_max_anisotropy->integer ) ); + + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + else + { + if ( textureFilterAnisotropic ) + qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 ); + + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + } + + GL_CheckErrors(); + + if ( scaledBuffer != 0 ) + ri.Hunk_FreeTempMemory( scaledBuffer ); + if ( resampledBuffer != 0 ) + ri.Hunk_FreeTempMemory( resampledBuffer ); +} + + +static void EmptyTexture( int width, int height, imgType_t type, imgFlags_t flags, + qboolean lightMap, GLenum internalFormat, int *pUploadWidth, int *pUploadHeight ) +{ + int scaled_width, scaled_height; + + RawImage_ScaleToPower2(NULL, &width, &height, &scaled_width, &scaled_height, type, flags, NULL); + + *pUploadWidth = scaled_width; + *pUploadHeight = scaled_height; + + RawImage_UploadTexture(NULL, 0, 0, scaled_width, scaled_height, internalFormat, type, flags, qfalse); + + if (flags & IMGFLAG_MIPMAP) + { + if ( textureFilterAnisotropic ) + qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, + (GLint)Com_Clamp( 1, maxAnisotropy, r_ext_max_anisotropy->integer ) ); + + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max); + } + else + { + if ( textureFilterAnisotropic ) + qglTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1 ); + + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); + } + + // Fix for sampling depth buffer on old nVidia cards + // from http://www.idevgames.com/forums/thread-4141-post-34844.html#pid34844 + switch(internalFormat) + { + case GL_DEPTH_COMPONENT: + case GL_DEPTH_COMPONENT16_ARB: + case GL_DEPTH_COMPONENT24_ARB: + case GL_DEPTH_COMPONENT32_ARB: + qglTexParameterf(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE ); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); + qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); + break; + default: + break; + } + + GL_CheckErrors(); +} + + +/* +================ +R_CreateImage + +This is the only way any image_t are created +================ +*/ +image_t *R_CreateImage( const char *name, byte *pic, int width, int height, imgType_t type, imgFlags_t flags, int internalFormat ) { + image_t *image; + qboolean isLightmap = qfalse; + long hash; + int glWrapClampMode; + + if (strlen(name) >= MAX_QPATH ) { + ri.Error (ERR_DROP, "R_CreateImage: \"%s\" is too long", name); + } + if ( !strncmp( name, "*lightmap", 9 ) ) { + isLightmap = qtrue; + } + + if ( tr.numImages == MAX_DRAWIMAGES ) { + ri.Error( ERR_DROP, "R_CreateImage: MAX_DRAWIMAGES hit"); + } + + image = tr.images[tr.numImages] = ri.Hunk_Alloc( sizeof( image_t ), h_low ); + image->texnum = 1024 + tr.numImages; + tr.numImages++; + + image->type = type; + image->flags = flags; + + strcpy (image->imgName, name); + + image->width = width; + image->height = height; + if (flags & IMGFLAG_CLAMPTOEDGE) + glWrapClampMode = GL_CLAMP_TO_EDGE; + else + glWrapClampMode = GL_REPEAT; + + if (!internalFormat) + { + if (image->flags & IMGFLAG_CUBEMAP) + internalFormat = GL_RGBA8; + else + internalFormat = RawImage_GetFormat(pic, width * height, isLightmap, image->type, image->flags); + } + + image->internalFormat = internalFormat; + + + // lightmaps are always allocated on TMU 1 + if ( qglActiveTextureARB && isLightmap ) { + image->TMU = 1; + } else { + image->TMU = 0; + } + + if ( qglActiveTextureARB ) { + GL_SelectTexture( image->TMU ); + } + + if (image->flags & IMGFLAG_CUBEMAP) + { + GL_BindCubemap(image); + qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + qglTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + qglTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); + qglTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); + qglTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); + qglTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); + qglTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); + qglTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, pic); + + image->uploadWidth = width; + image->uploadHeight = height; + } + else + { + GL_Bind(image); + + if (pic) + { + Upload32( pic, image->width, image->height, image->type, image->flags, + isLightmap, image->internalFormat, &image->uploadWidth, + &image->uploadHeight ); + } + else + { + EmptyTexture(image->width, image->height, image->type, image->flags, + isLightmap, image->internalFormat, &image->uploadWidth, + &image->uploadHeight ); + } + + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glWrapClampMode ); + qglTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glWrapClampMode ); + } + + GL_SelectTexture( 0 ); + + hash = generateHashValue(name); + image->next = hashTable[hash]; + hashTable[hash] = image; + + return image; +} + +void R_UpdateSubImage( image_t *image, byte *pic, int x, int y, int width, int height ) +{ + byte *scaledBuffer = NULL; + byte *resampledBuffer = NULL; + int scaled_width, scaled_height, scaled_x, scaled_y; + byte *data = pic; + + // normals are always swizzled + if (image->type == IMGTYPE_NORMAL || image->type == IMGTYPE_NORMALHEIGHT) + { + RawImage_SwizzleRA(pic, width, height); + } + + // LATC2 is only used for normals + if (image->internalFormat == GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT) + { + byte *in = data; + int c = width * height; + while (c--) + { + in[0] = in[1]; + in[2] = in[1]; + in += 4; + } + } + + + RawImage_ScaleToPower2(&pic, &width, &height, &scaled_width, &scaled_height, image->type, image->flags, &resampledBuffer); + + scaledBuffer = ri.Hunk_AllocateTempMemory( sizeof( unsigned ) * scaled_width * scaled_height ); + + if ( qglActiveTextureARB ) { + GL_SelectTexture( image->TMU ); + } + + GL_Bind(image); + + // copy or resample data as appropriate for first MIP level + if ( ( scaled_width == width ) && + ( scaled_height == height ) ) { + if (!(image->flags & IMGFLAG_MIPMAP)) + { + scaled_x = x * scaled_width / width; + scaled_y = y * scaled_height / height; + RawImage_UploadTexture( data, scaled_x, scaled_y, scaled_width, scaled_height, image->internalFormat, image->type, image->flags, qtrue ); + //qglTexSubImage2D( GL_TEXTURE_2D, 0, scaled_x, scaled_y, scaled_width, scaled_height, GL_RGBA, GL_UNSIGNED_BYTE, data ); + + GL_CheckErrors(); + goto done; + } + Com_Memcpy (scaledBuffer, data, width*height*4); + } + else + { + // use the normal mip-mapping function to go down from here + while ( width > scaled_width || height > scaled_height ) { + + if (image->flags & IMGFLAG_SRGB) + { + R_MipMapsRGB( (byte *)data, width, height ); + } + else + { + R_MipMap( (byte *)data, width, height ); + } + + width >>= 1; + height >>= 1; + x >>= 1; + y >>= 1; + if ( width < 1 ) { + width = 1; + } + if ( height < 1 ) { + height = 1; + } + } + Com_Memcpy( scaledBuffer, data, width * height * 4 ); + } + + if (!(image->flags & IMGFLAG_NOLIGHTSCALE)) + R_LightScaleTexture (scaledBuffer, scaled_width, scaled_height, !(image->flags & IMGFLAG_MIPMAP) ); + + scaled_x = x * scaled_width / width; + scaled_y = y * scaled_height / height; + RawImage_UploadTexture( (byte *)data, scaled_x, scaled_y, scaled_width, scaled_height, image->internalFormat, image->type, image->flags, qtrue ); + +done: + + GL_SelectTexture( 0 ); + + GL_CheckErrors(); + + if ( scaledBuffer != 0 ) + ri.Hunk_FreeTempMemory( scaledBuffer ); + if ( resampledBuffer != 0 ) + ri.Hunk_FreeTempMemory( resampledBuffer ); +} + +//=================================================================== + +typedef struct +{ + char *ext; + void (*ImageLoader)( const char *, unsigned char **, int *, int * ); +} imageExtToLoaderMap_t; + +// Note that the ordering indicates the order of preference used +// when there are multiple images of different formats available +static imageExtToLoaderMap_t imageLoaders[ ] = +{ + { "tga", R_LoadTGA }, + { "jpg", R_LoadJPG }, + { "jpeg", R_LoadJPG }, + { "png", R_LoadPNG }, + { "pcx", R_LoadPCX }, + { "bmp", R_LoadBMP } +}; + +static int numImageLoaders = ARRAY_LEN( imageLoaders ); + +/* +================= +R_LoadImage + +Loads any of the supported image types into a cannonical +32 bit format. +================= +*/ +void R_LoadImage( const char *name, byte **pic, int *width, int *height ) +{ + qboolean orgNameFailed = qfalse; + int orgLoader = -1; + int i; + char localName[ MAX_QPATH ]; + const char *ext; + char *altName; + + *pic = NULL; + *width = 0; + *height = 0; + + Q_strncpyz( localName, name, MAX_QPATH ); + + ext = COM_GetExtension( localName ); + + if( *ext ) + { + // Look for the correct loader and use it + for( i = 0; i < numImageLoaders; i++ ) + { + if( !Q_stricmp( ext, imageLoaders[ i ].ext ) ) + { + // Load + imageLoaders[ i ].ImageLoader( localName, pic, width, height ); + break; + } + } + + // A loader was found + if( i < numImageLoaders ) + { + if( *pic == NULL ) + { + // Loader failed, most likely because the file isn't there; + // try again without the extension + orgNameFailed = qtrue; + orgLoader = i; + COM_StripExtension( name, localName, MAX_QPATH ); + } + else + { + // Something loaded + return; + } + } + } + + // Try and find a suitable match using all + // the image formats supported + for( i = 0; i < numImageLoaders; i++ ) + { + if (i == orgLoader) + continue; + + altName = va( "%s.%s", localName, imageLoaders[ i ].ext ); + + // Load + imageLoaders[ i ].ImageLoader( altName, pic, width, height ); + + if( *pic ) + { + if( orgNameFailed ) + { + ri.Printf( PRINT_DEVELOPER, "WARNING: %s not present, using %s instead\n", + name, altName ); + } + + break; + } + } +} + + +/* +=============== +R_FindImageFile + +Finds or loads the given image. +Returns NULL if it fails, not a default image. +============== +*/ +image_t *R_FindImageFile( const char *name, imgType_t type, imgFlags_t flags ) +{ + image_t *image; + int width, height; + byte *pic; + long hash; + + if (!name) { + return NULL; + } + + hash = generateHashValue(name); + + // + // see if the image is already loaded + // + for (image=hashTable[hash]; image; image=image->next) { + if ( !strcmp( name, image->imgName ) ) { + // the white image can be used with any set of parms, but other mismatches are errors + if ( strcmp( name, "*white" ) ) { + if ( image->flags != flags ) { + ri.Printf( PRINT_DEVELOPER, "WARNING: reused image %s with mixed flags (%i vs %i)\n", name, image->flags, flags ); + } + } + return image; + } + } + + // + // load the pic from disk + // + R_LoadImage( name, &pic, &width, &height ); + if ( pic == NULL ) { + return NULL; + } + + if (r_normalMapping->integer && !(type == IMGTYPE_NORMAL) && (flags & IMGFLAG_PICMIP) && (flags & IMGFLAG_MIPMAP) && (flags & IMGFLAG_GENNORMALMAP)) + { + char normalName[MAX_QPATH]; + image_t *normalImage; + int normalWidth, normalHeight; + imgFlags_t normalFlags; + + normalFlags = (flags & ~(IMGFLAG_GENNORMALMAP | IMGFLAG_SRGB)) | IMGFLAG_NOLIGHTSCALE; + + COM_StripExtension(name, normalName, MAX_QPATH); + Q_strcat(normalName, MAX_QPATH, "_n"); + + // find normalmap in case it's there + normalImage = R_FindImageFile(normalName, IMGTYPE_NORMAL, normalFlags); + + // if not, generate it + if (normalImage == NULL) + { + byte *normalPic; + int x, y; + + normalWidth = width; + normalHeight = height; + normalPic = ri.Malloc(width * height * 4); + RGBAtoNormal(pic, normalPic, width, height, flags & IMGFLAG_CLAMPTOEDGE); + + // Brighten up the original image to work with the normal map + RGBAtoYCoCgA(pic, pic, width, height); + for (y = 0; y < height; y++) + { + byte *picbyte = pic + y * width * 4; + byte *normbyte = normalPic + y * width * 4; + for (x = 0; x < width; x++) + { + int div = MAX(normbyte[2] - 127, 16); + picbyte[0] = CLAMP(picbyte[0] * 128 / div, 0, 255); + picbyte += 4; + normbyte += 4; + } + } + YCoCgAtoRGBA(pic, pic, width, height); + + R_CreateImage( normalName, normalPic, normalWidth, normalHeight, IMGTYPE_NORMAL, normalFlags, 0 ); + ri.Free( normalPic ); + } + } + + image = R_CreateImage( ( char * ) name, pic, width, height, type, flags, 0 ); + ri.Free( pic ); + return image; +} + + +/* +================ +R_CreateDlightImage +================ +*/ +#define DLIGHT_SIZE 16 +static void R_CreateDlightImage( void ) { + int x,y; + byte data[DLIGHT_SIZE][DLIGHT_SIZE][4]; + int b; + + // make a centered inverse-square falloff blob for dynamic lighting + for (x=0 ; x 255) { + b = 255; + } else if ( b < 75 ) { + b = 0; + } + data[y][x][0] = + data[y][x][1] = + data[y][x][2] = b; + data[y][x][3] = 255; + } + } + tr.dlightImage = R_CreateImage("*dlight", (byte *)data, DLIGHT_SIZE, DLIGHT_SIZE, IMGTYPE_COLORALPHA, IMGFLAG_CLAMPTOEDGE, 0 ); +} + + +/* +================= +R_InitFogTable +================= +*/ +void R_InitFogTable( void ) { + int i; + float d; + float exp; + + exp = 0.5; + + for ( i = 0 ; i < FOG_TABLE_SIZE ; i++ ) { + d = pow ( (float)i/(FOG_TABLE_SIZE-1), exp ); + + tr.fogTable[i] = d; + } +} + +/* +================ +R_FogFactor + +Returns a 0.0 to 1.0 fog density value +This is called for each texel of the fog texture on startup +and for each vertex of transparent shaders in fog dynamically +================ +*/ +float R_FogFactor( float s, float t ) { + float d; + + s -= 1.0/512; + if ( s < 0 ) { + return 0; + } + if ( t < 1.0/32 ) { + return 0; + } + if ( t < 31.0/32 ) { + s *= (t - 1.0f/32.0f) / (30.0f/32.0f); + } + + // we need to leave a lot of clamp range + s *= 8; + + if ( s > 1.0 ) { + s = 1.0; + } + + d = tr.fogTable[ (int)(s * (FOG_TABLE_SIZE-1)) ]; + + return d; +} + +/* +================ +R_CreateFogImage +================ +*/ +#define FOG_S 256 +#define FOG_T 32 +static void R_CreateFogImage( void ) { + int x,y; + byte *data; + float d; + float borderColor[4]; + + data = ri.Hunk_AllocateTempMemory( FOG_S * FOG_T * 4 ); + + // S is distance, T is depth + for (x=0 ; xinteger >= 2) + { + for( x = 0; x < MAX_DLIGHTS; x++) + { + tr.shadowCubemaps[x] = R_CreateImage(va("*shadowcubemap%i", x), (byte *)data, DEFAULT_SIZE, DEFAULT_SIZE, IMGTYPE_COLORALPHA, IMGFLAG_CLAMPTOEDGE | IMGFLAG_CUBEMAP, 0); + } + } + + // with overbright bits active, we need an image which is some fraction of full color, + // for default lightmaps, etc + for (x=0 ; xinteger && glRefConfig.framebufferObject && glRefConfig.textureFloat) + hdrFormat = GL_RGB16F_ARB; + + tr.renderImage = R_CreateImage("_render", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, hdrFormat); + + if (r_drawSunRays->integer) + tr.sunRaysImage = R_CreateImage("*sunRays", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_RGBA8); + + if (r_softOverbright->integer) + { + int format; + + format = GL_RGBA8; + + tr.screenScratchImage = R_CreateImage("*screenScratch", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, format); + } + + if (glRefConfig.framebufferObject) + { + tr.renderDepthImage = R_CreateImage("*renderdepth", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_DEPTH_COMPONENT24_ARB); + tr.textureDepthImage = R_CreateImage("*texturedepth", NULL, PSHADOW_MAP_SIZE, PSHADOW_MAP_SIZE, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_DEPTH_COMPONENT24_ARB); + } + + { + unsigned short sdata[4]; + void *p; + + if (hdrFormat == GL_RGB16F_ARB) + { + sdata[0] = FloatToHalf(0.0f); + sdata[1] = FloatToHalf(0.45f); + sdata[2] = FloatToHalf(1.0f); + sdata[3] = FloatToHalf(1.0f); + p = &sdata[0]; + } + else + { + data[0][0][0] = 0; + data[0][0][1] = 0.45f * 255; + data[0][0][2] = 255; + data[0][0][3] = 255; + p = data; + } + + tr.calcLevelsImage = R_CreateImage("*calcLevels", p, 1, 1, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, hdrFormat); + tr.targetLevelsImage = R_CreateImage("*targetLevels", p, 1, 1, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, hdrFormat); + tr.fixedLevelsImage = R_CreateImage("*fixedLevels", p, 1, 1, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, hdrFormat); + } + + for (x = 0; x < 2; x++) + { + tr.textureScratchImage[x] = R_CreateImage(va("*textureScratch%d", x), NULL, 256, 256, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_RGBA8); + } + for (x = 0; x < 2; x++) + { + tr.quarterImage[x] = R_CreateImage(va("*quarter%d", x), NULL, width / 2, height / 2, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_RGBA8); + } + + tr.screenShadowImage = R_CreateImage("*screenShadow", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_RGBA8); + + if (r_ssao->integer) + { + tr.screenSsaoImage = R_CreateImage("*screenSsao", NULL, width / 2, height / 2, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_RGBA8); + tr.hdrDepthImage = R_CreateImage("*hdrDepth", NULL, width, height, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_INTENSITY32F_ARB); + } + + for( x = 0; x < MAX_DRAWN_PSHADOWS; x++) + { + tr.pshadowMaps[x] = R_CreateImage(va("*shadowmap%i", x), NULL, PSHADOW_MAP_SIZE, PSHADOW_MAP_SIZE, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_RGBA8); + } + + for ( x = 0; x < 3; x++) + { + tr.sunShadowDepthImage[x] = R_CreateImage(va("*sunshadowdepth%i", x), NULL, r_shadowMapSize->integer, r_shadowMapSize->integer, IMGTYPE_COLORALPHA, IMGFLAG_NO_COMPRESSION | IMGFLAG_CLAMPTOEDGE, GL_DEPTH_COMPONENT24_ARB); + } + } +} + + +/* +=============== +R_SetColorMappings +=============== +*/ +void R_SetColorMappings( void ) { + int i, j; + float g; + int inf; + int shift; + + // setup the overbright lighting + tr.overbrightBits = r_overBrightBits->integer; + if ( !glConfig.deviceSupportsGamma ) { + tr.overbrightBits = 0; // need hardware gamma for overbright + } + + // never overbright in windowed mode without soft overbright + if ( !glConfig.isFullscreen && !r_softOverbright->integer ) + { + tr.overbrightBits = 0; + } + + // never overbright with tonemapping + if ( r_toneMap->integer && r_hdr->integer ) + { + tr.overbrightBits = 0; + } + + // allow 2 overbright bits in 24 bit, but only 1 in 16 bit + if ( glConfig.colorBits > 16 ) { + if ( tr.overbrightBits > 2 ) { + tr.overbrightBits = 2; + } + } else { + if ( tr.overbrightBits > 1 ) { + tr.overbrightBits = 1; + } + } + if ( tr.overbrightBits < 0 ) { + tr.overbrightBits = 0; + } + + tr.identityLight = 1.0f / ( 1 << tr.overbrightBits ); + tr.identityLightByte = 255 * tr.identityLight; + + + if ( r_intensity->value <= 1 ) { + ri.Cvar_Set( "r_intensity", "1" ); + } + + if ( r_gamma->value < 0.5f ) { + ri.Cvar_Set( "r_gamma", "0.5" ); + } else if ( r_gamma->value > 3.0f ) { + ri.Cvar_Set( "r_gamma", "3.0" ); + } + + g = r_gamma->value; + + shift = tr.overbrightBits; + + // no shift with soft overbright + if (r_softOverbright->integer) + { + shift = 0; + } + + for ( i = 0; i < 256; i++ ) { + int i2; + + if (r_srgb->integer) + { + i2 = 255 * RGBtosRGB(i/255.0f) + 0.5f; + } + else + { + i2 = i; + } + + if ( g == 1 ) { + inf = i2; + } else { + inf = 255 * pow ( i2/255.0f, 1.0f / g ) + 0.5f; + } + inf <<= shift; + if (inf < 0) { + inf = 0; + } + if (inf > 255) { + inf = 255; + } + s_gammatable[i] = inf; + } + + for (i=0 ; i<256 ; i++) { + j = i * r_intensity->value; + if (j > 255) { + j = 255; + } + s_intensitytable[i] = j; + } + + if ( glConfig.deviceSupportsGamma ) + { + GLimp_SetGamma( s_gammatable, s_gammatable, s_gammatable ); + } +} + +/* +=============== +R_InitImages +=============== +*/ +void R_InitImages( void ) { + Com_Memset(hashTable, 0, sizeof(hashTable)); + // build brightness translation tables + R_SetColorMappings(); + + // create default texture and white texture + R_CreateBuiltinImages(); +} + +/* +=============== +R_DeleteTextures +=============== +*/ +void R_DeleteTextures( void ) { + int i; + + for ( i=0; itexnum ); + } + Com_Memset( tr.images, 0, sizeof( tr.images ) ); + + tr.numImages = 0; + + Com_Memset( glState.currenttextures, 0, sizeof( glState.currenttextures ) ); + if ( qglActiveTextureARB ) { + GL_SelectTexture( 1 ); + qglBindTexture( GL_TEXTURE_2D, 0 ); + GL_SelectTexture( 0 ); + qglBindTexture( GL_TEXTURE_2D, 0 ); + } else { + qglBindTexture( GL_TEXTURE_2D, 0 ); + } +} + +/* +============================================================================ + +SKINS + +============================================================================ +*/ + +/* +================== +CommaParse + +This is unfortunate, but the skin files aren't +compatable with our normal parsing rules. +================== +*/ +static char *CommaParse( char **data_p ) { + int c = 0, len; + char *data; + static char com_token[MAX_TOKEN_CHARS]; + + data = *data_p; + len = 0; + com_token[0] = 0; + + // make sure incoming data is valid + if ( !data ) { + *data_p = NULL; + return com_token; + } + + while ( 1 ) { + // skip whitespace + while( (c = *data) <= ' ') { + if( !c ) { + break; + } + data++; + } + + + c = *data; + + // skip double slash comments + if ( c == '/' && data[1] == '/' ) + { + while (*data && *data != '\n') + data++; + } + // skip /* */ comments + else if ( c=='/' && data[1] == '*' ) + { + while ( *data && ( *data != '*' || data[1] != '/' ) ) + { + data++; + } + if ( *data ) + { + data += 2; + } + } + else + { + break; + } + } + + if ( c == 0 ) { + return ""; + } + + // handle quoted strings + if (c == '\"') + { + data++; + while (1) + { + c = *data++; + if (c=='\"' || !c) + { + com_token[len] = 0; + *data_p = ( char * ) data; + return com_token; + } + if (len < MAX_TOKEN_CHARS) + { + com_token[len] = c; + len++; + } + } + } + + // parse a regular word + do + { + if (len < MAX_TOKEN_CHARS) + { + com_token[len] = c; + len++; + } + data++; + c = *data; + } while (c>32 && c != ',' ); + + if (len == MAX_TOKEN_CHARS) + { +// ri.Printf (PRINT_DEVELOPER, "Token exceeded %i chars, discarded.\n", MAX_TOKEN_CHARS); + len = 0; + } + com_token[len] = 0; + + *data_p = ( char * ) data; + return com_token; +} + + +/* +=============== +RE_RegisterSkin + +=============== +*/ +qhandle_t RE_RegisterSkin( const char *name ) { + qhandle_t hSkin; + skin_t *skin; + skinSurface_t *surf; + union { + char *c; + void *v; + } text; + char *text_p; + char *token; + char surfName[MAX_QPATH]; + + if ( !name || !name[0] ) { + ri.Printf( PRINT_DEVELOPER, "Empty name passed to RE_RegisterSkin\n" ); + return 0; + } + + if ( strlen( name ) >= MAX_QPATH ) { + ri.Printf( PRINT_DEVELOPER, "Skin name exceeds MAX_QPATH\n" ); + return 0; + } + + + // see if the skin is already loaded + for ( hSkin = 1; hSkin < tr.numSkins ; hSkin++ ) { + skin = tr.skins[hSkin]; + if ( !Q_stricmp( skin->name, name ) ) { + if( skin->numSurfaces == 0 ) { + return 0; // default skin + } + return hSkin; + } + } + + // allocate a new skin + if ( tr.numSkins == MAX_SKINS ) { + ri.Printf( PRINT_WARNING, "WARNING: RE_RegisterSkin( '%s' ) MAX_SKINS hit\n", name ); + return 0; + } + tr.numSkins++; + skin = ri.Hunk_Alloc( sizeof( skin_t ), h_low ); + tr.skins[hSkin] = skin; + Q_strncpyz( skin->name, name, sizeof( skin->name ) ); + skin->numSurfaces = 0; + + R_IssuePendingRenderCommands(); + + // If not a .skin file, load as a single shader + if ( strcmp( name + strlen( name ) - 5, ".skin" ) ) { + skin->numSurfaces = 1; + skin->surfaces[0] = ri.Hunk_Alloc( sizeof(skin->surfaces[0]), h_low ); + skin->surfaces[0]->shader = R_FindShader( name, LIGHTMAP_NONE, qtrue ); + return hSkin; + } + + // load and parse the skin file + ri.FS_ReadFile( name, &text.v ); + if ( !text.c ) { + return 0; + } + + text_p = text.c; + while ( text_p && *text_p ) { + // get surface name + token = CommaParse( &text_p ); + Q_strncpyz( surfName, token, sizeof( surfName ) ); + + if ( !token[0] ) { + break; + } + // lowercase the surface name so skin compares are faster + Q_strlwr( surfName ); + + if ( *text_p == ',' ) { + text_p++; + } + + if ( strstr( token, "tag_" ) ) { + continue; + } + + // parse the shader name + token = CommaParse( &text_p ); + + surf = skin->surfaces[ skin->numSurfaces ] = ri.Hunk_Alloc( sizeof( *skin->surfaces[0] ), h_low ); + Q_strncpyz( surf->name, surfName, sizeof( surf->name ) ); + surf->shader = R_FindShader( token, LIGHTMAP_NONE, qtrue ); + skin->numSurfaces++; + } + + ri.FS_FreeFile( text.v ); + + + // never let a skin have 0 shaders + if ( skin->numSurfaces == 0 ) { + return 0; // use default skin + } + + return hSkin; +} + + +/* +=============== +R_InitSkins +=============== +*/ +void R_InitSkins( void ) { + skin_t *skin; + + tr.numSkins = 1; + + // make the default skin have all default shaders + skin = tr.skins[0] = ri.Hunk_Alloc( sizeof( skin_t ), h_low ); + Q_strncpyz( skin->name, "", sizeof( skin->name ) ); + skin->numSurfaces = 1; + skin->surfaces[0] = ri.Hunk_Alloc( sizeof( *skin->surfaces ), h_low ); + skin->surfaces[0]->shader = tr.defaultShader; +} + +/* +=============== +R_GetSkinByHandle +=============== +*/ +skin_t *R_GetSkinByHandle( qhandle_t hSkin ) { + if ( hSkin < 1 || hSkin >= tr.numSkins ) { + return tr.skins[0]; + } + return tr.skins[ hSkin ]; +} + +/* +=============== +R_SkinList_f +=============== +*/ +void R_SkinList_f( void ) { + int i, j; + skin_t *skin; + + ri.Printf (PRINT_ALL, "------------------\n"); + + for ( i = 0 ; i < tr.numSkins ; i++ ) { + skin = tr.skins[i]; + + ri.Printf( PRINT_ALL, "%3i:%s\n", i, skin->name ); + for ( j = 0 ; j < skin->numSurfaces ; j++ ) { + ri.Printf( PRINT_ALL, " %s = %s\n", + skin->surfaces[j]->name, skin->surfaces[j]->shader->name ); + } + } + ri.Printf (PRINT_ALL, "------------------\n"); +} + + diff --git a/src/renderergl2/tr_image_bmp.c b/src/renderergl2/tr_image_bmp.c new file mode 100644 index 00000000..87ea9e4a --- /dev/null +++ b/src/renderergl2/tr_image_bmp.c @@ -0,0 +1,243 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qfiles.h" +#include "../qcommon/qcommon.h" +#include "../renderercommon/tr_public.h" +extern refimport_t ri; + +typedef struct +{ + char id[2]; + unsigned fileSize; + unsigned reserved0; + unsigned bitmapDataOffset; + unsigned bitmapHeaderSize; + unsigned width; + unsigned height; + unsigned short planes; + unsigned short bitsPerPixel; + unsigned compression; + unsigned bitmapDataSize; + unsigned hRes; + unsigned vRes; + unsigned colors; + unsigned importantColors; + unsigned char palette[256][4]; +} BMPHeader_t; + +void R_LoadBMP( const char *name, byte **pic, int *width, int *height ) +{ + int columns, rows; + unsigned numPixels; + byte *pixbuf; + int row, column; + byte *buf_p; + byte *end; + union { + byte *b; + void *v; + } buffer; + int length; + BMPHeader_t bmpHeader; + byte *bmpRGBA; + + *pic = NULL; + + if(width) + *width = 0; + + if(height) + *height = 0; + + // + // load the file + // + length = ri.FS_ReadFile( ( char * ) name, &buffer.v); + if (!buffer.b || length < 0) { + return; + } + + if (length < 54) + { + ri.Error( ERR_DROP, "LoadBMP: header too short (%s)", name ); + } + + buf_p = buffer.b; + end = buffer.b + length; + + bmpHeader.id[0] = *buf_p++; + bmpHeader.id[1] = *buf_p++; + bmpHeader.fileSize = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.reserved0 = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.bitmapDataOffset = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.bitmapHeaderSize = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.width = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.height = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.planes = LittleShort( * ( short * ) buf_p ); + buf_p += 2; + bmpHeader.bitsPerPixel = LittleShort( * ( short * ) buf_p ); + buf_p += 2; + bmpHeader.compression = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.bitmapDataSize = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.hRes = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.vRes = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.colors = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + bmpHeader.importantColors = LittleLong( * ( int * ) buf_p ); + buf_p += 4; + + if ( bmpHeader.bitsPerPixel == 8 ) + { + if (buf_p + sizeof(bmpHeader.palette) > end) + ri.Error( ERR_DROP, "LoadBMP: header too short (%s)", name ); + + Com_Memcpy( bmpHeader.palette, buf_p, sizeof( bmpHeader.palette ) ); + buf_p += sizeof(bmpHeader.palette); + } + + if (buffer.b + bmpHeader.bitmapDataOffset > end) + { + ri.Error( ERR_DROP, "LoadBMP: invalid offset value in header (%s)", name ); + } + + buf_p = buffer.b + bmpHeader.bitmapDataOffset; + + if ( bmpHeader.id[0] != 'B' && bmpHeader.id[1] != 'M' ) + { + ri.Error( ERR_DROP, "LoadBMP: only Windows-style BMP files supported (%s)", name ); + } + if ( bmpHeader.fileSize != length ) + { + ri.Error( ERR_DROP, "LoadBMP: header size does not match file size (%u vs. %u) (%s)", bmpHeader.fileSize, length, name ); + } + if ( bmpHeader.compression != 0 ) + { + ri.Error( ERR_DROP, "LoadBMP: only uncompressed BMP files supported (%s)", name ); + } + if ( bmpHeader.bitsPerPixel < 8 ) + { + ri.Error( ERR_DROP, "LoadBMP: monochrome and 4-bit BMP files not supported (%s)", name ); + } + + switch ( bmpHeader.bitsPerPixel ) + { + case 8: + case 16: + case 24: + case 32: + break; + default: + ri.Error( ERR_DROP, "LoadBMP: illegal pixel_size '%hu' in file '%s'", bmpHeader.bitsPerPixel, name ); + break; + } + + columns = bmpHeader.width; + rows = bmpHeader.height; + if ( rows < 0 ) + rows = -rows; + numPixels = columns * rows; + + if(columns <= 0 || !rows || numPixels > 0x1FFFFFFF // 4*1FFFFFFF == 0x7FFFFFFC < 0x7FFFFFFF + || ((numPixels * 4) / columns) / 4 != rows) + { + ri.Error (ERR_DROP, "LoadBMP: %s has an invalid image size", name); + } + if(buf_p + numPixels*bmpHeader.bitsPerPixel/8 > end) + { + ri.Error (ERR_DROP, "LoadBMP: file truncated (%s)", name); + } + + if ( width ) + *width = columns; + if ( height ) + *height = rows; + + bmpRGBA = ri.Malloc( numPixels * 4 ); + *pic = bmpRGBA; + + + for ( row = rows-1; row >= 0; row-- ) + { + pixbuf = bmpRGBA + row*columns*4; + + for ( column = 0; column < columns; column++ ) + { + unsigned char red, green, blue, alpha; + int palIndex; + unsigned short shortPixel; + + switch ( bmpHeader.bitsPerPixel ) + { + case 8: + palIndex = *buf_p++; + *pixbuf++ = bmpHeader.palette[palIndex][2]; + *pixbuf++ = bmpHeader.palette[palIndex][1]; + *pixbuf++ = bmpHeader.palette[palIndex][0]; + *pixbuf++ = 0xff; + break; + case 16: + shortPixel = * ( unsigned short * ) pixbuf; + pixbuf += 2; + *pixbuf++ = ( shortPixel & ( 31 << 10 ) ) >> 7; + *pixbuf++ = ( shortPixel & ( 31 << 5 ) ) >> 2; + *pixbuf++ = ( shortPixel & ( 31 ) ) << 3; + *pixbuf++ = 0xff; + break; + + case 24: + blue = *buf_p++; + green = *buf_p++; + red = *buf_p++; + *pixbuf++ = red; + *pixbuf++ = green; + *pixbuf++ = blue; + *pixbuf++ = 255; + break; + case 32: + blue = *buf_p++; + green = *buf_p++; + red = *buf_p++; + alpha = *buf_p++; + *pixbuf++ = red; + *pixbuf++ = green; + *pixbuf++ = blue; + *pixbuf++ = alpha; + break; + } + } + } + + ri.FS_FreeFile( buffer.v ); + +} diff --git a/src/renderergl2/tr_image_jpg.c b/src/renderergl2/tr_image_jpg.c new file mode 100644 index 00000000..fb769263 --- /dev/null +++ b/src/renderergl2/tr_image_jpg.c @@ -0,0 +1,441 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qfiles.h" +#include "../qcommon/qcommon.h" +#include "../renderercommon/tr_public.h" +extern refimport_t ri; + +/* + * Include file for users of JPEG library. + * You will need to have included system headers that define at least + * the typedefs FILE and size_t before you can include jpeglib.h. + * (stdio.h is sufficient on ANSI-conforming systems.) + * You may also wish to include "jerror.h". + */ + +#ifdef USE_INTERNAL_JPEG +# define JPEG_INTERNALS +#endif + +#include + +#ifndef USE_INTERNAL_JPEG +# if JPEG_LIB_VERSION < 80 +# error Need system libjpeg >= 80 +# endif +#endif + +static void R_JPGErrorExit(j_common_ptr cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + + (*cinfo->err->format_message) (cinfo, buffer); + + /* Let the memory manager delete any temp files before we die */ + jpeg_destroy(cinfo); + + ri.Error(ERR_FATAL, "%s", buffer); +} + +static void R_JPGOutputMessage(j_common_ptr cinfo) +{ + char buffer[JMSG_LENGTH_MAX]; + + /* Create the message */ + (*cinfo->err->format_message) (cinfo, buffer); + + /* Send it to stderr, adding a newline */ + ri.Printf(PRINT_ALL, "%s\n", buffer); +} + +void R_LoadJPG(const char *filename, unsigned char **pic, int *width, int *height) +{ + /* This struct contains the JPEG decompression parameters and pointers to + * working space (which is allocated as needed by the JPEG library). + */ + struct jpeg_decompress_struct cinfo = {NULL}; + /* We use our private extension JPEG error handler. + * Note that this struct must live as long as the main JPEG parameter + * struct, to avoid dangling-pointer problems. + */ + /* This struct represents a JPEG error handler. It is declared separately + * because applications often want to supply a specialized error handler + * (see the second half of this file for an example). But here we just + * take the easy way out and use the standard error handler, which will + * print a message on stderr and call exit() if compression fails. + * Note that this struct must live as long as the main JPEG parameter + * struct, to avoid dangling-pointer problems. + */ + struct jpeg_error_mgr jerr; + /* More stuff */ + JSAMPARRAY buffer; /* Output row buffer */ + unsigned int row_stride; /* physical row width in output buffer */ + unsigned int pixelcount, memcount; + unsigned int sindex, dindex; + byte *out; + int len; + union { + byte *b; + void *v; + } fbuffer; + byte *buf; + + /* In this example we want to open the input file before doing anything else, + * so that the setjmp() error recovery below can assume the file is open. + * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that + * requires it in order to read binary files. + */ + + len = ri.FS_ReadFile ( ( char * ) filename, &fbuffer.v); + if (!fbuffer.b || len < 0) { + return; + } + + /* Step 1: allocate and initialize JPEG decompression object */ + + /* We have to set up the error handler first, in case the initialization + * step fails. (Unlikely, but it could happen if you are out of memory.) + * This routine fills in the contents of struct jerr, and returns jerr's + * address which we place into the link field in cinfo. + */ + cinfo.err = jpeg_std_error(&jerr); + cinfo.err->error_exit = R_JPGErrorExit; + cinfo.err->output_message = R_JPGOutputMessage; + + /* Now we can initialize the JPEG decompression object. */ + jpeg_create_decompress(&cinfo); + + /* Step 2: specify data source (eg, a file) */ + + jpeg_mem_src(&cinfo, fbuffer.b, len); + + /* Step 3: read file parameters with jpeg_read_header() */ + + (void) jpeg_read_header(&cinfo, TRUE); + /* We can ignore the return value from jpeg_read_header since + * (a) suspension is not possible with the stdio data source, and + * (b) we passed TRUE to reject a tables-only JPEG file as an error. + * See libjpeg.doc for more info. + */ + + /* Step 4: set parameters for decompression */ + + /* + * Make sure it always converts images to RGB color space. This will + * automatically convert 8-bit greyscale images to RGB as well. + */ + cinfo.out_color_space = JCS_RGB; + + /* Step 5: Start decompressor */ + + (void) jpeg_start_decompress(&cinfo); + /* We can ignore the return value since suspension is not possible + * with the stdio data source. + */ + + /* We may need to do some setup of our own at this point before reading + * the data. After jpeg_start_decompress() we have the correct scaled + * output image dimensions available, as well as the output colormap + * if we asked for color quantization. + * In this example, we need to make an output work buffer of the right size. + */ + /* JSAMPLEs per row in output buffer */ + + pixelcount = cinfo.output_width * cinfo.output_height; + + if(!cinfo.output_width || !cinfo.output_height + || ((pixelcount * 4) / cinfo.output_width) / 4 != cinfo.output_height + || pixelcount > 0x1FFFFFFF || cinfo.output_components != 3 + ) + { + // Free the memory to make sure we don't leak memory + ri.FS_FreeFile (fbuffer.v); + jpeg_destroy_decompress(&cinfo); + + ri.Error(ERR_DROP, "LoadJPG: %s has an invalid image format: %dx%d*4=%d, components: %d", filename, + cinfo.output_width, cinfo.output_height, pixelcount * 4, cinfo.output_components); + } + + memcount = pixelcount * 4; + row_stride = cinfo.output_width * cinfo.output_components; + + out = ri.Malloc(memcount); + + *width = cinfo.output_width; + *height = cinfo.output_height; + + /* Step 6: while (scan lines remain to be read) */ + /* jpeg_read_scanlines(...); */ + + /* Here we use the library's state variable cinfo.output_scanline as the + * loop counter, so that we don't have to keep track ourselves. + */ + while (cinfo.output_scanline < cinfo.output_height) { + /* jpeg_read_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could ask for + * more than one scanline at a time if that's more convenient. + */ + buf = ((out+(row_stride*cinfo.output_scanline))); + buffer = &buf; + (void) jpeg_read_scanlines(&cinfo, buffer, 1); + } + + buf = out; + + // Expand from RGB to RGBA + sindex = pixelcount * cinfo.output_components; + dindex = memcount; + + do + { + buf[--dindex] = 255; + buf[--dindex] = buf[--sindex]; + buf[--dindex] = buf[--sindex]; + buf[--dindex] = buf[--sindex]; + } while(sindex); + + *pic = out; + + /* Step 7: Finish decompression */ + + jpeg_finish_decompress(&cinfo); + /* We can ignore the return value since suspension is not possible + * with the stdio data source. + */ + + /* Step 8: Release JPEG decompression object */ + + /* This is an important step since it will release a good deal of memory. */ + jpeg_destroy_decompress(&cinfo); + + /* After finish_decompress, we can close the input file. + * Here we postpone it until after no more JPEG errors are possible, + * so as to simplify the setjmp error logic above. (Actually, I don't + * think that jpeg_destroy can do an error exit, but why assume anything...) + */ + ri.FS_FreeFile (fbuffer.v); + + /* At this point you may want to check to see whether any corrupt-data + * warnings occurred (test whether jerr.pub.num_warnings is nonzero). + */ + + /* And we're done! */ +} + + +/* Expanded data destination object for stdio output */ + +typedef struct { + struct jpeg_destination_mgr pub; /* public fields */ + + byte* outfile; /* target stream */ + int size; +} my_destination_mgr; + +typedef my_destination_mgr * my_dest_ptr; + + +/* + * Initialize destination --- called by jpeg_start_compress + * before any data is actually written. + */ + +static void +init_destination (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + dest->pub.next_output_byte = dest->outfile; + dest->pub.free_in_buffer = dest->size; +} + + +/* + * Empty the output buffer --- called whenever buffer fills up. + * + * In typical applications, this should write the entire output buffer + * (ignoring the current state of next_output_byte & free_in_buffer), + * reset the pointer & count to the start of the buffer, and return TRUE + * indicating that the buffer has been dumped. + * + * In applications that need to be able to suspend compression due to output + * overrun, a FALSE return indicates that the buffer cannot be emptied now. + * In this situation, the compressor will return to its caller (possibly with + * an indication that it has not accepted all the supplied scanlines). The + * application should resume compression after it has made more room in the + * output buffer. Note that there are substantial restrictions on the use of + * suspension --- see the documentation. + * + * When suspending, the compressor will back up to a convenient restart point + * (typically the start of the current MCU). next_output_byte & free_in_buffer + * indicate where the restart point will be if the current call returns FALSE. + * Data beyond this point will be regenerated after resumption, so do not + * write it out when emptying the buffer externally. + */ + +static boolean +empty_output_buffer (j_compress_ptr cinfo) +{ + my_dest_ptr dest = (my_dest_ptr) cinfo->dest; + + jpeg_destroy_compress(cinfo); + + // Make crash fatal or we would probably leak memory. + ri.Error(ERR_FATAL, "Output buffer for encoded JPEG image has insufficient size of %d bytes", + dest->size); + + return FALSE; +} + +/* + * Terminate destination --- called by jpeg_finish_compress + * after all data has been written. Usually needs to flush buffer. + * + * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding + * application must deal with any cleanup that should happen even + * for error exit. + */ + +static void term_destination(j_compress_ptr cinfo) +{ +} + + +/* + * Prepare for output to a stdio stream. + * The caller must have already opened the stream, and is responsible + * for closing it after finishing compression. + */ + +static void +jpegDest (j_compress_ptr cinfo, byte* outfile, int size) +{ + my_dest_ptr dest; + + /* The destination object is made permanent so that multiple JPEG images + * can be written to the same file without re-executing jpeg_stdio_dest. + * This makes it dangerous to use this manager and a different destination + * manager serially with the same JPEG object, because their private object + * sizes may be different. Caveat programmer. + */ + if (cinfo->dest == NULL) { /* first time for this JPEG object? */ + cinfo->dest = (struct jpeg_destination_mgr *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + sizeof(my_destination_mgr)); + } + + dest = (my_dest_ptr) cinfo->dest; + dest->pub.init_destination = init_destination; + dest->pub.empty_output_buffer = empty_output_buffer; + dest->pub.term_destination = term_destination; + dest->outfile = outfile; + dest->size = size; +} + +/* +================= +SaveJPGToBuffer + +Encodes JPEG from image in image_buffer and writes to buffer. +Expects RGB input data +================= +*/ +size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality, + int image_width, int image_height, byte *image_buffer, int padding) +{ + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */ + my_dest_ptr dest; + int row_stride; /* physical row width in image buffer */ + size_t outcount; + + /* Step 1: allocate and initialize JPEG compression object */ + cinfo.err = jpeg_std_error(&jerr); + cinfo.err->error_exit = R_JPGErrorExit; + cinfo.err->output_message = R_JPGOutputMessage; + + /* Now we can initialize the JPEG compression object. */ + jpeg_create_compress(&cinfo); + + /* Step 2: specify data destination (eg, a file) */ + /* Note: steps 2 and 3 can be done in either order. */ + jpegDest(&cinfo, buffer, bufSize); + + /* Step 3: set parameters for compression */ + cinfo.image_width = image_width; /* image width and height, in pixels */ + cinfo.image_height = image_height; + cinfo.input_components = 3; /* # of color components per pixel */ + cinfo.in_color_space = JCS_RGB; /* colorspace of input image */ + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */); + /* If quality is set high, disable chroma subsampling */ + if (quality >= 85) { + cinfo.comp_info[0].h_samp_factor = 1; + cinfo.comp_info[0].v_samp_factor = 1; + } + + /* Step 4: Start compressor */ + jpeg_start_compress(&cinfo, TRUE); + + /* Step 5: while (scan lines remain to be written) */ + /* jpeg_write_scanlines(...); */ + row_stride = image_width * cinfo.input_components + padding; /* JSAMPLEs per row in image_buffer */ + + while (cinfo.next_scanline < cinfo.image_height) { + /* jpeg_write_scanlines expects an array of pointers to scanlines. + * Here the array is only one element long, but you could pass + * more than one scanline at a time if that's more convenient. + */ + row_pointer[0] = &image_buffer[((cinfo.image_height-1)*row_stride)-cinfo.next_scanline * row_stride]; + (void) jpeg_write_scanlines(&cinfo, row_pointer, 1); + } + + /* Step 6: Finish compression */ + jpeg_finish_compress(&cinfo); + + dest = (my_dest_ptr) cinfo.dest; + outcount = dest->size - dest->pub.free_in_buffer; + + /* Step 7: release JPEG compression object */ + jpeg_destroy_compress(&cinfo); + + /* And we're done! */ + return outcount; +} + +void RE_SaveJPG(char * filename, int quality, int image_width, int image_height, byte *image_buffer, int padding) +{ + byte *out; + size_t bufSize; + + bufSize = image_width * image_height * 3; + out = ri.Hunk_AllocateTempMemory(bufSize); + + bufSize = RE_SaveJPGToBuffer(out, bufSize, quality, image_width, image_height, image_buffer, padding); + ri.FS_WriteFile(filename, out, bufSize); + + ri.Hunk_FreeTempMemory(out); +} diff --git a/src/renderergl2/tr_image_pcx.c b/src/renderergl2/tr_image_pcx.c new file mode 100644 index 00000000..5e1de51b --- /dev/null +++ b/src/renderergl2/tr_image_pcx.c @@ -0,0 +1,179 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + 2008 Ludwig Nussel + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qfiles.h" +#include "../qcommon/qcommon.h" +#include "../renderercommon/tr_public.h" +extern refimport_t ri; + +/* +======================================================================== + +PCX files are used for 8 bit images + +======================================================================== +*/ + +typedef struct { + char manufacturer; + char version; + char encoding; + char bits_per_pixel; + unsigned short xmin,ymin,xmax,ymax; + unsigned short hres,vres; + unsigned char palette[48]; + char reserved; + char color_planes; + unsigned short bytes_per_line; + unsigned short palette_type; + unsigned short hscreensize, vscreensize; + char filler[54]; + unsigned char data[]; +} pcx_t; + +void R_LoadPCX ( const char *filename, byte **pic, int *width, int *height) +{ + union { + byte *b; + void *v; + } raw; + byte *end; + pcx_t *pcx; + int len; + unsigned char dataByte = 0, runLength = 0; + byte *out, *pix; + unsigned short w, h; + byte *pic8; + byte *palette; + int i; + unsigned size = 0; + + if (width) + *width = 0; + if (height) + *height = 0; + *pic = NULL; + + // + // load the file + // + len = ri.FS_ReadFile( ( char * ) filename, &raw.v); + if (!raw.b || len < 0) { + return; + } + + if((unsigned)len < sizeof(pcx_t)) + { + ri.Printf (PRINT_ALL, "PCX truncated: %s\n", filename); + ri.FS_FreeFile (raw.v); + return; + } + + // + // parse the PCX file + // + pcx = (pcx_t *)raw.b; + end = raw.b+len; + + w = LittleShort(pcx->xmax)+1; + h = LittleShort(pcx->ymax)+1; + size = w*h; + + if (pcx->manufacturer != 0x0a + || pcx->version != 5 + || pcx->encoding != 1 + || pcx->color_planes != 1 + || pcx->bits_per_pixel != 8 + || w >= 1024 + || h >= 1024) + { + ri.Printf (PRINT_ALL, "Bad or unsupported pcx file %s (%dx%d@%d)\n", filename, w, h, pcx->bits_per_pixel); + return; + } + + pix = pic8 = ri.Malloc ( size ); + + raw.b = pcx->data; + // FIXME: should use bytes_per_line but original q3 didn't do that either + while(pix < pic8+size) + { + if(runLength > 0) { + *pix++ = dataByte; + --runLength; + continue; + } + + if(raw.b+1 > end) + break; + dataByte = *raw.b++; + + if((dataByte & 0xC0) == 0xC0) + { + if(raw.b+1 > end) + break; + runLength = dataByte & 0x3F; + dataByte = *raw.b++; + } + else + runLength = 1; + } + + if(pix < pic8+size) + { + ri.Printf (PRINT_ALL, "PCX file truncated: %s\n", filename); + ri.FS_FreeFile (pcx); + ri.Free (pic8); + } + + if (raw.b-(byte*)pcx >= end - (byte*)769 || end[-769] != 0x0c) + { + ri.Printf (PRINT_ALL, "PCX missing palette: %s\n", filename); + ri.FS_FreeFile (pcx); + ri.Free (pic8); + return; + } + + palette = end-768; + + pix = out = ri.Malloc(4 * size ); + for (i = 0 ; i < size ; i++) + { + unsigned char p = pic8[i]; + pix[0] = palette[p*3]; + pix[1] = palette[p*3 + 1]; + pix[2] = palette[p*3 + 2]; + pix[3] = 255; + pix += 4; + } + + if (width) + *width = w; + if (height) + *height = h; + + *pic = out; + + ri.FS_FreeFile (pcx); + ri.Free (pic8); +} diff --git a/src/renderergl2/tr_image_png.c b/src/renderergl2/tr_image_png.c new file mode 100644 index 00000000..a0729c39 --- /dev/null +++ b/src/renderergl2/tr_image_png.c @@ -0,0 +1,2490 @@ +/* +=========================================================================== +ioquake3 png decoder +Copyright (C) 2007,2008 Joerg Dietrich + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +=========================================================================== +*/ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qfiles.h" +#include "../qcommon/qcommon.h" +#include "../renderercommon/tr_public.h" +extern refimport_t ri; + +#include "../qcommon/puff.h" + +// we could limit the png size to a lower value here +#ifndef INT_MAX +#define INT_MAX 0x1fffffff +#endif + +/* +================= +PNG LOADING +================= +*/ + +/* + * Quake 3 image format : RGBA + */ + +#define Q3IMAGE_BYTESPERPIXEL (4) + +/* + * PNG specifications + */ + +/* + * The first 8 Bytes of every PNG-File are a fixed signature + * to identify the file as a PNG. + */ + +#define PNG_Signature "\x89\x50\x4E\x47\xD\xA\x1A\xA" +#define PNG_Signature_Size (8) + +/* + * After the signature diverse chunks follow. + * A chunk consists of a header and if Length + * is bigger than 0 a body and a CRC of the body follow. + */ + +struct PNG_ChunkHeader +{ + uint32_t Length; + uint32_t Type; +}; + +#define PNG_ChunkHeader_Size (8) + +typedef uint32_t PNG_ChunkCRC; + +#define PNG_ChunkCRC_Size (4) + +/* + * We use the following ChunkTypes. + * All others are ignored. + */ + +#define MAKE_CHUNKTYPE(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | ((d))) + +#define PNG_ChunkType_IHDR MAKE_CHUNKTYPE('I', 'H', 'D', 'R') +#define PNG_ChunkType_PLTE MAKE_CHUNKTYPE('P', 'L', 'T', 'E') +#define PNG_ChunkType_IDAT MAKE_CHUNKTYPE('I', 'D', 'A', 'T') +#define PNG_ChunkType_IEND MAKE_CHUNKTYPE('I', 'E', 'N', 'D') +#define PNG_ChunkType_tRNS MAKE_CHUNKTYPE('t', 'R', 'N', 'S') + +/* + * Per specification the first chunk after the signature SHALL be IHDR. + */ + +struct PNG_Chunk_IHDR +{ + uint32_t Width; + uint32_t Height; + uint8_t BitDepth; + uint8_t ColourType; + uint8_t CompressionMethod; + uint8_t FilterMethod; + uint8_t InterlaceMethod; +}; + +#define PNG_Chunk_IHDR_Size (13) + +/* + * ColourTypes + */ + +#define PNG_ColourType_Grey (0) +#define PNG_ColourType_True (2) +#define PNG_ColourType_Indexed (3) +#define PNG_ColourType_GreyAlpha (4) +#define PNG_ColourType_TrueAlpha (6) + +/* + * number of colour components + * + * Grey : 1 grey + * True : 1 R, 1 G, 1 B + * Indexed : 1 index + * GreyAlpha : 1 grey, 1 alpha + * TrueAlpha : 1 R, 1 G, 1 B, 1 alpha + */ + +#define PNG_NumColourComponents_Grey (1) +#define PNG_NumColourComponents_True (3) +#define PNG_NumColourComponents_Indexed (1) +#define PNG_NumColourComponents_GreyAlpha (2) +#define PNG_NumColourComponents_TrueAlpha (4) + +/* + * For the different ColourTypes + * different BitDepths are specified. + */ + +#define PNG_BitDepth_1 ( 1) +#define PNG_BitDepth_2 ( 2) +#define PNG_BitDepth_4 ( 4) +#define PNG_BitDepth_8 ( 8) +#define PNG_BitDepth_16 (16) + +/* + * Only one valid CompressionMethod is standardized. + */ + +#define PNG_CompressionMethod_0 (0) + +/* + * Only one valid FilterMethod is currently standardized. + */ + +#define PNG_FilterMethod_0 (0) + +/* + * This FilterMethod defines 5 FilterTypes + */ + +#define PNG_FilterType_None (0) +#define PNG_FilterType_Sub (1) +#define PNG_FilterType_Up (2) +#define PNG_FilterType_Average (3) +#define PNG_FilterType_Paeth (4) + +/* + * Two InterlaceMethods are standardized : + * 0 - NonInterlaced + * 1 - Interlaced + */ + +#define PNG_InterlaceMethod_NonInterlaced (0) +#define PNG_InterlaceMethod_Interlaced (1) + +/* + * The Adam7 interlace method uses 7 passes. + */ + +#define PNG_Adam7_NumPasses (7) + +/* + * The compressed data starts with a header ... + */ + +struct PNG_ZlibHeader +{ + uint8_t CompressionMethod; + uint8_t Flags; +}; + +#define PNG_ZlibHeader_Size (2) + +/* + * ... and is followed by a check value + */ + +#define PNG_ZlibCheckValue_Size (4) + +/* + * Some support functions for buffered files follow. + */ + +/* + * buffered file representation + */ + +struct BufferedFile +{ + byte *Buffer; + int Length; + byte *Ptr; + int BytesLeft; +}; + +/* + * Read a file into a buffer. + */ + +static struct BufferedFile *ReadBufferedFile(const char *name) +{ + struct BufferedFile *BF; + union { + byte *b; + void *v; + } buffer; + + /* + * input verification + */ + + if(!name) + { + return(NULL); + } + + /* + * Allocate control struct. + */ + + BF = ri.Malloc(sizeof(struct BufferedFile)); + if(!BF) + { + return(NULL); + } + + /* + * Initialize the structs components. + */ + + BF->Length = 0; + BF->Buffer = NULL; + BF->Ptr = NULL; + BF->BytesLeft = 0; + + /* + * Read the file. + */ + + BF->Length = ri.FS_ReadFile((char *) name, &buffer.v); + BF->Buffer = buffer.b; + + /* + * Did we get it? Is it big enough? + */ + + if(!(BF->Buffer && (BF->Length > 0))) + { + ri.Free(BF); + + return(NULL); + } + + /* + * Set the pointers and counters. + */ + + BF->Ptr = BF->Buffer; + BF->BytesLeft = BF->Length; + + return(BF); +} + +/* + * Close a buffered file. + */ + +static void CloseBufferedFile(struct BufferedFile *BF) +{ + if(BF) + { + if(BF->Buffer) + { + ri.FS_FreeFile(BF->Buffer); + } + + ri.Free(BF); + } +} + +/* + * Get a pointer to the requested bytes. + */ + +static void *BufferedFileRead(struct BufferedFile *BF, unsigned Length) +{ + void *RetVal; + + /* + * input verification + */ + + if(!(BF && Length)) + { + return(NULL); + } + + /* + * not enough bytes left + */ + + if(Length > BF->BytesLeft) + { + return(NULL); + } + + /* + * the pointer to the requested data + */ + + RetVal = BF->Ptr; + + /* + * Raise the pointer and counter. + */ + + BF->Ptr += Length; + BF->BytesLeft -= Length; + + return(RetVal); +} + +/* + * Rewind the buffer. + */ + +static qboolean BufferedFileRewind(struct BufferedFile *BF, unsigned Offset) +{ + unsigned BytesRead; + + /* + * input verification + */ + + if(!BF) + { + return(qfalse); + } + + /* + * special trick to rewind to the beginning of the buffer + */ + + if(Offset == (unsigned)-1) + { + BF->Ptr = BF->Buffer; + BF->BytesLeft = BF->Length; + + return(qtrue); + } + + /* + * How many bytes do we have already read? + */ + + BytesRead = BF->Ptr - BF->Buffer; + + /* + * We can only rewind to the beginning of the BufferedFile. + */ + + if(Offset > BytesRead) + { + return(qfalse); + } + + /* + * lower the pointer and counter. + */ + + BF->Ptr -= Offset; + BF->BytesLeft += Offset; + + return(qtrue); +} + +/* + * Skip some bytes. + */ + +static qboolean BufferedFileSkip(struct BufferedFile *BF, unsigned Offset) +{ + /* + * input verification + */ + + if(!BF) + { + return(qfalse); + } + + /* + * We can only skip to the end of the BufferedFile. + */ + + if(Offset > BF->BytesLeft) + { + return(qfalse); + } + + /* + * lower the pointer and counter. + */ + + BF->Ptr += Offset; + BF->BytesLeft -= Offset; + + return(qtrue); +} + +/* + * Find a chunk + */ + +static qboolean FindChunk(struct BufferedFile *BF, uint32_t ChunkType) +{ + struct PNG_ChunkHeader *CH; + + uint32_t Length; + uint32_t Type; + + /* + * input verification + */ + + if(!BF) + { + return(qfalse); + } + + /* + * cycle trough the chunks + */ + + while(qtrue) + { + /* + * Read the chunk-header. + */ + + CH = BufferedFileRead(BF, PNG_ChunkHeader_Size); + if(!CH) + { + return(qfalse); + } + + /* + * Do not swap the original types + * they might be needed later. + */ + + Length = BigLong(CH->Length); + Type = BigLong(CH->Type); + + /* + * We found it! + */ + + if(Type == ChunkType) + { + /* + * Rewind to the start of the chunk. + */ + + BufferedFileRewind(BF, PNG_ChunkHeader_Size); + + break; + } + else + { + /* + * Skip the rest of the chunk. + */ + + if(Length) + { + if(!BufferedFileSkip(BF, Length + PNG_ChunkCRC_Size)) + { + return(qfalse); + } + } + } + } + + return(qtrue); +} + +/* + * Decompress all IDATs + */ + +static uint32_t DecompressIDATs(struct BufferedFile *BF, uint8_t **Buffer) +{ + uint8_t *DecompressedData; + uint32_t DecompressedDataLength; + + uint8_t *CompressedData; + uint8_t *CompressedDataPtr; + uint32_t CompressedDataLength; + + struct PNG_ChunkHeader *CH; + + uint32_t Length; + uint32_t Type; + + int BytesToRewind; + + int32_t puffResult; + uint8_t *puffDest; + uint32_t puffDestLen; + uint8_t *puffSrc; + uint32_t puffSrcLen; + + /* + * input verification + */ + + if(!(BF && Buffer)) + { + return(-1); + } + + /* + * some zeroing + */ + + DecompressedData = NULL; + DecompressedDataLength = 0; + *Buffer = DecompressedData; + + CompressedData = NULL; + CompressedDataLength = 0; + + BytesToRewind = 0; + + /* + * Find the first IDAT chunk. + */ + + if(!FindChunk(BF, PNG_ChunkType_IDAT)) + { + return(-1); + } + + /* + * Count the size of the uncompressed data + */ + + while(qtrue) + { + /* + * Read chunk header + */ + + CH = BufferedFileRead(BF, PNG_ChunkHeader_Size); + if(!CH) + { + /* + * Rewind to the start of this adventure + * and return unsuccessfull + */ + + BufferedFileRewind(BF, BytesToRewind); + + return(-1); + } + + /* + * Length and Type of chunk + */ + + Length = BigLong(CH->Length); + Type = BigLong(CH->Type); + + /* + * We have reached the end of the IDAT chunks + */ + + if(!(Type == PNG_ChunkType_IDAT)) + { + BufferedFileRewind(BF, PNG_ChunkHeader_Size); + + break; + } + + /* + * Add chunk header to count. + */ + + BytesToRewind += PNG_ChunkHeader_Size; + + /* + * Skip to next chunk + */ + + if(Length) + { + if(!BufferedFileSkip(BF, Length + PNG_ChunkCRC_Size)) + { + BufferedFileRewind(BF, BytesToRewind); + + return(-1); + } + + BytesToRewind += Length + PNG_ChunkCRC_Size; + CompressedDataLength += Length; + } + } + + BufferedFileRewind(BF, BytesToRewind); + + CompressedData = ri.Malloc(CompressedDataLength); + if(!CompressedData) + { + return(-1); + } + + CompressedDataPtr = CompressedData; + + /* + * Collect the compressed Data + */ + + while(qtrue) + { + /* + * Read chunk header + */ + + CH = BufferedFileRead(BF, PNG_ChunkHeader_Size); + if(!CH) + { + ri.Free(CompressedData); + + return(-1); + } + + /* + * Length and Type of chunk + */ + + Length = BigLong(CH->Length); + Type = BigLong(CH->Type); + + /* + * We have reached the end of the IDAT chunks + */ + + if(!(Type == PNG_ChunkType_IDAT)) + { + BufferedFileRewind(BF, PNG_ChunkHeader_Size); + + break; + } + + /* + * Copy the Data + */ + + if(Length) + { + uint8_t *OrigCompressedData; + + OrigCompressedData = BufferedFileRead(BF, Length); + if(!OrigCompressedData) + { + ri.Free(CompressedData); + + return(-1); + } + + if(!BufferedFileSkip(BF, PNG_ChunkCRC_Size)) + { + ri.Free(CompressedData); + + return(-1); + } + + memcpy(CompressedDataPtr, OrigCompressedData, Length); + CompressedDataPtr += Length; + } + } + + /* + * Let puff() calculate the decompressed data length. + */ + + puffDest = NULL; + puffDestLen = 0; + + /* + * The zlib header and checkvalue don't belong to the compressed data. + */ + + puffSrc = CompressedData + PNG_ZlibHeader_Size; + puffSrcLen = CompressedDataLength - PNG_ZlibHeader_Size - PNG_ZlibCheckValue_Size; + + /* + * first puff() to calculate the size of the uncompressed data + */ + + puffResult = puff(puffDest, &puffDestLen, puffSrc, &puffSrcLen); + if(!((puffResult == 0) && (puffDestLen > 0))) + { + ri.Free(CompressedData); + + return(-1); + } + + /* + * Allocate the buffer for the uncompressed data. + */ + + DecompressedData = ri.Malloc(puffDestLen); + if(!DecompressedData) + { + ri.Free(CompressedData); + + return(-1); + } + + /* + * Set the input again in case something was changed by the last puff() . + */ + + puffDest = DecompressedData; + puffSrc = CompressedData + PNG_ZlibHeader_Size; + puffSrcLen = CompressedDataLength - PNG_ZlibHeader_Size - PNG_ZlibCheckValue_Size; + + /* + * decompression puff() + */ + + puffResult = puff(puffDest, &puffDestLen, puffSrc, &puffSrcLen); + + /* + * The compressed data is not needed anymore. + */ + + ri.Free(CompressedData); + + /* + * Check if the last puff() was successfull. + */ + + if(!((puffResult == 0) && (puffDestLen > 0))) + { + ri.Free(DecompressedData); + + return(-1); + } + + /* + * Set the output of this function. + */ + + DecompressedDataLength = puffDestLen; + *Buffer = DecompressedData; + + return(DecompressedDataLength); +} + +/* + * the Paeth predictor + */ + +static uint8_t PredictPaeth(uint8_t a, uint8_t b, uint8_t c) +{ + /* + * a == Left + * b == Up + * c == UpLeft + */ + + uint8_t Pr; + int p; + int pa, pb, pc; + + p = ((int) a) + ((int) b) - ((int) c); + pa = abs(p - ((int) a)); + pb = abs(p - ((int) b)); + pc = abs(p - ((int) c)); + + if((pa <= pb) && (pa <= pc)) + { + Pr = a; + } + else if(pb <= pc) + { + Pr = b; + } + else + { + Pr = c; + } + + return(Pr); + +} + +/* + * Reverse the filters. + */ + +static qboolean UnfilterImage(uint8_t *DecompressedData, + uint32_t ImageHeight, + uint32_t BytesPerScanline, + uint32_t BytesPerPixel) +{ + uint8_t *DecompPtr; + uint8_t FilterType; + uint8_t *PixelLeft, *PixelUp, *PixelUpLeft; + uint32_t w, h, p; + + /* + * some zeros for the filters + */ + + uint8_t Zeros[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + + /* + * input verification + */ + + if(!(DecompressedData && BytesPerPixel)) + { + return(qfalse); + } + + /* + * ImageHeight and BytesPerScanline can be zero in small interlaced images. + */ + + if((!ImageHeight) || (!BytesPerScanline)) + { + return(qtrue); + } + + /* + * Set the pointer to the start of the decompressed Data. + */ + + DecompPtr = DecompressedData; + + /* + * Un-filtering is done in place. + */ + + /* + * Go trough all scanlines. + */ + + for(h = 0; h < ImageHeight; h++) + { + /* + * Every scanline starts with a FilterType byte. + */ + + FilterType = *DecompPtr; + DecompPtr++; + + /* + * Left pixel of the first byte in a scanline is zero. + */ + + PixelLeft = Zeros; + + /* + * Set PixelUp to previous line only if we are on the second line or above. + * + * Plus one byte for the FilterType + */ + + if(h > 0) + { + PixelUp = DecompPtr - (BytesPerScanline + 1); + } + else + { + PixelUp = Zeros; + } + + /* + * The pixel left to the first pixel of the previous scanline is zero too. + */ + + PixelUpLeft = Zeros; + + /* + * Cycle trough all pixels of the scanline. + */ + + for(w = 0; w < (BytesPerScanline / BytesPerPixel); w++) + { + /* + * Cycle trough the bytes of the pixel. + */ + + for(p = 0; p < BytesPerPixel; p++) + { + switch(FilterType) + { + case PNG_FilterType_None : + { + /* + * The byte is unfiltered. + */ + + break; + } + + case PNG_FilterType_Sub : + { + DecompPtr[p] += PixelLeft[p]; + + break; + } + + case PNG_FilterType_Up : + { + DecompPtr[p] += PixelUp[p]; + + break; + } + + case PNG_FilterType_Average : + { + DecompPtr[p] += ((uint8_t) ((((uint16_t) PixelLeft[p]) + ((uint16_t) PixelUp[p])) / 2)); + + break; + } + + case PNG_FilterType_Paeth : + { + DecompPtr[p] += PredictPaeth(PixelLeft[p], PixelUp[p], PixelUpLeft[p]); + + break; + } + + default : + { + return(qfalse); + } + } + } + + PixelLeft = DecompPtr; + + /* + * We only have an upleft pixel if we are on the second line or above. + */ + + if(h > 0) + { + PixelUpLeft = DecompPtr - (BytesPerScanline + 1); + } + + /* + * Skip to the next pixel. + */ + + DecompPtr += BytesPerPixel; + + /* + * We only have a previous line if we are on the second line and above. + */ + + if(h > 0) + { + PixelUp = DecompPtr - (BytesPerScanline + 1); + } + } + } + + return(qtrue); +} + +/* + * Convert a raw input pixel to Quake 3 RGA format. + */ + +static qboolean ConvertPixel(struct PNG_Chunk_IHDR *IHDR, + byte *OutPtr, + uint8_t *DecompPtr, + qboolean HasTransparentColour, + uint8_t *TransparentColour, + uint8_t *OutPal) +{ + /* + * input verification + */ + + if(!(IHDR && OutPtr && DecompPtr && TransparentColour && OutPal)) + { + return(qfalse); + } + + switch(IHDR->ColourType) + { + case PNG_ColourType_Grey : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_1 : + case PNG_BitDepth_2 : + case PNG_BitDepth_4 : + { + uint8_t Step; + uint8_t GreyValue; + + Step = 0xFF / ((1 << IHDR->BitDepth) - 1); + + GreyValue = DecompPtr[0] * Step; + + OutPtr[0] = GreyValue; + OutPtr[1] = GreyValue; + OutPtr[2] = GreyValue; + OutPtr[3] = 0xFF; + + /* + * Grey supports full transparency for one specified colour + */ + + if(HasTransparentColour) + { + if(TransparentColour[1] == DecompPtr[0]) + { + OutPtr[3] = 0x00; + } + } + + + break; + } + + case PNG_BitDepth_8 : + case PNG_BitDepth_16 : + { + OutPtr[0] = DecompPtr[0]; + OutPtr[1] = DecompPtr[0]; + OutPtr[2] = DecompPtr[0]; + OutPtr[3] = 0xFF; + + /* + * Grey supports full transparency for one specified colour + */ + + if(HasTransparentColour) + { + if(IHDR->BitDepth == PNG_BitDepth_8) + { + if(TransparentColour[1] == DecompPtr[0]) + { + OutPtr[3] = 0x00; + } + } + else + { + if((TransparentColour[0] == DecompPtr[0]) && (TransparentColour[1] == DecompPtr[1])) + { + OutPtr[3] = 0x00; + } + } + } + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_True : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_8 : + { + OutPtr[0] = DecompPtr[0]; + OutPtr[1] = DecompPtr[1]; + OutPtr[2] = DecompPtr[2]; + OutPtr[3] = 0xFF; + + /* + * True supports full transparency for one specified colour + */ + + if(HasTransparentColour) + { + if((TransparentColour[1] == DecompPtr[0]) && + (TransparentColour[3] == DecompPtr[1]) && + (TransparentColour[5] == DecompPtr[2])) + { + OutPtr[3] = 0x00; + } + } + + break; + } + + case PNG_BitDepth_16 : + { + /* + * We use only the upper byte. + */ + + OutPtr[0] = DecompPtr[0]; + OutPtr[1] = DecompPtr[2]; + OutPtr[2] = DecompPtr[4]; + OutPtr[3] = 0xFF; + + /* + * True supports full transparency for one specified colour + */ + + if(HasTransparentColour) + { + if((TransparentColour[0] == DecompPtr[0]) && (TransparentColour[1] == DecompPtr[1]) && + (TransparentColour[2] == DecompPtr[2]) && (TransparentColour[3] == DecompPtr[3]) && + (TransparentColour[4] == DecompPtr[4]) && (TransparentColour[5] == DecompPtr[5])) + { + OutPtr[3] = 0x00; + } + } + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_Indexed : + { + OutPtr[0] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 0]; + OutPtr[1] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 1]; + OutPtr[2] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 2]; + OutPtr[3] = OutPal[DecompPtr[0] * Q3IMAGE_BYTESPERPIXEL + 3]; + + break; + } + + case PNG_ColourType_GreyAlpha : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_8 : + { + OutPtr[0] = DecompPtr[0]; + OutPtr[1] = DecompPtr[0]; + OutPtr[2] = DecompPtr[0]; + OutPtr[3] = DecompPtr[1]; + + break; + } + + case PNG_BitDepth_16 : + { + /* + * We use only the upper byte. + */ + + OutPtr[0] = DecompPtr[0]; + OutPtr[1] = DecompPtr[0]; + OutPtr[2] = DecompPtr[0]; + OutPtr[3] = DecompPtr[2]; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_TrueAlpha : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_8 : + { + OutPtr[0] = DecompPtr[0]; + OutPtr[1] = DecompPtr[1]; + OutPtr[2] = DecompPtr[2]; + OutPtr[3] = DecompPtr[3]; + + break; + } + + case PNG_BitDepth_16 : + { + /* + * We use only the upper byte. + */ + + OutPtr[0] = DecompPtr[0]; + OutPtr[1] = DecompPtr[2]; + OutPtr[2] = DecompPtr[4]; + OutPtr[3] = DecompPtr[6]; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + default : + { + return(qfalse); + } + } + + return(qtrue); +} + + +/* + * Decode a non-interlaced image. + */ + +static qboolean DecodeImageNonInterlaced(struct PNG_Chunk_IHDR *IHDR, + byte *OutBuffer, + uint8_t *DecompressedData, + uint32_t DecompressedDataLength, + qboolean HasTransparentColour, + uint8_t *TransparentColour, + uint8_t *OutPal) +{ + uint32_t IHDR_Width; + uint32_t IHDR_Height; + uint32_t BytesPerScanline, BytesPerPixel, PixelsPerByte; + uint32_t w, h, p; + byte *OutPtr; + uint8_t *DecompPtr; + + /* + * input verification + */ + + if(!(IHDR && OutBuffer && DecompressedData && DecompressedDataLength && TransparentColour && OutPal)) + { + return(qfalse); + } + + /* + * byte swapping + */ + + IHDR_Width = BigLong(IHDR->Width); + IHDR_Height = BigLong(IHDR->Height); + + /* + * information for un-filtering + */ + + switch(IHDR->ColourType) + { + case PNG_ColourType_Grey : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_1 : + case PNG_BitDepth_2 : + case PNG_BitDepth_4 : + { + BytesPerPixel = 1; + PixelsPerByte = 8 / IHDR->BitDepth; + + break; + } + + case PNG_BitDepth_8 : + case PNG_BitDepth_16 : + { + BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_Grey; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_True : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_8 : + case PNG_BitDepth_16 : + { + BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_True; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_Indexed : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_1 : + case PNG_BitDepth_2 : + case PNG_BitDepth_4 : + { + BytesPerPixel = 1; + PixelsPerByte = 8 / IHDR->BitDepth; + + break; + } + + case PNG_BitDepth_8 : + { + BytesPerPixel = PNG_NumColourComponents_Indexed; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_GreyAlpha : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_8 : + case PNG_BitDepth_16 : + { + BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_GreyAlpha; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_TrueAlpha : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_8 : + case PNG_BitDepth_16 : + { + BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_TrueAlpha; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + default : + { + return(qfalse); + } + } + + /* + * Calculate the size of one scanline + */ + + BytesPerScanline = (IHDR_Width * BytesPerPixel + (PixelsPerByte - 1)) / PixelsPerByte; + + /* + * Check if we have enough data for the whole image. + */ + + if(!(DecompressedDataLength == ((BytesPerScanline + 1) * IHDR_Height))) + { + return(qfalse); + } + + /* + * Unfilter the image. + */ + + if(!UnfilterImage(DecompressedData, IHDR_Height, BytesPerScanline, BytesPerPixel)) + { + return(qfalse); + } + + /* + * Set the working pointers to the beginning of the buffers. + */ + + OutPtr = OutBuffer; + DecompPtr = DecompressedData; + + /* + * Create the output image. + */ + + for(h = 0; h < IHDR_Height; h++) + { + /* + * Count the pixels on the scanline for those multipixel bytes + */ + + uint32_t CurrPixel; + + /* + * skip FilterType + */ + + DecompPtr++; + + /* + * Reset the pixel count. + */ + + CurrPixel = 0; + + for(w = 0; w < (BytesPerScanline / BytesPerPixel); w++) + { + if(PixelsPerByte > 1) + { + uint8_t Mask; + uint32_t Shift; + uint8_t SinglePixel; + + for(p = 0; p < PixelsPerByte; p++) + { + if(CurrPixel < IHDR_Width) + { + Mask = (1 << IHDR->BitDepth) - 1; + Shift = (PixelsPerByte - 1 - p) * IHDR->BitDepth; + + SinglePixel = ((DecompPtr[0] & (Mask << Shift)) >> Shift); + + if(!ConvertPixel(IHDR, OutPtr, &SinglePixel, HasTransparentColour, TransparentColour, OutPal)) + { + return(qfalse); + } + + OutPtr += Q3IMAGE_BYTESPERPIXEL; + CurrPixel++; + } + } + + } + else + { + if(!ConvertPixel(IHDR, OutPtr, DecompPtr, HasTransparentColour, TransparentColour, OutPal)) + { + return(qfalse); + } + + + OutPtr += Q3IMAGE_BYTESPERPIXEL; + } + + DecompPtr += BytesPerPixel; + } + } + + return(qtrue); +} + +/* + * Decode an interlaced image. + */ + +static qboolean DecodeImageInterlaced(struct PNG_Chunk_IHDR *IHDR, + byte *OutBuffer, + uint8_t *DecompressedData, + uint32_t DecompressedDataLength, + qboolean HasTransparentColour, + uint8_t *TransparentColour, + uint8_t *OutPal) +{ + uint32_t IHDR_Width; + uint32_t IHDR_Height; + uint32_t BytesPerScanline[PNG_Adam7_NumPasses], BytesPerPixel, PixelsPerByte; + uint32_t PassWidth[PNG_Adam7_NumPasses], PassHeight[PNG_Adam7_NumPasses]; + uint32_t WSkip[PNG_Adam7_NumPasses], WOffset[PNG_Adam7_NumPasses], HSkip[PNG_Adam7_NumPasses], HOffset[PNG_Adam7_NumPasses]; + uint32_t w, h, p, a; + byte *OutPtr; + uint8_t *DecompPtr; + uint32_t TargetLength; + + /* + * input verification + */ + + if(!(IHDR && OutBuffer && DecompressedData && DecompressedDataLength && TransparentColour && OutPal)) + { + return(qfalse); + } + + /* + * byte swapping + */ + + IHDR_Width = BigLong(IHDR->Width); + IHDR_Height = BigLong(IHDR->Height); + + /* + * Skip and Offset for the passes. + */ + + WSkip[0] = 8; + WOffset[0] = 0; + HSkip[0] = 8; + HOffset[0] = 0; + + WSkip[1] = 8; + WOffset[1] = 4; + HSkip[1] = 8; + HOffset[1] = 0; + + WSkip[2] = 4; + WOffset[2] = 0; + HSkip[2] = 8; + HOffset[2] = 4; + + WSkip[3] = 4; + WOffset[3] = 2; + HSkip[3] = 4; + HOffset[3] = 0; + + WSkip[4] = 2; + WOffset[4] = 0; + HSkip[4] = 4; + HOffset[4] = 2; + + WSkip[5] = 2; + WOffset[5] = 1; + HSkip[5] = 2; + HOffset[5] = 0; + + WSkip[6] = 1; + WOffset[6] = 0; + HSkip[6] = 2; + HOffset[6] = 1; + + /* + * Calculate the sizes of the passes. + */ + + PassWidth[0] = (IHDR_Width + 7) / 8; + PassHeight[0] = (IHDR_Height + 7) / 8; + + PassWidth[1] = (IHDR_Width + 3) / 8; + PassHeight[1] = (IHDR_Height + 7) / 8; + + PassWidth[2] = (IHDR_Width + 3) / 4; + PassHeight[2] = (IHDR_Height + 3) / 8; + + PassWidth[3] = (IHDR_Width + 1) / 4; + PassHeight[3] = (IHDR_Height + 3) / 4; + + PassWidth[4] = (IHDR_Width + 1) / 2; + PassHeight[4] = (IHDR_Height + 1) / 4; + + PassWidth[5] = (IHDR_Width + 0) / 2; + PassHeight[5] = (IHDR_Height + 1) / 2; + + PassWidth[6] = (IHDR_Width + 0) / 1; + PassHeight[6] = (IHDR_Height + 0) / 2; + + /* + * information for un-filtering + */ + + switch(IHDR->ColourType) + { + case PNG_ColourType_Grey : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_1 : + case PNG_BitDepth_2 : + case PNG_BitDepth_4 : + { + BytesPerPixel = 1; + PixelsPerByte = 8 / IHDR->BitDepth; + + break; + } + + case PNG_BitDepth_8 : + case PNG_BitDepth_16 : + { + BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_Grey; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_True : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_8 : + case PNG_BitDepth_16 : + { + BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_True; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_Indexed : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_1 : + case PNG_BitDepth_2 : + case PNG_BitDepth_4 : + { + BytesPerPixel = 1; + PixelsPerByte = 8 / IHDR->BitDepth; + + break; + } + + case PNG_BitDepth_8 : + { + BytesPerPixel = PNG_NumColourComponents_Indexed; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_GreyAlpha : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_8 : + case PNG_BitDepth_16 : + { + BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_GreyAlpha; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + case PNG_ColourType_TrueAlpha : + { + switch(IHDR->BitDepth) + { + case PNG_BitDepth_8 : + case PNG_BitDepth_16 : + { + BytesPerPixel = (IHDR->BitDepth / 8) * PNG_NumColourComponents_TrueAlpha; + PixelsPerByte = 1; + + break; + } + + default : + { + return(qfalse); + } + } + + break; + } + + default : + { + return(qfalse); + } + } + + /* + * Calculate the size of the scanlines per pass + */ + + for(a = 0; a < PNG_Adam7_NumPasses; a++) + { + BytesPerScanline[a] = (PassWidth[a] * BytesPerPixel + (PixelsPerByte - 1)) / PixelsPerByte; + } + + /* + * Calculate the size of all passes + */ + + TargetLength = 0; + + for(a = 0; a < PNG_Adam7_NumPasses; a++) + { + TargetLength += ((BytesPerScanline[a] + (BytesPerScanline[a] ? 1 : 0)) * PassHeight[a]); + } + + /* + * Check if we have enough data for the whole image. + */ + + if(!(DecompressedDataLength == TargetLength)) + { + return(qfalse); + } + + /* + * Unfilter the image. + */ + + DecompPtr = DecompressedData; + + for(a = 0; a < PNG_Adam7_NumPasses; a++) + { + if(!UnfilterImage(DecompPtr, PassHeight[a], BytesPerScanline[a], BytesPerPixel)) + { + return(qfalse); + } + + DecompPtr += ((BytesPerScanline[a] + (BytesPerScanline[a] ? 1 : 0)) * PassHeight[a]); + } + + /* + * Set the working pointers to the beginning of the buffers. + */ + + DecompPtr = DecompressedData; + + /* + * Create the output image. + */ + + for(a = 0; a < PNG_Adam7_NumPasses; a++) + { + for(h = 0; h < PassHeight[a]; h++) + { + /* + * Count the pixels on the scanline for those multipixel bytes + */ + + uint32_t CurrPixel; + + /* + * skip FilterType + * but only when the pass has a width bigger than zero + */ + + if(BytesPerScanline[a]) + { + DecompPtr++; + } + + /* + * Reset the pixel count. + */ + + CurrPixel = 0; + + for(w = 0; w < (BytesPerScanline[a] / BytesPerPixel); w++) + { + if(PixelsPerByte > 1) + { + uint8_t Mask; + uint32_t Shift; + uint8_t SinglePixel; + + for(p = 0; p < PixelsPerByte; p++) + { + if(CurrPixel < PassWidth[a]) + { + Mask = (1 << IHDR->BitDepth) - 1; + Shift = (PixelsPerByte - 1 - p) * IHDR->BitDepth; + + SinglePixel = ((DecompPtr[0] & (Mask << Shift)) >> Shift); + + OutPtr = OutBuffer + (((((h * HSkip[a]) + HOffset[a]) * IHDR_Width) + ((CurrPixel * WSkip[a]) + WOffset[a])) * Q3IMAGE_BYTESPERPIXEL); + + if(!ConvertPixel(IHDR, OutPtr, &SinglePixel, HasTransparentColour, TransparentColour, OutPal)) + { + return(qfalse); + } + + CurrPixel++; + } + } + + } + else + { + OutPtr = OutBuffer + (((((h * HSkip[a]) + HOffset[a]) * IHDR_Width) + ((w * WSkip[a]) + WOffset[a])) * Q3IMAGE_BYTESPERPIXEL); + + if(!ConvertPixel(IHDR, OutPtr, DecompPtr, HasTransparentColour, TransparentColour, OutPal)) + { + return(qfalse); + } + } + + DecompPtr += BytesPerPixel; + } + } + } + + return(qtrue); +} + +/* + * The PNG loader + */ + +void R_LoadPNG(const char *name, byte **pic, int *width, int *height) +{ + struct BufferedFile *ThePNG; + byte *OutBuffer; + uint8_t *Signature; + struct PNG_ChunkHeader *CH; + uint32_t ChunkHeaderLength; + uint32_t ChunkHeaderType; + struct PNG_Chunk_IHDR *IHDR; + uint32_t IHDR_Width; + uint32_t IHDR_Height; + PNG_ChunkCRC *CRC; + uint8_t *InPal; + uint8_t *DecompressedData; + uint32_t DecompressedDataLength; + uint32_t i; + + /* + * palette with 256 RGBA entries + */ + + uint8_t OutPal[1024]; + + /* + * transparent colour from the tRNS chunk + */ + + qboolean HasTransparentColour = qfalse; + uint8_t TransparentColour[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + /* + * input verification + */ + + if(!(name && pic)) + { + return; + } + + /* + * Zero out return values. + */ + + *pic = NULL; + + if(width) + { + *width = 0; + } + + if(height) + { + *height = 0; + } + + /* + * Read the file. + */ + + ThePNG = ReadBufferedFile(name); + if(!ThePNG) + { + return; + } + + /* + * Read the siganture of the file. + */ + + Signature = BufferedFileRead(ThePNG, PNG_Signature_Size); + if(!Signature) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Is it a PNG? + */ + + if(memcmp(Signature, PNG_Signature, PNG_Signature_Size)) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Read the first chunk-header. + */ + + CH = BufferedFileRead(ThePNG, PNG_ChunkHeader_Size); + if(!CH) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * PNG multi-byte types are in Big Endian + */ + + ChunkHeaderLength = BigLong(CH->Length); + ChunkHeaderType = BigLong(CH->Type); + + /* + * Check if the first chunk is an IHDR. + */ + + if(!((ChunkHeaderType == PNG_ChunkType_IHDR) && (ChunkHeaderLength == PNG_Chunk_IHDR_Size))) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Read the IHDR. + */ + + IHDR = BufferedFileRead(ThePNG, PNG_Chunk_IHDR_Size); + if(!IHDR) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Read the CRC for IHDR + */ + + CRC = BufferedFileRead(ThePNG, PNG_ChunkCRC_Size); + if(!CRC) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Here we could check the CRC if we wanted to. + */ + + /* + * multi-byte type swapping + */ + + IHDR_Width = BigLong(IHDR->Width); + IHDR_Height = BigLong(IHDR->Height); + + /* + * Check if Width and Height are valid. + */ + + if(!((IHDR_Width > 0) && (IHDR_Height > 0)) + || IHDR_Width > INT_MAX / Q3IMAGE_BYTESPERPIXEL / IHDR_Height) + { + CloseBufferedFile(ThePNG); + + ri.Printf( PRINT_WARNING, "%s: invalid image size\n", name ); + + return; + } + + /* + * Do we need to check if the dimensions of the image are valid for Quake3? + */ + + /* + * Check if CompressionMethod and FilterMethod are valid. + */ + + if(!((IHDR->CompressionMethod == PNG_CompressionMethod_0) && (IHDR->FilterMethod == PNG_FilterMethod_0))) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Check if InterlaceMethod is valid. + */ + + if(!((IHDR->InterlaceMethod == PNG_InterlaceMethod_NonInterlaced) || (IHDR->InterlaceMethod == PNG_InterlaceMethod_Interlaced))) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Read palette for an indexed image. + */ + + if(IHDR->ColourType == PNG_ColourType_Indexed) + { + /* + * We need the palette first. + */ + + if(!FindChunk(ThePNG, PNG_ChunkType_PLTE)) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Read the chunk-header. + */ + + CH = BufferedFileRead(ThePNG, PNG_ChunkHeader_Size); + if(!CH) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * PNG multi-byte types are in Big Endian + */ + + ChunkHeaderLength = BigLong(CH->Length); + ChunkHeaderType = BigLong(CH->Type); + + /* + * Check if the chunk is a PLTE. + */ + + if(!(ChunkHeaderType == PNG_ChunkType_PLTE)) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Check if Length is divisible by 3 + */ + + if(ChunkHeaderLength % 3) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Read the raw palette data + */ + + InPal = BufferedFileRead(ThePNG, ChunkHeaderLength); + if(!InPal) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Read the CRC for the palette + */ + + CRC = BufferedFileRead(ThePNG, PNG_ChunkCRC_Size); + if(!CRC) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Set some default values. + */ + + for(i = 0; i < 256; i++) + { + OutPal[i * Q3IMAGE_BYTESPERPIXEL + 0] = 0x00; + OutPal[i * Q3IMAGE_BYTESPERPIXEL + 1] = 0x00; + OutPal[i * Q3IMAGE_BYTESPERPIXEL + 2] = 0x00; + OutPal[i * Q3IMAGE_BYTESPERPIXEL + 3] = 0xFF; + } + + /* + * Convert to the Quake3 RGBA-format. + */ + + for(i = 0; i < (ChunkHeaderLength / 3); i++) + { + OutPal[i * Q3IMAGE_BYTESPERPIXEL + 0] = InPal[i*3+0]; + OutPal[i * Q3IMAGE_BYTESPERPIXEL + 1] = InPal[i*3+1]; + OutPal[i * Q3IMAGE_BYTESPERPIXEL + 2] = InPal[i*3+2]; + OutPal[i * Q3IMAGE_BYTESPERPIXEL + 3] = 0xFF; + } + } + + /* + * transparency information is sometimes stored in a tRNS chunk + */ + + /* + * Let's see if there is a tRNS chunk + */ + + if(FindChunk(ThePNG, PNG_ChunkType_tRNS)) + { + uint8_t *Trans; + + /* + * Read the chunk-header. + */ + + CH = BufferedFileRead(ThePNG, PNG_ChunkHeader_Size); + if(!CH) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * PNG multi-byte types are in Big Endian + */ + + ChunkHeaderLength = BigLong(CH->Length); + ChunkHeaderType = BigLong(CH->Type); + + /* + * Check if the chunk is a tRNS. + */ + + if(!(ChunkHeaderType == PNG_ChunkType_tRNS)) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Read the transparency information. + */ + + Trans = BufferedFileRead(ThePNG, ChunkHeaderLength); + if(!Trans) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Read the CRC. + */ + + CRC = BufferedFileRead(ThePNG, PNG_ChunkCRC_Size); + if(!CRC) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Only for Grey, True and Indexed ColourType should tRNS exist. + */ + + switch(IHDR->ColourType) + { + case PNG_ColourType_Grey : + { + if(!ChunkHeaderLength == 2) + { + CloseBufferedFile(ThePNG); + + return; + } + + HasTransparentColour = qtrue; + + /* + * Grey can have one colour which is completely transparent. + * This colour is always stored in 16 bits. + */ + + TransparentColour[0] = Trans[0]; + TransparentColour[1] = Trans[1]; + + break; + } + + case PNG_ColourType_True : + { + if(!ChunkHeaderLength == 6) + { + CloseBufferedFile(ThePNG); + + return; + } + + HasTransparentColour = qtrue; + + /* + * True can have one colour which is completely transparent. + * This colour is always stored in 16 bits. + */ + + TransparentColour[0] = Trans[0]; + TransparentColour[1] = Trans[1]; + TransparentColour[2] = Trans[2]; + TransparentColour[3] = Trans[3]; + TransparentColour[4] = Trans[4]; + TransparentColour[5] = Trans[5]; + + break; + } + + case PNG_ColourType_Indexed : + { + /* + * Maximum of 256 one byte transparency entries. + */ + + if(ChunkHeaderLength > 256) + { + CloseBufferedFile(ThePNG); + + return; + } + + HasTransparentColour = qtrue; + + /* + * alpha values for palette entries + */ + + for(i = 0; i < ChunkHeaderLength; i++) + { + OutPal[i * Q3IMAGE_BYTESPERPIXEL + 3] = Trans[i]; + } + + break; + } + + /* + * All other ColourTypes should not have tRNS chunks + */ + + default : + { + CloseBufferedFile(ThePNG); + + return; + } + } + } + + /* + * Rewind to the start of the file. + */ + + if(!BufferedFileRewind(ThePNG, -1)) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Skip the signature + */ + + if(!BufferedFileSkip(ThePNG, PNG_Signature_Size)) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Decompress all IDAT chunks + */ + + DecompressedDataLength = DecompressIDATs(ThePNG, &DecompressedData); + if(!(DecompressedDataLength && DecompressedData)) + { + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Allocate output buffer. + */ + + OutBuffer = ri.Malloc(IHDR_Width * IHDR_Height * Q3IMAGE_BYTESPERPIXEL); + if(!OutBuffer) + { + ri.Free(DecompressedData); + CloseBufferedFile(ThePNG); + + return; + } + + /* + * Interlaced and Non-interlaced images need to be handled differently. + */ + + switch(IHDR->InterlaceMethod) + { + case PNG_InterlaceMethod_NonInterlaced : + { + if(!DecodeImageNonInterlaced(IHDR, OutBuffer, DecompressedData, DecompressedDataLength, HasTransparentColour, TransparentColour, OutPal)) + { + ri.Free(OutBuffer); + ri.Free(DecompressedData); + CloseBufferedFile(ThePNG); + + return; + } + + break; + } + + case PNG_InterlaceMethod_Interlaced : + { + if(!DecodeImageInterlaced(IHDR, OutBuffer, DecompressedData, DecompressedDataLength, HasTransparentColour, TransparentColour, OutPal)) + { + ri.Free(OutBuffer); + ri.Free(DecompressedData); + CloseBufferedFile(ThePNG); + + return; + } + + break; + } + + default : + { + ri.Free(OutBuffer); + ri.Free(DecompressedData); + CloseBufferedFile(ThePNG); + + return; + } + } + + /* + * update the pointer to the image data + */ + + *pic = OutBuffer; + + /* + * Fill width and height. + */ + + if(width) + { + *width = IHDR_Width; + } + + if(height) + { + *height = IHDR_Height; + } + + /* + * DecompressedData is not needed anymore. + */ + + ri.Free(DecompressedData); + + /* + * We have all data, so close the file. + */ + + CloseBufferedFile(ThePNG); +} diff --git a/src/renderergl2/tr_image_tga.c b/src/renderergl2/tr_image_tga.c new file mode 100644 index 00000000..363c0b3f --- /dev/null +++ b/src/renderergl2/tr_image_tga.c @@ -0,0 +1,324 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "../qcommon/q_shared.h" +#include "../qcommon/qfiles.h" +#include "../qcommon/qcommon.h" +#include "../renderercommon/tr_public.h" +extern refimport_t ri; + +/* +======================================================================== + +TGA files are used for 24/32 bit images + +======================================================================== +*/ + +typedef struct _TargaHeader { + unsigned char id_length, colormap_type, image_type; + unsigned short colormap_index, colormap_length; + unsigned char colormap_size; + unsigned short x_origin, y_origin, width, height; + unsigned char pixel_size, attributes; +} TargaHeader; + +void R_LoadTGA ( const char *name, byte **pic, int *width, int *height) +{ + unsigned columns, rows, numPixels; + byte *pixbuf; + int row, column; + byte *buf_p; + byte *end; + union { + byte *b; + void *v; + } buffer; + TargaHeader targa_header; + byte *targa_rgba; + int length; + + *pic = NULL; + + if(width) + *width = 0; + if(height) + *height = 0; + + // + // load the file + // + length = ri.FS_ReadFile ( ( char * ) name, &buffer.v); + if (!buffer.b || length < 0) { + return; + } + + if(length < 18) + { + ri.Error( ERR_DROP, "LoadTGA: header too short (%s)", name ); + } + + buf_p = buffer.b; + end = buffer.b + length; + + targa_header.id_length = buf_p[0]; + targa_header.colormap_type = buf_p[1]; + targa_header.image_type = buf_p[2]; + + memcpy(&targa_header.colormap_index, &buf_p[3], 2); + memcpy(&targa_header.colormap_length, &buf_p[5], 2); + targa_header.colormap_size = buf_p[7]; + memcpy(&targa_header.x_origin, &buf_p[8], 2); + memcpy(&targa_header.y_origin, &buf_p[10], 2); + memcpy(&targa_header.width, &buf_p[12], 2); + memcpy(&targa_header.height, &buf_p[14], 2); + targa_header.pixel_size = buf_p[16]; + targa_header.attributes = buf_p[17]; + + targa_header.colormap_index = LittleShort(targa_header.colormap_index); + targa_header.colormap_length = LittleShort(targa_header.colormap_length); + targa_header.x_origin = LittleShort(targa_header.x_origin); + targa_header.y_origin = LittleShort(targa_header.y_origin); + targa_header.width = LittleShort(targa_header.width); + targa_header.height = LittleShort(targa_header.height); + + buf_p += 18; + + if (targa_header.image_type!=2 + && targa_header.image_type!=10 + && targa_header.image_type != 3 ) + { + ri.Error (ERR_DROP, "LoadTGA: Only type 2 (RGB), 3 (gray), and 10 (RGB) TGA images supported"); + } + + if ( targa_header.colormap_type != 0 ) + { + ri.Error( ERR_DROP, "LoadTGA: colormaps not supported" ); + } + + if ( ( targa_header.pixel_size != 32 && targa_header.pixel_size != 24 ) && targa_header.image_type != 3 ) + { + ri.Error (ERR_DROP, "LoadTGA: Only 32 or 24 bit images supported (no colormaps)"); + } + + columns = targa_header.width; + rows = targa_header.height; + numPixels = columns * rows * 4; + + if(!columns || !rows || numPixels > 0x7FFFFFFF || numPixels / columns / 4 != rows) + { + ri.Error (ERR_DROP, "LoadTGA: %s has an invalid image size", name); + } + + + targa_rgba = ri.Malloc (numPixels); + + if (targa_header.id_length != 0) + { + if (buf_p + targa_header.id_length > end) + ri.Error( ERR_DROP, "LoadTGA: header too short (%s)", name ); + + buf_p += targa_header.id_length; // skip TARGA image comment + } + + if ( targa_header.image_type==2 || targa_header.image_type == 3 ) + { + if(buf_p + columns*rows*targa_header.pixel_size/8 > end) + { + ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name); + } + + // Uncompressed RGB or gray scale image + for(row=rows-1; row>=0; row--) + { + pixbuf = targa_rgba + row*columns*4; + for(column=0; column=0; row--) { + pixbuf = targa_rgba + row*columns*4; + for(column=0; column end) + ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name); + packetHeader= *buf_p++; + packetSize = 1 + (packetHeader & 0x7f); + if (packetHeader & 0x80) { // run-length packet + if(buf_p + targa_header.pixel_size/8 > end) + ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name); + switch (targa_header.pixel_size) { + case 24: + blue = *buf_p++; + green = *buf_p++; + red = *buf_p++; + alphabyte = 255; + break; + case 32: + blue = *buf_p++; + green = *buf_p++; + red = *buf_p++; + alphabyte = *buf_p++; + break; + default: + ri.Error( ERR_DROP, "LoadTGA: illegal pixel_size '%d' in file '%s'", targa_header.pixel_size, name ); + break; + } + + for(j=0;j0) + row--; + else + goto breakOut; + pixbuf = targa_rgba + row*columns*4; + } + } + } + else { // non run-length packet + + if(buf_p + targa_header.pixel_size/8*packetSize > end) + ri.Error (ERR_DROP, "LoadTGA: file truncated (%s)", name); + for(j=0;j0) + row--; + else + goto breakOut; + pixbuf = targa_rgba + row*columns*4; + } + } + } + } + breakOut:; + } + } + +#if 0 + // TTimo: this is the chunk of code to ensure a behavior that meets TGA specs + // bit 5 set => top-down + if (targa_header.attributes & 0x20) { + unsigned char *flip = (unsigned char*)malloc (columns*4); + unsigned char *src, *dst; + + for (row = 0; row < rows/2; row++) { + src = targa_rgba + row * 4 * columns; + dst = targa_rgba + (rows - row - 1) * 4 * columns; + + memcpy (flip, src, columns*4); + memcpy (src, dst, columns*4); + memcpy (dst, flip, columns*4); + } + free (flip); + } +#endif + // instead we just print a warning + if (targa_header.attributes & 0x20) { + ri.Printf( PRINT_WARNING, "WARNING: '%s' TGA file header declares top-down image, ignoring\n", name); + } + + if (width) + *width = columns; + if (height) + *height = rows; + + *pic = targa_rgba; + + ri.FS_FreeFile (buffer.v); +} diff --git a/src/renderergl2/tr_init.c b/src/renderergl2/tr_init.c new file mode 100644 index 00000000..7228d01b --- /dev/null +++ b/src/renderergl2/tr_init.c @@ -0,0 +1,1480 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_init.c -- functions that are not called every frame + +#include "tr_local.h" + +glconfig_t glConfig; +glRefConfig_t glRefConfig; +qboolean textureFilterAnisotropic = qfalse; +int maxAnisotropy = 0; +float displayAspect = 0.0f; + +glstate_t glState; + +static void GfxInfo_f( void ); +static void GfxMemInfo_f( void ); + +#ifdef USE_RENDERER_DLOPEN +cvar_t *com_altivec; +#endif + +cvar_t *r_flareSize; +cvar_t *r_flareFade; +cvar_t *r_flareCoeff; + +cvar_t *r_railWidth; +cvar_t *r_railCoreWidth; +cvar_t *r_railSegmentLength; + +cvar_t *r_verbose; +cvar_t *r_ignore; + +cvar_t *r_detailTextures; + +cvar_t *r_znear; +cvar_t *r_zproj; +cvar_t *r_stereoSeparation; + +cvar_t *r_skipBackEnd; + +cvar_t *r_stereoEnabled; +cvar_t *r_anaglyphMode; + +cvar_t *r_greyscale; + +cvar_t *r_ignorehwgamma; +cvar_t *r_measureOverdraw; + +cvar_t *r_inGameVideo; +cvar_t *r_fastsky; +cvar_t *r_drawSun; +cvar_t *r_dynamiclight; +cvar_t *r_dlightBacks; + +cvar_t *r_lodbias; +cvar_t *r_lodscale; + +cvar_t *r_norefresh; +cvar_t *r_drawentities; +cvar_t *r_drawworld; +cvar_t *r_speeds; +cvar_t *r_fullbright; +cvar_t *r_novis; +cvar_t *r_nocull; +cvar_t *r_facePlaneCull; +cvar_t *r_showcluster; +cvar_t *r_nocurves; + +cvar_t *r_allowExtensions; + +cvar_t *r_ext_compressed_textures; +cvar_t *r_ext_multitexture; +cvar_t *r_ext_compiled_vertex_array; +cvar_t *r_ext_texture_env_add; +cvar_t *r_ext_texture_filter_anisotropic; +cvar_t *r_ext_max_anisotropy; + +cvar_t *r_ext_draw_range_elements; +cvar_t *r_ext_multi_draw_arrays; +cvar_t *r_ext_framebuffer_object; +cvar_t *r_ext_texture_float; +cvar_t *r_arb_half_float_pixel; +cvar_t *r_ext_framebuffer_multisample; + +cvar_t *r_mergeMultidraws; +cvar_t *r_mergeLeafSurfaces; + +cvar_t *r_cameraExposure; + +cvar_t *r_softOverbright; + +cvar_t *r_hdr; +cvar_t *r_postProcess; + +cvar_t *r_toneMap; +cvar_t *r_forceToneMap; +cvar_t *r_forceToneMapMin; +cvar_t *r_forceToneMapAvg; +cvar_t *r_forceToneMapMax; + +cvar_t *r_autoExposure; +cvar_t *r_forceAutoExposure; +cvar_t *r_forceAutoExposureMin; +cvar_t *r_forceAutoExposureMax; + +cvar_t *r_srgb; + +cvar_t *r_depthPrepass; +cvar_t *r_ssao; + +cvar_t *r_normalMapping; +cvar_t *r_specularMapping; +cvar_t *r_deluxeMapping; +cvar_t *r_parallaxMapping; +cvar_t *r_normalAmbient; +cvar_t *r_recalcMD3Normals; +cvar_t *r_mergeLightmaps; +cvar_t *r_dlightMode; +cvar_t *r_pshadowDist; +cvar_t *r_imageUpsample; +cvar_t *r_imageUpsampleMaxSize; +cvar_t *r_imageUpsampleType; +cvar_t *r_genNormalMaps; +cvar_t *r_forceSun; +cvar_t *r_forceSunMapLightScale; +cvar_t *r_forceSunLightScale; +cvar_t *r_forceSunAmbientScale; +cvar_t *r_drawSunRays; +cvar_t *r_sunShadows; +cvar_t *r_shadowFilter; +cvar_t *r_shadowMapSize; +cvar_t *r_shadowCascadeZNear; +cvar_t *r_shadowCascadeZFar; +cvar_t *r_shadowCascadeZBias; + +cvar_t *r_ignoreGLErrors; +cvar_t *r_logFile; + +cvar_t *r_stencilbits; +cvar_t *r_depthbits; +cvar_t *r_colorbits; +cvar_t *r_texturebits; +cvar_t *r_ext_multisample; + +cvar_t *r_drawBuffer; +cvar_t *r_lightmap; +cvar_t *r_vertexLight; +cvar_t *r_uiFullScreen; +cvar_t *r_shadows; +cvar_t *r_flares; +cvar_t *r_mode; +cvar_t *r_nobind; +cvar_t *r_singleShader; +cvar_t *r_roundImagesDown; +cvar_t *r_colorMipLevels; +cvar_t *r_picmip; +cvar_t *r_showtris; +cvar_t *r_showsky; +cvar_t *r_shownormals; +cvar_t *r_finish; +cvar_t *r_clear; +cvar_t *r_swapInterval; +cvar_t *r_textureMode; +cvar_t *r_offsetFactor; +cvar_t *r_offsetUnits; +cvar_t *r_gamma; +cvar_t *r_intensity; +cvar_t *r_lockpvs; +cvar_t *r_noportals; +cvar_t *r_portalOnly; + +cvar_t *r_subdivisions; +cvar_t *r_lodCurveError; + +cvar_t *r_fullscreen; +cvar_t *r_noborder; + +cvar_t *r_width; +cvar_t *r_height; +cvar_t *r_pixelAspect; + +cvar_t *r_overBrightBits; +cvar_t *r_mapOverBrightBits; + +cvar_t *r_debugSurface; +cvar_t *r_simpleMipMaps; + +cvar_t *r_showImages; + +cvar_t *r_ambientScale; +cvar_t *r_directedScale; +cvar_t *r_debugLight; +cvar_t *r_debugSort; +cvar_t *r_printShaders; +cvar_t *r_saveFontData; + +cvar_t *r_marksOnTriangleMeshes; + +cvar_t *r_aviMotionJpegQuality; +cvar_t *r_screenshotJpegQuality; + +cvar_t *r_maxpolys; +int max_polys; +cvar_t *r_maxpolyverts; +int max_polyverts; + +/* +** InitOpenGL +** +** This function is responsible for initializing a valid OpenGL subsystem. This +** is done by calling GLimp_Init (which gives us a working OGL subsystem) then +** setting variables, checking GL constants, and reporting the gfx system config +** to the user. +*/ +static void InitOpenGL( void ) +{ + char renderer_buffer[1024]; + + // + // initialize OS specific portions of the renderer + // + // GLimp_Init directly or indirectly references the following cvars: + // - r_fullscreen + // - r_mode + // - r_(color|depth|stencil)bits + // - r_ignorehwgamma + // - r_gamma + // + + if ( glConfig.vidWidth == 0 ) + { + GLint temp; + + GLimp_Init(); + GLimp_InitExtraExtensions(); + + strcpy( renderer_buffer, glConfig.renderer_string ); + Q_strlwr( renderer_buffer ); + + // OpenGL driver constants + qglGetIntegerv( GL_MAX_TEXTURE_SIZE, &temp ); + glConfig.maxTextureSize = temp; + + // stubbed or broken drivers may have reported 0... + if ( glConfig.maxTextureSize <= 0 ) + { + glConfig.maxTextureSize = 0; + } + } + + // set default state + GL_SetDefaultState(); +} + +/* +================== +GL_CheckErrors +================== +*/ +void GL_CheckErrs( char *file, int line ) { + int err; + char s[64]; + + err = qglGetError(); + if ( err == GL_NO_ERROR ) { + return; + } + if ( r_ignoreGLErrors->integer ) { + return; + } + switch( err ) { + case GL_INVALID_ENUM: + strcpy( s, "GL_INVALID_ENUM" ); + break; + case GL_INVALID_VALUE: + strcpy( s, "GL_INVALID_VALUE" ); + break; + case GL_INVALID_OPERATION: + strcpy( s, "GL_INVALID_OPERATION" ); + break; + case GL_STACK_OVERFLOW: + strcpy( s, "GL_STACK_OVERFLOW" ); + break; + case GL_STACK_UNDERFLOW: + strcpy( s, "GL_STACK_UNDERFLOW" ); + break; + case GL_OUT_OF_MEMORY: + strcpy( s, "GL_OUT_OF_MEMORY" ); + break; + default: + Com_sprintf( s, sizeof(s), "%i", err); + break; + } + + ri.Error( ERR_FATAL, "GL_CheckErrors: %s in %s at line %d", s , file, line); +} + + +/* +============================================================================== + + SCREEN SHOTS + +NOTE TTimo +some thoughts about the screenshots system: +screenshots get written in fs_homepath + fs_gamedir +vanilla q3 .. baseq3/screenshots/ *.tga +team arena .. missionpack/screenshots/ *.tga + +two commands: "screenshot" and "screenshotJPEG" +we use statics to store a count and start writing the first screenshot/screenshot????.tga (.jpg) available +(with FS_FileExists / FS_FOpenFileWrite calls) +FIXME: the statics don't get a reinit between fs_game changes + +============================================================================== +*/ + +/* +================== +RB_ReadPixels + +Reads an image but takes care of alignment issues for reading RGB images. + +Reads a minimum offset for where the RGB data starts in the image from +integer stored at pointer offset. When the function has returned the actual +offset was written back to address offset. This address will always have an +alignment of packAlign to ensure efficient copying. + +Stores the length of padding after a line of pixels to address padlen + +Return value must be freed with ri.Hunk_FreeTempMemory() +================== +*/ + +byte *RB_ReadPixels(int x, int y, int width, int height, size_t *offset, int *padlen) +{ + byte *buffer, *bufstart; + int padwidth, linelen; + GLint packAlign; + + qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); + + linelen = width * 3; + padwidth = PAD(linelen, packAlign); + + // Allocate a few more bytes so that we can choose an alignment we like + buffer = ri.Hunk_AllocateTempMemory(padwidth * height + *offset + packAlign - 1); + + bufstart = PADP((intptr_t) buffer + *offset, packAlign); + qglReadPixels(x, y, width, height, GL_RGB, GL_UNSIGNED_BYTE, bufstart); + + *offset = bufstart - buffer; + *padlen = padwidth - linelen; + + return buffer; +} + +/* +================== +RB_TakeScreenshot +================== +*/ +void RB_TakeScreenshot(int x, int y, int width, int height, char *fileName) +{ + byte *allbuf, *buffer; + byte *srcptr, *destptr; + byte *endline, *endmem; + byte temp; + + int linelen, padlen; + size_t offset = 18, memcount; + + allbuf = RB_ReadPixels(x, y, width, height, &offset, &padlen); + buffer = allbuf + offset - 18; + + Com_Memset (buffer, 0, 18); + buffer[2] = 2; // uncompressed type + buffer[12] = width & 255; + buffer[13] = width >> 8; + buffer[14] = height & 255; + buffer[15] = height >> 8; + buffer[16] = 24; // pixel size + + // swap rgb to bgr and remove padding from line endings + linelen = width * 3; + + srcptr = destptr = allbuf + offset; + endmem = srcptr + (linelen + padlen) * height; + + while(srcptr < endmem) + { + endline = srcptr + linelen; + + while(srcptr < endline) + { + temp = srcptr[0]; + *destptr++ = srcptr[2]; + *destptr++ = srcptr[1]; + *destptr++ = temp; + + srcptr += 3; + } + + // Skip the pad + srcptr += padlen; + } + + memcount = linelen * height; + + // gamma correct + if(glConfig.deviceSupportsGamma) + R_GammaCorrect(allbuf + offset, memcount); + + ri.FS_WriteFile(fileName, buffer, memcount + 18); + + ri.Hunk_FreeTempMemory(allbuf); +} + +/* +================== +RB_TakeScreenshotJPEG +================== +*/ + +void RB_TakeScreenshotJPEG(int x, int y, int width, int height, char *fileName) +{ + byte *buffer; + size_t offset = 0, memcount; + int padlen; + + buffer = RB_ReadPixels(x, y, width, height, &offset, &padlen); + memcount = (width * 3 + padlen) * height; + + // gamma correct + if(glConfig.deviceSupportsGamma) + R_GammaCorrect(buffer + offset, memcount); + + RE_SaveJPG(fileName, r_screenshotJpegQuality->integer, width, height, buffer + offset, padlen); + ri.Hunk_FreeTempMemory(buffer); +} + +/* +================== +RB_TakeScreenshotCmd +================== +*/ +const void *RB_TakeScreenshotCmd( const void *data ) { + const screenshotCommand_t *cmd; + + cmd = (const screenshotCommand_t *)data; + + // finish any 2D drawing if needed + if(tess.numIndexes) + RB_EndSurface(); + + if (cmd->jpeg) + RB_TakeScreenshotJPEG( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName); + else + RB_TakeScreenshot( cmd->x, cmd->y, cmd->width, cmd->height, cmd->fileName); + + return (const void *)(cmd + 1); +} + +/* +================== +R_TakeScreenshot +================== +*/ +void R_TakeScreenshot( int x, int y, int width, int height, char *name, qboolean jpeg ) { + static char fileName[MAX_OSPATH]; // bad things if two screenshots per frame? + screenshotCommand_t *cmd; + + cmd = R_GetCommandBuffer( sizeof( *cmd ) ); + if ( !cmd ) { + return; + } + cmd->commandId = RC_SCREENSHOT; + + cmd->x = x; + cmd->y = y; + cmd->width = width; + cmd->height = height; + Q_strncpyz( fileName, name, sizeof(fileName) ); + cmd->fileName = fileName; + cmd->jpeg = jpeg; +} + +/* +================== +R_ScreenshotFilename +================== +*/ +void R_ScreenshotFilename( int lastNumber, char *fileName ) { + int a,b,c,d; + + if ( lastNumber < 0 || lastNumber > 9999 ) { + Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot9999.tga" ); + return; + } + + a = lastNumber / 1000; + lastNumber -= a*1000; + b = lastNumber / 100; + lastNumber -= b*100; + c = lastNumber / 10; + lastNumber -= c*10; + d = lastNumber; + + Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot%i%i%i%i.tga" + , a, b, c, d ); +} + +/* +================== +R_ScreenshotFilename +================== +*/ +void R_ScreenshotFilenameJPEG( int lastNumber, char *fileName ) { + int a,b,c,d; + + if ( lastNumber < 0 || lastNumber > 9999 ) { + Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot9999.jpg" ); + return; + } + + a = lastNumber / 1000; + lastNumber -= a*1000; + b = lastNumber / 100; + lastNumber -= b*100; + c = lastNumber / 10; + lastNumber -= c*10; + d = lastNumber; + + Com_sprintf( fileName, MAX_OSPATH, "screenshots/shot%i%i%i%i.jpg" + , a, b, c, d ); +} + +/* +==================== +R_LevelShot + +levelshots are specialized 128*128 thumbnails for +the menu system, sampled down from full screen distorted images +==================== +*/ +void R_LevelShot( void ) { + char checkname[MAX_OSPATH]; + byte *buffer; + byte *source, *allsource; + byte *src, *dst; + size_t offset = 0; + int padlen; + int x, y; + int r, g, b; + float xScale, yScale; + int xx, yy; + + Com_sprintf(checkname, sizeof(checkname), "levelshots/%s.tga", tr.world->baseName); + + allsource = RB_ReadPixels(0, 0, glConfig.vidWidth, glConfig.vidHeight, &offset, &padlen); + source = allsource + offset; + + buffer = ri.Hunk_AllocateTempMemory(128 * 128*3 + 18); + Com_Memset (buffer, 0, 18); + buffer[2] = 2; // uncompressed type + buffer[12] = 128; + buffer[14] = 128; + buffer[16] = 24; // pixel size + + // resample from source + xScale = glConfig.vidWidth / 512.0f; + yScale = glConfig.vidHeight / 384.0f; + for ( y = 0 ; y < 128 ; y++ ) { + for ( x = 0 ; x < 128 ; x++ ) { + r = g = b = 0; + for ( yy = 0 ; yy < 3 ; yy++ ) { + for ( xx = 0 ; xx < 4 ; xx++ ) { + src = source + (3 * glConfig.vidWidth + padlen) * (int)((y*3 + yy) * yScale) + + 3 * (int) ((x*4 + xx) * xScale); + r += src[0]; + g += src[1]; + b += src[2]; + } + } + dst = buffer + 18 + 3 * ( y * 128 + x ); + dst[0] = b / 12; + dst[1] = g / 12; + dst[2] = r / 12; + } + } + + // gamma correct + if ( glConfig.deviceSupportsGamma ) { + R_GammaCorrect( buffer + 18, 128 * 128 * 3 ); + } + + ri.FS_WriteFile( checkname, buffer, 128 * 128*3 + 18 ); + + ri.Hunk_FreeTempMemory(buffer); + ri.Hunk_FreeTempMemory(allsource); + + ri.Printf( PRINT_ALL, "Wrote %s\n", checkname ); +} + +/* +================== +R_ScreenShot_f + +screenshot +screenshot [silent] +screenshot [levelshot] +screenshot [filename] + +Doesn't print the pacifier message if there is a second arg +================== +*/ +void R_ScreenShot_f (void) { + char checkname[MAX_OSPATH]; + static int lastNumber = -1; + qboolean silent; + + if ( !strcmp( ri.Cmd_Argv(1), "levelshot" ) ) { + R_LevelShot(); + return; + } + + if ( !strcmp( ri.Cmd_Argv(1), "silent" ) ) { + silent = qtrue; + } else { + silent = qfalse; + } + + if ( ri.Cmd_Argc() == 2 && !silent ) { + // explicit filename + Com_sprintf( checkname, MAX_OSPATH, "screenshots/%s.tga", ri.Cmd_Argv( 1 ) ); + } else { + // scan for a free filename + + // if we have saved a previous screenshot, don't scan + // again, because recording demo avis can involve + // thousands of shots + if ( lastNumber == -1 ) { + lastNumber = 0; + } + // scan for a free number + for ( ; lastNumber <= 9999 ; lastNumber++ ) { + R_ScreenshotFilename( lastNumber, checkname ); + + if (!ri.FS_FileExists( checkname )) + { + break; // file doesn't exist + } + } + + if ( lastNumber >= 9999 ) { + ri.Printf (PRINT_ALL, "ScreenShot: Couldn't create a file\n"); + return; + } + + lastNumber++; + } + + R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, qfalse ); + + if ( !silent ) { + ri.Printf (PRINT_ALL, "Wrote %s\n", checkname); + } +} + +void R_ScreenShotJPEG_f (void) { + char checkname[MAX_OSPATH]; + static int lastNumber = -1; + qboolean silent; + + if ( !strcmp( ri.Cmd_Argv(1), "levelshot" ) ) { + R_LevelShot(); + return; + } + + if ( !strcmp( ri.Cmd_Argv(1), "silent" ) ) { + silent = qtrue; + } else { + silent = qfalse; + } + + if ( ri.Cmd_Argc() == 2 && !silent ) { + // explicit filename + Com_sprintf( checkname, MAX_OSPATH, "screenshots/%s.jpg", ri.Cmd_Argv( 1 ) ); + } else { + // scan for a free filename + + // if we have saved a previous screenshot, don't scan + // again, because recording demo avis can involve + // thousands of shots + if ( lastNumber == -1 ) { + lastNumber = 0; + } + // scan for a free number + for ( ; lastNumber <= 9999 ; lastNumber++ ) { + R_ScreenshotFilenameJPEG( lastNumber, checkname ); + + if (!ri.FS_FileExists( checkname )) + { + break; // file doesn't exist + } + } + + if ( lastNumber == 10000 ) { + ri.Printf (PRINT_ALL, "ScreenShot: Couldn't create a file\n"); + return; + } + + lastNumber++; + } + + R_TakeScreenshot( 0, 0, glConfig.vidWidth, glConfig.vidHeight, checkname, qtrue ); + + if ( !silent ) { + ri.Printf (PRINT_ALL, "Wrote %s\n", checkname); + } +} + +//============================================================================ + +/* +================== +RB_TakeVideoFrameCmd +================== +*/ +const void *RB_TakeVideoFrameCmd( const void *data ) +{ + const videoFrameCommand_t *cmd; + byte *cBuf; + size_t memcount, linelen; + int padwidth, avipadwidth, padlen, avipadlen; + GLint packAlign; + + // finish any 2D drawing if needed + if(tess.numIndexes) + RB_EndSurface(); + + cmd = (const videoFrameCommand_t *)data; + + qglGetIntegerv(GL_PACK_ALIGNMENT, &packAlign); + + linelen = cmd->width * 3; + + // Alignment stuff for glReadPixels + padwidth = PAD(linelen, packAlign); + padlen = padwidth - linelen; + // AVI line padding + avipadwidth = PAD(linelen, AVI_LINE_PADDING); + avipadlen = avipadwidth - linelen; + + cBuf = PADP(cmd->captureBuffer, packAlign); + + qglReadPixels(0, 0, cmd->width, cmd->height, GL_RGB, + GL_UNSIGNED_BYTE, cBuf); + + memcount = padwidth * cmd->height; + + // gamma correct + if(glConfig.deviceSupportsGamma) + R_GammaCorrect(cBuf, memcount); + + if(cmd->motionJpeg) + { + memcount = RE_SaveJPGToBuffer(cmd->encodeBuffer, linelen * cmd->height, + r_aviMotionJpegQuality->integer, + cmd->width, cmd->height, cBuf, padlen); + ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, memcount); + } + else + { + byte *lineend, *memend; + byte *srcptr, *destptr; + + srcptr = cBuf; + destptr = cmd->encodeBuffer; + memend = srcptr + memcount; + + // swap R and B and remove line paddings + while(srcptr < memend) + { + lineend = srcptr + linelen; + while(srcptr < lineend) + { + *destptr++ = srcptr[2]; + *destptr++ = srcptr[1]; + *destptr++ = srcptr[0]; + srcptr += 3; + } + + Com_Memset(destptr, '\0', avipadlen); + destptr += avipadlen; + + srcptr += padlen; + } + + ri.CL_WriteAVIVideoFrame(cmd->encodeBuffer, avipadwidth * cmd->height); + } + + return (const void *)(cmd + 1); +} + +//============================================================================ + +/* +** GL_SetDefaultState +*/ +void GL_SetDefaultState( void ) +{ + qglClearDepth( 1.0f ); + + qglCullFace(GL_FRONT); + + qglColor4f (1,1,1,1); + + // initialize downstream texture unit if we're running + // in a multitexture environment + if ( qglActiveTextureARB ) { + GL_SelectTexture( 1 ); + GL_TextureMode( r_textureMode->string ); + GL_TexEnv( GL_MODULATE ); + qglDisable( GL_TEXTURE_2D ); + GL_SelectTexture( 0 ); + } + + qglEnable(GL_TEXTURE_2D); + GL_TextureMode( r_textureMode->string ); + GL_TexEnv( GL_MODULATE ); + + //qglShadeModel( GL_SMOOTH ); + qglDepthFunc( GL_LEQUAL ); + + // + // make sure our GL state vector is set correctly + // + glState.glStateBits = GLS_DEPTHTEST_DISABLE | GLS_DEPTHMASK_TRUE; + + glState.vertexAttribsState = 0; + glState.vertexAttribPointersSet = 0; + glState.currentProgram = 0; + qglUseProgramObjectARB(0); + + qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); + glState.currentVBO = NULL; + glState.currentIBO = NULL; + + qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); + qglDepthMask( GL_TRUE ); + qglDisable( GL_DEPTH_TEST ); + qglEnable( GL_SCISSOR_TEST ); + qglDisable( GL_CULL_FACE ); + qglDisable( GL_BLEND ); +} + +/* +================ +R_PrintLongString + +Workaround for ri.Printf's 1024 characters buffer limit. +================ +*/ +void R_PrintLongString(const char *string) { + char buffer[1024]; + const char *p; + int size = strlen(string); + + p = string; + while(size > 0) + { + Q_strncpyz(buffer, p, sizeof (buffer) ); + ri.Printf( PRINT_ALL, "%s", buffer ); + p += 1023; + size -= 1023; + } +} + +/* +================ +GfxInfo_f +================ +*/ +void GfxInfo_f( void ) +{ + const char *enablestrings[] = + { + "disabled", + "enabled" + }; + const char *fsstrings[] = + { + "windowed", + "fullscreen" + }; + + ri.Printf( PRINT_ALL, "\nGL_VENDOR: %s\n", glConfig.vendor_string ); + ri.Printf( PRINT_ALL, "GL_RENDERER: %s\n", glConfig.renderer_string ); + ri.Printf( PRINT_ALL, "GL_VERSION: %s\n", glConfig.version_string ); + ri.Printf( PRINT_ALL, "GL_EXTENSIONS: " ); + R_PrintLongString( glConfig.extensions_string ); + ri.Printf( PRINT_ALL, "\n" ); + ri.Printf( PRINT_ALL, "GL_MAX_TEXTURE_SIZE: %d\n", glConfig.maxTextureSize ); + ri.Printf( PRINT_ALL, "GL_MAX_TEXTURE_UNITS_ARB: %d\n", glConfig.numTextureUnits ); + ri.Printf( PRINT_ALL, "\nPIXELFORMAT: color(%d-bits) Z(%d-bit) stencil(%d-bits)\n", glConfig.colorBits, glConfig.depthBits, glConfig.stencilBits ); + ri.Printf( PRINT_ALL, "MODE: %d, %d x %d %s hz:", r_mode->integer, glConfig.vidWidth, glConfig.vidHeight, fsstrings[r_fullscreen->integer == 1] ); + if ( glConfig.displayFrequency ) + { + ri.Printf( PRINT_ALL, "%d\n", glConfig.displayFrequency ); + } + else + { + ri.Printf( PRINT_ALL, "N/A\n" ); + } + if ( glConfig.deviceSupportsGamma ) + { + ri.Printf( PRINT_ALL, "GAMMA: hardware w/ %d overbright bits\n", tr.overbrightBits ); + } + else + { + ri.Printf( PRINT_ALL, "GAMMA: software w/ %d overbright bits\n", tr.overbrightBits ); + } + + ri.Printf( PRINT_ALL, "texturemode: %s\n", r_textureMode->string ); + ri.Printf( PRINT_ALL, "picmip: %d\n", r_picmip->integer ); + ri.Printf( PRINT_ALL, "texture bits: %d\n", r_texturebits->integer ); + ri.Printf( PRINT_ALL, "multitexture: %s\n", enablestrings[qglActiveTextureARB != 0] ); + ri.Printf( PRINT_ALL, "compiled vertex arrays: %s\n", enablestrings[qglLockArraysEXT != 0 ] ); + ri.Printf( PRINT_ALL, "texenv add: %s\n", enablestrings[glConfig.textureEnvAddAvailable != 0] ); + ri.Printf( PRINT_ALL, "compressed textures: %s\n", enablestrings[glConfig.textureCompression!=TC_NONE] ); + if ( r_vertexLight->integer || glConfig.hardwareType == GLHW_PERMEDIA2 ) + { + ri.Printf( PRINT_ALL, "HACK: using vertex lightmap approximation\n" ); + } + if ( glConfig.hardwareType == GLHW_RAGEPRO ) + { + ri.Printf( PRINT_ALL, "HACK: ragePro approximations\n" ); + } + if ( glConfig.hardwareType == GLHW_RIVA128 ) + { + ri.Printf( PRINT_ALL, "HACK: riva128 approximations\n" ); + } + if ( r_finish->integer ) { + ri.Printf( PRINT_ALL, "Forcing glFinish\n" ); + } +} + +/* +================ +GfxMemInfo_f +================ +*/ +void GfxMemInfo_f( void ) +{ + switch (glRefConfig.memInfo) + { + case MI_NONE: + { + ri.Printf(PRINT_ALL, "No extension found for GPU memory info.\n"); + } + break; + case MI_NVX: + { + int value; + + qglGetIntegerv(GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX, &value); + ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX: %ikb\n", value); + + qglGetIntegerv(GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX, &value); + ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX: %ikb\n", value); + + qglGetIntegerv(GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX, &value); + ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX: %ikb\n", value); + + qglGetIntegerv(GL_GPU_MEMORY_INFO_EVICTION_COUNT_NVX, &value); + ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_EVICTION_COUNT_NVX: %i\n", value); + + qglGetIntegerv(GL_GPU_MEMORY_INFO_EVICTED_MEMORY_NVX, &value); + ri.Printf(PRINT_ALL, "GPU_MEMORY_INFO_EVICTED_MEMORY_NVX: %ikb\n", value); + } + break; + case MI_ATI: + { + // GL_ATI_meminfo + int value[4]; + + qglGetIntegerv(GL_VBO_FREE_MEMORY_ATI, &value[0]); + ri.Printf(PRINT_ALL, "VBO_FREE_MEMORY_ATI: %ikb total %ikb largest aux: %ikb total %ikb largest\n", value[0], value[1], value[2], value[3]); + + qglGetIntegerv(GL_TEXTURE_FREE_MEMORY_ATI, &value[0]); + ri.Printf(PRINT_ALL, "TEXTURE_FREE_MEMORY_ATI: %ikb total %ikb largest aux: %ikb total %ikb largest\n", value[0], value[1], value[2], value[3]); + + qglGetIntegerv(GL_RENDERBUFFER_FREE_MEMORY_ATI, &value[0]); + ri.Printf(PRINT_ALL, "RENDERBUFFER_FREE_MEMORY_ATI: %ikb total %ikb largest aux: %ikb total %ikb largest\n", value[0], value[1], value[2], value[3]); + } + break; + } +} + +/* +=============== +R_Register +=============== +*/ +void R_Register( void ) +{ + #ifdef USE_RENDERER_DLOPEN + com_altivec = ri.Cvar_Get("com_altivec", "1", CVAR_ARCHIVE); + #endif + + // + // latched and archived variables + // + r_allowExtensions = ri.Cvar_Get( "r_allowExtensions", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_ext_compressed_textures = ri.Cvar_Get( "r_ext_compressed_textures", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_ext_multitexture = ri.Cvar_Get( "r_ext_multitexture", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_ext_compiled_vertex_array = ri.Cvar_Get( "r_ext_compiled_vertex_array", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_texture_env_add = ri.Cvar_Get( "r_ext_texture_env_add", "1", CVAR_ARCHIVE | CVAR_LATCH); + + r_ext_draw_range_elements = ri.Cvar_Get( "r_ext_draw_range_elements", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_multi_draw_arrays = ri.Cvar_Get( "r_ext_multi_draw_arrays", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_framebuffer_object = ri.Cvar_Get( "r_ext_framebuffer_object", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_texture_float = ri.Cvar_Get( "r_ext_texture_float", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_arb_half_float_pixel = ri.Cvar_Get( "r_arb_half_float_pixel", "1", CVAR_ARCHIVE | CVAR_LATCH); + r_ext_framebuffer_multisample = ri.Cvar_Get( "r_ext_framebuffer_multisample", "0", CVAR_ARCHIVE | CVAR_LATCH); + + r_ext_texture_filter_anisotropic = ri.Cvar_Get( "r_ext_texture_filter_anisotropic", + "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_ext_max_anisotropy = ri.Cvar_Get( "r_ext_max_anisotropy", "2", CVAR_ARCHIVE | CVAR_LATCH ); + + r_picmip = ri.Cvar_Get ("r_picmip", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_roundImagesDown = ri.Cvar_Get ("r_roundImagesDown", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_colorMipLevels = ri.Cvar_Get ("r_colorMipLevels", "0", CVAR_LATCH ); + ri.Cvar_CheckRange( r_picmip, 0, 16, qtrue ); + r_detailTextures = ri.Cvar_Get( "r_detailtextures", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_texturebits = ri.Cvar_Get( "r_texturebits", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_colorbits = ri.Cvar_Get( "r_colorbits", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_stencilbits = ri.Cvar_Get( "r_stencilbits", "8", CVAR_ARCHIVE | CVAR_LATCH ); + r_depthbits = ri.Cvar_Get( "r_depthbits", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_ext_multisample = ri.Cvar_Get( "r_ext_multisample", "0", CVAR_ARCHIVE | CVAR_LATCH ); + ri.Cvar_CheckRange( r_ext_multisample, 0, 4, qtrue ); + r_overBrightBits = ri.Cvar_Get ("r_overBrightBits", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_ignorehwgamma = ri.Cvar_Get( "r_ignorehwgamma", "0", CVAR_ARCHIVE | CVAR_LATCH); + r_mode = ri.Cvar_Get( "r_mode", "-2", CVAR_ARCHIVE | CVAR_LATCH ); + r_fullscreen = ri.Cvar_Get( "r_fullscreen", "1", CVAR_ARCHIVE ); + r_noborder = ri.Cvar_Get("r_noborder", "0", CVAR_ARCHIVE); + r_width = ri.Cvar_Get( "r_width", "640", CVAR_ARCHIVE | CVAR_LATCH ); + r_height = ri.Cvar_Get( "r_height", "480", CVAR_ARCHIVE | CVAR_LATCH ); + r_pixelAspect = ri.Cvar_Get( "r_pixelAspect", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_simpleMipMaps = ri.Cvar_Get( "r_simpleMipMaps", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_vertexLight = ri.Cvar_Get( "r_vertexLight", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_uiFullScreen = ri.Cvar_Get( "r_uifullscreen", "0", 0); + r_subdivisions = ri.Cvar_Get ("r_subdivisions", "4", CVAR_ARCHIVE | CVAR_LATCH); + r_stereoEnabled = ri.Cvar_Get( "r_stereoEnabled", "0", CVAR_ARCHIVE | CVAR_LATCH); + r_greyscale = ri.Cvar_Get("r_greyscale", "0", CVAR_ARCHIVE | CVAR_LATCH); + ri.Cvar_CheckRange(r_greyscale, 0, 1, qfalse); + + r_softOverbright = ri.Cvar_Get( "r_softOverbright", "1", CVAR_ARCHIVE | CVAR_LATCH ); + + r_hdr = ri.Cvar_Get( "r_hdr", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_postProcess = ri.Cvar_Get( "r_postProcess", "1", CVAR_ARCHIVE ); + + r_toneMap = ri.Cvar_Get( "r_toneMap", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_forceToneMap = ri.Cvar_Get( "r_forceToneMap", "0", CVAR_CHEAT ); + r_forceToneMapMin = ri.Cvar_Get( "r_forceToneMapMin", "-8.0", CVAR_CHEAT ); + r_forceToneMapAvg = ri.Cvar_Get( "r_forceToneMapAvg", "-2.0", CVAR_CHEAT ); + r_forceToneMapMax = ri.Cvar_Get( "r_forceToneMapMax", "0.0", CVAR_CHEAT ); + + r_autoExposure = ri.Cvar_Get( "r_autoExposure", "1", CVAR_ARCHIVE ); + r_forceAutoExposure = ri.Cvar_Get( "r_forceAutoExposure", "0", CVAR_CHEAT ); + r_forceAutoExposureMin = ri.Cvar_Get( "r_forceAutoExposureMin", "-2.0", CVAR_CHEAT ); + r_forceAutoExposureMax = ri.Cvar_Get( "r_forceAutoExposureMax", "2.0", CVAR_CHEAT ); + + r_cameraExposure = ri.Cvar_Get( "r_cameraExposure", "0", CVAR_CHEAT ); + + r_srgb = ri.Cvar_Get( "r_srgb", "0", CVAR_ARCHIVE | CVAR_LATCH ); + + r_depthPrepass = ri.Cvar_Get( "r_depthPrepass", "1", CVAR_ARCHIVE ); + r_ssao = ri.Cvar_Get( "r_ssao", "0", CVAR_LATCH | CVAR_ARCHIVE ); + + r_normalMapping = ri.Cvar_Get( "r_normalMapping", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_specularMapping = ri.Cvar_Get( "r_specularMapping", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_deluxeMapping = ri.Cvar_Get( "r_deluxeMapping", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_parallaxMapping = ri.Cvar_Get( "r_parallaxMapping", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_normalAmbient = ri.Cvar_Get( "r_normalAmbient", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_dlightMode = ri.Cvar_Get( "r_dlightMode", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_pshadowDist = ri.Cvar_Get( "r_pshadowDist", "128", CVAR_ARCHIVE ); + r_recalcMD3Normals = ri.Cvar_Get( "r_recalcMD3Normals", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_mergeLightmaps = ri.Cvar_Get( "r_mergeLightmaps", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_imageUpsample = ri.Cvar_Get( "r_imageUpsample", "0", CVAR_ARCHIVE | CVAR_LATCH ); + r_imageUpsampleMaxSize = ri.Cvar_Get( "r_imageUpsampleMaxSize", "1024", CVAR_ARCHIVE | CVAR_LATCH ); + r_imageUpsampleType = ri.Cvar_Get( "r_imageUpsampleType", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_genNormalMaps = ri.Cvar_Get( "r_genNormalMaps", "0", CVAR_ARCHIVE | CVAR_LATCH ); + + r_forceSun = ri.Cvar_Get( "r_forceSun", "0", CVAR_CHEAT ); + r_forceSunMapLightScale = ri.Cvar_Get( "r_forceSunMapLightScale", "0.5", CVAR_CHEAT ); + r_forceSunLightScale = ri.Cvar_Get( "r_forceSunLightScale", "0.5", CVAR_CHEAT ); + r_forceSunAmbientScale = ri.Cvar_Get( "r_forceSunAmbientScale", "0.2", CVAR_CHEAT ); + r_drawSunRays = ri.Cvar_Get( "r_drawSunRays", "0", CVAR_ARCHIVE | CVAR_LATCH ); + + r_sunShadows = ri.Cvar_Get( "r_sunShadows", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_shadowFilter = ri.Cvar_Get( "r_shadowFilter", "1", CVAR_ARCHIVE | CVAR_LATCH ); + r_shadowMapSize = ri.Cvar_Get( "r_shadowMapSize", "1024", CVAR_ARCHIVE | CVAR_LATCH ); + r_shadowCascadeZNear = ri.Cvar_Get( "r_shadowCascadeZNear", "4", CVAR_ARCHIVE | CVAR_LATCH ); + r_shadowCascadeZFar = ri.Cvar_Get( "r_shadowCascadeZFar", "3072", CVAR_ARCHIVE | CVAR_LATCH ); + r_shadowCascadeZBias = ri.Cvar_Get( "r_shadowCascadeZBias", "-320", CVAR_ARCHIVE | CVAR_LATCH ); + + // + // temporary latched variables that can only change over a restart + // + r_fullbright = ri.Cvar_Get ("r_fullbright", "0", CVAR_LATCH|CVAR_CHEAT ); + r_mapOverBrightBits = ri.Cvar_Get ("r_mapOverBrightBits", "2", CVAR_LATCH ); + r_intensity = ri.Cvar_Get ("r_intensity", "1", CVAR_LATCH ); + r_singleShader = ri.Cvar_Get ("r_singleShader", "0", CVAR_CHEAT | CVAR_LATCH ); + + // + // archived variables that can change at any time + // + r_lodCurveError = ri.Cvar_Get( "r_lodCurveError", "250", CVAR_ARCHIVE|CVAR_CHEAT ); + r_lodbias = ri.Cvar_Get( "r_lodbias", "0", CVAR_ARCHIVE ); + r_flares = ri.Cvar_Get ("r_flares", "0", CVAR_ARCHIVE ); + r_znear = ri.Cvar_Get( "r_znear", "4", CVAR_CHEAT ); + ri.Cvar_CheckRange( r_znear, 0.001f, 200, qfalse ); + r_zproj = ri.Cvar_Get( "r_zproj", "64", CVAR_ARCHIVE ); + r_stereoSeparation = ri.Cvar_Get( "r_stereoSeparation", "64", CVAR_ARCHIVE ); + r_ignoreGLErrors = ri.Cvar_Get( "r_ignoreGLErrors", "1", CVAR_ARCHIVE ); + r_fastsky = ri.Cvar_Get( "r_fastsky", "0", CVAR_ARCHIVE ); + r_inGameVideo = ri.Cvar_Get( "r_inGameVideo", "1", CVAR_ARCHIVE ); + r_drawSun = ri.Cvar_Get( "r_drawSun", "0", CVAR_ARCHIVE ); + r_dynamiclight = ri.Cvar_Get( "r_dynamiclight", "1", CVAR_ARCHIVE ); + r_dlightBacks = ri.Cvar_Get( "r_dlightBacks", "1", CVAR_ARCHIVE ); + r_finish = ri.Cvar_Get ("r_finish", "0", CVAR_ARCHIVE); + r_textureMode = ri.Cvar_Get( "r_textureMode", "GL_LINEAR_MIPMAP_NEAREST", CVAR_ARCHIVE ); + r_swapInterval = ri.Cvar_Get( "r_swapInterval", "0", + CVAR_ARCHIVE | CVAR_LATCH ); + r_gamma = ri.Cvar_Get( "r_gamma", "1", CVAR_ARCHIVE ); + r_facePlaneCull = ri.Cvar_Get ("r_facePlaneCull", "1", CVAR_ARCHIVE ); + + r_railWidth = ri.Cvar_Get( "r_railWidth", "16", CVAR_ARCHIVE ); + r_railCoreWidth = ri.Cvar_Get( "r_railCoreWidth", "6", CVAR_ARCHIVE ); + r_railSegmentLength = ri.Cvar_Get( "r_railSegmentLength", "32", CVAR_ARCHIVE ); + + r_ambientScale = ri.Cvar_Get( "r_ambientScale", "0.6", CVAR_CHEAT ); + r_directedScale = ri.Cvar_Get( "r_directedScale", "1", CVAR_CHEAT ); + + r_anaglyphMode = ri.Cvar_Get("r_anaglyphMode", "0", CVAR_ARCHIVE); + r_mergeMultidraws = ri.Cvar_Get("r_mergeMultidraws", "1", CVAR_ARCHIVE); + r_mergeLeafSurfaces = ri.Cvar_Get("r_mergeLeafSurfaces", "1", CVAR_ARCHIVE); + + // + // temporary variables that can change at any time + // + r_showImages = ri.Cvar_Get( "r_showImages", "0", CVAR_TEMP ); + + r_debugLight = ri.Cvar_Get( "r_debuglight", "0", CVAR_TEMP ); + r_debugSort = ri.Cvar_Get( "r_debugSort", "0", CVAR_CHEAT ); + r_printShaders = ri.Cvar_Get( "r_printShaders", "0", 0 ); + r_saveFontData = ri.Cvar_Get( "r_saveFontData", "0", 0 ); + + r_nocurves = ri.Cvar_Get ("r_nocurves", "0", CVAR_CHEAT ); + r_drawworld = ri.Cvar_Get ("r_drawworld", "1", CVAR_CHEAT ); + r_lightmap = ri.Cvar_Get ("r_lightmap", "0", 0 ); + r_portalOnly = ri.Cvar_Get ("r_portalOnly", "0", CVAR_CHEAT ); + + r_flareSize = ri.Cvar_Get ("r_flareSize", "40", CVAR_CHEAT); + r_flareFade = ri.Cvar_Get ("r_flareFade", "7", CVAR_CHEAT); + r_flareCoeff = ri.Cvar_Get ("r_flareCoeff", FLARE_STDCOEFF, CVAR_CHEAT); + + r_skipBackEnd = ri.Cvar_Get ("r_skipBackEnd", "0", CVAR_CHEAT); + + r_measureOverdraw = ri.Cvar_Get( "r_measureOverdraw", "0", CVAR_CHEAT ); + r_lodscale = ri.Cvar_Get( "r_lodscale", "5", CVAR_CHEAT ); + r_norefresh = ri.Cvar_Get ("r_norefresh", "0", CVAR_CHEAT); + r_drawentities = ri.Cvar_Get ("r_drawentities", "1", CVAR_CHEAT ); + r_ignore = ri.Cvar_Get( "r_ignore", "1", CVAR_CHEAT ); + r_nocull = ri.Cvar_Get ("r_nocull", "0", CVAR_CHEAT); + r_novis = ri.Cvar_Get ("r_novis", "0", CVAR_CHEAT); + r_showcluster = ri.Cvar_Get ("r_showcluster", "0", CVAR_CHEAT); + r_speeds = ri.Cvar_Get ("r_speeds", "0", CVAR_CHEAT); + r_verbose = ri.Cvar_Get( "r_verbose", "0", CVAR_CHEAT ); + r_logFile = ri.Cvar_Get( "r_logFile", "0", CVAR_CHEAT ); + r_debugSurface = ri.Cvar_Get ("r_debugSurface", "0", CVAR_CHEAT); + r_nobind = ri.Cvar_Get ("r_nobind", "0", CVAR_CHEAT); + r_showtris = ri.Cvar_Get ("r_showtris", "0", CVAR_CHEAT); + r_showsky = ri.Cvar_Get ("r_showsky", "0", CVAR_CHEAT); + r_shownormals = ri.Cvar_Get ("r_shownormals", "0", CVAR_CHEAT); + r_clear = ri.Cvar_Get ("r_clear", "0", CVAR_CHEAT); + r_offsetFactor = ri.Cvar_Get( "r_offsetfactor", "-1", CVAR_CHEAT ); + r_offsetUnits = ri.Cvar_Get( "r_offsetunits", "-2", CVAR_CHEAT ); + r_drawBuffer = ri.Cvar_Get( "r_drawBuffer", "GL_BACK", CVAR_CHEAT ); + r_lockpvs = ri.Cvar_Get ("r_lockpvs", "0", CVAR_CHEAT); + r_noportals = ri.Cvar_Get ("r_noportals", "0", CVAR_CHEAT); + r_shadows = ri.Cvar_Get( "cg_shadows", "1", 0 ); + + r_marksOnTriangleMeshes = ri.Cvar_Get("r_marksOnTriangleMeshes", "0", CVAR_ARCHIVE); + + r_aviMotionJpegQuality = ri.Cvar_Get("r_aviMotionJpegQuality", "90", CVAR_ARCHIVE); + r_screenshotJpegQuality = ri.Cvar_Get("r_screenshotJpegQuality", "90", CVAR_ARCHIVE); + + r_maxpolys = ri.Cvar_Get( "r_maxpolys", va("%d", MAX_POLYS), 0); + r_maxpolyverts = ri.Cvar_Get( "r_maxpolyverts", va("%d", MAX_POLYVERTS), 0); + + // make sure all the commands added here are also + // removed in R_Shutdown + ri.Cmd_AddCommand( "imagelist", R_ImageList_f ); + ri.Cmd_AddCommand( "shaderlist", R_ShaderList_f ); + ri.Cmd_AddCommand( "skinlist", R_SkinList_f ); + ri.Cmd_AddCommand( "modellist", R_Modellist_f ); + ri.Cmd_AddCommand( "screenshot", R_ScreenShot_f ); + ri.Cmd_AddCommand( "screenshotJPEG", R_ScreenShotJPEG_f ); + ri.Cmd_AddCommand( "gfxinfo", GfxInfo_f ); + ri.Cmd_AddCommand( "minimize", GLimp_Minimize ); + ri.Cmd_AddCommand( "gfxmeminfo", GfxMemInfo_f ); +} + +void R_InitQueries(void) +{ + if (!glRefConfig.occlusionQuery) + return; + + if (r_drawSunRays->integer) + qglGenQueriesARB(ARRAY_LEN(tr.sunFlareQuery), tr.sunFlareQuery); +} + +void R_ShutDownQueries(void) +{ + if (!glRefConfig.occlusionQuery) + return; + + if (r_drawSunRays->integer) + qglDeleteQueriesARB(ARRAY_LEN(tr.sunFlareQuery), tr.sunFlareQuery); +} + +/* +=============== +R_Init +=============== +*/ +void R_Init( void ) { + int err; + int i; + byte *ptr; + + ri.Printf( PRINT_ALL, "----- R_Init -----\n" ); + + // clear all our internal state + Com_Memset( &tr, 0, sizeof( tr ) ); + Com_Memset( &backEnd, 0, sizeof( backEnd ) ); + Com_Memset( &tess, 0, sizeof( tess ) ); + +// Swap_Init(); + + if ( (intptr_t)tess.xyz & 15 ) { + ri.Printf( PRINT_WARNING, "tess.xyz not 16 byte aligned\n" ); + } + //Com_Memset( tess.constantColor255, 255, sizeof( tess.constantColor255 ) ); + + // + // init function tables + // + for ( i = 0; i < FUNCTABLE_SIZE; i++ ) + { + tr.sinTable[i] = sin( DEG2RAD( i * 360.0f / ( ( float ) ( FUNCTABLE_SIZE - 1 ) ) ) ); + tr.squareTable[i] = ( i < FUNCTABLE_SIZE/2 ) ? 1.0f : -1.0f; + tr.sawToothTable[i] = (float)i / FUNCTABLE_SIZE; + tr.inverseSawToothTable[i] = 1.0f - tr.sawToothTable[i]; + + if ( i < FUNCTABLE_SIZE / 2 ) + { + if ( i < FUNCTABLE_SIZE / 4 ) + { + tr.triangleTable[i] = ( float ) i / ( FUNCTABLE_SIZE / 4 ); + } + else + { + tr.triangleTable[i] = 1.0f - tr.triangleTable[i-FUNCTABLE_SIZE / 4]; + } + } + else + { + tr.triangleTable[i] = -tr.triangleTable[i-FUNCTABLE_SIZE/2]; + } + } + + R_InitFogTable(); + + R_NoiseInit(); + + R_Register(); + + max_polys = r_maxpolys->integer; + if (max_polys < MAX_POLYS) + max_polys = MAX_POLYS; + + max_polyverts = r_maxpolyverts->integer; + if (max_polyverts < MAX_POLYVERTS) + max_polyverts = MAX_POLYVERTS; + + ptr = ri.Hunk_Alloc( sizeof( *backEndData ) + sizeof(srfPoly_t) * max_polys + sizeof(polyVert_t) * max_polyverts, h_low); + backEndData = (backEndData_t *) ptr; + backEndData->polys = (srfPoly_t *) ((char *) ptr + sizeof( *backEndData )); + backEndData->polyVerts = (polyVert_t *) ((char *) ptr + sizeof( *backEndData ) + sizeof(srfPoly_t) * max_polys); + R_InitNextFrame(); + + InitOpenGL(); + + R_InitImages(); + + if (glRefConfig.framebufferObject) + FBO_Init(); + + GLSL_InitGPUShaders(); + + R_InitVBOs(); + + R_InitShaders(); + + R_InitSkins(); + + R_ModelInit(); + + R_InitFreeType(); + + R_InitQueries(); + + + err = qglGetError(); + if ( err != GL_NO_ERROR ) + ri.Printf (PRINT_ALL, "glGetError() = 0x%x\n", err); + + // print info + GfxInfo_f(); + ri.Printf( PRINT_ALL, "----- finished R_Init -----\n" ); +} + +/* +=============== +RE_Shutdown +=============== +*/ +void RE_Shutdown( qboolean destroyWindow ) { + + ri.Printf( PRINT_ALL, "RE_Shutdown( %i )\n", destroyWindow ); + + ri.Cmd_RemoveCommand ("modellist"); + ri.Cmd_RemoveCommand ("screenshotJPEG"); + ri.Cmd_RemoveCommand ("screenshot"); + ri.Cmd_RemoveCommand ("imagelist"); + ri.Cmd_RemoveCommand ("shaderlist"); + ri.Cmd_RemoveCommand ("skinlist"); + ri.Cmd_RemoveCommand ("gfxinfo"); + ri.Cmd_RemoveCommand("minimize"); + ri.Cmd_RemoveCommand( "shaderstate" ); + ri.Cmd_RemoveCommand( "gfxmeminfo" ); + + + if ( tr.registered ) { + R_IssuePendingRenderCommands(); + R_ShutDownQueries(); + if (glRefConfig.framebufferObject) + FBO_Shutdown(); + R_DeleteTextures(); + R_ShutdownVBOs(); + GLSL_ShutdownGPUShaders(); + } + + R_DoneFreeType(); + + // shut down platform specific OpenGL stuff + if ( destroyWindow ) { + GLimp_Shutdown(); + } + + tr.registered = qfalse; +} + + +/* +============= +RE_EndRegistration + +Touch all images to make sure they are resident +============= +*/ +void RE_EndRegistration( void ) { + R_IssuePendingRenderCommands(); + if (!ri.Sys_LowPhysicalMemory()) { + RB_ShowImages(); + } +} + + +/* +@@@@@@@@@@@@@@@@@@@@@ +GetRefAPI + +@@@@@@@@@@@@@@@@@@@@@ +*/ +#ifdef USE_RENDERER_DLOPEN +Q_EXPORT refexport_t* QDECL GetRefAPI ( int apiVersion, refimport_t *rimp ) { +#else +refexport_t *GetRefAPI ( int apiVersion, refimport_t *rimp ) { +#endif + + static refexport_t re; + + ri = *rimp; + + Com_Memset( &re, 0, sizeof( re ) ); + + if ( apiVersion != REF_API_VERSION ) { + ri.Printf(PRINT_ALL, "Mismatched REF_API_VERSION: expected %i, got %i\n", + REF_API_VERSION, apiVersion ); + return NULL; + } + + // the RE_ functions are Renderer Entry points + + re.Shutdown = RE_Shutdown; + + re.BeginRegistration = RE_BeginRegistration; + re.RegisterModel = RE_RegisterModel; + re.RegisterSkin = RE_RegisterSkin; + re.RegisterShader = RE_RegisterShader; + re.RegisterShaderNoMip = RE_RegisterShaderNoMip; + re.LoadWorld = RE_LoadWorldMap; + re.SetWorldVisData = RE_SetWorldVisData; + re.EndRegistration = RE_EndRegistration; + + re.BeginFrame = RE_BeginFrame; + re.EndFrame = RE_EndFrame; + + re.MarkFragments = R_MarkFragments; + re.LerpTag = R_LerpTag; + re.ModelBounds = R_ModelBounds; + + re.ClearScene = RE_ClearScene; + re.AddRefEntityToScene = RE_AddRefEntityToScene; + re.AddPolyToScene = RE_AddPolyToScene; + re.LightForPoint = R_LightForPoint; + re.AddLightToScene = RE_AddLightToScene; + re.AddAdditiveLightToScene = RE_AddAdditiveLightToScene; + re.RenderScene = RE_RenderScene; + + re.SetColor = RE_SetColor; + re.SetClipRegion = RE_SetClipRegion; + re.DrawStretchPic = RE_StretchPic; + re.DrawStretchRaw = RE_StretchRaw; + re.UploadCinematic = RE_UploadCinematic; + + re.RegisterFont = RE_RegisterFont; + re.RemapShader = R_RemapShader; + re.GetEntityToken = R_GetEntityToken; + re.inPVS = R_inPVS; + + re.TakeVideoFrame = RE_TakeVideoFrame; + + return &re; +} diff --git a/src/renderergl2/tr_light.c b/src/renderergl2/tr_light.c new file mode 100644 index 00000000..4e90ce20 --- /dev/null +++ b/src/renderergl2/tr_light.c @@ -0,0 +1,447 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_light.c + +#include "tr_local.h" + +#define DLIGHT_AT_RADIUS 16 +// at the edge of a dlight's influence, this amount of light will be added + +#define DLIGHT_MINIMUM_RADIUS 16 +// never calculate a range less than this to prevent huge light numbers + + +/* +=============== +R_TransformDlights + +Transforms the origins of an array of dlights. +Used by both the front end (for DlightBmodel) and +the back end (before doing the lighting calculation) +=============== +*/ +void R_TransformDlights( int count, dlight_t *dl, orientationr_t *or) { + int i; + vec3_t temp; + + for ( i = 0 ; i < count ; i++, dl++ ) { + VectorSubtract( dl->origin, or->origin, temp ); + dl->transformed[0] = DotProduct( temp, or->axis[0] ); + dl->transformed[1] = DotProduct( temp, or->axis[1] ); + dl->transformed[2] = DotProduct( temp, or->axis[2] ); + } +} + +/* +============= +R_DlightBmodel + +Determine which dynamic lights may effect this bmodel +============= +*/ +void R_DlightBmodel( bmodel_t *bmodel ) { + int i, j; + dlight_t *dl; + int mask; + msurface_t *surf; + + // transform all the lights + R_TransformDlights( tr.refdef.num_dlights, tr.refdef.dlights, &tr.or ); + + mask = 0; + for ( i=0 ; itransformed[j] - bmodel->bounds[1][j] > dl->radius ) { + break; + } + if ( bmodel->bounds[0][j] - dl->transformed[j] > dl->radius ) { + break; + } + } + if ( j < 3 ) { + continue; + } + + // we need to check this light + mask |= 1 << i; + } + + tr.currentEntity->needDlights = (mask != 0); + + // set the dlight bits in all the surfaces + for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) { + surf = tr.world->surfaces + bmodel->firstSurface + i; + + if ( *surf->data == SF_FACE ) { + ((srfSurfaceFace_t *)surf->data)->dlightBits = mask; + } else if ( *surf->data == SF_GRID ) { + ((srfGridMesh_t *)surf->data)->dlightBits = mask; + } else if ( *surf->data == SF_TRIANGLES ) { + ((srfTriangles_t *)surf->data)->dlightBits = mask; + } + } +} + + +/* +============================================================================= + +LIGHT SAMPLING + +============================================================================= +*/ + +extern cvar_t *r_ambientScale; +extern cvar_t *r_directedScale; +extern cvar_t *r_debugLight; + +/* +================= +R_SetupEntityLightingGrid + +================= +*/ +static void R_SetupEntityLightingGrid( trRefEntity_t *ent, world_t *world ) { + vec3_t lightOrigin; + int pos[3]; + int i, j; + byte *gridData; + float frac[3]; + int gridStep[3]; + vec3_t direction; + float totalFactor; + + if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) { + // seperate lightOrigins are needed so an object that is + // sinking into the ground can still be lit, and so + // multi-part models can be lit identically + VectorCopy( ent->e.lightingOrigin, lightOrigin ); + } else { + VectorCopy( ent->e.origin, lightOrigin ); + } + + VectorSubtract( lightOrigin, world->lightGridOrigin, lightOrigin ); + for ( i = 0 ; i < 3 ; i++ ) { + float v; + + v = lightOrigin[i]*world->lightGridInverseSize[i]; + pos[i] = floor( v ); + frac[i] = v - pos[i]; + if ( pos[i] < 0 ) { + pos[i] = 0; + } else if ( pos[i] >= world->lightGridBounds[i] - 1 ) { + pos[i] = world->lightGridBounds[i] - 1; + } + } + + VectorClear( ent->ambientLight ); + VectorClear( ent->directedLight ); + VectorClear( direction ); + + assert( world->lightGridData ); // NULL with -nolight maps + + // trilerp the light value + gridStep[0] = 8; + gridStep[1] = 8 * world->lightGridBounds[0]; + gridStep[2] = 8 * world->lightGridBounds[0] * world->lightGridBounds[1]; + gridData = world->lightGridData + pos[0] * gridStep[0] + + pos[1] * gridStep[1] + pos[2] * gridStep[2]; + + totalFactor = 0; + for ( i = 0 ; i < 8 ; i++ ) { + float factor; + byte *data; + int lat, lng; + vec3_t normal; + qboolean ignore; + #if idppc + float d0, d1, d2, d3, d4, d5; + #endif + factor = 1.0; + data = gridData; + ignore = qfalse; + for ( j = 0 ; j < 3 ; j++ ) { + if ( i & (1<= world->lightGridBounds[j] - 1) + { + ignore = qtrue; // ignore values outside lightgrid + } + factor *= frac[j]; + data += gridStep[j]; + } else { + factor *= (1.0f - frac[j]); + } + } + + if ( ignore ) + continue; + + if (world->hdrLightGrid) + { + float *hdrData = world->hdrLightGrid + (int)(data - world->lightGridData) / 8 * 6; + if (!(hdrData[0]+hdrData[1]+hdrData[2]+hdrData[3]+hdrData[4]+hdrData[5]) ) { + continue; // ignore samples in walls + } + } + else + { + if (!(data[0]+data[1]+data[2]+data[3]+data[4]+data[5]) ) { + continue; // ignore samples in walls + } + } + totalFactor += factor; + #if idppc + d0 = data[0]; d1 = data[1]; d2 = data[2]; + d3 = data[3]; d4 = data[4]; d5 = data[5]; + + ent->ambientLight[0] += factor * d0; + ent->ambientLight[1] += factor * d1; + ent->ambientLight[2] += factor * d2; + + ent->directedLight[0] += factor * d3; + ent->directedLight[1] += factor * d4; + ent->directedLight[2] += factor * d5; + #else + if (world->hdrLightGrid) + { + // FIXME: this is hideous + float *hdrData = world->hdrLightGrid + (int)(data - world->lightGridData) / 8 * 6; + + ent->ambientLight[0] += factor * hdrData[0]; + ent->ambientLight[1] += factor * hdrData[1]; + ent->ambientLight[2] += factor * hdrData[2]; + + ent->directedLight[0] += factor * hdrData[3]; + ent->directedLight[1] += factor * hdrData[4]; + ent->directedLight[2] += factor * hdrData[5]; + } + else + { + ent->ambientLight[0] += factor * data[0]; + ent->ambientLight[1] += factor * data[1]; + ent->ambientLight[2] += factor * data[2]; + + ent->directedLight[0] += factor * data[3]; + ent->directedLight[1] += factor * data[4]; + ent->directedLight[2] += factor * data[5]; + } + #endif + lat = data[7]; + lng = data[6]; + lat *= (FUNCTABLE_SIZE/256); + lng *= (FUNCTABLE_SIZE/256); + + // decode X as cos( lat ) * sin( long ) + // decode Y as sin( lat ) * sin( long ) + // decode Z as cos( long ) + + normal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; + normal[1] = tr.sinTable[lat] * tr.sinTable[lng]; + normal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; + + VectorMA( direction, factor, normal, direction ); + } + + if ( totalFactor > 0 && totalFactor < 0.99 ) { + totalFactor = 1.0f / totalFactor; + VectorScale( ent->ambientLight, totalFactor, ent->ambientLight ); + VectorScale( ent->directedLight, totalFactor, ent->directedLight ); + } + + VectorScale( ent->ambientLight, r_ambientScale->value, ent->ambientLight ); + VectorScale( ent->directedLight, r_directedScale->value, ent->directedLight ); + + VectorNormalize2( direction, ent->lightDir ); +} + + +/* +=============== +LogLight +=============== +*/ +static void LogLight( trRefEntity_t *ent ) { + int max1, max2; + + if ( !(ent->e.renderfx & RF_FIRST_PERSON ) ) { + return; + } + + max1 = ent->ambientLight[0]; + if ( ent->ambientLight[1] > max1 ) { + max1 = ent->ambientLight[1]; + } else if ( ent->ambientLight[2] > max1 ) { + max1 = ent->ambientLight[2]; + } + + max2 = ent->directedLight[0]; + if ( ent->directedLight[1] > max2 ) { + max2 = ent->directedLight[1]; + } else if ( ent->directedLight[2] > max2 ) { + max2 = ent->directedLight[2]; + } + + ri.Printf( PRINT_ALL, "amb:%i dir:%i\n", max1, max2 ); +} + +/* +================= +R_SetupEntityLighting + +Calculates all the lighting values that will be used +by the Calc_* functions +================= +*/ +void R_SetupEntityLighting( const trRefdef_t *refdef, trRefEntity_t *ent ) { + int i; + dlight_t *dl; + float power; + vec3_t dir; + float d; + vec3_t lightDir; + vec3_t lightOrigin; + + // lighting calculations + if ( ent->lightingCalculated ) { + return; + } + ent->lightingCalculated = qtrue; + + // + // trace a sample point down to find ambient light + // + if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) { + // seperate lightOrigins are needed so an object that is + // sinking into the ground can still be lit, and so + // multi-part models can be lit identically + VectorCopy( ent->e.lightingOrigin, lightOrigin ); + } else { + VectorCopy( ent->e.origin, lightOrigin ); + } + + // if NOWORLDMODEL, only use dynamic lights (menu system, etc) + if ( !(refdef->rdflags & RDF_NOWORLDMODEL ) + && tr.world->lightGridData ) { + R_SetupEntityLightingGrid( ent, tr.world ); + } else { + ent->ambientLight[0] = ent->ambientLight[1] = + ent->ambientLight[2] = tr.identityLight * 150; + ent->directedLight[0] = ent->directedLight[1] = + ent->directedLight[2] = tr.identityLight * 150; + VectorCopy( tr.sunDirection, ent->lightDir ); + } + + // bonus items and view weapons have a fixed minimum add + if ( !r_hdr->integer /* ent->e.renderfx & RF_MINLIGHT */ ) { + // give everything a minimum light add + ent->ambientLight[0] += tr.identityLight * 32; + ent->ambientLight[1] += tr.identityLight * 32; + ent->ambientLight[2] += tr.identityLight * 32; + } + + // + // modify the light by dynamic lights + // + d = VectorLength( ent->directedLight ); + VectorScale( ent->lightDir, d, lightDir ); + + for ( i = 0 ; i < refdef->num_dlights ; i++ ) { + dl = &refdef->dlights[i]; + VectorSubtract( dl->origin, lightOrigin, dir ); + d = VectorNormalize( dir ); + + power = DLIGHT_AT_RADIUS * ( dl->radius * dl->radius ); + if ( d < DLIGHT_MINIMUM_RADIUS ) { + d = DLIGHT_MINIMUM_RADIUS; + } + d = power / ( d * d ); + + VectorMA( ent->directedLight, d, dl->color, ent->directedLight ); + VectorMA( lightDir, d, dir, lightDir ); + } + + // clamp ambient + if ( !r_hdr->integer ) + { + for ( i = 0 ; i < 3 ; i++ ) { + if ( ent->ambientLight[i] > tr.identityLightByte ) { + ent->ambientLight[i] = tr.identityLightByte; + } + } + } + + if ( r_debugLight->integer ) { + LogLight( ent ); + } + + // save out the byte packet version + ((byte *)&ent->ambientLightInt)[0] = ri.ftol(ent->ambientLight[0]); + ((byte *)&ent->ambientLightInt)[1] = ri.ftol(ent->ambientLight[1]); + ((byte *)&ent->ambientLightInt)[2] = ri.ftol(ent->ambientLight[2]); + ((byte *)&ent->ambientLightInt)[3] = 0xff; + + // transform the direction to local space + // no need to do this if using lightentity glsl shader + VectorNormalize( lightDir ); + VectorCopy(lightDir, ent->lightDir); +} + +/* +================= +R_LightForPoint +================= +*/ +int R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ) +{ + trRefEntity_t ent; + + if ( tr.world->lightGridData == NULL ) + return qfalse; + + Com_Memset(&ent, 0, sizeof(ent)); + VectorCopy( point, ent.e.origin ); + R_SetupEntityLightingGrid( &ent, tr.world ); + VectorCopy(ent.ambientLight, ambientLight); + VectorCopy(ent.directedLight, directedLight); + VectorCopy(ent.lightDir, lightDir); + + return qtrue; +} + + +int R_LightDirForPoint( vec3_t point, vec3_t lightDir, vec3_t normal, world_t *world ) +{ + trRefEntity_t ent; + + if ( world->lightGridData == NULL ) + return qfalse; + + Com_Memset(&ent, 0, sizeof(ent)); + VectorCopy( point, ent.e.origin ); + R_SetupEntityLightingGrid( &ent, world ); + VectorCopy(ent.lightDir, lightDir); + + return qtrue; +} diff --git a/src/renderergl2/tr_local.h b/src/renderergl2/tr_local.h new file mode 100644 index 00000000..dd0b62d1 --- /dev/null +++ b/src/renderergl2/tr_local.h @@ -0,0 +1,2856 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + + +#ifndef TR_LOCAL_H +#define TR_LOCAL_H + +#include "../qcommon/q_shared.h" +#include "../qcommon/qfiles.h" +#include "../qcommon/qcommon.h" +#include "../renderercommon/tr_public.h" +#include "tr_extratypes.h" +#include "tr_extramath.h" +#include "tr_fbo.h" +#include "tr_postprocess.h" +#include "qgl.h" +#include "../renderercommon/iqm.h" + +#define GL_INDEX_TYPE GL_UNSIGNED_INT +typedef unsigned int glIndex_t; + +#define BUFFER_OFFSET(i) ((char *)NULL + (i)) + +// 14 bits +// can't be increased without changing bit packing for drawsurfs +// see QSORT_SHADERNUM_SHIFT +#define SHADERNUM_BITS 14 +#define MAX_SHADERS (1<or.origin in local coordinates + float modelMatrix[16]; + float transformMatrix[16]; +} orientationr_t; + +typedef enum +{ + IMGTYPE_COLORALPHA, // for color, lightmap, diffuse, and specular + IMGTYPE_NORMAL, + IMGTYPE_NORMALHEIGHT, + IMGTYPE_DELUXE, // normals are swizzled, deluxe are not +} imgType_t; + +typedef enum +{ + IMGFLAG_NONE = 0x0000, + IMGFLAG_MIPMAP = 0x0001, + IMGFLAG_PICMIP = 0x0002, + IMGFLAG_CUBEMAP = 0x0004, + IMGFLAG_NO_COMPRESSION = 0x0010, + IMGFLAG_NOLIGHTSCALE = 0x0020, + IMGFLAG_CLAMPTOEDGE = 0x0040, + IMGFLAG_SRGB = 0x0080, + IMGFLAG_GENNORMALMAP = 0x0100, +} imgFlags_t; + +typedef struct image_s { + char imgName[MAX_QPATH]; // game path, including extension + int width, height; // source image + int uploadWidth, uploadHeight; // after power of two and picmip but not including clamp to MAX_TEXTURE_SIZE + GLuint texnum; // gl texture binding + + int frameUsed; // for texture usage in frame statistics + + int internalFormat; + int TMU; // only needed for voodoo2 + + imgType_t type; + imgFlags_t flags; + + struct image_s* next; +} image_t; + +typedef enum +{ + VBO_USAGE_STATIC, + VBO_USAGE_DYNAMIC +} vboUsage_t; + +typedef struct VBO_s +{ + char name[MAX_QPATH]; + + uint32_t vertexesVBO; + int vertexesSize; // amount of memory data allocated for all vertices in bytes + uint32_t ofs_xyz; + uint32_t ofs_normal; + uint32_t ofs_st; + uint32_t ofs_lightmap; + uint32_t ofs_vertexcolor; + uint32_t ofs_lightdir; +#ifdef USE_VERT_TANGENT_SPACE + uint32_t ofs_tangent; + uint32_t ofs_bitangent; +#endif + uint32_t stride_xyz; + uint32_t stride_normal; + uint32_t stride_st; + uint32_t stride_lightmap; + uint32_t stride_vertexcolor; + uint32_t stride_lightdir; +#ifdef USE_VERT_TANGENT_SPACE + uint32_t stride_tangent; + uint32_t stride_bitangent; +#endif + uint32_t size_xyz; + uint32_t size_normal; + + int attribs; +} VBO_t; + +typedef struct IBO_s +{ + char name[MAX_QPATH]; + + uint32_t indexesVBO; + int indexesSize; // amount of memory data allocated for all triangles in bytes +// uint32_t ofsIndexes; +} IBO_t; + +//=============================================================================== + +typedef enum { + SS_BAD, + SS_PORTAL, // mirrors, portals, viewscreens + SS_ENVIRONMENT, // sky box + SS_OPAQUE, // opaque + + SS_DECAL, // scorch marks, etc. + SS_SEE_THROUGH, // ladders, grates, grills that may have small blended edges + // in addition to alpha test + SS_BANNER, + + SS_FOG, + + SS_UNDERWATER, // for items that should be drawn in front of the water plane + + SS_BLEND0, // regular transparency and filters + SS_BLEND1, // generally only used for additive type effects + SS_BLEND2, + SS_BLEND3, + + SS_BLEND6, + SS_STENCIL_SHADOW, + SS_ALMOST_NEAREST, // gun smoke puffs + + SS_NEAREST // blood blobs +} shaderSort_t; + + +#define MAX_SHADER_STAGES 8 + +typedef enum { + GF_NONE, + + GF_SIN, + GF_SQUARE, + GF_TRIANGLE, + GF_SAWTOOTH, + GF_INVERSE_SAWTOOTH, + + GF_NOISE + +} genFunc_t; + + +typedef enum { + DEFORM_NONE, + DEFORM_WAVE, + DEFORM_NORMALS, + DEFORM_BULGE, + DEFORM_MOVE, + DEFORM_PROJECTION_SHADOW, + DEFORM_AUTOSPRITE, + DEFORM_AUTOSPRITE2, + DEFORM_TEXT0, + DEFORM_TEXT1, + DEFORM_TEXT2, + DEFORM_TEXT3, + DEFORM_TEXT4, + DEFORM_TEXT5, + DEFORM_TEXT6, + DEFORM_TEXT7 +} deform_t; + +// deformVertexes types that can be handled by the GPU +typedef enum +{ + // do not edit: same as genFunc_t + + DGEN_NONE, + DGEN_WAVE_SIN, + DGEN_WAVE_SQUARE, + DGEN_WAVE_TRIANGLE, + DGEN_WAVE_SAWTOOTH, + DGEN_WAVE_INVERSE_SAWTOOTH, + DGEN_WAVE_NOISE, + + // do not edit until this line + + DGEN_BULGE, + DGEN_MOVE +} deformGen_t; + +typedef enum { + AGEN_IDENTITY, + AGEN_SKIP, + AGEN_ENTITY, + AGEN_ONE_MINUS_ENTITY, + AGEN_VERTEX, + AGEN_ONE_MINUS_VERTEX, + AGEN_LIGHTING_SPECULAR, + AGEN_WAVEFORM, + AGEN_PORTAL, + AGEN_CONST, + AGEN_FRESNEL +} alphaGen_t; + +typedef enum { + CGEN_BAD, + CGEN_IDENTITY_LIGHTING, // tr.identityLight + CGEN_IDENTITY, // always (1,1,1,1) + CGEN_ENTITY, // grabbed from entity's modulate field + CGEN_ONE_MINUS_ENTITY, // grabbed from 1 - entity.modulate + CGEN_EXACT_VERTEX, // tess.vertexColors + CGEN_VERTEX, // tess.vertexColors * tr.identityLight + CGEN_EXACT_VERTEX_LIT, // like CGEN_EXACT_VERTEX but takes a light direction from the lightgrid + CGEN_VERTEX_LIT, // like CGEN_VERTEX but takes a light direction from the lightgrid + CGEN_ONE_MINUS_VERTEX, + CGEN_WAVEFORM, // programmatically generated + CGEN_LIGHTING_DIFFUSE, + CGEN_FOG, // standard fog + CGEN_CONST // fixed color +} colorGen_t; + +typedef enum { + TCGEN_BAD, + TCGEN_IDENTITY, // clear to 0,0 + TCGEN_LIGHTMAP, + TCGEN_TEXTURE, + TCGEN_ENVIRONMENT_MAPPED, + TCGEN_FOG, + TCGEN_VECTOR // S and T from world coordinates +} texCoordGen_t; + +typedef enum { + ACFF_NONE, + ACFF_MODULATE_RGB, + ACFF_MODULATE_RGBA, + ACFF_MODULATE_ALPHA +} acff_t; + +typedef struct { + genFunc_t func; + + float base; + float amplitude; + float phase; + float frequency; +} waveForm_t; + +#define TR_MAX_TEXMODS 4 + +typedef enum { + TMOD_NONE, + TMOD_TRANSFORM, + TMOD_TURBULENT, + TMOD_SCROLL, + TMOD_SCALE, + TMOD_STRETCH, + TMOD_ROTATE, + TMOD_ENTITY_TRANSLATE +} texMod_t; + +#define MAX_SHADER_DEFORMS 3 +typedef struct { + deform_t deformation; // vertex coordinate modification type + + vec3_t moveVector; + waveForm_t deformationWave; + float deformationSpread; + + float bulgeWidth; + float bulgeHeight; + float bulgeSpeed; +} deformStage_t; + + +typedef struct { + texMod_t type; + + // used for TMOD_TURBULENT and TMOD_STRETCH + waveForm_t wave; + + // used for TMOD_TRANSFORM + float matrix[2][2]; // s' = s * m[0][0] + t * m[1][0] + trans[0] + float translate[2]; // t' = s * m[0][1] + t * m[0][1] + trans[1] + + // used for TMOD_SCALE + float scale[2]; // s *= scale[0] + // t *= scale[1] + + // used for TMOD_SCROLL + float scroll[2]; // s' = s + scroll[0] * time + // t' = t + scroll[1] * time + + // + = clockwise + // - = counterclockwise + float rotateSpeed; + +} texModInfo_t; + + +#define MAX_IMAGE_ANIMATIONS 8 + +typedef struct { + image_t *image[MAX_IMAGE_ANIMATIONS]; + int numImageAnimations; + float imageAnimationSpeed; + + texCoordGen_t tcGen; + vec3_t tcGenVectors[2]; + + int numTexMods; + texModInfo_t *texMods; + + int videoMapHandle; + qboolean isLightmap; + qboolean vertexLightmap; + qboolean isVideoMap; +} textureBundle_t; + +enum +{ + TB_COLORMAP = 0, + TB_DIFFUSEMAP = 0, + TB_LIGHTMAP = 1, + TB_LEVELSMAP = 1, + TB_SHADOWMAP = 1, + TB_NORMALMAP = 2, + TB_DELUXEMAP = 3, + TB_SHADOWMAP2 = 3, + TB_SPECULARMAP = 4, + TB_SHADOWMAP3 = 5, + NUM_TEXTURE_BUNDLES = 6 +}; + +typedef enum +{ + // material shader stage types + ST_COLORMAP = 0, // vanilla Q3A style shader treatening + ST_DIFFUSEMAP = 0, // treat color and diffusemap the same + ST_NORMALMAP, + ST_NORMALPARALLAXMAP, + ST_SPECULARMAP, + ST_GLSL +} stageType_t; + +typedef struct { + qboolean active; + + textureBundle_t bundle[NUM_TEXTURE_BUNDLES]; + + waveForm_t rgbWave; + colorGen_t rgbGen; + + waveForm_t alphaWave; + alphaGen_t alphaGen; + + byte constantColor[4]; // for CGEN_CONST and AGEN_CONST + + unsigned stateBits; // GLS_xxxx mask + + acff_t adjustColorsForFog; + + qboolean isDetail; + + stageType_t type; + struct shaderProgram_s *glslShaderGroup; + int glslShaderIndex; + vec2_t materialInfo; +} shaderStage_t; + +struct shaderCommands_s; + +// any change in the LIGHTMAP_* defines here MUST be reflected in +// R_FindShader() in tr_bsp.c +#define LIGHTMAP_2D -4 // shader is for 2D rendering +#define LIGHTMAP_BY_VERTEX -3 // pre-lit triangle models +#define LIGHTMAP_WHITEIMAGE -2 +#define LIGHTMAP_NONE -1 + +typedef enum { + CT_FRONT_SIDED, + CT_BACK_SIDED, + CT_TWO_SIDED +} cullType_t; + +typedef enum { + FP_NONE, // surface is translucent and will just be adjusted properly + FP_EQUAL, // surface is opaque but possibly alpha tested + FP_LE // surface is trnaslucent, but still needs a fog pass (fog surface) +} fogPass_t; + +typedef struct { + float cloudHeight; + image_t *outerbox[6], *innerbox[6]; +} skyParms_t; + +typedef struct { + vec3_t color; + float depthForOpaque; +} fogParms_t; + + +typedef struct shader_s { + char name[MAX_QPATH]; // game path, including extension + int lightmapIndex; // for a shader to match, both name and lightmapIndex must match + + int index; // this shader == tr.shaders[index] + int sortedIndex; // this shader == tr.sortedShaders[sortedIndex] + + float sort; // lower numbered shaders draw before higher numbered + + qboolean defaultShader; // we want to return index 0 if the shader failed to + // load for some reason, but R_FindShader should + // still keep a name allocated for it, so if + // something calls RE_RegisterShader again with + // the same name, we don't try looking for it again + + qboolean explicitlyDefined; // found in a .shader file + + int surfaceFlags; // if explicitlyDefined, this will have SURF_* flags + int contentFlags; + + qboolean entityMergable; // merge across entites optimizable (smoke, blood) + + qboolean isSky; + skyParms_t sky; + fogParms_t fogParms; + + float portalRange; // distance to fog out at + qboolean isPortal; + + int multitextureEnv; // 0, GL_MODULATE, GL_ADD (FIXME: put in stage) + + cullType_t cullType; // CT_FRONT_SIDED, CT_BACK_SIDED, or CT_TWO_SIDED + qboolean polygonOffset; // set for decals and other items that must be offset + qboolean noMipMaps; // for console fonts, 2D elements, etc. + qboolean noPicMip; // for images that must always be full resolution + + fogPass_t fogPass; // draw a blended pass, possibly with depth test equals + + int vertexAttribs; // not all shaders will need all data to be gathered + + int numDeforms; + deformStage_t deforms[MAX_SHADER_DEFORMS]; + + int numUnfoggedPasses; + shaderStage_t *stages[MAX_SHADER_STAGES]; + + void (*optimalStageIteratorFunc)( void ); + + float clampTime; // time this shader is clamped to + float timeOffset; // current time offset for this shader + + int numStates; // if non-zero this is a state shader + struct shader_s *currentShader; // current state if this is a state shader + struct shader_s *parentShader; // current state if this is a state shader + int currentState; // current state index for cycle purposes + long expireTime; // time in milliseconds this expires + + struct shader_s *remappedShader; // current shader this one is remapped too + + int shaderStates[MAX_STATES_PER_SHADER]; // index to valid shader states + + struct shader_s *next; +} shader_t; + +static ID_INLINE qboolean ShaderRequiresCPUDeforms(const shader_t * shader) +{ + if(shader->numDeforms) + { + const deformStage_t *ds = &shader->deforms[0]; + + if (shader->numDeforms > 1) + return qtrue; + + switch (ds->deformation) + { + case DEFORM_WAVE: + case DEFORM_BULGE: + return qfalse; + + default: + return qtrue; + } + } + + return qfalse; +} + +typedef struct shaderState_s { + char shaderName[MAX_QPATH]; // name of shader this state belongs to + char name[MAX_STATE_NAME]; // name of this state + char stateShader[MAX_QPATH]; // shader this name invokes + int cycleTime; // time this cycle lasts, <= 0 is forever + shader_t *shader; +} shaderState_t; + +enum +{ + ATTR_INDEX_POSITION = 0, + ATTR_INDEX_TEXCOORD0 = 1, + ATTR_INDEX_TEXCOORD1 = 2, + ATTR_INDEX_TANGENT = 3, + ATTR_INDEX_BITANGENT = 4, + ATTR_INDEX_NORMAL = 5, + ATTR_INDEX_COLOR = 6, + ATTR_INDEX_PAINTCOLOR = 7, + ATTR_INDEX_LIGHTDIRECTION = 8, + ATTR_INDEX_BONE_INDEXES = 9, + ATTR_INDEX_BONE_WEIGHTS = 10, + + // GPU vertex animations + ATTR_INDEX_POSITION2 = 11, + ATTR_INDEX_TANGENT2 = 12, + ATTR_INDEX_BITANGENT2 = 13, + ATTR_INDEX_NORMAL2 = 14 +}; + +enum +{ + GLS_SRCBLEND_ZERO = (1 << 0), + GLS_SRCBLEND_ONE = (1 << 1), + GLS_SRCBLEND_DST_COLOR = (1 << 2), + GLS_SRCBLEND_ONE_MINUS_DST_COLOR = (1 << 3), + GLS_SRCBLEND_SRC_ALPHA = (1 << 4), + GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA = (1 << 5), + GLS_SRCBLEND_DST_ALPHA = (1 << 6), + GLS_SRCBLEND_ONE_MINUS_DST_ALPHA = (1 << 7), + GLS_SRCBLEND_ALPHA_SATURATE = (1 << 8), + + GLS_SRCBLEND_BITS = GLS_SRCBLEND_ZERO + | GLS_SRCBLEND_ONE + | GLS_SRCBLEND_DST_COLOR + | GLS_SRCBLEND_ONE_MINUS_DST_COLOR + | GLS_SRCBLEND_SRC_ALPHA + | GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA + | GLS_SRCBLEND_DST_ALPHA + | GLS_SRCBLEND_ONE_MINUS_DST_ALPHA + | GLS_SRCBLEND_ALPHA_SATURATE, + + GLS_DSTBLEND_ZERO = (1 << 9), + GLS_DSTBLEND_ONE = (1 << 10), + GLS_DSTBLEND_SRC_COLOR = (1 << 11), + GLS_DSTBLEND_ONE_MINUS_SRC_COLOR = (1 << 12), + GLS_DSTBLEND_SRC_ALPHA = (1 << 13), + GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA = (1 << 14), + GLS_DSTBLEND_DST_ALPHA = (1 << 15), + GLS_DSTBLEND_ONE_MINUS_DST_ALPHA = (1 << 16), + + GLS_DSTBLEND_BITS = GLS_DSTBLEND_ZERO + | GLS_DSTBLEND_ONE + | GLS_DSTBLEND_SRC_COLOR + | GLS_DSTBLEND_ONE_MINUS_SRC_COLOR + | GLS_DSTBLEND_SRC_ALPHA + | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA + | GLS_DSTBLEND_DST_ALPHA + | GLS_DSTBLEND_ONE_MINUS_DST_ALPHA, + + GLS_DEPTHMASK_TRUE = (1 << 17), + + GLS_POLYMODE_LINE = (1 << 18), + + GLS_DEPTHTEST_DISABLE = (1 << 19), + + GLS_DEPTHFUNC_LESS = (1 << 20), + GLS_DEPTHFUNC_EQUAL = (1 << 21), + + GLS_DEPTHFUNC_BITS = GLS_DEPTHFUNC_LESS + | GLS_DEPTHFUNC_EQUAL, + + GLS_ATEST_GT_0 = (1 << 22), + GLS_ATEST_LT_128 = (1 << 23), + GLS_ATEST_GE_128 = (1 << 24), +// GLS_ATEST_GE_CUSTOM = (1 << 25), + + GLS_ATEST_BITS = GLS_ATEST_GT_0 + | GLS_ATEST_LT_128 + | GLS_ATEST_GE_128, +// | GLS_ATEST_GT_CUSTOM, + + GLS_REDMASK_FALSE = (1 << 26), + GLS_GREENMASK_FALSE = (1 << 27), + GLS_BLUEMASK_FALSE = (1 << 28), + GLS_ALPHAMASK_FALSE = (1 << 29), + + GLS_COLORMASK_BITS = GLS_REDMASK_FALSE + | GLS_GREENMASK_FALSE + | GLS_BLUEMASK_FALSE + | GLS_ALPHAMASK_FALSE, + + GLS_STENCILTEST_ENABLE = (1 << 30), + + GLS_DEFAULT = GLS_DEPTHMASK_TRUE +}; + +enum +{ + ATTR_POSITION = 0x0001, + ATTR_TEXCOORD = 0x0002, + ATTR_LIGHTCOORD = 0x0004, + ATTR_TANGENT = 0x0008, + ATTR_BITANGENT = 0x0010, + ATTR_NORMAL = 0x0020, + ATTR_COLOR = 0x0040, + ATTR_PAINTCOLOR = 0x0080, + ATTR_LIGHTDIRECTION = 0x0100, + ATTR_BONE_INDEXES = 0x0200, + ATTR_BONE_WEIGHTS = 0x0400, + + // for .md3 interpolation + ATTR_POSITION2 = 0x0800, + ATTR_TANGENT2 = 0x1000, + ATTR_BITANGENT2 = 0x2000, + ATTR_NORMAL2 = 0x4000, + + ATTR_DEFAULT = ATTR_POSITION, + ATTR_BITS = ATTR_POSITION | + ATTR_TEXCOORD | + ATTR_LIGHTCOORD | + ATTR_TANGENT | + ATTR_BITANGENT | + ATTR_NORMAL | + ATTR_COLOR | + ATTR_PAINTCOLOR | + ATTR_LIGHTDIRECTION | + ATTR_BONE_INDEXES | + ATTR_BONE_WEIGHTS | + ATTR_POSITION2 | + ATTR_TANGENT2 | + ATTR_BITANGENT2 | + ATTR_NORMAL2 +}; + +enum +{ + GENERICDEF_USE_DEFORM_VERTEXES = 0x0001, + GENERICDEF_USE_TCGEN_AND_TCMOD = 0x0002, + GENERICDEF_USE_VERTEX_ANIMATION = 0x0004, + GENERICDEF_USE_FOG = 0x0008, + GENERICDEF_USE_RGBAGEN = 0x0010, + GENERICDEF_USE_LIGHTMAP = 0x0020, + GENERICDEF_ALL = 0x003F, + GENERICDEF_COUNT = 0x0040, +}; + +enum +{ + FOGDEF_USE_DEFORM_VERTEXES = 0x0001, + FOGDEF_USE_VERTEX_ANIMATION = 0x0002, + FOGDEF_ALL = 0x0003, + FOGDEF_COUNT = 0x0004, +}; + +enum +{ + DLIGHTDEF_USE_DEFORM_VERTEXES = 0x0001, + DLIGHTDEF_ALL = 0x0001, + DLIGHTDEF_COUNT = 0x0002, +}; + +enum +{ + LIGHTDEF_USE_LIGHTMAP = 0x0001, + LIGHTDEF_USE_LIGHT_VECTOR = 0x0002, + LIGHTDEF_USE_LIGHT_VERTEX = 0x0003, + LIGHTDEF_LIGHTTYPE_MASK = 0x0003, + LIGHTDEF_ENTITY = 0x0004, + LIGHTDEF_USE_TCGEN_AND_TCMOD = 0x0008, + LIGHTDEF_USE_NORMALMAP = 0x0010, + LIGHTDEF_USE_SPECULARMAP = 0x0020, + LIGHTDEF_USE_DELUXEMAP = 0x0040, + LIGHTDEF_USE_PARALLAXMAP = 0x0080, + LIGHTDEF_USE_SHADOWMAP = 0x0100, + LIGHTDEF_ALL = 0x01FF, + LIGHTDEF_COUNT = 0x0200 +}; + +enum +{ + GLSL_INT, + GLSL_FLOAT, + GLSL_FLOAT5, + GLSL_VEC2, + GLSL_VEC3, + GLSL_VEC4, + GLSL_MAT16 +}; + +// Tr3B - shaderProgram_t represents a pair of one +// GLSL vertex and one GLSL fragment shader +typedef struct shaderProgram_s +{ + char name[MAX_QPATH]; + + GLhandleARB program; + GLhandleARB vertexShader; + GLhandleARB fragmentShader; + uint32_t attribs; // vertex array attributes + + // uniform parameters + int numUniforms; + GLint *uniforms; + char *uniformTypes; // max 127 uniform types + short *uniformBufferOffsets; // max 32767/64=511 uniforms + char *uniformBuffer; +} shaderProgram_t; + + +enum +{ + TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX = 0, + TEXTURECOLOR_UNIFORM_INVTEXRES, + TEXTURECOLOR_UNIFORM_AUTOEXPOSUREMINMAX, + TEXTURECOLOR_UNIFORM_TONEMINAVGMAXLINEAR, + TEXTURECOLOR_UNIFORM_TEXTUREMAP, + TEXTURECOLOR_UNIFORM_LEVELSMAP, + TEXTURECOLOR_UNIFORM_COLOR, + TEXTURECOLOR_UNIFORM_COUNT +}; + + +enum +{ + FOGPASS_UNIFORM_FOGDISTANCE = 0, + FOGPASS_UNIFORM_FOGDEPTH, + FOGPASS_UNIFORM_FOGEYET, + FOGPASS_UNIFORM_DEFORMGEN, + FOGPASS_UNIFORM_DEFORMPARAMS, + FOGPASS_UNIFORM_TIME, + FOGPASS_UNIFORM_COLOR, + FOGPASS_UNIFORM_MODELVIEWPROJECTIONMATRIX, + FOGPASS_UNIFORM_VERTEXLERP, + FOGPASS_UNIFORM_COUNT +}; + + +enum +{ + DLIGHT_UNIFORM_DIFFUSEMAP = 0, + DLIGHT_UNIFORM_DLIGHTINFO, + DLIGHT_UNIFORM_DEFORMGEN, + DLIGHT_UNIFORM_DEFORMPARAMS, + DLIGHT_UNIFORM_TIME, + DLIGHT_UNIFORM_COLOR, + DLIGHT_UNIFORM_MODELVIEWPROJECTIONMATRIX, + DLIGHT_UNIFORM_VERTEXLERP, + DLIGHT_UNIFORM_COUNT +}; + + +enum +{ + PSHADOW_UNIFORM_SHADOWMAP = 0, + PSHADOW_UNIFORM_MODELVIEWPROJECTIONMATRIX, + PSHADOW_UNIFORM_LIGHTFORWARD, + PSHADOW_UNIFORM_LIGHTUP, + PSHADOW_UNIFORM_LIGHTRIGHT, + PSHADOW_UNIFORM_LIGHTORIGIN, + PSHADOW_UNIFORM_LIGHTRADIUS, + PSHADOW_UNIFORM_COUNT +}; + + +enum +{ + GENERIC_UNIFORM_DIFFUSEMAP = 0, + GENERIC_UNIFORM_LIGHTMAP, + GENERIC_UNIFORM_NORMALMAP, + GENERIC_UNIFORM_DELUXEMAP, + GENERIC_UNIFORM_SPECULARMAP, + GENERIC_UNIFORM_SHADOWMAP, + GENERIC_UNIFORM_DIFFUSETEXMATRIX, + GENERIC_UNIFORM_DIFFUSETEXOFFTURB, + //GENERIC_UNIFORM_NORMALTEXMATRIX, + //GENERIC_UNIFORM_SPECULARTEXMATRIX, + GENERIC_UNIFORM_TEXTURE1ENV, + GENERIC_UNIFORM_VIEWORIGIN, + GENERIC_UNIFORM_TCGEN0, + GENERIC_UNIFORM_TCGEN0VECTOR0, + GENERIC_UNIFORM_TCGEN0VECTOR1, + GENERIC_UNIFORM_DEFORMGEN, + GENERIC_UNIFORM_DEFORMPARAMS, + GENERIC_UNIFORM_COLORGEN, + GENERIC_UNIFORM_ALPHAGEN, + GENERIC_UNIFORM_BASECOLOR, + GENERIC_UNIFORM_VERTCOLOR, + GENERIC_UNIFORM_AMBIENTLIGHT, + GENERIC_UNIFORM_DIRECTEDLIGHT, + GENERIC_UNIFORM_LIGHTORIGIN, + GENERIC_UNIFORM_LIGHTRADIUS, + GENERIC_UNIFORM_PORTALRANGE, + GENERIC_UNIFORM_FOGDISTANCE, + GENERIC_UNIFORM_FOGDEPTH, + GENERIC_UNIFORM_FOGEYET, + GENERIC_UNIFORM_FOGCOLORMASK, + GENERIC_UNIFORM_MODELMATRIX, + GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, + GENERIC_UNIFORM_TIME, + GENERIC_UNIFORM_VERTEXLERP, + GENERIC_UNIFORM_MATERIALINFO, + GENERIC_UNIFORM_COUNT +}; + +enum +{ + SHADOWMASK_UNIFORM_SCREENDEPTHMAP = 0, + SHADOWMASK_UNIFORM_SHADOWMAP, + SHADOWMASK_UNIFORM_SHADOWMAP2, + SHADOWMASK_UNIFORM_SHADOWMAP3, + SHADOWMASK_UNIFORM_SHADOWMVP, + SHADOWMASK_UNIFORM_SHADOWMVP2, + SHADOWMASK_UNIFORM_SHADOWMVP3, + SHADOWMASK_UNIFORM_VIEWORIGIN, + SHADOWMASK_UNIFORM_VIEWINFO, // znear, zfar, width/2, height/2 + SHADOWMASK_UNIFORM_VIEWFORWARD, + SHADOWMASK_UNIFORM_VIEWLEFT, + SHADOWMASK_UNIFORM_VIEWUP, + SHADOWMASK_UNIFORM_COUNT +}; + +enum +{ + SSAO_UNIFORM_SCREENDEPTHMAP = 0, + SSAO_UNIFORM_VIEWINFO, // znear, zfar, width/2, height/2 + SSAO_UNIFORM_COUNT +}; + +enum +{ + DEPTHBLUR_UNIFORM_SCREENIMAGEMAP = 0, + DEPTHBLUR_UNIFORM_SCREENDEPTHMAP, + DEPTHBLUR_UNIFORM_VIEWINFO, // znear, zfar, width/2, height/2 + DEPTHBLUR_UNIFORM_COUNT +}; + +// +// Tr3B: these are fire wall functions to avoid expensive redundant glUniform* calls +//#define USE_UNIFORM_FIREWALL 1 +//#define LOG_GLSL_UNIFORMS 1 + +// trRefdef_t holds everything that comes in refdef_t, +// as well as the locally generated scene information +typedef struct { + int x, y, width, height; + float fov_x, fov_y; + vec3_t vieworg; + vec3_t viewaxis[3]; // transformation matrix + + stereoFrame_t stereoFrame; + + int time; // time in milliseconds for shader effects and other time dependent rendering issues + int rdflags; // RDF_NOWORLDMODEL, etc + + // 1 bits will prevent the associated area from rendering at all + byte areamask[MAX_MAP_AREA_BYTES]; + qboolean areamaskModified; // qtrue if areamask changed since last scene + + float floatTime; // tr.refdef.time / 1000.0 + + float blurFactor; + + // text messages for deform text shaders + char text[MAX_RENDER_STRINGS][MAX_RENDER_STRING_LENGTH]; + + int num_entities; + trRefEntity_t *entities; + + int num_dlights; + struct dlight_s *dlights; + + int numPolys; + struct srfPoly_s *polys; + + int numDrawSurfs; + struct drawSurf_s *drawSurfs; + + unsigned int dlightMask; + int num_pshadows; + struct pshadow_s *pshadows; + + float sunShadowMvp[3][16]; + float sunDir[4]; + float sunCol[4]; + float sunAmbCol[4]; + float colorScale; + + float autoExposureMinMax[2]; + float toneMinAvgMaxLinear[3]; +} trRefdef_t; + + +//================================================================================= + +// skins allow models to be retextured without modifying the model file +typedef struct { + char name[MAX_QPATH]; + shader_t *shader; +} skinSurface_t; + +typedef struct skin_s { + char name[MAX_QPATH]; // game path, including extension + int numSurfaces; + skinSurface_t *surfaces[MD3_MAX_SURFACES]; +} skin_t; + + +typedef struct { + int originalBrushNumber; + vec3_t bounds[2]; + + unsigned colorInt; // in packed byte format + float tcScale; // texture coordinate vector scales + fogParms_t parms; + + // for clipping distance in fog when outside + qboolean hasSurface; + float surface[4]; +} fog_t; + +typedef enum { + VPF_NONE = 0x00, + VPF_SHADOWMAP = 0x01, + VPF_DEPTHSHADOW = 0x02, + VPF_DEPTHCLAMP = 0x04, + VPF_ORTHOGRAPHIC = 0x08, + VPF_USESUNLIGHT = 0x10, + VPF_FARPLANEFRUSTUM = 0x20 +} viewParmFlags_t; + +typedef struct { + orientationr_t or; + orientationr_t world; + vec3_t pvsOrigin; // may be different than or.origin for portals + qboolean isPortal; // true if this view is through a portal + qboolean isMirror; // the portal is a mirror, invert the face culling + viewParmFlags_t flags; + int frameSceneNum; // copied from tr.frameSceneNum + int frameCount; // copied from tr.frameCount + cplane_t portalPlane; // clip anything behind this if mirroring + int viewportX, viewportY, viewportWidth, viewportHeight; + FBO_t *targetFbo; + float fovX, fovY; + float projectionMatrix[16]; + cplane_t frustum[5]; + vec3_t visBounds[2]; + float zFar; + float zNear; + stereoFrame_t stereoFrame; +} viewParms_t; + + +/* +============================================================================== + +SURFACES + +============================================================================== +*/ +typedef byte color4ub_t[4]; + +// any changes in surfaceType must be mirrored in rb_surfaceTable[] +typedef enum { + SF_BAD, + SF_SKIP, // ignore + SF_FACE, + SF_GRID, + SF_TRIANGLES, + SF_POLY, + SF_MDV, + SF_MD4, +#ifdef RAVENMD4 + SF_MDR, +#endif + SF_IQM, + SF_FLARE, + SF_ENTITY, // beams, rails, lightning, etc that can be determined by entity + SF_DISPLAY_LIST, + SF_VBO_MESH, + SF_VBO_MDVMESH, + + SF_NUM_SURFACE_TYPES, + SF_MAX = 0x7fffffff // ensures that sizeof( surfaceType_t ) == sizeof( int ) +} surfaceType_t; + +typedef struct drawSurf_s { + unsigned sort; // bit combination for fast compares + surfaceType_t *surface; // any of surface*_t +} drawSurf_t; + +#define MAX_FACE_POINTS 64 + +#define MAX_PATCH_SIZE 32 // max dimensions of a patch mesh in map file +#define MAX_GRID_SIZE 65 // max dimensions of a grid mesh in memory + +// when cgame directly specifies a polygon, it becomes a srfPoly_t +// as soon as it is called +typedef struct srfPoly_s { + surfaceType_t surfaceType; + qhandle_t hShader; + int fogIndex; + int numVerts; + polyVert_t *verts; +} srfPoly_t; + +typedef struct srfDisplayList_s { + surfaceType_t surfaceType; + int listNum; +} srfDisplayList_t; + + +typedef struct srfFlare_s { + surfaceType_t surfaceType; + vec3_t origin; + vec3_t normal; + vec3_t color; +} srfFlare_t; + +typedef struct +{ + vec3_t xyz; + vec2_t st; + vec2_t lightmap; + vec3_t normal; +#ifdef USE_VERT_TANGENT_SPACE + vec3_t tangent; + vec3_t bitangent; +#endif + vec3_t lightdir; + vec4_t vertexColors; + +#if DEBUG_OPTIMIZEVERTICES + unsigned int id; +#endif +} srfVert_t; + +#ifdef USE_VERT_TANGENT_SPACE +#define srfVert_t_cleared(x) srfVert_t (x) = {{0, 0, 0}, {0, 0}, {0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0, 0}} +#else +#define srfVert_t_cleared(x) srfVert_t (x) = {{0, 0, 0}, {0, 0}, {0, 0}, {0, 0, 0}, {0, 0, 0}, {0, 0, 0, 0}} +#endif + +typedef struct +{ + int indexes[3]; + int neighbors[3]; + vec4_t plane; + qboolean facingLight; + qboolean degenerated; +} srfTriangle_t; + + +typedef struct srfGridMesh_s +{ + surfaceType_t surfaceType; + + // dynamic lighting information + int dlightBits; + int pshadowBits; + + // culling information + vec3_t meshBounds[2]; + vec3_t localOrigin; + float meshRadius; + + // lod information, which may be different + // than the culling information to allow for + // groups of curves that LOD as a unit + vec3_t lodOrigin; + float lodRadius; + int lodFixed; + int lodStitched; + + // vertexes + int width, height; + float *widthLodError; + float *heightLodError; + + int numTriangles; + srfTriangle_t *triangles; + + int numVerts; + srfVert_t *verts; + + // BSP VBO offsets + int firstVert; + int firstIndex; + glIndex_t minIndex; + glIndex_t maxIndex; + + // static render data + VBO_t *vbo; // points to bsp model VBO + IBO_t *ibo; +} srfGridMesh_t; + + +typedef struct +{ + surfaceType_t surfaceType; + + // dynamic lighting information + int dlightBits; + int pshadowBits; + + // culling information + cplane_t plane; +// vec3_t bounds[2]; + + // triangle definitions + int numTriangles; + srfTriangle_t *triangles; + + int numVerts; + srfVert_t *verts; + + // BSP VBO offsets + int firstVert; + int firstIndex; + glIndex_t minIndex; + glIndex_t maxIndex; + + // static render data + VBO_t *vbo; // points to bsp model VBO + IBO_t *ibo; +} srfSurfaceFace_t; + + +// misc_models in maps are turned into direct geometry by xmap +typedef struct +{ + surfaceType_t surfaceType; + + // dynamic lighting information + int dlightBits; + int pshadowBits; + + // culling information +// vec3_t bounds[2]; + + // triangle definitions + int numTriangles; + srfTriangle_t *triangles; + + int numVerts; + srfVert_t *verts; + + // BSP VBO offsets + int firstVert; + int firstIndex; + glIndex_t minIndex; + glIndex_t maxIndex; + + // static render data + VBO_t *vbo; // points to bsp model VBO + IBO_t *ibo; +} srfTriangles_t; + +// inter-quake-model +typedef struct { + int num_vertexes; + int num_triangles; + int num_frames; + int num_surfaces; + int num_joints; + struct srfIQModel_s *surfaces; + + float *positions; + float *texcoords; + float *normals; + float *tangents; + byte *blendIndexes; + byte *blendWeights; + byte *colors; + int *triangles; + + int *jointParents; + float *poseMats; + float *bounds; + char *names; +} iqmData_t; + +// inter-quake-model surface +typedef struct srfIQModel_s { + surfaceType_t surfaceType; + char name[MAX_QPATH]; + shader_t *shader; + iqmData_t *data; + int first_vertex, num_vertexes; + int first_triangle, num_triangles; +} srfIQModel_t; + +typedef struct srfVBOMesh_s +{ + surfaceType_t surfaceType; + + struct shader_s *shader; // FIXME move this to somewhere else + int fogIndex; + + // dynamic lighting information + int dlightBits; + int pshadowBits; + + // culling information + vec3_t bounds[2]; + + // backEnd stats + int numIndexes; + int numVerts; + int firstIndex; + glIndex_t minIndex; + glIndex_t maxIndex; + + // static render data + VBO_t *vbo; + IBO_t *ibo; +} srfVBOMesh_t; + +typedef struct srfVBOMDVMesh_s +{ + surfaceType_t surfaceType; + + struct mdvModel_s *mdvModel; + struct mdvSurface_s *mdvSurface; + + // backEnd stats + int numIndexes; + int numVerts; + glIndex_t minIndex; + glIndex_t maxIndex; + + // static render data + VBO_t *vbo; + IBO_t *ibo; +} srfVBOMDVMesh_t; + +extern void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])(void *); + +/* +============================================================================== + +SHADOWS + +============================================================================== +*/ + +typedef struct pshadow_s +{ + float sort; + + int numEntities; + int entityNums[8]; + vec3_t entityOrigins[8]; + float entityRadiuses[8]; + + float viewRadius; + vec3_t viewOrigin; + + vec3_t lightViewAxis[3]; + vec3_t lightOrigin; + float lightRadius; + cplane_t cullPlane; +} pshadow_t; + + +/* +============================================================================== + +BRUSH MODELS + +============================================================================== +*/ + + +// +// in memory representation +// + +#define SIDE_FRONT 0 +#define SIDE_BACK 1 +#define SIDE_ON 2 + +#define CULLINFO_NONE 0 +#define CULLINFO_BOX 1 +#define CULLINFO_SPHERE 2 +#define CULLINFO_PLANE 4 + +typedef struct cullinfo_s { + int type; + vec3_t bounds[2]; + vec3_t localOrigin; + float radius; + cplane_t plane; +} cullinfo_t; + +typedef struct msurface_s { + //int viewCount; // if == tr.viewCount, already added + struct shader_s *shader; + int fogIndex; + cullinfo_t cullinfo; + + surfaceType_t *data; // any of srf*_t +} msurface_t; + + +#define CONTENTS_NODE -1 +typedef struct mnode_s { + // common with leaf and node + int contents; // -1 for nodes, to differentiate from leafs + int visCounts[MAX_VISCOUNTS]; // node needs to be traversed if current + vec3_t mins, maxs; // for bounding box culling + struct mnode_s *parent; + + // node specific + cplane_t *plane; + struct mnode_s *children[2]; + + // leaf specific + int cluster; + int area; + + int firstmarksurface; + int nummarksurfaces; +} mnode_t; + +typedef struct { + vec3_t bounds[2]; // for culling + int firstSurface; + int numSurfaces; +} bmodel_t; + +typedef struct { + char name[MAX_QPATH]; // ie: maps/tim_dm2.bsp + char baseName[MAX_QPATH]; // ie: tim_dm2 + + int dataSize; + + int numShaders; + dshader_t *shaders; + + int numBModels; + bmodel_t *bmodels; + + int numplanes; + cplane_t *planes; + + int numnodes; // includes leafs + int numDecisionNodes; + mnode_t *nodes; + + VBO_t *vbo; + IBO_t *ibo; + + int numWorldSurfaces; + + int numsurfaces; + msurface_t *surfaces; + int *surfacesViewCount; + int *surfacesDlightBits; + int *surfacesPshadowBits; + + int numMergedSurfaces; + msurface_t *mergedSurfaces; + int *mergedSurfacesViewCount; + int *mergedSurfacesDlightBits; + int *mergedSurfacesPshadowBits; + + int nummarksurfaces; + int *marksurfaces; + int *viewSurfaces; + + int numfogs; + fog_t *fogs; + + vec3_t lightGridOrigin; + vec3_t lightGridSize; + vec3_t lightGridInverseSize; + int lightGridBounds[3]; + byte *lightGridData; + float *hdrLightGrid; + + + int numClusters; + int clusterBytes; + const byte *vis; // may be passed in by CM_LoadMap to save space + + byte *novis; // clusterBytes of 0xff + + char *entityString; + char *entityParsePoint; +} world_t; + + +/* +============================================================================== +MDV MODELS - meta format for vertex animation models like .md2, .md3, .mdc +============================================================================== +*/ +typedef struct +{ + float bounds[2][3]; + float localOrigin[3]; + float radius; +} mdvFrame_t; + +typedef struct +{ + float origin[3]; + float axis[3][3]; +} mdvTag_t; + +typedef struct +{ + char name[MAX_QPATH]; // tag name +} mdvTagName_t; + +typedef struct +{ + vec3_t xyz; + vec3_t normal; +#ifdef USE_VERT_TANGENT_SPACE + vec3_t tangent; + vec3_t bitangent; +#endif +} mdvVertex_t; + +typedef struct +{ + float st[2]; +} mdvSt_t; + +typedef struct mdvSurface_s +{ + surfaceType_t surfaceType; + + char name[MAX_QPATH]; // polyset name + + int numShaderIndexes; + int *shaderIndexes; + + int numVerts; + mdvVertex_t *verts; + mdvSt_t *st; + + int numTriangles; + srfTriangle_t *triangles; + + struct mdvModel_s *model; +} mdvSurface_t; + +typedef struct mdvModel_s +{ + int numFrames; + mdvFrame_t *frames; + + int numTags; + mdvTag_t *tags; + mdvTagName_t *tagNames; + + int numSurfaces; + mdvSurface_t *surfaces; + + int numVBOSurfaces; + srfVBOMDVMesh_t *vboSurfaces; + + int numSkins; +} mdvModel_t; + + +//====================================================================== + +typedef enum { + MOD_BAD, + MOD_BRUSH, + MOD_MESH, + MOD_MD4, +#ifdef RAVENMD4 + MOD_MDR, +#endif + MOD_IQM +} modtype_t; + +typedef struct model_s { + char name[MAX_QPATH]; + modtype_t type; + int index; // model = tr.models[model->index] + + int dataSize; // just for listing purposes + bmodel_t *bmodel; // only if type == MOD_BRUSH + mdvModel_t *mdv[MD3_MAX_LODS]; // only if type == MOD_MESH + void *modelData; // only if type == (MOD_MD4 | MOD_MDR | MOD_IQM) + + int numLods; +} model_t; + + +#define MAX_MOD_KNOWN 1024 + +void R_ModelInit (void); +model_t *R_GetModelByHandle( qhandle_t hModel ); +int R_LerpTag( orientation_t *tag, qhandle_t handle, int startFrame, int endFrame, + float frac, const char *tagName ); +void R_ModelBounds( qhandle_t handle, vec3_t mins, vec3_t maxs ); + +void R_Modellist_f (void); + +//==================================================== +extern refimport_t ri; + +#define MAX_DRAWIMAGES 2048 +#define MAX_SKINS 1024 + + +#define MAX_DRAWSURFS 0x10000 +#define DRAWSURF_MASK (MAX_DRAWSURFS-1) + +/* + +the drawsurf sort data is packed into a single 32 bit value so it can be +compared quickly during the qsorting process + +the bits are allocated as follows: + +0 - 1 : dlightmap index +//2 : used to be clipped flag REMOVED - 03.21.00 rad +2 - 6 : fog index +11 - 20 : entity index +21 - 31 : sorted shader index + + TTimo - 1.32 +0-1 : dlightmap index +2-6 : fog index +7-16 : entity index +17-30 : sorted shader index + + SmileTheory - for pshadows +17-31 : sorted shader index +7-16 : entity index +2-6 : fog index +1 : pshadow flag +0 : dlight flag +*/ +#define QSORT_FOGNUM_SHIFT 2 +#define QSORT_REFENTITYNUM_SHIFT 7 +#define QSORT_SHADERNUM_SHIFT (QSORT_REFENTITYNUM_SHIFT+REFENTITYNUM_BITS) +#if (QSORT_SHADERNUM_SHIFT+SHADERNUM_BITS) > 32 + #error "Need to update sorting, too many bits." +#endif +#define QSORT_PSHADOW_SHIFT 1 + +extern int gl_filter_min, gl_filter_max; + +/* +** performanceCounters_t +*/ +typedef struct { + int c_sphere_cull_patch_in, c_sphere_cull_patch_clip, c_sphere_cull_patch_out; + int c_box_cull_patch_in, c_box_cull_patch_clip, c_box_cull_patch_out; + int c_sphere_cull_md3_in, c_sphere_cull_md3_clip, c_sphere_cull_md3_out; + int c_box_cull_md3_in, c_box_cull_md3_clip, c_box_cull_md3_out; + + int c_leafs; + int c_dlightSurfaces; + int c_dlightSurfacesCulled; +} frontEndCounters_t; + +#define FOG_TABLE_SIZE 256 +#define FUNCTABLE_SIZE 1024 +#define FUNCTABLE_SIZE2 10 +#define FUNCTABLE_MASK (FUNCTABLE_SIZE-1) + + +// the renderer front end should never modify glstate_t +typedef struct { + int currenttextures[NUM_TEXTURE_BUNDLES]; + int currenttmu; + qboolean finishCalled; + int texEnv[2]; + int faceCulling; + unsigned long glStateBits; + uint32_t vertexAttribsState; + uint32_t vertexAttribPointersSet; + uint32_t vertexAttribsNewFrame; + uint32_t vertexAttribsOldFrame; + float vertexAttribsInterpolation; + shaderProgram_t *currentProgram; + FBO_t *currentFBO; + VBO_t *currentVBO; + IBO_t *currentIBO; + matrix_t modelview; + matrix_t projection; + matrix_t modelviewProjection; +} glstate_t; + +typedef enum { + MI_NONE, + MI_NVX, + MI_ATI +} memInfo_t; + +typedef enum { + TCR_NONE = 0x0000, + TCR_LATC = 0x0001, + TCR_BPTC = 0x0002, +} textureCompressionRef_t; + +// We can't change glConfig_t without breaking DLL/vms compatibility, so +// store extensions we have here. +typedef struct { + qboolean drawRangeElements; + qboolean multiDrawArrays; + qboolean occlusionQuery; + + int glslMajorVersion; + int glslMinorVersion; + + memInfo_t memInfo; + + qboolean framebufferObject; + int maxRenderbufferSize; + int maxColorAttachments; + + qboolean textureNonPowerOfTwo; + qboolean textureFloat; + qboolean halfFloatPixel; + qboolean packedDepthStencil; + textureCompressionRef_t textureCompression; + + qboolean framebufferMultisample; + qboolean framebufferBlit; + + qboolean texture_srgb; + + qboolean depthClamp; +} glRefConfig_t; + + +typedef struct { + int c_surfaces, c_shaders, c_vertexes, c_indexes, c_totalIndexes; + int c_surfBatches; + float c_overDraw; + + int c_vboVertexBuffers; + int c_vboIndexBuffers; + int c_vboVertexes; + int c_vboIndexes; + + int c_staticVboDraws; + int c_dynamicVboDraws; + + int c_multidraws; + int c_multidrawsMerged; + + int c_dlightVertexes; + int c_dlightIndexes; + + int c_flareAdds; + int c_flareTests; + int c_flareRenders; + + int c_glslShaderBinds; + int c_genericDraws; + int c_lightallDraws; + int c_fogDraws; + int c_dlightDraws; + + int msec; // total msec for backend run +} backEndCounters_t; + +// all state modified by the back end is seperated +// from the front end state +typedef struct { + trRefdef_t refdef; + viewParms_t viewParms; + orientationr_t or; + backEndCounters_t pc; + qboolean isHyperspace; + trRefEntity_t *currentEntity; + qboolean skyRenderedThisView; // flag for drawing sun + + qboolean projection2D; // if qtrue, drawstretchpic doesn't need to change modes + byte color2D[4]; + qboolean vertexes2D; // shader needs to be finished + trRefEntity_t entity2D; // currentEntity will point at this when doing 2D rendering + + FBO_t *last2DFBO; + qboolean colorMask[4]; + qboolean framePostProcessed; + qboolean depthFill; +} backEndState_t; + +/* +** trGlobals_t +** +** Most renderer globals are defined here. +** backend functions should never modify any of these fields, +** but may read fields that aren't dynamically modified +** by the frontend. +*/ +typedef struct { + qboolean registered; // cleared at shutdown, set at beginRegistration + + int visIndex; + int visClusters[MAX_VISCOUNTS]; + int visCounts[MAX_VISCOUNTS]; // incremented every time a new vis cluster is entered + + int frameCount; // incremented every frame + int sceneCount; // incremented every scene + int viewCount; // incremented every view (twice a scene if portaled) + // and every R_MarkFragments call + + int frameSceneNum; // zeroed at RE_BeginFrame + + qboolean worldMapLoaded; + qboolean worldDeluxeMapping; + vec2_t autoExposureMinMax; + vec3_t toneMinAvgMaxLevel; + world_t *world; + + const byte *externalVisData; // from RE_SetWorldVisData, shared with CM_Load + + image_t *defaultImage; + image_t *scratchImage[32]; + image_t *fogImage; + image_t *dlightImage; // inverse-quare highlight for projective adding + image_t *flareImage; + image_t *whiteImage; // full of 0xff + image_t *identityLightImage; // full of tr.identityLightByte + + image_t *shadowCubemaps[MAX_DLIGHTS]; + + + image_t *renderImage; + image_t *sunRaysImage; + image_t *renderDepthImage; + image_t *pshadowMaps[MAX_DRAWN_PSHADOWS]; + image_t *textureScratchImage[2]; + image_t *screenScratchImage; + image_t *quarterImage[2]; + image_t *calcLevelsImage; + image_t *targetLevelsImage; + image_t *fixedLevelsImage; + image_t *sunShadowDepthImage[3]; + image_t *screenShadowImage; + image_t *screenSsaoImage; + image_t *hdrDepthImage; + + image_t *textureDepthImage; + + FBO_t *renderFbo; + FBO_t *msaaResolveFbo; + FBO_t *sunRaysFbo; + FBO_t *depthFbo; + FBO_t *pshadowFbos[MAX_DRAWN_PSHADOWS]; + FBO_t *textureScratchFbo[2]; + FBO_t *screenScratchFbo; + FBO_t *quarterFbo[2]; + FBO_t *calcLevelsFbo; + FBO_t *targetLevelsFbo; + FBO_t *sunShadowFbo[3]; + FBO_t *screenShadowFbo; + FBO_t *screenSsaoFbo; + FBO_t *hdrDepthFbo; + + shader_t *defaultShader; + shader_t *shadowShader; + shader_t *projectionShadowShader; + + shader_t *flareShader; + shader_t *sunShader; + shader_t *sunFlareShader; + + int numLightmaps; + int lightmapSize; + image_t **lightmaps; + image_t **deluxemaps; + + int fatLightmapSize; + int fatLightmapStep; + + trRefEntity_t *currentEntity; + trRefEntity_t worldEntity; // point currentEntity at this when rendering world + int currentEntityNum; + int shiftedEntityNum; // currentEntityNum << QSORT_REFENTITYNUM_SHIFT + model_t *currentModel; + + // + // GPU shader programs + // + shaderProgram_t genericShader[GENERICDEF_COUNT]; + shaderProgram_t textureColorShader; + shaderProgram_t fogShader[FOGDEF_COUNT]; + shaderProgram_t dlightShader[DLIGHTDEF_COUNT]; + shaderProgram_t lightallShader[LIGHTDEF_COUNT]; + shaderProgram_t shadowmapShader; + shaderProgram_t pshadowShader; + shaderProgram_t down4xShader; + shaderProgram_t bokehShader; + shaderProgram_t tonemapShader; + shaderProgram_t calclevels4xShader[2]; + shaderProgram_t shadowmaskShader; + shaderProgram_t ssaoShader; + shaderProgram_t depthBlurShader[2]; + + + // ----------------------------------------- + + viewParms_t viewParms; + + float identityLight; // 1.0 / ( 1 << overbrightBits ) + int identityLightByte; // identityLight * 255 + int overbrightBits; // r_overbrightBits->integer, but set to 0 if no hw gamma + + orientationr_t or; // for current entity + + trRefdef_t refdef; + + int viewCluster; + + float mapLightScale; + + qboolean sunShadows; + vec3_t sunLight; // from the sky shader for this level + vec3_t sunAmbient; + vec3_t sunDirection; + + frontEndCounters_t pc; + int frontEndMsec; // not in pc due to clearing issue + + vec4_t clipRegion; // 2D clipping region + + // + // put large tables at the end, so most elements will be + // within the +/32K indexed range on risc processors + // + model_t *models[MAX_MOD_KNOWN]; + int numModels; + + int numImages; + image_t *images[MAX_DRAWIMAGES]; + + int numFBOs; + FBO_t *fbos[MAX_FBOS]; + + int numVBOs; + VBO_t *vbos[MAX_VBOS]; + + int numIBOs; + IBO_t *ibos[MAX_IBOS]; + + // shader indexes from other modules will be looked up in tr.shaders[] + // shader indexes from drawsurfs will be looked up in sortedShaders[] + // lower indexed sortedShaders must be rendered first (opaque surfaces before translucent) + int numShaders; + shader_t *shaders[MAX_SHADERS]; + shader_t *sortedShaders[MAX_SHADERS]; + + int numSkins; + skin_t *skins[MAX_SKINS]; + + GLuint sunFlareQuery[2]; + int sunFlareQueryIndex; + qboolean sunFlareQueryActive[2]; + + float sinTable[FUNCTABLE_SIZE]; + float squareTable[FUNCTABLE_SIZE]; + float triangleTable[FUNCTABLE_SIZE]; + float sawToothTable[FUNCTABLE_SIZE]; + float inverseSawToothTable[FUNCTABLE_SIZE]; + float fogTable[FOG_TABLE_SIZE]; +} trGlobals_t; + +extern backEndState_t backEnd; +extern trGlobals_t tr; +extern glconfig_t glConfig; // outside of TR since it shouldn't be cleared during ref re-init +extern glstate_t glState; // outside of TR since it shouldn't be cleared during ref re-init + +// These three variables should live inside glConfig but can't because of compatibility issues to the original ID vms. +// If you release a stand-alone game and your mod uses tr_types.h from this build you can safely move them to +// the glconfig_t struct. +extern qboolean textureFilterAnisotropic; +extern int maxAnisotropy; +extern glRefConfig_t glRefConfig; +extern float displayAspect; + + +// +// cvars +// +extern cvar_t *r_flareSize; +extern cvar_t *r_flareFade; +// coefficient for the flare intensity falloff function. +#define FLARE_STDCOEFF "150" +extern cvar_t *r_flareCoeff; + +extern cvar_t *r_railWidth; +extern cvar_t *r_railCoreWidth; +extern cvar_t *r_railSegmentLength; + +extern cvar_t *r_ignore; // used for debugging anything +extern cvar_t *r_verbose; // used for verbose debug spew + +extern cvar_t *r_znear; // near Z clip plane +extern cvar_t *r_zproj; // z distance of projection plane +extern cvar_t *r_stereoSeparation; // separation of cameras for stereo rendering + +extern cvar_t *r_stencilbits; // number of desired stencil bits +extern cvar_t *r_depthbits; // number of desired depth bits +extern cvar_t *r_colorbits; // number of desired color bits, only relevant for fullscreen +extern cvar_t *r_texturebits; // number of desired texture bits +extern cvar_t *r_ext_multisample; + // 0 = use framebuffer depth + // 16 = use 16-bit textures + // 32 = use 32-bit textures + // all else = error + +extern cvar_t *r_measureOverdraw; // enables stencil buffer overdraw measurement + +extern cvar_t *r_lodbias; // push/pull LOD transitions +extern cvar_t *r_lodscale; + +extern cvar_t *r_inGameVideo; // controls whether in game video should be draw +extern cvar_t *r_fastsky; // controls whether sky should be cleared or drawn +extern cvar_t *r_drawSun; // controls drawing of sun quad +extern cvar_t *r_dynamiclight; // dynamic lights enabled/disabled +extern cvar_t *r_dlightBacks; // dlight non-facing surfaces for continuity + +extern cvar_t *r_norefresh; // bypasses the ref rendering +extern cvar_t *r_drawentities; // disable/enable entity rendering +extern cvar_t *r_drawworld; // disable/enable world rendering +extern cvar_t *r_speeds; // various levels of information display +extern cvar_t *r_detailTextures; // enables/disables detail texturing stages +extern cvar_t *r_novis; // disable/enable usage of PVS +extern cvar_t *r_nocull; +extern cvar_t *r_facePlaneCull; // enables culling of planar surfaces with back side test +extern cvar_t *r_nocurves; +extern cvar_t *r_showcluster; + +extern cvar_t *r_mode; // video mode +extern cvar_t *r_fullscreen; +extern cvar_t *r_noborder; +extern cvar_t *r_gamma; +extern cvar_t *r_ignorehwgamma; // overrides hardware gamma capabilities + +extern cvar_t *r_allowExtensions; // global enable/disable of OpenGL extensions +extern cvar_t *r_ext_compressed_textures; // these control use of specific extensions +extern cvar_t *r_ext_multitexture; +extern cvar_t *r_ext_compiled_vertex_array; +extern cvar_t *r_ext_texture_env_add; + +extern cvar_t *r_ext_texture_filter_anisotropic; +extern cvar_t *r_ext_max_anisotropy; + +extern cvar_t *r_ext_draw_range_elements; +extern cvar_t *r_ext_multi_draw_arrays; +extern cvar_t *r_ext_framebuffer_object; +extern cvar_t *r_ext_texture_float; +extern cvar_t *r_arb_half_float_pixel; +extern cvar_t *r_ext_framebuffer_multisample; + +extern cvar_t *r_nobind; // turns off binding to appropriate textures +extern cvar_t *r_singleShader; // make most world faces use default shader +extern cvar_t *r_roundImagesDown; +extern cvar_t *r_colorMipLevels; // development aid to see texture mip usage +extern cvar_t *r_picmip; // controls picmip values +extern cvar_t *r_finish; +extern cvar_t *r_drawBuffer; +extern cvar_t *r_swapInterval; +extern cvar_t *r_textureMode; +extern cvar_t *r_offsetFactor; +extern cvar_t *r_offsetUnits; + +extern cvar_t *r_fullbright; // avoid lightmap pass +extern cvar_t *r_lightmap; // render lightmaps only +extern cvar_t *r_vertexLight; // vertex lighting mode for better performance +extern cvar_t *r_uiFullScreen; // ui is running fullscreen + +extern cvar_t *r_logFile; // number of frames to emit GL logs +extern cvar_t *r_showtris; // enables wireframe rendering of the world +extern cvar_t *r_showsky; // forces sky in front of all surfaces +extern cvar_t *r_shownormals; // draws wireframe normals +extern cvar_t *r_clear; // force screen clear every frame + +extern cvar_t *r_shadows; // controls shadows: 0 = none, 1 = blur, 2 = stencil, 3 = black planar projection +extern cvar_t *r_flares; // light flares + +extern cvar_t *r_intensity; + +extern cvar_t *r_lockpvs; +extern cvar_t *r_noportals; +extern cvar_t *r_portalOnly; + +extern cvar_t *r_subdivisions; +extern cvar_t *r_lodCurveError; +extern cvar_t *r_skipBackEnd; + +extern cvar_t *r_stereoEnabled; +extern cvar_t *r_anaglyphMode; + +extern cvar_t *r_mergeMultidraws; +extern cvar_t *r_mergeLeafSurfaces; + +extern cvar_t *r_softOverbright; + +extern cvar_t *r_hdr; +extern cvar_t *r_postProcess; + +extern cvar_t *r_toneMap; +extern cvar_t *r_forceToneMap; +extern cvar_t *r_forceToneMapMin; +extern cvar_t *r_forceToneMapAvg; +extern cvar_t *r_forceToneMapMax; + +extern cvar_t *r_autoExposure; +extern cvar_t *r_forceAutoExposure; +extern cvar_t *r_forceAutoExposureMin; +extern cvar_t *r_forceAutoExposureMax; + +extern cvar_t *r_cameraExposure; + +extern cvar_t *r_srgb; + +extern cvar_t *r_depthPrepass; +extern cvar_t *r_ssao; + +extern cvar_t *r_normalMapping; +extern cvar_t *r_specularMapping; +extern cvar_t *r_deluxeMapping; +extern cvar_t *r_parallaxMapping; +extern cvar_t *r_normalAmbient; +extern cvar_t *r_dlightMode; +extern cvar_t *r_pshadowDist; +extern cvar_t *r_recalcMD3Normals; +extern cvar_t *r_mergeLightmaps; +extern cvar_t *r_imageUpsample; +extern cvar_t *r_imageUpsampleMaxSize; +extern cvar_t *r_imageUpsampleType; +extern cvar_t *r_genNormalMaps; +extern cvar_t *r_forceSun; +extern cvar_t *r_forceSunMapLightScale; +extern cvar_t *r_forceSunLightScale; +extern cvar_t *r_forceSunAmbientScale; +extern cvar_t *r_drawSunRays; +extern cvar_t *r_sunShadows; +extern cvar_t *r_shadowFilter; +extern cvar_t *r_shadowMapSize; +extern cvar_t *r_shadowCascadeZNear; +extern cvar_t *r_shadowCascadeZFar; +extern cvar_t *r_shadowCascadeZBias; + +extern cvar_t *r_greyscale; + +extern cvar_t *r_ignoreGLErrors; + +extern cvar_t *r_overBrightBits; +extern cvar_t *r_mapOverBrightBits; + +extern cvar_t *r_debugSurface; +extern cvar_t *r_simpleMipMaps; + +extern cvar_t *r_showImages; +extern cvar_t *r_debugSort; + +extern cvar_t *r_printShaders; +extern cvar_t *r_saveFontData; + +extern cvar_t *r_marksOnTriangleMeshes; + +//==================================================================== + +float R_NoiseGet4f( float x, float y, float z, float t ); +void R_NoiseInit( void ); + +void R_SwapBuffers( int ); + +void R_RenderView( viewParms_t *parms ); +void R_RenderDlightCubemaps(const refdef_t *fd); +void R_RenderPshadowMaps(const refdef_t *fd); +void R_RenderSunShadowMaps(const refdef_t *fd, int level); + +void R_AddMD3Surfaces( trRefEntity_t *e ); +void R_AddNullModelSurfaces( trRefEntity_t *e ); +void R_AddBeamSurfaces( trRefEntity_t *e ); +void R_AddRailSurfaces( trRefEntity_t *e, qboolean isUnderwater ); +void R_AddLightningBoltSurfaces( trRefEntity_t *e ); + +void R_AddPolygonSurfaces( void ); + +void R_DecomposeSort( unsigned sort, int *entityNum, shader_t **shader, + int *fogNum, int *dlightMap, int *pshadowMap ); + +void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, + int fogIndex, int dlightMap, int pshadowMap ); + +void R_CalcTangentSpace(vec3_t tangent, vec3_t bitangent, vec3_t normal, + const vec3_t v0, const vec3_t v1, const vec3_t v2, const vec2_t t0, const vec2_t t1, const vec2_t t2); +qboolean R_CalcTangentVectors(srfVert_t * dv[3]); +void R_CalcSurfaceTriangleNeighbors(int numTriangles, srfTriangle_t * triangles); +void R_CalcSurfaceTrianglePlanes(int numTriangles, srfTriangle_t * triangles, srfVert_t * verts); + +#define CULL_IN 0 // completely unclipped +#define CULL_CLIP 1 // clipped by one or more planes +#define CULL_OUT 2 // completely outside the clipping planes +void R_LocalNormalToWorld (const vec3_t local, vec3_t world); +void R_LocalPointToWorld (const vec3_t local, vec3_t world); +int R_CullBox (vec3_t bounds[2]); +int R_CullLocalBox (vec3_t bounds[2]); +int R_CullPointAndRadiusEx( const vec3_t origin, float radius, const cplane_t* frustum, int numPlanes ); +int R_CullPointAndRadius( const vec3_t origin, float radius ); +int R_CullLocalPointAndRadius( const vec3_t origin, float radius ); + +void R_SetupProjection(viewParms_t *dest, float zProj, float zFar, qboolean computeFrustum); +void R_RotateForEntity( const trRefEntity_t *ent, const viewParms_t *viewParms, orientationr_t *or ); + +/* +** GL wrapper/helper functions +*/ +void GL_Bind( image_t *image ); +void GL_BindCubemap( image_t *image ); +void GL_BindToTMU( image_t *image, int tmu ); +void GL_SetDefaultState (void); +void GL_SelectTexture( int unit ); +void GL_TextureMode( const char *string ); +void GL_CheckErrs( char *file, int line ); +#define GL_CheckErrors(...) GL_CheckErrs(__FILE__, __LINE__) +void GL_State( unsigned long stateVector ); +void GL_SetProjectionMatrix(matrix_t matrix); +void GL_SetModelviewMatrix(matrix_t matrix); +void GL_TexEnv( int env ); +void GL_Cull( int cullType ); + +#define GLS_SRCBLEND_ZERO 0x00000001 +#define GLS_SRCBLEND_ONE 0x00000002 +#define GLS_SRCBLEND_DST_COLOR 0x00000003 +#define GLS_SRCBLEND_ONE_MINUS_DST_COLOR 0x00000004 +#define GLS_SRCBLEND_SRC_ALPHA 0x00000005 +#define GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA 0x00000006 +#define GLS_SRCBLEND_DST_ALPHA 0x00000007 +#define GLS_SRCBLEND_ONE_MINUS_DST_ALPHA 0x00000008 +#define GLS_SRCBLEND_ALPHA_SATURATE 0x00000009 +#define GLS_SRCBLEND_BITS 0x0000000f + +#define GLS_DSTBLEND_ZERO 0x00000010 +#define GLS_DSTBLEND_ONE 0x00000020 +#define GLS_DSTBLEND_SRC_COLOR 0x00000030 +#define GLS_DSTBLEND_ONE_MINUS_SRC_COLOR 0x00000040 +#define GLS_DSTBLEND_SRC_ALPHA 0x00000050 +#define GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA 0x00000060 +#define GLS_DSTBLEND_DST_ALPHA 0x00000070 +#define GLS_DSTBLEND_ONE_MINUS_DST_ALPHA 0x00000080 +#define GLS_DSTBLEND_BITS 0x000000f0 + +#define GLS_DEPTHMASK_TRUE 0x00000100 + +#define GLS_POLYMODE_LINE 0x00001000 + +#define GLS_DEPTHTEST_DISABLE 0x00010000 +#define GLS_DEPTHFUNC_EQUAL 0x00020000 +#define GLS_DEPTHFUNC_GREATER 0x00040000 +#define GLS_DEPTHFUNC_BITS 0x00060000 + +#define GLS_ATEST_GT_0 0x10000000 +#define GLS_ATEST_LT_80 0x20000000 +#define GLS_ATEST_GE_80 0x40000000 +#define GLS_ATEST_BITS 0x70000000 + +#define GLS_DEFAULT GLS_DEPTHMASK_TRUE + +void RE_StretchRaw (int x, int y, int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty); +void RE_UploadCinematic (int w, int h, int cols, int rows, const byte *data, int client, qboolean dirty); + +void RE_BeginFrame( stereoFrame_t stereoFrame ); +void RE_BeginRegistration( glconfig_t *glconfig ); +void RE_LoadWorldMap( const char *mapname ); +void RE_SetWorldVisData( const byte *vis ); +qhandle_t RE_RegisterModel( const char *name ); +qhandle_t RE_RegisterSkin( const char *name ); +void RE_Shutdown( qboolean destroyWindow ); + +qboolean R_GetEntityToken( char *buffer, int size ); + +model_t *R_AllocModel( void ); + +void R_Init( void ); +image_t *R_FindImageFile( const char *name, imgType_t type, imgFlags_t flags ); +image_t *R_CreateImage( const char *name, byte *pic, int width, int height, imgType_t type, imgFlags_t flags, int internalFormat ); +void R_UpdateSubImage( image_t *image, byte *pic, int x, int y, int width, int height ); +qboolean R_GetModeInfo( int *width, int *height, float *windowAspect, int mode ); + +void R_SetColorMappings( void ); +void R_GammaCorrect( byte *buffer, int bufSize ); + +void R_ImageList_f( void ); +void R_SkinList_f( void ); +// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=516 +const void *RB_TakeScreenshotCmd( const void *data ); +void R_ScreenShot_f( void ); + +void R_InitFogTable( void ); +float R_FogFactor( float s, float t ); +void R_InitImages( void ); +void R_DeleteTextures( void ); +int R_SumOfUsedImages( void ); +void R_InitSkins( void ); +skin_t *R_GetSkinByHandle( qhandle_t hSkin ); + +int R_ComputeLOD( trRefEntity_t *ent ); + +const void *RB_TakeVideoFrameCmd( const void *data ); + +// +// tr_shader.c +// +qhandle_t RE_RegisterShaderLightMap( const char *name, int lightmapIndex ); +qhandle_t RE_RegisterShader( const char *name ); +qhandle_t RE_RegisterShaderNoMip( const char *name ); +qhandle_t RE_RegisterShaderFromImage(const char *name, int lightmapIndex, image_t *image, qboolean mipRawImage); + +shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImage ); +shader_t *R_GetShaderByHandle( qhandle_t hShader ); +shader_t *R_GetShaderByState( int index, long *cycleTime ); +shader_t *R_FindShaderByName( const char *name ); +void R_InitShaders( void ); +void R_ShaderList_f( void ); +void R_RemapShader(const char *oldShader, const char *newShader, const char *timeOffset); + +/* +==================================================================== + +IMPLEMENTATION SPECIFIC FUNCTIONS + +==================================================================== +*/ + +void GLimp_Init( void ); +void GLimp_Shutdown( void ); +void GLimp_EndFrame( void ); + +void GLimp_LogComment( char *comment ); +void GLimp_Minimize(void); + +// NOTE TTimo linux works with float gamma value, not the gamma table +// the params won't be used, getting the r_gamma cvar directly +void GLimp_SetGamma( unsigned char red[256], + unsigned char green[256], + unsigned char blue[256] ); + + +void GLimp_InitExtraExtensions( void ); +/* +==================================================================== + +TESSELATOR/SHADER DECLARATIONS + +==================================================================== +*/ + +typedef struct stageVars +{ + color4ub_t colors[SHADER_MAX_VERTEXES]; + vec2_t texcoords[NUM_TEXTURE_BUNDLES][SHADER_MAX_VERTEXES]; +} stageVars_t; + +#define MAX_MULTIDRAW_PRIMITIVES 16384 + +typedef struct shaderCommands_s +{ + glIndex_t indexes[SHADER_MAX_INDEXES] QALIGN(16); + vec4_t xyz[SHADER_MAX_VERTEXES] QALIGN(16); + vec4_t normal[SHADER_MAX_VERTEXES] QALIGN(16); +#ifdef USE_VERT_TANGENT_SPACE + vec4_t tangent[SHADER_MAX_VERTEXES] QALIGN(16); + vec4_t bitangent[SHADER_MAX_VERTEXES] QALIGN(16); +#endif + vec2_t texCoords[SHADER_MAX_VERTEXES][2] QALIGN(16); + vec4_t vertexColors[SHADER_MAX_VERTEXES] QALIGN(16); + vec4_t lightdir[SHADER_MAX_VERTEXES] QALIGN(16); + //int vertexDlightBits[SHADER_MAX_VERTEXES] QALIGN(16); + + VBO_t *vbo; + IBO_t *ibo; + qboolean useInternalVBO; + + stageVars_t svars QALIGN(16); + + //color4ub_t constantColor255[SHADER_MAX_VERTEXES] QALIGN(16); + + shader_t *shader; + float shaderTime; + int fogNum; + + int dlightBits; // or together of all vertexDlightBits + int pshadowBits; + + int firstIndex; + int numIndexes; + int numVertexes; + glIndex_t minIndex; + glIndex_t maxIndex; + + int multiDrawPrimitives; + GLsizei multiDrawNumIndexes[MAX_MULTIDRAW_PRIMITIVES]; + glIndex_t *multiDrawFirstIndex[MAX_MULTIDRAW_PRIMITIVES]; + glIndex_t *multiDrawLastIndex[MAX_MULTIDRAW_PRIMITIVES]; + glIndex_t multiDrawMinIndex[MAX_MULTIDRAW_PRIMITIVES]; + glIndex_t multiDrawMaxIndex[MAX_MULTIDRAW_PRIMITIVES]; + + // info extracted from current shader + int numPasses; + void (*currentStageIteratorFunc)( void ); + shaderStage_t **xstages; +} shaderCommands_t; + +extern shaderCommands_t tess; + +void RB_BeginSurface(shader_t *shader, int fogNum ); +void RB_EndSurface(void); +void RB_CheckOverflow( int verts, int indexes ); +#define RB_CHECKOVERFLOW(v,i) if (tess.numVertexes + (v) >= SHADER_MAX_VERTEXES || tess.numIndexes + (i) >= SHADER_MAX_INDEXES ) {RB_CheckOverflow(v,i);} + +void R_DrawElementsVBO( int numIndexes, glIndex_t firstIndex, glIndex_t minIndex, glIndex_t maxIndex ); +void RB_StageIteratorGeneric( void ); +void RB_StageIteratorSky( void ); +void RB_StageIteratorVertexLitTexture( void ); +void RB_StageIteratorLightmappedMultitexture( void ); + +void RB_AddQuadStamp( vec3_t origin, vec3_t left, vec3_t up, float color[4] ); +void RB_AddQuadStampExt( vec3_t origin, vec3_t left, vec3_t up, float color[4], float s1, float t1, float s2, float t2 ); +void RB_InstantQuad( vec4_t quadVerts[4] ); +//void RB_InstantQuad2(vec4_t quadVerts[4], vec2_t texCoords[4], vec4_t color, shaderProgram_t *sp, vec2_t invTexRes); +void RB_InstantQuad2(vec4_t quadVerts[4], vec2_t texCoords[4]); + +void RB_ShowImages( void ); + + +/* +============================================================ + +WORLD MAP + +============================================================ +*/ + +void R_AddBrushModelSurfaces( trRefEntity_t *e ); +void R_AddWorldSurfaces( void ); +qboolean R_inPVS( const vec3_t p1, const vec3_t p2 ); + + +/* +============================================================ + +FLARES + +============================================================ +*/ + +void R_ClearFlares( void ); + +void RB_AddFlare( void *surface, int fogNum, vec3_t point, vec3_t color, vec3_t normal ); +void RB_AddDlightFlares( void ); +void RB_RenderFlares (void); + +/* +============================================================ + +LIGHTS + +============================================================ +*/ + +void R_DlightBmodel( bmodel_t *bmodel ); +void R_SetupEntityLighting( const trRefdef_t *refdef, trRefEntity_t *ent ); +void R_TransformDlights( int count, dlight_t *dl, orientationr_t *or ); +int R_LightForPoint( vec3_t point, vec3_t ambientLight, vec3_t directedLight, vec3_t lightDir ); +int R_LightDirForPoint( vec3_t point, vec3_t lightDir, vec3_t normal, world_t *world ); + + +/* +============================================================ + +SHADOWS + +============================================================ +*/ + +void RB_ShadowTessEnd( void ); +void RB_ShadowFinish( void ); +void RB_ProjectionShadowDeform( void ); + +/* +============================================================ + +SKIES + +============================================================ +*/ + +void R_BuildCloudData( shaderCommands_t *shader ); +void R_InitSkyTexCoords( float cloudLayerHeight ); +void R_DrawSkyBox( shaderCommands_t *shader ); +void RB_DrawSun( float scale, shader_t *shader ); +void RB_ClipSkyPolygons( shaderCommands_t *shader ); + +/* +============================================================ + +CURVE TESSELATION + +============================================================ +*/ + +#define PATCH_STITCHING + +srfGridMesh_t *R_SubdividePatchToGrid( int width, int height, + srfVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ); +srfGridMesh_t *R_GridInsertColumn( srfGridMesh_t *grid, int column, int row, vec3_t point, float loderror ); +srfGridMesh_t *R_GridInsertRow( srfGridMesh_t *grid, int row, int column, vec3_t point, float loderror ); +void R_FreeSurfaceGridMesh( srfGridMesh_t *grid ); + +/* +============================================================ + +MARKERS, POLYGON PROJECTION ON WORLD POLYGONS + +============================================================ +*/ + +int R_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection, + int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ); + + +/* +============================================================ + +VERTEX BUFFER OBJECTS + +============================================================ +*/ +VBO_t *R_CreateVBO(const char *name, byte * vertexes, int vertexesSize, vboUsage_t usage); +VBO_t *R_CreateVBO2(const char *name, int numVertexes, srfVert_t * vertexes, uint32_t stateBits, vboUsage_t usage); + +IBO_t *R_CreateIBO(const char *name, byte * indexes, int indexesSize, vboUsage_t usage); +IBO_t *R_CreateIBO2(const char *name, int numTriangles, srfTriangle_t * triangles, vboUsage_t usage); + +void R_BindVBO(VBO_t * vbo); +void R_BindNullVBO(void); + +void R_BindIBO(IBO_t * ibo); +void R_BindNullIBO(void); + +void R_InitVBOs(void); +void R_ShutdownVBOs(void); +void R_VBOList_f(void); + +void RB_UpdateVBOs(unsigned int attribBits); + + +/* +============================================================ + +GLSL + +============================================================ +*/ + +void GLSL_InitGPUShaders(void); +void GLSL_ShutdownGPUShaders(void); +void GLSL_VertexAttribsState(uint32_t stateBits); +void GLSL_VertexAttribPointers(uint32_t attribBits); +void GLSL_BindProgram(shaderProgram_t * program); +void GLSL_BindNullProgram(void); + +void GLSL_SetNumUniforms(shaderProgram_t *program, int numUniforms); +void GLSL_SetUniformName(shaderProgram_t *program, int uniformNum, const char *name); +void GLSL_SetUniformInt(shaderProgram_t *program, int uniformNum, GLint value); +void GLSL_SetUniformFloat(shaderProgram_t *program, int uniformNum, GLfloat value); +void GLSL_SetUniformFloat5(shaderProgram_t *program, int uniformNum, const vec5_t v); +void GLSL_SetUniformVec2(shaderProgram_t *program, int uniformNum, const vec2_t v); +void GLSL_SetUniformVec3(shaderProgram_t *program, int uniformNum, const vec3_t v); +void GLSL_SetUniformVec4(shaderProgram_t *program, int uniformNum, const vec4_t v); +void GLSL_SetUniformMatrix16(shaderProgram_t *program, int uniformNum, const matrix_t matrix); + +shaderProgram_t *GLSL_GetGenericShaderProgram(int stage); + +/* +============================================================ + +SCENE GENERATION + +============================================================ +*/ + +void R_InitNextFrame( void ); + +void RE_ClearScene( void ); +void RE_AddRefEntityToScene( const refEntity_t *ent ); +void RE_AddPolyToScene( qhandle_t hShader , int numVerts, const polyVert_t *verts, int num ); +void RE_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ); +void RE_AddAdditiveLightToScene( const vec3_t org, float intensity, float r, float g, float b ); +void RE_RenderScene( const refdef_t *fd ); + +#ifdef RAVENMD4 +/* +============================================================= + +UNCOMPRESSING BONES + +============================================================= +*/ + +#define MC_BITS_X (16) +#define MC_BITS_Y (16) +#define MC_BITS_Z (16) +#define MC_BITS_VECT (16) + +#define MC_SCALE_X (1.0f/64) +#define MC_SCALE_Y (1.0f/64) +#define MC_SCALE_Z (1.0f/64) + +void MC_UnCompress(float mat[3][4],const unsigned char * comp); +#endif + +/* +============================================================= + +ANIMATED MODELS + +============================================================= +*/ + +// void R_MakeAnimModel( model_t *model ); haven't seen this one really, so not needed I guess. +void R_AddAnimSurfaces( trRefEntity_t *ent ); +void RB_SurfaceAnim( md4Surface_t *surfType ); +#ifdef RAVENMD4 +void R_MDRAddAnimSurfaces( trRefEntity_t *ent ); +void RB_MDRSurfaceAnim( md4Surface_t *surface ); +#endif +qboolean R_LoadIQM (model_t *mod, void *buffer, int filesize, const char *name ); +void R_AddIQMSurfaces( trRefEntity_t *ent ); +void RB_IQMSurfaceAnim( surfaceType_t *surface ); +int R_IQMLerpTag( orientation_t *tag, iqmData_t *data, + int startFrame, int endFrame, + float frac, const char *tagName ); + +/* +============================================================= + +IMAGE LOADERS + +============================================================= +*/ + +void R_LoadBMP( const char *name, byte **pic, int *width, int *height ); +void R_LoadJPG( const char *name, byte **pic, int *width, int *height ); +void R_LoadPCX( const char *name, byte **pic, int *width, int *height ); +void R_LoadPNG( const char *name, byte **pic, int *width, int *height ); +void R_LoadTGA( const char *name, byte **pic, int *width, int *height ); + +/* +============================================================= +============================================================= +*/ +void R_TransformModelToClip( const vec3_t src, const float *modelMatrix, const float *projectionMatrix, + vec4_t eye, vec4_t dst ); +void R_TransformClipToWindow( const vec4_t clip, const viewParms_t *view, vec4_t normalized, vec4_t window ); + +void RB_DeformTessGeometry( void ); + +void RB_CalcEnvironmentTexCoords( float *dstTexCoords ); +void RB_CalcFogTexCoords( float *dstTexCoords ); +void RB_CalcScrollTexCoords( const float scroll[2], float *dstTexCoords ); +void RB_CalcRotateTexCoords( float rotSpeed, float *dstTexCoords ); +void RB_CalcScaleTexCoords( const float scale[2], float *dstTexCoords ); +void RB_CalcTurbulentTexCoords( const waveForm_t *wf, float *dstTexCoords ); +void RB_CalcTransformTexCoords( const texModInfo_t *tmi, float *dstTexCoords ); + +void RB_CalcScaleTexMatrix( const float scale[2], float *matrix ); +void RB_CalcScrollTexMatrix( const float scrollSpeed[2], float *matrix ); +void RB_CalcRotateTexMatrix( float degsPerSecond, float *matrix ); +void RB_CalcTurbulentTexMatrix( const waveForm_t *wf, matrix_t matrix ); +void RB_CalcTransformTexMatrix( const texModInfo_t *tmi, float *matrix ); +void RB_CalcStretchTexMatrix( const waveForm_t *wf, float *matrix ); + +void RB_CalcModulateColorsByFog( unsigned char *dstColors ); +void RB_CalcModulateAlphasByFog( unsigned char *dstColors ); +void RB_CalcModulateRGBAsByFog( unsigned char *dstColors ); +void RB_CalcWaveAlpha( const waveForm_t *wf, unsigned char *dstColors ); +float RB_CalcWaveAlphaSingle( const waveForm_t *wf ); +void RB_CalcWaveColor( const waveForm_t *wf, unsigned char *dstColors ); +float RB_CalcWaveColorSingle( const waveForm_t *wf ); +void RB_CalcAlphaFromEntity( unsigned char *dstColors ); +void RB_CalcAlphaFromOneMinusEntity( unsigned char *dstColors ); +void RB_CalcStretchTexCoords( const waveForm_t *wf, float *texCoords ); +void RB_CalcColorFromEntity( unsigned char *dstColors ); +void RB_CalcColorFromOneMinusEntity( unsigned char *dstColors ); +void RB_CalcSpecularAlpha( unsigned char *alphas ); +void RB_CalcDiffuseColor( unsigned char *colors ); + +/* +============================================================= + +RENDERER BACK END FUNCTIONS + +============================================================= +*/ + +void RB_ExecuteRenderCommands( const void *data ); + +/* +============================================================= + +RENDERER BACK END COMMAND QUEUE + +============================================================= +*/ + +#define MAX_RENDER_COMMANDS 0x40000 + +typedef struct { + byte cmds[MAX_RENDER_COMMANDS]; + int used; +} renderCommandList_t; + +typedef struct { + int commandId; + float color[4]; +} setColorCommand_t; + +typedef struct { + int commandId; + int buffer; +} drawBufferCommand_t; + +typedef struct { + int commandId; + image_t *image; + int width; + int height; + void *data; +} subImageCommand_t; + +typedef struct { + int commandId; +} swapBuffersCommand_t; + +typedef struct { + int commandId; + int buffer; +} endFrameCommand_t; + +typedef struct { + int commandId; + shader_t *shader; + float x, y; + float w, h; + float s1, t1; + float s2, t2; +} stretchPicCommand_t; + +typedef struct { + int commandId; + trRefdef_t refdef; + viewParms_t viewParms; + drawSurf_t *drawSurfs; + int numDrawSurfs; +} drawSurfsCommand_t; + +typedef struct { + int commandId; + int x; + int y; + int width; + int height; + char *fileName; + qboolean jpeg; +} screenshotCommand_t; + +typedef struct { + int commandId; + int width; + int height; + byte *captureBuffer; + byte *encodeBuffer; + qboolean motionJpeg; +} videoFrameCommand_t; + +typedef struct +{ + int commandId; + + GLboolean rgba[4]; +} colorMaskCommand_t; + +typedef struct +{ + int commandId; +} clearDepthCommand_t; + +typedef struct { + int commandId; + int map; + int cubeSide; +} capShadowmapCommand_t; + +typedef struct { + int commandId; + trRefdef_t refdef; + viewParms_t viewParms; +} postProcessCommand_t; + +typedef enum { + RC_END_OF_LIST, + RC_SET_COLOR, + RC_STRETCH_PIC, + RC_DRAW_SURFS, + RC_DRAW_BUFFER, + RC_SWAP_BUFFERS, + RC_SCREENSHOT, + RC_VIDEOFRAME, + RC_COLORMASK, + RC_CLEARDEPTH, + RC_CAPSHADOWMAP, + RC_POSTPROCESS +} renderCommand_t; + + +// these are sort of arbitrary limits. +// the limits apply to the sum of all scenes in a frame -- +// the main view, all the 3D icons, etc +#define MAX_POLYS 600 +#define MAX_POLYVERTS 3000 + +// all of the information needed by the back end must be +// contained in a backEndData_t +typedef struct { + drawSurf_t drawSurfs[MAX_DRAWSURFS]; + dlight_t dlights[MAX_DLIGHTS]; + trRefEntity_t entities[MAX_REFENTITIES]; + srfPoly_t *polys;//[MAX_POLYS]; + polyVert_t *polyVerts;//[MAX_POLYVERTS]; + pshadow_t pshadows[MAX_CALC_PSHADOWS]; + renderCommandList_t commands; +} backEndData_t; + +extern int max_polys; +extern int max_polyverts; + +extern backEndData_t *backEndData; // the second one may not be allocated + +extern volatile renderCommandList_t *renderCommandList; + + +void *R_GetCommandBuffer( int bytes ); +void RB_ExecuteRenderCommands( const void *data ); + +void R_IssuePendingRenderCommands( void ); + +void R_AddDrawSurfCmd( drawSurf_t *drawSurfs, int numDrawSurfs ); +void R_AddCapShadowmapCmd( int dlight, int cubeSide ); +void R_AddPostProcessCmd (void); + +void RE_SetColor( const float *rgba ); +void RE_SetClipRegion( const float *region ); +void RE_StretchPic ( float x, float y, float w, float h, + float s1, float t1, float s2, float t2, qhandle_t hShader ); +void RE_BeginFrame( stereoFrame_t stereoFrame ); +void RE_EndFrame( int *frontEndMsec, int *backEndMsec ); +void RE_SaveJPG(char * filename, int quality, int image_width, int image_height, + unsigned char *image_buffer, int padding); +size_t RE_SaveJPGToBuffer(byte *buffer, size_t bufSize, int quality, + int image_width, int image_height, byte *image_buffer, int padding); +void RE_TakeVideoFrame( int width, int height, + byte *captureBuffer, byte *encodeBuffer, qboolean motionJpeg ); + +// font stuff +void R_InitFreeType( void ); +void R_DoneFreeType( void ); +void RE_RegisterFont(const char *fontName, int pointSize, fontInfo_t *font); + + +#endif //TR_LOCAL_H diff --git a/src/renderergl2/tr_main.c b/src/renderergl2/tr_main.c new file mode 100644 index 00000000..0935dce7 --- /dev/null +++ b/src/renderergl2/tr_main.c @@ -0,0 +1,2882 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_main.c -- main control flow for each frame + +#include "tr_local.h" + +#include // memcpy + +trGlobals_t tr; + +static float s_flipMatrix[16] = { + // convert from our coordinate system (looking down X) + // to OpenGL's coordinate system (looking down -Z) + 0, 0, -1, 0, + -1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 0, 1 +}; + + +refimport_t ri; + +// entities that will have procedurally generated surfaces will just +// point at this for their sorting surface +surfaceType_t entitySurface = SF_ENTITY; + +/* +================ +R_CompareVert +================ +*/ +qboolean R_CompareVert(srfVert_t * v1, srfVert_t * v2, qboolean checkST) +{ + int i; + + for(i = 0; i < 3; i++) + { + if(floor(v1->xyz[i] + 0.1) != floor(v2->xyz[i] + 0.1)) + { + return qfalse; + } + + if(checkST && ((v1->st[0] != v2->st[0]) || (v1->st[1] != v2->st[1]))) + { + return qfalse; + } + } + + return qtrue; +} + +/* +============= +R_CalcNormalForTriangle +============= +*/ +void R_CalcNormalForTriangle(vec3_t normal, const vec3_t v0, const vec3_t v1, const vec3_t v2) +{ + vec3_t udir, vdir; + + // compute the face normal based on vertex points + VectorSubtract(v2, v0, udir); + VectorSubtract(v1, v0, vdir); + CrossProduct(udir, vdir, normal); + + VectorNormalize(normal); +} + +/* +============= +R_CalcTangentsForTriangle +http://members.rogers.com/deseric/tangentspace.htm +============= +*/ +void R_CalcTangentsForTriangle(vec3_t tangent, vec3_t bitangent, + const vec3_t v0, const vec3_t v1, const vec3_t v2, + const vec2_t t0, const vec2_t t1, const vec2_t t2) +{ + int i; + vec3_t planes[3]; + vec3_t u, v; + + for(i = 0; i < 3; i++) + { + VectorSet(u, v1[i] - v0[i], t1[0] - t0[0], t1[1] - t0[1]); + VectorSet(v, v2[i] - v0[i], t2[0] - t0[0], t2[1] - t0[1]); + + VectorNormalize(u); + VectorNormalize(v); + + CrossProduct(u, v, planes[i]); + } + + //So your tangent space will be defined by this : + //Normal = Normal of the triangle or Tangent X Bitangent (careful with the cross product, + // you have to make sure the normal points in the right direction) + //Tangent = ( dp(Fx(s,t)) / ds, dp(Fy(s,t)) / ds, dp(Fz(s,t)) / ds ) or ( -Bx/Ax, -By/Ay, - Bz/Az ) + //Bitangent = ( dp(Fx(s,t)) / dt, dp(Fy(s,t)) / dt, dp(Fz(s,t)) / dt ) or ( -Cx/Ax, -Cy/Ay, -Cz/Az ) + + // tangent... + tangent[0] = -planes[0][1] / planes[0][0]; + tangent[1] = -planes[1][1] / planes[1][0]; + tangent[2] = -planes[2][1] / planes[2][0]; + VectorNormalize(tangent); + + // bitangent... + bitangent[0] = -planes[0][2] / planes[0][0]; + bitangent[1] = -planes[1][2] / planes[1][0]; + bitangent[2] = -planes[2][2] / planes[2][0]; + VectorNormalize(bitangent); +} + + + + +/* +============= +R_CalcTangentSpace +============= +*/ +void R_CalcTangentSpace(vec3_t tangent, vec3_t bitangent, vec3_t normal, + const vec3_t v0, const vec3_t v1, const vec3_t v2, const vec2_t t0, const vec2_t t1, const vec2_t t2) +{ + vec3_t cp, u, v; + vec3_t faceNormal; + + VectorSet(u, v1[0] - v0[0], t1[0] - t0[0], t1[1] - t0[1]); + VectorSet(v, v2[0] - v0[0], t2[0] - t0[0], t2[1] - t0[1]); + + CrossProduct(u, v, cp); + if(fabs(cp[0]) > 10e-6) + { + tangent[0] = -cp[1] / cp[0]; + bitangent[0] = -cp[2] / cp[0]; + } + + u[0] = v1[1] - v0[1]; + v[0] = v2[1] - v0[1]; + + CrossProduct(u, v, cp); + if(fabs(cp[0]) > 10e-6) + { + tangent[1] = -cp[1] / cp[0]; + bitangent[1] = -cp[2] / cp[0]; + } + + u[0] = v1[2] - v0[2]; + v[0] = v2[2] - v0[2]; + + CrossProduct(u, v, cp); + if(fabs(cp[0]) > 10e-6) + { + tangent[2] = -cp[1] / cp[0]; + bitangent[2] = -cp[2] / cp[0]; + } + + VectorNormalize(tangent); + VectorNormalize(bitangent); + + // compute the face normal based on vertex points + if ( normal[0] == 0.0f && normal[1] == 0.0f && normal[2] == 0.0f ) + { + VectorSubtract(v2, v0, u); + VectorSubtract(v1, v0, v); + CrossProduct(u, v, faceNormal); + } + else + { + VectorCopy(normal, faceNormal); + } + + VectorNormalize(faceNormal); + +#if 1 + // Gram-Schmidt orthogonalize + //tangent[a] = (t - n * Dot(n, t)).Normalize(); + VectorMA(tangent, -DotProduct(faceNormal, tangent), faceNormal, tangent); + VectorNormalize(tangent); + + // compute the cross product B=NxT + //CrossProduct(normal, tangent, bitangent); +#else + // normal, compute the cross product N=TxB + CrossProduct(tangent, bitangent, normal); + VectorNormalize(normal); + + if(DotProduct(normal, faceNormal) < 0) + { + //VectorInverse(normal); + //VectorInverse(tangent); + //VectorInverse(bitangent); + + // compute the cross product T=BxN + CrossProduct(bitangent, faceNormal, tangent); + + // compute the cross product B=NxT + //CrossProduct(normal, tangent, bitangent); + } +#endif + + VectorCopy(faceNormal, normal); +} + +void R_CalcTangentSpaceFast(vec3_t tangent, vec3_t bitangent, vec3_t normal, + const vec3_t v0, const vec3_t v1, const vec3_t v2, const vec2_t t0, const vec2_t t1, const vec2_t t2) +{ + vec3_t cp, u, v; + vec3_t faceNormal; + + VectorSet(u, v1[0] - v0[0], t1[0] - t0[0], t1[1] - t0[1]); + VectorSet(v, v2[0] - v0[0], t2[0] - t0[0], t2[1] - t0[1]); + + CrossProduct(u, v, cp); + if(fabs(cp[0]) > 10e-6) + { + tangent[0] = -cp[1] / cp[0]; + bitangent[0] = -cp[2] / cp[0]; + } + + u[0] = v1[1] - v0[1]; + v[0] = v2[1] - v0[1]; + + CrossProduct(u, v, cp); + if(fabs(cp[0]) > 10e-6) + { + tangent[1] = -cp[1] / cp[0]; + bitangent[1] = -cp[2] / cp[0]; + } + + u[0] = v1[2] - v0[2]; + v[0] = v2[2] - v0[2]; + + CrossProduct(u, v, cp); + if(fabs(cp[0]) > 10e-6) + { + tangent[2] = -cp[1] / cp[0]; + bitangent[2] = -cp[2] / cp[0]; + } + + VectorNormalizeFast(tangent); + VectorNormalizeFast(bitangent); + + // compute the face normal based on vertex points + VectorSubtract(v2, v0, u); + VectorSubtract(v1, v0, v); + CrossProduct(u, v, faceNormal); + + VectorNormalizeFast(faceNormal); + +#if 0 + // normal, compute the cross product N=TxB + CrossProduct(tangent, bitangent, normal); + VectorNormalizeFast(normal); + + if(DotProduct(normal, faceNormal) < 0) + { + VectorInverse(normal); + //VectorInverse(tangent); + //VectorInverse(bitangent); + + CrossProduct(normal, tangent, bitangent); + } + + VectorCopy(faceNormal, normal); +#else + // Gram-Schmidt orthogonalize + //tangent[a] = (t - n * Dot(n, t)).Normalize(); + VectorMA(tangent, -DotProduct(faceNormal, tangent), faceNormal, tangent); + VectorNormalizeFast(tangent); +#endif + + VectorCopy(faceNormal, normal); +} + +/* +http://www.terathon.com/code/tangent.html +*/ +void R_CalcTBN(vec3_t tangent, vec3_t bitangent, vec3_t normal, + const vec3_t v1, const vec3_t v2, const vec3_t v3, const vec2_t w1, const vec2_t w2, const vec2_t w3) +{ + vec3_t u, v; + float x1, x2, y1, y2, z1, z2; + float s1, s2, t1, t2; + float r, dot; + + x1 = v2[0] - v1[0]; + x2 = v3[0] - v1[0]; + y1 = v2[1] - v1[1]; + y2 = v3[1] - v1[1]; + z1 = v2[2] - v1[2]; + z2 = v3[2] - v1[2]; + + s1 = w2[0] - w1[0]; + s2 = w3[0] - w1[0]; + t1 = w2[1] - w1[1]; + t2 = w3[1] - w1[1]; + + r = 1.0f / (s1 * t2 - s2 * t1); + + VectorSet(tangent, (t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); + VectorSet(bitangent, (s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); + + // compute the face normal based on vertex points + VectorSubtract(v3, v1, u); + VectorSubtract(v2, v1, v); + CrossProduct(u, v, normal); + + VectorNormalize(normal); + + // Gram-Schmidt orthogonalize + //tangent[a] = (t - n * Dot(n, t)).Normalize(); + dot = DotProduct(normal, tangent); + VectorMA(tangent, -dot, normal, tangent); + VectorNormalize(tangent); + + // B=NxT + //CrossProduct(normal, tangent, bitangent); +} + +void R_CalcTBN2(vec3_t tangent, vec3_t bitangent, vec3_t normal, + const vec3_t v1, const vec3_t v2, const vec3_t v3, const vec2_t t1, const vec2_t t2, const vec2_t t3) +{ + vec3_t v2v1; + vec3_t v3v1; + + float c2c1_T; + float c2c1_B; + + float c3c1_T; + float c3c1_B; + + float denominator; + float scale1, scale2; + + vec3_t T, B, N, C; + + + // Calculate the tangent basis for each vertex of the triangle + // UPDATE: In the 3rd edition of the accompanying article, the for-loop located here has + // been removed as it was redundant (the entire TBN matrix was calculated three times + // instead of just one). + // + // Please note, that this function relies on the fact that the input geometry are triangles + // and the tangent basis for each vertex thus is identical! + // + + // Calculate the vectors from the current vertex to the two other vertices in the triangle + VectorSubtract(v2, v1, v2v1); + VectorSubtract(v3, v1, v3v1); + + // The equation presented in the article states that: + // c2c1_T = V2.texcoord.x - V1.texcoord.x + // c2c1_B = V2.texcoord.y - V1.texcoord.y + // c3c1_T = V3.texcoord.x - V1.texcoord.x + // c3c1_B = V3.texcoord.y - V1.texcoord.y + + // Calculate c2c1_T and c2c1_B + c2c1_T = t2[0] - t1[0]; + c2c1_B = t2[1] - t2[1]; + + // Calculate c3c1_T and c3c1_B + c3c1_T = t3[0] - t1[0]; + c3c1_B = t3[1] - t1[1]; + + denominator = c2c1_T * c3c1_B - c3c1_T * c2c1_B; + //if(ROUNDOFF(fDenominator) == 0.0f) + if(denominator == 0.0f) + { + // We won't risk a divide by zero, so set the tangent matrix to the identity matrix + VectorSet(tangent, 1, 0, 0); + VectorSet(bitangent, 0, 1, 0); + VectorSet(normal, 0, 0, 1); + } + else + { + // Calculate the reciprocal value once and for all (to achieve speed) + scale1 = 1.0f / denominator; + + // T and B are calculated just as the equation in the article states + VectorSet(T, (c3c1_B * v2v1[0] - c2c1_B * v3v1[0]) * scale1, + (c3c1_B * v2v1[1] - c2c1_B * v3v1[1]) * scale1, + (c3c1_B * v2v1[2] - c2c1_B * v3v1[2]) * scale1); + + VectorSet(B, (-c3c1_T * v2v1[0] + c2c1_T * v3v1[0]) * scale1, + (-c3c1_T * v2v1[1] + c2c1_T * v3v1[1]) * scale1, + (-c3c1_T * v2v1[2] + c2c1_T * v3v1[2]) * scale1); + + // The normal N is calculated as the cross product between T and B + CrossProduct(T, B, N); + +#if 0 + VectorCopy(T, tangent); + VectorCopy(B, bitangent); + VectorCopy(N, normal); +#else + // Calculate the reciprocal value once and for all (to achieve speed) + scale2 = 1.0f / ((T[0] * B[1] * N[2] - T[2] * B[1] * N[0]) + + (B[0] * N[1] * T[2] - B[2] * N[1] * T[0]) + + (N[0] * T[1] * B[2] - N[2] * T[1] * B[0])); + + // Calculate the inverse if the TBN matrix using the formula described in the article. + // We store the basis vectors directly in the provided TBN matrix: pvTBNMatrix + CrossProduct(B, N, C); tangent[0] = C[0] * scale2; + CrossProduct(N, T, C); tangent[1] = -C[0] * scale2; + CrossProduct(T, B, C); tangent[2] = C[0] * scale2; + VectorNormalize(tangent); + + CrossProduct(B, N, C); bitangent[0] = -C[1] * scale2; + CrossProduct(N, T, C); bitangent[1] = C[1] * scale2; + CrossProduct(T, B, C); bitangent[2] = -C[1] * scale2; + VectorNormalize(bitangent); + + CrossProduct(B, N, C); normal[0] = C[2] * scale2; + CrossProduct(N, T, C); normal[1] = -C[2] * scale2; + CrossProduct(T, B, C); normal[2] = C[2] * scale2; + VectorNormalize(normal); +#endif + } +} + + +#ifdef USE_VERT_TANGENT_SPACE +qboolean R_CalcTangentVectors(srfVert_t * dv[3]) +{ + int i; + float bb, s, t; + vec3_t bary; + + + /* calculate barycentric basis for the triangle */ + bb = (dv[1]->st[0] - dv[0]->st[0]) * (dv[2]->st[1] - dv[0]->st[1]) - (dv[2]->st[0] - dv[0]->st[0]) * (dv[1]->st[1] - dv[0]->st[1]); + if(fabs(bb) < 0.00000001f) + return qfalse; + + /* do each vertex */ + for(i = 0; i < 3; i++) + { + // calculate s tangent vector + s = dv[i]->st[0] + 10.0f; + t = dv[i]->st[1]; + bary[0] = ((dv[1]->st[0] - s) * (dv[2]->st[1] - t) - (dv[2]->st[0] - s) * (dv[1]->st[1] - t)) / bb; + bary[1] = ((dv[2]->st[0] - s) * (dv[0]->st[1] - t) - (dv[0]->st[0] - s) * (dv[2]->st[1] - t)) / bb; + bary[2] = ((dv[0]->st[0] - s) * (dv[1]->st[1] - t) - (dv[1]->st[0] - s) * (dv[0]->st[1] - t)) / bb; + + dv[i]->tangent[0] = bary[0] * dv[0]->xyz[0] + bary[1] * dv[1]->xyz[0] + bary[2] * dv[2]->xyz[0]; + dv[i]->tangent[1] = bary[0] * dv[0]->xyz[1] + bary[1] * dv[1]->xyz[1] + bary[2] * dv[2]->xyz[1]; + dv[i]->tangent[2] = bary[0] * dv[0]->xyz[2] + bary[1] * dv[1]->xyz[2] + bary[2] * dv[2]->xyz[2]; + + VectorSubtract(dv[i]->tangent, dv[i]->xyz, dv[i]->tangent); + VectorNormalize(dv[i]->tangent); + + // calculate t tangent vector + s = dv[i]->st[0]; + t = dv[i]->st[1] + 10.0f; + bary[0] = ((dv[1]->st[0] - s) * (dv[2]->st[1] - t) - (dv[2]->st[0] - s) * (dv[1]->st[1] - t)) / bb; + bary[1] = ((dv[2]->st[0] - s) * (dv[0]->st[1] - t) - (dv[0]->st[0] - s) * (dv[2]->st[1] - t)) / bb; + bary[2] = ((dv[0]->st[0] - s) * (dv[1]->st[1] - t) - (dv[1]->st[0] - s) * (dv[0]->st[1] - t)) / bb; + + dv[i]->bitangent[0] = bary[0] * dv[0]->xyz[0] + bary[1] * dv[1]->xyz[0] + bary[2] * dv[2]->xyz[0]; + dv[i]->bitangent[1] = bary[0] * dv[0]->xyz[1] + bary[1] * dv[1]->xyz[1] + bary[2] * dv[2]->xyz[1]; + dv[i]->bitangent[2] = bary[0] * dv[0]->xyz[2] + bary[1] * dv[1]->xyz[2] + bary[2] * dv[2]->xyz[2]; + + VectorSubtract(dv[i]->bitangent, dv[i]->xyz, dv[i]->bitangent); + VectorNormalize(dv[i]->bitangent); + + // debug code + //% Sys_FPrintf( SYS_VRB, "%d S: (%f %f %f) T: (%f %f %f)\n", i, + //% stv[ i ][ 0 ], stv[ i ][ 1 ], stv[ i ][ 2 ], ttv[ i ][ 0 ], ttv[ i ][ 1 ], ttv[ i ][ 2 ] ); + } + + return qtrue; +} +#endif + + +/* +================= +R_FindSurfaceTriangleWithEdge +Tr3B - recoded from Q2E +================= +*/ +static int R_FindSurfaceTriangleWithEdge(int numTriangles, srfTriangle_t * triangles, int start, int end, int ignore) +{ + srfTriangle_t *tri; + int count, match; + int i; + + count = 0; + match = -1; + + for(i = 0, tri = triangles; i < numTriangles; i++, tri++) + { + if((tri->indexes[0] == start && tri->indexes[1] == end) || + (tri->indexes[1] == start && tri->indexes[2] == end) || (tri->indexes[2] == start && tri->indexes[0] == end)) + { + if(i != ignore) + { + match = i; + } + + count++; + } + else if((tri->indexes[1] == start && tri->indexes[0] == end) || + (tri->indexes[2] == start && tri->indexes[1] == end) || (tri->indexes[0] == start && tri->indexes[2] == end)) + { + count++; + } + } + + // detect edges shared by three triangles and make them seams + if(count > 2) + { + match = -1; + } + + return match; +} + + +/* +================= +R_CalcSurfaceTriangleNeighbors +Tr3B - recoded from Q2E +================= +*/ +void R_CalcSurfaceTriangleNeighbors(int numTriangles, srfTriangle_t * triangles) +{ + int i; + srfTriangle_t *tri; + + for(i = 0, tri = triangles; i < numTriangles; i++, tri++) + { + tri->neighbors[0] = R_FindSurfaceTriangleWithEdge(numTriangles, triangles, tri->indexes[1], tri->indexes[0], i); + tri->neighbors[1] = R_FindSurfaceTriangleWithEdge(numTriangles, triangles, tri->indexes[2], tri->indexes[1], i); + tri->neighbors[2] = R_FindSurfaceTriangleWithEdge(numTriangles, triangles, tri->indexes[0], tri->indexes[2], i); + } +} + +/* +================= +R_CalcSurfaceTrianglePlanes +================= +*/ +void R_CalcSurfaceTrianglePlanes(int numTriangles, srfTriangle_t * triangles, srfVert_t * verts) +{ + int i; + srfTriangle_t *tri; + + for(i = 0, tri = triangles; i < numTriangles; i++, tri++) + { + float *v1, *v2, *v3; + vec3_t d1, d2; + + v1 = verts[tri->indexes[0]].xyz; + v2 = verts[tri->indexes[1]].xyz; + v3 = verts[tri->indexes[2]].xyz; + + VectorSubtract(v2, v1, d1); + VectorSubtract(v3, v1, d2); + + CrossProduct(d2, d1, tri->plane); + tri->plane[3] = DotProduct(tri->plane, v1); + } +} + + +/* +================= +R_CullLocalBox + +Returns CULL_IN, CULL_CLIP, or CULL_OUT +================= +*/ +int R_CullLocalBox(vec3_t localBounds[2]) { +#if 0 + int i, j; + vec3_t transformed[8]; + float dists[8]; + vec3_t v; + cplane_t *frust; + int anyBack; + int front, back; + + if ( r_nocull->integer ) { + return CULL_CLIP; + } + + // transform into world space + for (i = 0 ; i < 8 ; i++) { + v[0] = bounds[i&1][0]; + v[1] = bounds[(i>>1)&1][1]; + v[2] = bounds[(i>>2)&1][2]; + + VectorCopy( tr.or.origin, transformed[i] ); + VectorMA( transformed[i], v[0], tr.or.axis[0], transformed[i] ); + VectorMA( transformed[i], v[1], tr.or.axis[1], transformed[i] ); + VectorMA( transformed[i], v[2], tr.or.axis[2], transformed[i] ); + } + + // check against frustum planes + anyBack = 0; + for (i = 0 ; i < 4 ; i++) { + frust = &tr.viewParms.frustum[i]; + + front = back = 0; + for (j = 0 ; j < 8 ; j++) { + dists[j] = DotProduct(transformed[j], frust->normal); + if ( dists[j] > frust->dist ) { + front = 1; + if ( back ) { + break; // a point is in front + } + } else { + back = 1; + } + } + if ( !front ) { + // all points were behind one of the planes + return CULL_OUT; + } + anyBack |= back; + } + + if ( !anyBack ) { + return CULL_IN; // completely inside frustum + } + + return CULL_CLIP; // partially clipped +#else + int j; + vec3_t transformed; + vec3_t v; + vec3_t worldBounds[2]; + + if(r_nocull->integer) + { + return CULL_CLIP; + } + + // transform into world space + ClearBounds(worldBounds[0], worldBounds[1]); + + for(j = 0; j < 8; j++) + { + v[0] = localBounds[j & 1][0]; + v[1] = localBounds[(j >> 1) & 1][1]; + v[2] = localBounds[(j >> 2) & 1][2]; + + R_LocalPointToWorld(v, transformed); + + AddPointToBounds(transformed, worldBounds[0], worldBounds[1]); + } + + return R_CullBox(worldBounds); +#endif +} + +/* +================= +R_CullBox + +Returns CULL_IN, CULL_CLIP, or CULL_OUT +================= +*/ +int R_CullBox(vec3_t worldBounds[2]) { + int i; + cplane_t *frust; + qboolean anyClip; + int r, numPlanes; + + numPlanes = (tr.viewParms.flags & VPF_FARPLANEFRUSTUM) ? 5 : 4; + + // check against frustum planes + anyClip = qfalse; + for(i = 0; i < numPlanes; i++) + { + frust = &tr.viewParms.frustum[i]; + + r = BoxOnPlaneSide(worldBounds[0], worldBounds[1], frust); + + if(r == 2) + { + // completely outside frustum + return CULL_OUT; + } + if(r == 3) + { + anyClip = qtrue; + } + } + + if(!anyClip) + { + // completely inside frustum + return CULL_IN; + } + + // partially clipped + return CULL_CLIP; +} + +/* +** R_CullLocalPointAndRadius +*/ +int R_CullLocalPointAndRadius( const vec3_t pt, float radius ) +{ + vec3_t transformed; + + R_LocalPointToWorld( pt, transformed ); + + return R_CullPointAndRadius( transformed, radius ); +} + +/* +** R_CullPointAndRadius +*/ +int R_CullPointAndRadiusEx( const vec3_t pt, float radius, const cplane_t* frustum, int numPlanes ) +{ + int i; + float dist; + const cplane_t *frust; + qboolean mightBeClipped = qfalse; + + if ( r_nocull->integer ) { + return CULL_CLIP; + } + + // check against frustum planes + for (i = 0 ; i < numPlanes ; i++) + { + frust = &frustum[i]; + + dist = DotProduct( pt, frust->normal) - frust->dist; + if ( dist < -radius ) + { + return CULL_OUT; + } + else if ( dist <= radius ) + { + mightBeClipped = qtrue; + } + } + + if ( mightBeClipped ) + { + return CULL_CLIP; + } + + return CULL_IN; // completely inside frustum +} + +/* +** R_CullPointAndRadius +*/ +int R_CullPointAndRadius( const vec3_t pt, float radius ) +{ + return R_CullPointAndRadiusEx(pt, radius, tr.viewParms.frustum, (tr.viewParms.flags & VPF_FARPLANEFRUSTUM) ? 5 : 4); +} + +/* +================= +R_LocalNormalToWorld + +================= +*/ +void R_LocalNormalToWorld (const vec3_t local, vec3_t world) { + world[0] = local[0] * tr.or.axis[0][0] + local[1] * tr.or.axis[1][0] + local[2] * tr.or.axis[2][0]; + world[1] = local[0] * tr.or.axis[0][1] + local[1] * tr.or.axis[1][1] + local[2] * tr.or.axis[2][1]; + world[2] = local[0] * tr.or.axis[0][2] + local[1] * tr.or.axis[1][2] + local[2] * tr.or.axis[2][2]; +} + +/* +================= +R_LocalPointToWorld + +================= +*/ +void R_LocalPointToWorld (const vec3_t local, vec3_t world) { + world[0] = local[0] * tr.or.axis[0][0] + local[1] * tr.or.axis[1][0] + local[2] * tr.or.axis[2][0] + tr.or.origin[0]; + world[1] = local[0] * tr.or.axis[0][1] + local[1] * tr.or.axis[1][1] + local[2] * tr.or.axis[2][1] + tr.or.origin[1]; + world[2] = local[0] * tr.or.axis[0][2] + local[1] * tr.or.axis[1][2] + local[2] * tr.or.axis[2][2] + tr.or.origin[2]; +} + +/* +================= +R_WorldToLocal + +================= +*/ +void R_WorldToLocal (const vec3_t world, vec3_t local) { + local[0] = DotProduct(world, tr.or.axis[0]); + local[1] = DotProduct(world, tr.or.axis[1]); + local[2] = DotProduct(world, tr.or.axis[2]); +} + +/* +========================== +R_TransformModelToClip + +========================== +*/ +void R_TransformModelToClip( const vec3_t src, const float *modelMatrix, const float *projectionMatrix, + vec4_t eye, vec4_t dst ) { + int i; + + for ( i = 0 ; i < 4 ; i++ ) { + eye[i] = + src[0] * modelMatrix[ i + 0 * 4 ] + + src[1] * modelMatrix[ i + 1 * 4 ] + + src[2] * modelMatrix[ i + 2 * 4 ] + + 1 * modelMatrix[ i + 3 * 4 ]; + } + + for ( i = 0 ; i < 4 ; i++ ) { + dst[i] = + eye[0] * projectionMatrix[ i + 0 * 4 ] + + eye[1] * projectionMatrix[ i + 1 * 4 ] + + eye[2] * projectionMatrix[ i + 2 * 4 ] + + eye[3] * projectionMatrix[ i + 3 * 4 ]; + } +} + +/* +========================== +R_TransformClipToWindow + +========================== +*/ +void R_TransformClipToWindow( const vec4_t clip, const viewParms_t *view, vec4_t normalized, vec4_t window ) { + normalized[0] = clip[0] / clip[3]; + normalized[1] = clip[1] / clip[3]; + normalized[2] = ( clip[2] + clip[3] ) / ( 2 * clip[3] ); + + window[0] = 0.5f * ( 1.0f + normalized[0] ) * view->viewportWidth; + window[1] = 0.5f * ( 1.0f + normalized[1] ) * view->viewportHeight; + window[2] = normalized[2]; + + window[0] = (int) ( window[0] + 0.5 ); + window[1] = (int) ( window[1] + 0.5 ); +} + + +/* +========================== +myGlMultMatrix + +========================== +*/ +void myGlMultMatrix( const float *a, const float *b, float *out ) { + int i, j; + + for ( i = 0 ; i < 4 ; i++ ) { + for ( j = 0 ; j < 4 ; j++ ) { + out[ i * 4 + j ] = + a [ i * 4 + 0 ] * b [ 0 * 4 + j ] + + a [ i * 4 + 1 ] * b [ 1 * 4 + j ] + + a [ i * 4 + 2 ] * b [ 2 * 4 + j ] + + a [ i * 4 + 3 ] * b [ 3 * 4 + j ]; + } + } +} + +/* +================= +R_RotateForEntity + +Generates an orientation for an entity and viewParms +Does NOT produce any GL calls +Called by both the front end and the back end +================= +*/ +void R_RotateForEntity( const trRefEntity_t *ent, const viewParms_t *viewParms, + orientationr_t *or ) { + float glMatrix[16]; + vec3_t delta; + float axisLength; + + if ( ent->e.reType != RT_MODEL ) { + *or = viewParms->world; + return; + } + + VectorCopy( ent->e.origin, or->origin ); + + VectorCopy( ent->e.axis[0], or->axis[0] ); + VectorCopy( ent->e.axis[1], or->axis[1] ); + VectorCopy( ent->e.axis[2], or->axis[2] ); + + glMatrix[0] = or->axis[0][0]; + glMatrix[4] = or->axis[1][0]; + glMatrix[8] = or->axis[2][0]; + glMatrix[12] = or->origin[0]; + + glMatrix[1] = or->axis[0][1]; + glMatrix[5] = or->axis[1][1]; + glMatrix[9] = or->axis[2][1]; + glMatrix[13] = or->origin[1]; + + glMatrix[2] = or->axis[0][2]; + glMatrix[6] = or->axis[1][2]; + glMatrix[10] = or->axis[2][2]; + glMatrix[14] = or->origin[2]; + + glMatrix[3] = 0; + glMatrix[7] = 0; + glMatrix[11] = 0; + glMatrix[15] = 1; + + Matrix16Copy(glMatrix, or->transformMatrix); + myGlMultMatrix( glMatrix, viewParms->world.modelMatrix, or->modelMatrix ); + + // calculate the viewer origin in the model's space + // needed for fog, specular, and environment mapping + VectorSubtract( viewParms->or.origin, or->origin, delta ); + + // compensate for scale in the axes if necessary + if ( ent->e.nonNormalizedAxes ) { + axisLength = VectorLength( ent->e.axis[0] ); + if ( !axisLength ) { + axisLength = 0; + } else { + axisLength = 1.0f / axisLength; + } + } else { + axisLength = 1.0f; + } + + or->viewOrigin[0] = DotProduct( delta, or->axis[0] ) * axisLength; + or->viewOrigin[1] = DotProduct( delta, or->axis[1] ) * axisLength; + or->viewOrigin[2] = DotProduct( delta, or->axis[2] ) * axisLength; +} + +/* +================= +R_RotateForViewer + +Sets up the modelview matrix for a given viewParm +================= +*/ +void R_RotateForViewer (void) +{ + float viewerMatrix[16]; + vec3_t origin; + + Com_Memset (&tr.or, 0, sizeof(tr.or)); + tr.or.axis[0][0] = 1; + tr.or.axis[1][1] = 1; + tr.or.axis[2][2] = 1; + VectorCopy (tr.viewParms.or.origin, tr.or.viewOrigin); + + // transform by the camera placement + VectorCopy( tr.viewParms.or.origin, origin ); + + viewerMatrix[0] = tr.viewParms.or.axis[0][0]; + viewerMatrix[4] = tr.viewParms.or.axis[0][1]; + viewerMatrix[8] = tr.viewParms.or.axis[0][2]; + viewerMatrix[12] = -origin[0] * viewerMatrix[0] + -origin[1] * viewerMatrix[4] + -origin[2] * viewerMatrix[8]; + + viewerMatrix[1] = tr.viewParms.or.axis[1][0]; + viewerMatrix[5] = tr.viewParms.or.axis[1][1]; + viewerMatrix[9] = tr.viewParms.or.axis[1][2]; + viewerMatrix[13] = -origin[0] * viewerMatrix[1] + -origin[1] * viewerMatrix[5] + -origin[2] * viewerMatrix[9]; + + viewerMatrix[2] = tr.viewParms.or.axis[2][0]; + viewerMatrix[6] = tr.viewParms.or.axis[2][1]; + viewerMatrix[10] = tr.viewParms.or.axis[2][2]; + viewerMatrix[14] = -origin[0] * viewerMatrix[2] + -origin[1] * viewerMatrix[6] + -origin[2] * viewerMatrix[10]; + + viewerMatrix[3] = 0; + viewerMatrix[7] = 0; + viewerMatrix[11] = 0; + viewerMatrix[15] = 1; + + // convert from our coordinate system (looking down X) + // to OpenGL's coordinate system (looking down -Z) + myGlMultMatrix( viewerMatrix, s_flipMatrix, tr.or.modelMatrix ); + + tr.viewParms.world = tr.or; + +} + +/* +** SetFarClip +*/ +static void R_SetFarClip( void ) +{ + float farthestCornerDistance = 0; + int i; + + // if not rendering the world (icons, menus, etc) + // set a 2k far clip plane + if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { + tr.viewParms.zFar = 2048; + return; + } + + // + // set far clipping planes dynamically + // + farthestCornerDistance = 0; + for ( i = 0; i < 8; i++ ) + { + vec3_t v; + vec3_t vecTo; + float distance; + + if ( i & 1 ) + { + v[0] = tr.viewParms.visBounds[0][0]; + } + else + { + v[0] = tr.viewParms.visBounds[1][0]; + } + + if ( i & 2 ) + { + v[1] = tr.viewParms.visBounds[0][1]; + } + else + { + v[1] = tr.viewParms.visBounds[1][1]; + } + + if ( i & 4 ) + { + v[2] = tr.viewParms.visBounds[0][2]; + } + else + { + v[2] = tr.viewParms.visBounds[1][2]; + } + + VectorSubtract( v, tr.viewParms.or.origin, vecTo ); + + distance = vecTo[0] * vecTo[0] + vecTo[1] * vecTo[1] + vecTo[2] * vecTo[2]; + + if ( distance > farthestCornerDistance ) + { + farthestCornerDistance = distance; + } + } + tr.viewParms.zFar = sqrt( farthestCornerDistance ); +} + +/* +================= +R_SetupFrustum + +Set up the culling frustum planes for the current view using the results we got from computing the first two rows of +the projection matrix. +================= +*/ +void R_SetupFrustum (viewParms_t *dest, float xmin, float xmax, float ymax, float zProj, float zFar, float stereoSep) +{ + vec3_t ofsorigin; + float oppleg, adjleg, length; + int i; + + if(stereoSep == 0 && xmin == -xmax) + { + // symmetric case can be simplified + VectorCopy(dest->or.origin, ofsorigin); + + length = sqrt(xmax * xmax + zProj * zProj); + oppleg = xmax / length; + adjleg = zProj / length; + + VectorScale(dest->or.axis[0], oppleg, dest->frustum[0].normal); + VectorMA(dest->frustum[0].normal, adjleg, dest->or.axis[1], dest->frustum[0].normal); + + VectorScale(dest->or.axis[0], oppleg, dest->frustum[1].normal); + VectorMA(dest->frustum[1].normal, -adjleg, dest->or.axis[1], dest->frustum[1].normal); + } + else + { + // In stereo rendering, due to the modification of the projection matrix, dest->or.origin is not the + // actual origin that we're rendering so offset the tip of the view pyramid. + VectorMA(dest->or.origin, stereoSep, dest->or.axis[1], ofsorigin); + + oppleg = xmax + stereoSep; + length = sqrt(oppleg * oppleg + zProj * zProj); + VectorScale(dest->or.axis[0], oppleg / length, dest->frustum[0].normal); + VectorMA(dest->frustum[0].normal, zProj / length, dest->or.axis[1], dest->frustum[0].normal); + + oppleg = xmin + stereoSep; + length = sqrt(oppleg * oppleg + zProj * zProj); + VectorScale(dest->or.axis[0], -oppleg / length, dest->frustum[1].normal); + VectorMA(dest->frustum[1].normal, -zProj / length, dest->or.axis[1], dest->frustum[1].normal); + } + + length = sqrt(ymax * ymax + zProj * zProj); + oppleg = ymax / length; + adjleg = zProj / length; + + VectorScale(dest->or.axis[0], oppleg, dest->frustum[2].normal); + VectorMA(dest->frustum[2].normal, adjleg, dest->or.axis[2], dest->frustum[2].normal); + + VectorScale(dest->or.axis[0], oppleg, dest->frustum[3].normal); + VectorMA(dest->frustum[3].normal, -adjleg, dest->or.axis[2], dest->frustum[3].normal); + + for (i=0 ; i<4 ; i++) { + dest->frustum[i].type = PLANE_NON_AXIAL; + dest->frustum[i].dist = DotProduct (ofsorigin, dest->frustum[i].normal); + SetPlaneSignbits( &dest->frustum[i] ); + } + + if (zFar != 0.0f) + { + vec3_t farpoint; + + VectorMA(ofsorigin, zFar, dest->or.axis[0], farpoint); + VectorScale(dest->or.axis[0], -1.0f, dest->frustum[4].normal); + + dest->frustum[4].type = PLANE_NON_AXIAL; + dest->frustum[4].dist = DotProduct (farpoint, dest->frustum[4].normal); + SetPlaneSignbits( &dest->frustum[4] ); + dest->flags |= VPF_FARPLANEFRUSTUM; + } +} + +/* +=============== +R_SetupProjection +=============== +*/ +void R_SetupProjection(viewParms_t *dest, float zProj, float zFar, qboolean computeFrustum) +{ + float xmin, xmax, ymin, ymax; + float width, height, stereoSep = r_stereoSeparation->value; + + /* + * offset the view origin of the viewer for stereo rendering + * by setting the projection matrix appropriately. + */ + + if(stereoSep != 0) + { + if(dest->stereoFrame == STEREO_LEFT) + stereoSep = zProj / stereoSep; + else if(dest->stereoFrame == STEREO_RIGHT) + stereoSep = zProj / -stereoSep; + else + stereoSep = 0; + } + + ymax = zProj * tan(dest->fovY * M_PI / 360.0f); + ymin = -ymax; + + xmax = zProj * tan(dest->fovX * M_PI / 360.0f); + xmin = -xmax; + + width = xmax - xmin; + height = ymax - ymin; + + dest->projectionMatrix[0] = 2 * zProj / width; + dest->projectionMatrix[4] = 0; + dest->projectionMatrix[8] = (xmax + xmin + 2 * stereoSep) / width; + dest->projectionMatrix[12] = 2 * zProj * stereoSep / width; + + dest->projectionMatrix[1] = 0; + dest->projectionMatrix[5] = 2 * zProj / height; + dest->projectionMatrix[9] = ( ymax + ymin ) / height; // normally 0 + dest->projectionMatrix[13] = 0; + + dest->projectionMatrix[3] = 0; + dest->projectionMatrix[7] = 0; + dest->projectionMatrix[11] = -1; + dest->projectionMatrix[15] = 0; + + // Now that we have all the data for the projection matrix we can also setup the view frustum. + if(computeFrustum) + R_SetupFrustum(dest, xmin, xmax, ymax, zProj, zFar, stereoSep); +} + +/* +=============== +R_SetupProjectionZ + +Sets the z-component transformation part in the projection matrix +=============== +*/ +void R_SetupProjectionZ(viewParms_t *dest) +{ + float zNear, zFar, depth; + + zNear = r_znear->value; + zFar = dest->zFar; + + depth = zFar - zNear; + + dest->projectionMatrix[2] = 0; + dest->projectionMatrix[6] = 0; + dest->projectionMatrix[10] = -( zFar + zNear ) / depth; + dest->projectionMatrix[14] = -2 * zFar * zNear / depth; + + if (dest->isPortal) + { + float plane[4]; + float plane2[4]; + vec4_t q, c; + + // transform portal plane into camera space + plane[0] = dest->portalPlane.normal[0]; + plane[1] = dest->portalPlane.normal[1]; + plane[2] = dest->portalPlane.normal[2]; + plane[3] = dest->portalPlane.dist; + + plane2[0] = -DotProduct (dest->or.axis[1], plane); + plane2[1] = DotProduct (dest->or.axis[2], plane); + plane2[2] = -DotProduct (dest->or.axis[0], plane); + plane2[3] = DotProduct (plane, dest->or.origin) - plane[3]; + + // Lengyel, Eric. "Modifying the Projection Matrix to Perform Oblique Near-plane Clipping". + // Terathon Software 3D Graphics Library, 2004. http://www.terathon.com/code/oblique.html + q[0] = (SGN(plane2[0]) + dest->projectionMatrix[8]) / dest->projectionMatrix[0]; + q[1] = (SGN(plane2[1]) + dest->projectionMatrix[9]) / dest->projectionMatrix[5]; + q[2] = -1.0f; + q[3] = (1.0f + dest->projectionMatrix[10]) / dest->projectionMatrix[14]; + + VectorScale4(plane2, 2.0f / DotProduct4(plane2, q), c); + + dest->projectionMatrix[2] = c[0]; + dest->projectionMatrix[6] = c[1]; + dest->projectionMatrix[10] = c[2] + 1.0f; + dest->projectionMatrix[14] = c[3]; + + } + +} + +/* +=============== +R_SetupProjectionOrtho +=============== +*/ +void R_SetupProjectionOrtho(viewParms_t *dest, vec3_t viewBounds[2]) +{ + float xmin, xmax, ymin, ymax, znear, zfar; + //viewParms_t *dest = &tr.viewParms; + int i; + vec3_t pop; + + // Quake3: Projection: + // + // Z X Y Z + // | / | / + // |/ |/ + // Y--+ +--X + + xmin = viewBounds[0][1]; + xmax = viewBounds[1][1]; + ymin = -viewBounds[1][2]; + ymax = -viewBounds[0][2]; + znear = viewBounds[0][0]; + zfar = viewBounds[1][0]; + + dest->projectionMatrix[0] = 2 / (xmax - xmin); + dest->projectionMatrix[4] = 0; + dest->projectionMatrix[8] = 0; + dest->projectionMatrix[12] = (xmax + xmin) / (xmax - xmin); + + dest->projectionMatrix[1] = 0; + dest->projectionMatrix[5] = 2 / (ymax - ymin); + dest->projectionMatrix[9] = 0; + dest->projectionMatrix[13] = (ymax + ymin) / (ymax - ymin); + + dest->projectionMatrix[2] = 0; + dest->projectionMatrix[6] = 0; + dest->projectionMatrix[10] = -2 / (zfar - znear); + dest->projectionMatrix[14] = -(zfar + znear) / (zfar - znear); + + dest->projectionMatrix[3] = 0; + dest->projectionMatrix[7] = 0; + dest->projectionMatrix[11] = 0; + dest->projectionMatrix[15] = 1; + + VectorScale(dest->or.axis[1], 1.0f, dest->frustum[0].normal); + VectorMA(dest->or.origin, viewBounds[0][1], dest->frustum[0].normal, pop); + dest->frustum[0].dist = DotProduct(pop, dest->frustum[0].normal); + + VectorScale(dest->or.axis[1], -1.0f, dest->frustum[1].normal); + VectorMA(dest->or.origin, -viewBounds[1][1], dest->frustum[1].normal, pop); + dest->frustum[1].dist = DotProduct(pop, dest->frustum[1].normal); + + VectorScale(dest->or.axis[2], 1.0f, dest->frustum[2].normal); + VectorMA(dest->or.origin, viewBounds[0][2], dest->frustum[2].normal, pop); + dest->frustum[2].dist = DotProduct(pop, dest->frustum[2].normal); + + VectorScale(dest->or.axis[2], -1.0f, dest->frustum[3].normal); + VectorMA(dest->or.origin, -viewBounds[1][2], dest->frustum[3].normal, pop); + dest->frustum[3].dist = DotProduct(pop, dest->frustum[3].normal); + + VectorScale(dest->or.axis[0], -1.0f, dest->frustum[4].normal); + VectorMA(dest->or.origin, -viewBounds[1][0], dest->frustum[4].normal, pop); + dest->frustum[4].dist = DotProduct(pop, dest->frustum[4].normal); + + for (i = 0; i < 5; i++) + { + dest->frustum[i].type = PLANE_NON_AXIAL; + SetPlaneSignbits (&dest->frustum[i]); + } + + dest->flags |= VPF_FARPLANEFRUSTUM; +} + +/* +================= +R_MirrorPoint +================= +*/ +void R_MirrorPoint (vec3_t in, orientation_t *surface, orientation_t *camera, vec3_t out) { + int i; + vec3_t local; + vec3_t transformed; + float d; + + VectorSubtract( in, surface->origin, local ); + + VectorClear( transformed ); + for ( i = 0 ; i < 3 ; i++ ) { + d = DotProduct(local, surface->axis[i]); + VectorMA( transformed, d, camera->axis[i], transformed ); + } + + VectorAdd( transformed, camera->origin, out ); +} + +void R_MirrorVector (vec3_t in, orientation_t *surface, orientation_t *camera, vec3_t out) { + int i; + float d; + + VectorClear( out ); + for ( i = 0 ; i < 3 ; i++ ) { + d = DotProduct(in, surface->axis[i]); + VectorMA( out, d, camera->axis[i], out ); + } +} + + +/* +============= +R_PlaneForSurface +============= +*/ +void R_PlaneForSurface (surfaceType_t *surfType, cplane_t *plane) { + srfTriangles_t *tri; + srfPoly_t *poly; + srfVert_t *v1, *v2, *v3; + vec4_t plane4; + + if (!surfType) { + Com_Memset (plane, 0, sizeof(*plane)); + plane->normal[0] = 1; + return; + } + switch (*surfType) { + case SF_FACE: + *plane = ((srfSurfaceFace_t *)surfType)->plane; + return; + case SF_TRIANGLES: + tri = (srfTriangles_t *)surfType; + v1 = tri->verts + tri->triangles[0].indexes[0]; + v2 = tri->verts + tri->triangles[0].indexes[1]; + v3 = tri->verts + tri->triangles[0].indexes[2]; + PlaneFromPoints( plane4, v1->xyz, v2->xyz, v3->xyz ); + VectorCopy( plane4, plane->normal ); + plane->dist = plane4[3]; + return; + case SF_POLY: + poly = (srfPoly_t *)surfType; + PlaneFromPoints( plane4, poly->verts[0].xyz, poly->verts[1].xyz, poly->verts[2].xyz ); + VectorCopy( plane4, plane->normal ); + plane->dist = plane4[3]; + return; + default: + Com_Memset (plane, 0, sizeof(*plane)); + plane->normal[0] = 1; + return; + } +} + +/* +================= +R_GetPortalOrientation + +entityNum is the entity that the portal surface is a part of, which may +be moving and rotating. + +Returns qtrue if it should be mirrored +================= +*/ +qboolean R_GetPortalOrientations( drawSurf_t *drawSurf, int entityNum, + orientation_t *surface, orientation_t *camera, + vec3_t pvsOrigin, qboolean *mirror ) { + int i; + cplane_t originalPlane, plane; + trRefEntity_t *e; + float d; + vec3_t transformed; + + // create plane axis for the portal we are seeing + R_PlaneForSurface( drawSurf->surface, &originalPlane ); + + // rotate the plane if necessary + if ( entityNum != REFENTITYNUM_WORLD ) { + tr.currentEntityNum = entityNum; + tr.currentEntity = &tr.refdef.entities[entityNum]; + + // get the orientation of the entity + R_RotateForEntity( tr.currentEntity, &tr.viewParms, &tr.or ); + + // rotate the plane, but keep the non-rotated version for matching + // against the portalSurface entities + R_LocalNormalToWorld( originalPlane.normal, plane.normal ); + plane.dist = originalPlane.dist + DotProduct( plane.normal, tr.or.origin ); + + // translate the original plane + originalPlane.dist = originalPlane.dist + DotProduct( originalPlane.normal, tr.or.origin ); + } else { + plane = originalPlane; + } + + VectorCopy( plane.normal, surface->axis[0] ); + PerpendicularVector( surface->axis[1], surface->axis[0] ); + CrossProduct( surface->axis[0], surface->axis[1], surface->axis[2] ); + + // locate the portal entity closest to this plane. + // origin will be the origin of the portal, origin2 will be + // the origin of the camera + for ( i = 0 ; i < tr.refdef.num_entities ; i++ ) { + e = &tr.refdef.entities[i]; + if ( e->e.reType != RT_PORTALSURFACE ) { + continue; + } + + d = DotProduct( e->e.origin, originalPlane.normal ) - originalPlane.dist; + if ( d > 64 || d < -64) { + continue; + } + + // get the pvsOrigin from the entity + VectorCopy( e->e.oldorigin, pvsOrigin ); + + // if the entity is just a mirror, don't use as a camera point + if ( e->e.oldorigin[0] == e->e.origin[0] && + e->e.oldorigin[1] == e->e.origin[1] && + e->e.oldorigin[2] == e->e.origin[2] ) { + VectorScale( plane.normal, plane.dist, surface->origin ); + VectorCopy( surface->origin, camera->origin ); + VectorSubtract( vec3_origin, surface->axis[0], camera->axis[0] ); + VectorCopy( surface->axis[1], camera->axis[1] ); + VectorCopy( surface->axis[2], camera->axis[2] ); + + *mirror = qtrue; + return qtrue; + } + + // project the origin onto the surface plane to get + // an origin point we can rotate around + d = DotProduct( e->e.origin, plane.normal ) - plane.dist; + VectorMA( e->e.origin, -d, surface->axis[0], surface->origin ); + + // now get the camera origin and orientation + VectorCopy( e->e.oldorigin, camera->origin ); + AxisCopy( e->e.axis, camera->axis ); + VectorSubtract( vec3_origin, camera->axis[0], camera->axis[0] ); + VectorSubtract( vec3_origin, camera->axis[1], camera->axis[1] ); + + // optionally rotate + if ( e->e.oldframe ) { + // if a speed is specified + if ( e->e.frame ) { + // continuous rotate + d = (tr.refdef.time/1000.0f) * e->e.frame; + VectorCopy( camera->axis[1], transformed ); + RotatePointAroundVector( camera->axis[1], camera->axis[0], transformed, d ); + CrossProduct( camera->axis[0], camera->axis[1], camera->axis[2] ); + } else { + // bobbing rotate, with skinNum being the rotation offset + d = sin( tr.refdef.time * 0.003f ); + d = e->e.skinNum + d * 4; + VectorCopy( camera->axis[1], transformed ); + RotatePointAroundVector( camera->axis[1], camera->axis[0], transformed, d ); + CrossProduct( camera->axis[0], camera->axis[1], camera->axis[2] ); + } + } + else if ( e->e.skinNum ) { + d = e->e.skinNum; + VectorCopy( camera->axis[1], transformed ); + RotatePointAroundVector( camera->axis[1], camera->axis[0], transformed, d ); + CrossProduct( camera->axis[0], camera->axis[1], camera->axis[2] ); + } + *mirror = qfalse; + return qtrue; + } + + // if we didn't locate a portal entity, don't render anything. + // We don't want to just treat it as a mirror, because without a + // portal entity the server won't have communicated a proper entity set + // in the snapshot + + // unfortunately, with local movement prediction it is easily possible + // to see a surface before the server has communicated the matching + // portal surface entity, so we don't want to print anything here... + + //ri.Printf( PRINT_ALL, "Portal surface without a portal entity\n" ); + + return qfalse; +} + +static qboolean IsMirror( const drawSurf_t *drawSurf, int entityNum ) +{ + int i; + cplane_t originalPlane, plane; + trRefEntity_t *e; + float d; + + // create plane axis for the portal we are seeing + R_PlaneForSurface( drawSurf->surface, &originalPlane ); + + // rotate the plane if necessary + if ( entityNum != REFENTITYNUM_WORLD ) + { + tr.currentEntityNum = entityNum; + tr.currentEntity = &tr.refdef.entities[entityNum]; + + // get the orientation of the entity + R_RotateForEntity( tr.currentEntity, &tr.viewParms, &tr.or ); + + // rotate the plane, but keep the non-rotated version for matching + // against the portalSurface entities + R_LocalNormalToWorld( originalPlane.normal, plane.normal ); + plane.dist = originalPlane.dist + DotProduct( plane.normal, tr.or.origin ); + + // translate the original plane + originalPlane.dist = originalPlane.dist + DotProduct( originalPlane.normal, tr.or.origin ); + } + else + { + plane = originalPlane; + } + + // locate the portal entity closest to this plane. + // origin will be the origin of the portal, origin2 will be + // the origin of the camera + for ( i = 0 ; i < tr.refdef.num_entities ; i++ ) + { + e = &tr.refdef.entities[i]; + if ( e->e.reType != RT_PORTALSURFACE ) { + continue; + } + + d = DotProduct( e->e.origin, originalPlane.normal ) - originalPlane.dist; + if ( d > 64 || d < -64) { + continue; + } + + // if the entity is just a mirror, don't use as a camera point + if ( e->e.oldorigin[0] == e->e.origin[0] && + e->e.oldorigin[1] == e->e.origin[1] && + e->e.oldorigin[2] == e->e.origin[2] ) + { + return qtrue; + } + + return qfalse; + } + return qfalse; +} + +/* +** SurfIsOffscreen +** +** Determines if a surface is completely offscreen. +*/ +static qboolean SurfIsOffscreen( const drawSurf_t *drawSurf, vec4_t clipDest[128] ) { + float shortest = 100000000; + int entityNum; + int numTriangles; + shader_t *shader; + int fogNum; + int dlighted; + int pshadowed; + vec4_t clip, eye; + int i; + unsigned int pointOr = 0; + unsigned int pointAnd = (unsigned int)~0; + + R_RotateForViewer(); + + R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlighted, &pshadowed ); + RB_BeginSurface( shader, fogNum ); + rb_surfaceTable[ *drawSurf->surface ]( drawSurf->surface ); + + assert( tess.numVertexes < 128 ); + + for ( i = 0; i < tess.numVertexes; i++ ) + { + int j; + unsigned int pointFlags = 0; + + R_TransformModelToClip( tess.xyz[i], tr.or.modelMatrix, tr.viewParms.projectionMatrix, eye, clip ); + + for ( j = 0; j < 3; j++ ) + { + if ( clip[j] >= clip[3] ) + { + pointFlags |= (1 << (j*2)); + } + else if ( clip[j] <= -clip[3] ) + { + pointFlags |= ( 1 << (j*2+1)); + } + } + pointAnd &= pointFlags; + pointOr |= pointFlags; + } + + // trivially reject + if ( pointAnd ) + { + return qtrue; + } + + // determine if this surface is backfaced and also determine the distance + // to the nearest vertex so we can cull based on portal range. Culling + // based on vertex distance isn't 100% correct (we should be checking for + // range to the surface), but it's good enough for the types of portals + // we have in the game right now. + numTriangles = tess.numIndexes / 3; + + for ( i = 0; i < tess.numIndexes; i += 3 ) + { + vec3_t normal; + float len; + + VectorSubtract( tess.xyz[tess.indexes[i]], tr.viewParms.or.origin, normal ); + + len = VectorLengthSquared( normal ); // lose the sqrt + if ( len < shortest ) + { + shortest = len; + } + + if ( DotProduct( normal, tess.normal[tess.indexes[i]] ) >= 0 ) + { + numTriangles--; + } + } + if ( !numTriangles ) + { + return qtrue; + } + + // mirrors can early out at this point, since we don't do a fade over distance + // with them (although we could) + if ( IsMirror( drawSurf, entityNum ) ) + { + return qfalse; + } + + if ( shortest > (tess.shader->portalRange*tess.shader->portalRange) ) + { + return qtrue; + } + + return qfalse; +} + +/* +======================== +R_MirrorViewBySurface + +Returns qtrue if another view has been rendered +======================== +*/ +qboolean R_MirrorViewBySurface (drawSurf_t *drawSurf, int entityNum) { + vec4_t clipDest[128]; + viewParms_t newParms; + viewParms_t oldParms; + orientation_t surface, camera; + + // don't recursively mirror + if (tr.viewParms.isPortal) { + ri.Printf( PRINT_DEVELOPER, "WARNING: recursive mirror/portal found\n" ); + return qfalse; + } + + if ( r_noportals->integer || (r_fastsky->integer == 1) ) { + return qfalse; + } + + // trivially reject portal/mirror + if ( SurfIsOffscreen( drawSurf, clipDest ) ) { + return qfalse; + } + + // save old viewParms so we can return to it after the mirror view + oldParms = tr.viewParms; + + newParms = tr.viewParms; + newParms.isPortal = qtrue; + newParms.zFar = 0.0f; + newParms.flags &= ~VPF_FARPLANEFRUSTUM; + if ( !R_GetPortalOrientations( drawSurf, entityNum, &surface, &camera, + newParms.pvsOrigin, &newParms.isMirror ) ) { + return qfalse; // bad portal, no portalentity + } + + R_MirrorPoint (oldParms.or.origin, &surface, &camera, newParms.or.origin ); + + VectorSubtract( vec3_origin, camera.axis[0], newParms.portalPlane.normal ); + newParms.portalPlane.dist = DotProduct( camera.origin, newParms.portalPlane.normal ); + + R_MirrorVector (oldParms.or.axis[0], &surface, &camera, newParms.or.axis[0]); + R_MirrorVector (oldParms.or.axis[1], &surface, &camera, newParms.or.axis[1]); + R_MirrorVector (oldParms.or.axis[2], &surface, &camera, newParms.or.axis[2]); + + // OPTIMIZE: restrict the viewport on the mirrored view + + // render the mirror view + R_RenderView (&newParms); + + tr.viewParms = oldParms; + + return qtrue; +} + +/* +================= +R_SpriteFogNum + +See if a sprite is inside a fog volume +================= +*/ +int R_SpriteFogNum( trRefEntity_t *ent ) { + int i, j; + fog_t *fog; + + if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { + return 0; + } + + for ( i = 1 ; i < tr.world->numfogs ; i++ ) { + fog = &tr.world->fogs[i]; + for ( j = 0 ; j < 3 ; j++ ) { + if ( ent->e.origin[j] - ent->e.radius >= fog->bounds[1][j] ) { + break; + } + if ( ent->e.origin[j] + ent->e.radius <= fog->bounds[0][j] ) { + break; + } + } + if ( j == 3 ) { + return i; + } + } + + return 0; +} + +/* +========================================================================================== + +DRAWSURF SORTING + +========================================================================================== +*/ + +/* +=============== +R_Radix +=============== +*/ +static ID_INLINE void R_Radix( int byte, int size, drawSurf_t *source, drawSurf_t *dest ) +{ + int count[ 256 ] = { 0 }; + int index[ 256 ]; + int i; + unsigned char *sortKey = NULL; + unsigned char *end = NULL; + + sortKey = ( (unsigned char *)&source[ 0 ].sort ) + byte; + end = sortKey + ( size * sizeof( drawSurf_t ) ); + for( ; sortKey < end; sortKey += sizeof( drawSurf_t ) ) + ++count[ *sortKey ]; + + index[ 0 ] = 0; + + for( i = 1; i < 256; ++i ) + index[ i ] = index[ i - 1 ] + count[ i - 1 ]; + + sortKey = ( (unsigned char *)&source[ 0 ].sort ) + byte; + for( i = 0; i < size; ++i, sortKey += sizeof( drawSurf_t ) ) + dest[ index[ *sortKey ]++ ] = source[ i ]; +} + +/* +=============== +R_RadixSort + +Radix sort with 4 byte size buckets +=============== +*/ +static void R_RadixSort( drawSurf_t *source, int size ) +{ + static drawSurf_t scratch[ MAX_DRAWSURFS ]; +#ifdef Q3_LITTLE_ENDIAN + R_Radix( 0, size, source, scratch ); + R_Radix( 1, size, scratch, source ); + R_Radix( 2, size, source, scratch ); + R_Radix( 3, size, scratch, source ); +#else + R_Radix( 3, size, source, scratch ); + R_Radix( 2, size, scratch, source ); + R_Radix( 1, size, source, scratch ); + R_Radix( 0, size, scratch, source ); +#endif //Q3_LITTLE_ENDIAN +} + +//========================================================================================== + +/* +================= +R_AddDrawSurf +================= +*/ +void R_AddDrawSurf( surfaceType_t *surface, shader_t *shader, + int fogIndex, int dlightMap, int pshadowMap ) { + int index; + + // instead of checking for overflow, we just mask the index + // so it wraps around + index = tr.refdef.numDrawSurfs & DRAWSURF_MASK; + // the sort data is packed into a single 32 bit value so it can be + // compared quickly during the qsorting process + tr.refdef.drawSurfs[index].sort = (shader->sortedIndex << QSORT_SHADERNUM_SHIFT) + | tr.shiftedEntityNum | ( fogIndex << QSORT_FOGNUM_SHIFT ) + | ((int)pshadowMap << QSORT_PSHADOW_SHIFT) | (int)dlightMap; + tr.refdef.drawSurfs[index].surface = surface; + tr.refdef.numDrawSurfs++; +} + +/* +================= +R_DecomposeSort +================= +*/ +void R_DecomposeSort( unsigned sort, int *entityNum, shader_t **shader, + int *fogNum, int *dlightMap, int *pshadowMap ) { + *fogNum = ( sort >> QSORT_FOGNUM_SHIFT ) & 31; + *shader = tr.sortedShaders[ ( sort >> QSORT_SHADERNUM_SHIFT ) & (MAX_SHADERS-1) ]; + *entityNum = ( sort >> QSORT_REFENTITYNUM_SHIFT ) & REFENTITYNUM_MASK; + *pshadowMap = (sort >> QSORT_PSHADOW_SHIFT ) & 1; + *dlightMap = sort & 1; +} + +/* +================= +R_SortDrawSurfs +================= +*/ +void R_SortDrawSurfs( drawSurf_t *drawSurfs, int numDrawSurfs ) { + shader_t *shader; + int fogNum; + int entityNum; + int dlighted; + int pshadowed; + int i; + + //ri.Printf(PRINT_ALL, "firstDrawSurf %d numDrawSurfs %d\n", (int)(drawSurfs - tr.refdef.drawSurfs), numDrawSurfs); + + // it is possible for some views to not have any surfaces + if ( numDrawSurfs < 1 ) { + // we still need to add it for hyperspace cases + R_AddDrawSurfCmd( drawSurfs, numDrawSurfs ); + return; + } + + // if we overflowed MAX_DRAWSURFS, the drawsurfs + // wrapped around in the buffer and we will be missing + // the first surfaces, not the last ones + if ( numDrawSurfs > MAX_DRAWSURFS ) { + numDrawSurfs = MAX_DRAWSURFS; + } + + // sort the drawsurfs by sort type, then orientation, then shader + R_RadixSort( drawSurfs, numDrawSurfs ); + + // skip pass through drawing if rendering a shadow map + if (tr.viewParms.flags & (VPF_SHADOWMAP | VPF_DEPTHSHADOW)) + { + R_AddDrawSurfCmd( drawSurfs, numDrawSurfs ); + return; + } + + // check for any pass through drawing, which + // may cause another view to be rendered first + for ( i = 0 ; i < numDrawSurfs ; i++ ) { + R_DecomposeSort( (drawSurfs+i)->sort, &entityNum, &shader, &fogNum, &dlighted, &pshadowed ); + + if ( shader->sort > SS_PORTAL ) { + break; + } + + // no shader should ever have this sort type + if ( shader->sort == SS_BAD ) { + ri.Error (ERR_DROP, "Shader '%s'with sort == SS_BAD", shader->name ); + } + + // if the mirror was completely clipped away, we may need to check another surface + if ( R_MirrorViewBySurface( (drawSurfs+i), entityNum) ) { + // this is a debug option to see exactly what is being mirrored + if ( r_portalOnly->integer ) { + return; + } + break; // only one mirror view at a time + } + } + + R_AddDrawSurfCmd( drawSurfs, numDrawSurfs ); +} + +static void R_AddEntitySurface (int entityNum) +{ + trRefEntity_t *ent; + shader_t *shader; + + tr.currentEntityNum = entityNum; + + ent = tr.currentEntity = &tr.refdef.entities[tr.currentEntityNum]; + + ent->needDlights = qfalse; + + // preshift the value we are going to OR into the drawsurf sort + tr.shiftedEntityNum = tr.currentEntityNum << QSORT_REFENTITYNUM_SHIFT; + + // + // the weapon model must be handled special -- + // we don't want the hacked weapon position showing in + // mirrors, because the true body position will already be drawn + // + if ( (ent->e.renderfx & RF_FIRST_PERSON) && (tr.viewParms.isPortal + || (tr.viewParms.flags & (VPF_SHADOWMAP | VPF_DEPTHSHADOW))) ) { + return; + } + + // simple generated models, like sprites and beams, are not culled + switch ( ent->e.reType ) { + case RT_PORTALSURFACE: + break; // don't draw anything + case RT_SPRITE: + case RT_BEAM: + case RT_LIGHTNING: + case RT_RAIL_CORE: + case RT_RAIL_RINGS: + // self blood sprites, talk balloons, etc should not be drawn in the primary + // view. We can't just do this check for all entities, because md3 + // entities may still want to cast shadows from them + if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal) { + return; + } + shader = R_GetShaderByHandle( ent->e.customShader ); + R_AddDrawSurf( &entitySurface, shader, R_SpriteFogNum( ent ), 0, 0 ); + break; + + case RT_MODEL: + // we must set up parts of tr.or for model culling + R_RotateForEntity( ent, &tr.viewParms, &tr.or ); + + tr.currentModel = R_GetModelByHandle( ent->e.hModel ); + if (!tr.currentModel) { + R_AddDrawSurf( &entitySurface, tr.defaultShader, 0, 0, 0 ); + } else { + switch ( tr.currentModel->type ) { + case MOD_MESH: + R_AddMD3Surfaces( ent ); + break; + case MOD_MD4: + R_AddAnimSurfaces( ent ); + break; +#ifdef RAVENMD4 + case MOD_MDR: + R_MDRAddAnimSurfaces( ent ); + break; +#endif + case MOD_IQM: + R_AddIQMSurfaces( ent ); + break; + case MOD_BRUSH: + R_AddBrushModelSurfaces( ent ); + break; + case MOD_BAD: // null model axis + if ( (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal) { + break; + } + R_AddDrawSurf( &entitySurface, tr.defaultShader, 0, 0, 0 ); + break; + default: + ri.Error( ERR_DROP, "R_AddEntitySurfaces: Bad modeltype" ); + break; + } + } + break; + default: + ri.Error( ERR_DROP, "R_AddEntitySurfaces: Bad reType" ); + } +} + +/* +============= +R_AddEntitySurfaces +============= +*/ +void R_AddEntitySurfaces (void) { + int i; + + if ( !r_drawentities->integer ) { + return; + } + + for ( i = 0; i < tr.refdef.num_entities; i++) + R_AddEntitySurface(i); +} + + +/* +==================== +R_GenerateDrawSurfs +==================== +*/ +void R_GenerateDrawSurfs( void ) { + R_AddWorldSurfaces (); + + R_AddPolygonSurfaces(); + + // set the projection matrix with the minimum zfar + // now that we have the world bounded + // this needs to be done before entities are + // added, because they use the projection + // matrix for lod calculation + + // dynamically compute far clip plane distance + if (!(tr.viewParms.flags & VPF_SHADOWMAP)) + { + R_SetFarClip(); + } + + // we know the size of the clipping volume. Now set the rest of the projection matrix. + R_SetupProjectionZ (&tr.viewParms); + + R_AddEntitySurfaces (); +} + +/* +================ +R_DebugPolygon +================ +*/ +void R_DebugPolygon( int color, int numPoints, float *points ) { + // FIXME: implement this +#if 0 + int i; + + GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + + // draw solid shade + + qglColor3f( color&1, (color>>1)&1, (color>>2)&1 ); + qglBegin( GL_POLYGON ); + for ( i = 0 ; i < numPoints ; i++ ) { + qglVertex3fv( points + i * 3 ); + } + qglEnd(); + + // draw wireframe outline + GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + qglDepthRange( 0, 0 ); + qglColor3f( 1, 1, 1 ); + qglBegin( GL_POLYGON ); + for ( i = 0 ; i < numPoints ; i++ ) { + qglVertex3fv( points + i * 3 ); + } + qglEnd(); + qglDepthRange( 0, 1 ); +#endif +} + +/* +==================== +R_DebugGraphics + +Visualization aid for movement clipping debugging +==================== +*/ +void R_DebugGraphics( void ) { + if ( !r_debugSurface->integer ) { + return; + } + + R_IssuePendingRenderCommands(); + + GL_Bind( tr.whiteImage); + GL_Cull( CT_FRONT_SIDED ); + ri.CM_DrawDebugSurface( R_DebugPolygon ); +} + + +/* +================ +R_RenderView + +A view may be either the actual camera view, +or a mirror / remote location +================ +*/ +void R_RenderView (viewParms_t *parms) { + int firstDrawSurf; + + if ( parms->viewportWidth <= 0 || parms->viewportHeight <= 0 ) { + return; + } + + tr.viewCount++; + + tr.viewParms = *parms; + tr.viewParms.frameSceneNum = tr.frameSceneNum; + tr.viewParms.frameCount = tr.frameCount; + + firstDrawSurf = tr.refdef.numDrawSurfs; + + tr.viewCount++; + + // set viewParms.world + R_RotateForViewer (); + + R_SetupProjection(&tr.viewParms, r_zproj->value, tr.viewParms.zFar, qtrue); + + R_GenerateDrawSurfs(); + + R_SortDrawSurfs( tr.refdef.drawSurfs + firstDrawSurf, tr.refdef.numDrawSurfs - firstDrawSurf ); + + // draw main system development information (surface outlines, etc) + R_DebugGraphics(); +} + + +void R_RenderDlightCubemaps(const refdef_t *fd) +{ + int i; + + for (i = 0; i < tr.refdef.num_dlights; i++) + { + viewParms_t shadowParms; + int j; + + // use previous frame to determine visible dlights + if ((1 << i) & tr.refdef.dlightMask) + continue; + + Com_Memset( &shadowParms, 0, sizeof( shadowParms ) ); + + shadowParms.viewportX = tr.refdef.x; + shadowParms.viewportY = glConfig.vidHeight - ( tr.refdef.y + PSHADOW_MAP_SIZE ); + shadowParms.viewportWidth = PSHADOW_MAP_SIZE; + shadowParms.viewportHeight = PSHADOW_MAP_SIZE; + shadowParms.isPortal = qfalse; + shadowParms.isMirror = qtrue; // because it is + + shadowParms.fovX = 90; + shadowParms.fovY = 90; + + shadowParms.flags = VPF_SHADOWMAP | VPF_DEPTHSHADOW; + shadowParms.zFar = tr.refdef.dlights[i].radius; + + VectorCopy( tr.refdef.dlights[i].origin, shadowParms.or.origin ); + + for (j = 0; j < 6; j++) + { + switch(j) + { + case 0: + // -X + VectorSet( shadowParms.or.axis[0], -1, 0, 0); + VectorSet( shadowParms.or.axis[1], 0, 0, -1); + VectorSet( shadowParms.or.axis[2], 0, 1, 0); + break; + case 1: + // +X + VectorSet( shadowParms.or.axis[0], 1, 0, 0); + VectorSet( shadowParms.or.axis[1], 0, 0, 1); + VectorSet( shadowParms.or.axis[2], 0, 1, 0); + break; + case 2: + // -Y + VectorSet( shadowParms.or.axis[0], 0, -1, 0); + VectorSet( shadowParms.or.axis[1], 1, 0, 0); + VectorSet( shadowParms.or.axis[2], 0, 0, -1); + break; + case 3: + // +Y + VectorSet( shadowParms.or.axis[0], 0, 1, 0); + VectorSet( shadowParms.or.axis[1], 1, 0, 0); + VectorSet( shadowParms.or.axis[2], 0, 0, 1); + break; + case 4: + // -Z + VectorSet( shadowParms.or.axis[0], 0, 0, -1); + VectorSet( shadowParms.or.axis[1], 1, 0, 0); + VectorSet( shadowParms.or.axis[2], 0, 1, 0); + break; + case 5: + // +Z + VectorSet( shadowParms.or.axis[0], 0, 0, 1); + VectorSet( shadowParms.or.axis[1], -1, 0, 0); + VectorSet( shadowParms.or.axis[2], 0, 1, 0); + break; + } + + R_RenderView(&shadowParms); + R_AddCapShadowmapCmd( i, j ); + } + } +} + + +void R_RenderPshadowMaps(const refdef_t *fd) +{ + viewParms_t shadowParms; + int i; + + // first, make a list of shadows + for ( i = 0; i < tr.refdef.num_entities; i++) + { + trRefEntity_t *ent = &tr.refdef.entities[i]; + + if((ent->e.renderfx & (RF_FIRST_PERSON | RF_NOSHADOW))) + continue; + + //if((ent->e.renderfx & RF_THIRD_PERSON)) + //continue; + + if (ent->e.reType == RT_MODEL) + { + model_t *model = R_GetModelByHandle( ent->e.hModel ); + pshadow_t shadow; + float radius = 0.0f; + float scale = 1.0f; + vec3_t diff; + int j; + + if (!model) + continue; + + if (ent->e.nonNormalizedAxes) + { + scale = VectorLength( ent->e.axis[0] ); + } + + switch (model->type) + { + case MOD_MESH: + { + mdvFrame_t *frame = &model->mdv[0]->frames[ent->e.frame]; + + radius = frame->radius * scale; + } + break; + + case MOD_MD4: + { + // FIXME: actually calculate the radius and bounds, this is a horrible hack + radius = r_pshadowDist->value / 2.0f; + } + break; +#ifdef RAVENMD4 + case MOD_MDR: + { + // FIXME: never actually tested this + mdrHeader_t *header = model->modelData; + int frameSize = (size_t)( &((mdrFrame_t *)0)->bones[ header->numBones ] ); + mdrFrame_t *frame = ( mdrFrame_t * ) ( ( byte * ) header + header->ofsFrames + frameSize * ent->e.frame); + + radius = frame->radius; + } + break; +#endif + case MOD_IQM: + { + // FIXME: never actually tested this + iqmData_t *data = model->modelData; + vec3_t diag; + float *framebounds; + + framebounds = data->bounds + 6*ent->e.frame; + VectorSubtract( framebounds+3, framebounds, diag ); + radius = 0.5f * VectorLength( diag ); + } + break; + + default: + break; + } + + if (!radius) + continue; + + // Cull entities that are behind the viewer by more than lightRadius + VectorSubtract(ent->e.origin, fd->vieworg, diff); + if (DotProduct(diff, fd->viewaxis[0]) < -r_pshadowDist->value) + continue; + + memset(&shadow, 0, sizeof(shadow)); + + shadow.numEntities = 1; + shadow.entityNums[0] = i; + shadow.viewRadius = radius; + shadow.lightRadius = r_pshadowDist->value; + VectorCopy(ent->e.origin, shadow.viewOrigin); + shadow.sort = DotProduct(diff, diff) / (radius * radius); + VectorCopy(ent->e.origin, shadow.entityOrigins[0]); + shadow.entityRadiuses[0] = radius; + + for (j = 0; j < MAX_CALC_PSHADOWS; j++) + { + pshadow_t swap; + + if (j + 1 > tr.refdef.num_pshadows) + { + tr.refdef.num_pshadows = j + 1; + tr.refdef.pshadows[j] = shadow; + break; + } + + // sort shadows by distance from camera divided by radius + // FIXME: sort better + if (tr.refdef.pshadows[j].sort <= shadow.sort) + continue; + + swap = tr.refdef.pshadows[j]; + tr.refdef.pshadows[j] = shadow; + shadow = swap; + } + } + } + + // next, merge touching pshadows + for ( i = 0; i < tr.refdef.num_pshadows; i++) + { + pshadow_t *ps1 = &tr.refdef.pshadows[i]; + int j; + + for (j = i + 1; j < tr.refdef.num_pshadows; j++) + { + pshadow_t *ps2 = &tr.refdef.pshadows[j]; + int k; + qboolean touch; + + if (ps1->numEntities == 8) + break; + + touch = qfalse; + if (SpheresIntersect(ps1->viewOrigin, ps1->viewRadius, ps2->viewOrigin, ps2->viewRadius)) + { + for (k = 0; k < ps1->numEntities; k++) + { + if (SpheresIntersect(ps1->entityOrigins[k], ps1->entityRadiuses[k], ps2->viewOrigin, ps2->viewRadius)) + { + touch = qtrue; + break; + } + } + } + + if (touch) + { + vec3_t newOrigin; + float newRadius; + + BoundingSphereOfSpheres(ps1->viewOrigin, ps1->viewRadius, ps2->viewOrigin, ps2->viewRadius, newOrigin, &newRadius); + VectorCopy(newOrigin, ps1->viewOrigin); + ps1->viewRadius = newRadius; + + ps1->entityNums[ps1->numEntities] = ps2->entityNums[0]; + VectorCopy(ps2->viewOrigin, ps1->entityOrigins[ps1->numEntities]); + ps1->entityRadiuses[ps1->numEntities] = ps2->viewRadius; + + ps1->numEntities++; + + for (k = j; k < tr.refdef.num_pshadows - 1; k++) + { + tr.refdef.pshadows[k] = tr.refdef.pshadows[k + 1]; + } + + j--; + tr.refdef.num_pshadows--; + } + } + } + + // cap number of drawn pshadows + if (tr.refdef.num_pshadows > MAX_DRAWN_PSHADOWS) + { + tr.refdef.num_pshadows = MAX_DRAWN_PSHADOWS; + } + + // next, fill up the rest of the shadow info + for ( i = 0; i < tr.refdef.num_pshadows; i++) + { + pshadow_t *shadow = &tr.refdef.pshadows[i]; + vec3_t up; + vec3_t ambientLight, directedLight, lightDir; + + VectorSet(lightDir, 0.57735f, 0.57735f, 0.57735f); +#if 1 + R_LightForPoint(shadow->viewOrigin, ambientLight, directedLight, lightDir); + + // sometimes there's no light + if (DotProduct(lightDir, lightDir) < 0.9f) + VectorSet(lightDir, 0.0f, 0.0f, 1.0f); +#endif + + if (shadow->viewRadius * 3.0f > shadow->lightRadius) + { + shadow->lightRadius = shadow->viewRadius * 3.0f; + } + + VectorMA(shadow->viewOrigin, shadow->viewRadius, lightDir, shadow->lightOrigin); + + // make up a projection, up doesn't matter + VectorScale(lightDir, -1.0f, shadow->lightViewAxis[0]); + VectorSet(up, 0, 0, -1); + + if ( abs(DotProduct(up, shadow->lightViewAxis[0])) > 0.9f ) + { + VectorSet(up, -1, 0, 0); + } + + CrossProduct(shadow->lightViewAxis[0], up, shadow->lightViewAxis[1]); + VectorNormalize(shadow->lightViewAxis[1]); + CrossProduct(shadow->lightViewAxis[0], shadow->lightViewAxis[1], shadow->lightViewAxis[2]); + + VectorCopy(shadow->lightViewAxis[0], shadow->cullPlane.normal); + shadow->cullPlane.dist = DotProduct(shadow->cullPlane.normal, shadow->lightOrigin); + shadow->cullPlane.type = PLANE_NON_AXIAL; + SetPlaneSignbits(&shadow->cullPlane); + } + + // next, render shadowmaps + for ( i = 0; i < tr.refdef.num_pshadows; i++) + { + int firstDrawSurf; + pshadow_t *shadow = &tr.refdef.pshadows[i]; + int j; + + Com_Memset( &shadowParms, 0, sizeof( shadowParms ) ); + + if (glRefConfig.framebufferObject) + { + shadowParms.viewportX = 0; + shadowParms.viewportY = 0; + } + else + { + shadowParms.viewportX = tr.refdef.x; + shadowParms.viewportY = glConfig.vidHeight - ( tr.refdef.y + PSHADOW_MAP_SIZE ); + } + shadowParms.viewportWidth = PSHADOW_MAP_SIZE; + shadowParms.viewportHeight = PSHADOW_MAP_SIZE; + shadowParms.isPortal = qfalse; + shadowParms.isMirror = qfalse; + + shadowParms.fovX = 90; + shadowParms.fovY = 90; + + if (glRefConfig.framebufferObject) + shadowParms.targetFbo = tr.pshadowFbos[i]; + + shadowParms.flags = VPF_SHADOWMAP | VPF_DEPTHSHADOW; + shadowParms.zFar = shadow->lightRadius; + + VectorCopy(shadow->lightOrigin, shadowParms.or.origin); + + VectorCopy(shadow->lightViewAxis[0], shadowParms.or.axis[0]); + VectorCopy(shadow->lightViewAxis[1], shadowParms.or.axis[1]); + VectorCopy(shadow->lightViewAxis[2], shadowParms.or.axis[2]); + + { + tr.viewCount++; + + tr.viewParms = shadowParms; + tr.viewParms.frameSceneNum = tr.frameSceneNum; + tr.viewParms.frameCount = tr.frameCount; + + firstDrawSurf = tr.refdef.numDrawSurfs; + + tr.viewCount++; + + // set viewParms.world + R_RotateForViewer (); + + { + float xmin, xmax, ymin, ymax, znear, zfar; + viewParms_t *dest = &tr.viewParms; + vec3_t pop; + + xmin = ymin = -shadow->viewRadius; + xmax = ymax = shadow->viewRadius; + znear = 0; + zfar = shadow->lightRadius; + + dest->projectionMatrix[0] = 2 / (xmax - xmin); + dest->projectionMatrix[4] = 0; + dest->projectionMatrix[8] = (xmax + xmin) / (xmax - xmin); + dest->projectionMatrix[12] =0; + + dest->projectionMatrix[1] = 0; + dest->projectionMatrix[5] = 2 / (ymax - ymin); + dest->projectionMatrix[9] = ( ymax + ymin ) / (ymax - ymin); // normally 0 + dest->projectionMatrix[13] = 0; + + dest->projectionMatrix[2] = 0; + dest->projectionMatrix[6] = 0; + dest->projectionMatrix[10] = 2 / (zfar - znear); + dest->projectionMatrix[14] = 0; + + dest->projectionMatrix[3] = 0; + dest->projectionMatrix[7] = 0; + dest->projectionMatrix[11] = 0; + dest->projectionMatrix[15] = 1; + + VectorScale(dest->or.axis[1], 1.0f, dest->frustum[0].normal); + VectorMA(dest->or.origin, -shadow->viewRadius, dest->frustum[0].normal, pop); + dest->frustum[0].dist = DotProduct(pop, dest->frustum[0].normal); + + VectorScale(dest->or.axis[1], -1.0f, dest->frustum[1].normal); + VectorMA(dest->or.origin, -shadow->viewRadius, dest->frustum[1].normal, pop); + dest->frustum[1].dist = DotProduct(pop, dest->frustum[1].normal); + + VectorScale(dest->or.axis[2], 1.0f, dest->frustum[2].normal); + VectorMA(dest->or.origin, -shadow->viewRadius, dest->frustum[2].normal, pop); + dest->frustum[2].dist = DotProduct(pop, dest->frustum[2].normal); + + VectorScale(dest->or.axis[2], -1.0f, dest->frustum[3].normal); + VectorMA(dest->or.origin, -shadow->viewRadius, dest->frustum[3].normal, pop); + dest->frustum[3].dist = DotProduct(pop, dest->frustum[3].normal); + + VectorScale(dest->or.axis[0], -1.0f, dest->frustum[4].normal); + VectorMA(dest->or.origin, -shadow->lightRadius, dest->frustum[4].normal, pop); + dest->frustum[4].dist = DotProduct(pop, dest->frustum[4].normal); + + for (j = 0; j < 5; j++) + { + dest->frustum[j].type = PLANE_NON_AXIAL; + SetPlaneSignbits (&dest->frustum[j]); + } + + dest->flags |= VPF_FARPLANEFRUSTUM; + } + + for (j = 0; j < shadow->numEntities; j++) + { + R_AddEntitySurface(shadow->entityNums[j]); + } + + R_SortDrawSurfs( tr.refdef.drawSurfs + firstDrawSurf, tr.refdef.numDrawSurfs - firstDrawSurf ); + + if (!glRefConfig.framebufferObject) + R_AddCapShadowmapCmd( i, -1 ); + } + } +} + +static float CalcSplit(float n, float f, float i, float m) +{ + return (n * pow(f / n, i / m) + (f - n) * i / m) / 2.0f; +} + + +void R_RenderSunShadowMaps(const refdef_t *fd, int level) +{ + viewParms_t shadowParms; + vec4_t lightDir, lightCol; + vec3_t lightViewAxis[3]; + vec3_t lightOrigin; + float splitZNear, splitZFar, splitBias; + float viewZNear, viewZFar; + vec3_t lightviewBounds[2]; + qboolean lightViewIndependentOfCameraView = qfalse; + + if (r_forceSun->integer == 2) + { + int scale = 32768; + float angle = (fd->time % scale) / (float)scale * M_PI; + lightDir[0] = cos(angle); + lightDir[1] = sin(35.0f * M_PI / 180.0f); + lightDir[2] = sin(angle) * cos(35.0f * M_PI / 180.0f); + lightDir[3] = 0.0f; + + if (1) //((fd->time % (scale * 2)) < scale) + { + lightCol[0] = + lightCol[1] = + lightCol[2] = CLAMP(sin(angle) * 2.0f, 0.0f, 1.0f) * 2.0f; + lightCol[3] = 1.0f; + } + else + { + lightCol[0] = + lightCol[1] = + lightCol[2] = CLAMP(sin(angle) * 2.0f * 0.1f, 0.0f, 0.1f); + lightCol[3] = 1.0f; + } + + VectorCopy4(lightDir, tr.refdef.sunDir); + VectorCopy4(lightCol, tr.refdef.sunCol); + VectorScale4(lightCol, 0.2f, tr.refdef.sunAmbCol); + } + else + { + VectorCopy4(tr.refdef.sunDir, lightDir); + } + + viewZNear = r_shadowCascadeZNear->value; + viewZFar = r_shadowCascadeZFar->value; + splitBias = r_shadowCascadeZBias->value; + + switch(level) + { + case 0: + default: + //splitZNear = r_znear->value; + //splitZFar = 256; + splitZNear = viewZNear; + splitZFar = CalcSplit(viewZNear, viewZFar, 1, 3) + splitBias; + break; + case 1: + splitZNear = CalcSplit(viewZNear, viewZFar, 1, 3) + splitBias; + splitZFar = CalcSplit(viewZNear, viewZFar, 2, 3) + splitBias; + //splitZNear = 256; + //splitZFar = 896; + break; + case 2: + splitZNear = CalcSplit(viewZNear, viewZFar, 2, 3) + splitBias; + splitZFar = viewZFar; + //splitZNear = 896; + //splitZFar = 3072; + break; + } + + VectorCopy(fd->vieworg, lightOrigin); + + + // Make up a projection + VectorScale(lightDir, -1.0f, lightViewAxis[0]); + + if (lightViewIndependentOfCameraView) + { + // Use world up as light view up + VectorSet(lightViewAxis[2], 0, 0, 1); + } + else if (level == 0) + { + // Level 0 tries to use a diamond texture orientation relative to camera view + // Use halfway between camera view forward and left for light view up + VectorAdd(fd->viewaxis[0], fd->viewaxis[1], lightViewAxis[2]); + } + else + { + // Use camera view up as light view up + VectorCopy(fd->viewaxis[2], lightViewAxis[2]); + } + + // Check if too close to parallel to light direction + if (abs(DotProduct(lightViewAxis[2], lightViewAxis[0])) > 0.9f) + { + if (lightViewIndependentOfCameraView) + { + // Use world left as light view up + VectorSet(lightViewAxis[2], 0, 1, 0); + } + else if (level == 0) + { + // Level 0 tries to use a diamond texture orientation relative to camera view + // Use halfway between camera view forward and up for light view up + VectorAdd(fd->viewaxis[0], fd->viewaxis[2], lightViewAxis[2]); + } + else + { + // Use camera view left as light view up + VectorCopy(fd->viewaxis[1], lightViewAxis[2]); + } + } + + // clean axes + CrossProduct(lightViewAxis[2], lightViewAxis[0], lightViewAxis[1]); + VectorNormalize(lightViewAxis[1]); + CrossProduct(lightViewAxis[0], lightViewAxis[1], lightViewAxis[2]); + + // Create bounds for light projection using slice of view projection + { + matrix_t lightViewMatrix; + vec4_t point, base, lightViewPoint; + float lx, ly; + + base[3] = 1; + point[3] = 1; + lightViewPoint[3] = 1; + + Matrix16View(lightViewAxis, lightOrigin, lightViewMatrix); + + ClearBounds(lightviewBounds[0], lightviewBounds[1]); + + // add view near plane + lx = splitZNear * tan(fd->fov_x * M_PI / 360.0f); + ly = splitZNear * tan(fd->fov_y * M_PI / 360.0f); + VectorMA(fd->vieworg, splitZNear, fd->viewaxis[0], base); + + VectorMA(base, lx, fd->viewaxis[1], point); + VectorMA(point, ly, fd->viewaxis[2], point); + Matrix16Transform(lightViewMatrix, point, lightViewPoint); + AddPointToBounds(lightViewPoint, lightviewBounds[0], lightviewBounds[1]); + + VectorMA(base, -lx, fd->viewaxis[1], point); + VectorMA(point, ly, fd->viewaxis[2], point); + Matrix16Transform(lightViewMatrix, point, lightViewPoint); + AddPointToBounds(lightViewPoint, lightviewBounds[0], lightviewBounds[1]); + + VectorMA(base, lx, fd->viewaxis[1], point); + VectorMA(point, -ly, fd->viewaxis[2], point); + Matrix16Transform(lightViewMatrix, point, lightViewPoint); + AddPointToBounds(lightViewPoint, lightviewBounds[0], lightviewBounds[1]); + + VectorMA(base, -lx, fd->viewaxis[1], point); + VectorMA(point, -ly, fd->viewaxis[2], point); + Matrix16Transform(lightViewMatrix, point, lightViewPoint); + AddPointToBounds(lightViewPoint, lightviewBounds[0], lightviewBounds[1]); + + + // add view far plane + lx = splitZFar * tan(fd->fov_x * M_PI / 360.0f); + ly = splitZFar * tan(fd->fov_y * M_PI / 360.0f); + VectorMA(fd->vieworg, splitZFar, fd->viewaxis[0], base); + + VectorMA(base, lx, fd->viewaxis[1], point); + VectorMA(point, ly, fd->viewaxis[2], point); + Matrix16Transform(lightViewMatrix, point, lightViewPoint); + AddPointToBounds(lightViewPoint, lightviewBounds[0], lightviewBounds[1]); + + VectorMA(base, -lx, fd->viewaxis[1], point); + VectorMA(point, ly, fd->viewaxis[2], point); + Matrix16Transform(lightViewMatrix, point, lightViewPoint); + AddPointToBounds(lightViewPoint, lightviewBounds[0], lightviewBounds[1]); + + VectorMA(base, lx, fd->viewaxis[1], point); + VectorMA(point, -ly, fd->viewaxis[2], point); + Matrix16Transform(lightViewMatrix, point, lightViewPoint); + AddPointToBounds(lightViewPoint, lightviewBounds[0], lightviewBounds[1]); + + VectorMA(base, -lx, fd->viewaxis[1], point); + VectorMA(point, -ly, fd->viewaxis[2], point); + Matrix16Transform(lightViewMatrix, point, lightViewPoint); + AddPointToBounds(lightViewPoint, lightviewBounds[0], lightviewBounds[1]); + + if (!glRefConfig.depthClamp) + lightviewBounds[0][0] = lightviewBounds[1][0] - 8192; + + // Moving the Light in Texel-Sized Increments + // from http://msdn.microsoft.com/en-us/library/windows/desktop/ee416324%28v=vs.85%29.aspx + // + if (lightViewIndependentOfCameraView) + { + float cascadeBound, worldUnitsPerTexel, invWorldUnitsPerTexel; + + cascadeBound = MAX(lightviewBounds[1][0] - lightviewBounds[0][0], lightviewBounds[1][1] - lightviewBounds[0][1]); + cascadeBound = MAX(cascadeBound, lightviewBounds[1][2] - lightviewBounds[0][2]); + worldUnitsPerTexel = cascadeBound / tr.sunShadowFbo[level]->width; + invWorldUnitsPerTexel = 1.0f / worldUnitsPerTexel; + + VectorScale(lightviewBounds[0], invWorldUnitsPerTexel, lightviewBounds[0]); + lightviewBounds[0][0] = floor(lightviewBounds[0][0]); + lightviewBounds[0][1] = floor(lightviewBounds[0][1]); + lightviewBounds[0][2] = floor(lightviewBounds[0][2]); + VectorScale(lightviewBounds[0], worldUnitsPerTexel, lightviewBounds[0]); + + VectorScale(lightviewBounds[1], invWorldUnitsPerTexel, lightviewBounds[1]); + lightviewBounds[1][0] = floor(lightviewBounds[1][0]); + lightviewBounds[1][1] = floor(lightviewBounds[1][1]); + lightviewBounds[1][2] = floor(lightviewBounds[1][2]); + VectorScale(lightviewBounds[1], worldUnitsPerTexel, lightviewBounds[1]); + } + + //ri.Printf(PRINT_ALL, "znear %f zfar %f\n", lightviewBounds[0][0], lightviewBounds[1][0]); + //ri.Printf(PRINT_ALL, "fovx %f fovy %f xmin %f xmax %f ymin %f ymax %f\n", fd->fov_x, fd->fov_y, xmin, xmax, ymin, ymax); + } + + + { + int firstDrawSurf; + + Com_Memset( &shadowParms, 0, sizeof( shadowParms ) ); + + if (glRefConfig.framebufferObject) + { + shadowParms.viewportX = 0; + shadowParms.viewportY = 0; + } + else + { + shadowParms.viewportX = tr.refdef.x; + shadowParms.viewportY = glConfig.vidHeight - ( tr.refdef.y + tr.sunShadowFbo[level]->height ); + } + shadowParms.viewportWidth = tr.sunShadowFbo[level]->width; + shadowParms.viewportHeight = tr.sunShadowFbo[level]->height; + shadowParms.isPortal = qfalse; + shadowParms.isMirror = qfalse; + + shadowParms.fovX = 90; + shadowParms.fovY = 90; + + if (glRefConfig.framebufferObject) + shadowParms.targetFbo = tr.sunShadowFbo[level]; + + shadowParms.flags = VPF_DEPTHSHADOW | VPF_DEPTHCLAMP | VPF_ORTHOGRAPHIC; + shadowParms.zFar = lightviewBounds[1][0]; + + VectorCopy(lightOrigin, shadowParms.or.origin); + + VectorCopy(lightViewAxis[0], shadowParms.or.axis[0]); + VectorCopy(lightViewAxis[1], shadowParms.or.axis[1]); + VectorCopy(lightViewAxis[2], shadowParms.or.axis[2]); + + VectorCopy(lightOrigin, shadowParms.pvsOrigin ); + + { + tr.viewCount++; + + tr.viewParms = shadowParms; + tr.viewParms.frameSceneNum = tr.frameSceneNum; + tr.viewParms.frameCount = tr.frameCount; + + firstDrawSurf = tr.refdef.numDrawSurfs; + + tr.viewCount++; + + // set viewParms.world + R_RotateForViewer (); + + R_SetupProjectionOrtho(&tr.viewParms, lightviewBounds); + + R_AddWorldSurfaces (); + + R_AddPolygonSurfaces(); + + R_AddEntitySurfaces (); + + R_SortDrawSurfs( tr.refdef.drawSurfs + firstDrawSurf, tr.refdef.numDrawSurfs - firstDrawSurf ); + } + + Matrix16Multiply(tr.viewParms.projectionMatrix, tr.viewParms.world.modelMatrix, tr.refdef.sunShadowMvp[level]); + } +} diff --git a/src/renderergl2/tr_marks.c b/src/renderergl2/tr_marks.c new file mode 100644 index 00000000..f24459e2 --- /dev/null +++ b/src/renderergl2/tr_marks.c @@ -0,0 +1,466 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_marks.c -- polygon projection on the world polygons + +#include "tr_local.h" +//#include "assert.h" + +#define MAX_VERTS_ON_POLY 64 + +#define MARKER_OFFSET 0 // 1 + +/* +============= +R_ChopPolyBehindPlane + +Out must have space for two more vertexes than in +============= +*/ +#define SIDE_FRONT 0 +#define SIDE_BACK 1 +#define SIDE_ON 2 +static void R_ChopPolyBehindPlane( int numInPoints, vec3_t inPoints[MAX_VERTS_ON_POLY], + int *numOutPoints, vec3_t outPoints[MAX_VERTS_ON_POLY], + vec3_t normal, vec_t dist, vec_t epsilon) { + float dists[MAX_VERTS_ON_POLY+4]; + int sides[MAX_VERTS_ON_POLY+4]; + int counts[3]; + float dot; + int i, j; + float *p1, *p2, *clip; + float d; + + // don't clip if it might overflow + if ( numInPoints >= MAX_VERTS_ON_POLY - 2 ) { + *numOutPoints = 0; + return; + } + + counts[0] = counts[1] = counts[2] = 0; + + // determine sides for each point + for ( i = 0 ; i < numInPoints ; i++ ) { + dot = DotProduct( inPoints[i], normal ); + dot -= dist; + dists[i] = dot; + if ( dot > epsilon ) { + sides[i] = SIDE_FRONT; + } else if ( dot < -epsilon ) { + sides[i] = SIDE_BACK; + } else { + sides[i] = SIDE_ON; + } + counts[sides[i]]++; + } + sides[i] = sides[0]; + dists[i] = dists[0]; + + *numOutPoints = 0; + + if ( !counts[0] ) { + return; + } + if ( !counts[1] ) { + *numOutPoints = numInPoints; + Com_Memcpy( outPoints, inPoints, numInPoints * sizeof(vec3_t) ); + return; + } + + for ( i = 0 ; i < numInPoints ; i++ ) { + p1 = inPoints[i]; + clip = outPoints[ *numOutPoints ]; + + if ( sides[i] == SIDE_ON ) { + VectorCopy( p1, clip ); + (*numOutPoints)++; + continue; + } + + if ( sides[i] == SIDE_FRONT ) { + VectorCopy( p1, clip ); + (*numOutPoints)++; + clip = outPoints[ *numOutPoints ]; + } + + if ( sides[i+1] == SIDE_ON || sides[i+1] == sides[i] ) { + continue; + } + + // generate a split point + p2 = inPoints[ (i+1) % numInPoints ]; + + d = dists[i] - dists[i+1]; + if ( d == 0 ) { + dot = 0; + } else { + dot = dists[i] / d; + } + + // clip xyz + + for (j=0 ; j<3 ; j++) { + clip[j] = p1[j] + dot * ( p2[j] - p1[j] ); + } + + (*numOutPoints)++; + } +} + +/* +================= +R_BoxSurfaces_r + +================= +*/ +void R_BoxSurfaces_r(mnode_t *node, vec3_t mins, vec3_t maxs, surfaceType_t **list, int listsize, int *listlength, vec3_t dir) { + + int s, c; + msurface_t *surf; + int *mark; + + // do the tail recursion in a loop + while ( node->contents == -1 ) { + s = BoxOnPlaneSide( mins, maxs, node->plane ); + if (s == 1) { + node = node->children[0]; + } else if (s == 2) { + node = node->children[1]; + } else { + R_BoxSurfaces_r(node->children[0], mins, maxs, list, listsize, listlength, dir); + node = node->children[1]; + } + } + + // add the individual surfaces + mark = tr.world->marksurfaces + node->firstmarksurface; + c = node->nummarksurfaces; + while (c--) { + int *surfViewCount; + // + if (*listlength >= listsize) break; + // + surfViewCount = &tr.world->surfacesViewCount[*mark]; + surf = tr.world->surfaces + *mark; + // check if the surface has NOIMPACT or NOMARKS set + if ( ( surf->shader->surfaceFlags & ( SURF_NOIMPACT | SURF_NOMARKS ) ) + || ( surf->shader->contentFlags & CONTENTS_FOG ) ) { + *surfViewCount = tr.viewCount; + } + // extra check for surfaces to avoid list overflows + else if (*(surf->data) == SF_FACE) { + // the face plane should go through the box + s = BoxOnPlaneSide( mins, maxs, &surf->cullinfo.plane ); + if (s == 1 || s == 2) { + *surfViewCount = tr.viewCount; + } else if (DotProduct(surf->cullinfo.plane.normal, dir) > -0.5) { + // don't add faces that make sharp angles with the projection direction + *surfViewCount = tr.viewCount; + } + } + else if (*(surf->data) != SF_GRID && + *(surf->data) != SF_TRIANGLES) + *surfViewCount = tr.viewCount; + // check the viewCount because the surface may have + // already been added if it spans multiple leafs + if (*surfViewCount != tr.viewCount) { + *surfViewCount = tr.viewCount; + list[*listlength] = surf->data; + (*listlength)++; + } + mark++; + } +} + +/* +================= +R_AddMarkFragments + +================= +*/ +void R_AddMarkFragments(int numClipPoints, vec3_t clipPoints[2][MAX_VERTS_ON_POLY], + int numPlanes, vec3_t *normals, float *dists, + int maxPoints, vec3_t pointBuffer, + int maxFragments, markFragment_t *fragmentBuffer, + int *returnedPoints, int *returnedFragments, + vec3_t mins, vec3_t maxs) { + int pingPong, i; + markFragment_t *mf; + + // chop the surface by all the bounding planes of the to be projected polygon + pingPong = 0; + + for ( i = 0 ; i < numPlanes ; i++ ) { + + R_ChopPolyBehindPlane( numClipPoints, clipPoints[pingPong], + &numClipPoints, clipPoints[!pingPong], + normals[i], dists[i], 0.5 ); + pingPong ^= 1; + if ( numClipPoints == 0 ) { + break; + } + } + // completely clipped away? + if ( numClipPoints == 0 ) { + return; + } + + // add this fragment to the returned list + if ( numClipPoints + (*returnedPoints) > maxPoints ) { + return; // not enough space for this polygon + } + /* + // all the clip points should be within the bounding box + for ( i = 0 ; i < numClipPoints ; i++ ) { + int j; + for ( j = 0 ; j < 3 ; j++ ) { + if (clipPoints[pingPong][i][j] < mins[j] - 0.5) break; + if (clipPoints[pingPong][i][j] > maxs[j] + 0.5) break; + } + if (j < 3) break; + } + if (i < numClipPoints) return; + */ + + mf = fragmentBuffer + (*returnedFragments); + mf->firstPoint = (*returnedPoints); + mf->numPoints = numClipPoints; + Com_Memcpy( pointBuffer + (*returnedPoints) * 3, clipPoints[pingPong], numClipPoints * sizeof(vec3_t) ); + + (*returnedPoints) += numClipPoints; + (*returnedFragments)++; +} + +/* +================= +R_MarkFragments + +================= +*/ +int R_MarkFragments( int numPoints, const vec3_t *points, const vec3_t projection, + int maxPoints, vec3_t pointBuffer, int maxFragments, markFragment_t *fragmentBuffer ) { + int numsurfaces, numPlanes; + int i, j, k, m, n; + surfaceType_t *surfaces[64]; + vec3_t mins, maxs; + int returnedFragments; + int returnedPoints; + vec3_t normals[MAX_VERTS_ON_POLY+2]; + float dists[MAX_VERTS_ON_POLY+2]; + vec3_t clipPoints[2][MAX_VERTS_ON_POLY]; + int numClipPoints; + float *v; + srfGridMesh_t *cv; + srfTriangle_t *tri; + srfVert_t *dv; + vec3_t normal; + vec3_t projectionDir; + vec3_t v1, v2; + + if (numPoints <= 0) { + return 0; + } + + //increment view count for double check prevention + tr.viewCount++; + + // + VectorNormalize2( projection, projectionDir ); + // find all the brushes that are to be considered + ClearBounds( mins, maxs ); + for ( i = 0 ; i < numPoints ; i++ ) { + vec3_t temp; + + AddPointToBounds( points[i], mins, maxs ); + VectorAdd( points[i], projection, temp ); + AddPointToBounds( temp, mins, maxs ); + // make sure we get all the leafs (also the one(s) in front of the hit surface) + VectorMA( points[i], -20, projectionDir, temp ); + AddPointToBounds( temp, mins, maxs ); + } + + if (numPoints > MAX_VERTS_ON_POLY) numPoints = MAX_VERTS_ON_POLY; + // create the bounding planes for the to be projected polygon + for ( i = 0 ; i < numPoints ; i++ ) { + VectorSubtract(points[(i+1)%numPoints], points[i], v1); + VectorAdd(points[i], projection, v2); + VectorSubtract(points[i], v2, v2); + CrossProduct(v1, v2, normals[i]); + VectorNormalizeFast(normals[i]); + dists[i] = DotProduct(normals[i], points[i]); + } + // add near and far clipping planes for projection + VectorCopy(projectionDir, normals[numPoints]); + dists[numPoints] = DotProduct(normals[numPoints], points[0]) - 32; + VectorCopy(projectionDir, normals[numPoints+1]); + VectorInverse(normals[numPoints+1]); + dists[numPoints+1] = DotProduct(normals[numPoints+1], points[0]) - 20; + numPlanes = numPoints + 2; + + numsurfaces = 0; + R_BoxSurfaces_r(tr.world->nodes, mins, maxs, surfaces, 64, &numsurfaces, projectionDir); + //assert(numsurfaces <= 64); + //assert(numsurfaces != 64); + + returnedPoints = 0; + returnedFragments = 0; + + for ( i = 0 ; i < numsurfaces ; i++ ) { + + if (*surfaces[i] == SF_GRID) { + + cv = (srfGridMesh_t *) surfaces[i]; + for ( m = 0 ; m < cv->height - 1 ; m++ ) { + for ( n = 0 ; n < cv->width - 1 ; n++ ) { + // We triangulate the grid and chop all triangles within + // the bounding planes of the to be projected polygon. + // LOD is not taken into account, not such a big deal though. + // + // It's probably much nicer to chop the grid itself and deal + // with this grid as a normal SF_GRID surface so LOD will + // be applied. However the LOD of that chopped grid must + // be synced with the LOD of the original curve. + // One way to do this; the chopped grid shares vertices with + // the original curve. When LOD is applied to the original + // curve the unused vertices are flagged. Now the chopped curve + // should skip the flagged vertices. This still leaves the + // problems with the vertices at the chopped grid edges. + // + // To avoid issues when LOD applied to "hollow curves" (like + // the ones around many jump pads) we now just add a 2 unit + // offset to the triangle vertices. + // The offset is added in the vertex normal vector direction + // so all triangles will still fit together. + // The 2 unit offset should avoid pretty much all LOD problems. + + numClipPoints = 3; + + dv = cv->verts + m * cv->width + n; + + VectorCopy(dv[0].xyz, clipPoints[0][0]); + VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[0].normal, clipPoints[0][0]); + VectorCopy(dv[cv->width].xyz, clipPoints[0][1]); + VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]); + VectorCopy(dv[1].xyz, clipPoints[0][2]); + VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[1].normal, clipPoints[0][2]); + // check the normal of this triangle + VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1); + VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2); + CrossProduct(v1, v2, normal); + VectorNormalizeFast(normal); + if (DotProduct(normal, projectionDir) < -0.1) { + // add the fragments of this triangle + R_AddMarkFragments(numClipPoints, clipPoints, + numPlanes, normals, dists, + maxPoints, pointBuffer, + maxFragments, fragmentBuffer, + &returnedPoints, &returnedFragments, mins, maxs); + + if ( returnedFragments == maxFragments ) { + return returnedFragments; // not enough space for more fragments + } + } + + VectorCopy(dv[1].xyz, clipPoints[0][0]); + VectorMA(clipPoints[0][0], MARKER_OFFSET, dv[1].normal, clipPoints[0][0]); + VectorCopy(dv[cv->width].xyz, clipPoints[0][1]); + VectorMA(clipPoints[0][1], MARKER_OFFSET, dv[cv->width].normal, clipPoints[0][1]); + VectorCopy(dv[cv->width+1].xyz, clipPoints[0][2]); + VectorMA(clipPoints[0][2], MARKER_OFFSET, dv[cv->width+1].normal, clipPoints[0][2]); + // check the normal of this triangle + VectorSubtract(clipPoints[0][0], clipPoints[0][1], v1); + VectorSubtract(clipPoints[0][2], clipPoints[0][1], v2); + CrossProduct(v1, v2, normal); + VectorNormalizeFast(normal); + if (DotProduct(normal, projectionDir) < -0.05) { + // add the fragments of this triangle + R_AddMarkFragments(numClipPoints, clipPoints, + numPlanes, normals, dists, + maxPoints, pointBuffer, + maxFragments, fragmentBuffer, + &returnedPoints, &returnedFragments, mins, maxs); + + if ( returnedFragments == maxFragments ) { + return returnedFragments; // not enough space for more fragments + } + } + } + } + } + else if (*surfaces[i] == SF_FACE) { + + srfSurfaceFace_t *surf = ( srfSurfaceFace_t * ) surfaces[i]; + + // check the normal of this face + if (DotProduct(surf->plane.normal, projectionDir) > -0.5) { + continue; + } + + for(k = 0, tri = surf->triangles; k < surf->numTriangles; k++, tri++) + { + for(j = 0; j < 3; j++) + { + v = surf->verts[tri->indexes[j]].xyz; + VectorMA(v, MARKER_OFFSET, surf->plane.normal, clipPoints[0][j]); + } + + // add the fragments of this face + R_AddMarkFragments( 3 , clipPoints, + numPlanes, normals, dists, + maxPoints, pointBuffer, + maxFragments, fragmentBuffer, + &returnedPoints, &returnedFragments, mins, maxs); + if ( returnedFragments == maxFragments ) { + return returnedFragments; // not enough space for more fragments + } + } + } + else if(*surfaces[i] == SF_TRIANGLES && r_marksOnTriangleMeshes->integer) { + + srfTriangles_t *surf = (srfTriangles_t *) surfaces[i]; + + for(k = 0, tri = surf->triangles; k < surf->numTriangles; k++, tri++) + { + for(j = 0; j < 3; j++) + { + v = surf->verts[tri->indexes[j]].xyz; + VectorMA(v, MARKER_OFFSET, surf->verts[tri->indexes[j]].normal, clipPoints[0][j]); + } + + // add the fragments of this face + R_AddMarkFragments(3, clipPoints, + numPlanes, normals, dists, + maxPoints, pointBuffer, + maxFragments, fragmentBuffer, &returnedPoints, &returnedFragments, mins, maxs); + if(returnedFragments == maxFragments) + { + return returnedFragments; // not enough space for more fragments + } + } + } + } + return returnedFragments; +} + + + + + diff --git a/src/renderergl2/tr_mesh.c b/src/renderergl2/tr_mesh.c new file mode 100644 index 00000000..342854bf --- /dev/null +++ b/src/renderergl2/tr_mesh.c @@ -0,0 +1,405 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_mesh.c: triangle model functions + +#include "tr_local.h" + +static float ProjectRadius( float r, vec3_t location ) +{ + float pr; + float dist; + float c; + vec3_t p; + float projected[4]; + + c = DotProduct( tr.viewParms.or.axis[0], tr.viewParms.or.origin ); + dist = DotProduct( tr.viewParms.or.axis[0], location ) - c; + + if ( dist <= 0 ) + return 0; + + p[0] = 0; + p[1] = fabs( r ); + p[2] = -dist; + + projected[0] = p[0] * tr.viewParms.projectionMatrix[0] + + p[1] * tr.viewParms.projectionMatrix[4] + + p[2] * tr.viewParms.projectionMatrix[8] + + tr.viewParms.projectionMatrix[12]; + + projected[1] = p[0] * tr.viewParms.projectionMatrix[1] + + p[1] * tr.viewParms.projectionMatrix[5] + + p[2] * tr.viewParms.projectionMatrix[9] + + tr.viewParms.projectionMatrix[13]; + + projected[2] = p[0] * tr.viewParms.projectionMatrix[2] + + p[1] * tr.viewParms.projectionMatrix[6] + + p[2] * tr.viewParms.projectionMatrix[10] + + tr.viewParms.projectionMatrix[14]; + + projected[3] = p[0] * tr.viewParms.projectionMatrix[3] + + p[1] * tr.viewParms.projectionMatrix[7] + + p[2] * tr.viewParms.projectionMatrix[11] + + tr.viewParms.projectionMatrix[15]; + + + pr = projected[1] / projected[3]; + + if ( pr > 1.0f ) + pr = 1.0f; + + return pr; +} + +/* +============= +R_CullModel +============= +*/ +static int R_CullModel( mdvModel_t *model, trRefEntity_t *ent ) { + vec3_t bounds[2]; + mdvFrame_t *oldFrame, *newFrame; + int i; + + // compute frame pointers + newFrame = model->frames + ent->e.frame; + oldFrame = model->frames + ent->e.oldframe; + + // cull bounding sphere ONLY if this is not an upscaled entity + if ( !ent->e.nonNormalizedAxes ) + { + if ( ent->e.frame == ent->e.oldframe ) + { + switch ( R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ) ) + { + case CULL_OUT: + tr.pc.c_sphere_cull_md3_out++; + return CULL_OUT; + + case CULL_IN: + tr.pc.c_sphere_cull_md3_in++; + return CULL_IN; + + case CULL_CLIP: + tr.pc.c_sphere_cull_md3_clip++; + break; + } + } + else + { + int sphereCull, sphereCullB; + + sphereCull = R_CullLocalPointAndRadius( newFrame->localOrigin, newFrame->radius ); + if ( newFrame == oldFrame ) { + sphereCullB = sphereCull; + } else { + sphereCullB = R_CullLocalPointAndRadius( oldFrame->localOrigin, oldFrame->radius ); + } + + if ( sphereCull == sphereCullB ) + { + if ( sphereCull == CULL_OUT ) + { + tr.pc.c_sphere_cull_md3_out++; + return CULL_OUT; + } + else if ( sphereCull == CULL_IN ) + { + tr.pc.c_sphere_cull_md3_in++; + return CULL_IN; + } + else + { + tr.pc.c_sphere_cull_md3_clip++; + } + } + } + } + + // calculate a bounding box in the current coordinate system + for (i = 0 ; i < 3 ; i++) { + bounds[0][i] = oldFrame->bounds[0][i] < newFrame->bounds[0][i] ? oldFrame->bounds[0][i] : newFrame->bounds[0][i]; + bounds[1][i] = oldFrame->bounds[1][i] > newFrame->bounds[1][i] ? oldFrame->bounds[1][i] : newFrame->bounds[1][i]; + } + + switch ( R_CullLocalBox( bounds ) ) + { + case CULL_IN: + tr.pc.c_box_cull_md3_in++; + return CULL_IN; + case CULL_CLIP: + tr.pc.c_box_cull_md3_clip++; + return CULL_CLIP; + case CULL_OUT: + default: + tr.pc.c_box_cull_md3_out++; + return CULL_OUT; + } +} + + +/* +================= +R_ComputeLOD + +================= +*/ +int R_ComputeLOD( trRefEntity_t *ent ) { + float radius; + float flod, lodscale; + float projectedRadius; + mdvFrame_t *frame; +#ifdef RAVENMD4 + mdrHeader_t *mdr; + mdrFrame_t *mdrframe; +#endif + int lod; + + if ( tr.currentModel->numLods < 2 ) + { + // model has only 1 LOD level, skip computations and bias + lod = 0; + } + else + { + // multiple LODs exist, so compute projected bounding sphere + // and use that as a criteria for selecting LOD + +#ifdef RAVENMD4 + if(tr.currentModel->type == MOD_MDR) + { + int frameSize; + mdr = (mdrHeader_t *) tr.currentModel->modelData; + frameSize = (size_t) (&((mdrFrame_t *)0)->bones[mdr->numBones]); + + mdrframe = (mdrFrame_t *) ((byte *) mdr + mdr->ofsFrames + frameSize * ent->e.frame); + + radius = RadiusFromBounds(mdrframe->bounds[0], mdrframe->bounds[1]); + } + else +#endif + { + //frame = ( md3Frame_t * ) ( ( ( unsigned char * ) tr.currentModel->md3[0] ) + tr.currentModel->md3[0]->ofsFrames ); + frame = tr.currentModel->mdv[0]->frames; + + frame += ent->e.frame; + + radius = RadiusFromBounds( frame->bounds[0], frame->bounds[1] ); + } + + if ( ( projectedRadius = ProjectRadius( radius, ent->e.origin ) ) != 0 ) + { + lodscale = r_lodscale->value; + if (lodscale > 20) lodscale = 20; + flod = 1.0f - projectedRadius * lodscale; + } + else + { + // object intersects near view plane, e.g. view weapon + flod = 0; + } + + flod *= tr.currentModel->numLods; + lod = ri.ftol(flod); + + if ( lod < 0 ) + { + lod = 0; + } + else if ( lod >= tr.currentModel->numLods ) + { + lod = tr.currentModel->numLods - 1; + } + } + + lod += r_lodbias->integer; + + if ( lod >= tr.currentModel->numLods ) + lod = tr.currentModel->numLods - 1; + if ( lod < 0 ) + lod = 0; + + return lod; +} + +/* +================= +R_ComputeFogNum + +================= +*/ +int R_ComputeFogNum( mdvModel_t *model, trRefEntity_t *ent ) { + int i, j; + fog_t *fog; + mdvFrame_t *mdvFrame; + vec3_t localOrigin; + + if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { + return 0; + } + + // FIXME: non-normalized axis issues + mdvFrame = model->frames + ent->e.frame; + VectorAdd( ent->e.origin, mdvFrame->localOrigin, localOrigin ); + for ( i = 1 ; i < tr.world->numfogs ; i++ ) { + fog = &tr.world->fogs[i]; + for ( j = 0 ; j < 3 ; j++ ) { + if ( localOrigin[j] - mdvFrame->radius >= fog->bounds[1][j] ) { + break; + } + if ( localOrigin[j] + mdvFrame->radius <= fog->bounds[0][j] ) { + break; + } + } + if ( j == 3 ) { + return i; + } + } + + return 0; +} + +/* +================= +R_AddMD3Surfaces + +================= +*/ +void R_AddMD3Surfaces( trRefEntity_t *ent ) { + int i; + mdvModel_t *model = NULL; + mdvSurface_t *surface = NULL; + shader_t *shader = NULL; + int cull; + int lod; + int fogNum; + qboolean personalModel; + + // don't add third_person objects if not in a portal + personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !(tr.viewParms.isPortal + || (tr.viewParms.flags & (VPF_SHADOWMAP | VPF_DEPTHSHADOW))); + + if ( ent->e.renderfx & RF_WRAP_FRAMES ) { + ent->e.frame %= tr.currentModel->mdv[0]->numFrames; + ent->e.oldframe %= tr.currentModel->mdv[0]->numFrames; + } + + // + // Validate the frames so there is no chance of a crash. + // This will write directly into the entity structure, so + // when the surfaces are rendered, they don't need to be + // range checked again. + // + if ( (ent->e.frame >= tr.currentModel->mdv[0]->numFrames) + || (ent->e.frame < 0) + || (ent->e.oldframe >= tr.currentModel->mdv[0]->numFrames) + || (ent->e.oldframe < 0) ) { + ri.Printf( PRINT_DEVELOPER, "R_AddMD3Surfaces: no such frame %d to %d for '%s'\n", + ent->e.oldframe, ent->e.frame, + tr.currentModel->name ); + ent->e.frame = 0; + ent->e.oldframe = 0; + } + + // + // compute LOD + // + lod = R_ComputeLOD( ent ); + + model = tr.currentModel->mdv[lod]; + + // + // cull the entire model if merged bounding box of both frames + // is outside the view frustum. + // + cull = R_CullModel ( model, ent ); + if ( cull == CULL_OUT ) { + return; + } + + // + // set up lighting now that we know we aren't culled + // + if ( !personalModel || r_shadows->integer > 1 ) { + R_SetupEntityLighting( &tr.refdef, ent ); + } + + // + // see if we are in a fog volume + // + fogNum = R_ComputeFogNum( model, ent ); + + // + // draw all surfaces + // + surface = model->surfaces; + for ( i = 0 ; i < model->numSurfaces ; i++ ) { + + if ( ent->e.customShader ) { + shader = R_GetShaderByHandle( ent->e.customShader ); + } else if ( ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins ) { + skin_t *skin; + int j; + + skin = R_GetSkinByHandle( ent->e.customSkin ); + + // match the surface name to something in the skin file + shader = tr.defaultShader; + for ( j = 0 ; j < skin->numSurfaces ; j++ ) { + // the names have both been lowercased + if ( !strcmp( skin->surfaces[j]->name, surface->name ) ) { + shader = skin->surfaces[j]->shader; + break; + } + } + if (shader == tr.defaultShader) { + ri.Printf( PRINT_DEVELOPER, "WARNING: no shader for surface %s in skin %s\n", surface->name, skin->name); + } + else if (shader->defaultShader) { + ri.Printf( PRINT_DEVELOPER, "WARNING: shader %s in skin %s not found\n", shader->name, skin->name); + } + //} else if ( surface->numShaders <= 0 ) { + //shader = tr.defaultShader; + } else { + //md3Shader = (md3Shader_t *) ( (byte *)surface + surface->ofsShaders ); + //md3Shader += ent->e.skinNum % surface->numShaders; + //shader = tr.shaders[ md3Shader->shaderIndex ]; + shader = tr.shaders[ surface->shaderIndexes[ ent->e.skinNum % surface->numShaderIndexes ] ]; + } + + // don't add third_person objects if not viewing through a portal + if(!personalModel) + { + srfVBOMDVMesh_t *vboSurface = &model->vboSurfaces[i]; + + R_AddDrawSurf((void *)vboSurface, shader, fogNum, qfalse, qfalse ); + } + + surface++; + } + +} + + + + + diff --git a/src/renderergl2/tr_model.c b/src/renderergl2/tr_model.c new file mode 100644 index 00000000..5aeab8ef --- /dev/null +++ b/src/renderergl2/tr_model.c @@ -0,0 +1,1583 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_models.c -- model loading and caching + +#include "tr_local.h" + +#define LL(x) x=LittleLong(x) + +static qboolean R_LoadMD3(model_t *mod, int lod, void *buffer, int bufferSize, const char *modName); +static qboolean R_LoadMD4(model_t *mod, void *buffer, const char *name ); +#ifdef RAVENMD4 +static qboolean R_LoadMDR(model_t *mod, void *buffer, int filesize, const char *name ); +#endif + +/* +==================== +R_RegisterMD3 +==================== +*/ +qhandle_t R_RegisterMD3(const char *name, model_t *mod) +{ + union { + unsigned *u; + void *v; + } buf; + int size; + int lod; + int ident; + qboolean loaded = qfalse; + int numLoaded; + char filename[MAX_QPATH], namebuf[MAX_QPATH+20]; + char *fext, defex[] = "md3"; + + numLoaded = 0; + + strcpy(filename, name); + + fext = strchr(filename, '.'); + if(!fext) + fext = defex; + else + { + *fext = '\0'; + fext++; + } + + for (lod = MD3_MAX_LODS - 1 ; lod >= 0 ; lod--) + { + if(lod) + Com_sprintf(namebuf, sizeof(namebuf), "%s_%d.%s", filename, lod, fext); + else + Com_sprintf(namebuf, sizeof(namebuf), "%s.%s", filename, fext); + + size = ri.FS_ReadFile( namebuf, &buf.v ); + if(!buf.u) + continue; + + ident = LittleLong(* (unsigned *) buf.u); + if (ident == MD4_IDENT) + loaded = R_LoadMD4(mod, buf.u, name); + else + { + if (ident == MD3_IDENT) + loaded = R_LoadMD3(mod, lod, buf.u, size, name); + else + ri.Printf(PRINT_WARNING,"R_RegisterMD3: unknown fileid for %s\n", name); + } + + ri.FS_FreeFile(buf.v); + + if(loaded) + { + mod->numLods++; + numLoaded++; + } + else + break; + } + + if(numLoaded) + { + // duplicate into higher lod spots that weren't + // loaded, in case the user changes r_lodbias on the fly + for(lod--; lod >= 0; lod--) + { + mod->numLods++; + mod->mdv[lod] = mod->mdv[lod + 1]; + } + + return mod->index; + } + +#ifdef _DEBUG + ri.Printf(PRINT_WARNING,"R_RegisterMD3: couldn't load %s\n", name); +#endif + + mod->type = MOD_BAD; + return 0; +} + +#ifdef RAVENMD4 +/* +==================== +R_RegisterMDR +==================== +*/ +qhandle_t R_RegisterMDR(const char *name, model_t *mod) +{ + union { + unsigned *u; + void *v; + } buf; + int ident; + qboolean loaded = qfalse; + int filesize; + + filesize = ri.FS_ReadFile(name, (void **) &buf.v); + if(!buf.u) + { + mod->type = MOD_BAD; + return 0; + } + + ident = LittleLong(*(unsigned *)buf.u); + if(ident == MDR_IDENT) + loaded = R_LoadMDR(mod, buf.u, filesize, name); + + ri.FS_FreeFile (buf.v); + + if(!loaded) + { + ri.Printf(PRINT_WARNING,"R_RegisterMDR: couldn't load mdr file %s\n", name); + mod->type = MOD_BAD; + return 0; + } + + return mod->index; +} +#endif + +/* +==================== +R_RegisterIQM +==================== +*/ +qhandle_t R_RegisterIQM(const char *name, model_t *mod) +{ + union { + unsigned *u; + void *v; + } buf; + qboolean loaded = qfalse; + int filesize; + + filesize = ri.FS_ReadFile(name, (void **) &buf.v); + if(!buf.u) + { + mod->type = MOD_BAD; + return 0; + } + + loaded = R_LoadIQM(mod, buf.u, filesize, name); + + ri.FS_FreeFile (buf.v); + + if(!loaded) + { + ri.Printf(PRINT_WARNING,"R_RegisterIQM: couldn't load iqm file %s\n", name); + mod->type = MOD_BAD; + return 0; + } + + return mod->index; +} + + +typedef struct +{ + char *ext; + qhandle_t (*ModelLoader)( const char *, model_t * ); +} modelExtToLoaderMap_t; + +// Note that the ordering indicates the order of preference used +// when there are multiple models of different formats available +static modelExtToLoaderMap_t modelLoaders[ ] = +{ + { "iqm", R_RegisterIQM }, +#ifdef RAVENMD4 + { "mdr", R_RegisterMDR }, +#endif + { "md4", R_RegisterMD3 }, + { "md3", R_RegisterMD3 } +}; + +static int numModelLoaders = ARRAY_LEN(modelLoaders); + +//=============================================================================== + +/* +** R_GetModelByHandle +*/ +model_t *R_GetModelByHandle( qhandle_t index ) { + model_t *mod; + + // out of range gets the defualt model + if ( index < 1 || index >= tr.numModels ) { + return tr.models[0]; + } + + mod = tr.models[index]; + + return mod; +} + +//=============================================================================== + +/* +** R_AllocModel +*/ +model_t *R_AllocModel( void ) { + model_t *mod; + + if ( tr.numModels == MAX_MOD_KNOWN ) { + return NULL; + } + + mod = ri.Hunk_Alloc( sizeof( *tr.models[tr.numModels] ), h_low ); + mod->index = tr.numModels; + tr.models[tr.numModels] = mod; + tr.numModels++; + + return mod; +} + +/* +==================== +RE_RegisterModel + +Loads in a model for the given name + +Zero will be returned if the model fails to load. +An entry will be retained for failed models as an +optimization to prevent disk rescanning if they are +asked for again. +==================== +*/ +qhandle_t RE_RegisterModel( const char *name ) { + model_t *mod; + qhandle_t hModel; + qboolean orgNameFailed = qfalse; + int orgLoader = -1; + int i; + char localName[ MAX_QPATH ]; + const char *ext; + char altName[ MAX_QPATH ]; + + if ( !name || !name[0] ) { + ri.Printf( PRINT_ALL, "RE_RegisterModel: NULL name\n" ); + return 0; + } + + if ( strlen( name ) >= MAX_QPATH ) { + ri.Printf( PRINT_ALL, "Model name exceeds MAX_QPATH\n" ); + return 0; + } + + // + // search the currently loaded models + // + for ( hModel = 1 ; hModel < tr.numModels; hModel++ ) { + mod = tr.models[hModel]; + if ( !strcmp( mod->name, name ) ) { + if( mod->type == MOD_BAD ) { + return 0; + } + return hModel; + } + } + + // allocate a new model_t + + if ( ( mod = R_AllocModel() ) == NULL ) { + ri.Printf( PRINT_WARNING, "RE_RegisterModel: R_AllocModel() failed for '%s'\n", name); + return 0; + } + + // only set the name after the model has been successfully loaded + Q_strncpyz( mod->name, name, sizeof( mod->name ) ); + + + R_IssuePendingRenderCommands(); + + mod->type = MOD_BAD; + mod->numLods = 0; + + // + // load the files + // + Q_strncpyz( localName, name, MAX_QPATH ); + + ext = COM_GetExtension( localName ); + + if( *ext ) + { + // Look for the correct loader and use it + for( i = 0; i < numModelLoaders; i++ ) + { + if( !Q_stricmp( ext, modelLoaders[ i ].ext ) ) + { + // Load + hModel = modelLoaders[ i ].ModelLoader( localName, mod ); + break; + } + } + + // A loader was found + if( i < numModelLoaders ) + { + if( !hModel ) + { + // Loader failed, most likely because the file isn't there; + // try again without the extension + orgNameFailed = qtrue; + orgLoader = i; + COM_StripExtension( name, localName, MAX_QPATH ); + } + else + { + // Something loaded + return mod->index; + } + } + } + + // Try and find a suitable match using all + // the model formats supported + for( i = 0; i < numModelLoaders; i++ ) + { + if (i == orgLoader) + continue; + + Com_sprintf( altName, sizeof (altName), "%s.%s", localName, modelLoaders[ i ].ext ); + + // Load + hModel = modelLoaders[ i ].ModelLoader( altName, mod ); + + if( hModel ) + { + if( orgNameFailed ) + { + ri.Printf( PRINT_DEVELOPER, "WARNING: %s not present, using %s instead\n", + name, altName ); + } + + break; + } + } + + return hModel; +} + +/* +================= +R_LoadMD3 +================= +*/ +static qboolean R_LoadMD3(model_t * mod, int lod, void *buffer, int bufferSize, const char *modName) +{ + int f, i, j, k; + + md3Header_t *md3Model; + md3Frame_t *md3Frame; + md3Surface_t *md3Surf; + md3Shader_t *md3Shader; + md3Triangle_t *md3Tri; + md3St_t *md3st; + md3XyzNormal_t *md3xyz; + md3Tag_t *md3Tag; + + mdvModel_t *mdvModel; + mdvFrame_t *frame; + mdvSurface_t *surf;//, *surface; + int *shaderIndex; + srfTriangle_t *tri; + mdvVertex_t *v; + mdvSt_t *st; + mdvTag_t *tag; + mdvTagName_t *tagName; + + int version; + int size; + + md3Model = (md3Header_t *) buffer; + + version = LittleLong(md3Model->version); + if(version != MD3_VERSION) + { + ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has wrong version (%i should be %i)\n", modName, version, MD3_VERSION); + return qfalse; + } + + mod->type = MOD_MESH; + size = LittleLong(md3Model->ofsEnd); + mod->dataSize += size; + mdvModel = mod->mdv[lod] = ri.Hunk_Alloc(sizeof(mdvModel_t), h_low); + +// Com_Memcpy(mod->md3[lod], buffer, LittleLong(md3Model->ofsEnd)); + + LL(md3Model->ident); + LL(md3Model->version); + LL(md3Model->numFrames); + LL(md3Model->numTags); + LL(md3Model->numSurfaces); + LL(md3Model->ofsFrames); + LL(md3Model->ofsTags); + LL(md3Model->ofsSurfaces); + LL(md3Model->ofsEnd); + + if(md3Model->numFrames < 1) + { + ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has no frames\n", modName); + return qfalse; + } + + // swap all the frames + mdvModel->numFrames = md3Model->numFrames; + mdvModel->frames = frame = ri.Hunk_Alloc(sizeof(*frame) * md3Model->numFrames, h_low); + + md3Frame = (md3Frame_t *) ((byte *) md3Model + md3Model->ofsFrames); + for(i = 0; i < md3Model->numFrames; i++, frame++, md3Frame++) + { + frame->radius = LittleFloat(md3Frame->radius); + for(j = 0; j < 3; j++) + { + frame->bounds[0][j] = LittleFloat(md3Frame->bounds[0][j]); + frame->bounds[1][j] = LittleFloat(md3Frame->bounds[1][j]); + frame->localOrigin[j] = LittleFloat(md3Frame->localOrigin[j]); + } + } + + // swap all the tags + mdvModel->numTags = md3Model->numTags; + mdvModel->tags = tag = ri.Hunk_Alloc(sizeof(*tag) * (md3Model->numTags * md3Model->numFrames), h_low); + + md3Tag = (md3Tag_t *) ((byte *) md3Model + md3Model->ofsTags); + for(i = 0; i < md3Model->numTags * md3Model->numFrames; i++, tag++, md3Tag++) + { + for(j = 0; j < 3; j++) + { + tag->origin[j] = LittleFloat(md3Tag->origin[j]); + tag->axis[0][j] = LittleFloat(md3Tag->axis[0][j]); + tag->axis[1][j] = LittleFloat(md3Tag->axis[1][j]); + tag->axis[2][j] = LittleFloat(md3Tag->axis[2][j]); + } + } + + + mdvModel->tagNames = tagName = ri.Hunk_Alloc(sizeof(*tagName) * (md3Model->numTags), h_low); + + md3Tag = (md3Tag_t *) ((byte *) md3Model + md3Model->ofsTags); + for(i = 0; i < md3Model->numTags; i++, tagName++, md3Tag++) + { + Q_strncpyz(tagName->name, md3Tag->name, sizeof(tagName->name)); + } + + // swap all the surfaces + mdvModel->numSurfaces = md3Model->numSurfaces; + mdvModel->surfaces = surf = ri.Hunk_Alloc(sizeof(*surf) * md3Model->numSurfaces, h_low); + + md3Surf = (md3Surface_t *) ((byte *) md3Model + md3Model->ofsSurfaces); + for(i = 0; i < md3Model->numSurfaces; i++) + { + LL(md3Surf->ident); + LL(md3Surf->flags); + LL(md3Surf->numFrames); + LL(md3Surf->numShaders); + LL(md3Surf->numTriangles); + LL(md3Surf->ofsTriangles); + LL(md3Surf->numVerts); + LL(md3Surf->ofsShaders); + LL(md3Surf->ofsSt); + LL(md3Surf->ofsXyzNormals); + LL(md3Surf->ofsEnd); + + if(md3Surf->numVerts > SHADER_MAX_VERTEXES) + { + ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has more than %i verts on a surface (%i)", + modName, SHADER_MAX_VERTEXES, md3Surf->numVerts); + return qfalse; + } + if(md3Surf->numTriangles * 3 > SHADER_MAX_INDEXES) + { + ri.Printf(PRINT_WARNING, "R_LoadMD3: %s has more than %i triangles on a surface (%i)", + modName, SHADER_MAX_INDEXES / 3, md3Surf->numTriangles); + return qfalse; + } + + // change to surface identifier + surf->surfaceType = SF_MDV; + + // give pointer to model for Tess_SurfaceMDX + surf->model = mdvModel; + + // copy surface name + Q_strncpyz(surf->name, md3Surf->name, sizeof(surf->name)); + + // lowercase the surface name so skin compares are faster + Q_strlwr(surf->name); + + // strip off a trailing _1 or _2 + // this is a crutch for q3data being a mess + j = strlen(surf->name); + if(j > 2 && surf->name[j - 2] == '_') + { + surf->name[j - 2] = 0; + } + + // register the shaders + surf->numShaderIndexes = md3Surf->numShaders; + surf->shaderIndexes = shaderIndex = ri.Hunk_Alloc(sizeof(*shaderIndex) * md3Surf->numShaders, h_low); + + md3Shader = (md3Shader_t *) ((byte *) md3Surf + md3Surf->ofsShaders); + for(j = 0; j < md3Surf->numShaders; j++, shaderIndex++, md3Shader++) + { + shader_t *sh; + + sh = R_FindShader(md3Shader->name, LIGHTMAP_NONE, qtrue); + if(sh->defaultShader) + { + *shaderIndex = 0; + } + else + { + *shaderIndex = sh->index; + } + } + + // swap all the triangles + surf->numTriangles = md3Surf->numTriangles; + surf->triangles = tri = ri.Hunk_Alloc(sizeof(*tri) * md3Surf->numTriangles, h_low); + + md3Tri = (md3Triangle_t *) ((byte *) md3Surf + md3Surf->ofsTriangles); + for(j = 0; j < md3Surf->numTriangles; j++, tri++, md3Tri++) + { + tri->indexes[0] = LittleLong(md3Tri->indexes[0]); + tri->indexes[1] = LittleLong(md3Tri->indexes[1]); + tri->indexes[2] = LittleLong(md3Tri->indexes[2]); + } + + R_CalcSurfaceTriangleNeighbors(surf->numTriangles, surf->triangles); + + // swap all the XyzNormals + surf->numVerts = md3Surf->numVerts; + surf->verts = v = ri.Hunk_Alloc(sizeof(*v) * (md3Surf->numVerts * md3Surf->numFrames), h_low); + + md3xyz = (md3XyzNormal_t *) ((byte *) md3Surf + md3Surf->ofsXyzNormals); + for(j = 0; j < md3Surf->numVerts * md3Surf->numFrames; j++, md3xyz++, v++) + { + unsigned lat, lng; + unsigned short normal; + + v->xyz[0] = LittleShort(md3xyz->xyz[0]) * MD3_XYZ_SCALE; + v->xyz[1] = LittleShort(md3xyz->xyz[1]) * MD3_XYZ_SCALE; + v->xyz[2] = LittleShort(md3xyz->xyz[2]) * MD3_XYZ_SCALE; + + normal = LittleShort(md3xyz->normal); + + lat = ( normal >> 8 ) & 0xff; + lng = ( normal & 0xff ); + lat *= (FUNCTABLE_SIZE/256); + lng *= (FUNCTABLE_SIZE/256); + + // decode X as cos( lat ) * sin( long ) + // decode Y as sin( lat ) * sin( long ) + // decode Z as cos( long ) + + v->normal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; + v->normal[1] = tr.sinTable[lat] * tr.sinTable[lng]; + v->normal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; + } + + // swap all the ST + surf->st = st = ri.Hunk_Alloc(sizeof(*st) * md3Surf->numVerts, h_low); + + md3st = (md3St_t *) ((byte *) md3Surf + md3Surf->ofsSt); + for(j = 0; j < md3Surf->numVerts; j++, md3st++, st++) + { + st->st[0] = LittleFloat(md3st->st[0]); + st->st[1] = LittleFloat(md3st->st[1]); + } + +#ifdef USE_VERT_TANGENT_SPACE + // calc tangent spaces + { + // Valgrind complaints: Conditional jump or move depends on uninitialised value(s) + // So lets Initialize them. + const float *v0 = NULL, *v1 = NULL, *v2 = NULL; + const float *t0 = NULL, *t1 = NULL, *t2 = NULL; + vec3_t tangent = { 0, 0, 0 }; + vec3_t bitangent = { 0, 0, 0 }; + vec3_t normal = { 0, 0, 0 }; + + for(j = 0, v = surf->verts; j < (surf->numVerts * mdvModel->numFrames); j++, v++) + { + VectorClear(v->tangent); + VectorClear(v->bitangent); + if (r_recalcMD3Normals->integer) + VectorClear(v->normal); + } + + for(f = 0; f < mdvModel->numFrames; f++) + { + for(j = 0, tri = surf->triangles; j < surf->numTriangles; j++, tri++) + { + v0 = surf->verts[surf->numVerts * f + tri->indexes[0]].xyz; + v1 = surf->verts[surf->numVerts * f + tri->indexes[1]].xyz; + v2 = surf->verts[surf->numVerts * f + tri->indexes[2]].xyz; + + t0 = surf->st[tri->indexes[0]].st; + t1 = surf->st[tri->indexes[1]].st; + t2 = surf->st[tri->indexes[2]].st; + + if (!r_recalcMD3Normals->integer) + VectorCopy(v->normal, normal); + else + VectorClear(normal); + + #if 1 + R_CalcTangentSpace(tangent, bitangent, normal, v0, v1, v2, t0, t1, t2); + #else + R_CalcNormalForTriangle(normal, v0, v1, v2); + R_CalcTangentsForTriangle(tangent, bitangent, v0, v1, v2, t0, t1, t2); + #endif + + for(k = 0; k < 3; k++) + { + float *v; + + v = surf->verts[surf->numVerts * f + tri->indexes[k]].tangent; + VectorAdd(v, tangent, v); + + v = surf->verts[surf->numVerts * f + tri->indexes[k]].bitangent; + VectorAdd(v, bitangent, v); + + if (r_recalcMD3Normals->integer) + { + v = surf->verts[surf->numVerts * f + tri->indexes[k]].normal; + VectorAdd(v, normal, v); + } + } + } + } + + for(j = 0, v = surf->verts; j < (surf->numVerts * mdvModel->numFrames); j++, v++) + { + VectorNormalize(v->tangent); + VectorNormalize(v->bitangent); + VectorNormalize(v->normal); + } + } +#endif + + // find the next surface + md3Surf = (md3Surface_t *) ((byte *) md3Surf + md3Surf->ofsEnd); + surf++; + } + + { + srfVBOMDVMesh_t *vboSurf; + + mdvModel->numVBOSurfaces = mdvModel->numSurfaces; + mdvModel->vboSurfaces = ri.Hunk_Alloc(sizeof(*mdvModel->vboSurfaces) * mdvModel->numSurfaces, h_low); + + vboSurf = mdvModel->vboSurfaces; + surf = mdvModel->surfaces; + for (i = 0; i < mdvModel->numSurfaces; i++, vboSurf++, surf++) + { + vec3_t *verts; + vec3_t *normals; + vec2_t *texcoords; +#ifdef USE_VERT_TANGENT_SPACE + vec3_t *tangents; + vec3_t *bitangents; +#endif + + byte *data; + int dataSize; + + int ofs_xyz, ofs_normal, ofs_st; +#ifdef USE_VERT_TANGENT_SPACE + int ofs_tangent, ofs_bitangent; +#endif + + dataSize = 0; + + ofs_xyz = dataSize; + dataSize += surf->numVerts * mdvModel->numFrames * sizeof(*verts); + + ofs_normal = dataSize; + dataSize += surf->numVerts * mdvModel->numFrames * sizeof(*normals); + +#ifdef USE_VERT_TANGENT_SPACE + ofs_tangent = dataSize; + dataSize += surf->numVerts * mdvModel->numFrames * sizeof(*tangents); + + ofs_bitangent = dataSize; + dataSize += surf->numVerts * mdvModel->numFrames * sizeof(*bitangents); +#endif + + ofs_st = dataSize; + dataSize += surf->numVerts * sizeof(*texcoords); + + data = ri.Malloc(dataSize); + + verts = (void *)(data + ofs_xyz); + normals = (void *)(data + ofs_normal); +#ifdef USE_VERT_TANGENT_SPACE + tangents = (void *)(data + ofs_tangent); + bitangents = (void *)(data + ofs_bitangent); +#endif + texcoords = (void *)(data + ofs_st); + + v = surf->verts; + for ( j = 0; j < surf->numVerts * mdvModel->numFrames ; j++, v++ ) + { + VectorCopy(v->xyz, verts[j]); + VectorCopy(v->normal, normals[j]); +#ifdef USE_VERT_TANGENT_SPACE + VectorCopy(v->tangent, tangents[j]); + VectorCopy(v->bitangent, bitangents[j]); +#endif + } + + st = surf->st; + for ( j = 0 ; j < surf->numVerts ; j++, st++ ) { + texcoords[j][0] = st->st[0]; + texcoords[j][1] = st->st[1]; + } + + vboSurf->surfaceType = SF_VBO_MDVMESH; + vboSurf->mdvModel = mdvModel; + vboSurf->mdvSurface = surf; + vboSurf->numIndexes = surf->numTriangles * 3; + vboSurf->numVerts = surf->numVerts; + + vboSurf->minIndex = 0; + vboSurf->maxIndex = surf->numVerts; + + vboSurf->vbo = R_CreateVBO(va("staticMD3Mesh_VBO '%s'", surf->name), data, dataSize, VBO_USAGE_STATIC); + + vboSurf->vbo->ofs_xyz = ofs_xyz; + vboSurf->vbo->ofs_normal = ofs_normal; +#ifdef USE_VERT_TANGENT_SPACE + vboSurf->vbo->ofs_tangent = ofs_tangent; + vboSurf->vbo->ofs_bitangent = ofs_bitangent; +#endif + vboSurf->vbo->ofs_st = ofs_st; + + vboSurf->vbo->stride_xyz = sizeof(*verts); + vboSurf->vbo->stride_normal = sizeof(*normals); +#ifdef USE_VERT_TANGENT_SPACE + vboSurf->vbo->stride_tangent = sizeof(*tangents); + vboSurf->vbo->stride_bitangent = sizeof(*bitangents); +#endif + vboSurf->vbo->stride_st = sizeof(*st); + + vboSurf->vbo->size_xyz = sizeof(*verts) * surf->numVerts; + vboSurf->vbo->size_normal = sizeof(*normals) * surf->numVerts; + + ri.Free(data); + + vboSurf->ibo = R_CreateIBO2(va("staticMD3Mesh_IBO %s", surf->name), surf->numTriangles, surf->triangles, VBO_USAGE_STATIC); + } + } + + return qtrue; +} + + +#ifdef RAVENMD4 + +/* +================= +R_LoadMDR +================= +*/ +static qboolean R_LoadMDR( model_t *mod, void *buffer, int filesize, const char *mod_name ) +{ + int i, j, k, l; + mdrHeader_t *pinmodel, *mdr; + mdrFrame_t *frame; + mdrLOD_t *lod, *curlod; + mdrSurface_t *surf, *cursurf; + mdrTriangle_t *tri, *curtri; + mdrVertex_t *v, *curv; + mdrWeight_t *weight, *curweight; + mdrTag_t *tag, *curtag; + int size; + shader_t *sh; + + pinmodel = (mdrHeader_t *)buffer; + + pinmodel->version = LittleLong(pinmodel->version); + if (pinmodel->version != MDR_VERSION) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has wrong version (%i should be %i)\n", mod_name, pinmodel->version, MDR_VERSION); + return qfalse; + } + + size = LittleLong(pinmodel->ofsEnd); + + if(size > filesize) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: Header of %s is broken. Wrong filesize declared!\n", mod_name); + return qfalse; + } + + mod->type = MOD_MDR; + + LL(pinmodel->numFrames); + LL(pinmodel->numBones); + LL(pinmodel->ofsFrames); + + // This is a model that uses some type of compressed Bones. We don't want to uncompress every bone for each rendered frame + // over and over again, we'll uncompress it in this function already, so we must adjust the size of the target md4. + if(pinmodel->ofsFrames < 0) + { + // mdrFrame_t is larger than mdrCompFrame_t: + size += pinmodel->numFrames * sizeof(frame->name); + // now add enough space for the uncompressed bones. + size += pinmodel->numFrames * pinmodel->numBones * ((sizeof(mdrBone_t) - sizeof(mdrCompBone_t))); + } + + // simple bounds check + if(pinmodel->numBones < 0 || + sizeof(*mdr) + pinmodel->numFrames * (sizeof(*frame) + (pinmodel->numBones - 1) * sizeof(*frame->bones)) > size) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); + return qfalse; + } + + mod->dataSize += size; + mod->modelData = mdr = ri.Hunk_Alloc( size, h_low ); + + // Copy all the values over from the file and fix endian issues in the process, if necessary. + + mdr->ident = LittleLong(pinmodel->ident); + mdr->version = pinmodel->version; // Don't need to swap byte order on this one, we already did above. + Q_strncpyz(mdr->name, pinmodel->name, sizeof(mdr->name)); + mdr->numFrames = pinmodel->numFrames; + mdr->numBones = pinmodel->numBones; + mdr->numLODs = LittleLong(pinmodel->numLODs); + mdr->numTags = LittleLong(pinmodel->numTags); + // We don't care about the other offset values, we'll generate them ourselves while loading. + + mod->numLods = mdr->numLODs; + + if ( mdr->numFrames < 1 ) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has no frames\n", mod_name); + return qfalse; + } + + /* The first frame will be put into the first free space after the header */ + frame = (mdrFrame_t *)(mdr + 1); + mdr->ofsFrames = (int)((byte *) frame - (byte *) mdr); + + if (pinmodel->ofsFrames < 0) + { + mdrCompFrame_t *cframe; + + // compressed model... + cframe = (mdrCompFrame_t *)((byte *) pinmodel - pinmodel->ofsFrames); + + for(i = 0; i < mdr->numFrames; i++) + { + for(j = 0; j < 3; j++) + { + frame->bounds[0][j] = LittleFloat(cframe->bounds[0][j]); + frame->bounds[1][j] = LittleFloat(cframe->bounds[1][j]); + frame->localOrigin[j] = LittleFloat(cframe->localOrigin[j]); + } + + frame->radius = LittleFloat(cframe->radius); + frame->name[0] = '\0'; // No name supplied in the compressed version. + + for(j = 0; j < mdr->numBones; j++) + { + for(k = 0; k < (sizeof(cframe->bones[j].Comp) / 2); k++) + { + // Do swapping for the uncompressing functions. They seem to use shorts + // values only, so I assume this will work. Never tested it on other + // platforms, though. + + ((unsigned short *)(cframe->bones[j].Comp))[k] = + LittleShort( ((unsigned short *)(cframe->bones[j].Comp))[k] ); + } + + /* Now do the actual uncompressing */ + MC_UnCompress(frame->bones[j].matrix, cframe->bones[j].Comp); + } + + // Next Frame... + cframe = (mdrCompFrame_t *) &cframe->bones[j]; + frame = (mdrFrame_t *) &frame->bones[j]; + } + } + else + { + mdrFrame_t *curframe; + + // uncompressed model... + // + + curframe = (mdrFrame_t *)((byte *) pinmodel + pinmodel->ofsFrames); + + // swap all the frames + for ( i = 0 ; i < mdr->numFrames ; i++) + { + for(j = 0; j < 3; j++) + { + frame->bounds[0][j] = LittleFloat(curframe->bounds[0][j]); + frame->bounds[1][j] = LittleFloat(curframe->bounds[1][j]); + frame->localOrigin[j] = LittleFloat(curframe->localOrigin[j]); + } + + frame->radius = LittleFloat(curframe->radius); + Q_strncpyz(frame->name, curframe->name, sizeof(frame->name)); + + for (j = 0; j < (int) (mdr->numBones * sizeof(mdrBone_t) / 4); j++) + { + ((float *)frame->bones)[j] = LittleFloat( ((float *)curframe->bones)[j] ); + } + + curframe = (mdrFrame_t *) &curframe->bones[mdr->numBones]; + frame = (mdrFrame_t *) &frame->bones[mdr->numBones]; + } + } + + // frame should now point to the first free address after all frames. + lod = (mdrLOD_t *) frame; + mdr->ofsLODs = (int) ((byte *) lod - (byte *)mdr); + + curlod = (mdrLOD_t *)((byte *) pinmodel + LittleLong(pinmodel->ofsLODs)); + + // swap all the LOD's + for ( l = 0 ; l < mdr->numLODs ; l++) + { + // simple bounds check + if((byte *) (lod + 1) > (byte *) mdr + size) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); + return qfalse; + } + + lod->numSurfaces = LittleLong(curlod->numSurfaces); + + // swap all the surfaces + surf = (mdrSurface_t *) (lod + 1); + lod->ofsSurfaces = (int)((byte *) surf - (byte *) lod); + cursurf = (mdrSurface_t *) ((byte *)curlod + LittleLong(curlod->ofsSurfaces)); + + for ( i = 0 ; i < lod->numSurfaces ; i++) + { + // simple bounds check + if((byte *) (surf + 1) > (byte *) mdr + size) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); + return qfalse; + } + + // first do some copying stuff + + surf->ident = SF_MDR; + Q_strncpyz(surf->name, cursurf->name, sizeof(surf->name)); + Q_strncpyz(surf->shader, cursurf->shader, sizeof(surf->shader)); + + surf->ofsHeader = (byte *) mdr - (byte *) surf; + + surf->numVerts = LittleLong(cursurf->numVerts); + surf->numTriangles = LittleLong(cursurf->numTriangles); + // numBoneReferences and BoneReferences generally seem to be unused + + // now do the checks that may fail. + if ( surf->numVerts > SHADER_MAX_VERTEXES ) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has more than %i verts on a surface (%i).\n", + mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); + return qfalse; + } + if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has more than %i triangles on a surface (%i).\n", + mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); + return qfalse; + } + // lowercase the surface name so skin compares are faster + Q_strlwr( surf->name ); + + // register the shaders + sh = R_FindShader(surf->shader, LIGHTMAP_NONE, qtrue); + if ( sh->defaultShader ) { + surf->shaderIndex = 0; + } else { + surf->shaderIndex = sh->index; + } + + // now copy the vertexes. + v = (mdrVertex_t *) (surf + 1); + surf->ofsVerts = (int)((byte *) v - (byte *) surf); + curv = (mdrVertex_t *) ((byte *)cursurf + LittleLong(cursurf->ofsVerts)); + + for(j = 0; j < surf->numVerts; j++) + { + LL(curv->numWeights); + + // simple bounds check + if(curv->numWeights < 0 || (byte *) (v + 1) + (curv->numWeights - 1) * sizeof(*weight) > (byte *) mdr + size) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); + return qfalse; + } + + v->normal[0] = LittleFloat(curv->normal[0]); + v->normal[1] = LittleFloat(curv->normal[1]); + v->normal[2] = LittleFloat(curv->normal[2]); + + v->texCoords[0] = LittleFloat(curv->texCoords[0]); + v->texCoords[1] = LittleFloat(curv->texCoords[1]); + + v->numWeights = curv->numWeights; + weight = &v->weights[0]; + curweight = &curv->weights[0]; + + // Now copy all the weights + for(k = 0; k < v->numWeights; k++) + { + weight->boneIndex = LittleLong(curweight->boneIndex); + weight->boneWeight = LittleFloat(curweight->boneWeight); + + weight->offset[0] = LittleFloat(curweight->offset[0]); + weight->offset[1] = LittleFloat(curweight->offset[1]); + weight->offset[2] = LittleFloat(curweight->offset[2]); + + weight++; + curweight++; + } + + v = (mdrVertex_t *) weight; + curv = (mdrVertex_t *) curweight; + } + + // we know the offset to the triangles now: + tri = (mdrTriangle_t *) v; + surf->ofsTriangles = (int)((byte *) tri - (byte *) surf); + curtri = (mdrTriangle_t *)((byte *) cursurf + LittleLong(cursurf->ofsTriangles)); + + // simple bounds check + if(surf->numTriangles < 0 || (byte *) (tri + surf->numTriangles) > (byte *) mdr + size) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); + return qfalse; + } + + for(j = 0; j < surf->numTriangles; j++) + { + tri->indexes[0] = LittleLong(curtri->indexes[0]); + tri->indexes[1] = LittleLong(curtri->indexes[1]); + tri->indexes[2] = LittleLong(curtri->indexes[2]); + + tri++; + curtri++; + } + + // tri now points to the end of the surface. + surf->ofsEnd = (byte *) tri - (byte *) surf; + surf = (mdrSurface_t *) tri; + + // find the next surface. + cursurf = (mdrSurface_t *) ((byte *) cursurf + LittleLong(cursurf->ofsEnd)); + } + + // surf points to the next lod now. + lod->ofsEnd = (int)((byte *) surf - (byte *) lod); + lod = (mdrLOD_t *) surf; + + // find the next LOD. + curlod = (mdrLOD_t *)((byte *) curlod + LittleLong(curlod->ofsEnd)); + } + + // lod points to the first tag now, so update the offset too. + tag = (mdrTag_t *) lod; + mdr->ofsTags = (int)((byte *) tag - (byte *) mdr); + curtag = (mdrTag_t *) ((byte *)pinmodel + LittleLong(pinmodel->ofsTags)); + + // simple bounds check + if(mdr->numTags < 0 || (byte *) (tag + mdr->numTags) > (byte *) mdr + size) + { + ri.Printf(PRINT_WARNING, "R_LoadMDR: %s has broken structure.\n", mod_name); + return qfalse; + } + + for (i = 0 ; i < mdr->numTags ; i++) + { + tag->boneIndex = LittleLong(curtag->boneIndex); + Q_strncpyz(tag->name, curtag->name, sizeof(tag->name)); + + tag++; + curtag++; + } + + // And finally we know the real offset to the end. + mdr->ofsEnd = (int)((byte *) tag - (byte *) mdr); + + // phew! we're done. + + return qtrue; +} +#endif + +/* +================= +R_LoadMD4 +================= +*/ + +static qboolean R_LoadMD4( model_t *mod, void *buffer, const char *mod_name ) { + int i, j, k, lodindex; + md4Header_t *pinmodel, *md4; + md4Frame_t *frame; + md4LOD_t *lod; + md4Surface_t *surf; + md4Triangle_t *tri; + md4Vertex_t *v; + int version; + int size; + shader_t *sh; + int frameSize; + + pinmodel = (md4Header_t *)buffer; + + version = LittleLong (pinmodel->version); + if (version != MD4_VERSION) { + ri.Printf( PRINT_WARNING, "R_LoadMD4: %s has wrong version (%i should be %i)\n", + mod_name, version, MD4_VERSION); + return qfalse; + } + + mod->type = MOD_MD4; + size = LittleLong(pinmodel->ofsEnd); + mod->dataSize += size; + mod->modelData = md4 = ri.Hunk_Alloc( size, h_low ); + + Com_Memcpy(md4, buffer, size); + + LL(md4->ident); + LL(md4->version); + LL(md4->numFrames); + LL(md4->numBones); + LL(md4->numLODs); + LL(md4->ofsFrames); + LL(md4->ofsLODs); + md4->ofsEnd = size; + + if ( md4->numFrames < 1 ) { + ri.Printf( PRINT_WARNING, "R_LoadMD4: %s has no frames\n", mod_name ); + return qfalse; + } + + // we don't need to swap tags in the renderer, they aren't used + + // swap all the frames + frameSize = (size_t)( &((md4Frame_t *)0)->bones[ md4->numBones ] ); + for ( i = 0 ; i < md4->numFrames ; i++) { + frame = (md4Frame_t *) ( (byte *)md4 + md4->ofsFrames + i * frameSize ); + frame->radius = LittleFloat( frame->radius ); + for ( j = 0 ; j < 3 ; j++ ) { + frame->bounds[0][j] = LittleFloat( frame->bounds[0][j] ); + frame->bounds[1][j] = LittleFloat( frame->bounds[1][j] ); + frame->localOrigin[j] = LittleFloat( frame->localOrigin[j] ); + } + for ( j = 0 ; j < md4->numBones * sizeof( md4Bone_t ) / 4 ; j++ ) { + ((float *)frame->bones)[j] = LittleFloat( ((float *)frame->bones)[j] ); + } + } + + // swap all the LOD's + lod = (md4LOD_t *) ( (byte *)md4 + md4->ofsLODs ); + for ( lodindex = 0 ; lodindex < md4->numLODs ; lodindex++ ) { + + // swap all the surfaces + surf = (md4Surface_t *) ( (byte *)lod + lod->ofsSurfaces ); + for ( i = 0 ; i < lod->numSurfaces ; i++) { + LL(surf->ident); + LL(surf->numTriangles); + LL(surf->ofsTriangles); + LL(surf->numVerts); + LL(surf->ofsVerts); + LL(surf->ofsEnd); + + if ( surf->numVerts > SHADER_MAX_VERTEXES ) { + ri.Printf(PRINT_WARNING, "R_LoadMD4: %s has more than %i verts on a surface (%i).\n", + mod_name, SHADER_MAX_VERTEXES, surf->numVerts ); + return qfalse; + } + if ( surf->numTriangles*3 > SHADER_MAX_INDEXES ) { + ri.Printf(PRINT_WARNING, "R_LoadMD4: %s has more than %i triangles on a surface (%i).\n", + mod_name, SHADER_MAX_INDEXES / 3, surf->numTriangles ); + return qfalse; + } + + // change to surface identifier + surf->ident = SF_MD4; + + // lowercase the surface name so skin compares are faster + Q_strlwr( surf->name ); + + // register the shaders + sh = R_FindShader( surf->shader, LIGHTMAP_NONE, qtrue ); + if ( sh->defaultShader ) { + surf->shaderIndex = 0; + } else { + surf->shaderIndex = sh->index; + } + + // swap all the triangles + tri = (md4Triangle_t *) ( (byte *)surf + surf->ofsTriangles ); + for ( j = 0 ; j < surf->numTriangles ; j++, tri++ ) { + LL(tri->indexes[0]); + LL(tri->indexes[1]); + LL(tri->indexes[2]); + } + + // swap all the vertexes + // FIXME + // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left + // in for reference. + //v = (md4Vertex_t *) ( (byte *)surf + surf->ofsVerts + 12); + v = (md4Vertex_t *) ( (byte *)surf + surf->ofsVerts); + for ( j = 0 ; j < surf->numVerts ; j++ ) { + v->normal[0] = LittleFloat( v->normal[0] ); + v->normal[1] = LittleFloat( v->normal[1] ); + v->normal[2] = LittleFloat( v->normal[2] ); + + v->texCoords[0] = LittleFloat( v->texCoords[0] ); + v->texCoords[1] = LittleFloat( v->texCoords[1] ); + + v->numWeights = LittleLong( v->numWeights ); + + for ( k = 0 ; k < v->numWeights ; k++ ) { + v->weights[k].boneIndex = LittleLong( v->weights[k].boneIndex ); + v->weights[k].boneWeight = LittleFloat( v->weights[k].boneWeight ); + v->weights[k].offset[0] = LittleFloat( v->weights[k].offset[0] ); + v->weights[k].offset[1] = LittleFloat( v->weights[k].offset[1] ); + v->weights[k].offset[2] = LittleFloat( v->weights[k].offset[2] ); + } + // FIXME + // This makes TFC's skeletons work. Shouldn't be necessary anymore, but left + // in for reference. + //v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights] + 12 ); + v = (md4Vertex_t *)( ( byte * )&v->weights[v->numWeights]); + } + + // find the next surface + surf = (md4Surface_t *)( (byte *)surf + surf->ofsEnd ); + } + + // find the next LOD + lod = (md4LOD_t *)( (byte *)lod + lod->ofsEnd ); + } + + return qtrue; +} + + + +//============================================================================= + +/* +** RE_BeginRegistration +*/ +void RE_BeginRegistration( glconfig_t *glconfigOut ) { + + R_Init(); + + *glconfigOut = glConfig; + + R_IssuePendingRenderCommands(); + + tr.visIndex = 0; + memset(tr.visClusters, -2, sizeof(tr.visClusters)); // force markleafs to regenerate + + R_ClearFlares(); + RE_ClearScene(); + + tr.registered = qtrue; + + // NOTE: this sucks, for some reason the first stretch pic is never drawn + // without this we'd see a white flash on a level load because the very + // first time the level shot would not be drawn +// RE_StretchPic(0, 0, 0, 0, 0, 0, 1, 1, 0); +} + +//============================================================================= + +/* +=============== +R_ModelInit +=============== +*/ +void R_ModelInit( void ) { + model_t *mod; + + // leave a space for NULL model + tr.numModels = 0; + + mod = R_AllocModel(); + mod->type = MOD_BAD; +} + + +/* +================ +R_Modellist_f +================ +*/ +void R_Modellist_f( void ) { + int i, j; + model_t *mod; + int total; + int lods; + + total = 0; + for ( i = 1 ; i < tr.numModels; i++ ) { + mod = tr.models[i]; + lods = 1; + for ( j = 1 ; j < MD3_MAX_LODS ; j++ ) { + if ( mod->mdv[j] && mod->mdv[j] != mod->mdv[j-1] ) { + lods++; + } + } + ri.Printf( PRINT_ALL, "%8i : (%i) %s\n",mod->dataSize, lods, mod->name ); + total += mod->dataSize; + } + ri.Printf( PRINT_ALL, "%8i : Total models\n", total ); + +#if 0 // not working right with new hunk + if ( tr.world ) { + ri.Printf( PRINT_ALL, "\n%8i : %s\n", tr.world->dataSize, tr.world->name ); + } +#endif +} + + +//============================================================================= + + +/* +================ +R_GetTag +================ +*/ +static mdvTag_t *R_GetTag( mdvModel_t *mod, int frame, const char *_tagName ) { + int i; + mdvTag_t *tag; + mdvTagName_t *tagName; + + if ( frame >= mod->numFrames ) { + // it is possible to have a bad frame while changing models, so don't error + frame = mod->numFrames - 1; + } + + tag = mod->tags + frame * mod->numTags; + tagName = mod->tagNames; + for(i = 0; i < mod->numTags; i++, tag++, tagName++) + { + if(!strcmp(tagName->name, _tagName)) + { + return tag; + } + } + + return NULL; +} + +#ifdef RAVENMD4 +void R_GetAnimTag( mdrHeader_t *mod, int framenum, const char *tagName, md3Tag_t * dest) +{ + int i, j, k; + int frameSize; + mdrFrame_t *frame; + mdrTag_t *tag; + + if ( framenum >= mod->numFrames ) + { + // it is possible to have a bad frame while changing models, so don't error + framenum = mod->numFrames - 1; + } + + tag = (mdrTag_t *)((byte *)mod + mod->ofsTags); + for ( i = 0 ; i < mod->numTags ; i++, tag++ ) + { + if ( !strcmp( tag->name, tagName ) ) + { + Q_strncpyz(dest->name, tag->name, sizeof(dest->name)); + + // uncompressed model... + // + frameSize = (intptr_t)( &((mdrFrame_t *)0)->bones[ mod->numBones ] ); + frame = (mdrFrame_t *)((byte *)mod + mod->ofsFrames + framenum * frameSize ); + + for (j = 0; j < 3; j++) + { + for (k = 0; k < 3; k++) + dest->axis[j][k]=frame->bones[tag->boneIndex].matrix[k][j]; + } + + dest->origin[0]=frame->bones[tag->boneIndex].matrix[0][3]; + dest->origin[1]=frame->bones[tag->boneIndex].matrix[1][3]; + dest->origin[2]=frame->bones[tag->boneIndex].matrix[2][3]; + + return; + } + } + + AxisClear( dest->axis ); + VectorClear( dest->origin ); + strcpy(dest->name,""); +} +#endif + +/* +================ +R_LerpTag +================ +*/ +int R_LerpTag( orientation_t *tag, qhandle_t handle, int startFrame, int endFrame, + float frac, const char *tagName ) { + mdvTag_t *start, *end; +#ifdef RAVENMD4 + md3Tag_t start_space, end_space; +#endif + int i; + float frontLerp, backLerp; + model_t *model; + + model = R_GetModelByHandle( handle ); + if ( !model->mdv[0] ) + { +#ifdef RAVENMD4 + if(model->type == MOD_MDR) + { + start = &start_space; + end = &end_space; + R_GetAnimTag((mdrHeader_t *) model->modelData, startFrame, tagName, start); + R_GetAnimTag((mdrHeader_t *) model->modelData, endFrame, tagName, end); + } + else +#endif + if( model->type == MOD_IQM ) { + return R_IQMLerpTag( tag, model->modelData, + startFrame, endFrame, + frac, tagName ); + } else { + + AxisClear( tag->axis ); + VectorClear( tag->origin ); + return qfalse; + + } + } + else + { + start = R_GetTag( model->mdv[0], startFrame, tagName ); + end = R_GetTag( model->mdv[0], endFrame, tagName ); + if ( !start || !end ) { + AxisClear( tag->axis ); + VectorClear( tag->origin ); + return qfalse; + } + } + + frontLerp = frac; + backLerp = 1.0f - frac; + + for ( i = 0 ; i < 3 ; i++ ) { + tag->origin[i] = start->origin[i] * backLerp + end->origin[i] * frontLerp; + tag->axis[0][i] = start->axis[0][i] * backLerp + end->axis[0][i] * frontLerp; + tag->axis[1][i] = start->axis[1][i] * backLerp + end->axis[1][i] * frontLerp; + tag->axis[2][i] = start->axis[2][i] * backLerp + end->axis[2][i] * frontLerp; + } + VectorNormalize( tag->axis[0] ); + VectorNormalize( tag->axis[1] ); + VectorNormalize( tag->axis[2] ); + return qtrue; +} + + +/* +==================== +R_ModelBounds +==================== +*/ +void R_ModelBounds( qhandle_t handle, vec3_t mins, vec3_t maxs ) { + model_t *model; + + model = R_GetModelByHandle( handle ); + + if(model->type == MOD_BRUSH) { + VectorCopy( model->bmodel->bounds[0], mins ); + VectorCopy( model->bmodel->bounds[1], maxs ); + + return; + } else if (model->type == MOD_MESH) { + mdvModel_t *header; + mdvFrame_t *frame; + + header = model->mdv[0]; + frame = header->frames; + + VectorCopy( frame->bounds[0], mins ); + VectorCopy( frame->bounds[1], maxs ); + + return; + } else if (model->type == MOD_MD4) { + md4Header_t *header; + md4Frame_t *frame; + + header = (md4Header_t *)model->modelData; + frame = (md4Frame_t *) ((byte *)header + header->ofsFrames); + + VectorCopy( frame->bounds[0], mins ); + VectorCopy( frame->bounds[1], maxs ); + + return; +#ifdef RAVENMD4 + } else if (model->type == MOD_MDR) { + mdrHeader_t *header; + mdrFrame_t *frame; + + header = (mdrHeader_t *)model->modelData; + frame = (mdrFrame_t *) ((byte *)header + header->ofsFrames); + + VectorCopy( frame->bounds[0], mins ); + VectorCopy( frame->bounds[1], maxs ); + + return; +#endif + } else if(model->type == MOD_IQM) { + iqmData_t *iqmData; + + iqmData = model->modelData; + + if(iqmData->bounds) + { + VectorCopy(iqmData->bounds, mins); + VectorCopy(iqmData->bounds + 3, maxs); + return; + } + } + + VectorClear( mins ); + VectorClear( maxs ); +} diff --git a/src/renderergl2/tr_model_iqm.c b/src/renderergl2/tr_model_iqm.c new file mode 100644 index 00000000..1f1bf747 --- /dev/null +++ b/src/renderergl2/tr_model_iqm.c @@ -0,0 +1,1058 @@ +/* +=========================================================================== +Copyright (C) 2011 Thilo Schulz +Copyright (C) 2011 Matthias Bentrup + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "tr_local.h" + +#define LL(x) x=LittleLong(x) + +static qboolean IQM_CheckRange( iqmHeader_t *header, int offset, + int count,int size ) { + // return true if the range specified by offset, count and size + // doesn't fit into the file + return ( count <= 0 || + offset < 0 || + offset > header->filesize || + offset + count * size < 0 || + offset + count * size > header->filesize ); +} +// "multiply" 3x4 matrices, these are assumed to be the top 3 rows +// of a 4x4 matrix with the last row = (0 0 0 1) +static void Matrix34Multiply( float *a, float *b, float *out ) { + out[ 0] = a[0] * b[0] + a[1] * b[4] + a[ 2] * b[ 8]; + out[ 1] = a[0] * b[1] + a[1] * b[5] + a[ 2] * b[ 9]; + out[ 2] = a[0] * b[2] + a[1] * b[6] + a[ 2] * b[10]; + out[ 3] = a[0] * b[3] + a[1] * b[7] + a[ 2] * b[11] + a[ 3]; + out[ 4] = a[4] * b[0] + a[5] * b[4] + a[ 6] * b[ 8]; + out[ 5] = a[4] * b[1] + a[5] * b[5] + a[ 6] * b[ 9]; + out[ 6] = a[4] * b[2] + a[5] * b[6] + a[ 6] * b[10]; + out[ 7] = a[4] * b[3] + a[5] * b[7] + a[ 6] * b[11] + a[ 7]; + out[ 8] = a[8] * b[0] + a[9] * b[4] + a[10] * b[ 8]; + out[ 9] = a[8] * b[1] + a[9] * b[5] + a[10] * b[ 9]; + out[10] = a[8] * b[2] + a[9] * b[6] + a[10] * b[10]; + out[11] = a[8] * b[3] + a[9] * b[7] + a[10] * b[11] + a[11]; +} +static void InterpolateMatrix( float *a, float *b, float lerp, float *mat ) { + float unLerp = 1.0f - lerp; + + mat[ 0] = a[ 0] * unLerp + b[ 0] * lerp; + mat[ 1] = a[ 1] * unLerp + b[ 1] * lerp; + mat[ 2] = a[ 2] * unLerp + b[ 2] * lerp; + mat[ 3] = a[ 3] * unLerp + b[ 3] * lerp; + mat[ 4] = a[ 4] * unLerp + b[ 4] * lerp; + mat[ 5] = a[ 5] * unLerp + b[ 5] * lerp; + mat[ 6] = a[ 6] * unLerp + b[ 6] * lerp; + mat[ 7] = a[ 7] * unLerp + b[ 7] * lerp; + mat[ 8] = a[ 8] * unLerp + b[ 8] * lerp; + mat[ 9] = a[ 9] * unLerp + b[ 9] * lerp; + mat[10] = a[10] * unLerp + b[10] * lerp; + mat[11] = a[11] * unLerp + b[11] * lerp; +} +static void JointToMatrix( vec4_t rot, vec3_t scale, vec3_t trans, + float *mat ) { + float xx = 2.0f * rot[0] * rot[0]; + float yy = 2.0f * rot[1] * rot[1]; + float zz = 2.0f * rot[2] * rot[2]; + float xy = 2.0f * rot[0] * rot[1]; + float xz = 2.0f * rot[0] * rot[2]; + float yz = 2.0f * rot[1] * rot[2]; + float wx = 2.0f * rot[3] * rot[0]; + float wy = 2.0f * rot[3] * rot[1]; + float wz = 2.0f * rot[3] * rot[2]; + + mat[ 0] = scale[0] * (1.0f - (yy + zz)); + mat[ 1] = scale[0] * (xy - wz); + mat[ 2] = scale[0] * (xz + wy); + mat[ 3] = trans[0]; + mat[ 4] = scale[1] * (xy + wz); + mat[ 5] = scale[1] * (1.0f - (xx + zz)); + mat[ 6] = scale[1] * (yz - wx); + mat[ 7] = trans[1]; + mat[ 8] = scale[2] * (xz - wy); + mat[ 9] = scale[2] * (yz + wx); + mat[10] = scale[2] * (1.0f - (xx + yy)); + mat[11] = trans[2]; +} +static void Matrix34Invert( float *inMat, float *outMat ) +{ + vec3_t trans; + float invSqrLen, *v; + + outMat[ 0] = inMat[ 0]; outMat[ 1] = inMat[ 4]; outMat[ 2] = inMat[ 8]; + outMat[ 4] = inMat[ 1]; outMat[ 5] = inMat[ 5]; outMat[ 6] = inMat[ 9]; + outMat[ 8] = inMat[ 2]; outMat[ 9] = inMat[ 6]; outMat[10] = inMat[10]; + + v = outMat + 0; invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); + v = outMat + 4; invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); + v = outMat + 8; invSqrLen = 1.0f / DotProduct(v, v); VectorScale(v, invSqrLen, v); + + trans[0] = inMat[ 3]; + trans[1] = inMat[ 7]; + trans[2] = inMat[11]; + + outMat[ 3] = -DotProduct(outMat + 0, trans); + outMat[ 7] = -DotProduct(outMat + 4, trans); + outMat[11] = -DotProduct(outMat + 8, trans); +} + +/* +================= +R_LoadIQM + +Load an IQM model and compute the joint matrices for every frame. +================= +*/ +qboolean R_LoadIQM( model_t *mod, void *buffer, int filesize, const char *mod_name ) { + iqmHeader_t *header; + iqmVertexArray_t *vertexarray; + iqmTriangle_t *triangle; + iqmMesh_t *mesh; + iqmJoint_t *joint; + iqmPose_t *pose; + iqmBounds_t *bounds; + unsigned short *framedata; + char *str; + int i, j; + float jointMats[IQM_MAX_JOINTS * 2 * 12]; + float *mat; + size_t size, joint_names; + iqmData_t *iqmData; + srfIQModel_t *surface; + + if( filesize < sizeof(iqmHeader_t) ) { + return qfalse; + } + + header = (iqmHeader_t *)buffer; + if( Q_strncmp( header->magic, IQM_MAGIC, sizeof(header->magic) ) ) { + return qfalse; + } + + LL( header->version ); + if( header->version != IQM_VERSION ) { + ri.Printf(PRINT_WARNING, "R_LoadIQM: %s is a unsupported IQM version (%d), only version %d is supported.\n", + mod_name, header->version, IQM_VERSION); + return qfalse; + } + + LL( header->filesize ); + if( header->filesize > filesize || header->filesize > 16<<20 ) { + return qfalse; + } + + LL( header->flags ); + LL( header->num_text ); + LL( header->ofs_text ); + LL( header->num_meshes ); + LL( header->ofs_meshes ); + LL( header->num_vertexarrays ); + LL( header->num_vertexes ); + LL( header->ofs_vertexarrays ); + LL( header->num_triangles ); + LL( header->ofs_triangles ); + LL( header->ofs_adjacency ); + LL( header->num_joints ); + LL( header->ofs_joints ); + LL( header->num_poses ); + LL( header->ofs_poses ); + LL( header->num_anims ); + LL( header->ofs_anims ); + LL( header->num_frames ); + LL( header->num_framechannels ); + LL( header->ofs_frames ); + LL( header->ofs_bounds ); + LL( header->num_comment ); + LL( header->ofs_comment ); + LL( header->num_extensions ); + LL( header->ofs_extensions ); + + // check ioq3 joint limit + if ( header->num_joints > IQM_MAX_JOINTS ) { + ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %d joints (%d).\n", + mod_name, IQM_MAX_JOINTS, header->num_joints); + return qfalse; + } + + // check and swap vertex arrays + if( IQM_CheckRange( header, header->ofs_vertexarrays, + header->num_vertexarrays, + sizeof(iqmVertexArray_t) ) ) { + return qfalse; + } + vertexarray = (iqmVertexArray_t *)((byte *)header + header->ofs_vertexarrays); + for( i = 0; i < header->num_vertexarrays; i++, vertexarray++ ) { + int j, n, *intPtr; + + if( vertexarray->size <= 0 || vertexarray->size > 4 ) { + return qfalse; + } + + // total number of values + n = header->num_vertexes * vertexarray->size; + + switch( vertexarray->format ) { + case IQM_BYTE: + case IQM_UBYTE: + // 1 byte, no swapping necessary + if( IQM_CheckRange( header, vertexarray->offset, + n, sizeof(byte) ) ) { + return qfalse; + } + break; + case IQM_INT: + case IQM_UINT: + case IQM_FLOAT: + // 4-byte swap + if( IQM_CheckRange( header, vertexarray->offset, + n, sizeof(float) ) ) { + return qfalse; + } + intPtr = (int *)((byte *)header + vertexarray->offset); + for( j = 0; j < n; j++, intPtr++ ) { + LL( *intPtr ); + } + break; + default: + // not supported + return qfalse; + break; + } + + switch( vertexarray->type ) { + case IQM_POSITION: + case IQM_NORMAL: + if( vertexarray->format != IQM_FLOAT || + vertexarray->size != 3 ) { + return qfalse; + } + break; + case IQM_TANGENT: + if( vertexarray->format != IQM_FLOAT || + vertexarray->size != 4 ) { + return qfalse; + } + break; + case IQM_TEXCOORD: + if( vertexarray->format != IQM_FLOAT || + vertexarray->size != 2 ) { + return qfalse; + } + break; + case IQM_BLENDINDEXES: + case IQM_BLENDWEIGHTS: + if( vertexarray->format != IQM_UBYTE || + vertexarray->size != 4 ) { + return qfalse; + } + break; + case IQM_COLOR: + if( vertexarray->format != IQM_UBYTE || + vertexarray->size != 4 ) { + return qfalse; + } + break; + } + } + + // check and swap triangles + if( IQM_CheckRange( header, header->ofs_triangles, + header->num_triangles, sizeof(iqmTriangle_t) ) ) { + return qfalse; + } + triangle = (iqmTriangle_t *)((byte *)header + header->ofs_triangles); + for( i = 0; i < header->num_triangles; i++, triangle++ ) { + LL( triangle->vertex[0] ); + LL( triangle->vertex[1] ); + LL( triangle->vertex[2] ); + + if( triangle->vertex[0] > header->num_vertexes || + triangle->vertex[1] > header->num_vertexes || + triangle->vertex[2] > header->num_vertexes ) { + return qfalse; + } + } + + // check and swap meshes + if( IQM_CheckRange( header, header->ofs_meshes, + header->num_meshes, sizeof(iqmMesh_t) ) ) { + return qfalse; + } + mesh = (iqmMesh_t *)((byte *)header + header->ofs_meshes); + for( i = 0; i < header->num_meshes; i++, mesh++) { + LL( mesh->name ); + LL( mesh->material ); + LL( mesh->first_vertex ); + LL( mesh->num_vertexes ); + LL( mesh->first_triangle ); + LL( mesh->num_triangles ); + + // check ioq3 limits + if ( mesh->num_vertexes > SHADER_MAX_VERTEXES ) + { + ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i verts on a surface (%i).\n", + mod_name, SHADER_MAX_VERTEXES, mesh->num_vertexes ); + return qfalse; + } + if ( mesh->num_triangles*3 > SHADER_MAX_INDEXES ) + { + ri.Printf(PRINT_WARNING, "R_LoadIQM: %s has more than %i triangles on a surface (%i).\n", + mod_name, SHADER_MAX_INDEXES / 3, mesh->num_triangles ); + return qfalse; + } + + if( mesh->first_vertex >= header->num_vertexes || + mesh->first_vertex + mesh->num_vertexes > header->num_vertexes || + mesh->first_triangle >= header->num_triangles || + mesh->first_triangle + mesh->num_triangles > header->num_triangles || + mesh->name >= header->num_text || + mesh->material >= header->num_text ) { + return qfalse; + } + } + + // check and swap joints + if( IQM_CheckRange( header, header->ofs_joints, + header->num_joints, sizeof(iqmJoint_t) ) ) { + return qfalse; + } + joint = (iqmJoint_t *)((byte *)header + header->ofs_joints); + joint_names = 0; + for( i = 0; i < header->num_joints; i++, joint++ ) { + LL( joint->name ); + LL( joint->parent ); + LL( joint->translate[0] ); + LL( joint->translate[1] ); + LL( joint->translate[2] ); + LL( joint->rotate[0] ); + LL( joint->rotate[1] ); + LL( joint->rotate[2] ); + LL( joint->rotate[3] ); + LL( joint->scale[0] ); + LL( joint->scale[1] ); + LL( joint->scale[2] ); + + if( joint->parent < -1 || + joint->parent >= (int)header->num_joints || + joint->name >= (int)header->num_text ) { + return qfalse; + } + joint_names += strlen( (char *)header + header->ofs_text + + joint->name ) + 1; + } + + // check and swap poses + if( header->num_poses != header->num_joints ) { + return qfalse; + } + if( IQM_CheckRange( header, header->ofs_poses, + header->num_poses, sizeof(iqmPose_t) ) ) { + return qfalse; + } + pose = (iqmPose_t *)((byte *)header + header->ofs_poses); + for( i = 0; i < header->num_poses; i++, pose++ ) { + LL( pose->parent ); + LL( pose->mask ); + LL( pose->channeloffset[0] ); + LL( pose->channeloffset[1] ); + LL( pose->channeloffset[2] ); + LL( pose->channeloffset[3] ); + LL( pose->channeloffset[4] ); + LL( pose->channeloffset[5] ); + LL( pose->channeloffset[6] ); + LL( pose->channeloffset[7] ); + LL( pose->channeloffset[8] ); + LL( pose->channeloffset[9] ); + LL( pose->channelscale[0] ); + LL( pose->channelscale[1] ); + LL( pose->channelscale[2] ); + LL( pose->channelscale[3] ); + LL( pose->channelscale[4] ); + LL( pose->channelscale[5] ); + LL( pose->channelscale[6] ); + LL( pose->channelscale[7] ); + LL( pose->channelscale[8] ); + LL( pose->channelscale[9] ); + } + + if (header->ofs_bounds) + { + // check and swap model bounds + if(IQM_CheckRange(header, header->ofs_bounds, + header->num_frames, sizeof(*bounds))) + { + return qfalse; + } + bounds = (iqmBounds_t *) ((byte *) header + header->ofs_bounds); + for(i = 0; i < header->num_frames; i++) + { + LL(bounds->bbmin[0]); + LL(bounds->bbmin[1]); + LL(bounds->bbmin[2]); + LL(bounds->bbmax[0]); + LL(bounds->bbmax[1]); + LL(bounds->bbmax[2]); + + bounds++; + } + } + + // allocate the model and copy the data + size = sizeof(iqmData_t); + size += header->num_meshes * sizeof( srfIQModel_t ); + size += header->num_joints * header->num_frames * 12 * sizeof( float ); + if(header->ofs_bounds) + size += header->num_frames * 6 * sizeof(float); // model bounds + size += header->num_vertexes * 3 * sizeof(float); // positions + size += header->num_vertexes * 2 * sizeof(float); // texcoords + size += header->num_vertexes * 3 * sizeof(float); // normals + size += header->num_vertexes * 4 * sizeof(float); // tangents + size += header->num_vertexes * 4 * sizeof(byte); // blendIndexes + size += header->num_vertexes * 4 * sizeof(byte); // blendWeights + size += header->num_vertexes * 4 * sizeof(byte); // colors + size += header->num_joints * sizeof(int); // parents + size += header->num_triangles * 3 * sizeof(int); // triangles + size += joint_names; // joint names + + mod->type = MOD_IQM; + iqmData = (iqmData_t *)ri.Hunk_Alloc( size, h_low ); + mod->modelData = iqmData; + + // fill header + iqmData->num_vertexes = header->num_vertexes; + iqmData->num_triangles = header->num_triangles; + iqmData->num_frames = header->num_frames; + iqmData->num_surfaces = header->num_meshes; + iqmData->num_joints = header->num_joints; + iqmData->surfaces = (srfIQModel_t *)(iqmData + 1); + iqmData->poseMats = (float *) (iqmData->surfaces + iqmData->num_surfaces); + if(header->ofs_bounds) + { + iqmData->bounds = iqmData->poseMats + 12 * header->num_joints * header->num_frames; + iqmData->positions = iqmData->bounds + 6 * header->num_frames; + } + else + iqmData->positions = iqmData->poseMats + 12 * header->num_joints * header->num_frames; + iqmData->texcoords = iqmData->positions + 3 * header->num_vertexes; + iqmData->normals = iqmData->texcoords + 2 * header->num_vertexes; + iqmData->tangents = iqmData->normals + 3 * header->num_vertexes; + iqmData->blendIndexes = (byte *)(iqmData->tangents + 4 * header->num_vertexes); + iqmData->blendWeights = iqmData->blendIndexes + 4 * header->num_vertexes; + iqmData->colors = iqmData->blendWeights + 4 * header->num_vertexes; + iqmData->jointParents = (int *)(iqmData->colors + 4 * header->num_vertexes); + iqmData->triangles = iqmData->jointParents + header->num_joints; + iqmData->names = (char *)(iqmData->triangles + 3 * header->num_triangles); + + // calculate joint matrices and their inverses + // they are needed only until the pose matrices are calculated + mat = jointMats; + joint = (iqmJoint_t *)((byte *)header + header->ofs_joints); + for( i = 0; i < header->num_joints; i++, joint++ ) { + float baseFrame[12], invBaseFrame[12]; + + JointToMatrix( joint->rotate, joint->scale, joint->translate, baseFrame ); + Matrix34Invert( baseFrame, invBaseFrame ); + + if ( joint->parent >= 0 ) + { + Matrix34Multiply( jointMats + 2 * 12 * joint->parent, baseFrame, mat ); + mat += 12; + Matrix34Multiply( invBaseFrame, jointMats + 2 * 12 * joint->parent + 12, mat ); + mat += 12; + } + else + { + Com_Memcpy( mat, baseFrame, sizeof(baseFrame) ); + mat += 12; + Com_Memcpy( mat, invBaseFrame, sizeof(invBaseFrame) ); + mat += 12; + } + } + + // calculate pose matrices + framedata = (unsigned short *)((byte *)header + header->ofs_frames); + mat = iqmData->poseMats; + for( i = 0; i < header->num_frames; i++ ) { + pose = (iqmPose_t *)((byte *)header + header->ofs_poses); + for( j = 0; j < header->num_poses; j++, pose++ ) { + vec3_t translate; + vec4_t rotate; + vec3_t scale; + float mat1[12], mat2[12]; + + translate[0] = pose->channeloffset[0]; + if( pose->mask & 0x001) + translate[0] += *framedata++ * pose->channelscale[0]; + translate[1] = pose->channeloffset[1]; + if( pose->mask & 0x002) + translate[1] += *framedata++ * pose->channelscale[1]; + translate[2] = pose->channeloffset[2]; + if( pose->mask & 0x004) + translate[2] += *framedata++ * pose->channelscale[2]; + + rotate[0] = pose->channeloffset[3]; + if( pose->mask & 0x008) + rotate[0] += *framedata++ * pose->channelscale[3]; + rotate[1] = pose->channeloffset[4]; + if( pose->mask & 0x010) + rotate[1] += *framedata++ * pose->channelscale[4]; + rotate[2] = pose->channeloffset[5]; + if( pose->mask & 0x020) + rotate[2] += *framedata++ * pose->channelscale[5]; + rotate[3] = pose->channeloffset[6]; + if( pose->mask & 0x040) + rotate[3] += *framedata++ * pose->channelscale[6]; + + scale[0] = pose->channeloffset[7]; + if( pose->mask & 0x080) + scale[0] += *framedata++ * pose->channelscale[7]; + scale[1] = pose->channeloffset[8]; + if( pose->mask & 0x100) + scale[1] += *framedata++ * pose->channelscale[8]; + scale[2] = pose->channeloffset[9]; + if( pose->mask & 0x200) + scale[2] += *framedata++ * pose->channelscale[9]; + + // construct transformation matrix + JointToMatrix( rotate, scale, translate, mat1 ); + + if( pose->parent >= 0 ) { + Matrix34Multiply( jointMats + 12 * 2 * pose->parent, + mat1, mat2 ); + } else { + Com_Memcpy( mat2, mat1, sizeof(mat1) ); + } + + Matrix34Multiply( mat2, jointMats + 12 * (2 * j + 1), mat ); + mat += 12; + } + } + + // register shaders + // overwrite the material offset with the shader index + mesh = (iqmMesh_t *)((byte *)header + header->ofs_meshes); + surface = iqmData->surfaces; + str = (char *)header + header->ofs_text; + for( i = 0; i < header->num_meshes; i++, mesh++, surface++ ) { + surface->surfaceType = SF_IQM; + Q_strncpyz(surface->name, str + mesh->name, sizeof (surface->name)); + Q_strlwr(surface->name); // lowercase the surface name so skin compares are faster + surface->shader = R_FindShader( str + mesh->material, LIGHTMAP_NONE, qtrue ); + if( surface->shader->defaultShader ) + surface->shader = tr.defaultShader; + surface->data = iqmData; + surface->first_vertex = mesh->first_vertex; + surface->num_vertexes = mesh->num_vertexes; + surface->first_triangle = mesh->first_triangle; + surface->num_triangles = mesh->num_triangles; + } + + // copy vertexarrays and indexes + vertexarray = (iqmVertexArray_t *)((byte *)header + header->ofs_vertexarrays); + for( i = 0; i < header->num_vertexarrays; i++, vertexarray++ ) { + int n; + + // total number of values + n = header->num_vertexes * vertexarray->size; + + switch( vertexarray->type ) { + case IQM_POSITION: + Com_Memcpy( iqmData->positions, + (byte *)header + vertexarray->offset, + n * sizeof(float) ); + break; + case IQM_NORMAL: + Com_Memcpy( iqmData->normals, + (byte *)header + vertexarray->offset, + n * sizeof(float) ); + break; + case IQM_TANGENT: + Com_Memcpy( iqmData->tangents, + (byte *)header + vertexarray->offset, + n * sizeof(float) ); + break; + case IQM_TEXCOORD: + Com_Memcpy( iqmData->texcoords, + (byte *)header + vertexarray->offset, + n * sizeof(float) ); + break; + case IQM_BLENDINDEXES: + Com_Memcpy( iqmData->blendIndexes, + (byte *)header + vertexarray->offset, + n * sizeof(byte) ); + break; + case IQM_BLENDWEIGHTS: + Com_Memcpy( iqmData->blendWeights, + (byte *)header + vertexarray->offset, + n * sizeof(byte) ); + break; + case IQM_COLOR: + Com_Memcpy( iqmData->colors, + (byte *)header + vertexarray->offset, + n * sizeof(byte) ); + break; + } + } + + // copy joint parents + joint = (iqmJoint_t *)((byte *)header + header->ofs_joints); + for( i = 0; i < header->num_joints; i++, joint++ ) { + iqmData->jointParents[i] = joint->parent; + } + + // copy triangles + triangle = (iqmTriangle_t *)((byte *)header + header->ofs_triangles); + for( i = 0; i < header->num_triangles; i++, triangle++ ) { + iqmData->triangles[3*i+0] = triangle->vertex[0]; + iqmData->triangles[3*i+1] = triangle->vertex[1]; + iqmData->triangles[3*i+2] = triangle->vertex[2]; + } + + // copy joint names + str = iqmData->names; + joint = (iqmJoint_t *)((byte *)header + header->ofs_joints); + for( i = 0; i < header->num_joints; i++, joint++ ) { + char *name = (char *)header + header->ofs_text + + joint->name; + int len = strlen( name ) + 1; + Com_Memcpy( str, name, len ); + str += len; + } + + // copy model bounds + if(header->ofs_bounds) + { + mat = iqmData->bounds; + bounds = (iqmBounds_t *) ((byte *) header + header->ofs_bounds); + for(i = 0; i < header->num_frames; i++) + { + mat[0] = bounds->bbmin[0]; + mat[1] = bounds->bbmin[1]; + mat[2] = bounds->bbmin[2]; + mat[3] = bounds->bbmax[0]; + mat[4] = bounds->bbmax[1]; + mat[5] = bounds->bbmax[2]; + + mat += 6; + bounds++; + } + } + + return qtrue; +} + +/* +============= +R_CullIQM +============= +*/ +static int R_CullIQM( iqmData_t *data, trRefEntity_t *ent ) { + vec3_t bounds[2]; + vec_t *oldBounds, *newBounds; + int i; + + if (!data->bounds) { + tr.pc.c_box_cull_md3_clip++; + return CULL_CLIP; + } + + // compute bounds pointers + oldBounds = data->bounds + 6*ent->e.oldframe; + newBounds = data->bounds + 6*ent->e.frame; + + // calculate a bounding box in the current coordinate system + for (i = 0 ; i < 3 ; i++) { + bounds[0][i] = oldBounds[i] < newBounds[i] ? oldBounds[i] : newBounds[i]; + bounds[1][i] = oldBounds[i+3] > newBounds[i+3] ? oldBounds[i+3] : newBounds[i+3]; + } + + switch ( R_CullLocalBox( bounds ) ) + { + case CULL_IN: + tr.pc.c_box_cull_md3_in++; + return CULL_IN; + case CULL_CLIP: + tr.pc.c_box_cull_md3_clip++; + return CULL_CLIP; + case CULL_OUT: + default: + tr.pc.c_box_cull_md3_out++; + return CULL_OUT; + } +} + +/* +================= +R_ComputeIQMFogNum + +================= +*/ +int R_ComputeIQMFogNum( iqmData_t *data, trRefEntity_t *ent ) { + int i, j; + fog_t *fog; + const vec_t *bounds; + const vec_t defaultBounds[6] = { -8, -8, -8, 8, 8, 8 }; + vec3_t diag, center; + vec3_t localOrigin; + vec_t radius; + + if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { + return 0; + } + + // FIXME: non-normalized axis issues + if (data->bounds) { + bounds = data->bounds + 6*ent->e.frame; + } else { + bounds = defaultBounds; + } + VectorSubtract( bounds+3, bounds, diag ); + VectorMA( bounds, 0.5f, diag, center ); + VectorAdd( ent->e.origin, center, localOrigin ); + radius = 0.5f * VectorLength( diag ); + + for ( i = 1 ; i < tr.world->numfogs ; i++ ) { + fog = &tr.world->fogs[i]; + for ( j = 0 ; j < 3 ; j++ ) { + if ( localOrigin[j] - radius >= fog->bounds[1][j] ) { + break; + } + if ( localOrigin[j] + radius <= fog->bounds[0][j] ) { + break; + } + } + if ( j == 3 ) { + return i; + } + } + + return 0; +} + +/* +================= +R_AddIQMSurfaces + +Add all surfaces of this model +================= +*/ +void R_AddIQMSurfaces( trRefEntity_t *ent ) { + iqmData_t *data; + srfIQModel_t *surface; + int i, j; + qboolean personalModel; + int cull; + int fogNum; + shader_t *shader; + skin_t *skin; + + data = tr.currentModel->modelData; + surface = data->surfaces; + + // don't add third_person objects if not in a portal + personalModel = (ent->e.renderfx & RF_THIRD_PERSON) && !tr.viewParms.isPortal; + + if ( ent->e.renderfx & RF_WRAP_FRAMES ) { + ent->e.frame %= data->num_frames; + ent->e.oldframe %= data->num_frames; + } + + // + // Validate the frames so there is no chance of a crash. + // This will write directly into the entity structure, so + // when the surfaces are rendered, they don't need to be + // range checked again. + // + if ( (ent->e.frame >= data->num_frames) + || (ent->e.frame < 0) + || (ent->e.oldframe >= data->num_frames) + || (ent->e.oldframe < 0) ) { + ri.Printf( PRINT_DEVELOPER, "R_AddIQMSurfaces: no such frame %d to %d for '%s'\n", + ent->e.oldframe, ent->e.frame, + tr.currentModel->name ); + ent->e.frame = 0; + ent->e.oldframe = 0; + } + + // + // cull the entire model if merged bounding box of both frames + // is outside the view frustum. + // + cull = R_CullIQM ( data, ent ); + if ( cull == CULL_OUT ) { + return; + } + + // + // set up lighting now that we know we aren't culled + // + if ( !personalModel || r_shadows->integer > 1 ) { + R_SetupEntityLighting( &tr.refdef, ent ); + } + + // + // see if we are in a fog volume + // + fogNum = R_ComputeIQMFogNum( data, ent ); + + for ( i = 0 ; i < data->num_surfaces ; i++ ) { + if(ent->e.customShader) + shader = R_GetShaderByHandle( ent->e.customShader ); + else if(ent->e.customSkin > 0 && ent->e.customSkin < tr.numSkins) + { + skin = R_GetSkinByHandle(ent->e.customSkin); + shader = tr.defaultShader; + + for(j = 0; j < skin->numSurfaces; j++) + { + if (!strcmp(skin->surfaces[j]->name, surface->name)) + { + shader = skin->surfaces[j]->shader; + break; + } + } + } else { + shader = surface->shader; + } + + // we will add shadows even if the main object isn't visible in the view + + // stencil shadows can't do personal models unless I polyhedron clip + if ( !personalModel + && r_shadows->integer == 2 + && fogNum == 0 + && !(ent->e.renderfx & ( RF_NOSHADOW | RF_DEPTHHACK ) ) + && shader->sort == SS_OPAQUE ) { + R_AddDrawSurf( (void *)surface, tr.shadowShader, 0, 0, 0 ); + } + + // projection shadows work fine with personal models + if ( r_shadows->integer == 3 + && fogNum == 0 + && (ent->e.renderfx & RF_SHADOW_PLANE ) + && shader->sort == SS_OPAQUE ) { + R_AddDrawSurf( (void *)surface, tr.projectionShadowShader, 0, 0, 0 ); + } + + if( !personalModel ) { + R_AddDrawSurf( (void *)surface, shader, fogNum, 0, 0 ); + } + + surface++; + } +} + + +static void ComputeJointMats( iqmData_t *data, int frame, int oldframe, + float backlerp, float *mat ) { + float *mat1, *mat2; + int *joint = data->jointParents; + int i; + + if ( oldframe == frame ) { + mat1 = data->poseMats + 12 * data->num_joints * frame; + for( i = 0; i < data->num_joints; i++, joint++ ) { + if( *joint >= 0 ) { + Matrix34Multiply( mat + 12 * *joint, + mat1 + 12*i, mat + 12*i ); + } else { + Com_Memcpy( mat + 12*i, mat1 + 12*i, 12 * sizeof(float) ); + } + } + } else { + mat1 = data->poseMats + 12 * data->num_joints * frame; + mat2 = data->poseMats + 12 * data->num_joints * oldframe; + + for( i = 0; i < data->num_joints; i++, joint++ ) { + if( *joint >= 0 ) { + float tmpMat[12]; + InterpolateMatrix( mat1 + 12*i, mat2 + 12*i, + backlerp, tmpMat ); + Matrix34Multiply( mat + 12 * *joint, + tmpMat, mat + 12*i ); + + } else { + InterpolateMatrix( mat1 + 12*i, mat2 + 12*i, + backlerp, mat ); + } + } + } +} + + +/* +================= +RB_AddIQMSurfaces + +Compute vertices for this model surface +================= +*/ +void RB_IQMSurfaceAnim( surfaceType_t *surface ) { + srfIQModel_t *surf = (srfIQModel_t *)surface; + iqmData_t *data = surf->data; + float jointMats[IQM_MAX_JOINTS * 12]; + int i; + + vec4_t *outXYZ = &tess.xyz[tess.numVertexes]; + vec4_t *outNormal = &tess.normal[tess.numVertexes]; + vec2_t (*outTexCoord)[2] = &tess.texCoords[tess.numVertexes]; + vec4_t *outColor = &tess.vertexColors[tess.numVertexes]; + + int frame = backEnd.currentEntity->e.frame % data->num_frames; + int oldframe = backEnd.currentEntity->e.oldframe % data->num_frames; + float backlerp = backEnd.currentEntity->e.backlerp; + + int *tri; + glIndex_t *ptr; + glIndex_t base; + + RB_CHECKOVERFLOW( surf->num_vertexes, surf->num_triangles * 3 ); + + // compute interpolated joint matrices + ComputeJointMats( data, frame, oldframe, backlerp, jointMats ); + + // transform vertexes and fill other data + for( i = 0; i < surf->num_vertexes; + i++, outXYZ++, outNormal++, outTexCoord++, outColor++ ) { + int j, k; + float vtxMat[12]; + float nrmMat[9]; + int vtx = i + surf->first_vertex; + + // compute the vertex matrix by blending the up to + // four blend weights + for( k = 0; k < 12; k++ ) + vtxMat[k] = data->blendWeights[4*vtx] + * jointMats[12*data->blendIndexes[4*vtx] + k]; + for( j = 1; j < 4; j++ ) { + if( data->blendWeights[4*vtx + j] <= 0 ) + break; + for( k = 0; k < 12; k++ ) + vtxMat[k] += data->blendWeights[4*vtx + j] + * jointMats[12*data->blendIndexes[4*vtx + j] + k]; + } + for( k = 0; k < 12; k++ ) + vtxMat[k] *= 1.0f / 255.0f; + + // compute the normal matrix as transpose of the adjoint + // of the vertex matrix + nrmMat[ 0] = vtxMat[ 5]*vtxMat[10] - vtxMat[ 6]*vtxMat[ 9]; + nrmMat[ 1] = vtxMat[ 6]*vtxMat[ 8] - vtxMat[ 4]*vtxMat[10]; + nrmMat[ 2] = vtxMat[ 4]*vtxMat[ 9] - vtxMat[ 5]*vtxMat[ 8]; + nrmMat[ 3] = vtxMat[ 2]*vtxMat[ 9] - vtxMat[ 1]*vtxMat[10]; + nrmMat[ 4] = vtxMat[ 0]*vtxMat[10] - vtxMat[ 2]*vtxMat[ 8]; + nrmMat[ 5] = vtxMat[ 1]*vtxMat[ 8] - vtxMat[ 0]*vtxMat[ 9]; + nrmMat[ 6] = vtxMat[ 1]*vtxMat[ 6] - vtxMat[ 2]*vtxMat[ 5]; + nrmMat[ 7] = vtxMat[ 2]*vtxMat[ 4] - vtxMat[ 0]*vtxMat[ 6]; + nrmMat[ 8] = vtxMat[ 0]*vtxMat[ 5] - vtxMat[ 1]*vtxMat[ 4]; + + (*outTexCoord)[0][0] = data->texcoords[2*vtx + 0]; + (*outTexCoord)[0][1] = data->texcoords[2*vtx + 1]; + (*outTexCoord)[1][0] = (*outTexCoord)[0][0]; + (*outTexCoord)[1][1] = (*outTexCoord)[0][1]; + + (*outXYZ)[0] = + vtxMat[ 0] * data->positions[3*vtx+0] + + vtxMat[ 1] * data->positions[3*vtx+1] + + vtxMat[ 2] * data->positions[3*vtx+2] + + vtxMat[ 3]; + (*outXYZ)[1] = + vtxMat[ 4] * data->positions[3*vtx+0] + + vtxMat[ 5] * data->positions[3*vtx+1] + + vtxMat[ 6] * data->positions[3*vtx+2] + + vtxMat[ 7]; + (*outXYZ)[2] = + vtxMat[ 8] * data->positions[3*vtx+0] + + vtxMat[ 9] * data->positions[3*vtx+1] + + vtxMat[10] * data->positions[3*vtx+2] + + vtxMat[11]; + (*outXYZ)[3] = 1.0f; + + (*outNormal)[0] = + nrmMat[ 0] * data->normals[3*vtx+0] + + nrmMat[ 1] * data->normals[3*vtx+1] + + nrmMat[ 2] * data->normals[3*vtx+2]; + (*outNormal)[1] = + nrmMat[ 3] * data->normals[3*vtx+0] + + nrmMat[ 4] * data->normals[3*vtx+1] + + nrmMat[ 5] * data->normals[3*vtx+2]; + (*outNormal)[2] = + nrmMat[ 6] * data->normals[3*vtx+0] + + nrmMat[ 7] * data->normals[3*vtx+1] + + nrmMat[ 8] * data->normals[3*vtx+2]; + (*outNormal)[3] = 0.0f; + + (*outColor)[0] = data->colors[4*vtx+0] / 255.0f; + (*outColor)[1] = data->colors[4*vtx+1] / 255.0f; + (*outColor)[2] = data->colors[4*vtx+2] / 255.0f; + (*outColor)[3] = data->colors[4*vtx+3] / 255.0f; + } + + tri = data->triangles + 3 * surf->first_triangle; + ptr = &tess.indexes[tess.numIndexes]; + base = tess.numVertexes; + + for( i = 0; i < surf->num_triangles; i++ ) { + *ptr++ = base + (*tri++ - surf->first_vertex); + *ptr++ = base + (*tri++ - surf->first_vertex); + *ptr++ = base + (*tri++ - surf->first_vertex); + } + + tess.numIndexes += 3 * surf->num_triangles; + tess.numVertexes += surf->num_vertexes; +} + +int R_IQMLerpTag( orientation_t *tag, iqmData_t *data, + int startFrame, int endFrame, + float frac, const char *tagName ) { + float jointMats[IQM_MAX_JOINTS * 12]; + int joint; + char *names = data->names; + + // get joint number by reading the joint names + for( joint = 0; joint < data->num_joints; joint++ ) { + if( !strcmp( tagName, names ) ) + break; + names += strlen( names ) + 1; + } + if( joint >= data->num_joints ) { + AxisClear( tag->axis ); + VectorClear( tag->origin ); + return qfalse; + } + + ComputeJointMats( data, startFrame, endFrame, frac, jointMats ); + + tag->axis[0][0] = jointMats[12 * joint + 0]; + tag->axis[1][0] = jointMats[12 * joint + 1]; + tag->axis[2][0] = jointMats[12 * joint + 2]; + tag->origin[0] = jointMats[12 * joint + 3]; + tag->axis[0][1] = jointMats[12 * joint + 4]; + tag->axis[1][1] = jointMats[12 * joint + 5]; + tag->axis[2][1] = jointMats[12 * joint + 6]; + tag->origin[1] = jointMats[12 * joint + 7]; + tag->axis[0][2] = jointMats[12 * joint + 8]; + tag->axis[1][2] = jointMats[12 * joint + 9]; + tag->axis[2][2] = jointMats[12 * joint + 10]; + tag->origin[2] = jointMats[12 * joint + 11]; + + return qtrue; +} diff --git a/src/renderergl2/tr_noise.c b/src/renderergl2/tr_noise.c new file mode 100644 index 00000000..40eafc33 --- /dev/null +++ b/src/renderergl2/tr_noise.c @@ -0,0 +1,93 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_noise.c +#include "../qcommon/q_shared.h" +#include "../qcommon/qfiles.h" +#include "../qcommon/qcommon.h" + +#define NOISE_SIZE 256 +#define NOISE_MASK ( NOISE_SIZE - 1 ) + +#define VAL( a ) s_noise_perm[ ( a ) & ( NOISE_MASK )] +#define INDEX( x, y, z, t ) VAL( x + VAL( y + VAL( z + VAL( t ) ) ) ) + +static float s_noise_table[NOISE_SIZE]; +static int s_noise_perm[NOISE_SIZE]; + +static float GetNoiseValue( int x, int y, int z, int t ) +{ + int index = INDEX( ( int ) x, ( int ) y, ( int ) z, ( int ) t ); + + return s_noise_table[index]; +} + +void R_NoiseInit( void ) +{ + int i; + + for ( i = 0; i < NOISE_SIZE; i++ ) + { + s_noise_table[i] = ( float ) ( ( ( rand() / ( float ) RAND_MAX ) * 2.0 - 1.0 ) ); + s_noise_perm[i] = ( unsigned char ) ( rand() / ( float ) RAND_MAX * 255 ); + } +} + +float R_NoiseGet4f( float x, float y, float z, float t ) +{ + int i; + int ix, iy, iz, it; + float fx, fy, fz, ft; + float front[4]; + float back[4]; + float fvalue, bvalue, value[2], finalvalue; + + ix = ( int ) floor( x ); + fx = x - ix; + iy = ( int ) floor( y ); + fy = y - iy; + iz = ( int ) floor( z ); + fz = z - iz; + it = ( int ) floor( t ); + ft = t - it; + + for ( i = 0; i < 2; i++ ) + { + front[0] = GetNoiseValue( ix, iy, iz, it + i ); + front[1] = GetNoiseValue( ix+1, iy, iz, it + i ); + front[2] = GetNoiseValue( ix, iy+1, iz, it + i ); + front[3] = GetNoiseValue( ix+1, iy+1, iz, it + i ); + + back[0] = GetNoiseValue( ix, iy, iz + 1, it + i ); + back[1] = GetNoiseValue( ix+1, iy, iz + 1, it + i ); + back[2] = GetNoiseValue( ix, iy+1, iz + 1, it + i ); + back[3] = GetNoiseValue( ix+1, iy+1, iz + 1, it + i ); + + fvalue = LERP( LERP( front[0], front[1], fx ), LERP( front[2], front[3], fx ), fy ); + bvalue = LERP( LERP( back[0], back[1], fx ), LERP( back[2], back[3], fx ), fy ); + + value[i] = LERP( fvalue, bvalue, fz ); + } + + finalvalue = LERP( value[0], value[1], ft ); + + return finalvalue; +} diff --git a/src/renderergl2/tr_postprocess.c b/src/renderergl2/tr_postprocess.c new file mode 100644 index 00000000..a15c9b7a --- /dev/null +++ b/src/renderergl2/tr_postprocess.c @@ -0,0 +1,488 @@ +/* +=========================================================================== +Copyright (C) 2011 Andrei Drexler, Richard Allen, James Canete + +This file is part of Reaction source code. + +Reaction source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Reaction source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Reaction source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "tr_local.h" + +void RB_ToneMap(FBO_t *hdrFbo, int autoExposure) +{ + vec4i_t srcBox, dstBox; + vec4_t color; + static int lastFrameCount = 0; + + if (autoExposure) + { + if (lastFrameCount == 0 || tr.frameCount < lastFrameCount || tr.frameCount - lastFrameCount > 5) + { + // determine average log luminance + FBO_t *srcFbo, *dstFbo, *tmp; + int size = 256; + + lastFrameCount = tr.frameCount; + + VectorSet4(dstBox, 0, 0, size, size); + + srcFbo = hdrFbo; + dstFbo = tr.textureScratchFbo[0]; + FBO_Blit(srcFbo, NULL, NULL, dstFbo, dstBox, &tr.calclevels4xShader[0], NULL, 0); + + srcFbo = tr.textureScratchFbo[0]; + dstFbo = tr.textureScratchFbo[1]; + + // downscale to 1x1 texture + while (size > 1) + { + VectorSet4(srcBox, 0, 0, size, size); + //size >>= 2; + size >>= 1; + VectorSet4(dstBox, 0, 0, size, size); + + if (size == 1) + dstFbo = tr.targetLevelsFbo; + + //FBO_Blit(targetFbo, srcBox, NULL, tr.textureScratchFbo[nextScratch], dstBox, &tr.calclevels4xShader[1], NULL, 0); + FBO_FastBlit(srcFbo, srcBox, dstFbo, dstBox, GL_COLOR_BUFFER_BIT, GL_LINEAR); + + tmp = srcFbo; + srcFbo = dstFbo; + dstFbo = tmp; + } + } + + // blend with old log luminance for gradual change + VectorSet4(srcBox, 0, 0, 0, 0); + + color[0] = + color[1] = + color[2] = 1.0f; + if (glRefConfig.textureFloat) + color[3] = 0.03f; + else + color[3] = 0.1f; + + FBO_Blit(tr.targetLevelsFbo, srcBox, NULL, tr.calcLevelsFbo, NULL, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); + } + + // tonemap + color[0] = + color[1] = + color[2] = pow(2, r_cameraExposure->value); //exp2(r_cameraExposure->value); + color[3] = 1.0f; + + if (autoExposure) + GL_BindToTMU(tr.calcLevelsImage, TB_LEVELSMAP); + else + GL_BindToTMU(tr.fixedLevelsImage, TB_LEVELSMAP); + + FBO_Blit(hdrFbo, NULL, NULL, tr.screenScratchFbo, NULL, &tr.tonemapShader, color, 0); +} + + +void RB_BokehBlur(float blur) +{ +// vec4i_t srcBox, dstBox; + vec4_t color; + + blur *= 10.0f; + + if (blur < 0.004f) + return; + + if (glRefConfig.framebufferObject) + { + // bokeh blur + if (blur > 0.0f) + { + // create a quarter texture + //FBO_Blit(NULL, NULL, NULL, tr.quarterFbo[0], NULL, NULL, NULL, 0); + FBO_FastBlit(tr.screenScratchFbo, NULL, tr.quarterFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR); + } + +#ifndef HQ_BLUR + if (blur > 1.0f) + { + // create a 1/16th texture + //FBO_Blit(tr.quarterFbo[0], NULL, NULL, tr.textureScratchFbo[0], NULL, NULL, NULL, 0); + FBO_FastBlit(tr.quarterFbo[0], NULL, tr.textureScratchFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR); + } +#endif + + if (blur > 0.0f && blur <= 1.0f) + { + // Crossfade original with quarter texture + VectorSet4(color, 1, 1, 1, blur); + + FBO_Blit(tr.quarterFbo[0], NULL, NULL, tr.screenScratchFbo, NULL, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); + } +#ifndef HQ_BLUR + // ok blur, but can see some pixelization + else if (blur > 1.0f && blur <= 2.0f) + { + // crossfade quarter texture with 1/16th texture + FBO_Blit(tr.quarterFbo[0], NULL, NULL, tr.screenScratchFbo, NULL, NULL, NULL, 0); + + VectorSet4(color, 1, 1, 1, blur - 1.0f); + + FBO_Blit(tr.textureScratchFbo[0], NULL, NULL, tr.screenScratchFbo, NULL, NULL, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); + } + else if (blur > 2.0f) + { + // blur 1/16th texture then replace + int i; + + for (i = 0; i < 2; i++) + { + vec2_t blurTexScale; + float subblur; + + subblur = ((blur - 2.0f) / 2.0f) / 3.0f * (float)(i + 1); + + blurTexScale[0] = + blurTexScale[1] = subblur; + + color[0] = + color[1] = + color[2] = 0.5f; + color[3] = 1.0f; + + if (i != 0) + FBO_Blit(tr.textureScratchFbo[0], NULL, blurTexScale, tr.textureScratchFbo[1], NULL, &tr.bokehShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE); + else + FBO_Blit(tr.textureScratchFbo[0], NULL, blurTexScale, tr.textureScratchFbo[1], NULL, &tr.bokehShader, color, 0); + } + + FBO_Blit(tr.textureScratchFbo[1], NULL, NULL, tr.screenScratchFbo, NULL, &tr.textureColorShader, NULL, 0); + } +#else // higher quality blur, but slower + else if (blur > 1.0f) + { + // blur quarter texture then replace + int i; + + src = tr.quarterFbo[0]; + dst = tr.quarterFbo[1]; + + VectorSet4(color, 0.5f, 0.5f, 0.5f, 1); + + for (i = 0; i < 2; i++) + { + vec2_t blurTexScale; + float subblur; + + subblur = (blur - 1.0f) / 2.0f * (float)(i + 1); + + blurTexScale[0] = + blurTexScale[1] = subblur; + + color[0] = + color[1] = + color[2] = 1.0f; + if (i != 0) + color[3] = 1.0f; + else + color[3] = 0.5f; + + FBO_Blit(tr.quarterFbo[0], NULL, blurTexScale, tr.quarterFbo[1], NULL, &tr.bokehShader, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); + } + + FBO_Blit(tr.quarterFbo[1], NULL, NULL, tr.screenScratchFbo, NULL, &tr.textureColorShader, NULL, 0); + } +#endif + } +} + + +static void RB_RadialBlur(FBO_t *srcFbo, FBO_t *dstFbo, int passes, float stretch, float x, float y, float w, float h, float xcenter, float ycenter, float alpha) +{ + vec4i_t srcBox, dstBox; + vec4_t color; + const float inc = 1.f / passes; + const float mul = powf(stretch, inc); + float scale; + + { + vec2_t texScale; + + texScale[0] = + texScale[1] = 1.0f; + + alpha *= inc; + VectorSet4(color, alpha, alpha, alpha, 1.0f); + + VectorSet4(srcBox, 0, 0, srcFbo->width, srcFbo->height); + VectorSet4(dstBox, x, y, w, h); + FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, 0); + + --passes; + scale = mul; + while (passes > 0) + { + float iscale = 1.f / scale; + float s0 = xcenter * (1.f - iscale); + float t0 = (1.0f - ycenter) * (1.f - iscale); + float s1 = iscale + s0; + float t1 = iscale + t0; + + srcBox[0] = s0 * srcFbo->width; + srcBox[1] = t0 * srcFbo->height; + srcBox[2] = (s1 - s0) * srcFbo->width; + srcBox[3] = (t1 - t0) * srcFbo->height; + + FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + + scale *= mul; + --passes; + } + } +} + + +static qboolean RB_UpdateSunFlareVis(void) +{ + GLuint sampleCount = 0; + if (!glRefConfig.occlusionQuery) + return qtrue; + + tr.sunFlareQueryIndex ^= 1; + if (!tr.sunFlareQueryActive[tr.sunFlareQueryIndex]) + return qtrue; + + /* debug code */ + if (0) + { + int iter; + for (iter=0 ; ; ++iter) + { + GLint available = 0; + qglGetQueryObjectivARB(tr.sunFlareQuery[tr.sunFlareQueryIndex], GL_QUERY_RESULT_AVAILABLE_ARB, &available); + if (available) + break; + } + + ri.Printf(PRINT_DEVELOPER, "Waited %d iterations\n", iter); + } + + qglGetQueryObjectuivARB(tr.sunFlareQuery[tr.sunFlareQueryIndex], GL_QUERY_RESULT_ARB, &sampleCount); + return sampleCount > 0; +} + +void RB_SunRays(void) +{ + vec4i_t srcBox, dstBox; + vec4_t color; + float dot; + const float cutoff = 0.25f; + qboolean colorize = qtrue; + +// float w, h, w2, h2; + matrix_t mvp; + vec4_t pos, hpos; + + dot = DotProduct(tr.sunDirection, backEnd.viewParms.or.axis[0]); + if (dot < cutoff) + return; + + if (!RB_UpdateSunFlareVis()) + return; + + // From RB_DrawSun() + { + float dist; + matrix_t trans, model, mvp; + + Matrix16Translation( backEnd.viewParms.or.origin, trans ); + Matrix16Multiply( backEnd.viewParms.world.modelMatrix, trans, model ); + Matrix16Multiply(backEnd.viewParms.projectionMatrix, model, mvp); + + dist = backEnd.viewParms.zFar / 1.75; // div sqrt(3) + + VectorScale( tr.sunDirection, dist, pos ); + } + + // project sun point + //Matrix16Multiply(backEnd.viewParms.projectionMatrix, backEnd.viewParms.world.modelMatrix, mvp); + Matrix16Transform(mvp, pos, hpos); + + // transform to UV coords + hpos[3] = 0.5f / hpos[3]; + + pos[0] = 0.5f + hpos[0] * hpos[3]; + pos[1] = 0.5f - hpos[1] * hpos[3]; + + // viewport dimensions + // JBravo: Apparently not used +/* w = glConfig.vidWidth; + h = glConfig.vidHeight; + w2 = glConfig.vidWidth / 2; + h2 = glConfig.vidHeight / 2; */ + + // initialize quarter buffers + { + float mul = 1.f; + vec2_t texScale; + + texScale[0] = + texScale[1] = 1.0f; + + VectorSet4(color, mul, mul, mul, 1); + + // first, downsample the framebuffer + if (colorize) + { + FBO_FastBlit(tr.screenScratchFbo, NULL, tr.quarterFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR); + FBO_Blit(tr.sunRaysFbo, NULL, NULL, tr.quarterFbo[0], NULL, NULL, color, GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO); + } + else + { + FBO_FastBlit(tr.sunRaysFbo, NULL, tr.quarterFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR); + } + } + + // radial blur passes, ping-ponging between the two quarter-size buffers + { + const float stretch_add = 2.f/3.f; + float stretch = 1.f + stretch_add; + int i; + for (i=0; i<2; ++i) + { + RB_RadialBlur(tr.quarterFbo[i&1], tr.quarterFbo[(~i) & 1], 5, stretch, 0.f, 0.f, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height, pos[0], pos[1], 1.125f); + stretch += stretch_add; + } + } + + // add result back on top of the main buffer + { + float mul = 1.f; + vec2_t texScale; + + texScale[0] = + texScale[1] = 1.0f; + + VectorSet4(color, mul, mul, mul, 1); + + VectorSet4(srcBox, 0, 0, tr.quarterFbo[0]->width, tr.quarterFbo[0]->height); + VectorSet4(dstBox, 0, 0, glConfig.vidWidth, glConfig.vidHeight); + FBO_Blit(tr.quarterFbo[0], srcBox, texScale, tr.screenScratchFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE); + } +} + +static void RB_BlurAxis(FBO_t *srcFbo, FBO_t *dstFbo, float strength, qboolean horizontal) +{ + float dx, dy; + float xmul, ymul; + float weights[3] = { + 0.227027027f, + 0.316216216f, + 0.070270270f, + }; + float offsets[3] = { + 0.f, + 1.3846153846f, + 3.2307692308f, + }; + + xmul = horizontal; + ymul = 1.f - xmul; + + xmul *= strength; + ymul *= strength; + + { + vec4i_t srcBox, dstBox; + vec4_t color; + vec2_t texScale; + + texScale[0] = + texScale[1] = 1.0f; + + VectorSet4(color, weights[0], weights[0], weights[0], 1.0f); + VectorSet4(srcBox, 0, 0, srcFbo->width, srcFbo->height); + VectorSet4(dstBox, 0, 0, dstFbo->width, dstFbo->height); + FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, 0 ); + + VectorSet4(color, weights[1], weights[1], weights[1], 1.0f); + dx = offsets[1] * xmul; + dy = offsets[1] * ymul; + VectorSet4(srcBox, dx, dy, srcFbo->width, srcFbo->height); + FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + VectorSet4(srcBox, -dx, -dy, srcFbo->width, srcFbo->height); + FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + + VectorSet4(color, weights[2], weights[2], weights[2], 1.0f); + dx = offsets[2] * xmul; + dy = offsets[2] * ymul; + VectorSet4(srcBox, dx, dy, srcFbo->width, srcFbo->height); + FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + VectorSet4(srcBox, -dx, -dy, srcFbo->width, srcFbo->height); + FBO_Blit(srcFbo, srcBox, texScale, dstFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + } +} + +static void RB_HBlur(FBO_t *srcFbo, FBO_t *dstFbo, float strength) +{ + RB_BlurAxis(srcFbo, dstFbo, strength, qtrue); +} + +static void RB_VBlur(FBO_t *srcFbo, FBO_t *dstFbo, float strength) +{ + RB_BlurAxis(srcFbo, dstFbo, strength, qfalse); +} + +void RB_GaussianBlur(float blur) +{ + //float mul = 1.f; + float factor = Com_Clamp(0.f, 1.f, blur); + + if (factor <= 0.f) + return; + + { + vec4i_t srcBox, dstBox; + vec4_t color; + vec2_t texScale; + + texScale[0] = + texScale[1] = 1.0f; + + VectorSet4(color, 1, 1, 1, 1); + + // first, downsample the framebuffer + FBO_FastBlit(tr.screenScratchFbo, NULL, tr.quarterFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR); + FBO_FastBlit(tr.quarterFbo[0], NULL, tr.textureScratchFbo[0], NULL, GL_COLOR_BUFFER_BIT, GL_LINEAR); + + // set the alpha channel + VectorSet4(srcBox, 0, 0, tr.whiteImage->width, tr.whiteImage->height); + VectorSet4(dstBox, 0, 0, tr.textureScratchFbo[0]->width, tr.textureScratchFbo[0]->height); + qglColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); + FBO_BlitFromTexture(tr.whiteImage, srcBox, texScale, tr.textureScratchFbo[0], dstBox, &tr.textureColorShader, color, GLS_DEPTHTEST_DISABLE); + qglColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + // blur the tiny buffer horizontally and vertically + RB_HBlur(tr.textureScratchFbo[0], tr.textureScratchFbo[1], factor); + RB_VBlur(tr.textureScratchFbo[1], tr.textureScratchFbo[0], factor); + + // finally, merge back to framebuffer + VectorSet4(srcBox, 0, 0, tr.textureScratchFbo[0]->width, tr.textureScratchFbo[0]->height); + VectorSet4(dstBox, 0, 0, glConfig.vidWidth, glConfig.vidHeight); + color[3] = factor; + FBO_Blit(tr.textureScratchFbo[0], srcBox, texScale, tr.screenScratchFbo, dstBox, &tr.textureColorShader, color, GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA); + } +} diff --git a/src/renderergl2/tr_postprocess.h b/src/renderergl2/tr_postprocess.h new file mode 100644 index 00000000..1e5ebefe --- /dev/null +++ b/src/renderergl2/tr_postprocess.h @@ -0,0 +1,33 @@ +/* +=========================================================================== +Copyright (C) 2011 Andrei Drexler, Richard Allen, James Canete + +This file is part of Reaction source code. + +Reaction source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Reaction source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Reaction source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#ifndef TR_POSTPROCESS_H +#define TR_POSTPROCESS_H + +#include "tr_fbo.h" + +void RB_ToneMap(FBO_t *hdrFbo, int autoExposure); +void RB_BokehBlur(float blur); +void RB_SunRays(void); +void RB_GaussianBlur(float blur); + +#endif diff --git a/src/renderergl2/tr_scene.c b/src/renderergl2/tr_scene.c new file mode 100644 index 00000000..14c18339 --- /dev/null +++ b/src/renderergl2/tr_scene.c @@ -0,0 +1,520 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ + +#include "tr_local.h" + +int r_firstSceneDrawSurf; + +int r_numdlights; +int r_firstSceneDlight; + +int r_numentities; +int r_firstSceneEntity; + +int r_numpolys; +int r_firstScenePoly; + +int r_numpolyverts; + + +/* +==================== +R_InitNextFrame + +==================== +*/ +void R_InitNextFrame( void ) { + backEndData->commands.used = 0; + + r_firstSceneDrawSurf = 0; + + r_numdlights = 0; + r_firstSceneDlight = 0; + + r_numentities = 0; + r_firstSceneEntity = 0; + + r_numpolys = 0; + r_firstScenePoly = 0; + + r_numpolyverts = 0; +} + + +/* +==================== +RE_ClearScene + +==================== +*/ +void RE_ClearScene( void ) { + r_firstSceneDlight = r_numdlights; + r_firstSceneEntity = r_numentities; + r_firstScenePoly = r_numpolys; +} + +/* +=========================================================================== + +DISCRETE POLYS + +=========================================================================== +*/ + +/* +===================== +R_AddPolygonSurfaces + +Adds all the scene's polys into this view's drawsurf list +===================== +*/ +void R_AddPolygonSurfaces( void ) { + int i; + shader_t *sh; + srfPoly_t *poly; +// JBravo: Fog fixes + int fogMask; + + tr.currentEntityNum = REFENTITYNUM_WORLD; + tr.shiftedEntityNum = tr.currentEntityNum << QSORT_REFENTITYNUM_SHIFT; + fogMask = -((tr.refdef.rdflags & RDF_NOFOG) == 0); + + for ( i = 0, poly = tr.refdef.polys; i < tr.refdef.numPolys ; i++, poly++ ) { + sh = R_GetShaderByHandle( poly->hShader ); + R_AddDrawSurf( ( void * )poly, sh, poly->fogIndex & fogMask, qfalse, qfalse ); + } +} + +/* +===================== +RE_AddPolyToScene + +===================== +*/ +void RE_AddPolyToScene( qhandle_t hShader, int numVerts, const polyVert_t *verts, int numPolys ) { + srfPoly_t *poly; + int i, j; + int fogIndex; + fog_t *fog; + vec3_t bounds[2]; + + if ( !tr.registered ) { + return; + } + + if ( !hShader ) { + // This isn't a useful warning, and an hShader of zero isn't a null shader, it's + // the default shader. + //ri.Printf( PRINT_WARNING, "WARNING: RE_AddPolyToScene: NULL poly shader\n"); + //return; + } + + for ( j = 0; j < numPolys; j++ ) { + if ( r_numpolyverts + numVerts > max_polyverts || r_numpolys >= max_polys ) { + /* + NOTE TTimo this was initially a PRINT_WARNING + but it happens a lot with high fighting scenes and particles + since we don't plan on changing the const and making for room for those effects + simply cut this message to developer only + */ + ri.Printf( PRINT_DEVELOPER, "WARNING: RE_AddPolyToScene: r_max_polys or r_max_polyverts reached\n"); + return; + } + + poly = &backEndData->polys[r_numpolys]; + poly->surfaceType = SF_POLY; + poly->hShader = hShader; + poly->numVerts = numVerts; + poly->verts = &backEndData->polyVerts[r_numpolyverts]; + + Com_Memcpy( poly->verts, &verts[numVerts*j], numVerts * sizeof( *verts ) ); + + if ( glConfig.hardwareType == GLHW_RAGEPRO ) { + poly->verts->modulate[0] = 255; + poly->verts->modulate[1] = 255; + poly->verts->modulate[2] = 255; + poly->verts->modulate[3] = 255; + } + // done. + r_numpolys++; + r_numpolyverts += numVerts; + + // if no world is loaded + if ( tr.world == NULL ) { + fogIndex = 0; + } + // see if it is in a fog volume + else if ( tr.world->numfogs == 1 ) { + fogIndex = 0; + } else { + // find which fog volume the poly is in + VectorCopy( poly->verts[0].xyz, bounds[0] ); + VectorCopy( poly->verts[0].xyz, bounds[1] ); + for ( i = 1 ; i < poly->numVerts ; i++ ) { + AddPointToBounds( poly->verts[i].xyz, bounds[0], bounds[1] ); + } + for ( fogIndex = 1 ; fogIndex < tr.world->numfogs ; fogIndex++ ) { + fog = &tr.world->fogs[fogIndex]; + if ( bounds[1][0] >= fog->bounds[0][0] + && bounds[1][1] >= fog->bounds[0][1] + && bounds[1][2] >= fog->bounds[0][2] + && bounds[0][0] <= fog->bounds[1][0] + && bounds[0][1] <= fog->bounds[1][1] + && bounds[0][2] <= fog->bounds[1][2] ) { + break; + } + } + if ( fogIndex == tr.world->numfogs ) { + fogIndex = 0; + } + } + poly->fogIndex = fogIndex; + } +} + + +//================================================================================= + + +/* +===================== +RE_AddRefEntityToScene + +===================== +*/ +void RE_AddRefEntityToScene( const refEntity_t *ent ) { + // JBravo: Mirrored models + vec3_t cross; + + if ( !tr.registered ) { + return; + } + if ( r_numentities >= MAX_REFENTITIES ) { + ri.Printf(PRINT_DEVELOPER, "RE_AddRefEntityToScene: Dropping refEntity, reached MAX_REFENTITIES\n"); + return; + } + if ( Q_isnan(ent->origin[0]) || Q_isnan(ent->origin[1]) || Q_isnan(ent->origin[2]) ) { + static qboolean firstTime = qtrue; + if (firstTime) { + firstTime = qfalse; + ri.Printf( PRINT_WARNING, "RE_AddRefEntityToScene passed a refEntity which has an origin with a NaN component\n"); + } + return; + } + if ( (int)ent->reType < 0 || ent->reType >= RT_MAX_REF_ENTITY_TYPE ) { + ri.Error( ERR_DROP, "RE_AddRefEntityToScene: bad reType %i", ent->reType ); + } + + backEndData->entities[r_numentities].e = *ent; + backEndData->entities[r_numentities].lightingCalculated = qfalse; + + // JBravo: Mirrored models + CrossProduct(ent->axis[0], ent->axis[1], cross); + backEndData->entities[r_numentities].mirrored = (DotProduct(ent->axis[2], cross) < 0.f); + + r_numentities++; +} + + +/* +===================== +RE_AddDynamicLightToScene + +===================== +*/ +void RE_AddDynamicLightToScene( const vec3_t org, float intensity, float r, float g, float b, int additive ) { + dlight_t *dl; + + if ( !tr.registered ) { + return; + } + if ( r_numdlights >= MAX_DLIGHTS ) { + return; + } + if ( intensity <= 0 ) { + return; + } + // these cards don't have the correct blend mode + if ( glConfig.hardwareType == GLHW_RIVA128 || glConfig.hardwareType == GLHW_PERMEDIA2 ) { + return; + } + dl = &backEndData->dlights[r_numdlights++]; + VectorCopy (org, dl->origin); + dl->radius = intensity; + dl->color[0] = r; + dl->color[1] = g; + dl->color[2] = b; + dl->additive = additive; +} + +/* +===================== +RE_AddLightToScene + +===================== +*/ +void RE_AddLightToScene( const vec3_t org, float intensity, float r, float g, float b ) { + RE_AddDynamicLightToScene( org, intensity, r, g, b, qfalse ); +} + +/* +===================== +RE_AddAdditiveLightToScene + +===================== +*/ +void RE_AddAdditiveLightToScene( const vec3_t org, float intensity, float r, float g, float b ) { + RE_AddDynamicLightToScene( org, intensity, r, g, b, qtrue ); +} + +/* +@@@@@@@@@@@@@@@@@@@@@ +RE_RenderScene + +Draw a 3D view into a part of the window, then return +to 2D drawing. + +Rendering a scene may require multiple views to be rendered +to handle mirrors, +@@@@@@@@@@@@@@@@@@@@@ +*/ +void RE_RenderScene( const refdef_t *fd ) { + viewParms_t parms; + int startTime; + + if ( !tr.registered ) { + return; + } + GLimp_LogComment( "====== RE_RenderScene =====\n" ); + + if ( r_norefresh->integer ) { + return; + } + + startTime = ri.Milliseconds(); + + if (!tr.world && !( fd->rdflags & RDF_NOWORLDMODEL ) ) { + ri.Error (ERR_DROP, "R_RenderScene: NULL worldmodel"); + } + + Com_Memcpy( tr.refdef.text, fd->text, sizeof( tr.refdef.text ) ); + + tr.refdef.x = fd->x; + tr.refdef.y = fd->y; + tr.refdef.width = fd->width; + tr.refdef.height = fd->height; + tr.refdef.fov_x = fd->fov_x; + tr.refdef.fov_y = fd->fov_y; + + VectorCopy( fd->vieworg, tr.refdef.vieworg ); + VectorCopy( fd->viewaxis[0], tr.refdef.viewaxis[0] ); + VectorCopy( fd->viewaxis[1], tr.refdef.viewaxis[1] ); + VectorCopy( fd->viewaxis[2], tr.refdef.viewaxis[2] ); + + tr.refdef.time = fd->time; + tr.refdef.rdflags = fd->rdflags; + + // copy the areamask data over and note if it has changed, which + // will force a reset of the visible leafs even if the view hasn't moved + tr.refdef.areamaskModified = qfalse; + if ( ! (tr.refdef.rdflags & RDF_NOWORLDMODEL) ) { + int areaDiff; + int i; + + // compare the area bits + areaDiff = 0; + for (i = 0 ; i < MAX_MAP_AREA_BYTES/4 ; i++) { + areaDiff |= ((int *)tr.refdef.areamask)[i] ^ ((int *)fd->areamask)[i]; + ((int *)tr.refdef.areamask)[i] = ((int *)fd->areamask)[i]; + } + + if ( areaDiff ) { + // a door just opened or something + tr.refdef.areamaskModified = qtrue; + } + } + + tr.refdef.sunDir[3] = 0.0f; + tr.refdef.sunCol[3] = 1.0f; + tr.refdef.sunAmbCol[3] = 1.0f; + + VectorCopy(tr.sunDirection, tr.refdef.sunDir); + if ( (tr.refdef.rdflags & RDF_NOWORLDMODEL) || !(r_depthPrepass->value) ){ + tr.refdef.colorScale = 1.0f; + VectorSet(tr.refdef.sunCol, 0, 0, 0); + VectorSet(tr.refdef.sunAmbCol, 0, 0, 0); + } + else if (r_forceSun->integer == 1) + { + float scale = pow(2, r_mapOverBrightBits->integer - tr.overbrightBits - 8); + tr.refdef.colorScale = r_forceSunMapLightScale->value; + VectorScale(tr.sunLight, scale * r_forceSunLightScale->value, tr.refdef.sunCol); + VectorScale(tr.sunLight, scale * r_forceSunAmbientScale->value, tr.refdef.sunAmbCol); + } + else + { + float scale = pow(2, r_mapOverBrightBits->integer - tr.overbrightBits - 8); + tr.refdef.colorScale = tr.mapLightScale; + VectorScale(tr.sunLight, scale, tr.refdef.sunCol); + VectorScale(tr.sunAmbient, scale, tr.refdef.sunAmbCol); + } + + if (r_forceAutoExposure->integer) + { + tr.refdef.autoExposureMinMax[0] = r_forceAutoExposureMin->value; + tr.refdef.autoExposureMinMax[1] = r_forceAutoExposureMax->value; + } + else + { + tr.refdef.autoExposureMinMax[0] = tr.autoExposureMinMax[0]; + tr.refdef.autoExposureMinMax[1] = tr.autoExposureMinMax[1]; + } + + if (r_forceToneMap->integer) + { + tr.refdef.toneMinAvgMaxLinear[0] = pow(2, r_forceToneMapMin->value); + tr.refdef.toneMinAvgMaxLinear[1] = pow(2, r_forceToneMapAvg->value); + tr.refdef.toneMinAvgMaxLinear[2] = pow(2, r_forceToneMapMax->value); + } + else + { + tr.refdef.toneMinAvgMaxLinear[0] = pow(2, tr.toneMinAvgMaxLevel[0]); + tr.refdef.toneMinAvgMaxLinear[1] = pow(2, tr.toneMinAvgMaxLevel[1]); + tr.refdef.toneMinAvgMaxLinear[2] = pow(2, tr.toneMinAvgMaxLevel[2]); + } + + // Makro - copy exta info if present + if (fd->rdflags & RDF_EXTRA) { + const refdefex_t* extra = (const refdefex_t*) (fd+1); + + tr.refdef.blurFactor = extra->blurFactor; + + if (fd->rdflags & RDF_SUNLIGHT) + { + VectorCopy(extra->sunDir, tr.refdef.sunDir); + VectorCopy(extra->sunCol, tr.refdef.sunCol); + VectorCopy(extra->sunAmbCol, tr.refdef.sunAmbCol); + } + } + else + { + tr.refdef.blurFactor = 0.0f; + } + + // derived info + + tr.refdef.floatTime = tr.refdef.time * 0.001f; + + tr.refdef.numDrawSurfs = r_firstSceneDrawSurf; + tr.refdef.drawSurfs = backEndData->drawSurfs; + + tr.refdef.num_entities = r_numentities - r_firstSceneEntity; + tr.refdef.entities = &backEndData->entities[r_firstSceneEntity]; + + tr.refdef.num_dlights = r_numdlights - r_firstSceneDlight; + tr.refdef.dlights = &backEndData->dlights[r_firstSceneDlight]; + + tr.refdef.numPolys = r_numpolys - r_firstScenePoly; + tr.refdef.polys = &backEndData->polys[r_firstScenePoly]; + + tr.refdef.num_pshadows = 0; + tr.refdef.pshadows = &backEndData->pshadows[0]; + + // turn off dynamic lighting globally by clearing all the + // dlights if it needs to be disabled or if vertex lighting is enabled + if ( r_dynamiclight->integer == 0 || + r_vertexLight->integer == 1 || + glConfig.hardwareType == GLHW_PERMEDIA2 ) { + tr.refdef.num_dlights = 0; + } + + // a single frame may have multiple scenes draw inside it -- + // a 3D game view, 3D status bar renderings, 3D menus, etc. + // They need to be distinguished by the light flare code, because + // the visibility state for a given surface may be different in + // each scene / view. + tr.frameSceneNum++; + tr.sceneCount++; + + // SmileTheory: playing with shadow mapping + if (!( fd->rdflags & RDF_NOWORLDMODEL ) && tr.refdef.num_dlights && r_dlightMode->integer >= 2) + { + R_RenderDlightCubemaps(fd); + } + + /* playing with more shadows */ + if(glRefConfig.framebufferObject && !( fd->rdflags & RDF_NOWORLDMODEL ) && r_shadows->integer == 4) + { + R_RenderPshadowMaps(fd); + } + + // playing with even more shadows + if(glRefConfig.framebufferObject && !( fd->rdflags & RDF_NOWORLDMODEL ) && (r_forceSun->integer || tr.sunShadows)) + { + R_RenderSunShadowMaps(fd, 0); + R_RenderSunShadowMaps(fd, 1); + R_RenderSunShadowMaps(fd, 2); + } + + // setup view parms for the initial view + // + // set up viewport + // The refdef takes 0-at-the-top y coordinates, so + // convert to GL's 0-at-the-bottom space + // + Com_Memset( &parms, 0, sizeof( parms ) ); + parms.viewportX = tr.refdef.x; + parms.viewportY = glConfig.vidHeight - ( tr.refdef.y + tr.refdef.height ); + parms.viewportWidth = tr.refdef.width; + parms.viewportHeight = tr.refdef.height; + parms.isPortal = qfalse; + + parms.fovX = tr.refdef.fov_x; + parms.fovY = tr.refdef.fov_y; + + parms.stereoFrame = tr.refdef.stereoFrame; + + VectorCopy( fd->vieworg, parms.or.origin ); + VectorCopy( fd->viewaxis[0], parms.or.axis[0] ); + VectorCopy( fd->viewaxis[1], parms.or.axis[1] ); + VectorCopy( fd->viewaxis[2], parms.or.axis[2] ); + + VectorCopy( fd->vieworg, parms.pvsOrigin ); + + if(!( fd->rdflags & RDF_NOWORLDMODEL ) && r_depthPrepass->value && ((r_forceSun->integer) || tr.sunShadows)) + { + parms.flags = VPF_USESUNLIGHT; + } + + R_RenderView( &parms ); + + if(!( fd->rdflags & RDF_NOWORLDMODEL )) + R_AddPostProcessCmd(); + + // the next scene rendered in this frame will tack on after this one + r_firstSceneDrawSurf = tr.refdef.numDrawSurfs; + r_firstSceneEntity = r_numentities; + r_firstSceneDlight = r_numdlights; + r_firstScenePoly = r_numpolys; + + tr.frontEndMsec += ri.Milliseconds() - startTime; +} diff --git a/src/renderergl2/tr_shade.c b/src/renderergl2/tr_shade.c new file mode 100644 index 00000000..b4758a86 --- /dev/null +++ b/src/renderergl2/tr_shade.c @@ -0,0 +1,1866 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_shade.c + +#include "tr_local.h" +#if idppc_altivec && !defined(MACOS_X) +#include +#endif + +/* + + THIS ENTIRE FILE IS BACK END + + This file deals with applying shaders to surface data in the tess struct. +*/ + + +/* +================== +R_DrawElements + +================== +*/ + +void R_DrawElementsVBO( int numIndexes, glIndex_t firstIndex, glIndex_t minIndex, glIndex_t maxIndex ) +{ + if (glRefConfig.drawRangeElements) + qglDrawRangeElementsEXT(GL_TRIANGLES, minIndex, maxIndex, numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(firstIndex * sizeof(GL_INDEX_TYPE))); + else + qglDrawElements(GL_TRIANGLES, numIndexes, GL_INDEX_TYPE, BUFFER_OFFSET(firstIndex * sizeof(GL_INDEX_TYPE))); + +} + + +static void R_DrawMultiElementsVBO( int multiDrawPrimitives, glIndex_t *multiDrawMinIndex, glIndex_t *multiDrawMaxIndex, + GLsizei *multiDrawNumIndexes, glIndex_t **multiDrawFirstIndex) +{ + if (glRefConfig.multiDrawArrays) + { + qglMultiDrawElementsEXT(GL_TRIANGLES, multiDrawNumIndexes, GL_INDEX_TYPE, (const GLvoid **)multiDrawFirstIndex, multiDrawPrimitives); + } + else + { + int i; + + if (glRefConfig.drawRangeElements) + { + for (i = 0; i < multiDrawPrimitives; i++) + { + qglDrawRangeElementsEXT(GL_TRIANGLES, multiDrawMinIndex[i], multiDrawMaxIndex[i], multiDrawNumIndexes[i], GL_INDEX_TYPE, multiDrawFirstIndex[i]); + } + } + else + { + for (i = 0; i < multiDrawPrimitives; i++) + { + qglDrawElements(GL_TRIANGLES, multiDrawNumIndexes[i], GL_INDEX_TYPE, multiDrawFirstIndex[i]); + } + } + } +} + + +/* +============================================================= + +SURFACE SHADERS + +============================================================= +*/ + +shaderCommands_t tess; + + +/* +================= +R_BindAnimatedImageToTMU + +================= +*/ +static void R_BindAnimatedImageToTMU( textureBundle_t *bundle, int tmu ) { + int index; + + if ( bundle->isVideoMap ) { + int oldtmu = glState.currenttmu; + GL_SelectTexture(tmu); + ri.CIN_RunCinematic(bundle->videoMapHandle); + ri.CIN_UploadCinematic(bundle->videoMapHandle); + GL_SelectTexture(oldtmu); + return; + } + + if ( bundle->numImageAnimations <= 1 ) { + GL_BindToTMU( bundle->image[0], tmu); + return; + } + + // it is necessary to do this messy calc to make sure animations line up + // exactly with waveforms of the same frequency + index = ri.ftol(tess.shaderTime * bundle->imageAnimationSpeed * FUNCTABLE_SIZE); + index >>= FUNCTABLE_SIZE2; + + if ( index < 0 ) { + index = 0; // may happen with shader time offsets + } + index %= bundle->numImageAnimations; + + GL_BindToTMU( bundle->image[ index ], tmu ); +} + + +/* +================ +DrawTris + +Draws triangle outlines for debugging +================ +*/ +static void DrawTris (shaderCommands_t *input) { + GL_Bind( tr.whiteImage ); + + GL_State( GLS_POLYMODE_LINE | GLS_DEPTHMASK_TRUE ); + qglDepthRange( 0, 0 ); + + { + shaderProgram_t *sp = &tr.textureColorShader; + vec4_t color; + + GLSL_VertexAttribsState(ATTR_POSITION); + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + VectorSet4(color, 1, 1, 1, 1); + GLSL_SetUniformVec4(sp, TEXTURECOLOR_UNIFORM_COLOR, color); + + if (input->multiDrawPrimitives) + { + R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawMinIndex, input->multiDrawMaxIndex, input->multiDrawNumIndexes, input->multiDrawFirstIndex); + } + else + { + R_DrawElementsVBO(input->numIndexes, input->firstIndex, input->minIndex, input->maxIndex); + } + } + + qglDepthRange( 0, 1 ); +} + + +/* +================ +DrawNormals + +Draws vertex normals for debugging +================ +*/ +static void DrawNormals (shaderCommands_t *input) { + //FIXME: implement this +} + +/* +============== +RB_BeginSurface + +We must set some things up before beginning any tesselation, +because a surface may be forced to perform a RB_End due +to overflow. +============== +*/ +void RB_BeginSurface( shader_t *shader, int fogNum ) { + + shader_t *state = (shader->remappedShader) ? shader->remappedShader : shader; + + tess.numIndexes = 0; + tess.firstIndex = 0; + tess.numVertexes = 0; + tess.multiDrawPrimitives = 0; + tess.shader = state; + tess.fogNum = fogNum; + tess.dlightBits = 0; // will be OR'd in by surface functions + tess.pshadowBits = 0; // will be OR'd in by surface functions + tess.xstages = state->stages; + tess.numPasses = state->numUnfoggedPasses; + tess.currentStageIteratorFunc = state->optimalStageIteratorFunc; + tess.useInternalVBO = qtrue; + + tess.shaderTime = backEnd.refdef.floatTime - tess.shader->timeOffset; + if (tess.shader->clampTime && tess.shaderTime >= tess.shader->clampTime) { + tess.shaderTime = tess.shader->clampTime; + } + + if (backEnd.viewParms.flags & VPF_SHADOWMAP) + { + tess.currentStageIteratorFunc = RB_StageIteratorGeneric; + } +} + + + +extern float EvalWaveForm( const waveForm_t *wf ); +extern float EvalWaveFormClamped( const waveForm_t *wf ); + + +static void ComputeTexMatrix( shaderStage_t *pStage, int bundleNum, float *outmatrix) +{ + int tm; + float matrix[16], currentmatrix[16]; + textureBundle_t *bundle = &pStage->bundle[bundleNum]; + + Matrix16Identity(outmatrix); + Matrix16Identity(currentmatrix); + + for ( tm = 0; tm < bundle->numTexMods ; tm++ ) { + switch ( bundle->texMods[tm].type ) + { + + case TMOD_NONE: + tm = TR_MAX_TEXMODS; // break out of for loop + break; + + case TMOD_TURBULENT: + RB_CalcTurbulentTexMatrix( &bundle->texMods[tm].wave, + matrix ); + outmatrix[12] = matrix[12]; + outmatrix[13] = matrix[13]; + Matrix16Copy(outmatrix, currentmatrix); + break; + + case TMOD_ENTITY_TRANSLATE: + RB_CalcScrollTexMatrix( backEnd.currentEntity->e.shaderTexCoord, + matrix ); + Matrix16Multiply(matrix, currentmatrix, outmatrix); + Matrix16Copy(outmatrix, currentmatrix); + break; + + case TMOD_SCROLL: + RB_CalcScrollTexMatrix( bundle->texMods[tm].scroll, + matrix ); + Matrix16Multiply(matrix, currentmatrix, outmatrix); + Matrix16Copy(outmatrix, currentmatrix); + break; + + case TMOD_SCALE: + RB_CalcScaleTexMatrix( bundle->texMods[tm].scale, + matrix ); + Matrix16Multiply(matrix, currentmatrix, outmatrix); + Matrix16Copy(outmatrix, currentmatrix); + break; + + case TMOD_STRETCH: + RB_CalcStretchTexMatrix( &bundle->texMods[tm].wave, + matrix ); + Matrix16Multiply(matrix, currentmatrix, outmatrix); + Matrix16Copy(outmatrix, currentmatrix); + break; + + case TMOD_TRANSFORM: + RB_CalcTransformTexMatrix( &bundle->texMods[tm], + matrix ); + Matrix16Multiply(matrix, currentmatrix, outmatrix); + Matrix16Copy(outmatrix, currentmatrix); + break; + + case TMOD_ROTATE: + RB_CalcRotateTexMatrix( bundle->texMods[tm].rotateSpeed, + matrix ); + Matrix16Multiply(matrix, currentmatrix, outmatrix); + Matrix16Copy(outmatrix, currentmatrix); + break; + + default: + ri.Error( ERR_DROP, "ERROR: unknown texmod '%d' in shader '%s'", bundle->texMods[tm].type, tess.shader->name ); + break; + } + } +} + + +static void ComputeDeformValues(int *deformGen, vec5_t deformParams) +{ + // u_DeformGen + *deformGen = DGEN_NONE; + if(!ShaderRequiresCPUDeforms(tess.shader)) + { + deformStage_t *ds; + + // only support the first one + ds = &tess.shader->deforms[0]; + + switch (ds->deformation) + { + case DEFORM_WAVE: + *deformGen = ds->deformationWave.func; + + deformParams[0] = ds->deformationWave.base; + deformParams[1] = ds->deformationWave.amplitude; + deformParams[2] = ds->deformationWave.phase; + deformParams[3] = ds->deformationWave.frequency; + deformParams[4] = ds->deformationSpread; + break; + + case DEFORM_BULGE: + *deformGen = DGEN_BULGE; + + deformParams[0] = 0; + deformParams[1] = ds->bulgeHeight; // amplitude + deformParams[2] = ds->bulgeWidth; // phase + deformParams[3] = ds->bulgeSpeed; // frequency + deformParams[4] = 0; + break; + + default: + break; + } + } +} + + +static void ProjectDlightTexture( void ) { + int l; + vec3_t origin; + float scale; + float radius; + int deformGen; + vec5_t deformParams; + + if ( !backEnd.refdef.num_dlights ) { + return; + } + + ComputeDeformValues(&deformGen, deformParams); + + for ( l = 0 ; l < backEnd.refdef.num_dlights ; l++ ) { + dlight_t *dl; + shaderProgram_t *sp; + vec4_t vector; + + if ( !( tess.dlightBits & ( 1 << l ) ) ) { + continue; // this surface definately doesn't have any of this light + } + + dl = &backEnd.refdef.dlights[l]; + VectorCopy( dl->transformed, origin ); + radius = dl->radius; + scale = 1.0f / radius; + + sp = &tr.dlightShader[deformGen == DGEN_NONE ? 0 : 1]; + + backEnd.pc.c_dlightDraws++; + + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, DLIGHT_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + + GLSL_SetUniformFloat(sp, DLIGHT_UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); + + GLSL_SetUniformInt(sp, DLIGHT_UNIFORM_DEFORMGEN, deformGen); + if (deformGen != DGEN_NONE) + { + GLSL_SetUniformFloat5(sp, DLIGHT_UNIFORM_DEFORMPARAMS, deformParams); + GLSL_SetUniformFloat(sp, DLIGHT_UNIFORM_TIME, tess.shaderTime); + } + + vector[0] = dl->color[0]; + vector[1] = dl->color[1]; + vector[2] = dl->color[2]; + vector[3] = 1.0f; + GLSL_SetUniformVec4(sp, DLIGHT_UNIFORM_COLOR, vector); + + vector[0] = origin[0]; + vector[1] = origin[1]; + vector[2] = origin[2]; + vector[3] = scale; + GLSL_SetUniformVec4(sp, DLIGHT_UNIFORM_DLIGHTINFO, vector); + + GL_Bind( tr.dlightImage ); + + // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light + // where they aren't rendered + if ( dl->additive ) { + GL_State( GLS_ATEST_GT_0 | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); + } + else { + GL_State( GLS_ATEST_GT_0 | GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); + } + + if (tess.multiDrawPrimitives) + { + shaderCommands_t *input = &tess; + R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawMinIndex, input->multiDrawMaxIndex, input->multiDrawNumIndexes, input->multiDrawFirstIndex); + } + else + { + R_DrawElementsVBO(tess.numIndexes, tess.firstIndex, tess.minIndex, tess.maxIndex); + } + + backEnd.pc.c_totalIndexes += tess.numIndexes; + backEnd.pc.c_dlightIndexes += tess.numIndexes; + } +} + + +static void ComputeShaderColors( shaderStage_t *pStage, vec4_t baseColor, vec4_t vertColor ) +{ + // + // rgbGen + // + switch ( pStage->rgbGen ) + { + case CGEN_IDENTITY: + baseColor[0] = + baseColor[1] = + baseColor[2] = + baseColor[3] = 1.0f; + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = 0.0f; + break; + case CGEN_IDENTITY_LIGHTING: + baseColor[0] = + baseColor[1] = + baseColor[2] = tr.identityLight; + baseColor[3] = 1.0f; + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = 0.0f; + break; + case CGEN_EXACT_VERTEX: + baseColor[0] = + baseColor[1] = + baseColor[2] = + baseColor[3] = 0.0f; + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = 1.0f; + break; + case CGEN_EXACT_VERTEX_LIT: + baseColor[0] = + baseColor[1] = + baseColor[2] = + baseColor[3] = 0.0f; + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = 1.0f; + break; + case CGEN_CONST: + baseColor[0] = pStage->constantColor[0] / 255.0f; + baseColor[1] = pStage->constantColor[1] / 255.0f; + baseColor[2] = pStage->constantColor[2] / 255.0f; + baseColor[3] = pStage->constantColor[3] / 255.0f; + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = 0.0f; + break; + case CGEN_VERTEX: + baseColor[0] = + baseColor[1] = + baseColor[2] = + baseColor[3] = 0.0f; + + vertColor[0] = + vertColor[1] = + vertColor[2] = tr.identityLight; + vertColor[3] = 1.0f; + break; + case CGEN_VERTEX_LIT: + baseColor[0] = + baseColor[1] = + baseColor[2] = + baseColor[3] = 0.0f; + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = tr.identityLight; + break; + case CGEN_ONE_MINUS_VERTEX: + baseColor[0] = + baseColor[1] = + baseColor[2] = tr.identityLight; + baseColor[3] = 1.0f; + + vertColor[0] = + vertColor[1] = + vertColor[2] = -tr.identityLight; + vertColor[3] = 0.0f; + break; + case CGEN_FOG: + { + fog_t *fog; + + fog = tr.world->fogs + tess.fogNum; + + baseColor[0] = ((unsigned char *)(&fog->colorInt))[0] / 255.0f; + baseColor[1] = ((unsigned char *)(&fog->colorInt))[1] / 255.0f; + baseColor[2] = ((unsigned char *)(&fog->colorInt))[2] / 255.0f; + baseColor[3] = ((unsigned char *)(&fog->colorInt))[3] / 255.0f; + } + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = 0.0f; + break; + case CGEN_WAVEFORM: + baseColor[0] = + baseColor[1] = + baseColor[2] = RB_CalcWaveColorSingle( &pStage->rgbWave ); + baseColor[3] = 1.0f; + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = 0.0f; + break; + case CGEN_ENTITY: + if (backEnd.currentEntity) + { + baseColor[0] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[0] / 255.0f; + baseColor[1] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[1] / 255.0f; + baseColor[2] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[2] / 255.0f; + baseColor[3] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; + } + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = 0.0f; + break; + case CGEN_ONE_MINUS_ENTITY: + if (backEnd.currentEntity) + { + baseColor[0] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[0] / 255.0f; + baseColor[1] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[1] / 255.0f; + baseColor[2] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[2] / 255.0f; + baseColor[3] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; + } + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = 0.0f; + break; + case CGEN_LIGHTING_DIFFUSE: + case CGEN_BAD: + baseColor[0] = + baseColor[1] = + baseColor[2] = + baseColor[3] = 1.0f; + + vertColor[0] = + vertColor[1] = + vertColor[2] = + vertColor[3] = 0.0f; + break; + } + + // + // alphaGen + // + switch ( pStage->alphaGen ) + { + case AGEN_SKIP: + break; + case AGEN_IDENTITY: + baseColor[3] = 1.0f; + vertColor[3] = 0.0f; + break; + case AGEN_CONST: + baseColor[3] = pStage->constantColor[3] / 255.0f; + vertColor[3] = 0.0f; + break; + case AGEN_WAVEFORM: + baseColor[3] = RB_CalcWaveAlphaSingle( &pStage->alphaWave ); + vertColor[3] = 0.0f; + break; + case AGEN_ENTITY: + if (backEnd.currentEntity) + { + baseColor[3] = ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; + } + vertColor[3] = 0.0f; + break; + case AGEN_ONE_MINUS_ENTITY: + if (backEnd.currentEntity) + { + baseColor[3] = 1.0f - ((unsigned char *)backEnd.currentEntity->e.shaderRGBA)[3] / 255.0f; + } + vertColor[3] = 0.0f; + break; + case AGEN_VERTEX: + baseColor[3] = 0.0f; + vertColor[3] = 1.0f; + break; + case AGEN_ONE_MINUS_VERTEX: + baseColor[3] = 1.0f; + vertColor[3] = -1.0f; + break; + case AGEN_LIGHTING_SPECULAR: + case AGEN_PORTAL: + case AGEN_FRESNEL: + // Done entirely in vertex program + baseColor[3] = 1.0f; + vertColor[3] = 0.0f; + break; + } + + // FIXME: find some way to implement this. +#if 0 + // if in greyscale rendering mode turn all color values into greyscale. + if(r_greyscale->integer) + { + int scale; + + for(i = 0; i < tess.numVertexes; i++) + { + scale = (tess.svars.colors[i][0] + tess.svars.colors[i][1] + tess.svars.colors[i][2]) / 3; + tess.svars.colors[i][0] = tess.svars.colors[i][1] = tess.svars.colors[i][2] = scale; + } + } +#endif +} + + +static void ComputeFogValues(vec4_t fogDistanceVector, vec4_t fogDepthVector, float *eyeT) +{ + // from RB_CalcFogTexCoords() + fog_t *fog; + vec3_t local; + + if (!tess.fogNum) + return; + + fog = tr.world->fogs + tess.fogNum; + + VectorSubtract( backEnd.or.origin, backEnd.viewParms.or.origin, local ); + fogDistanceVector[0] = -backEnd.or.modelMatrix[2]; + fogDistanceVector[1] = -backEnd.or.modelMatrix[6]; + fogDistanceVector[2] = -backEnd.or.modelMatrix[10]; + fogDistanceVector[3] = DotProduct( local, backEnd.viewParms.or.axis[0] ); + + // scale the fog vectors based on the fog's thickness + VectorScale4(fogDistanceVector, fog->tcScale, fogDistanceVector); + + // rotate the gradient vector for this orientation + if ( fog->hasSurface ) { + fogDepthVector[0] = fog->surface[0] * backEnd.or.axis[0][0] + + fog->surface[1] * backEnd.or.axis[0][1] + fog->surface[2] * backEnd.or.axis[0][2]; + fogDepthVector[1] = fog->surface[0] * backEnd.or.axis[1][0] + + fog->surface[1] * backEnd.or.axis[1][1] + fog->surface[2] * backEnd.or.axis[1][2]; + fogDepthVector[2] = fog->surface[0] * backEnd.or.axis[2][0] + + fog->surface[1] * backEnd.or.axis[2][1] + fog->surface[2] * backEnd.or.axis[2][2]; + fogDepthVector[3] = -fog->surface[3] + DotProduct( backEnd.or.origin, fog->surface ); + + *eyeT = DotProduct( backEnd.or.viewOrigin, fogDepthVector ) + fogDepthVector[3]; + } else { + *eyeT = 1; // non-surface fog always has eye inside + } +} + + +static void ComputeFogColorMask( shaderStage_t *pStage, vec4_t fogColorMask ) +{ + switch(pStage->adjustColorsForFog) + { + case ACFF_MODULATE_RGB: + fogColorMask[0] = + fogColorMask[1] = + fogColorMask[2] = 1.0f; + fogColorMask[3] = 0.0f; + break; + case ACFF_MODULATE_ALPHA: + fogColorMask[0] = + fogColorMask[1] = + fogColorMask[2] = 0.0f; + fogColorMask[3] = 1.0f; + break; + case ACFF_MODULATE_RGBA: + fogColorMask[0] = + fogColorMask[1] = + fogColorMask[2] = + fogColorMask[3] = 1.0f; + break; + default: + fogColorMask[0] = + fogColorMask[1] = + fogColorMask[2] = + fogColorMask[3] = 0.0f; + break; + } +} + + +static void ForwardDlight( void ) { + int l; + //vec3_t origin; + //float scale; + float radius; + + int deformGen; + vec5_t deformParams; + + vec4_t fogDistanceVector, fogDepthVector = {0, 0, 0, 0}; + float eyeT = 0; + + shaderCommands_t *input = &tess; + shaderStage_t *pStage = tess.xstages[0]; + + if ( !backEnd.refdef.num_dlights ) { + return; + } + + ComputeDeformValues(&deformGen, deformParams); + + ComputeFogValues(fogDistanceVector, fogDepthVector, &eyeT); + + for ( l = 0 ; l < backEnd.refdef.num_dlights ; l++ ) { + dlight_t *dl; + shaderProgram_t *sp; + vec4_t vector; + matrix_t matrix; + + if ( !( tess.dlightBits & ( 1 << l ) ) ) { + continue; // this surface definately doesn't have any of this light + } + + dl = &backEnd.refdef.dlights[l]; + //VectorCopy( dl->transformed, origin ); + radius = dl->radius; + //scale = 1.0f / radius; + + //if (pStage->glslShaderGroup == tr.lightallShader) + { + int index = pStage->glslShaderIndex; + + index &= ~(LIGHTDEF_LIGHTTYPE_MASK | LIGHTDEF_USE_DELUXEMAP); + index |= LIGHTDEF_USE_LIGHT_VECTOR; + + sp = &tr.lightallShader[index]; + } + + backEnd.pc.c_lightallDraws++; + + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_VIEWORIGIN, backEnd.viewParms.or.origin); + + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); + + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_DEFORMGEN, deformGen); + if (deformGen != DGEN_NONE) + { + GLSL_SetUniformFloat5(sp, GENERIC_UNIFORM_DEFORMPARAMS, deformParams); + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_TIME, tess.shaderTime); + } + + if ( input->fogNum ) { + vec4_t fogColorMask; + + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGDISTANCE, fogDistanceVector); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGDEPTH, fogDepthVector); + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_FOGEYET, eyeT); + + ComputeFogColorMask(pStage, fogColorMask); + + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGCOLORMASK, fogColorMask); + } + + { + vec4_t baseColor; + vec4_t vertColor; + + ComputeShaderColors(pStage, baseColor, vertColor); + + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_BASECOLOR, baseColor); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_VERTCOLOR, vertColor); + } + + if (pStage->alphaGen == AGEN_PORTAL) + { + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_PORTALRANGE, tess.shader->portalRange); + } + + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_COLORGEN, pStage->rgbGen); + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_ALPHAGEN, pStage->alphaGen); + + GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_DIRECTEDLIGHT, dl->color); + + VectorSet(vector, 0, 0, 0); + GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_AMBIENTLIGHT, vector); + + VectorCopy(dl->origin, vector); + vector[3] = 1.0f; + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_LIGHTORIGIN, vector); + + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_LIGHTRADIUS, radius); + + GLSL_SetUniformVec2(sp, GENERIC_UNIFORM_MATERIALINFO, pStage->materialInfo); + + // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light + // where they aren't rendered + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL ); + + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELMATRIX, backEnd.or.transformMatrix); + + if (pStage->bundle[TB_DIFFUSEMAP].image[0]) + R_BindAnimatedImageToTMU( &pStage->bundle[TB_DIFFUSEMAP], TB_DIFFUSEMAP); + + if (pStage->bundle[TB_NORMALMAP].image[0]) + R_BindAnimatedImageToTMU( &pStage->bundle[TB_NORMALMAP], TB_NORMALMAP); + + if (pStage->bundle[TB_SPECULARMAP].image[0]) + R_BindAnimatedImageToTMU( &pStage->bundle[TB_SPECULARMAP], TB_SPECULARMAP); + + if (r_dlightMode->integer >= 2) + { + GL_SelectTexture(TB_SHADOWMAP); + GL_BindCubemap(tr.shadowCubemaps[l]); + GL_SelectTexture(0); + } + + ComputeTexMatrix( pStage, TB_DIFFUSEMAP, matrix ); + + VectorSet4(vector, matrix[0], matrix[1], matrix[4], matrix[5]); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_DIFFUSETEXMATRIX, vector); + + VectorSet4(vector, matrix[8], matrix[9], matrix[12], matrix[13]); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_DIFFUSETEXOFFTURB, vector); + + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_TCGEN0, pStage->bundle[0].tcGen); + + // + // draw + // + + if (input->multiDrawPrimitives) + { + R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawMinIndex, input->multiDrawMaxIndex, input->multiDrawNumIndexes, input->multiDrawFirstIndex); + } + else + { + R_DrawElementsVBO(input->numIndexes, input->firstIndex, input->minIndex, input->maxIndex); + } + + backEnd.pc.c_totalIndexes += tess.numIndexes; + backEnd.pc.c_dlightIndexes += tess.numIndexes; + } +} + + +static void ForwardSunlight( void ) { +// int l; + //vec3_t origin; + //float scale; + int stage; + int stageGlState[2]; + qboolean alphaOverride = qfalse; + + int deformGen; + vec5_t deformParams; + + vec4_t fogDistanceVector, fogDepthVector = {0, 0, 0, 0}; + float eyeT = 0; + + shaderCommands_t *input = &tess; + + ComputeDeformValues(&deformGen, deformParams); + + ComputeFogValues(fogDistanceVector, fogDepthVector, &eyeT); + + // deal with vertex alpha blended surfaces + if (input->xstages[0] && input->xstages[1] && + (input->xstages[1]->alphaGen == AGEN_VERTEX || input->xstages[1]->alphaGen == AGEN_ONE_MINUS_VERTEX)) + { + stageGlState[0] = input->xstages[0]->stateBits & (GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS); + + if (stageGlState[0] == 0 || stageGlState[0] == (GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO)) + { + stageGlState[1] = input->xstages[1]->stateBits & (GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS); + + if (stageGlState[1] == (GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA)) + { + alphaOverride = qtrue; + stageGlState[0] = GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL; + stageGlState[1] = GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL; + } + else if (stageGlState[1] == (GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA | GLS_DSTBLEND_SRC_ALPHA)) + { + alphaOverride = qtrue; + stageGlState[0] = GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL; + stageGlState[1] = GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL; + } + } + } + + if (!alphaOverride) + { + stageGlState[0] = + stageGlState[1] = GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE | GLS_DEPTHFUNC_EQUAL; + } + + for ( stage = 0; stage < 2 /*MAX_SHADER_STAGES */; stage++ ) + { + shaderStage_t *pStage = input->xstages[stage]; + shaderProgram_t *sp; + vec4_t vector; + matrix_t matrix; + + if ( !pStage ) + { + break; + } + + //VectorCopy( dl->transformed, origin ); + + //if (pStage->glslShaderGroup == tr.lightallShader) + { + int index = pStage->glslShaderIndex; + + index &= ~(LIGHTDEF_LIGHTTYPE_MASK | LIGHTDEF_USE_DELUXEMAP); + index |= LIGHTDEF_USE_LIGHT_VECTOR | LIGHTDEF_USE_SHADOWMAP; + + if (backEnd.currentEntity && backEnd.currentEntity != &tr.worldEntity) + { + index |= LIGHTDEF_ENTITY; + } + + sp = &tr.lightallShader[index]; + } + + backEnd.pc.c_lightallDraws++; + + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_VIEWORIGIN, backEnd.viewParms.or.origin); + + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); + + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_DEFORMGEN, deformGen); + if (deformGen != DGEN_NONE) + { + GLSL_SetUniformFloat5(sp, GENERIC_UNIFORM_DEFORMPARAMS, deformParams); + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_TIME, tess.shaderTime); + } + + if ( input->fogNum ) { + vec4_t fogColorMask; + + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGDISTANCE, fogDistanceVector); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGDEPTH, fogDepthVector); + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_FOGEYET, eyeT); + + ComputeFogColorMask(pStage, fogColorMask); + + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGCOLORMASK, fogColorMask); + } + + { + vec4_t baseColor; + vec4_t vertColor; + + ComputeShaderColors(pStage, baseColor, vertColor); + + if (alphaOverride) + { + if (input->xstages[1]->alphaGen == AGEN_VERTEX) + { + baseColor[3] = 0.0f; + vertColor[3] = 1.0f; + } + else if (input->xstages[1]->alphaGen == AGEN_ONE_MINUS_VERTEX) + { + baseColor[3] = 1.0f; + vertColor[3] = -1.0f; + } + } + + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_BASECOLOR, baseColor); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_VERTCOLOR, vertColor); + } + + if (pStage->alphaGen == AGEN_PORTAL) + { + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_PORTALRANGE, tess.shader->portalRange); + } + + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_COLORGEN, pStage->rgbGen); + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_ALPHAGEN, pStage->alphaGen); + + GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_DIRECTEDLIGHT, backEnd.refdef.sunCol); + GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_AMBIENTLIGHT, backEnd.refdef.sunAmbCol); + + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_LIGHTORIGIN, backEnd.refdef.sunDir); + + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_LIGHTRADIUS, 9999999999.9f); + + GLSL_SetUniformVec2(sp, GENERIC_UNIFORM_MATERIALINFO, pStage->materialInfo); + + GL_State( stageGlState[stage] ); + + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELMATRIX, backEnd.or.transformMatrix); + + if (pStage->bundle[TB_DIFFUSEMAP].image[0]) + R_BindAnimatedImageToTMU( &pStage->bundle[TB_DIFFUSEMAP], TB_DIFFUSEMAP); + + if (pStage->bundle[TB_NORMALMAP].image[0]) + R_BindAnimatedImageToTMU( &pStage->bundle[TB_NORMALMAP], TB_NORMALMAP); + + if (pStage->bundle[TB_SPECULARMAP].image[0]) + R_BindAnimatedImageToTMU( &pStage->bundle[TB_SPECULARMAP], TB_SPECULARMAP); + + /* + { + GL_BindToTMU(tr.sunShadowDepthImage[0], TB_SHADOWMAP); + GL_BindToTMU(tr.sunShadowDepthImage[1], TB_SHADOWMAP2); + GL_BindToTMU(tr.sunShadowDepthImage[2], TB_SHADOWMAP3); + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_SHADOWMVP, backEnd.refdef.sunShadowMvp[0]); + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_SHADOWMVP2, backEnd.refdef.sunShadowMvp[1]); + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_SHADOWMVP3, backEnd.refdef.sunShadowMvp[2]); + } + */ + GL_BindToTMU(tr.screenShadowImage, TB_SHADOWMAP); + + ComputeTexMatrix( pStage, TB_DIFFUSEMAP, matrix ); + + VectorSet4(vector, matrix[0], matrix[1], matrix[4], matrix[5]); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_DIFFUSETEXMATRIX, vector); + + VectorSet4(vector, matrix[8], matrix[9], matrix[12], matrix[13]); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_DIFFUSETEXOFFTURB, vector); + + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_TCGEN0, pStage->bundle[0].tcGen); + + // + // draw + // + + if (input->multiDrawPrimitives) + { + R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawMinIndex, input->multiDrawMaxIndex, input->multiDrawNumIndexes, input->multiDrawFirstIndex); + } + else + { + R_DrawElementsVBO(input->numIndexes, input->firstIndex, input->minIndex, input->maxIndex); + } + + backEnd.pc.c_totalIndexes += tess.numIndexes; + backEnd.pc.c_dlightIndexes += tess.numIndexes; + } +} + + +static void ProjectPshadowVBOGLSL( void ) { + int l; + vec3_t origin; + float radius; + + int deformGen; + vec5_t deformParams; + + shaderCommands_t *input = &tess; + + if ( !backEnd.refdef.num_pshadows ) { + return; + } + + ComputeDeformValues(&deformGen, deformParams); + + for ( l = 0 ; l < backEnd.refdef.num_pshadows ; l++ ) { + pshadow_t *ps; + shaderProgram_t *sp; + vec4_t vector; + + if ( !( tess.pshadowBits & ( 1 << l ) ) ) { + continue; // this surface definately doesn't have any of this shadow + } + + ps = &backEnd.refdef.pshadows[l]; + VectorCopy( ps->lightOrigin, origin ); + radius = ps->lightRadius; + + sp = &tr.pshadowShader; + + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, PSHADOW_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + + VectorCopy(origin, vector); + vector[3] = 1.0f; + GLSL_SetUniformVec4(sp, PSHADOW_UNIFORM_LIGHTORIGIN, vector); + + VectorScale(ps->lightViewAxis[0], 1.0f / ps->viewRadius, vector); + GLSL_SetUniformVec3(sp, PSHADOW_UNIFORM_LIGHTFORWARD, vector); + + VectorScale(ps->lightViewAxis[1], 1.0f / ps->viewRadius, vector); + GLSL_SetUniformVec3(sp, PSHADOW_UNIFORM_LIGHTRIGHT, vector); + + VectorScale(ps->lightViewAxis[2], 1.0f / ps->viewRadius, vector); + GLSL_SetUniformVec3(sp, PSHADOW_UNIFORM_LIGHTUP, vector); + + GLSL_SetUniformFloat(sp, PSHADOW_UNIFORM_LIGHTRADIUS, radius); + + // include GLS_DEPTHFUNC_EQUAL so alpha tested surfaces don't add light + // where they aren't rendered + GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_EQUAL ); + + GL_BindToTMU( tr.pshadowMaps[l], TB_DIFFUSEMAP ); + + // + // draw + // + + if (input->multiDrawPrimitives) + { + R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawMinIndex, input->multiDrawMaxIndex, input->multiDrawNumIndexes, input->multiDrawFirstIndex); + } + else + { + R_DrawElementsVBO(input->numIndexes, input->firstIndex, input->minIndex, input->maxIndex); + } + + backEnd.pc.c_totalIndexes += tess.numIndexes; + //backEnd.pc.c_dlightIndexes += tess.numIndexes; + } +} + + + +/* +=================== +RB_FogPass + +Blends a fog texture on top of everything else +=================== +*/ +static void RB_FogPass( void ) { + fog_t *fog; + vec4_t color; + vec4_t fogDistanceVector, fogDepthVector = {0, 0, 0, 0}; + float eyeT = 0; + shaderProgram_t *sp; + + int deformGen; + vec5_t deformParams; + + ComputeDeformValues(&deformGen, deformParams); + + { + int index = 0; + + if (deformGen != DGEN_NONE) + index |= FOGDEF_USE_DEFORM_VERTEXES; + + if (glState.vertexAttribsInterpolation) + index |= FOGDEF_USE_VERTEX_ANIMATION; + + sp = &tr.fogShader[index]; + } + + backEnd.pc.c_fogDraws++; + + GLSL_BindProgram(sp); + + fog = tr.world->fogs + tess.fogNum; + + GLSL_SetUniformMatrix16(sp, FOGPASS_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + + GLSL_SetUniformFloat(sp, FOGPASS_UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); + + GLSL_SetUniformInt(sp, FOGPASS_UNIFORM_DEFORMGEN, deformGen); + if (deformGen != DGEN_NONE) + { + GLSL_SetUniformFloat5(sp, FOGPASS_UNIFORM_DEFORMPARAMS, deformParams); + GLSL_SetUniformFloat(sp, FOGPASS_UNIFORM_TIME, tess.shaderTime); + } + + color[0] = ((unsigned char *)(&fog->colorInt))[0] / 255.0f; + color[1] = ((unsigned char *)(&fog->colorInt))[1] / 255.0f; + color[2] = ((unsigned char *)(&fog->colorInt))[2] / 255.0f; + color[3] = ((unsigned char *)(&fog->colorInt))[3] / 255.0f; + GLSL_SetUniformVec4(sp, FOGPASS_UNIFORM_COLOR, color); + + ComputeFogValues(fogDistanceVector, fogDepthVector, &eyeT); + + GLSL_SetUniformVec4(sp, FOGPASS_UNIFORM_FOGDISTANCE, fogDistanceVector); + GLSL_SetUniformVec4(sp, FOGPASS_UNIFORM_FOGDEPTH, fogDepthVector); + GLSL_SetUniformFloat(sp, FOGPASS_UNIFORM_FOGEYET, eyeT); + + if ( tess.shader->fogPass == FP_EQUAL ) { + GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_DEPTHFUNC_EQUAL ); + } else { + GL_State( GLS_SRCBLEND_SRC_ALPHA | GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ); + } + + if (tess.multiDrawPrimitives) + { + shaderCommands_t *input = &tess; + R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawMinIndex, input->multiDrawMaxIndex, input->multiDrawNumIndexes, input->multiDrawFirstIndex); + } + else + { + R_DrawElementsVBO(tess.numIndexes, tess.firstIndex, tess.minIndex, tess.maxIndex); + } +} + + +static unsigned int RB_CalcShaderVertexAttribs( shaderCommands_t *input ) +{ + unsigned int vertexAttribs = input->shader->vertexAttribs; + + if(glState.vertexAttribsInterpolation > 0.0f) + { + vertexAttribs |= ATTR_POSITION2; + if (vertexAttribs & ATTR_NORMAL) + { + vertexAttribs |= ATTR_NORMAL2; +#ifdef USE_VERT_TANGENT_SPACE + vertexAttribs |= ATTR_TANGENT2; + vertexAttribs |= ATTR_BITANGENT2; +#endif + } + } + + return vertexAttribs; +} + +static void RB_IterateStagesGeneric( shaderCommands_t *input ) +{ + int stage; + matrix_t matrix; + + vec4_t fogDistanceVector, fogDepthVector = {0, 0, 0, 0}; + float eyeT = 0; + + int deformGen; + vec5_t deformParams; + + ComputeDeformValues(&deformGen, deformParams); + + ComputeFogValues(fogDistanceVector, fogDepthVector, &eyeT); + + for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) + { + shaderStage_t *pStage = input->xstages[stage]; + shaderProgram_t *sp; + + if ( !pStage ) + { + break; + } + + if (backEnd.depthFill) + { + if (pStage->glslShaderGroup) + { + int index = 0; + + if (backEnd.currentEntity && backEnd.currentEntity != &tr.worldEntity) + { + index |= LIGHTDEF_ENTITY; + } + + if (pStage->stateBits & GLS_ATEST_BITS) + { + index |= LIGHTDEF_USE_TCGEN_AND_TCMOD; + } + + sp = &pStage->glslShaderGroup[index]; + } + else + { + int shaderAttribs = 0; + + if (tess.shader->numDeforms && !ShaderRequiresCPUDeforms(tess.shader)) + { + shaderAttribs |= GENERICDEF_USE_DEFORM_VERTEXES; + } + + if (glState.vertexAttribsInterpolation > 0.0f && backEnd.currentEntity && backEnd.currentEntity != &tr.worldEntity) + { + shaderAttribs |= GENERICDEF_USE_VERTEX_ANIMATION; + } + + if (pStage->stateBits & GLS_ATEST_BITS) + { + shaderAttribs |= GENERICDEF_USE_TCGEN_AND_TCMOD; + } + + sp = &tr.genericShader[shaderAttribs]; + } + } + else if (pStage->glslShaderGroup) + { + int index = pStage->glslShaderIndex; + + if (backEnd.currentEntity && backEnd.currentEntity != &tr.worldEntity) + { + index |= LIGHTDEF_ENTITY; + } + + if (r_lightmap->integer && index & LIGHTDEF_USE_LIGHTMAP) + { + index = LIGHTDEF_USE_LIGHTMAP; + } + + sp = &pStage->glslShaderGroup[index]; + + if (pStage->glslShaderGroup == tr.lightallShader) + { + backEnd.pc.c_lightallDraws++; + } + } + else + { + sp = GLSL_GetGenericShaderProgram(stage); + + backEnd.pc.c_genericDraws++; + } + + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_VIEWORIGIN, backEnd.viewParms.or.origin); + + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); + + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_DEFORMGEN, deformGen); + if (deformGen != DGEN_NONE) + { + GLSL_SetUniformFloat5(sp, GENERIC_UNIFORM_DEFORMPARAMS, deformParams); + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_TIME, tess.shaderTime); + } + + if ( input->fogNum ) { + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGDISTANCE, fogDistanceVector); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGDEPTH, fogDepthVector); + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_FOGEYET, eyeT); + } + + GL_State( pStage->stateBits ); + + { + vec4_t baseColor; + vec4_t vertColor; + qboolean tint = qtrue; + int stage2; + + ComputeShaderColors(pStage, baseColor, vertColor); + + for ( stage2 = stage + 1; stage2 < MAX_SHADER_STAGES; stage2++ ) + { + shaderStage_t *pStage2 = input->xstages[stage2]; + unsigned int srcBlendBits; + //unsigned int dstBlendBits; + + if ( !pStage2 ) + { + break; + } + + srcBlendBits = pStage2->stateBits & GLS_SRCBLEND_BITS; + //dstBlendBits = pStage2->stateBits & GLS_DSTBLEND_BITS; + + if (srcBlendBits == GLS_SRCBLEND_DST_COLOR) + { + tint = qfalse; + break; + } + } + + if (!((tr.sunShadows || r_forceSun->integer) && tess.shader->sort <= SS_OPAQUE + && !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) && tess.xstages[0]->glslShaderGroup == tr.lightallShader)) + { + tint = qfalse; + } + + if (tint) + { + // use VectorScale to only scale first three values, not alpha + VectorScale(baseColor, backEnd.refdef.colorScale, baseColor); + VectorScale(vertColor, backEnd.refdef.colorScale, vertColor); + } + + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_BASECOLOR, baseColor); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_VERTCOLOR, vertColor); + } + + if (pStage->rgbGen == CGEN_LIGHTING_DIFFUSE) + { + vec4_t vec; + + VectorScale(backEnd.currentEntity->ambientLight, 1.0f / 255.0f, vec); + GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_AMBIENTLIGHT, vec); + + VectorScale(backEnd.currentEntity->directedLight, 1.0f / 255.0f, vec); + GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_DIRECTEDLIGHT, vec); + + VectorCopy(backEnd.currentEntity->lightDir, vec); + vec[3] = 0.0f; + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_LIGHTORIGIN, vec); + + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_LIGHTRADIUS, 999999.0f); + } + + if (pStage->alphaGen == AGEN_PORTAL) + { + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_PORTALRANGE, tess.shader->portalRange); + } + + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_COLORGEN, pStage->rgbGen); + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_ALPHAGEN, pStage->alphaGen); + + if ( input->fogNum ) + { + vec4_t fogColorMask; + + ComputeFogColorMask(pStage, fogColorMask); + + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_FOGCOLORMASK, fogColorMask); + } + + ComputeTexMatrix( pStage, TB_DIFFUSEMAP, matrix ); + + { + vec4_t vector; + VectorSet4(vector, matrix[0], matrix[1], matrix[4], matrix[5]); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_DIFFUSETEXMATRIX, vector); + + VectorSet4(vector, matrix[8], matrix[9], matrix[12], matrix[13]); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_DIFFUSETEXOFFTURB, vector); + } + + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_TCGEN0, pStage->bundle[0].tcGen); + if (pStage->bundle[0].tcGen == TCGEN_VECTOR) + { + vec3_t vec; + + VectorCopy(pStage->bundle[0].tcGenVectors[0], vec); + GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_TCGEN0VECTOR0, vec); + VectorCopy(pStage->bundle[0].tcGenVectors[1], vec); + GLSL_SetUniformVec3(sp, GENERIC_UNIFORM_TCGEN0VECTOR1, vec); + } + + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELMATRIX, backEnd.or.transformMatrix); + + GLSL_SetUniformVec2(sp, GENERIC_UNIFORM_MATERIALINFO, pStage->materialInfo); + + //GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_MAPLIGHTSCALE, backEnd.refdef.mapLightScale); + + // + // do multitexture + // + if ( backEnd.depthFill ) + { + if (!(pStage->stateBits & GLS_ATEST_BITS)) + GL_BindToTMU( tr.whiteImage, 0 ); + else if ( pStage->bundle[TB_COLORMAP].image[0] != 0 ) + R_BindAnimatedImageToTMU( &pStage->bundle[TB_COLORMAP], TB_COLORMAP ); + } + else if ( pStage->glslShaderGroup ) + { + int i; + + if ((r_lightmap->integer == 1 || r_lightmap->integer == 2) && pStage->bundle[TB_LIGHTMAP].image[0]) + { + for (i = 0; i < NUM_TEXTURE_BUNDLES; i++) + { + if (i == TB_LIGHTMAP) + { + R_BindAnimatedImageToTMU( &pStage->bundle[i], i); + } + else if (pStage->bundle[i].image[0]) + { + GL_BindToTMU( tr.whiteImage, i); + } + } + } + else if (r_lightmap->integer == 3 && pStage->bundle[TB_DELUXEMAP].image[0]) + { + for (i = 0; i < NUM_TEXTURE_BUNDLES; i++) + { + if (i == TB_LIGHTMAP) + { + R_BindAnimatedImageToTMU( &pStage->bundle[TB_DELUXEMAP], i); + } + else if (pStage->bundle[i].image[0]) + { + GL_BindToTMU( tr.whiteImage, i); + } + } + } + else + { + for (i = 0; i < NUM_TEXTURE_BUNDLES; i++) + { + if (pStage->bundle[i].image[0]) + { + R_BindAnimatedImageToTMU( &pStage->bundle[i], i); + } + } + } + } + else if ( pStage->bundle[1].image[0] != 0 ) + { + R_BindAnimatedImageToTMU( &pStage->bundle[0], 0 ); + + // + // lightmap/secondary pass + // + if ( r_lightmap->integer ) { + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_TEXTURE1ENV, GL_REPLACE); + } else { + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_TEXTURE1ENV, tess.shader->multitextureEnv); + } + + R_BindAnimatedImageToTMU( &pStage->bundle[1], 1 ); + } + else + { + // + // set state + // + if ( pStage->bundle[0].vertexLightmap && ( (r_vertexLight->integer && !r_uiFullScreen->integer) || glConfig.hardwareType == GLHW_PERMEDIA2 ) && r_lightmap->integer ) + { + GL_BindToTMU( tr.whiteImage, 0 ); + } + else + R_BindAnimatedImageToTMU( &pStage->bundle[0], 0 ); + + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_TEXTURE1ENV, 0); + } + + // + // draw + // + if (input->multiDrawPrimitives) + { + R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawMinIndex, input->multiDrawMaxIndex, input->multiDrawNumIndexes, input->multiDrawFirstIndex); + } + else + { + R_DrawElementsVBO(input->numIndexes, input->firstIndex, input->minIndex, input->maxIndex); + } + + // allow skipping out to show just lightmaps during development + if ( r_lightmap->integer && ( pStage->bundle[0].isLightmap || pStage->bundle[1].isLightmap || pStage->bundle[0].vertexLightmap ) ) + { + break; + } + + if (backEnd.depthFill) + break; + } +} + + +static void RB_RenderShadowmap( shaderCommands_t *input ) +{ + int deformGen; + vec5_t deformParams; + + ComputeDeformValues(&deformGen, deformParams); + + { + shaderProgram_t *sp = &tr.shadowmapShader; + + vec4_t vector; + + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELMATRIX, backEnd.or.transformMatrix); + + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_VERTEXLERP, glState.vertexAttribsInterpolation); + + GLSL_SetUniformInt(sp, GENERIC_UNIFORM_DEFORMGEN, deformGen); + if (deformGen != DGEN_NONE) + { + GLSL_SetUniformFloat5(sp, GENERIC_UNIFORM_DEFORMPARAMS, deformParams); + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_TIME, tess.shaderTime); + } + + VectorCopy(backEnd.viewParms.or.origin, vector); + vector[3] = 1.0f; + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_LIGHTORIGIN, vector); + GLSL_SetUniformFloat(sp, GENERIC_UNIFORM_LIGHTRADIUS, backEnd.viewParms.zFar); + + GL_State( 0 ); + + // + // do multitexture + // + //if ( pStage->glslShaderGroup ) + { + // + // draw + // + + if (input->multiDrawPrimitives) + { + R_DrawMultiElementsVBO(input->multiDrawPrimitives, input->multiDrawMinIndex, input->multiDrawMaxIndex, input->multiDrawNumIndexes, input->multiDrawFirstIndex); + } + else + { + R_DrawElementsVBO(input->numIndexes, input->firstIndex, input->minIndex, input->maxIndex); + } + } + } +} + + + +/* +** RB_StageIteratorGeneric +*/ +void RB_StageIteratorGeneric( void ) +{ + shaderCommands_t *input; + unsigned int vertexAttribs = 0; + + input = &tess; + + if (!input->numVertexes || !input->numIndexes) + { + return; + } + + if (tess.useInternalVBO) + { + RB_DeformTessGeometry(); + } + + vertexAttribs = RB_CalcShaderVertexAttribs( input ); + + if (tess.useInternalVBO) + { + RB_UpdateVBOs(vertexAttribs); + } + else + { + backEnd.pc.c_staticVboDraws++; + } + + // + // log this call + // + if ( r_logFile->integer ) + { + // don't just call LogComment, or we will get + // a call to va() every frame! + GLimp_LogComment( va("--- RB_StageIteratorGeneric( %s ) ---\n", tess.shader->name) ); + } + + // + // set face culling appropriately + // + if ((backEnd.viewParms.flags & VPF_DEPTHSHADOW)) + { + //GL_Cull( CT_TWO_SIDED ); + + if (input->shader->cullType == CT_TWO_SIDED) + GL_Cull( CT_TWO_SIDED ); + else if (input->shader->cullType == CT_FRONT_SIDED) + GL_Cull( CT_BACK_SIDED ); + else + GL_Cull( CT_FRONT_SIDED ); + + } + else + GL_Cull( input->shader->cullType ); + + // set polygon offset if necessary + if ( input->shader->polygonOffset ) + { + qglEnable( GL_POLYGON_OFFSET_FILL ); + qglPolygonOffset( r_offsetFactor->value, r_offsetUnits->value ); + } + + // + // Set vertex attribs and pointers + // + GLSL_VertexAttribsState(vertexAttribs); + + // + // render depth if in depthfill mode + // + if (backEnd.depthFill) + { + RB_IterateStagesGeneric( input ); + + // + // reset polygon offset + // + if ( input->shader->polygonOffset ) + { + qglDisable( GL_POLYGON_OFFSET_FILL ); + } + + return; + } + + // + // render shadowmap if in shadowmap mode + // + if (backEnd.viewParms.flags & VPF_SHADOWMAP) + { + if ( input->shader->sort == SS_OPAQUE ) + { + RB_RenderShadowmap( input ); + } + // + // reset polygon offset + // + if ( input->shader->polygonOffset ) + { + qglDisable( GL_POLYGON_OFFSET_FILL ); + } + + return; + } + + // + // + // call shader function + // + RB_IterateStagesGeneric( input ); + + // + // pshadows! + // + if (glRefConfig.framebufferObject && tess.pshadowBits && tess.shader->sort <= SS_OPAQUE + && !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) ) { + ProjectPshadowVBOGLSL(); + } + + + // + // now do any dynamic lighting needed + // + if ( tess.dlightBits && tess.shader->sort <= SS_OPAQUE + && !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) ) { + if (tess.shader->numUnfoggedPasses == 1 && tess.xstages[0]->glslShaderGroup == tr.lightallShader + && (tess.xstages[0]->glslShaderIndex & LIGHTDEF_LIGHTTYPE_MASK) && r_dlightMode->integer) + { + ForwardDlight(); + } + else + { + ProjectDlightTexture(); + } + } + + if ((backEnd.viewParms.flags & VPF_USESUNLIGHT) && tess.shader->sort <= SS_OPAQUE + //if ((tr.sunShadows || r_forceSunlight->value > 0.0f) && tess.shader->sort <= SS_OPAQUE + && !(tess.shader->surfaceFlags & (SURF_NODLIGHT | SURF_SKY) ) && tess.xstages[0]->glslShaderGroup == tr.lightallShader) { + ForwardSunlight(); + } + + // + // now do fog + // + if ( tess.fogNum && tess.shader->fogPass ) { + RB_FogPass(); + } + + // + // reset polygon offset + // + if ( input->shader->polygonOffset ) + { + qglDisable( GL_POLYGON_OFFSET_FILL ); + } +} + + +/* +** RB_EndSurface +*/ +void RB_EndSurface( void ) { + shaderCommands_t *input; + + input = &tess; + + if (input->numIndexes == 0 || input->numVertexes == 0) { + return; + } + + if (input->indexes[SHADER_MAX_INDEXES-1] != 0) { + ri.Error (ERR_DROP, "RB_EndSurface() - SHADER_MAX_INDEXES hit"); + } + if (input->xyz[SHADER_MAX_VERTEXES-1][0] != 0) { + ri.Error (ERR_DROP, "RB_EndSurface() - SHADER_MAX_VERTEXES hit"); + } + + if ( tess.shader == tr.shadowShader ) { + RB_ShadowTessEnd(); + return; + } + + // for debugging of sort order issues, stop rendering after a given sort value + if ( r_debugSort->integer && r_debugSort->integer < tess.shader->sort ) { + return; + } + + // + // update performance counters + // + backEnd.pc.c_shaders++; + backEnd.pc.c_vertexes += tess.numVertexes; + backEnd.pc.c_indexes += tess.numIndexes; + backEnd.pc.c_totalIndexes += tess.numIndexes * tess.numPasses; + + // + // call off to shader specific tess end function + // + tess.currentStageIteratorFunc(); + + // + // draw debugging stuff + // + if ( r_showtris->integer ) { + DrawTris (input); + } + if ( r_shownormals->integer ) { + DrawNormals (input); + } + // clear shader so we can tell we don't have any unclosed surfaces + tess.numIndexes = 0; + tess.numVertexes = 0; + tess.firstIndex = 0; + tess.multiDrawPrimitives = 0; + + GLimp_LogComment( "----------\n" ); +} diff --git a/src/renderergl2/tr_shade_calc.c b/src/renderergl2/tr_shade_calc.c new file mode 100644 index 00000000..9421f646 --- /dev/null +++ b/src/renderergl2/tr_shade_calc.c @@ -0,0 +1,1339 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_shade_calc.c + +#include "tr_local.h" +#if idppc_altivec && !defined(MACOS_X) +#include +#endif + + +#define WAVEVALUE( table, base, amplitude, phase, freq ) ((base) + table[ ri.ftol( ( ( (phase) + tess.shaderTime * (freq) ) * FUNCTABLE_SIZE ) ) & FUNCTABLE_MASK ] * (amplitude)) + +static float *TableForFunc( genFunc_t func ) +{ + switch ( func ) + { + case GF_SIN: + return tr.sinTable; + case GF_TRIANGLE: + return tr.triangleTable; + case GF_SQUARE: + return tr.squareTable; + case GF_SAWTOOTH: + return tr.sawToothTable; + case GF_INVERSE_SAWTOOTH: + return tr.inverseSawToothTable; + case GF_NONE: + default: + break; + } + + ri.Error( ERR_DROP, "TableForFunc called with invalid function '%d' in shader '%s'", func, tess.shader->name ); + return NULL; +} + +/* +** EvalWaveForm +** +** Evaluates a given waveForm_t, referencing backEnd.refdef.time directly +*/ +static float EvalWaveForm( const waveForm_t *wf ) +{ + float *table; + + table = TableForFunc( wf->func ); + + return WAVEVALUE( table, wf->base, wf->amplitude, wf->phase, wf->frequency ); +} + +static float EvalWaveFormClamped( const waveForm_t *wf ) +{ + float glow = EvalWaveForm( wf ); + + if ( glow < 0 ) + { + return 0; + } + + if ( glow > 1 ) + { + return 1; + } + + return glow; +} + +/* +** RB_CalcStretchTexCoords +*/ +void RB_CalcStretchTexCoords( const waveForm_t *wf, float *st ) +{ + float p; + texModInfo_t tmi; + + p = 1.0f / EvalWaveForm( wf ); + + tmi.matrix[0][0] = p; + tmi.matrix[1][0] = 0; + tmi.translate[0] = 0.5f - 0.5f * p; + + tmi.matrix[0][1] = 0; + tmi.matrix[1][1] = p; + tmi.translate[1] = 0.5f - 0.5f * p; + + RB_CalcTransformTexCoords( &tmi, st ); +} + +void RB_CalcStretchTexMatrix( const waveForm_t *wf, float *matrix ) +{ + float p; + texModInfo_t tmi; + + p = 1.0f / EvalWaveForm( wf ); + + tmi.matrix[0][0] = p; + tmi.matrix[1][0] = 0; + tmi.translate[0] = 0.5f - 0.5f * p; + + tmi.matrix[0][1] = 0; + tmi.matrix[1][1] = p; + tmi.translate[1] = 0.5f - 0.5f * p; + + RB_CalcTransformTexMatrix( &tmi, matrix ); +} + +/* +==================================================================== + +DEFORMATIONS + +==================================================================== +*/ + +/* +======================== +RB_CalcDeformVertexes + +======================== +*/ +void RB_CalcDeformVertexes( deformStage_t *ds ) +{ + int i; + vec3_t offset; + float scale; + float *xyz = ( float * ) tess.xyz; + float *normal = ( float * ) tess.normal; + float *table; + + if ( ds->deformationWave.frequency == 0 ) + { + scale = EvalWaveForm( &ds->deformationWave ); + + for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 ) + { + VectorScale( normal, scale, offset ); + + xyz[0] += offset[0]; + xyz[1] += offset[1]; + xyz[2] += offset[2]; + } + } + else + { + table = TableForFunc( ds->deformationWave.func ); + + for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 ) + { + float off = ( xyz[0] + xyz[1] + xyz[2] ) * ds->deformationSpread; + + scale = WAVEVALUE( table, ds->deformationWave.base, + ds->deformationWave.amplitude, + ds->deformationWave.phase + off, + ds->deformationWave.frequency ); + + VectorScale( normal, scale, offset ); + + xyz[0] += offset[0]; + xyz[1] += offset[1]; + xyz[2] += offset[2]; + } + } +} + +/* +========================= +RB_CalcDeformNormals + +Wiggle the normals for wavy environment mapping +========================= +*/ +void RB_CalcDeformNormals( deformStage_t *ds ) { + int i; + float scale; + float *xyz = ( float * ) tess.xyz; + float *normal = ( float * ) tess.normal; + + for ( i = 0; i < tess.numVertexes; i++, xyz += 4, normal += 4 ) { + scale = 0.98f; + scale = R_NoiseGet4f( xyz[0] * scale, xyz[1] * scale, xyz[2] * scale, + tess.shaderTime * ds->deformationWave.frequency ); + normal[ 0 ] += ds->deformationWave.amplitude * scale; + + scale = 0.98f; + scale = R_NoiseGet4f( 100 + xyz[0] * scale, xyz[1] * scale, xyz[2] * scale, + tess.shaderTime * ds->deformationWave.frequency ); + normal[ 1 ] += ds->deformationWave.amplitude * scale; + + scale = 0.98f; + scale = R_NoiseGet4f( 200 + xyz[0] * scale, xyz[1] * scale, xyz[2] * scale, + tess.shaderTime * ds->deformationWave.frequency ); + normal[ 2 ] += ds->deformationWave.amplitude * scale; + + VectorNormalizeFast( normal ); + } +} + +/* +======================== +RB_CalcBulgeVertexes + +======================== +*/ +void RB_CalcBulgeVertexes( deformStage_t *ds ) { + int i; + const float *st = ( const float * ) tess.texCoords[0]; + float *xyz = ( float * ) tess.xyz; + float *normal = ( float * ) tess.normal; + float now; + + now = backEnd.refdef.time * ds->bulgeSpeed * 0.001f; + + for ( i = 0; i < tess.numVertexes; i++, xyz += 4, st += 4, normal += 4 ) { + int off; + float scale; + + off = (float)( FUNCTABLE_SIZE / (M_PI*2) ) * ( st[0] * ds->bulgeWidth + now ); + + scale = tr.sinTable[ off & FUNCTABLE_MASK ] * ds->bulgeHeight; + + xyz[0] += normal[0] * scale; + xyz[1] += normal[1] * scale; + xyz[2] += normal[2] * scale; + } +} + + +/* +====================== +RB_CalcMoveVertexes + +A deformation that can move an entire surface along a wave path +====================== +*/ +void RB_CalcMoveVertexes( deformStage_t *ds ) { + int i; + float *xyz; + float *table; + float scale; + vec3_t offset; + + table = TableForFunc( ds->deformationWave.func ); + + scale = WAVEVALUE( table, ds->deformationWave.base, + ds->deformationWave.amplitude, + ds->deformationWave.phase, + ds->deformationWave.frequency ); + + VectorScale( ds->moveVector, scale, offset ); + + xyz = ( float * ) tess.xyz; + for ( i = 0; i < tess.numVertexes; i++, xyz += 4 ) { + VectorAdd( xyz, offset, xyz ); + } +} + + +/* +============= +DeformText + +Change a polygon into a bunch of text polygons +============= +*/ +void DeformText( const char *text ) { + int i; + vec3_t origin, width, height; + int len; + int ch; + float color[4]; + float bottom, top; + vec3_t mid; + + height[0] = 0; + height[1] = 0; + height[2] = -1; + CrossProduct( tess.normal[0], height, width ); + + // find the midpoint of the box + VectorClear( mid ); + bottom = 999999; + top = -999999; + for ( i = 0 ; i < 4 ; i++ ) { + VectorAdd( tess.xyz[i], mid, mid ); + if ( tess.xyz[i][2] < bottom ) { + bottom = tess.xyz[i][2]; + } + if ( tess.xyz[i][2] > top ) { + top = tess.xyz[i][2]; + } + } + VectorScale( mid, 0.25f, origin ); + + // determine the individual character size + height[0] = 0; + height[1] = 0; + height[2] = ( top - bottom ) * 0.5f; + + VectorScale( width, height[2] * -0.75f, width ); + + // determine the starting position + len = strlen( text ); + VectorMA( origin, (len-1), width, origin ); + + // clear the shader indexes + tess.numIndexes = 0; + tess.numVertexes = 0; + tess.firstIndex = 0; + + color[0] = color[1] = color[2] = color[3] = 1.0f; + + // draw each character + for ( i = 0 ; i < len ; i++ ) { + ch = text[i]; + ch &= 255; + + if ( ch != ' ' ) { + int row, col; + float frow, fcol, size; + + row = ch>>4; + col = ch&15; + + frow = row*0.0625f; + fcol = col*0.0625f; + size = 0.0625f; + + RB_AddQuadStampExt( origin, width, height, color, fcol, frow, fcol + size, frow + size ); + } + VectorMA( origin, -2, width, origin ); + } +} + +/* +================== +GlobalVectorToLocal +================== +*/ +static void GlobalVectorToLocal( const vec3_t in, vec3_t out ) { + out[0] = DotProduct( in, backEnd.or.axis[0] ); + out[1] = DotProduct( in, backEnd.or.axis[1] ); + out[2] = DotProduct( in, backEnd.or.axis[2] ); +} + +/* +===================== +AutospriteDeform + +Assuming all the triangles for this shader are independant +quads, rebuild them as forward facing sprites +===================== +*/ +static void AutospriteDeform( void ) { + int i; + int oldVerts; + float *xyz; + vec3_t mid, delta; + float radius; + vec3_t left, up; + vec3_t leftDir, upDir; + + if ( tess.numVertexes & 3 ) { + ri.Printf( PRINT_WARNING, "Autosprite shader %s had odd vertex count\n", tess.shader->name ); + } + if ( tess.numIndexes != ( tess.numVertexes >> 2 ) * 6 ) { + ri.Printf( PRINT_WARNING, "Autosprite shader %s had odd index count\n", tess.shader->name ); + } + + oldVerts = tess.numVertexes; + tess.numVertexes = 0; + tess.numIndexes = 0; + tess.firstIndex = 0; + + if ( backEnd.currentEntity != &tr.worldEntity ) { + GlobalVectorToLocal( backEnd.viewParms.or.axis[1], leftDir ); + GlobalVectorToLocal( backEnd.viewParms.or.axis[2], upDir ); + } else { + VectorCopy( backEnd.viewParms.or.axis[1], leftDir ); + VectorCopy( backEnd.viewParms.or.axis[2], upDir ); + } + + for ( i = 0 ; i < oldVerts ; i+=4 ) { + // find the midpoint + xyz = tess.xyz[i]; + + mid[0] = 0.25f * (xyz[0] + xyz[4] + xyz[8] + xyz[12]); + mid[1] = 0.25f * (xyz[1] + xyz[5] + xyz[9] + xyz[13]); + mid[2] = 0.25f * (xyz[2] + xyz[6] + xyz[10] + xyz[14]); + + VectorSubtract( xyz, mid, delta ); + radius = VectorLength( delta ) * 0.707f; // / sqrt(2) + + VectorScale( leftDir, radius, left ); + VectorScale( upDir, radius, up ); + + if ( backEnd.viewParms.isMirror ) { + VectorSubtract( vec3_origin, left, left ); + } + + // compensate for scale in the axes if necessary + if ( backEnd.currentEntity->e.nonNormalizedAxes ) { + float axisLength; + axisLength = VectorLength( backEnd.currentEntity->e.axis[0] ); + if ( !axisLength ) { + axisLength = 0; + } else { + axisLength = 1.0f / axisLength; + } + VectorScale(left, axisLength, left); + VectorScale(up, axisLength, up); + } + + RB_AddQuadStamp( mid, left, up, tess.vertexColors[i] ); + } +} + + +/* +===================== +Autosprite2Deform + +Autosprite2 will pivot a rectangular quad along the center of its long axis +===================== +*/ +int edgeVerts[6][2] = { + { 0, 1 }, + { 0, 2 }, + { 0, 3 }, + { 1, 2 }, + { 1, 3 }, + { 2, 3 } +}; + +static void Autosprite2Deform( void ) { + int i, j, k; + int indexes; + float *xyz; + vec3_t forward; + + if ( tess.numVertexes & 3 ) { + ri.Printf( PRINT_WARNING, "Autosprite2 shader %s had odd vertex count", tess.shader->name ); + } + if ( tess.numIndexes != ( tess.numVertexes >> 2 ) * 6 ) { + ri.Printf( PRINT_WARNING, "Autosprite2 shader %s had odd index count", tess.shader->name ); + } + + if ( backEnd.currentEntity != &tr.worldEntity ) { + GlobalVectorToLocal( backEnd.viewParms.or.axis[0], forward ); + } else { + VectorCopy( backEnd.viewParms.or.axis[0], forward ); + } + + // this is a lot of work for two triangles... + // we could precalculate a lot of it is an issue, but it would mess up + // the shader abstraction + for ( i = 0, indexes = 0 ; i < tess.numVertexes ; i+=4, indexes+=6 ) { + float lengths[2]; + int nums[2]; + vec3_t mid[2]; + vec3_t major, minor; + float *v1, *v2; + + // find the midpoint + xyz = tess.xyz[i]; + + // identify the two shortest edges + nums[0] = nums[1] = 0; + lengths[0] = lengths[1] = 999999; + + for ( j = 0 ; j < 6 ; j++ ) { + float l; + vec3_t temp; + + v1 = xyz + 4 * edgeVerts[j][0]; + v2 = xyz + 4 * edgeVerts[j][1]; + + VectorSubtract( v1, v2, temp ); + + l = DotProduct( temp, temp ); + if ( l < lengths[0] ) { + nums[1] = nums[0]; + lengths[1] = lengths[0]; + nums[0] = j; + lengths[0] = l; + } else if ( l < lengths[1] ) { + nums[1] = j; + lengths[1] = l; + } + } + + for ( j = 0 ; j < 2 ; j++ ) { + v1 = xyz + 4 * edgeVerts[nums[j]][0]; + v2 = xyz + 4 * edgeVerts[nums[j]][1]; + + mid[j][0] = 0.5f * (v1[0] + v2[0]); + mid[j][1] = 0.5f * (v1[1] + v2[1]); + mid[j][2] = 0.5f * (v1[2] + v2[2]); + } + + // find the vector of the major axis + VectorSubtract( mid[1], mid[0], major ); + + // cross this with the view direction to get minor axis + CrossProduct( major, forward, minor ); + VectorNormalize( minor ); + + // re-project the points + for ( j = 0 ; j < 2 ; j++ ) { + float l; + + v1 = xyz + 4 * edgeVerts[nums[j]][0]; + v2 = xyz + 4 * edgeVerts[nums[j]][1]; + + l = 0.5 * sqrt( lengths[j] ); + + // we need to see which direction this edge + // is used to determine direction of projection + for ( k = 0 ; k < 5 ; k++ ) { + if ( tess.indexes[ indexes + k ] == i + edgeVerts[nums[j]][0] + && tess.indexes[ indexes + k + 1 ] == i + edgeVerts[nums[j]][1] ) { + break; + } + } + + if ( k == 5 ) { + VectorMA( mid[j], l, minor, v1 ); + VectorMA( mid[j], -l, minor, v2 ); + } else { + VectorMA( mid[j], -l, minor, v1 ); + VectorMA( mid[j], l, minor, v2 ); + } + } + } +} + + +/* +===================== +RB_DeformTessGeometry + +===================== +*/ +void RB_DeformTessGeometry( void ) { + int i; + deformStage_t *ds; + + if(!ShaderRequiresCPUDeforms(tess.shader)) + { + // we don't need the following CPU deforms + return; + } + + for ( i = 0 ; i < tess.shader->numDeforms ; i++ ) { + ds = &tess.shader->deforms[ i ]; + + switch ( ds->deformation ) { + case DEFORM_NONE: + break; + case DEFORM_NORMALS: + RB_CalcDeformNormals( ds ); + break; + case DEFORM_WAVE: + RB_CalcDeformVertexes( ds ); + break; + case DEFORM_BULGE: + RB_CalcBulgeVertexes( ds ); + break; + case DEFORM_MOVE: + RB_CalcMoveVertexes( ds ); + break; + case DEFORM_PROJECTION_SHADOW: + RB_ProjectionShadowDeform(); + break; + case DEFORM_AUTOSPRITE: + AutospriteDeform(); + break; + case DEFORM_AUTOSPRITE2: + Autosprite2Deform(); + break; + case DEFORM_TEXT0: + case DEFORM_TEXT1: + case DEFORM_TEXT2: + case DEFORM_TEXT3: + case DEFORM_TEXT4: + case DEFORM_TEXT5: + case DEFORM_TEXT6: + case DEFORM_TEXT7: + DeformText( backEnd.refdef.text[ds->deformation - DEFORM_TEXT0] ); + break; + } + } +} + +/* +==================================================================== + +COLORS + +==================================================================== +*/ + + +/* +** RB_CalcColorFromEntity +*/ +void RB_CalcColorFromEntity( unsigned char *dstColors ) +{ + int i; + int *pColors = ( int * ) dstColors; + int c; + + if ( !backEnd.currentEntity ) + return; + + c = * ( int * ) backEnd.currentEntity->e.shaderRGBA; + + for ( i = 0; i < tess.numVertexes; i++, pColors++ ) + { + *pColors = c; + } +} + +/* +** RB_CalcColorFromOneMinusEntity +*/ +void RB_CalcColorFromOneMinusEntity( unsigned char *dstColors ) +{ + int i; + int *pColors = ( int * ) dstColors; + unsigned char invModulate[4]; + int c; + + if ( !backEnd.currentEntity ) + return; + + invModulate[0] = 255 - backEnd.currentEntity->e.shaderRGBA[0]; + invModulate[1] = 255 - backEnd.currentEntity->e.shaderRGBA[1]; + invModulate[2] = 255 - backEnd.currentEntity->e.shaderRGBA[2]; + invModulate[3] = 255 - backEnd.currentEntity->e.shaderRGBA[3]; // this trashes alpha, but the AGEN block fixes it + + c = * ( int * ) invModulate; + + for ( i = 0; i < tess.numVertexes; i++, pColors++ ) + { + *pColors = c; + } +} + +/* +** RB_CalcAlphaFromEntity +*/ +void RB_CalcAlphaFromEntity( unsigned char *dstColors ) +{ + int i; + + if ( !backEnd.currentEntity ) + return; + + dstColors += 3; + + for ( i = 0; i < tess.numVertexes; i++, dstColors += 4 ) + { + *dstColors = backEnd.currentEntity->e.shaderRGBA[3]; + } +} + +/* +** RB_CalcAlphaFromOneMinusEntity +*/ +void RB_CalcAlphaFromOneMinusEntity( unsigned char *dstColors ) +{ + int i; + + if ( !backEnd.currentEntity ) + return; + + dstColors += 3; + + for ( i = 0; i < tess.numVertexes; i++, dstColors += 4 ) + { + *dstColors = 0xff - backEnd.currentEntity->e.shaderRGBA[3]; + } +} + +/* +** RB_CalcWaveColorSingle +*/ +float RB_CalcWaveColorSingle( const waveForm_t *wf ) +{ + float glow; + + if ( wf->func == GF_NOISE ) { + glow = wf->base + R_NoiseGet4f( 0, 0, 0, ( tess.shaderTime + wf->phase ) * wf->frequency ) * wf->amplitude; + } else { + glow = EvalWaveForm( wf ) * tr.identityLight; + } + + if ( glow < 0 ) { + glow = 0; + } + else if ( glow > 1 ) { + glow = 1; + } + + return glow; +} + +/* +** RB_CalcWaveColor +*/ +void RB_CalcWaveColor( const waveForm_t *wf, unsigned char *dstColors ) +{ + int i; + int v; + float glow; + int *colors = ( int * ) dstColors; + byte color[4]; + + glow = RB_CalcWaveColorSingle( wf ); + + v = ri.ftol(255 * glow); + color[0] = color[1] = color[2] = v; + color[3] = 255; + v = *(int *)color; + + for ( i = 0; i < tess.numVertexes; i++, colors++ ) { + *colors = v; + } +} + +/* +** RB_CalcWaveAlphaSingle +*/ +float RB_CalcWaveAlphaSingle( const waveForm_t *wf ) +{ + return EvalWaveFormClamped( wf ); +} + +/* +** RB_CalcWaveAlpha +*/ +void RB_CalcWaveAlpha( const waveForm_t *wf, unsigned char *dstColors ) +{ + int i; + int v; + float glow; + + glow = EvalWaveFormClamped( wf ); + + v = 255 * glow; + + for ( i = 0; i < tess.numVertexes; i++, dstColors += 4 ) + { + dstColors[3] = v; + } +} + +/* +** RB_CalcModulateColorsByFog +*/ +void RB_CalcModulateColorsByFog( unsigned char *colors ) { + int i; + float texCoords[SHADER_MAX_VERTEXES][2]; + + // calculate texcoords so we can derive density + // this is not wasted, because it would only have + // been previously called if the surface was opaque + RB_CalcFogTexCoords( texCoords[0] ); + + for ( i = 0; i < tess.numVertexes; i++, colors += 4 ) { + float f = 1.0 - R_FogFactor( texCoords[i][0], texCoords[i][1] ); + colors[0] *= f; + colors[1] *= f; + colors[2] *= f; + } +} + +/* +** RB_CalcModulateAlphasByFog +*/ +void RB_CalcModulateAlphasByFog( unsigned char *colors ) { + int i; + float texCoords[SHADER_MAX_VERTEXES][2]; + + // calculate texcoords so we can derive density + // this is not wasted, because it would only have + // been previously called if the surface was opaque + RB_CalcFogTexCoords( texCoords[0] ); + + for ( i = 0; i < tess.numVertexes; i++, colors += 4 ) { + float f = 1.0 - R_FogFactor( texCoords[i][0], texCoords[i][1] ); + colors[3] *= f; + } +} + +/* +** RB_CalcModulateRGBAsByFog +*/ +void RB_CalcModulateRGBAsByFog( unsigned char *colors ) { + int i; + float texCoords[SHADER_MAX_VERTEXES][2]; + + // calculate texcoords so we can derive density + // this is not wasted, because it would only have + // been previously called if the surface was opaque + RB_CalcFogTexCoords( texCoords[0] ); + + for ( i = 0; i < tess.numVertexes; i++, colors += 4 ) { + float f = 1.0 - R_FogFactor( texCoords[i][0], texCoords[i][1] ); + colors[0] *= f; + colors[1] *= f; + colors[2] *= f; + colors[3] *= f; + } +} + + +/* +==================================================================== + +TEX COORDS + +==================================================================== +*/ + +/* +======================== +RB_CalcFogTexCoords + +To do the clipped fog plane really correctly, we should use +projected textures, but I don't trust the drivers and it +doesn't fit our shader data. +======================== +*/ +void RB_CalcFogTexCoords( float *st ) { + int i; + float *v; + float s, t; + float eyeT; + qboolean eyeOutside; + fog_t *fog; + vec3_t local; + vec4_t fogDistanceVector, fogDepthVector = {0, 0, 0, 0}; + + fog = tr.world->fogs + tess.fogNum; + + // all fogging distance is based on world Z units + VectorSubtract( backEnd.or.origin, backEnd.viewParms.or.origin, local ); + fogDistanceVector[0] = -backEnd.or.modelMatrix[2]; + fogDistanceVector[1] = -backEnd.or.modelMatrix[6]; + fogDistanceVector[2] = -backEnd.or.modelMatrix[10]; + fogDistanceVector[3] = DotProduct( local, backEnd.viewParms.or.axis[0] ); + + // scale the fog vectors based on the fog's thickness + fogDistanceVector[0] *= fog->tcScale; + fogDistanceVector[1] *= fog->tcScale; + fogDistanceVector[2] *= fog->tcScale; + fogDistanceVector[3] *= fog->tcScale; + + // rotate the gradient vector for this orientation + if ( fog->hasSurface ) { + fogDepthVector[0] = fog->surface[0] * backEnd.or.axis[0][0] + + fog->surface[1] * backEnd.or.axis[0][1] + fog->surface[2] * backEnd.or.axis[0][2]; + fogDepthVector[1] = fog->surface[0] * backEnd.or.axis[1][0] + + fog->surface[1] * backEnd.or.axis[1][1] + fog->surface[2] * backEnd.or.axis[1][2]; + fogDepthVector[2] = fog->surface[0] * backEnd.or.axis[2][0] + + fog->surface[1] * backEnd.or.axis[2][1] + fog->surface[2] * backEnd.or.axis[2][2]; + fogDepthVector[3] = -fog->surface[3] + DotProduct( backEnd.or.origin, fog->surface ); + + eyeT = DotProduct( backEnd.or.viewOrigin, fogDepthVector ) + fogDepthVector[3]; + } else { + eyeT = 1; // non-surface fog always has eye inside + } + + // see if the viewpoint is outside + // this is needed for clipping distance even for constant fog + + if ( eyeT < 0 ) { + eyeOutside = qtrue; + } else { + eyeOutside = qfalse; + } + + fogDistanceVector[3] += 1.0/512; + + // calculate density for each point + for (i = 0, v = tess.xyz[0] ; i < tess.numVertexes ; i++, v += 4) { + // calculate the length in fog + s = DotProduct( v, fogDistanceVector ) + fogDistanceVector[3]; + t = DotProduct( v, fogDepthVector ) + fogDepthVector[3]; + + // partially clipped fogs use the T axis + if ( eyeOutside ) { + if ( t < 1.0 ) { + t = 1.0/32; // point is outside, so no fogging + } else { + t = 1.0/32 + 30.0/32 * t / ( t - eyeT ); // cut the distance at the fog plane + } + } else { + if ( t < 0 ) { + t = 1.0/32; // point is outside, so no fogging + } else { + t = 31.0/32; + } + } + + st[0] = s; + st[1] = t; + st += 2; + } +} + + + +/* +** RB_CalcEnvironmentTexCoords +*/ +void RB_CalcEnvironmentTexCoords( float *st ) +{ + int i; + float *v, *normal; + vec3_t viewer, reflected; + float d; + + v = tess.xyz[0]; + normal = tess.normal[0]; + + for (i = 0 ; i < tess.numVertexes ; i++, v += 4, normal += 4, st += 2 ) + { + VectorSubtract (backEnd.or.viewOrigin, v, viewer); + VectorNormalizeFast (viewer); + + d = DotProduct (normal, viewer); + + reflected[0] = normal[0]*2*d - viewer[0]; + reflected[1] = normal[1]*2*d - viewer[1]; + reflected[2] = normal[2]*2*d - viewer[2]; + + st[0] = 0.5 + reflected[1] * 0.5; + st[1] = 0.5 - reflected[2] * 0.5; + } +} + +/* +** RB_CalcTurbulentTexCoords +*/ +void RB_CalcTurbulentTexCoords( const waveForm_t *wf, float *st ) +{ + int i; + float now; + + now = ( wf->phase + tess.shaderTime * wf->frequency ); + + for ( i = 0; i < tess.numVertexes; i++, st += 2 ) + { + float s = st[0]; + float t = st[1]; + + st[0] = s + tr.sinTable[ ( ( int ) ( ( ( tess.xyz[i][0] + tess.xyz[i][2] )* 1.0/128 * 0.125 + now ) * FUNCTABLE_SIZE ) ) & ( FUNCTABLE_MASK ) ] * wf->amplitude; + st[1] = t + tr.sinTable[ ( ( int ) ( ( tess.xyz[i][1] * 1.0/128 * 0.125 + now ) * FUNCTABLE_SIZE ) ) & ( FUNCTABLE_MASK ) ] * wf->amplitude; + } +} + +void RB_CalcTurbulentTexMatrix( const waveForm_t *wf, matrix_t matrix ) +{ + float now; + + now = ( wf->phase + tess.shaderTime * wf->frequency ); + + // bit of a hack here, hide amplitude and now in the matrix + // the vertex program will extract them and perform a turbulent pass last if it's nonzero + + matrix[ 0] = 1.0f; matrix[ 4] = 0.0f; matrix[ 8] = 0.0f; matrix[12] = wf->amplitude; + matrix[ 1] = 0.0f; matrix[ 5] = 1.0f; matrix[ 9] = 0.0f; matrix[13] = now; + matrix[ 2] = 0.0f; matrix[ 6] = 0.0f; matrix[10] = 1.0f; matrix[14] = 0.0f; + matrix[ 3] = 0.0f; matrix[ 7] = 0.0f; matrix[11] = 0.0f; matrix[15] = 1.0f; +} + +/* +** RB_CalcScaleTexCoords +*/ +void RB_CalcScaleTexCoords( const float scale[2], float *st ) +{ + int i; + + for ( i = 0; i < tess.numVertexes; i++, st += 2 ) + { + st[0] *= scale[0]; + st[1] *= scale[1]; + } +} + +void RB_CalcScaleTexMatrix( const float scale[2], float *matrix ) +{ + matrix[ 0] = scale[0]; matrix[ 4] = 0.0f; matrix[ 8] = 0.0f; matrix[12] = 0.0f; + matrix[ 1] = 0.0f; matrix[ 5] = scale[1]; matrix[ 9] = 0.0f; matrix[13] = 0.0f; + matrix[ 2] = 0.0f; matrix[ 6] = 0.0f; matrix[10] = 1.0f; matrix[14] = 0.0f; + matrix[ 3] = 0.0f; matrix[ 7] = 0.0f; matrix[11] = 0.0f; matrix[15] = 1.0f; +} + +/* +** RB_CalcScrollTexCoords +*/ +void RB_CalcScrollTexCoords( const float scrollSpeed[2], float *st ) +{ + int i; + float timeScale = tess.shaderTime; + float adjustedScrollS, adjustedScrollT; + + adjustedScrollS = scrollSpeed[0] * timeScale; + adjustedScrollT = scrollSpeed[1] * timeScale; + + // clamp so coordinates don't continuously get larger, causing problems + // with hardware limits + adjustedScrollS = adjustedScrollS - floor( adjustedScrollS ); + adjustedScrollT = adjustedScrollT - floor( adjustedScrollT ); + + for ( i = 0; i < tess.numVertexes; i++, st += 2 ) + { + st[0] += adjustedScrollS; + st[1] += adjustedScrollT; + } +} + +void RB_CalcScrollTexMatrix( const float scrollSpeed[2], float *matrix ) +{ + float timeScale = tess.shaderTime; + float adjustedScrollS, adjustedScrollT; + + adjustedScrollS = scrollSpeed[0] * timeScale; + adjustedScrollT = scrollSpeed[1] * timeScale; + + // clamp so coordinates don't continuously get larger, causing problems + // with hardware limits + adjustedScrollS = adjustedScrollS - floor( adjustedScrollS ); + adjustedScrollT = adjustedScrollT - floor( adjustedScrollT ); + + + matrix[ 0] = 1.0f; matrix[ 4] = 0.0f; matrix[ 8] = adjustedScrollS; matrix[12] = 0.0f; + matrix[ 1] = 0.0f; matrix[ 5] = 1.0f; matrix[ 9] = adjustedScrollT; matrix[13] = 0.0f; + matrix[ 2] = 0.0f; matrix[ 6] = 0.0f; matrix[10] = 1.0f; matrix[14] = 0.0f; + matrix[ 3] = 0.0f; matrix[ 7] = 0.0f; matrix[11] = 0.0f; matrix[15] = 1.0f; +} + +/* +** RB_CalcTransformTexCoords +*/ +void RB_CalcTransformTexCoords( const texModInfo_t *tmi, float *st ) +{ + int i; + + for ( i = 0; i < tess.numVertexes; i++, st += 2 ) + { + float s = st[0]; + float t = st[1]; + + st[0] = s * tmi->matrix[0][0] + t * tmi->matrix[1][0] + tmi->translate[0]; + st[1] = s * tmi->matrix[0][1] + t * tmi->matrix[1][1] + tmi->translate[1]; + } +} + +void RB_CalcTransformTexMatrix( const texModInfo_t *tmi, float *matrix ) +{ + matrix[ 0] = tmi->matrix[0][0]; matrix[ 4] = tmi->matrix[1][0]; matrix[ 8] = tmi->translate[0]; matrix[12] = 0.0f; + matrix[ 1] = tmi->matrix[0][1]; matrix[ 5] = tmi->matrix[1][1]; matrix[ 9] = tmi->translate[1]; matrix[13] = 0.0f; + matrix[ 2] = 0.0f; matrix[ 6] = 0.0f; matrix[10] = 1.0f; matrix[14] = 0.0f; + matrix[ 3] = 0.0f; matrix[ 7] = 0.0f; matrix[11] = 0.0f; matrix[15] = 1.0f; +} + +/* +** RB_CalcRotateTexCoords +*/ +void RB_CalcRotateTexCoords( float degsPerSecond, float *st ) +{ + float timeScale = tess.shaderTime; + float degs; + int index; + float sinValue, cosValue; + texModInfo_t tmi; + + degs = -degsPerSecond * timeScale; + index = degs * ( FUNCTABLE_SIZE / 360.0f ); + + sinValue = tr.sinTable[ index & FUNCTABLE_MASK ]; + cosValue = tr.sinTable[ ( index + FUNCTABLE_SIZE / 4 ) & FUNCTABLE_MASK ]; + + tmi.matrix[0][0] = cosValue; + tmi.matrix[1][0] = -sinValue; + tmi.translate[0] = 0.5 - 0.5 * cosValue + 0.5 * sinValue; + + tmi.matrix[0][1] = sinValue; + tmi.matrix[1][1] = cosValue; + tmi.translate[1] = 0.5 - 0.5 * sinValue - 0.5 * cosValue; + + RB_CalcTransformTexCoords( &tmi, st ); +} + +void RB_CalcRotateTexMatrix( float degsPerSecond, float *matrix ) +{ + float timeScale = tess.shaderTime; + float degs; + int index; + float sinValue, cosValue; + texModInfo_t tmi; + + degs = -degsPerSecond * timeScale; + index = degs * ( FUNCTABLE_SIZE / 360.0f ); + + sinValue = tr.sinTable[ index & FUNCTABLE_MASK ]; + cosValue = tr.sinTable[ ( index + FUNCTABLE_SIZE / 4 ) & FUNCTABLE_MASK ]; + + tmi.matrix[0][0] = cosValue; + tmi.matrix[1][0] = -sinValue; + tmi.translate[0] = 0.5 - 0.5 * cosValue + 0.5 * sinValue; + + tmi.matrix[0][1] = sinValue; + tmi.matrix[1][1] = cosValue; + tmi.translate[1] = 0.5 - 0.5 * sinValue - 0.5 * cosValue; + + RB_CalcTransformTexMatrix( &tmi, matrix ); +} +/* +** RB_CalcSpecularAlpha +** +** Calculates specular coefficient and places it in the alpha channel +*/ +vec3_t lightOrigin = { -960, 1980, 96 }; // FIXME: track dynamically + +void RB_CalcSpecularAlpha( unsigned char *alphas ) { + int i; + float *v, *normal; + vec3_t viewer, reflected; + float l, d; + int b; + vec3_t lightDir; + int numVertexes; + + v = tess.xyz[0]; + normal = tess.normal[0]; + + alphas += 3; + + numVertexes = tess.numVertexes; + for (i = 0 ; i < numVertexes ; i++, v += 4, normal += 4, alphas += 4) { + float ilength; + + VectorSubtract( lightOrigin, v, lightDir ); +// ilength = Q_rsqrt( DotProduct( lightDir, lightDir ) ); + VectorNormalizeFast( lightDir ); + + // calculate the specular color + d = DotProduct (normal, lightDir); +// d *= ilength; + + // we don't optimize for the d < 0 case since this tends to + // cause visual artifacts such as faceted "snapping" + reflected[0] = normal[0]*2*d - lightDir[0]; + reflected[1] = normal[1]*2*d - lightDir[1]; + reflected[2] = normal[2]*2*d - lightDir[2]; + + VectorSubtract (backEnd.or.viewOrigin, v, viewer); + ilength = Q_rsqrt( DotProduct( viewer, viewer ) ); + l = DotProduct (reflected, viewer); + l *= ilength; + + if (l < 0) { + b = 0; + } else { + l = l*l; + l = l*l; + b = l * 255; + if (b > 255) { + b = 255; + } + } + + *alphas = b; + } +} + +/* +** RB_CalcDiffuseColor +** +** The basic vertex lighting calc +*/ +#if idppc_altivec +static void RB_CalcDiffuseColor_altivec( unsigned char *colors ) +{ + int i; + float *v, *normal; + trRefEntity_t *ent; + int ambientLightInt; + vec3_t lightDir; + int numVertexes; + vector unsigned char vSel = VECCONST_UINT8(0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff, + 0x00, 0x00, 0x00, 0xff); + vector float ambientLightVec; + vector float directedLightVec; + vector float lightDirVec; + vector float normalVec0, normalVec1; + vector float incomingVec0, incomingVec1, incomingVec2; + vector float zero, jVec; + vector signed int jVecInt; + vector signed short jVecShort; + vector unsigned char jVecChar, normalPerm; + ent = backEnd.currentEntity; + ambientLightInt = ent->ambientLightInt; + // A lot of this could be simplified if we made sure + // entities light info was 16-byte aligned. + jVecChar = vec_lvsl(0, ent->ambientLight); + ambientLightVec = vec_ld(0, (vector float *)ent->ambientLight); + jVec = vec_ld(11, (vector float *)ent->ambientLight); + ambientLightVec = vec_perm(ambientLightVec,jVec,jVecChar); + + jVecChar = vec_lvsl(0, ent->directedLight); + directedLightVec = vec_ld(0,(vector float *)ent->directedLight); + jVec = vec_ld(11,(vector float *)ent->directedLight); + directedLightVec = vec_perm(directedLightVec,jVec,jVecChar); + + jVecChar = vec_lvsl(0, ent->lightDir); + lightDirVec = vec_ld(0,(vector float *)ent->lightDir); + jVec = vec_ld(11,(vector float *)ent->lightDir); + lightDirVec = vec_perm(lightDirVec,jVec,jVecChar); + + zero = (vector float)vec_splat_s8(0); + VectorCopy( ent->lightDir, lightDir ); + + v = tess.xyz[0]; + normal = tess.normal[0]; + + normalPerm = vec_lvsl(0,normal); + numVertexes = tess.numVertexes; + for (i = 0 ; i < numVertexes ; i++, v += 4, normal += 4) { + normalVec0 = vec_ld(0,(vector float *)normal); + normalVec1 = vec_ld(11,(vector float *)normal); + normalVec0 = vec_perm(normalVec0,normalVec1,normalPerm); + incomingVec0 = vec_madd(normalVec0, lightDirVec, zero); + incomingVec1 = vec_sld(incomingVec0,incomingVec0,4); + incomingVec2 = vec_add(incomingVec0,incomingVec1); + incomingVec1 = vec_sld(incomingVec1,incomingVec1,4); + incomingVec2 = vec_add(incomingVec2,incomingVec1); + incomingVec0 = vec_splat(incomingVec2,0); + incomingVec0 = vec_max(incomingVec0,zero); + normalPerm = vec_lvsl(12,normal); + jVec = vec_madd(incomingVec0, directedLightVec, ambientLightVec); + jVecInt = vec_cts(jVec,0); // RGBx + jVecShort = vec_pack(jVecInt,jVecInt); // RGBxRGBx + jVecChar = vec_packsu(jVecShort,jVecShort); // RGBxRGBxRGBxRGBx + jVecChar = vec_sel(jVecChar,vSel,vSel); // RGBARGBARGBARGBA replace alpha with 255 + vec_ste((vector unsigned int)jVecChar,0,(unsigned int *)&colors[i*4]); // store color + } +} +#endif + +static void RB_CalcDiffuseColor_scalar( unsigned char *colors ) +{ + int i, j; + float *v, *normal; + float incoming; + trRefEntity_t *ent; + int ambientLightInt; + vec3_t ambientLight; + vec3_t lightDir; + vec3_t directedLight; + int numVertexes; + ent = backEnd.currentEntity; + ambientLightInt = ent->ambientLightInt; + VectorCopy( ent->ambientLight, ambientLight ); + VectorCopy( ent->directedLight, directedLight ); + VectorCopy( ent->lightDir, lightDir ); + + v = tess.xyz[0]; + normal = tess.normal[0]; + + numVertexes = tess.numVertexes; + for (i = 0 ; i < numVertexes ; i++, v += 4, normal += 4) { + incoming = DotProduct (normal, lightDir); + if ( incoming <= 0 ) { + *(int *)&colors[i*4] = ambientLightInt; + continue; + } + j = ri.ftol(ambientLight[0] + incoming * directedLight[0]); + if ( j > 255 ) { + j = 255; + } + colors[i*4+0] = j; + + j = ri.ftol(ambientLight[1] + incoming * directedLight[1]); + if ( j > 255 ) { + j = 255; + } + colors[i*4+1] = j; + + j = ri.ftol(ambientLight[2] + incoming * directedLight[2]); + if ( j > 255 ) { + j = 255; + } + colors[i*4+2] = j; + + colors[i*4+3] = 255; + } +} + +void RB_CalcDiffuseColor( unsigned char *colors ) +{ +#if idppc_altivec + if (com_altivec->integer) { + // must be in a seperate function or G3 systems will crash. + RB_CalcDiffuseColor_altivec( colors ); + return; + } +#endif + RB_CalcDiffuseColor_scalar( colors ); +} + + + + + diff --git a/src/renderergl2/tr_shader.c b/src/renderergl2/tr_shader.c new file mode 100644 index 00000000..8cad4b6b --- /dev/null +++ b/src/renderergl2/tr_shader.c @@ -0,0 +1,3741 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +#include "tr_local.h" + +// tr_shader.c -- this file deals with the parsing and definition of shaders + +static char *s_shaderText; + +// the shader is parsed into these global variables, then copied into +// dynamically allocated memory if it is valid. +static shaderStage_t stages[MAX_SHADER_STAGES]; +static shader_t shader; +static texModInfo_t texMods[MAX_SHADER_STAGES][TR_MAX_TEXMODS]; + +#define FILE_HASH_SIZE 1024 +static shader_t* hashTable[FILE_HASH_SIZE]; + +#define MAX_SHADERTEXT_HASH 2048 +static char **shaderTextHashTable[MAX_SHADERTEXT_HASH]; + +/* +================ +return a hash value for the filename +================ +*/ +#ifdef __GNUCC__ + #warning TODO: check if long is ok here +#endif +static long generateHashValue( const char *fname, const int size ) { + int i; + long hash; + char letter; + + hash = 0; + i = 0; + while (fname[i] != '\0') { + letter = tolower(fname[i]); + if (letter =='.') break; // don't include extension + if (letter =='\\') letter = '/'; // damn path names + if (letter == PATH_SEP) letter = '/'; // damn path names + hash+=(long)(letter)*(i+119); + i++; + } + hash = (hash ^ (hash >> 10) ^ (hash >> 20)); + hash &= (size-1); + return hash; +} + +void R_RemapShader(const char *shaderName, const char *newShaderName, const char *timeOffset) { + char strippedName[MAX_QPATH]; + int hash; + shader_t *sh, *sh2; + qhandle_t h; + + sh = R_FindShaderByName( shaderName ); + if (sh == NULL || sh == tr.defaultShader) { + h = RE_RegisterShaderLightMap(shaderName, 0); + sh = R_GetShaderByHandle(h); + } + if (sh == NULL || sh == tr.defaultShader) { + ri.Printf( PRINT_WARNING, "WARNING: R_RemapShader: shader %s not found\n", shaderName ); + return; + } + + sh2 = R_FindShaderByName( newShaderName ); + if (sh2 == NULL || sh2 == tr.defaultShader) { + h = RE_RegisterShaderLightMap(newShaderName, 0); + sh2 = R_GetShaderByHandle(h); + } + + if (sh2 == NULL || sh2 == tr.defaultShader) { + ri.Printf( PRINT_WARNING, "WARNING: R_RemapShader: new shader %s not found\n", newShaderName ); + return; + } + + // remap all the shaders with the given name + // even tho they might have different lightmaps + COM_StripExtension(shaderName, strippedName, sizeof(strippedName)); + hash = generateHashValue(strippedName, FILE_HASH_SIZE); + for (sh = hashTable[hash]; sh; sh = sh->next) { + if (Q_stricmp(sh->name, strippedName) == 0) { + if (sh != sh2) { + sh->remappedShader = sh2; + } else { + sh->remappedShader = NULL; + } + } + } + if (timeOffset) { + sh2->timeOffset = atof(timeOffset); + } +} + +/* +=============== +ParseVector +=============== +*/ +static qboolean ParseVector( char **text, int count, float *v ) { + char *token; + int i; + + // FIXME: spaces are currently required after parens, should change parseext... + token = COM_ParseExt( text, qfalse ); + if ( strcmp( token, "(" ) ) { + ri.Printf( PRINT_WARNING, "WARNING: missing parenthesis in shader '%s'\n", shader.name ); + return qfalse; + } + + for ( i = 0 ; i < count ; i++ ) { + token = COM_ParseExt( text, qfalse ); + if ( !token[0] ) { + ri.Printf( PRINT_WARNING, "WARNING: missing vector element in shader '%s'\n", shader.name ); + return qfalse; + } + v[i] = atof( token ); + } + + token = COM_ParseExt( text, qfalse ); + if ( strcmp( token, ")" ) ) { + ri.Printf( PRINT_WARNING, "WARNING: missing parenthesis in shader '%s'\n", shader.name ); + return qfalse; + } + + return qtrue; +} + + +/* +=============== +NameToAFunc +=============== +*/ +static unsigned NameToAFunc( const char *funcname ) +{ + if ( !Q_stricmp( funcname, "GT0" ) ) + { + return GLS_ATEST_GT_0; + } + else if ( !Q_stricmp( funcname, "LT128" ) ) + { + return GLS_ATEST_LT_80; + } + else if ( !Q_stricmp( funcname, "GE128" ) ) + { + return GLS_ATEST_GE_80; + } + + ri.Printf( PRINT_WARNING, "WARNING: invalid alphaFunc name '%s' in shader '%s'\n", funcname, shader.name ); + return 0; +} + + +/* +=============== +NameToSrcBlendMode +=============== +*/ +static int NameToSrcBlendMode( const char *name ) +{ + if ( !Q_stricmp( name, "GL_ONE" ) ) + { + return GLS_SRCBLEND_ONE; + } + else if ( !Q_stricmp( name, "GL_ZERO" ) ) + { + return GLS_SRCBLEND_ZERO; + } + else if ( !Q_stricmp( name, "GL_DST_COLOR" ) ) + { + return GLS_SRCBLEND_DST_COLOR; + } + else if ( !Q_stricmp( name, "GL_ONE_MINUS_DST_COLOR" ) ) + { + return GLS_SRCBLEND_ONE_MINUS_DST_COLOR; + } + else if ( !Q_stricmp( name, "GL_SRC_ALPHA" ) ) + { + return GLS_SRCBLEND_SRC_ALPHA; + } + else if ( !Q_stricmp( name, "GL_ONE_MINUS_SRC_ALPHA" ) ) + { + return GLS_SRCBLEND_ONE_MINUS_SRC_ALPHA; + } + else if ( !Q_stricmp( name, "GL_DST_ALPHA" ) ) + { + return GLS_SRCBLEND_DST_ALPHA; + } + else if ( !Q_stricmp( name, "GL_ONE_MINUS_DST_ALPHA" ) ) + { + return GLS_SRCBLEND_ONE_MINUS_DST_ALPHA; + } + else if ( !Q_stricmp( name, "GL_SRC_ALPHA_SATURATE" ) ) + { + return GLS_SRCBLEND_ALPHA_SATURATE; + } + + ri.Printf( PRINT_WARNING, "WARNING: unknown blend mode '%s' in shader '%s', substituting GL_ONE\n", name, shader.name ); + return GLS_SRCBLEND_ONE; +} + +/* +=============== +NameToDstBlendMode +=============== +*/ +static int NameToDstBlendMode( const char *name ) +{ + if ( !Q_stricmp( name, "GL_ONE" ) ) + { + return GLS_DSTBLEND_ONE; + } + else if ( !Q_stricmp( name, "GL_ZERO" ) ) + { + return GLS_DSTBLEND_ZERO; + } + else if ( !Q_stricmp( name, "GL_SRC_ALPHA" ) ) + { + return GLS_DSTBLEND_SRC_ALPHA; + } + else if ( !Q_stricmp( name, "GL_ONE_MINUS_SRC_ALPHA" ) ) + { + return GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; + } + else if ( !Q_stricmp( name, "GL_DST_ALPHA" ) ) + { + return GLS_DSTBLEND_DST_ALPHA; + } + else if ( !Q_stricmp( name, "GL_ONE_MINUS_DST_ALPHA" ) ) + { + return GLS_DSTBLEND_ONE_MINUS_DST_ALPHA; + } + else if ( !Q_stricmp( name, "GL_SRC_COLOR" ) ) + { + return GLS_DSTBLEND_SRC_COLOR; + } + else if ( !Q_stricmp( name, "GL_ONE_MINUS_SRC_COLOR" ) ) + { + return GLS_DSTBLEND_ONE_MINUS_SRC_COLOR; + } + + ri.Printf( PRINT_WARNING, "WARNING: unknown blend mode '%s' in shader '%s', substituting GL_ONE\n", name, shader.name ); + return GLS_DSTBLEND_ONE; +} + +/* +=============== +NameToGenFunc +=============== +*/ +static genFunc_t NameToGenFunc( const char *funcname ) +{ + if ( !Q_stricmp( funcname, "sin" ) ) + { + return GF_SIN; + } + else if ( !Q_stricmp( funcname, "square" ) ) + { + return GF_SQUARE; + } + else if ( !Q_stricmp( funcname, "triangle" ) ) + { + return GF_TRIANGLE; + } + else if ( !Q_stricmp( funcname, "sawtooth" ) ) + { + return GF_SAWTOOTH; + } + else if ( !Q_stricmp( funcname, "inversesawtooth" ) ) + { + return GF_INVERSE_SAWTOOTH; + } + else if ( !Q_stricmp( funcname, "noise" ) ) + { + return GF_NOISE; + } + + ri.Printf( PRINT_WARNING, "WARNING: invalid genfunc name '%s' in shader '%s'\n", funcname, shader.name ); + return GF_SIN; +} + + +/* +=================== +ParseWaveForm +=================== +*/ +static void ParseWaveForm( char **text, waveForm_t *wave ) +{ + char *token; + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); + return; + } + wave->func = NameToGenFunc( token ); + + // BASE, AMP, PHASE, FREQ + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); + return; + } + wave->base = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); + return; + } + wave->amplitude = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); + return; + } + wave->phase = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name ); + return; + } + wave->frequency = atof( token ); +} + + +/* +=================== +ParseTexMod +=================== +*/ +static void ParseTexMod( char *_text, shaderStage_t *stage ) +{ + const char *token; + char **text = &_text; + texModInfo_t *tmi; + + if ( stage->bundle[0].numTexMods == TR_MAX_TEXMODS ) { + ri.Error( ERR_DROP, "ERROR: too many tcMod stages in shader '%s'", shader.name ); + return; + } + + tmi = &stage->bundle[0].texMods[stage->bundle[0].numTexMods]; + stage->bundle[0].numTexMods++; + + token = COM_ParseExt( text, qfalse ); + + // + // turb + // + if ( !Q_stricmp( token, "turb" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb parms in shader '%s'\n", shader.name ); + return; + } + tmi->wave.base = atof( token ); + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name ); + return; + } + tmi->wave.amplitude = atof( token ); + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name ); + return; + } + tmi->wave.phase = atof( token ); + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name ); + return; + } + tmi->wave.frequency = atof( token ); + + tmi->type = TMOD_TURBULENT; + } + // + // scale + // + else if ( !Q_stricmp( token, "scale" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing scale parms in shader '%s'\n", shader.name ); + return; + } + tmi->scale[0] = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing scale parms in shader '%s'\n", shader.name ); + return; + } + tmi->scale[1] = atof( token ); + tmi->type = TMOD_SCALE; + } + // + // scroll + // + else if ( !Q_stricmp( token, "scroll" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing scale scroll parms in shader '%s'\n", shader.name ); + return; + } + tmi->scroll[0] = atof( token ); + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing scale scroll parms in shader '%s'\n", shader.name ); + return; + } + tmi->scroll[1] = atof( token ); + tmi->type = TMOD_SCROLL; + } + // + // stretch + // + else if ( !Q_stricmp( token, "stretch" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); + return; + } + tmi->wave.func = NameToGenFunc( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); + return; + } + tmi->wave.base = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); + return; + } + tmi->wave.amplitude = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); + return; + } + tmi->wave.phase = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name ); + return; + } + tmi->wave.frequency = atof( token ); + + tmi->type = TMOD_STRETCH; + } + // + // transform + // + else if ( !Q_stricmp( token, "transform" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); + return; + } + tmi->matrix[0][0] = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); + return; + } + tmi->matrix[0][1] = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); + return; + } + tmi->matrix[1][0] = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); + return; + } + tmi->matrix[1][1] = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); + return; + } + tmi->translate[0] = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name ); + return; + } + tmi->translate[1] = atof( token ); + + tmi->type = TMOD_TRANSFORM; + } + // + // rotate + // + else if ( !Q_stricmp( token, "rotate" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing tcMod rotate parms in shader '%s'\n", shader.name ); + return; + } + tmi->rotateSpeed = atof( token ); + tmi->type = TMOD_ROTATE; + } + // + // entityTranslate + // + else if ( !Q_stricmp( token, "entityTranslate" ) ) + { + tmi->type = TMOD_ENTITY_TRANSLATE; + } + else + { + ri.Printf( PRINT_WARNING, "WARNING: unknown tcMod '%s' in shader '%s'\n", token, shader.name ); + } +} + + +/* +=================== +ParseStage +=================== +*/ +static qboolean ParseStage( shaderStage_t *stage, char **text ) +{ + char *token; + int depthMaskBits = GLS_DEPTHMASK_TRUE, blendSrcBits = 0, blendDstBits = 0, atestBits = 0, depthFuncBits = 0; + qboolean depthMaskExplicit = qfalse; + + stage->active = qtrue; + + while ( 1 ) + { + token = COM_ParseExt( text, qtrue ); + if ( !token[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: no matching '}' found\n" ); + return qfalse; + } + + if ( token[0] == '}' ) + { + break; + } + // + // map + // + else if ( !Q_stricmp( token, "map" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( !token[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'map' keyword in shader '%s'\n", shader.name ); + return qfalse; + } + + if ( !Q_stricmp( token, "$whiteimage" ) ) + { + stage->bundle[0].image[0] = tr.whiteImage; + continue; + } + else if ( !Q_stricmp( token, "$lightmap" ) ) + { + stage->bundle[0].isLightmap = qtrue; + if ( shader.lightmapIndex < 0 ) { + stage->bundle[0].image[0] = tr.whiteImage; + } else { + stage->bundle[0].image[0] = tr.lightmaps[shader.lightmapIndex]; + } + continue; + } + else if ( !Q_stricmp( token, "$deluxemap" ) ) + { + if (!tr.worldDeluxeMapping) + { + ri.Printf( PRINT_WARNING, "WARNING: shader '%s' wants a deluxe map in a map compiled without them\n", shader.name ); + return qfalse; + } + + stage->bundle[0].isLightmap = qtrue; + if ( shader.lightmapIndex < 0 ) { + stage->bundle[0].image[0] = tr.whiteImage; + } else { + stage->bundle[0].image[0] = tr.deluxemaps[shader.lightmapIndex]; + } + continue; + } + else + { + imgType_t type = IMGTYPE_COLORALPHA; + imgFlags_t flags = IMGFLAG_NONE; + + if (!shader.noMipMaps) + flags |= IMGFLAG_MIPMAP; + + if (!shader.noPicMip) + flags |= IMGFLAG_PICMIP; + + if (stage->type == ST_NORMALMAP || stage->type == ST_NORMALPARALLAXMAP) + { + type = IMGTYPE_NORMAL; + flags |= IMGFLAG_NOLIGHTSCALE; + + if (stage->type == ST_NORMALPARALLAXMAP) + type = IMGTYPE_NORMALHEIGHT; + } + else + { + if (r_genNormalMaps->integer) + flags |= IMGFLAG_GENNORMALMAP; + + if (r_srgb->integer) + flags |= IMGFLAG_SRGB; + } + + stage->bundle[0].image[0] = R_FindImageFile( token, type, flags ); + + if ( !stage->bundle[0].image[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name ); + return qfalse; + } + } + } + // + // clampmap + // + else if ( !Q_stricmp( token, "clampmap" ) ) + { + imgType_t type = IMGTYPE_COLORALPHA; + imgFlags_t flags = IMGFLAG_CLAMPTOEDGE | IMGFLAG_SRGB; + + token = COM_ParseExt( text, qfalse ); + if ( !token[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'clampmap' keyword in shader '%s'\n", shader.name ); + return qfalse; + } + + if (!shader.noMipMaps) + flags |= IMGFLAG_MIPMAP; + + if (!shader.noPicMip) + flags |= IMGFLAG_PICMIP; + + if (stage->type == ST_NORMALMAP || stage->type == ST_NORMALPARALLAXMAP) + { + type = IMGTYPE_NORMAL; + flags |= IMGFLAG_NOLIGHTSCALE; + + if (stage->type == ST_NORMALPARALLAXMAP) + type = IMGTYPE_NORMALHEIGHT; + } + else + { + if (r_genNormalMaps->integer) + flags |= IMGFLAG_GENNORMALMAP; + + if (r_srgb->integer) + flags |= IMGFLAG_SRGB; + } + + + stage->bundle[0].image[0] = R_FindImageFile( token, type, flags ); + if ( !stage->bundle[0].image[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name ); + return qfalse; + } + } + // + // animMap .... + // + else if ( !Q_stricmp( token, "animMap" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( !token[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'animMmap' keyword in shader '%s'\n", shader.name ); + return qfalse; + } + stage->bundle[0].imageAnimationSpeed = atof( token ); + + // parse up to MAX_IMAGE_ANIMATIONS animations + while ( 1 ) { + int num; + + token = COM_ParseExt( text, qfalse ); + if ( !token[0] ) { + break; + } + num = stage->bundle[0].numImageAnimations; + if ( num < MAX_IMAGE_ANIMATIONS ) { + imgFlags_t flags = IMGFLAG_SRGB; + + if (!shader.noMipMaps) + flags |= IMGFLAG_MIPMAP; + + if (!shader.noPicMip) + flags |= IMGFLAG_PICMIP; + + stage->bundle[0].image[num] = R_FindImageFile( token, IMGTYPE_COLORALPHA, flags ); + if ( !stage->bundle[0].image[num] ) + { + ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name ); + return qfalse; + } + stage->bundle[0].numImageAnimations++; + } + } + } + else if ( !Q_stricmp( token, "videoMap" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( !token[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'videoMmap' keyword in shader '%s'\n", shader.name ); + return qfalse; + } + stage->bundle[0].videoMapHandle = ri.CIN_PlayCinematic( token, 0, 0, 256, 256, (CIN_loop | CIN_silent | CIN_shader)); + if (stage->bundle[0].videoMapHandle != -1) { + stage->bundle[0].isVideoMap = qtrue; + stage->bundle[0].image[0] = tr.scratchImage[stage->bundle[0].videoMapHandle]; + } + } + // + // alphafunc + // + else if ( !Q_stricmp( token, "alphaFunc" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( !token[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'alphaFunc' keyword in shader '%s'\n", shader.name ); + return qfalse; + } + + atestBits = NameToAFunc( token ); + } + // + // depthFunc + // + else if ( !Q_stricmp( token, "depthfunc" ) ) + { + token = COM_ParseExt( text, qfalse ); + + if ( !token[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'depthfunc' keyword in shader '%s'\n", shader.name ); + return qfalse; + } + + if ( !Q_stricmp( token, "lequal" ) ) + { + depthFuncBits = 0; + } + else if ( !Q_stricmp( token, "equal" ) ) + { + depthFuncBits = GLS_DEPTHFUNC_EQUAL; + } + else + { + ri.Printf( PRINT_WARNING, "WARNING: unknown depthfunc '%s' in shader '%s'\n", token, shader.name ); + continue; + } + } + // + // detail + // + else if ( !Q_stricmp( token, "detail" ) ) + { + stage->isDetail = qtrue; + } + // + // blendfunc + // or blendfunc + // + else if ( !Q_stricmp( token, "blendfunc" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parm for blendFunc in shader '%s'\n", shader.name ); + continue; + } + // check for "simple" blends first + if ( !Q_stricmp( token, "add" ) ) { + blendSrcBits = GLS_SRCBLEND_ONE; + blendDstBits = GLS_DSTBLEND_ONE; + } else if ( !Q_stricmp( token, "filter" ) ) { + blendSrcBits = GLS_SRCBLEND_DST_COLOR; + blendDstBits = GLS_DSTBLEND_ZERO; + } else if ( !Q_stricmp( token, "blend" ) ) { + blendSrcBits = GLS_SRCBLEND_SRC_ALPHA; + blendDstBits = GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; + } else { + // complex double blends + blendSrcBits = NameToSrcBlendMode( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parm for blendFunc in shader '%s'\n", shader.name ); + continue; + } + blendDstBits = NameToDstBlendMode( token ); + } + + // clear depth mask for blended surfaces + if ( !depthMaskExplicit ) + { + depthMaskBits = 0; + } + } + // + // stage + // + else if(!Q_stricmp(token, "stage")) + { + token = COM_ParseExt(text, qfalse); + if(token[0] == 0) + { + ri.Printf(PRINT_WARNING, "WARNING: missing parameters for stage in shader '%s'\n", shader.name); + continue; + } + + if(!Q_stricmp(token, "diffuseMap")) + { + stage->type = ST_DIFFUSEMAP; + } + else if(!Q_stricmp(token, "normalMap") || !Q_stricmp(token, "bumpMap")) + { + stage->type = ST_NORMALMAP; + } + else if(!Q_stricmp(token, "normalParallaxMap") || !Q_stricmp(token, "bumpParallaxMap")) + { + if (r_parallaxMapping->integer) + stage->type = ST_NORMALPARALLAXMAP; + else + stage->type = ST_NORMALMAP; + } + else if(!Q_stricmp(token, "specularMap")) + { + stage->type = ST_SPECULARMAP; + stage->materialInfo[0] = 0.04f; + stage->materialInfo[1] = 256.0f; + } + else + { + ri.Printf(PRINT_WARNING, "WARNING: unknown stage parameter '%s' in shader '%s'\n", token, shader.name); + continue; + } + } + // + // specularReflectance + // + else if (!Q_stricmp(token, "specularreflectance")) + { + token = COM_ParseExt(text, qfalse); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parameter for specular reflectance in shader '%s'\n", shader.name ); + continue; + } + stage->materialInfo[0] = atof( token ); + } + // + // specularExponent + // + else if (!Q_stricmp(token, "specularexponent")) + { + token = COM_ParseExt(text, qfalse); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parameter for specular exponent in shader '%s'\n", shader.name ); + continue; + } + stage->materialInfo[1] = atof( token ); + } + // + // rgbGen + // + else if ( !Q_stricmp( token, "rgbGen" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parameters for rgbGen in shader '%s'\n", shader.name ); + continue; + } + + if ( !Q_stricmp( token, "wave" ) ) + { + ParseWaveForm( text, &stage->rgbWave ); + stage->rgbGen = CGEN_WAVEFORM; + } + else if ( !Q_stricmp( token, "const" ) ) + { + vec3_t color; + + ParseVector( text, 3, color ); + stage->constantColor[0] = 255 * color[0]; + stage->constantColor[1] = 255 * color[1]; + stage->constantColor[2] = 255 * color[2]; + + stage->rgbGen = CGEN_CONST; + } + else if ( !Q_stricmp( token, "identity" ) ) + { + stage->rgbGen = CGEN_IDENTITY; + } + else if ( !Q_stricmp( token, "identityLighting" ) ) + { + stage->rgbGen = CGEN_IDENTITY_LIGHTING; + } + else if ( !Q_stricmp( token, "entity" ) ) + { + stage->rgbGen = CGEN_ENTITY; + } + else if ( !Q_stricmp( token, "oneMinusEntity" ) ) + { + stage->rgbGen = CGEN_ONE_MINUS_ENTITY; + } + else if ( !Q_stricmp( token, "vertex" ) ) + { + stage->rgbGen = CGEN_VERTEX; + if ( stage->alphaGen == 0 ) { + stage->alphaGen = AGEN_VERTEX; + } + } + else if ( !Q_stricmp( token, "exactVertex" ) ) + { + stage->rgbGen = CGEN_EXACT_VERTEX; + } + else if ( !Q_stricmp( token, "vertexLit" ) ) + { + stage->rgbGen = CGEN_VERTEX_LIT; + if ( stage->alphaGen == 0 ) { + stage->alphaGen = AGEN_VERTEX; + } + } + else if ( !Q_stricmp( token, "exactVertexLit" ) ) + { + stage->rgbGen = CGEN_EXACT_VERTEX_LIT; + } + else if ( !Q_stricmp( token, "lightingDiffuse" ) ) + { + stage->rgbGen = CGEN_LIGHTING_DIFFUSE; + } + else if ( !Q_stricmp( token, "oneMinusVertex" ) ) + { + stage->rgbGen = CGEN_ONE_MINUS_VERTEX; + } + else + { + ri.Printf( PRINT_WARNING, "WARNING: unknown rgbGen parameter '%s' in shader '%s'\n", token, shader.name ); + continue; + } + } + // + // alphaGen + // + else if ( !Q_stricmp( token, "alphaGen" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parameters for alphaGen in shader '%s'\n", shader.name ); + continue; + } + + if ( !Q_stricmp( token, "wave" ) ) + { + ParseWaveForm( text, &stage->alphaWave ); + stage->alphaGen = AGEN_WAVEFORM; + } + else if ( !Q_stricmp( token, "const" ) ) + { + token = COM_ParseExt( text, qfalse ); + stage->constantColor[3] = 255 * atof( token ); + stage->alphaGen = AGEN_CONST; + } + else if ( !Q_stricmp( token, "identity" ) ) + { + stage->alphaGen = AGEN_IDENTITY; + } + else if ( !Q_stricmp( token, "entity" ) ) + { + stage->alphaGen = AGEN_ENTITY; + } + else if ( !Q_stricmp( token, "oneMinusEntity" ) ) + { + stage->alphaGen = AGEN_ONE_MINUS_ENTITY; + } + else if ( !Q_stricmp( token, "vertex" ) ) + { + stage->alphaGen = AGEN_VERTEX; + } + else if ( !Q_stricmp( token, "lightingSpecular" ) ) + { + stage->alphaGen = AGEN_LIGHTING_SPECULAR; + } + else if ( !Q_stricmp( token, "oneMinusVertex" ) ) + { + stage->alphaGen = AGEN_ONE_MINUS_VERTEX; + } + else if ( !Q_stricmp( token, "portal" ) ) + { + stage->alphaGen = AGEN_PORTAL; + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + shader.portalRange = 256; + ri.Printf( PRINT_WARNING, "WARNING: missing range parameter for alphaGen portal in shader '%s', defaulting to 256\n", shader.name ); + } + else + { + shader.portalRange = atof( token ); + } + } + else if ( !Q_stricmp( token, "fresnel" ) ) + { + stage->alphaGen = AGEN_FRESNEL; + } + else + { + ri.Printf( PRINT_WARNING, "WARNING: unknown alphaGen parameter '%s' in shader '%s'\n", token, shader.name ); + continue; + } + } + // + // tcGen + // + else if ( !Q_stricmp(token, "texgen") || !Q_stricmp( token, "tcGen" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing texgen parm in shader '%s'\n", shader.name ); + continue; + } + + if ( !Q_stricmp( token, "environment" ) ) + { + stage->bundle[0].tcGen = TCGEN_ENVIRONMENT_MAPPED; + } + else if ( !Q_stricmp( token, "lightmap" ) ) + { + stage->bundle[0].tcGen = TCGEN_LIGHTMAP; + } + else if ( !Q_stricmp( token, "texture" ) || !Q_stricmp( token, "base" ) ) + { + stage->bundle[0].tcGen = TCGEN_TEXTURE; + } + else if ( !Q_stricmp( token, "vector" ) ) + { + ParseVector( text, 3, stage->bundle[0].tcGenVectors[0] ); + ParseVector( text, 3, stage->bundle[0].tcGenVectors[1] ); + + stage->bundle[0].tcGen = TCGEN_VECTOR; + } + else + { + ri.Printf( PRINT_WARNING, "WARNING: unknown texgen parm in shader '%s'\n", shader.name ); + } + } + // + // tcMod <...> + // + else if ( !Q_stricmp( token, "tcMod" ) ) + { + char buffer[1024] = ""; + + while ( 1 ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + break; + strcat( buffer, token ); + strcat( buffer, " " ); + } + + ParseTexMod( buffer, stage ); + + continue; + } + // + // depthmask + // + else if ( !Q_stricmp( token, "depthwrite" ) ) + { + depthMaskBits = GLS_DEPTHMASK_TRUE; + depthMaskExplicit = qtrue; + + continue; + } + else + { + ri.Printf( PRINT_WARNING, "WARNING: unknown parameter '%s' in shader '%s'\n", token, shader.name ); + return qfalse; + } + } + + // + // if cgen isn't explicitly specified, use either identity or identitylighting + // + if ( stage->rgbGen == CGEN_BAD ) { + if ( blendSrcBits == 0 || + blendSrcBits == GLS_SRCBLEND_ONE || + blendSrcBits == GLS_SRCBLEND_SRC_ALPHA ) { + stage->rgbGen = CGEN_IDENTITY_LIGHTING; + } else { + stage->rgbGen = CGEN_IDENTITY; + } + } + + + // + // implicitly assume that a GL_ONE GL_ZERO blend mask disables blending + // + if ( ( blendSrcBits == GLS_SRCBLEND_ONE ) && + ( blendDstBits == GLS_DSTBLEND_ZERO ) ) + { + blendDstBits = blendSrcBits = 0; + depthMaskBits = GLS_DEPTHMASK_TRUE; + } + + // decide which agens we can skip + if ( stage->alphaGen == AGEN_IDENTITY ) { + if ( stage->rgbGen == CGEN_IDENTITY + || stage->rgbGen == CGEN_LIGHTING_DIFFUSE ) { + stage->alphaGen = AGEN_SKIP; + } + } + + // + // compute state bits + // + stage->stateBits = depthMaskBits | + blendSrcBits | blendDstBits | + atestBits | + depthFuncBits; + + return qtrue; +} + +/* +=============== +ParseDeform + +deformVertexes wave +deformVertexes normal +deformVertexes move +deformVertexes bulge +deformVertexes projectionShadow +deformVertexes autoSprite +deformVertexes autoSprite2 +deformVertexes text[0-7] +=============== +*/ +static void ParseDeform( char **text ) { + char *token; + deformStage_t *ds; + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing deform parm in shader '%s'\n", shader.name ); + return; + } + + if ( shader.numDeforms == MAX_SHADER_DEFORMS ) { + ri.Printf( PRINT_WARNING, "WARNING: MAX_SHADER_DEFORMS in '%s'\n", shader.name ); + return; + } + + ds = &shader.deforms[ shader.numDeforms ]; + shader.numDeforms++; + + if ( !Q_stricmp( token, "projectionShadow" ) ) { + ds->deformation = DEFORM_PROJECTION_SHADOW; + return; + } + + if ( !Q_stricmp( token, "autosprite" ) ) { + ds->deformation = DEFORM_AUTOSPRITE; + return; + } + + if ( !Q_stricmp( token, "autosprite2" ) ) { + ds->deformation = DEFORM_AUTOSPRITE2; + return; + } + + if ( !Q_stricmpn( token, "text", 4 ) ) { + int n; + + n = token[4] - '0'; + if ( n < 0 || n > 7 ) { + n = 0; + } + ds->deformation = DEFORM_TEXT0 + n; + return; + } + + if ( !Q_stricmp( token, "bulge" ) ) { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name ); + return; + } + ds->bulgeWidth = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name ); + return; + } + ds->bulgeHeight = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name ); + return; + } + ds->bulgeSpeed = atof( token ); + + ds->deformation = DEFORM_BULGE; + return; + } + + if ( !Q_stricmp( token, "wave" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); + return; + } + + if ( atof( token ) != 0 ) + { + ds->deformationSpread = 1.0f / atof( token ); + } + else + { + ds->deformationSpread = 100.0f; + ri.Printf( PRINT_WARNING, "WARNING: illegal div value of 0 in deformVertexes command for shader '%s'\n", shader.name ); + } + + ParseWaveForm( text, &ds->deformationWave ); + ds->deformation = DEFORM_WAVE; + return; + } + + if ( !Q_stricmp( token, "normal" ) ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); + return; + } + ds->deformationWave.amplitude = atof( token ); + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); + return; + } + ds->deformationWave.frequency = atof( token ); + + ds->deformation = DEFORM_NORMALS; + return; + } + + if ( !Q_stricmp( token, "move" ) ) { + int i; + + for ( i = 0 ; i < 3 ; i++ ) { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) { + ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name ); + return; + } + ds->moveVector[i] = atof( token ); + } + + ParseWaveForm( text, &ds->deformationWave ); + ds->deformation = DEFORM_MOVE; + return; + } + + ri.Printf( PRINT_WARNING, "WARNING: unknown deformVertexes subtype '%s' found in shader '%s'\n", token, shader.name ); +} + + +/* +=============== +ParseSkyParms + +skyParms +=============== +*/ +static void ParseSkyParms( char **text ) { + char *token; + static char *suf[6] = {"rt", "bk", "lf", "ft", "up", "dn"}; + char pathname[MAX_QPATH]; + int i; + + // outerbox + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) { + ri.Printf( PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name ); + return; + } + if ( strcmp( token, "-" ) ) { + for (i=0 ; i<6 ; i++) { + Com_sprintf( pathname, sizeof(pathname), "%s_%s.tga" + , token, suf[i] ); + shader.sky.outerbox[i] = R_FindImageFile( ( char * ) pathname, IMGTYPE_COLORALPHA, IMGFLAG_SRGB | IMGFLAG_MIPMAP | IMGFLAG_PICMIP | IMGFLAG_CLAMPTOEDGE ); + + if ( !shader.sky.outerbox[i] ) { + shader.sky.outerbox[i] = tr.defaultImage; + } + } + } + + // cloudheight + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) { + ri.Printf( PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name ); + return; + } + shader.sky.cloudHeight = atof( token ); + if ( !shader.sky.cloudHeight ) { + shader.sky.cloudHeight = 512; + } + R_InitSkyTexCoords( shader.sky.cloudHeight ); + + + // innerbox + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) { + ri.Printf( PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name ); + return; + } + if ( strcmp( token, "-" ) ) { + for (i=0 ; i<6 ; i++) { + Com_sprintf( pathname, sizeof(pathname), "%s_%s.tga" + , token, suf[i] ); + shader.sky.innerbox[i] = R_FindImageFile( ( char * ) pathname, IMGTYPE_COLORALPHA, IMGFLAG_SRGB | IMGFLAG_MIPMAP | IMGFLAG_PICMIP ); + if ( !shader.sky.innerbox[i] ) { + shader.sky.innerbox[i] = tr.defaultImage; + } + } + } + + shader.isSky = qtrue; +} + + +/* +================= +ParseSort +================= +*/ +void ParseSort( char **text ) { + char *token; + + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) { + ri.Printf( PRINT_WARNING, "WARNING: missing sort parameter in shader '%s'\n", shader.name ); + return; + } + + if ( !Q_stricmp( token, "portal" ) ) { + shader.sort = SS_PORTAL; + } else if ( !Q_stricmp( token, "sky" ) ) { + shader.sort = SS_ENVIRONMENT; + } else if ( !Q_stricmp( token, "opaque" ) ) { + shader.sort = SS_OPAQUE; + }else if ( !Q_stricmp( token, "decal" ) ) { + shader.sort = SS_DECAL; + } else if ( !Q_stricmp( token, "seeThrough" ) ) { + shader.sort = SS_SEE_THROUGH; + } else if ( !Q_stricmp( token, "banner" ) ) { + shader.sort = SS_BANNER; + } else if ( !Q_stricmp( token, "additive" ) ) { + shader.sort = SS_BLEND1; + } else if ( !Q_stricmp( token, "nearest" ) ) { + shader.sort = SS_NEAREST; + } else if ( !Q_stricmp( token, "underwater" ) ) { + shader.sort = SS_UNDERWATER; + } else { + shader.sort = atof( token ); + } +} + + + +// this table is also present in q3map + +typedef struct { + char *name; + int clearSolid, surfaceFlags, contents; +} infoParm_t; + +infoParm_t infoParms[] = { + // server relevant contents + {"water", 1, 0, CONTENTS_WATER }, + {"slime", 1, 0, CONTENTS_SLIME }, // mildly damaging + {"lava", 1, 0, CONTENTS_LAVA }, // very damaging + {"playerclip", 1, 0, CONTENTS_PLAYERCLIP }, + {"monsterclip", 1, 0, CONTENTS_MONSTERCLIP }, + {"nodrop", 1, 0, CONTENTS_NODROP }, // don't drop items or leave bodies (death fog, lava, etc) + {"nonsolid", 1, SURF_NONSOLID, 0}, // clears the solid flag + + // utility relevant attributes + {"origin", 1, 0, CONTENTS_ORIGIN }, // center of rotating brushes + {"trans", 0, 0, CONTENTS_TRANSLUCENT }, // don't eat contained surfaces + {"detail", 0, 0, CONTENTS_DETAIL }, // don't include in structural bsp + {"structural", 0, 0, CONTENTS_STRUCTURAL }, // force into structural bsp even if trnas + {"areaportal", 1, 0, CONTENTS_AREAPORTAL }, // divides areas + {"clusterportal", 1,0, CONTENTS_CLUSTERPORTAL }, // for bots + {"donotenter", 1, 0, CONTENTS_DONOTENTER }, // for bots + + {"fog", 1, 0, CONTENTS_FOG}, // carves surfaces entering + {"sky", 0, SURF_SKY, 0 }, // emit light from an environment map + {"lightfilter", 0, SURF_LIGHTFILTER, 0 }, // filter light going through it + {"alphashadow", 0, SURF_ALPHASHADOW, 0 }, // test light on a per-pixel basis + {"hint", 0, SURF_HINT, 0 }, // use as a primary splitter + + // server attributes + {"slick", 0, SURF_SLICK, 0 }, + {"noimpact", 0, SURF_NOIMPACT, 0 }, // don't make impact explosions or marks + {"nomarks", 0, SURF_NOMARKS, 0 }, // don't make impact marks, but still explode + {"ladder", 0, SURF_LADDER, 0 }, + {"nodamage", 0, SURF_NODAMAGE, 0 }, + {"metalsteps", 0, SURF_METALSTEPS,0 }, + {"flesh", 0, SURF_FLESH, 0 }, + {"nosteps", 0, SURF_NOSTEPS, 0 }, + + // drawsurf attributes + {"nodraw", 0, SURF_NODRAW, 0 }, // don't generate a drawsurface (or a lightmap) + {"pointlight", 0, SURF_POINTLIGHT, 0 }, // sample lighting at vertexes + {"nolightmap", 0, SURF_NOLIGHTMAP,0 }, // don't generate a lightmap + {"nodlight", 0, SURF_NODLIGHT, 0 }, // don't ever add dynamic lights + {"dust", 0, SURF_DUST, 0} // leave a dust trail when walking on this surface +}; + + +/* +=============== +ParseSurfaceParm + +surfaceparm +=============== +*/ +static void ParseSurfaceParm( char **text ) { + char *token; + int numInfoParms = ARRAY_LEN( infoParms ); + int i; + + token = COM_ParseExt( text, qfalse ); + for ( i = 0 ; i < numInfoParms ; i++ ) { + if ( !Q_stricmp( token, infoParms[i].name ) ) { + shader.surfaceFlags |= infoParms[i].surfaceFlags; + shader.contentFlags |= infoParms[i].contents; +#if 0 + if ( infoParms[i].clearSolid ) { + si->contents &= ~CONTENTS_SOLID; + } +#endif + break; + } + } +} + +/* +================= +ParseShader + +The current text pointer is at the explicit text definition of the +shader. Parse it into the global shader variable. Later functions +will optimize it. +================= +*/ +static qboolean ParseShader( char **text ) +{ + char *token; + int s; + + s = 0; + + token = COM_ParseExt( text, qtrue ); + if ( token[0] != '{' ) + { + ri.Printf( PRINT_WARNING, "WARNING: expecting '{', found '%s' instead in shader '%s'\n", token, shader.name ); + return qfalse; + } + + while ( 1 ) + { + token = COM_ParseExt( text, qtrue ); + if ( !token[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: no concluding '}' in shader %s\n", shader.name ); + return qfalse; + } + + // end of shader definition + if ( token[0] == '}' ) + { + break; + } + // stage definition + else if ( token[0] == '{' ) + { + if ( s >= MAX_SHADER_STAGES ) { + ri.Printf( PRINT_WARNING, "WARNING: too many stages in shader %s\n", shader.name ); + return qfalse; + } + + if ( !ParseStage( &stages[s], text ) ) + { + return qfalse; + } + stages[s].active = qtrue; + s++; + + continue; + } + // skip stuff that only the QuakeEdRadient needs + else if ( !Q_stricmpn( token, "qer", 3 ) ) { + SkipRestOfLine( text ); + continue; + } + // sun parms + else if ( !Q_stricmp( token, "q3map_sun" ) || !Q_stricmp( token, "q3map_sunExt" ) || !Q_stricmp( token, "q3gl2_sun" ) ) { + float a, b; + qboolean isGL2Sun = qfalse; + + if (!Q_stricmp( token, "q3gl2_sun" ) && r_sunShadows->integer ) + { + isGL2Sun = qtrue; + tr.sunShadows = qtrue; + } + + token = COM_ParseExt( text, qfalse ); + tr.sunLight[0] = atof( token ); + token = COM_ParseExt( text, qfalse ); + tr.sunLight[1] = atof( token ); + token = COM_ParseExt( text, qfalse ); + tr.sunLight[2] = atof( token ); + + VectorNormalize( tr.sunLight ); + + token = COM_ParseExt( text, qfalse ); + a = atof( token ); + VectorScale( tr.sunLight, a, tr.sunLight); + + VectorSet( tr.sunAmbient, 0.0f, 0.0f, 0.0f); + + token = COM_ParseExt( text, qfalse ); + a = atof( token ); + a = a / 180 * M_PI; + + token = COM_ParseExt( text, qfalse ); + b = atof( token ); + b = b / 180 * M_PI; + + tr.sunDirection[0] = cos( a ) * cos( b ); + tr.sunDirection[1] = sin( a ) * cos( b ); + tr.sunDirection[2] = sin( b ); + + if (isGL2Sun) + { + token = COM_ParseExt( text, qfalse ); + tr.mapLightScale = atof(token); + + token = COM_ParseExt( text, qfalse ); + VectorScale( tr.sunLight, atof(token), tr.sunAmbient ); + } + + SkipRestOfLine( text ); + continue; + } + // tonemap parms + else if ( !Q_stricmp( token, "q3gl2_tonemap" ) ) { + token = COM_ParseExt( text, qfalse ); + tr.toneMinAvgMaxLevel[0] = atof( token ); + token = COM_ParseExt( text, qfalse ); + tr.toneMinAvgMaxLevel[1] = atof( token ); + token = COM_ParseExt( text, qfalse ); + tr.toneMinAvgMaxLevel[2] = atof( token ); + + token = COM_ParseExt( text, qfalse ); + tr.autoExposureMinMax[0] = atof( token ); + token = COM_ParseExt( text, qfalse ); + tr.autoExposureMinMax[1] = atof( token ); + + SkipRestOfLine( text ); + continue; + } + else if ( !Q_stricmp( token, "deformVertexes" ) ) { + ParseDeform( text ); + continue; + } + else if ( !Q_stricmp( token, "tesssize" ) ) { + SkipRestOfLine( text ); + continue; + } + else if ( !Q_stricmp( token, "clampTime" ) ) { + token = COM_ParseExt( text, qfalse ); + if (token[0]) { + shader.clampTime = atof(token); + } + } + // skip stuff that only the q3map needs + else if ( !Q_stricmpn( token, "q3map", 5 ) ) { + SkipRestOfLine( text ); + continue; + } + // skip stuff that only q3map or the server needs + else if ( !Q_stricmp( token, "surfaceParm" ) ) { + ParseSurfaceParm( text ); + continue; + } + // no mip maps + else if ( !Q_stricmp( token, "nomipmaps" ) ) + { + shader.noMipMaps = qtrue; + shader.noPicMip = qtrue; + continue; + } + // no picmip adjustment + else if ( !Q_stricmp( token, "nopicmip" ) ) + { + shader.noPicMip = qtrue; + continue; + } + // polygonOffset + else if ( !Q_stricmp( token, "polygonOffset" ) ) + { + shader.polygonOffset = qtrue; + continue; + } + // entityMergable, allowing sprite surfaces from multiple entities + // to be merged into one batch. This is a savings for smoke + // puffs and blood, but can't be used for anything where the + // shader calcs (not the surface function) reference the entity color or scroll + else if ( !Q_stricmp( token, "entityMergable" ) ) + { + shader.entityMergable = qtrue; + continue; + } + // fogParms + else if ( !Q_stricmp( token, "fogParms" ) ) + { + if ( !ParseVector( text, 3, shader.fogParms.color ) ) { + return qfalse; + } + + token = COM_ParseExt( text, qfalse ); + if ( !token[0] ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing parm for 'fogParms' keyword in shader '%s'\n", shader.name ); + continue; + } + shader.fogParms.depthForOpaque = atof( token ); + + // skip any old gradient directions + SkipRestOfLine( text ); + continue; + } + // portal + else if ( !Q_stricmp(token, "portal") ) + { + shader.sort = SS_PORTAL; + shader.isPortal = qtrue; + continue; + } + // skyparms + else if ( !Q_stricmp( token, "skyparms" ) ) + { + ParseSkyParms( text ); + continue; + } + // light determines flaring in q3map, not needed here + else if ( !Q_stricmp(token, "light") ) + { + token = COM_ParseExt( text, qfalse ); + continue; + } + // cull + else if ( !Q_stricmp( token, "cull") ) + { + token = COM_ParseExt( text, qfalse ); + if ( token[0] == 0 ) + { + ri.Printf( PRINT_WARNING, "WARNING: missing cull parms in shader '%s'\n", shader.name ); + continue; + } + + if ( !Q_stricmp( token, "none" ) || !Q_stricmp( token, "twosided" ) || !Q_stricmp( token, "disable" ) ) + { + shader.cullType = CT_TWO_SIDED; + } + else if ( !Q_stricmp( token, "back" ) || !Q_stricmp( token, "backside" ) || !Q_stricmp( token, "backsided" ) ) + { + shader.cullType = CT_BACK_SIDED; + } + else + { + ri.Printf( PRINT_WARNING, "WARNING: invalid cull parm '%s' in shader '%s'\n", token, shader.name ); + } + continue; + } + // sort + else if ( !Q_stricmp( token, "sort" ) ) + { + ParseSort( text ); + continue; + } + else + { + ri.Printf( PRINT_WARNING, "WARNING: unknown general shader parameter '%s' in '%s'\n", token, shader.name ); + return qfalse; + } + } + + // + // ignore shaders that don't have any stages, unless it is a sky or fog + // + if ( s == 0 && !shader.isSky && !(shader.contentFlags & CONTENTS_FOG ) ) { + return qfalse; + } + + shader.explicitlyDefined = qtrue; + + return qtrue; +} + +/* +======================================================================================== + +SHADER OPTIMIZATION AND FOGGING + +======================================================================================== +*/ + +/* +=================== +ComputeStageIteratorFunc + +See if we can use on of the simple fastpath stage functions, +otherwise set to the generic stage function +=================== +*/ +static void ComputeStageIteratorFunc( void ) +{ + shader.optimalStageIteratorFunc = RB_StageIteratorGeneric; + + // + // see if this should go into the sky path + // + if ( shader.isSky ) + { + shader.optimalStageIteratorFunc = RB_StageIteratorSky; + return; + } +} + +/* +=================== +ComputeVertexAttribs + +Check which vertex attributes we only need, so we +don't need to submit/copy all of them. +=================== +*/ +static void ComputeVertexAttribs(void) +{ + int i, stage; + + // dlights always need ATTR_NORMAL + shader.vertexAttribs = ATTR_POSITION | ATTR_NORMAL; + + // portals always need normals, for SurfIsOffscreen() + if (shader.isPortal) + { + shader.vertexAttribs |= ATTR_NORMAL; + } + + if (shader.defaultShader) + { + shader.vertexAttribs |= ATTR_TEXCOORD; + return; + } + + if(shader.numDeforms) + { + for ( i = 0; i < shader.numDeforms; i++) + { + deformStage_t *ds = &shader.deforms[i]; + + switch (ds->deformation) + { + case DEFORM_BULGE: + shader.vertexAttribs |= ATTR_NORMAL | ATTR_TEXCOORD; + break; + + case DEFORM_AUTOSPRITE: + shader.vertexAttribs |= ATTR_NORMAL | ATTR_COLOR; + break; + + case DEFORM_WAVE: + case DEFORM_NORMALS: + case DEFORM_TEXT0: + case DEFORM_TEXT1: + case DEFORM_TEXT2: + case DEFORM_TEXT3: + case DEFORM_TEXT4: + case DEFORM_TEXT5: + case DEFORM_TEXT6: + case DEFORM_TEXT7: + shader.vertexAttribs |= ATTR_NORMAL; + break; + + default: + case DEFORM_NONE: + case DEFORM_MOVE: + case DEFORM_PROJECTION_SHADOW: + case DEFORM_AUTOSPRITE2: + break; + } + } + } + + for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) + { + shaderStage_t *pStage = &stages[stage]; + + if ( !pStage->active ) + { + break; + } + + if (pStage->glslShaderGroup == tr.lightallShader) + { + shader.vertexAttribs |= ATTR_NORMAL; + +#ifdef USE_VERT_TANGENT_SPACE + if (pStage->glslShaderIndex & LIGHTDEF_USE_NORMALMAP) + { + shader.vertexAttribs |= ATTR_BITANGENT | ATTR_TANGENT; + } +#endif + + switch (pStage->glslShaderIndex & LIGHTDEF_LIGHTTYPE_MASK) + { + case LIGHTDEF_USE_LIGHTMAP: + case LIGHTDEF_USE_LIGHT_VERTEX: + shader.vertexAttribs |= ATTR_LIGHTDIRECTION; + break; + default: + break; + } + } + + for (i = 0; i < NUM_TEXTURE_BUNDLES; i++) + { + if ( pStage->bundle[i].image[0] == 0 ) + { + continue; + } + + switch(pStage->bundle[i].tcGen) + { + case TCGEN_TEXTURE: + shader.vertexAttribs |= ATTR_TEXCOORD; + break; + case TCGEN_LIGHTMAP: + shader.vertexAttribs |= ATTR_LIGHTCOORD; + break; + case TCGEN_ENVIRONMENT_MAPPED: + shader.vertexAttribs |= ATTR_NORMAL; + break; + + default: + break; + } + } + + switch(pStage->rgbGen) + { + case CGEN_EXACT_VERTEX: + case CGEN_VERTEX: + case CGEN_EXACT_VERTEX_LIT: + case CGEN_VERTEX_LIT: + case CGEN_ONE_MINUS_VERTEX: + shader.vertexAttribs |= ATTR_COLOR; + break; + + case CGEN_LIGHTING_DIFFUSE: + shader.vertexAttribs |= ATTR_NORMAL; + break; + + default: + break; + } + + switch(pStage->alphaGen) + { + case AGEN_LIGHTING_SPECULAR: + case AGEN_FRESNEL: + shader.vertexAttribs |= ATTR_NORMAL; + break; + + case AGEN_VERTEX: + case AGEN_ONE_MINUS_VERTEX: + shader.vertexAttribs |= ATTR_COLOR; + break; + + default: + break; + } + } +} + +typedef struct { + int blendA; + int blendB; + + int multitextureEnv; + int multitextureBlend; +} collapse_t; + +static collapse_t collapse[] = { + { 0, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, + GL_MODULATE, 0 }, + + { 0, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, + GL_MODULATE, 0 }, + + { GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, + GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, + + { GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, + GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, + + { GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, + GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, + + { GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO, + GL_MODULATE, GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR }, + + { 0, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE, + GL_ADD, 0 }, + + { GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE, + GL_ADD, GLS_DSTBLEND_ONE | GLS_SRCBLEND_ONE }, +#if 0 + { 0, GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA | GLS_SRCBLEND_SRC_ALPHA, + GL_DECAL, 0 }, +#endif + { -1 } +}; + +/* +================ +CollapseMultitexture + +Attempt to combine two stages into a single multitexture stage +FIXME: I think modulated add + modulated add collapses incorrectly +================= +*/ +static qboolean CollapseMultitexture( void ) { + int abits, bbits; + int i; + textureBundle_t tmpBundle; + + if ( !qglActiveTextureARB ) { + return qfalse; + } + + // make sure both stages are active + if ( !stages[0].active || !stages[1].active ) { + return qfalse; + } + + // on voodoo2, don't combine different tmus + if ( glConfig.driverType == GLDRV_VOODOO ) { + if ( stages[0].bundle[0].image[0]->TMU == + stages[1].bundle[0].image[0]->TMU ) { + return qfalse; + } + } + + abits = stages[0].stateBits; + bbits = stages[1].stateBits; + + // make sure that both stages have identical state other than blend modes + if ( ( abits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) != + ( bbits & ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS | GLS_DEPTHMASK_TRUE ) ) ) { + return qfalse; + } + + abits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); + bbits &= ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); + + // search for a valid multitexture blend function + for ( i = 0; collapse[i].blendA != -1 ; i++ ) { + if ( abits == collapse[i].blendA + && bbits == collapse[i].blendB ) { + break; + } + } + + // nothing found + if ( collapse[i].blendA == -1 ) { + return qfalse; + } + + // GL_ADD is a separate extension + if ( collapse[i].multitextureEnv == GL_ADD && !glConfig.textureEnvAddAvailable ) { + return qfalse; + } + + // make sure waveforms have identical parameters + if ( ( stages[0].rgbGen != stages[1].rgbGen ) || + ( stages[0].alphaGen != stages[1].alphaGen ) ) { + return qfalse; + } + + // an add collapse can only have identity colors + if ( collapse[i].multitextureEnv == GL_ADD && stages[0].rgbGen != CGEN_IDENTITY ) { + return qfalse; + } + + if ( stages[0].rgbGen == CGEN_WAVEFORM ) + { + if ( memcmp( &stages[0].rgbWave, + &stages[1].rgbWave, + sizeof( stages[0].rgbWave ) ) ) + { + return qfalse; + } + } + if ( stages[0].alphaGen == AGEN_WAVEFORM ) + { + if ( memcmp( &stages[0].alphaWave, + &stages[1].alphaWave, + sizeof( stages[0].alphaWave ) ) ) + { + return qfalse; + } + } + + + // make sure that lightmaps are in bundle 1 for 3dfx + if ( stages[0].bundle[0].isLightmap ) + { + tmpBundle = stages[0].bundle[0]; + stages[0].bundle[0] = stages[1].bundle[0]; + stages[0].bundle[1] = tmpBundle; + } + else + { + stages[0].bundle[1] = stages[1].bundle[0]; + } + + // set the new blend state bits + shader.multitextureEnv = collapse[i].multitextureEnv; + stages[0].stateBits &= ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); + stages[0].stateBits |= collapse[i].multitextureBlend; + + // + // move down subsequent shaders + // + memmove( &stages[1], &stages[2], sizeof( stages[0] ) * ( MAX_SHADER_STAGES - 2 ) ); + Com_Memset( &stages[MAX_SHADER_STAGES-1], 0, sizeof( stages[0] ) ); + + return qtrue; +} + +static void CollapseStagesToLightall(shaderStage_t *diffuse, + shaderStage_t *normal, shaderStage_t *specular, shaderStage_t *lightmap, + qboolean useLightVector, qboolean useLightVertex, qboolean parallax, qboolean environment) +{ + int defs = 0; + + //ri.Printf(PRINT_ALL, "shader %s has diffuse %s", shader.name, diffuse->bundle[0].image[0]->imgName); + + // reuse diffuse, mark others inactive + diffuse->type = ST_GLSL; + + if (lightmap) + { + //ri.Printf(PRINT_ALL, ", lightmap"); + diffuse->bundle[TB_LIGHTMAP] = lightmap->bundle[0]; + defs |= LIGHTDEF_USE_LIGHTMAP; + } + else if (useLightVector) + { + defs |= LIGHTDEF_USE_LIGHT_VECTOR; + } + else if (useLightVertex) + { + defs |= LIGHTDEF_USE_LIGHT_VERTEX; + } + + if (r_deluxeMapping->integer && tr.worldDeluxeMapping && lightmap) + { + //ri.Printf(PRINT_ALL, ", deluxemap"); + diffuse->bundle[TB_DELUXEMAP] = lightmap->bundle[0]; + diffuse->bundle[TB_DELUXEMAP].image[0] = tr.deluxemaps[shader.lightmapIndex]; + defs |= LIGHTDEF_USE_DELUXEMAP; + } + + if (r_normalMapping->integer) + { + image_t *diffuseImg; + if (normal) + { + //ri.Printf(PRINT_ALL, ", normalmap %s", normal->bundle[0].image[0]->imgName); + diffuse->bundle[TB_NORMALMAP] = normal->bundle[0]; + defs |= LIGHTDEF_USE_NORMALMAP; + if (parallax && r_parallaxMapping->integer) + defs |= LIGHTDEF_USE_PARALLAXMAP; + } + else if ((lightmap || useLightVector || useLightVertex) && (diffuseImg = diffuse->bundle[TB_DIFFUSEMAP].image[0])) + { + char normalName[MAX_QPATH]; + image_t *normalImg; + imgFlags_t normalFlags = (diffuseImg->flags & ~(IMGFLAG_GENNORMALMAP | IMGFLAG_SRGB)) | IMGFLAG_NOLIGHTSCALE; + + COM_StripExtension(diffuseImg->imgName, normalName, MAX_QPATH); + Q_strcat(normalName, MAX_QPATH, "_n"); + + normalImg = R_FindImageFile(normalName, IMGTYPE_NORMAL, normalFlags); + + if (normalImg) + { + diffuse->bundle[TB_NORMALMAP] = diffuse->bundle[0]; + diffuse->bundle[TB_NORMALMAP].image[0] = normalImg; + + defs |= LIGHTDEF_USE_NORMALMAP; + if (parallax && r_parallaxMapping->integer) + defs |= LIGHTDEF_USE_PARALLAXMAP; + } + } + } + + if (r_specularMapping->integer) + { + if (specular) + { + //ri.Printf(PRINT_ALL, ", specularmap %s", specular->bundle[0].image[0]->imgName); + diffuse->bundle[TB_SPECULARMAP] = specular->bundle[0]; + diffuse->materialInfo[0] = specular->materialInfo[0]; + diffuse->materialInfo[1] = specular->materialInfo[1]; + defs |= LIGHTDEF_USE_SPECULARMAP; + } + } + + if (environment || diffuse->bundle[0].numTexMods) + { + defs |= LIGHTDEF_USE_TCGEN_AND_TCMOD; + } + + //ri.Printf(PRINT_ALL, ".\n"); + + diffuse->glslShaderGroup = tr.lightallShader; + diffuse->glslShaderIndex = defs; +} + + +static qboolean CollapseStagesToGLSL(void) +{ + int i, j, numStages; + qboolean skip = qfalse; + + // skip shaders with deforms + if (shader.numDeforms != 0) + { + skip = qtrue; + } + + if (!skip) + { + // if 2+ stages and first stage is lightmap, switch them + // this makes it easier for the later bits to process + if (stages[0].active && stages[0].bundle[0].isLightmap && stages[1].active) + { + int blendBits = stages[1].stateBits & ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); + + if (blendBits == (GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO) + || blendBits == (GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR)) + { + int stateBits0 = stages[0].stateBits; + int stateBits1 = stages[1].stateBits; + shaderStage_t swapStage; + + swapStage = stages[0]; + stages[0] = stages[1]; + stages[1] = swapStage; + + stages[0].stateBits = stateBits0; + stages[1].stateBits = stateBits1; + } + } + } + + if (!skip) + { + // scan for shaders that aren't supported + for (i = 0; i < MAX_SHADER_STAGES; i++) + { + shaderStage_t *pStage = &stages[i]; + + if (!pStage->active) + continue; + + if (pStage->adjustColorsForFog) + { + skip = qtrue; + break; + } + + if (pStage->bundle[0].isLightmap) + { + int blendBits = pStage->stateBits & ( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); + + if (blendBits != (GLS_DSTBLEND_SRC_COLOR | GLS_SRCBLEND_ZERO) + && blendBits != (GLS_DSTBLEND_ZERO | GLS_SRCBLEND_DST_COLOR)) + { + skip = qtrue; + break; + } + } + + switch(pStage->bundle[0].tcGen) + { + case TCGEN_TEXTURE: + case TCGEN_LIGHTMAP: + case TCGEN_ENVIRONMENT_MAPPED: + break; + default: + skip = qtrue; + break; + } + + switch(pStage->alphaGen) + { + case AGEN_LIGHTING_SPECULAR: + case AGEN_PORTAL: + case AGEN_FRESNEL: + skip = qtrue; + break; + default: + break; + } + } + } + + if (!skip) + { + for (i = 0; i < MAX_SHADER_STAGES; i++) + { + shaderStage_t *pStage = &stages[i]; + shaderStage_t *diffuse, *normal, *specular, *lightmap; + qboolean parallax, environment, diffuselit, vertexlit; + + if (!pStage->active) + continue; + + // skip normal and specular maps + if (pStage->type != ST_COLORMAP) + continue; + + // skip lightmaps + if (pStage->bundle[0].isLightmap) + continue; + + diffuse = pStage; + normal = NULL; + parallax = qfalse; + specular = NULL; + lightmap = NULL; + + // we have a diffuse map, find matching normal, specular, and lightmap + for (j = i + 1; j < MAX_SHADER_STAGES; j++) + { + shaderStage_t *pStage2 = &stages[j]; + + if (!pStage2->active) + continue; + + switch(pStage2->type) + { + case ST_NORMALMAP: + if (!normal) + { + normal = pStage2; + } + break; + + case ST_NORMALPARALLAXMAP: + if (!normal) + { + normal = pStage2; + parallax = qtrue; + } + break; + + case ST_SPECULARMAP: + if (!specular) + { + specular = pStage2; + } + break; + + case ST_COLORMAP: + if (pStage2->bundle[0].isLightmap) + { + lightmap = pStage2; + } + break; + + default: + break; + } + } + + environment = qfalse; + if (diffuse->bundle[0].tcGen == TCGEN_ENVIRONMENT_MAPPED) + { + environment = qtrue; + } + + diffuselit = qfalse; + if (diffuse->rgbGen == CGEN_LIGHTING_DIFFUSE) + { + diffuselit = qtrue; + } + + vertexlit = qfalse; + if (diffuse->rgbGen == CGEN_VERTEX_LIT || diffuse->rgbGen == CGEN_EXACT_VERTEX_LIT) + { + vertexlit = qtrue; + } + + CollapseStagesToLightall(diffuse, normal, specular, lightmap, diffuselit, vertexlit, parallax, environment); + } + + // deactivate lightmap stages + for (i = 0; i < MAX_SHADER_STAGES; i++) + { + shaderStage_t *pStage = &stages[i]; + + if (!pStage->active) + continue; + + if (pStage->bundle[0].isLightmap) + { + pStage->active = qfalse; + } + } + } + + // deactivate normal and specular stages + for (i = 0; i < MAX_SHADER_STAGES; i++) + { + shaderStage_t *pStage = &stages[i]; + + if (!pStage->active) + continue; + + if (pStage->type == ST_NORMALMAP) + { + pStage->active = qfalse; + } + + if (pStage->type == ST_NORMALPARALLAXMAP) + { + pStage->active = qfalse; + } + + if (pStage->type == ST_SPECULARMAP) + { + pStage->active = qfalse; + } + } + + // remove inactive stages + numStages = 0; + for (i = 0; i < MAX_SHADER_STAGES; i++) + { + if (!stages[i].active) + continue; + + if (i == numStages) + { + numStages++; + continue; + } + + stages[numStages] = stages[i]; + stages[i].active = qfalse; + numStages++; + } + + if (numStages == i && i >= 2 && CollapseMultitexture()) + numStages--; + + return numStages; +} + +/* +============= + +FixRenderCommandList +https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=493 +Arnout: this is a nasty issue. Shaders can be registered after drawsurfaces are generated +but before the frame is rendered. This will, for the duration of one frame, cause drawsurfaces +to be rendered with bad shaders. To fix this, need to go through all render commands and fix +sortedIndex. +============== +*/ +static void FixRenderCommandList( int newShader ) { + renderCommandList_t *cmdList = &backEndData->commands; + + if( cmdList ) { + const void *curCmd = cmdList->cmds; + + while ( 1 ) { + curCmd = PADP(curCmd, sizeof(void *)); + + switch ( *(const int *)curCmd ) { + case RC_SET_COLOR: + { + const setColorCommand_t *sc_cmd = (const setColorCommand_t *)curCmd; + curCmd = (const void *)(sc_cmd + 1); + break; + } + case RC_STRETCH_PIC: + { + const stretchPicCommand_t *sp_cmd = (const stretchPicCommand_t *)curCmd; + curCmd = (const void *)(sp_cmd + 1); + break; + } + case RC_DRAW_SURFS: + { + int i; + drawSurf_t *drawSurf; + shader_t *shader; + int fogNum; + int entityNum; + int dlightMap; + int pshadowMap; + int sortedIndex; + const drawSurfsCommand_t *ds_cmd = (const drawSurfsCommand_t *)curCmd; + + for( i = 0, drawSurf = ds_cmd->drawSurfs; i < ds_cmd->numDrawSurfs; i++, drawSurf++ ) { + R_DecomposeSort( drawSurf->sort, &entityNum, &shader, &fogNum, &dlightMap, &pshadowMap ); + sortedIndex = (( drawSurf->sort >> QSORT_SHADERNUM_SHIFT ) & (MAX_SHADERS-1)); + if( sortedIndex >= newShader ) { + sortedIndex++; + drawSurf->sort = (sortedIndex << QSORT_SHADERNUM_SHIFT) | entityNum | ( fogNum << QSORT_FOGNUM_SHIFT ) | ( (int)pshadowMap << QSORT_PSHADOW_SHIFT) | (int)dlightMap; + } + } + curCmd = (const void *)(ds_cmd + 1); + break; + } + case RC_DRAW_BUFFER: + { + const drawBufferCommand_t *db_cmd = (const drawBufferCommand_t *)curCmd; + curCmd = (const void *)(db_cmd + 1); + break; + } + case RC_SWAP_BUFFERS: + { + const swapBuffersCommand_t *sb_cmd = (const swapBuffersCommand_t *)curCmd; + curCmd = (const void *)(sb_cmd + 1); + break; + } + case RC_END_OF_LIST: + default: + return; + } + } + } +} + +/* +============== +SortNewShader + +Positions the most recently created shader in the tr.sortedShaders[] +array so that the shader->sort key is sorted reletive to the other +shaders. + +Sets shader->sortedIndex +============== +*/ +static void SortNewShader( void ) { + int i; + float sort; + shader_t *newShader; + + newShader = tr.shaders[ tr.numShaders - 1 ]; + sort = newShader->sort; + + for ( i = tr.numShaders - 2 ; i >= 0 ; i-- ) { + if ( tr.sortedShaders[ i ]->sort <= sort ) { + break; + } + tr.sortedShaders[i+1] = tr.sortedShaders[i]; + tr.sortedShaders[i+1]->sortedIndex++; + } + + // Arnout: fix rendercommandlist + // https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=493 + FixRenderCommandList( i+1 ); + + newShader->sortedIndex = i+1; + tr.sortedShaders[i+1] = newShader; +} + + +/* +==================== +GeneratePermanentShader +==================== +*/ +static shader_t *GeneratePermanentShader( void ) { + shader_t *newShader; + int i, b; + int size, hash; + + if ( tr.numShaders == MAX_SHADERS ) { + ri.Printf( PRINT_WARNING, "WARNING: GeneratePermanentShader - MAX_SHADERS hit\n"); + return tr.defaultShader; + } + + newShader = ri.Hunk_Alloc( sizeof( shader_t ), h_low ); + + *newShader = shader; + + if ( shader.sort <= SS_OPAQUE ) { + newShader->fogPass = FP_EQUAL; + } else if ( shader.contentFlags & CONTENTS_FOG ) { + newShader->fogPass = FP_LE; + } + + tr.shaders[ tr.numShaders ] = newShader; + newShader->index = tr.numShaders; + + tr.sortedShaders[ tr.numShaders ] = newShader; + newShader->sortedIndex = tr.numShaders; + + tr.numShaders++; + + for ( i = 0 ; i < newShader->numUnfoggedPasses ; i++ ) { + if ( !stages[i].active ) { + break; + } + newShader->stages[i] = ri.Hunk_Alloc( sizeof( stages[i] ), h_low ); + *newShader->stages[i] = stages[i]; + + for ( b = 0 ; b < NUM_TEXTURE_BUNDLES ; b++ ) { + size = newShader->stages[i]->bundle[b].numTexMods * sizeof( texModInfo_t ); + newShader->stages[i]->bundle[b].texMods = ri.Hunk_Alloc( size, h_low ); + Com_Memcpy( newShader->stages[i]->bundle[b].texMods, stages[i].bundle[b].texMods, size ); + } + } + + SortNewShader(); + + hash = generateHashValue(newShader->name, FILE_HASH_SIZE); + newShader->next = hashTable[hash]; + hashTable[hash] = newShader; + + return newShader; +} + +/* +================= +VertexLightingCollapse + +If vertex lighting is enabled, only render a single +pass, trying to guess which is the correct one to best aproximate +what it is supposed to look like. +================= +*/ +static void VertexLightingCollapse( void ) { + int stage; + shaderStage_t *bestStage; + int bestImageRank; + int rank; + + // if we aren't opaque, just use the first pass + if ( shader.sort == SS_OPAQUE ) { + + // pick the best texture for the single pass + bestStage = &stages[0]; + bestImageRank = -999999; + + for ( stage = 0; stage < MAX_SHADER_STAGES; stage++ ) { + shaderStage_t *pStage = &stages[stage]; + + if ( !pStage->active ) { + break; + } + rank = 0; + + if ( pStage->bundle[0].isLightmap ) { + rank -= 100; + } + if ( pStage->bundle[0].tcGen != TCGEN_TEXTURE ) { + rank -= 5; + } + if ( pStage->bundle[0].numTexMods ) { + rank -= 5; + } + if ( pStage->rgbGen != CGEN_IDENTITY && pStage->rgbGen != CGEN_IDENTITY_LIGHTING ) { + rank -= 3; + } + + if ( rank > bestImageRank ) { + bestImageRank = rank; + bestStage = pStage; + } + } + + stages[0].bundle[0] = bestStage->bundle[0]; + stages[0].stateBits &= ~( GLS_DSTBLEND_BITS | GLS_SRCBLEND_BITS ); + stages[0].stateBits |= GLS_DEPTHMASK_TRUE; + if ( shader.lightmapIndex == LIGHTMAP_NONE ) { + stages[0].rgbGen = CGEN_LIGHTING_DIFFUSE; + } else { + stages[0].rgbGen = CGEN_EXACT_VERTEX; + } + stages[0].alphaGen = AGEN_SKIP; + } else { + // don't use a lightmap (tesla coils) + if ( stages[0].bundle[0].isLightmap ) { + stages[0] = stages[1]; + } + + // if we were in a cross-fade cgen, hack it to normal + if ( stages[0].rgbGen == CGEN_ONE_MINUS_ENTITY || stages[1].rgbGen == CGEN_ONE_MINUS_ENTITY ) { + stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; + } + if ( ( stages[0].rgbGen == CGEN_WAVEFORM && stages[0].rgbWave.func == GF_SAWTOOTH ) + && ( stages[1].rgbGen == CGEN_WAVEFORM && stages[1].rgbWave.func == GF_INVERSE_SAWTOOTH ) ) { + stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; + } + if ( ( stages[0].rgbGen == CGEN_WAVEFORM && stages[0].rgbWave.func == GF_INVERSE_SAWTOOTH ) + && ( stages[1].rgbGen == CGEN_WAVEFORM && stages[1].rgbWave.func == GF_SAWTOOTH ) ) { + stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; + } + } + + for ( stage = 1; stage < MAX_SHADER_STAGES; stage++ ) { + shaderStage_t *pStage = &stages[stage]; + + if ( !pStage->active ) { + break; + } + + Com_Memset( pStage, 0, sizeof( *pStage ) ); + } +} + +/* +========================= +FinishShader + +Returns a freshly allocated shader with all the needed info +from the current global working shader +========================= +*/ +static shader_t *FinishShader( void ) { + int stage; + qboolean hasLightmapStage; + qboolean vertexLightmap; + + hasLightmapStage = qfalse; + vertexLightmap = qfalse; + + // + // set sky stuff appropriate + // + if ( shader.isSky ) { + shader.sort = SS_ENVIRONMENT; + } + + // + // set polygon offset + // + if ( shader.polygonOffset && !shader.sort ) { + shader.sort = SS_DECAL; + } + + // + // set appropriate stage information + // + for ( stage = 0; stage < MAX_SHADER_STAGES; ) { + shaderStage_t *pStage = &stages[stage]; + + if ( !pStage->active ) { + break; + } + + // check for a missing texture + if ( !pStage->bundle[0].image[0] ) { + ri.Printf( PRINT_WARNING, "Shader %s has a stage with no image\n", shader.name ); + pStage->active = qfalse; + stage++; + continue; + } + + // + // ditch this stage if it's detail and detail textures are disabled + // + if ( pStage->isDetail && !r_detailTextures->integer ) + { + int index; + + for(index = stage + 1; index < MAX_SHADER_STAGES; index++) + { + if(!stages[index].active) + break; + } + + if(index < MAX_SHADER_STAGES) + memmove(pStage, pStage + 1, sizeof(*pStage) * (index - stage)); + else + { + if(stage + 1 < MAX_SHADER_STAGES) + memmove(pStage, pStage + 1, sizeof(*pStage) * (index - stage - 1)); + + Com_Memset(&stages[index - 1], 0, sizeof(*stages)); + } + + continue; + } + + // + // default texture coordinate generation + // + if ( pStage->bundle[0].isLightmap ) { + if ( pStage->bundle[0].tcGen == TCGEN_BAD ) { + pStage->bundle[0].tcGen = TCGEN_LIGHTMAP; + } + hasLightmapStage = qtrue; + } else { + if ( pStage->bundle[0].tcGen == TCGEN_BAD ) { + pStage->bundle[0].tcGen = TCGEN_TEXTURE; + } + } + + + // not a true lightmap but we want to leave existing + // behaviour in place and not print out a warning + //if (pStage->rgbGen == CGEN_VERTEX) { + // vertexLightmap = qtrue; + //} + + + + // + // determine sort order and fog color adjustment + // + if ( ( pStage->stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) && + ( stages[0].stateBits & ( GLS_SRCBLEND_BITS | GLS_DSTBLEND_BITS ) ) ) { + int blendSrcBits = pStage->stateBits & GLS_SRCBLEND_BITS; + int blendDstBits = pStage->stateBits & GLS_DSTBLEND_BITS; + + // fog color adjustment only works for blend modes that have a contribution + // that aproaches 0 as the modulate values aproach 0 -- + // GL_ONE, GL_ONE + // GL_ZERO, GL_ONE_MINUS_SRC_COLOR + // GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA + + // modulate, additive + if ( ( ( blendSrcBits == GLS_SRCBLEND_ONE ) && ( blendDstBits == GLS_DSTBLEND_ONE ) ) || + ( ( blendSrcBits == GLS_SRCBLEND_ZERO ) && ( blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_COLOR ) ) ) { + pStage->adjustColorsForFog = ACFF_MODULATE_RGB; + } + // strict blend + else if ( ( blendSrcBits == GLS_SRCBLEND_SRC_ALPHA ) && ( blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ) ) + { + pStage->adjustColorsForFog = ACFF_MODULATE_ALPHA; + } + // premultiplied alpha + else if ( ( blendSrcBits == GLS_SRCBLEND_ONE ) && ( blendDstBits == GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA ) ) + { + pStage->adjustColorsForFog = ACFF_MODULATE_RGBA; + } else { + // we can't adjust this one correctly, so it won't be exactly correct in fog + } + + // don't screw with sort order if this is a portal or environment + if ( !shader.sort ) { + // see through item, like a grill or grate + if ( pStage->stateBits & GLS_DEPTHMASK_TRUE ) { + shader.sort = SS_SEE_THROUGH; + } else { + shader.sort = SS_BLEND0; + } + } + } + + stage++; + } + + // there are times when you will need to manually apply a sort to + // opaque alpha tested shaders that have later blend passes + if ( !shader.sort ) { + shader.sort = SS_OPAQUE; + } + + // + // if we are in r_vertexLight mode, never use a lightmap texture + // + if ( stage > 1 && ( (r_vertexLight->integer && !r_uiFullScreen->integer) || glConfig.hardwareType == GLHW_PERMEDIA2 ) ) { + VertexLightingCollapse(); + stage = 1; + hasLightmapStage = qfalse; + } + + // + // look for multitexture potential + // + stage = CollapseStagesToGLSL(); + + if ( shader.lightmapIndex >= 0 && !hasLightmapStage ) { + if (vertexLightmap) { + ri.Printf( PRINT_DEVELOPER, "WARNING: shader '%s' has VERTEX forced lightmap!\n", shader.name ); + } else { + ri.Printf( PRINT_DEVELOPER, "WARNING: shader '%s' has lightmap but no lightmap stage!\n", shader.name ); + // Don't set this, it will just add duplicate shaders to the hash + //shader.lightmapIndex = LIGHTMAP_NONE; + } + } + + + // + // compute number of passes + // + shader.numUnfoggedPasses = stage; + + // fogonly shaders don't have any normal passes + if (stage == 0 && !shader.isSky) + shader.sort = SS_FOG; + + // determine which stage iterator function is appropriate + ComputeStageIteratorFunc(); + + // determine which vertex attributes this shader needs + ComputeVertexAttribs(); + + return GeneratePermanentShader(); +} + +//======================================================================================== + +/* +==================== +FindShaderInShaderText + +Scans the combined text description of all the shader files for +the given shader name. + +return NULL if not found + +If found, it will return a valid shader +===================== +*/ +static char *FindShaderInShaderText( const char *shadername ) { + + char *token, *p; + + int i, hash; + + hash = generateHashValue(shadername, MAX_SHADERTEXT_HASH); + + if(shaderTextHashTable[hash]) + { + for (i = 0; shaderTextHashTable[hash][i]; i++) + { + p = shaderTextHashTable[hash][i]; + token = COM_ParseExt(&p, qtrue); + + if(!Q_stricmp(token, shadername)) + return p; + } + } + + p = s_shaderText; + + if ( !p ) { + return NULL; + } + + // look for label + while ( 1 ) { + token = COM_ParseExt( &p, qtrue ); + if ( token[0] == 0 ) { + break; + } + + if ( !Q_stricmp( token, shadername ) ) { + return p; + } + else { + // skip the definition + SkipBracedSection( &p ); + } + } + + return NULL; +} + + +/* +================== +R_FindShaderByName + +Will always return a valid shader, but it might be the +default shader if the real one can't be found. +================== +*/ +shader_t *R_FindShaderByName( const char *name ) { + char strippedName[MAX_QPATH]; + int hash; + shader_t *sh; + + if ( (name==NULL) || (name[0] == 0) ) { + return tr.defaultShader; + } + + COM_StripExtension(name, strippedName, sizeof(strippedName)); + + hash = generateHashValue(strippedName, FILE_HASH_SIZE); + + // + // see if the shader is already loaded + // + for (sh=hashTable[hash]; sh; sh=sh->next) { + // NOTE: if there was no shader or image available with the name strippedName + // then a default shader is created with lightmapIndex == LIGHTMAP_NONE, so we + // have to check all default shaders otherwise for every call to R_FindShader + // with that same strippedName a new default shader is created. + if (Q_stricmp(sh->name, strippedName) == 0) { + // match found + return sh; + } + } + + return tr.defaultShader; +} + + +/* +=============== +R_FindShader + +Will always return a valid shader, but it might be the +default shader if the real one can't be found. + +In the interest of not requiring an explicit shader text entry to +be defined for every single image used in the game, three default +shader behaviors can be auto-created for any image: + +If lightmapIndex == LIGHTMAP_NONE, then the image will have +dynamic diffuse lighting applied to it, as apropriate for most +entity skin surfaces. + +If lightmapIndex == LIGHTMAP_2D, then the image will be used +for 2D rendering unless an explicit shader is found + +If lightmapIndex == LIGHTMAP_BY_VERTEX, then the image will use +the vertex rgba modulate values, as apropriate for misc_model +pre-lit surfaces. + +Other lightmapIndex values will have a lightmap stage created +and src*dest blending applied with the texture, as apropriate for +most world construction surfaces. + +=============== +*/ +shader_t *R_FindShader( const char *name, int lightmapIndex, qboolean mipRawImage ) { + char strippedName[MAX_QPATH]; + int i, hash; + char *shaderText; + image_t *image; + shader_t *sh; + + if ( name[0] == 0 ) { + return tr.defaultShader; + } + + // use (fullbright) vertex lighting if the bsp file doesn't have + // lightmaps + if ( lightmapIndex >= 0 && lightmapIndex >= tr.numLightmaps ) { + lightmapIndex = LIGHTMAP_BY_VERTEX; + } else if ( lightmapIndex < LIGHTMAP_2D ) { + // negative lightmap indexes cause stray pointers (think tr.lightmaps[lightmapIndex]) + ri.Printf( PRINT_WARNING, "WARNING: shader '%s' has invalid lightmap index of %d\n", name, lightmapIndex ); + lightmapIndex = LIGHTMAP_BY_VERTEX; + } + + COM_StripExtension(name, strippedName, sizeof(strippedName)); + + hash = generateHashValue(strippedName, FILE_HASH_SIZE); + + // + // see if the shader is already loaded + // + for (sh = hashTable[hash]; sh; sh = sh->next) { + // NOTE: if there was no shader or image available with the name strippedName + // then a default shader is created with lightmapIndex == LIGHTMAP_NONE, so we + // have to check all default shaders otherwise for every call to R_FindShader + // with that same strippedName a new default shader is created. + if ( (sh->lightmapIndex == lightmapIndex || sh->defaultShader) && + !Q_stricmp(sh->name, strippedName)) { + // match found + return sh; + } + } + + // clear the global shader + Com_Memset( &shader, 0, sizeof( shader ) ); + Com_Memset( &stages, 0, sizeof( stages ) ); + Q_strncpyz(shader.name, strippedName, sizeof(shader.name)); + shader.lightmapIndex = lightmapIndex; + for ( i = 0 ; i < MAX_SHADER_STAGES ; i++ ) { + stages[i].bundle[0].texMods = texMods[i]; + } + + // + // attempt to define shader from an explicit parameter file + // + shaderText = FindShaderInShaderText( strippedName ); + if ( shaderText ) { + // enable this when building a pak file to get a global list + // of all explicit shaders + if ( r_printShaders->integer ) { + ri.Printf( PRINT_ALL, "*SHADER* %s\n", name ); + } + + if ( !ParseShader( &shaderText ) ) { + // had errors, so use default shader + shader.defaultShader = qtrue; + } + sh = FinishShader(); + return sh; + } + + + // + // if not defined in the in-memory shader descriptions, + // look for a single supported image file + // + { + imgFlags_t flags; + + flags = IMGFLAG_NONE; + + if (r_srgb->integer) + flags |= IMGFLAG_SRGB; + + if (mipRawImage) + { + flags |= IMGFLAG_MIPMAP | IMGFLAG_PICMIP; + + if (r_genNormalMaps->integer) + flags |= IMGFLAG_GENNORMALMAP; + } + else + { + flags |= IMGFLAG_CLAMPTOEDGE; + } + + image = R_FindImageFile( name, IMGTYPE_COLORALPHA, flags ); + if ( !image ) { + ri.Printf( PRINT_DEVELOPER, "Couldn't find image file for shader %s\n", name ); + shader.defaultShader = qtrue; + return FinishShader(); + } + } + + // + // create the default shading commands + // + if ( shader.lightmapIndex == LIGHTMAP_NONE ) { + // dynamic colors at vertexes + stages[0].bundle[0].image[0] = image; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_LIGHTING_DIFFUSE; + stages[0].stateBits = GLS_DEFAULT; + } else if ( shader.lightmapIndex == LIGHTMAP_BY_VERTEX ) { + // explicit colors at vertexes + stages[0].bundle[0].image[0] = image; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_EXACT_VERTEX; + stages[0].alphaGen = AGEN_SKIP; + stages[0].stateBits = GLS_DEFAULT; + } else if ( shader.lightmapIndex == LIGHTMAP_2D ) { + // GUI elements + stages[0].bundle[0].image[0] = image; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_VERTEX; + stages[0].alphaGen = AGEN_VERTEX; + stages[0].stateBits = GLS_DEPTHTEST_DISABLE | + GLS_SRCBLEND_SRC_ALPHA | + GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; + } else if ( shader.lightmapIndex == LIGHTMAP_WHITEIMAGE ) { + // fullbright level + stages[0].bundle[0].image[0] = tr.whiteImage; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; + stages[0].stateBits = GLS_DEFAULT; + + stages[1].bundle[0].image[0] = image; + stages[1].active = qtrue; + stages[1].rgbGen = CGEN_IDENTITY; + stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; + } else { + // two pass lightmap + stages[0].bundle[0].image[0] = tr.lightmaps[shader.lightmapIndex]; + stages[0].bundle[0].isLightmap = qtrue; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_IDENTITY; // lightmaps are scaled on creation + // for identitylight + stages[0].stateBits = GLS_DEFAULT; + + stages[1].bundle[0].image[0] = image; + stages[1].active = qtrue; + stages[1].rgbGen = CGEN_IDENTITY; + stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; + } + + return FinishShader(); +} + + +qhandle_t RE_RegisterShaderFromImage(const char *name, int lightmapIndex, image_t *image, qboolean mipRawImage) { + int i, hash; + shader_t *sh; + + hash = generateHashValue(name, FILE_HASH_SIZE); + + // probably not necessary since this function + // only gets called from tr_font.c with lightmapIndex == LIGHTMAP_2D + // but better safe than sorry. + if ( lightmapIndex >= tr.numLightmaps ) { + lightmapIndex = LIGHTMAP_WHITEIMAGE; + } + + // + // see if the shader is already loaded + // + for (sh=hashTable[hash]; sh; sh=sh->next) { + // NOTE: if there was no shader or image available with the name strippedName + // then a default shader is created with lightmapIndex == LIGHTMAP_NONE, so we + // have to check all default shaders otherwise for every call to R_FindShader + // with that same strippedName a new default shader is created. + if ( (sh->lightmapIndex == lightmapIndex || sh->defaultShader) && + // index by name + !Q_stricmp(sh->name, name)) { + // match found + return sh->index; + } + } + + // clear the global shader + Com_Memset( &shader, 0, sizeof( shader ) ); + Com_Memset( &stages, 0, sizeof( stages ) ); + Q_strncpyz(shader.name, name, sizeof(shader.name)); + shader.lightmapIndex = lightmapIndex; + for ( i = 0 ; i < MAX_SHADER_STAGES ; i++ ) { + stages[i].bundle[0].texMods = texMods[i]; + } + + // + // create the default shading commands + // + if ( shader.lightmapIndex == LIGHTMAP_NONE ) { + // dynamic colors at vertexes + stages[0].bundle[0].image[0] = image; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_LIGHTING_DIFFUSE; + stages[0].stateBits = GLS_DEFAULT; + } else if ( shader.lightmapIndex == LIGHTMAP_BY_VERTEX ) { + // explicit colors at vertexes + stages[0].bundle[0].image[0] = image; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_EXACT_VERTEX; + stages[0].alphaGen = AGEN_SKIP; + stages[0].stateBits = GLS_DEFAULT; + } else if ( shader.lightmapIndex == LIGHTMAP_2D ) { + // GUI elements + stages[0].bundle[0].image[0] = image; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_VERTEX; + stages[0].alphaGen = AGEN_VERTEX; + stages[0].stateBits = GLS_DEPTHTEST_DISABLE | + GLS_SRCBLEND_SRC_ALPHA | + GLS_DSTBLEND_ONE_MINUS_SRC_ALPHA; + } else if ( shader.lightmapIndex == LIGHTMAP_WHITEIMAGE ) { + // fullbright level + stages[0].bundle[0].image[0] = tr.whiteImage; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_IDENTITY_LIGHTING; + stages[0].stateBits = GLS_DEFAULT; + + stages[1].bundle[0].image[0] = image; + stages[1].active = qtrue; + stages[1].rgbGen = CGEN_IDENTITY; + stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; + } else { + // two pass lightmap + stages[0].bundle[0].image[0] = tr.lightmaps[shader.lightmapIndex]; + stages[0].bundle[0].isLightmap = qtrue; + stages[0].active = qtrue; + stages[0].rgbGen = CGEN_IDENTITY; // lightmaps are scaled on creation + // for identitylight + stages[0].stateBits = GLS_DEFAULT; + + stages[1].bundle[0].image[0] = image; + stages[1].active = qtrue; + stages[1].rgbGen = CGEN_IDENTITY; + stages[1].stateBits |= GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO; + } + + sh = FinishShader(); + return sh->index; +} + + +/* +==================== +RE_RegisterShader + +This is the exported shader entry point for the rest of the system +It will always return an index that will be valid. + +This should really only be used for explicit shaders, because there is no +way to ask for different implicit lighting modes (vertex, lightmap, etc) +==================== +*/ +qhandle_t RE_RegisterShaderLightMap( const char *name, int lightmapIndex ) { + shader_t *sh; + + if ( strlen( name ) >= MAX_QPATH ) { + ri.Printf( PRINT_ALL, "Shader name exceeds MAX_QPATH\n" ); + return 0; + } + + sh = R_FindShader( name, lightmapIndex, qtrue ); + + // we want to return 0 if the shader failed to + // load for some reason, but R_FindShader should + // still keep a name allocated for it, so if + // something calls RE_RegisterShader again with + // the same name, we don't try looking for it again + if ( sh->defaultShader ) { + return 0; + } + + return sh->index; +} + + +/* +==================== +RE_RegisterShader + +This is the exported shader entry point for the rest of the system +It will always return an index that will be valid. + +This should really only be used for explicit shaders, because there is no +way to ask for different implicit lighting modes (vertex, lightmap, etc) +==================== +*/ +qhandle_t RE_RegisterShader( const char *name ) { + shader_t *sh; + + if ( strlen( name ) >= MAX_QPATH ) { + ri.Printf( PRINT_ALL, "Shader name exceeds MAX_QPATH\n" ); + return 0; + } + + sh = R_FindShader( name, LIGHTMAP_2D, qtrue ); + + // we want to return 0 if the shader failed to + // load for some reason, but R_FindShader should + // still keep a name allocated for it, so if + // something calls RE_RegisterShader again with + // the same name, we don't try looking for it again + if ( sh->defaultShader ) { + return 0; + } + + return sh->index; +} + + +/* +==================== +RE_RegisterShaderNoMip + +For menu graphics that should never be picmiped +==================== +*/ +qhandle_t RE_RegisterShaderNoMip( const char *name ) { + shader_t *sh; + + if ( strlen( name ) >= MAX_QPATH ) { + ri.Printf( PRINT_ALL, "Shader name exceeds MAX_QPATH\n" ); + return 0; + } + + sh = R_FindShader( name, LIGHTMAP_2D, qfalse ); + + // we want to return 0 if the shader failed to + // load for some reason, but R_FindShader should + // still keep a name allocated for it, so if + // something calls RE_RegisterShader again with + // the same name, we don't try looking for it again + if ( sh->defaultShader ) { + return 0; + } + + return sh->index; +} + +/* +==================== +R_GetShaderByHandle + +When a handle is passed in by another module, this range checks +it and returns a valid (possibly default) shader_t to be used internally. +==================== +*/ +shader_t *R_GetShaderByHandle( qhandle_t hShader ) { + if ( hShader < 0 ) { + ri.Printf( PRINT_WARNING, "R_GetShaderByHandle: out of range hShader '%d'\n", hShader ); + return tr.defaultShader; + } + if ( hShader >= tr.numShaders ) { + ri.Printf( PRINT_WARNING, "R_GetShaderByHandle: out of range hShader '%d'\n", hShader ); + return tr.defaultShader; + } + return tr.shaders[hShader]; +} + +/* +=============== +R_ShaderList_f + +Dump information on all valid shaders to the console +A second parameter will cause it to print in sorted order +=============== +*/ +void R_ShaderList_f (void) { + int i; + int count; + shader_t *shader; + + ri.Printf (PRINT_ALL, "-----------------------\n"); + + count = 0; + for ( i = 0 ; i < tr.numShaders ; i++ ) { + if ( ri.Cmd_Argc() > 1 ) { + shader = tr.sortedShaders[i]; + } else { + shader = tr.shaders[i]; + } + + ri.Printf( PRINT_ALL, "%i ", shader->numUnfoggedPasses ); + + if (shader->lightmapIndex >= 0 ) { + ri.Printf (PRINT_ALL, "L "); + } else { + ri.Printf (PRINT_ALL, " "); + } + if ( shader->multitextureEnv == GL_ADD ) { + ri.Printf( PRINT_ALL, "MT(a) " ); + } else if ( shader->multitextureEnv == GL_MODULATE ) { + ri.Printf( PRINT_ALL, "MT(m) " ); + } else if ( shader->multitextureEnv == GL_DECAL ) { + ri.Printf( PRINT_ALL, "MT(d) " ); + } else { + ri.Printf( PRINT_ALL, " " ); + } + if ( shader->explicitlyDefined ) { + ri.Printf( PRINT_ALL, "E " ); + } else { + ri.Printf( PRINT_ALL, " " ); + } + + if ( shader->optimalStageIteratorFunc == RB_StageIteratorGeneric ) { + ri.Printf( PRINT_ALL, "gen " ); + } else if ( shader->optimalStageIteratorFunc == RB_StageIteratorSky ) { + ri.Printf( PRINT_ALL, "sky " ); + } else { + ri.Printf( PRINT_ALL, " " ); + } + + if ( shader->defaultShader ) { + ri.Printf (PRINT_ALL, ": %s (DEFAULTED)\n", shader->name); + } else { + ri.Printf (PRINT_ALL, ": %s\n", shader->name); + } + count++; + } + ri.Printf (PRINT_ALL, "%i total shaders\n", count); + ri.Printf (PRINT_ALL, "------------------\n"); +} + +/* +==================== +ScanAndLoadShaderFiles + +Finds and loads all .shader files, combining them into +a single large text block that can be scanned for shader names +===================== +*/ +#define MAX_SHADER_FILES 4096 +static void ScanAndLoadShaderFiles( void ) +{ + char **shaderFiles; + char *buffers[MAX_SHADER_FILES]; + char *p; + int numShaderFiles; + int i; + char *oldp, *token, *hashMem, *textEnd; + int shaderTextHashTableSizes[MAX_SHADERTEXT_HASH], hash, size; + + long sum = 0, summand; + // scan for shader files + shaderFiles = ri.FS_ListFiles( "scripts", ".shader", &numShaderFiles ); + + if ( !shaderFiles || !numShaderFiles ) + { + ri.Printf( PRINT_WARNING, "WARNING: no shader files found\n" ); + return; + } + + if ( numShaderFiles > MAX_SHADER_FILES ) { + numShaderFiles = MAX_SHADER_FILES; + } + + // load and parse shader files + for ( i = 0; i < numShaderFiles; i++ ) + { + char filename[MAX_QPATH]; + + // look for a .mtr file first + { + char *ext; + Com_sprintf( filename, sizeof( filename ), "scripts/%s", shaderFiles[i] ); + if ( (ext = strrchr(filename, '.')) ) + { + strcpy(ext, ".mtr"); + } + + if ( ri.FS_ReadFile( filename, NULL ) <= 0 ) + { + Com_sprintf( filename, sizeof( filename ), "scripts/%s", shaderFiles[i] ); + } + } + + ri.Printf( PRINT_DEVELOPER, "...loading '%s'\n", filename ); + summand = ri.FS_ReadFile( filename, (void **)&buffers[i] ); + + if ( !buffers[i] ) + ri.Error( ERR_DROP, "Couldn't load %s", filename ); + + // Do a simple check on the shader structure in that file to make sure one bad shader file cannot fuck up all other shaders. + p = buffers[i]; + while(1) + { + token = COM_ParseExt(&p, qtrue); + + if(!*token) + break; + + oldp = p; + + token = COM_ParseExt(&p, qtrue); + if(token[0] != '{' && token[1] != '\0') + { + ri.Printf(PRINT_WARNING, "WARNING: Bad shader file %s has incorrect syntax.\n", filename); + ri.FS_FreeFile(buffers[i]); + buffers[i] = NULL; + break; + } + + SkipBracedSection(&oldp); + p = oldp; + } + + + if (buffers[i]) + sum += summand; + } + + // build single large buffer + s_shaderText = ri.Hunk_Alloc( sum + numShaderFiles*2, h_low ); + s_shaderText[ 0 ] = '\0'; + textEnd = s_shaderText; + + // free in reverse order, so the temp files are all dumped + for ( i = numShaderFiles - 1; i >= 0 ; i-- ) + { + if ( !buffers[i] ) + continue; + + strcat( textEnd, buffers[i] ); + strcat( textEnd, "\n" ); + textEnd += strlen( textEnd ); + ri.FS_FreeFile( buffers[i] ); + } + + COM_Compress( s_shaderText ); + + // free up memory + ri.FS_FreeFileList( shaderFiles ); + + Com_Memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes)); + size = 0; + + p = s_shaderText; + // look for shader names + while ( 1 ) { + token = COM_ParseExt( &p, qtrue ); + if ( token[0] == 0 ) { + break; + } + + hash = generateHashValue(token, MAX_SHADERTEXT_HASH); + shaderTextHashTableSizes[hash]++; + size++; + SkipBracedSection(&p); + } + + size += MAX_SHADERTEXT_HASH; + + hashMem = ri.Hunk_Alloc( size * sizeof(char *), h_low ); + + for (i = 0; i < MAX_SHADERTEXT_HASH; i++) { + shaderTextHashTable[i] = (char **) hashMem; + hashMem = ((char *) hashMem) + ((shaderTextHashTableSizes[i] + 1) * sizeof(char *)); + } + + Com_Memset(shaderTextHashTableSizes, 0, sizeof(shaderTextHashTableSizes)); + + p = s_shaderText; + // look for shader names + while ( 1 ) { + oldp = p; + token = COM_ParseExt( &p, qtrue ); + if ( token[0] == 0 ) { + break; + } + + hash = generateHashValue(token, MAX_SHADERTEXT_HASH); + shaderTextHashTable[hash][shaderTextHashTableSizes[hash]++] = oldp; + + SkipBracedSection(&p); + } + + return; + +} + + +/* +==================== +CreateInternalShaders +==================== +*/ +static void CreateInternalShaders( void ) { + tr.numShaders = 0; + + // init the default shader + Com_Memset( &shader, 0, sizeof( shader ) ); + Com_Memset( &stages, 0, sizeof( stages ) ); + + Q_strncpyz( shader.name, "", sizeof( shader.name ) ); + + shader.lightmapIndex = LIGHTMAP_NONE; + stages[0].bundle[0].image[0] = tr.defaultImage; + stages[0].active = qtrue; + stages[0].stateBits = GLS_DEFAULT; + tr.defaultShader = FinishShader(); + + // shadow shader is just a marker + Q_strncpyz( shader.name, "", sizeof( shader.name ) ); + shader.sort = SS_STENCIL_SHADOW; + tr.shadowShader = FinishShader(); +} + +static void CreateExternalShaders( void ) { + tr.projectionShadowShader = R_FindShader( "projectionShadow", LIGHTMAP_NONE, qtrue ); + tr.flareShader = R_FindShader( "flareShader", LIGHTMAP_NONE, qtrue ); + + // Hack to make fogging work correctly on flares. Fog colors are calculated + // in tr_flare.c already. + if(!tr.flareShader->defaultShader) + { + int index; + + for(index = 0; index < tr.flareShader->numUnfoggedPasses; index++) + { + tr.flareShader->stages[index]->adjustColorsForFog = ACFF_NONE; + tr.flareShader->stages[index]->stateBits |= GLS_DEPTHTEST_DISABLE; + } + } + + tr.sunShader = R_FindShader( "sun", LIGHTMAP_NONE, qtrue ); + + tr.sunFlareShader = R_FindShader( "gfx/2d/sunflare", LIGHTMAP_NONE, qtrue); + + // HACK: if sunflare is missing, make one using the flare image or dlight image + if (tr.sunFlareShader->defaultShader) + { + image_t *image; + + if (!tr.flareShader->defaultShader && tr.flareShader->stages[0] && tr.flareShader->stages[0]->bundle[0].image[0]) + image = tr.flareShader->stages[0]->bundle[0].image[0]; + else + image = tr.dlightImage; + + Com_Memset( &shader, 0, sizeof( shader ) ); + Com_Memset( &stages, 0, sizeof( stages ) ); + + Q_strncpyz( shader.name, "gfx/2d/sunflare", sizeof( shader.name ) ); + + shader.lightmapIndex = LIGHTMAP_NONE; + stages[0].bundle[0].image[0] = image; + stages[0].active = qtrue; + stages[0].stateBits = GLS_DEFAULT; + tr.sunFlareShader = FinishShader(); + } + +} + +/* +================== +R_InitShaders +================== +*/ +void R_InitShaders( void ) { + ri.Printf( PRINT_ALL, "Initializing Shaders\n" ); + + Com_Memset(hashTable, 0, sizeof(hashTable)); + + CreateInternalShaders(); + + ScanAndLoadShaderFiles(); + + CreateExternalShaders(); +} diff --git a/src/renderergl2/tr_shadows.c b/src/renderergl2/tr_shadows.c new file mode 100644 index 00000000..04777799 --- /dev/null +++ b/src/renderergl2/tr_shadows.c @@ -0,0 +1,344 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2009 Darklegion Development + +This file is part of Tremulous. + +Tremulous is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Tremulous is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Tremulous; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +#include "tr_local.h" + + +/* + + for a projection shadow: + + point[x] += light vector * ( z - shadow plane ) + point[y] += + point[z] = shadow plane + + 1 0 light[x] / light[z] + +*/ + +typedef struct { + int i2; + int facing; +} edgeDef_t; + +#define MAX_EDGE_DEFS 32 + +static edgeDef_t edgeDefs[SHADER_MAX_VERTEXES][MAX_EDGE_DEFS]; +static int numEdgeDefs[SHADER_MAX_VERTEXES]; +static int facing[SHADER_MAX_INDEXES/3]; + +void R_AddEdgeDef( int i1, int i2, int facing ) { + int c; + + c = numEdgeDefs[ i1 ]; + if ( c == MAX_EDGE_DEFS ) { + return; // overflow + } + edgeDefs[ i1 ][ c ].i2 = i2; + edgeDefs[ i1 ][ c ].facing = facing; + + numEdgeDefs[ i1 ]++; +} + +void R_RenderShadowEdges( void ) { + int i; + +#if 0 + int numTris; + + // dumb way -- render every triangle's edges + numTris = tess.numIndexes / 3; + + for ( i = 0 ; i < numTris ; i++ ) { + int i1, i2, i3; + + if ( !facing[i] ) { + continue; + } + + i1 = tess.indexes[ i*3 + 0 ]; + i2 = tess.indexes[ i*3 + 1 ]; + i3 = tess.indexes[ i*3 + 2 ]; + + qglBegin( GL_TRIANGLE_STRIP ); + qglVertex3fv( tess.xyz[ i1 ] ); + qglVertex3fv( tess.xyz[ i1 + tess.numVertexes ] ); + qglVertex3fv( tess.xyz[ i2 ] ); + qglVertex3fv( tess.xyz[ i2 + tess.numVertexes ] ); + qglVertex3fv( tess.xyz[ i3 ] ); + qglVertex3fv( tess.xyz[ i3 + tess.numVertexes ] ); + qglVertex3fv( tess.xyz[ i1 ] ); + qglVertex3fv( tess.xyz[ i1 + tess.numVertexes ] ); + qglEnd(); + } +#else + int c, c2; + int j, k; + int i2; + int c_edges, c_rejected; + int hit[2]; + + // an edge is NOT a silhouette edge if its face doesn't face the light, + // or if it has a reverse paired edge that also faces the light. + // A well behaved polyhedron would have exactly two faces for each edge, + // but lots of models have dangling edges or overfanned edges + c_edges = 0; + c_rejected = 0; + + for ( i = 0 ; i < tess.numVertexes ; i++ ) { + c = numEdgeDefs[ i ]; + for ( j = 0 ; j < c ; j++ ) { + if ( !edgeDefs[ i ][ j ].facing ) { + continue; + } + + hit[0] = 0; + hit[1] = 0; + + i2 = edgeDefs[ i ][ j ].i2; + c2 = numEdgeDefs[ i2 ]; + for ( k = 0 ; k < c2 ; k++ ) { + if ( edgeDefs[ i2 ][ k ].i2 == i ) { + hit[ edgeDefs[ i2 ][ k ].facing ]++; + } + } + + // if it doesn't share the edge with another front facing + // triangle, it is a sil edge + if ( hit[ 1 ] == 0 ) { + qglBegin( GL_TRIANGLE_STRIP ); + qglVertex3fv( tess.xyz[ i ] ); + qglVertex3fv( tess.xyz[ i + tess.numVertexes ] ); + qglVertex3fv( tess.xyz[ i2 ] ); + qglVertex3fv( tess.xyz[ i2 + tess.numVertexes ] ); + qglEnd(); + c_edges++; + } else { + c_rejected++; + } + } + } +#endif +} + +/* +================= +RB_ShadowTessEnd + +triangleFromEdge[ v1 ][ v2 ] + + + set triangle from edge( v1, v2, tri ) + if ( facing[ triangleFromEdge[ v1 ][ v2 ] ] && !facing[ triangleFromEdge[ v2 ][ v1 ] ) { + } +================= +*/ +void RB_ShadowTessEnd( void ) { + int i; + int numTris; + vec3_t lightDir; + GLboolean rgba[4]; + + // we can only do this if we have enough space in the vertex buffers + if ( tess.numVertexes >= SHADER_MAX_VERTEXES / 2 ) { + return; + } + + if ( glConfig.stencilBits < 4 ) { + return; + } + + VectorCopy( backEnd.currentEntity->lightDir, lightDir ); + + // project vertexes away from light direction + for ( i = 0 ; i < tess.numVertexes ; i++ ) { + VectorMA( tess.xyz[i], -512, lightDir, tess.xyz[i+tess.numVertexes] ); + } + + // decide which triangles face the light + Com_Memset( numEdgeDefs, 0, 4 * tess.numVertexes ); + + numTris = tess.numIndexes / 3; + for ( i = 0 ; i < numTris ; i++ ) { + int i1, i2, i3; + vec3_t d1, d2, normal; + float *v1, *v2, *v3; + float d; + + i1 = tess.indexes[ i*3 + 0 ]; + i2 = tess.indexes[ i*3 + 1 ]; + i3 = tess.indexes[ i*3 + 2 ]; + + v1 = tess.xyz[ i1 ]; + v2 = tess.xyz[ i2 ]; + v3 = tess.xyz[ i3 ]; + + VectorSubtract( v2, v1, d1 ); + VectorSubtract( v3, v1, d2 ); + CrossProduct( d1, d2, normal ); + + d = DotProduct( normal, lightDir ); + if ( d > 0 ) { + facing[ i ] = 1; + } else { + facing[ i ] = 0; + } + + // create the edges + R_AddEdgeDef( i1, i2, facing[ i ] ); + R_AddEdgeDef( i2, i3, facing[ i ] ); + R_AddEdgeDef( i3, i1, facing[ i ] ); + } + + // draw the silhouette edges + + GL_Bind( tr.whiteImage ); + qglEnable( GL_CULL_FACE ); + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO ); + qglColor3f( 0.2f, 0.2f, 0.2f ); + + // don't write to the color buffer + qglGetBooleanv(GL_COLOR_WRITEMASK, rgba); + qglColorMask( GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE ); + + qglEnable( GL_STENCIL_TEST ); + qglStencilFunc( GL_ALWAYS, 1, 255 ); + + // mirrors have the culling order reversed + if ( backEnd.viewParms.isMirror ) { + qglCullFace( GL_FRONT ); + qglStencilOp( GL_KEEP, GL_KEEP, GL_INCR ); + + R_RenderShadowEdges(); + + qglCullFace( GL_BACK ); + qglStencilOp( GL_KEEP, GL_KEEP, GL_DECR ); + + R_RenderShadowEdges(); + } else { + qglCullFace( GL_BACK ); + qglStencilOp( GL_KEEP, GL_KEEP, GL_INCR ); + + R_RenderShadowEdges(); + + qglCullFace( GL_FRONT ); + qglStencilOp( GL_KEEP, GL_KEEP, GL_DECR ); + + R_RenderShadowEdges(); + } + + + // reenable writing to the color buffer + qglColorMask(rgba[0], rgba[1], rgba[2], rgba[3]); +} + + +/* +================= +RB_ShadowFinish + +Darken everything that is is a shadow volume. +We have to delay this until everything has been shadowed, +because otherwise shadows from different body parts would +overlap and double darken. +================= +*/ +void RB_ShadowFinish( void ) { + if ( r_shadows->integer != 2 ) { + return; + } + if ( glConfig.stencilBits < 4 ) { + return; + } + qglEnable( GL_STENCIL_TEST ); + qglStencilFunc( GL_NOTEQUAL, 0, 255 ); + + qglDisable (GL_CLIP_PLANE0); + qglDisable (GL_CULL_FACE); + + GL_Bind( tr.whiteImage ); + + qglLoadIdentity (); + + qglColor3f( 0.6f, 0.6f, 0.6f ); + GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_DST_COLOR | GLS_DSTBLEND_ZERO ); + +// qglColor3f( 1, 0, 0 ); +// GL_State( GLS_DEPTHMASK_TRUE | GLS_SRCBLEND_ONE | GLS_DSTBLEND_ZERO ); + + qglBegin( GL_QUADS ); + qglVertex3f( -100, 100, -10 ); + qglVertex3f( 100, 100, -10 ); + qglVertex3f( 100, -100, -10 ); + qglVertex3f( -100, -100, -10 ); + qglEnd (); + + qglColor4f(1,1,1,1); + qglDisable( GL_STENCIL_TEST ); +} + + +/* +================= +RB_ProjectionShadowDeform + +================= +*/ +void RB_ProjectionShadowDeform( void ) { + float *xyz; + int i; + float h; + vec3_t ground; + vec3_t light; + float groundDist; + float d; + vec3_t lightDir; + + xyz = ( float * ) tess.xyz; + + ground[0] = backEnd.or.axis[0][2]; + ground[1] = backEnd.or.axis[1][2]; + ground[2] = backEnd.or.axis[2][2]; + + groundDist = backEnd.or.origin[2] - backEnd.currentEntity->e.shadowPlane; + + VectorCopy( backEnd.currentEntity->lightDir, lightDir ); + d = DotProduct( lightDir, ground ); + // don't let the shadows get too long or go negative + if ( d < 0.5 ) { + VectorMA( lightDir, (0.5 - d), ground, lightDir ); + d = DotProduct( lightDir, ground ); + } + d = 1.0 / d; + + light[0] = lightDir[0] * d; + light[1] = lightDir[1] * d; + light[2] = lightDir[2] * d; + + for ( i = 0; i < tess.numVertexes; i++, xyz += 4 ) { + h = DotProduct( xyz, ground ) + groundDist; + + xyz[0] -= light[0] * h; + xyz[1] -= light[1] * h; + xyz[2] -= light[2] * h; + } +} diff --git a/src/renderergl2/tr_sky.c b/src/renderergl2/tr_sky.c new file mode 100644 index 00000000..f3c24a45 --- /dev/null +++ b/src/renderergl2/tr_sky.c @@ -0,0 +1,916 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_sky.c +#include "tr_local.h" + +#define SKY_SUBDIVISIONS 8 +#define HALF_SKY_SUBDIVISIONS (SKY_SUBDIVISIONS/2) + +static float s_cloudTexCoords[6][SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1][2]; +static float s_cloudTexP[6][SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1]; + +/* +=================================================================================== + +POLYGON TO BOX SIDE PROJECTION + +=================================================================================== +*/ + +static vec3_t sky_clip[6] = +{ + {1,1,0}, + {1,-1,0}, + {0,-1,1}, + {0,1,1}, + {1,0,1}, + {-1,0,1} +}; + +static float sky_mins[2][6], sky_maxs[2][6]; +static float sky_min, sky_max; + +/* +================ +AddSkyPolygon +================ +*/ +static void AddSkyPolygon (int nump, vec3_t vecs) +{ + int i,j; + vec3_t v, av; + float s, t, dv; + int axis; + float *vp; + // s = [0]/[2], t = [1]/[2] + static int vec_to_st[6][3] = + { + {-2,3,1}, + {2,3,-1}, + + {1,3,2}, + {-1,3,-2}, + + {-2,-1,3}, + {-2,1,-3} + + // {-1,2,3}, + // {1,2,-3} + }; + + // decide which face it maps to + VectorCopy (vec3_origin, v); + for (i=0, vp=vecs ; i av[1] && av[0] > av[2]) + { + if (v[0] < 0) + axis = 1; + else + axis = 0; + } + else if (av[1] > av[2] && av[1] > av[0]) + { + if (v[1] < 0) + axis = 3; + else + axis = 2; + } + else + { + if (v[2] < 0) + axis = 5; + else + axis = 4; + } + + // project new texture coords + for (i=0 ; i 0) + dv = vecs[j - 1]; + else + dv = -vecs[-j - 1]; + if (dv < 0.001) + continue; // don't divide by zero + j = vec_to_st[axis][0]; + if (j < 0) + s = -vecs[-j -1] / dv; + else + s = vecs[j-1] / dv; + j = vec_to_st[axis][1]; + if (j < 0) + t = -vecs[-j -1] / dv; + else + t = vecs[j-1] / dv; + + if (s < sky_mins[0][axis]) + sky_mins[0][axis] = s; + if (t < sky_mins[1][axis]) + sky_mins[1][axis] = t; + if (s > sky_maxs[0][axis]) + sky_maxs[0][axis] = s; + if (t > sky_maxs[1][axis]) + sky_maxs[1][axis] = t; + } +} + +#define ON_EPSILON 0.1f // point on plane side epsilon +#define MAX_CLIP_VERTS 64 +/* +================ +ClipSkyPolygon +================ +*/ +static void ClipSkyPolygon (int nump, vec3_t vecs, int stage) +{ + float *norm; + float *v; + qboolean front, back; + float d, e; + float dists[MAX_CLIP_VERTS]; + int sides[MAX_CLIP_VERTS]; + vec3_t newv[2][MAX_CLIP_VERTS]; + int newc[2]; + int i, j; + + if (nump > MAX_CLIP_VERTS-2) + ri.Error (ERR_DROP, "ClipSkyPolygon: MAX_CLIP_VERTS"); + if (stage == 6) + { // fully clipped, so draw it + AddSkyPolygon (nump, vecs); + return; + } + + front = back = qfalse; + norm = sky_clip[stage]; + for (i=0, v = vecs ; i ON_EPSILON) + { + front = qtrue; + sides[i] = SIDE_FRONT; + } + else if (d < -ON_EPSILON) + { + back = qtrue; + sides[i] = SIDE_BACK; + } + else + sides[i] = SIDE_ON; + dists[i] = d; + } + + if (!front || !back) + { // not clipped + ClipSkyPolygon (nump, vecs, stage+1); + return; + } + + // clip it + sides[i] = sides[0]; + dists[i] = dists[0]; + VectorCopy (vecs, (vecs+(i*3)) ); + newc[0] = newc[1] = 0; + + for (i=0, v = vecs ; inumIndexes; i += 3 ) + { + for (j = 0 ; j < 3 ; j++) + { + VectorSubtract( input->xyz[input->indexes[i+j]], + backEnd.viewParms.or.origin, + p[j] ); + } + ClipSkyPolygon( 3, p[0], 0 ); + } +} + +/* +=================================================================================== + +CLOUD VERTEX GENERATION + +=================================================================================== +*/ + +/* +** MakeSkyVec +** +** Parms: s, t range from -1 to 1 +*/ +static void MakeSkyVec( float s, float t, int axis, float outSt[2], vec3_t outXYZ ) +{ + // 1 = s, 2 = t, 3 = 2048 + static int st_to_vec[6][3] = + { + {3,-1,2}, + {-3,1,2}, + + {1,3,2}, + {-1,-3,2}, + + {-2,-1,3}, // 0 degrees yaw, look straight up + {2,-1,-3} // look straight down + }; + + vec3_t b; + int j, k; + float boxSize; + + boxSize = backEnd.viewParms.zFar / 1.75; // div sqrt(3) + b[0] = s*boxSize; + b[1] = t*boxSize; + b[2] = boxSize; + + for (j=0 ; j<3 ; j++) + { + k = st_to_vec[axis][j]; + if (k < 0) + { + outXYZ[j] = -b[-k - 1]; + } + else + { + outXYZ[j] = b[k - 1]; + } + } + + // avoid bilerp seam + s = (s+1)*0.5; + t = (t+1)*0.5; + if (s < sky_min) + { + s = sky_min; + } + else if (s > sky_max) + { + s = sky_max; + } + + if (t < sky_min) + { + t = sky_min; + } + else if (t > sky_max) + { + t = sky_max; + } + + t = 1.0 - t; + + + if ( outSt ) + { + outSt[0] = s; + outSt[1] = t; + } +} + +static int sky_texorder[6] = {0,2,1,3,4,5}; +static vec3_t s_skyPoints[SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1]; +static float s_skyTexCoords[SKY_SUBDIVISIONS+1][SKY_SUBDIVISIONS+1][2]; + +static void DrawSkySide( struct image_s *image, const int mins[2], const int maxs[2] ) +{ + int s, t; + int firstVertex = tess.numVertexes; + //int firstIndex = tess.numIndexes; + int minIndex = tess.minIndex; + int maxIndex = tess.maxIndex; + vec4_t color; + + //tess.numVertexes = 0; + //tess.numIndexes = 0; + tess.firstIndex = tess.numIndexes; + + GL_Bind( image ); + GL_Cull( CT_TWO_SIDED ); + + for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t <= maxs[1]+HALF_SKY_SUBDIVISIONS; t++ ) + { + for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ ) + { + tess.xyz[tess.numVertexes][0] = s_skyPoints[t][s][0]; + tess.xyz[tess.numVertexes][1] = s_skyPoints[t][s][1]; + tess.xyz[tess.numVertexes][2] = s_skyPoints[t][s][2]; + tess.xyz[tess.numVertexes][3] = 1.0; + + tess.texCoords[tess.numVertexes][0][0] = s_skyTexCoords[t][s][0]; + tess.texCoords[tess.numVertexes][0][1] = s_skyTexCoords[t][s][1]; + + tess.numVertexes++; + + if(tess.numVertexes >= SHADER_MAX_VERTEXES) + { + ri.Error(ERR_DROP, "SHADER_MAX_VERTEXES hit in DrawSkySideVBO()"); + } + } + } + + for ( t = 0; t < maxs[1] - mins[1]; t++ ) + { + for ( s = 0; s < maxs[0] - mins[0]; s++ ) + { + if (tess.numIndexes + 6 >= SHADER_MAX_INDEXES) + { + ri.Error(ERR_DROP, "SHADER_MAX_INDEXES hit in DrawSkySideVBO()"); + } + + tess.indexes[tess.numIndexes++] = s + t * (maxs[0] - mins[0] + 1) + firstVertex; + tess.indexes[tess.numIndexes++] = s + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex; + tess.indexes[tess.numIndexes++] = (s + 1) + t * (maxs[0] - mins[0] + 1) + firstVertex; + + tess.indexes[tess.numIndexes++] = (s + 1) + t * (maxs[0] - mins[0] + 1) + firstVertex; + tess.indexes[tess.numIndexes++] = s + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex; + tess.indexes[tess.numIndexes++] = (s + 1) + (t + 1) * (maxs[0] - mins[0] + 1) + firstVertex; + } + } + + tess.minIndex = firstVertex; + tess.maxIndex = tess.numVertexes; + + // FIXME: A lot of this can probably be removed for speed, and refactored into a more convenient function + RB_UpdateVBOs(ATTR_POSITION | ATTR_TEXCOORD); +/* + { + shaderProgram_t *sp = &tr.textureColorShader; + + GLSL_VertexAttribsState(ATTR_POSITION | ATTR_TEXCOORD); + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + + color[0] = + color[1] = + color[2] = tr.identityLight; + color[3] = 1.0f; + GLSL_SetUniformVec4(sp, TEXTURECOLOR_UNIFORM_COLOR, color); + } +*/ + { + shaderProgram_t *sp = &tr.lightallShader[0]; + vec4_t vector; + + GLSL_VertexAttribsState(ATTR_POSITION | ATTR_TEXCOORD); + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, GENERIC_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + + color[0] = + color[1] = + color[2] = tr.identityLight; + color[3] = 1.0f; + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_BASECOLOR, color); + + color[0] = + color[1] = + color[2] = + color[3] = 0.0f; + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_VERTCOLOR, color); + + VectorSet4(vector, 1.0, 0.0, 0.0, 1.0); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_DIFFUSETEXMATRIX, vector); + + VectorSet4(vector, 0.0, 0.0, 0.0, 0.0); + GLSL_SetUniformVec4(sp, GENERIC_UNIFORM_DIFFUSETEXOFFTURB, vector); + } + + R_DrawElementsVBO(tess.numIndexes - tess.firstIndex, tess.firstIndex, tess.minIndex, tess.maxIndex); + + //qglDrawElements(GL_TRIANGLES, tess.numIndexes - tess.firstIndex, GL_INDEX_TYPE, BUFFER_OFFSET(tess.firstIndex * sizeof(GL_INDEX_TYPE))); + + //R_BindNullVBO(); + //R_BindNullIBO(); + + tess.numIndexes = tess.firstIndex; + tess.numVertexes = firstVertex; + tess.firstIndex = 0; + tess.minIndex = minIndex; + tess.maxIndex = maxIndex; +} + +static void DrawSkyBox( shader_t *shader ) +{ + int i; + + sky_min = 0; + sky_max = 1; + + Com_Memset( s_skyTexCoords, 0, sizeof( s_skyTexCoords ) ); + + for (i=0 ; i<6 ; i++) + { + int sky_mins_subd[2], sky_maxs_subd[2]; + int s, t; + + sky_mins[0][i] = floor( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; + sky_mins[1][i] = floor( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; + sky_maxs[0][i] = ceil( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; + sky_maxs[1][i] = ceil( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; + + if ( ( sky_mins[0][i] >= sky_maxs[0][i] ) || + ( sky_mins[1][i] >= sky_maxs[1][i] ) ) + { + continue; + } + + sky_mins_subd[0] = sky_mins[0][i] * HALF_SKY_SUBDIVISIONS; + sky_mins_subd[1] = sky_mins[1][i] * HALF_SKY_SUBDIVISIONS; + sky_maxs_subd[0] = sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS; + sky_maxs_subd[1] = sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS; + + if ( sky_mins_subd[0] < -HALF_SKY_SUBDIVISIONS ) + sky_mins_subd[0] = -HALF_SKY_SUBDIVISIONS; + else if ( sky_mins_subd[0] > HALF_SKY_SUBDIVISIONS ) + sky_mins_subd[0] = HALF_SKY_SUBDIVISIONS; + if ( sky_mins_subd[1] < -HALF_SKY_SUBDIVISIONS ) + sky_mins_subd[1] = -HALF_SKY_SUBDIVISIONS; + else if ( sky_mins_subd[1] > HALF_SKY_SUBDIVISIONS ) + sky_mins_subd[1] = HALF_SKY_SUBDIVISIONS; + + if ( sky_maxs_subd[0] < -HALF_SKY_SUBDIVISIONS ) + sky_maxs_subd[0] = -HALF_SKY_SUBDIVISIONS; + else if ( sky_maxs_subd[0] > HALF_SKY_SUBDIVISIONS ) + sky_maxs_subd[0] = HALF_SKY_SUBDIVISIONS; + if ( sky_maxs_subd[1] < -HALF_SKY_SUBDIVISIONS ) + sky_maxs_subd[1] = -HALF_SKY_SUBDIVISIONS; + else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS ) + sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS; + + // + // iterate through the subdivisions + // + for ( t = sky_mins_subd[1]+HALF_SKY_SUBDIVISIONS; t <= sky_maxs_subd[1]+HALF_SKY_SUBDIVISIONS; t++ ) + { + for ( s = sky_mins_subd[0]+HALF_SKY_SUBDIVISIONS; s <= sky_maxs_subd[0]+HALF_SKY_SUBDIVISIONS; s++ ) + { + MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, + ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, + i, + s_skyTexCoords[t][s], + s_skyPoints[t][s] ); + } + } + + DrawSkySide( shader->sky.outerbox[sky_texorder[i]], + sky_mins_subd, + sky_maxs_subd ); + } + +} + +static void FillCloudySkySide( const int mins[2], const int maxs[2], qboolean addIndexes ) +{ + int s, t; + int vertexStart = tess.numVertexes; + int tHeight, sWidth; + + tHeight = maxs[1] - mins[1] + 1; + sWidth = maxs[0] - mins[0] + 1; + + for ( t = mins[1]+HALF_SKY_SUBDIVISIONS; t <= maxs[1]+HALF_SKY_SUBDIVISIONS; t++ ) + { + for ( s = mins[0]+HALF_SKY_SUBDIVISIONS; s <= maxs[0]+HALF_SKY_SUBDIVISIONS; s++ ) + { + VectorAdd( s_skyPoints[t][s], backEnd.viewParms.or.origin, tess.xyz[tess.numVertexes] ); + tess.texCoords[tess.numVertexes][0][0] = s_skyTexCoords[t][s][0]; + tess.texCoords[tess.numVertexes][0][1] = s_skyTexCoords[t][s][1]; + + tess.numVertexes++; + + if ( tess.numVertexes >= SHADER_MAX_VERTEXES ) + { + ri.Error( ERR_DROP, "SHADER_MAX_VERTEXES hit in FillCloudySkySide()" ); + } + } + } + + // only add indexes for one pass, otherwise it would draw multiple times for each pass + if ( addIndexes ) { + for ( t = 0; t < tHeight-1; t++ ) + { + for ( s = 0; s < sWidth-1; s++ ) + { + tess.indexes[tess.numIndexes] = vertexStart + s + t * ( sWidth ); + tess.numIndexes++; + tess.indexes[tess.numIndexes] = vertexStart + s + ( t + 1 ) * ( sWidth ); + tess.numIndexes++; + tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * ( sWidth ); + tess.numIndexes++; + + tess.indexes[tess.numIndexes] = vertexStart + s + ( t + 1 ) * ( sWidth ); + tess.numIndexes++; + tess.indexes[tess.numIndexes] = vertexStart + s + 1 + ( t + 1 ) * ( sWidth ); + tess.numIndexes++; + tess.indexes[tess.numIndexes] = vertexStart + s + 1 + t * ( sWidth ); + tess.numIndexes++; + } + } + } +} + +static void FillCloudBox( const shader_t *shader, int stage ) +{ + int i; + + for ( i =0; i < 6; i++ ) + { + int sky_mins_subd[2], sky_maxs_subd[2]; + int s, t; + float MIN_T; + + if ( 1 ) // FIXME? shader->sky.fullClouds ) + { + MIN_T = -HALF_SKY_SUBDIVISIONS; + + // still don't want to draw the bottom, even if fullClouds + if ( i == 5 ) + continue; + } + else + { + switch( i ) + { + case 0: + case 1: + case 2: + case 3: + MIN_T = -1; + break; + case 5: + // don't draw clouds beneath you + continue; + case 4: // top + default: + MIN_T = -HALF_SKY_SUBDIVISIONS; + break; + } + } + + sky_mins[0][i] = floor( sky_mins[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; + sky_mins[1][i] = floor( sky_mins[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; + sky_maxs[0][i] = ceil( sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; + sky_maxs[1][i] = ceil( sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS ) / HALF_SKY_SUBDIVISIONS; + + if ( ( sky_mins[0][i] >= sky_maxs[0][i] ) || + ( sky_mins[1][i] >= sky_maxs[1][i] ) ) + { + continue; + } + + sky_mins_subd[0] = ri.ftol(sky_mins[0][i] * HALF_SKY_SUBDIVISIONS); + sky_mins_subd[1] = ri.ftol(sky_mins[1][i] * HALF_SKY_SUBDIVISIONS); + sky_maxs_subd[0] = ri.ftol(sky_maxs[0][i] * HALF_SKY_SUBDIVISIONS); + sky_maxs_subd[1] = ri.ftol(sky_maxs[1][i] * HALF_SKY_SUBDIVISIONS); + + if ( sky_mins_subd[0] < -HALF_SKY_SUBDIVISIONS ) + sky_mins_subd[0] = -HALF_SKY_SUBDIVISIONS; + else if ( sky_mins_subd[0] > HALF_SKY_SUBDIVISIONS ) + sky_mins_subd[0] = HALF_SKY_SUBDIVISIONS; + if ( sky_mins_subd[1] < MIN_T ) + sky_mins_subd[1] = MIN_T; + else if ( sky_mins_subd[1] > HALF_SKY_SUBDIVISIONS ) + sky_mins_subd[1] = HALF_SKY_SUBDIVISIONS; + + if ( sky_maxs_subd[0] < -HALF_SKY_SUBDIVISIONS ) + sky_maxs_subd[0] = -HALF_SKY_SUBDIVISIONS; + else if ( sky_maxs_subd[0] > HALF_SKY_SUBDIVISIONS ) + sky_maxs_subd[0] = HALF_SKY_SUBDIVISIONS; + if ( sky_maxs_subd[1] < MIN_T ) + sky_maxs_subd[1] = MIN_T; + else if ( sky_maxs_subd[1] > HALF_SKY_SUBDIVISIONS ) + sky_maxs_subd[1] = HALF_SKY_SUBDIVISIONS; + + // + // iterate through the subdivisions + // + for ( t = sky_mins_subd[1]+HALF_SKY_SUBDIVISIONS; t <= sky_maxs_subd[1]+HALF_SKY_SUBDIVISIONS; t++ ) + { + for ( s = sky_mins_subd[0]+HALF_SKY_SUBDIVISIONS; s <= sky_maxs_subd[0]+HALF_SKY_SUBDIVISIONS; s++ ) + { + MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, + ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, + i, + NULL, + s_skyPoints[t][s] ); + + s_skyTexCoords[t][s][0] = s_cloudTexCoords[i][t][s][0]; + s_skyTexCoords[t][s][1] = s_cloudTexCoords[i][t][s][1]; + } + } + + // only add indexes for first stage + FillCloudySkySide( sky_mins_subd, sky_maxs_subd, ( stage == 0 ) ); + } +} + +/* +** R_BuildCloudData +*/ +void R_BuildCloudData( shaderCommands_t *input ) +{ + int i; + shader_t *shader; + + shader = input->shader; + + assert( shader->isSky ); + + sky_min = 1.0 / 256.0f; // FIXME: not correct? + sky_max = 255.0 / 256.0f; + + // set up for drawing + tess.numIndexes = 0; + tess.numVertexes = 0; + tess.firstIndex = 0; + + if ( shader->sky.cloudHeight ) + { + for ( i = 0; i < MAX_SHADER_STAGES; i++ ) + { + if ( !tess.xstages[i] ) { + break; + } + FillCloudBox( shader, i ); + } + } +} + +/* +** R_InitSkyTexCoords +** Called when a sky shader is parsed +*/ +#define SQR( a ) ((a)*(a)) +void R_InitSkyTexCoords( float heightCloud ) +{ + int i, s, t; + float radiusWorld = 4096; + float p; + float sRad, tRad; + vec3_t skyVec; + vec3_t v; + + // init zfar so MakeSkyVec works even though + // a world hasn't been bounded + backEnd.viewParms.zFar = 1024; + + for ( i = 0; i < 6; i++ ) + { + for ( t = 0; t <= SKY_SUBDIVISIONS; t++ ) + { + for ( s = 0; s <= SKY_SUBDIVISIONS; s++ ) + { + // compute vector from view origin to sky side integral point + MakeSkyVec( ( s - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, + ( t - HALF_SKY_SUBDIVISIONS ) / ( float ) HALF_SKY_SUBDIVISIONS, + i, + NULL, + skyVec ); + + // compute parametric value 'p' that intersects with cloud layer + p = ( 1.0f / ( 2 * DotProduct( skyVec, skyVec ) ) ) * + ( -2 * skyVec[2] * radiusWorld + + 2 * sqrt( SQR( skyVec[2] ) * SQR( radiusWorld ) + + 2 * SQR( skyVec[0] ) * radiusWorld * heightCloud + + SQR( skyVec[0] ) * SQR( heightCloud ) + + 2 * SQR( skyVec[1] ) * radiusWorld * heightCloud + + SQR( skyVec[1] ) * SQR( heightCloud ) + + 2 * SQR( skyVec[2] ) * radiusWorld * heightCloud + + SQR( skyVec[2] ) * SQR( heightCloud ) ) ); + + s_cloudTexP[i][t][s] = p; + + // compute intersection point based on p + VectorScale( skyVec, p, v ); + v[2] += radiusWorld; + + // compute vector from world origin to intersection point 'v' + VectorNormalize( v ); + + sRad = Q_acos( v[0] ); + tRad = Q_acos( v[1] ); + + s_cloudTexCoords[i][t][s][0] = sRad; + s_cloudTexCoords[i][t][s][1] = tRad; + } + } + } +} + +//====================================================================================== + +/* +** RB_DrawSun +*/ +void RB_DrawSun( float scale, shader_t *shader ) { + float size; + float dist; + vec3_t origin, vec1, vec2; + + if ( !backEnd.skyRenderedThisView ) { + return; + } + + //qglLoadMatrixf( backEnd.viewParms.world.modelMatrix ); + //qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]); + { + // FIXME: this could be a lot cleaner + matrix_t translation, modelview; + + Matrix16Translation( backEnd.viewParms.or.origin, translation ); + Matrix16Multiply( backEnd.viewParms.world.modelMatrix, translation, modelview ); + GL_SetModelviewMatrix( modelview ); + } + + dist = backEnd.viewParms.zFar / 1.75; // div sqrt(3) + size = dist * scale; + + VectorScale( tr.sunDirection, dist, origin ); + PerpendicularVector( vec1, tr.sunDirection ); + CrossProduct( tr.sunDirection, vec1, vec2 ); + + VectorScale( vec1, size, vec1 ); + VectorScale( vec2, size, vec2 ); + + // farthest depth range + qglDepthRange( 1.0, 1.0 ); + + RB_BeginSurface( shader, 0 ); + + { + vec4_t color; + color[0] = color[1] = color[2] = color[3] = 1; + RB_AddQuadStamp(origin, vec1, vec2, color); + } + + RB_EndSurface(); + + // back to normal depth range + qglDepthRange( 0.0, 1.0 ); +} + + + + +/* +================ +RB_StageIteratorSky + +All of the visible sky triangles are in tess + +Other things could be stuck in here, like birds in the sky, etc +================ +*/ +void RB_StageIteratorSky( void ) { + if ( r_fastsky->integer ) { + return; + } + + // go through all the polygons and project them onto + // the sky box to see which blocks on each side need + // to be drawn + RB_ClipSkyPolygons( &tess ); + + // r_showsky will let all the sky blocks be drawn in + // front of everything to allow developers to see how + // much sky is getting sucked in + if ( r_showsky->integer ) { + qglDepthRange( 0.0, 0.0 ); + } else { + qglDepthRange( 1.0, 1.0 ); + } + + // draw the outer skybox + if ( tess.shader->sky.outerbox[0] && tess.shader->sky.outerbox[0] != tr.defaultImage ) { + matrix_t oldmodelview; + + GL_State( 0 ); + //qglTranslatef (backEnd.viewParms.or.origin[0], backEnd.viewParms.or.origin[1], backEnd.viewParms.or.origin[2]); + + { + // FIXME: this could be a lot cleaner + matrix_t trans, product; + + Matrix16Copy( glState.modelview, oldmodelview ); + Matrix16Translation( backEnd.viewParms.or.origin, trans ); + Matrix16Multiply( glState.modelview, trans, product ); + GL_SetModelviewMatrix( product ); + + } + + DrawSkyBox( tess.shader ); + + GL_SetModelviewMatrix( oldmodelview ); + } + + // generate the vertexes for all the clouds, which will be drawn + // by the generic shader routine + R_BuildCloudData( &tess ); + + RB_StageIteratorGeneric(); + + // draw the inner skybox + + + // back to normal depth range + qglDepthRange( 0.0, 1.0 ); + + // note that sky was drawn so we will draw a sun later + backEnd.skyRenderedThisView = qtrue; +} + + + + + diff --git a/src/renderergl2/tr_subs.c b/src/renderergl2/tr_subs.c new file mode 100644 index 00000000..6f490128 --- /dev/null +++ b/src/renderergl2/tr_subs.c @@ -0,0 +1,48 @@ +/* +=========================================================================== +Copyright (C) 2010 James Canete (use.less01@gmail.com) + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_subs.c - common function replacements for modular renderer + +#include "tr_local.h" + +void QDECL Com_Printf( const char *msg, ... ) +{ + va_list argptr; + char text[1024]; + + va_start(argptr, msg); + Q_vsnprintf(text, sizeof(text), msg, argptr); + va_end(argptr); + + ri.Printf(PRINT_ALL, "%s", text); +} + +void QDECL Com_Error( int level, const char *error, ... ) +{ + va_list argptr; + char text[1024]; + + va_start(argptr, error); + Q_vsnprintf(text, sizeof(text), error, argptr); + va_end(argptr); + + ri.Error(level, "%s", text); +} diff --git a/src/renderergl2/tr_surface.c b/src/renderergl2/tr_surface.c new file mode 100644 index 00000000..ad7f5a5f --- /dev/null +++ b/src/renderergl2/tr_surface.c @@ -0,0 +1,1698 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_surf.c +#include "tr_local.h" +#if idppc_altivec && !defined(MACOS_X) +#include +#endif + +/* + + THIS ENTIRE FILE IS BACK END + +backEnd.currentEntity will be valid. + +Tess_Begin has already been called for the surface's shader. + +The modelview matrix will be set. + +It is safe to actually issue drawing commands here if you don't want to +use the shader system. +*/ + + +//============================================================================ + + +/* +============== +RB_CheckOverflow +============== +*/ +void RB_CheckOverflow( int verts, int indexes ) { + if (tess.numVertexes + verts < SHADER_MAX_VERTEXES + && tess.numIndexes + indexes < SHADER_MAX_INDEXES) { + return; + } + + RB_EndSurface(); + + if ( verts >= SHADER_MAX_VERTEXES ) { + ri.Error(ERR_DROP, "RB_CheckOverflow: verts > MAX (%d > %d)", verts, SHADER_MAX_VERTEXES ); + } + if ( indexes >= SHADER_MAX_INDEXES ) { + ri.Error(ERR_DROP, "RB_CheckOverflow: indices > MAX (%d > %d)", indexes, SHADER_MAX_INDEXES ); + } + + RB_BeginSurface(tess.shader, tess.fogNum ); +} + +void RB_CheckVBOandIBO(VBO_t *vbo, IBO_t *ibo) +{ + if (!(vbo == glState.currentVBO && ibo == glState.currentIBO) || tess.multiDrawPrimitives >= MAX_MULTIDRAW_PRIMITIVES) + { + RB_EndSurface(); + RB_BeginSurface(tess.shader, tess.fogNum); + + R_BindVBO(vbo); + R_BindIBO(ibo); + } + + if (vbo != tess.vbo && ibo != tess.ibo) + tess.useInternalVBO = qfalse; +} + + +/* +============== +RB_AddQuadStampExt +============== +*/ +void RB_AddQuadStampExt( vec3_t origin, vec3_t left, vec3_t up, float color[4], float s1, float t1, float s2, float t2 ) { + vec3_t normal; + int ndx; + + RB_CHECKOVERFLOW( 4, 6 ); + + ndx = tess.numVertexes; + + // triangle indexes for a simple quad + tess.indexes[ tess.numIndexes ] = ndx; + tess.indexes[ tess.numIndexes + 1 ] = ndx + 1; + tess.indexes[ tess.numIndexes + 2 ] = ndx + 3; + + tess.indexes[ tess.numIndexes + 3 ] = ndx + 3; + tess.indexes[ tess.numIndexes + 4 ] = ndx + 1; + tess.indexes[ tess.numIndexes + 5 ] = ndx + 2; + + tess.xyz[ndx][0] = origin[0] + left[0] + up[0]; + tess.xyz[ndx][1] = origin[1] + left[1] + up[1]; + tess.xyz[ndx][2] = origin[2] + left[2] + up[2]; + + tess.xyz[ndx+1][0] = origin[0] - left[0] + up[0]; + tess.xyz[ndx+1][1] = origin[1] - left[1] + up[1]; + tess.xyz[ndx+1][2] = origin[2] - left[2] + up[2]; + + tess.xyz[ndx+2][0] = origin[0] - left[0] - up[0]; + tess.xyz[ndx+2][1] = origin[1] - left[1] - up[1]; + tess.xyz[ndx+2][2] = origin[2] - left[2] - up[2]; + + tess.xyz[ndx+3][0] = origin[0] + left[0] - up[0]; + tess.xyz[ndx+3][1] = origin[1] + left[1] - up[1]; + tess.xyz[ndx+3][2] = origin[2] + left[2] - up[2]; + + + // constant normal all the way around + VectorSubtract( vec3_origin, backEnd.viewParms.or.axis[0], normal ); + + tess.normal[ndx][0] = tess.normal[ndx+1][0] = tess.normal[ndx+2][0] = tess.normal[ndx+3][0] = normal[0]; + tess.normal[ndx][1] = tess.normal[ndx+1][1] = tess.normal[ndx+2][1] = tess.normal[ndx+3][1] = normal[1]; + tess.normal[ndx][2] = tess.normal[ndx+1][2] = tess.normal[ndx+2][2] = tess.normal[ndx+3][2] = normal[2]; + + // standard square texture coordinates + tess.texCoords[ndx][0][0] = tess.texCoords[ndx][1][0] = s1; + tess.texCoords[ndx][0][1] = tess.texCoords[ndx][1][1] = t1; + + tess.texCoords[ndx+1][0][0] = tess.texCoords[ndx+1][1][0] = s2; + tess.texCoords[ndx+1][0][1] = tess.texCoords[ndx+1][1][1] = t1; + + tess.texCoords[ndx+2][0][0] = tess.texCoords[ndx+2][1][0] = s2; + tess.texCoords[ndx+2][0][1] = tess.texCoords[ndx+2][1][1] = t2; + + tess.texCoords[ndx+3][0][0] = tess.texCoords[ndx+3][1][0] = s1; + tess.texCoords[ndx+3][0][1] = tess.texCoords[ndx+3][1][1] = t2; + + // constant color all the way around + // should this be identity and let the shader specify from entity? + tess.vertexColors[ndx][0] = color[0]; + tess.vertexColors[ndx][1] = color[1]; + tess.vertexColors[ndx][2] = color[2]; + tess.vertexColors[ndx][3] = color[3]; + + tess.vertexColors[ndx+1][0] = color[0]; + tess.vertexColors[ndx+1][1] = color[1]; + tess.vertexColors[ndx+1][2] = color[2]; + tess.vertexColors[ndx+1][3] = color[3]; + + tess.vertexColors[ndx+2][0] = color[0]; + tess.vertexColors[ndx+2][1] = color[1]; + tess.vertexColors[ndx+2][2] = color[2]; + tess.vertexColors[ndx+2][3] = color[3]; + + tess.vertexColors[ndx+3][0] = color[0]; + tess.vertexColors[ndx+3][1] = color[1]; + tess.vertexColors[ndx+3][2] = color[2]; + tess.vertexColors[ndx+3][3] = color[3]; + + tess.numVertexes += 4; + tess.numIndexes += 6; +} + +/* +============== +RB_AddQuadStamp +============== +*/ +void RB_AddQuadStamp( vec3_t origin, vec3_t left, vec3_t up, float color[4] ) { + RB_AddQuadStampExt( origin, left, up, color, 0, 0, 1, 1 ); +} + + +/* +============== +RB_InstantQuad + +based on Tess_InstantQuad from xreal +============== +*/ +void RB_InstantQuad2(vec4_t quadVerts[4], vec2_t texCoords[4]) +{ + GLimp_LogComment("--- RB_InstantQuad2 ---\n"); + + tess.numVertexes = 0; + tess.numIndexes = 0; + tess.firstIndex = 0; + + VectorCopy4(quadVerts[0], tess.xyz[tess.numVertexes]); + VectorCopy2(texCoords[0], tess.texCoords[tess.numVertexes][0]); + tess.numVertexes++; + + VectorCopy4(quadVerts[1], tess.xyz[tess.numVertexes]); + VectorCopy2(texCoords[1], tess.texCoords[tess.numVertexes][0]); + tess.numVertexes++; + + VectorCopy4(quadVerts[2], tess.xyz[tess.numVertexes]); + VectorCopy2(texCoords[2], tess.texCoords[tess.numVertexes][0]); + tess.numVertexes++; + + VectorCopy4(quadVerts[3], tess.xyz[tess.numVertexes]); + VectorCopy2(texCoords[3], tess.texCoords[tess.numVertexes][0]); + tess.numVertexes++; + + tess.indexes[tess.numIndexes++] = 0; + tess.indexes[tess.numIndexes++] = 1; + tess.indexes[tess.numIndexes++] = 2; + tess.indexes[tess.numIndexes++] = 0; + tess.indexes[tess.numIndexes++] = 2; + tess.indexes[tess.numIndexes++] = 3; + tess.minIndex = 0; + tess.maxIndex = 3; + + RB_UpdateVBOs(ATTR_POSITION | ATTR_TEXCOORD); + + GLSL_VertexAttribsState(ATTR_POSITION | ATTR_TEXCOORD); + + R_DrawElementsVBO(tess.numIndexes, tess.firstIndex, tess.minIndex, tess.maxIndex); + + tess.numIndexes = 0; + tess.numVertexes = 0; + tess.firstIndex = 0; + tess.minIndex = 0; + tess.maxIndex = 0; +} + + +void RB_InstantQuad(vec4_t quadVerts[4]) +{ + vec4_t color; + vec2_t texCoords[4]; + vec2_t invTexRes; + + VectorSet4(color, 1, 1, 1, 1); + + texCoords[0][0] = 0; + texCoords[0][1] = 0; + + texCoords[1][0] = 1; + texCoords[1][1] = 0; + + texCoords[2][0] = 1; + texCoords[2][1] = 1; + + texCoords[3][0] = 0; + texCoords[3][1] = 1; + + invTexRes[0] = 1.0f / 256.0f; + invTexRes[1] = 1.0f / 256.0f; + + GLSL_BindProgram(&tr.textureColorShader); + + GLSL_SetUniformMatrix16(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + GLSL_SetUniformVec4(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_COLOR, color); + GLSL_SetUniformVec2(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_INVTEXRES, invTexRes); + GLSL_SetUniformVec2(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_AUTOEXPOSUREMINMAX, tr.refdef.autoExposureMinMax); + GLSL_SetUniformVec3(&tr.textureColorShader, TEXTURECOLOR_UNIFORM_TONEMINAVGMAXLINEAR, tr.refdef.toneMinAvgMaxLinear); + + RB_InstantQuad2(quadVerts, texCoords); //, color, &tr.textureColorShader, invTexRes); +} + + +/* +============== +RB_SurfaceSprite +============== +*/ +static void RB_SurfaceSprite( void ) { + vec3_t left, up; + float radius; + float colors[4]; + trRefEntity_t *ent = backEnd.currentEntity; + + // calculate the xyz locations for the four corners + radius = ent->e.radius; + if ( ent->e.rotation == 0 ) { + VectorScale( backEnd.viewParms.or.axis[1], radius, left ); + VectorScale( backEnd.viewParms.or.axis[2], radius, up ); + } else { + float s, c; + float ang; + + ang = M_PI * ent->e.rotation / 180; + s = sin( ang ); + c = cos( ang ); + + VectorScale( backEnd.viewParms.or.axis[1], c * radius, left ); + VectorMA( left, -s * radius, backEnd.viewParms.or.axis[2], left ); + + VectorScale( backEnd.viewParms.or.axis[2], c * radius, up ); + VectorMA( up, s * radius, backEnd.viewParms.or.axis[1], up ); + } + if ( backEnd.viewParms.isMirror ) { + VectorSubtract( vec3_origin, left, left ); + } + + VectorScale4(ent->e.shaderRGBA, 1.0f / 255.0f, colors); + + RB_AddQuadStamp( ent->e.origin, left, up, colors ); +} + + +/* +============= +RB_SurfacePolychain +============= +*/ +static void RB_SurfacePolychain( srfPoly_t *p ) { + int i; + int numv; + + RB_CHECKOVERFLOW( p->numVerts, 3*(p->numVerts - 2) ); + + // fan triangles into the tess array + numv = tess.numVertexes; + for ( i = 0; i < p->numVerts; i++ ) { + VectorCopy( p->verts[i].xyz, tess.xyz[numv] ); + tess.texCoords[numv][0][0] = p->verts[i].st[0]; + tess.texCoords[numv][0][1] = p->verts[i].st[1]; + tess.vertexColors[numv][0] = p->verts[ i ].modulate[0] / 255.0f; + tess.vertexColors[numv][1] = p->verts[ i ].modulate[1] / 255.0f; + tess.vertexColors[numv][2] = p->verts[ i ].modulate[2] / 255.0f; + tess.vertexColors[numv][3] = p->verts[ i ].modulate[3] / 255.0f; + + numv++; + } + + // generate fan indexes into the tess array + for ( i = 0; i < p->numVerts-2; i++ ) { + tess.indexes[tess.numIndexes + 0] = tess.numVertexes; + tess.indexes[tess.numIndexes + 1] = tess.numVertexes + i + 1; + tess.indexes[tess.numIndexes + 2] = tess.numVertexes + i + 2; + tess.numIndexes += 3; + } + + tess.numVertexes = numv; +} + +static void RB_SurfaceHelper( int numVerts, srfVert_t *verts, int numTriangles, srfTriangle_t *triangles, int dlightBits, int pshadowBits) +{ + int i; + srfTriangle_t *tri; + srfVert_t *dv; + float *xyz, *normal, *texCoords, *lightCoords, *lightdir; +#ifdef USE_VERT_TANGENT_SPACE + float *tangent, *bitangent; +#endif + glIndex_t *index; + float *color; + + RB_CheckVBOandIBO(tess.vbo, tess.ibo); + + RB_CHECKOVERFLOW( numVerts, numTriangles * 3 ); + + tri = triangles; + index = &tess.indexes[ tess.numIndexes ]; + for ( i = 0 ; i < numTriangles ; i++, tri++ ) { + *index++ = tess.numVertexes + tri->indexes[0]; + *index++ = tess.numVertexes + tri->indexes[1]; + *index++ = tess.numVertexes + tri->indexes[2]; + } + tess.numIndexes += numTriangles * 3; + + if ( tess.shader->vertexAttribs & ATTR_POSITION ) + { + dv = verts; + xyz = tess.xyz[ tess.numVertexes ]; + for ( i = 0 ; i < numVerts ; i++, dv++, xyz+=4 ) + VectorCopy(dv->xyz, xyz); + } + + if ( tess.shader->vertexAttribs & ATTR_NORMAL ) + { + dv = verts; + normal = tess.normal[ tess.numVertexes ]; + for ( i = 0 ; i < numVerts ; i++, dv++, normal+=4 ) + VectorCopy(dv->normal, normal); + } + +#ifdef USE_VERT_TANGENT_SPACE + if ( tess.shader->vertexAttribs & ATTR_TANGENT ) + { + dv = verts; + tangent = tess.tangent[ tess.numVertexes ]; + for ( i = 0 ; i < numVerts ; i++, dv++, tangent+=4 ) + VectorCopy(dv->tangent, tangent); + } + + if ( tess.shader->vertexAttribs & ATTR_BITANGENT ) + { + dv = verts; + bitangent = tess.bitangent[ tess.numVertexes ]; + for ( i = 0 ; i < numVerts ; i++, dv++, bitangent+=4 ) + VectorCopy(dv->bitangent, bitangent); + } +#endif + + if ( tess.shader->vertexAttribs & ATTR_TEXCOORD ) + { + dv = verts; + texCoords = tess.texCoords[ tess.numVertexes ][0]; + for ( i = 0 ; i < numVerts ; i++, dv++, texCoords+=4 ) + VectorCopy2(dv->st, texCoords); + } + + if ( tess.shader->vertexAttribs & ATTR_LIGHTCOORD ) + { + dv = verts; + lightCoords = tess.texCoords[ tess.numVertexes ][1]; + for ( i = 0 ; i < numVerts ; i++, dv++, lightCoords+=4 ) + VectorCopy2(dv->lightmap, lightCoords); + } + + if ( tess.shader->vertexAttribs & ATTR_COLOR ) + { + dv = verts; + color = tess.vertexColors[ tess.numVertexes ]; + for ( i = 0 ; i < numVerts ; i++, dv++, color+=4 ) + VectorCopy4(dv->vertexColors, color); + } + + if ( tess.shader->vertexAttribs & ATTR_LIGHTDIRECTION ) + { + dv = verts; + lightdir = tess.lightdir[ tess.numVertexes ]; + for ( i = 0 ; i < numVerts ; i++, dv++, lightdir+=4 ) + VectorCopy(dv->lightdir, lightdir); + } + +#if 0 // nothing even uses vertex dlightbits + for ( i = 0 ; i < numVerts ; i++ ) { + tess.vertexDlightBits[ tess.numVertexes + i ] = dlightBits; + } +#endif + + tess.dlightBits |= dlightBits; + tess.pshadowBits |= pshadowBits; + + tess.numVertexes += numVerts; +} + +static qboolean RB_SurfaceHelperVBO(VBO_t *vbo, IBO_t *ibo, int numVerts, int numIndexes, int firstIndex, int minIndex, int maxIndex, int dlightBits, int pshadowBits, qboolean shaderCheck) +{ + int i, mergeForward, mergeBack; + GLvoid *firstIndexOffset, *lastIndexOffset; + + if (!vbo || !ibo) + { + return qfalse; + } + + if (shaderCheck && !(!ShaderRequiresCPUDeforms(tess.shader) && !tess.shader->isSky && !tess.shader->isPortal)) + { + return qfalse; + } + + RB_CheckVBOandIBO(vbo, ibo); + + tess.dlightBits |= dlightBits; + tess.pshadowBits |= pshadowBits; + + // merge this into any existing multidraw primitives + mergeForward = -1; + mergeBack = -1; + firstIndexOffset = BUFFER_OFFSET(firstIndex * sizeof(GL_INDEX_TYPE)); + lastIndexOffset = BUFFER_OFFSET((firstIndex + numIndexes) * sizeof(GL_INDEX_TYPE)); + + if (r_mergeMultidraws->integer) + { + i = 0; + + if (r_mergeMultidraws->integer == 1) + { + // lazy merge, only check the last primitive + if (tess.multiDrawPrimitives) + { + i = tess.multiDrawPrimitives - 1; + } + } + + for (; i < tess.multiDrawPrimitives; i++) + { + if (tess.multiDrawLastIndex[i] == firstIndexOffset) + { + mergeBack = i; + } + + if (lastIndexOffset == tess.multiDrawFirstIndex[i]) + { + mergeForward = i; + } + } + } + + if (mergeBack != -1 && mergeForward == -1) + { + tess.multiDrawNumIndexes[mergeBack] += numIndexes; + tess.multiDrawLastIndex[mergeBack] = tess.multiDrawFirstIndex[mergeBack] + tess.multiDrawNumIndexes[mergeBack]; + tess.multiDrawMinIndex[mergeBack] = MIN(tess.multiDrawMinIndex[mergeBack], minIndex); + tess.multiDrawMaxIndex[mergeBack] = MAX(tess.multiDrawMaxIndex[mergeBack], maxIndex); + backEnd.pc.c_multidrawsMerged++; + } + else if (mergeBack == -1 && mergeForward != -1) + { + tess.multiDrawNumIndexes[mergeForward] += numIndexes; + tess.multiDrawFirstIndex[mergeForward] = firstIndexOffset; + tess.multiDrawLastIndex[mergeForward] = tess.multiDrawFirstIndex[mergeForward] + tess.multiDrawNumIndexes[mergeForward]; + tess.multiDrawMinIndex[mergeForward] = MIN(tess.multiDrawMinIndex[mergeForward], minIndex); + tess.multiDrawMaxIndex[mergeForward] = MAX(tess.multiDrawMaxIndex[mergeForward], maxIndex); + backEnd.pc.c_multidrawsMerged++; + } + else if (mergeBack != -1 && mergeForward != -1) + { + tess.multiDrawNumIndexes[mergeBack] += numIndexes + tess.multiDrawNumIndexes[mergeForward]; + tess.multiDrawLastIndex[mergeBack] = tess.multiDrawFirstIndex[mergeBack] + tess.multiDrawNumIndexes[mergeBack]; + tess.multiDrawMinIndex[mergeBack] = MIN(tess.multiDrawMinIndex[mergeBack], MIN(tess.multiDrawMinIndex[mergeForward], minIndex)); + tess.multiDrawMaxIndex[mergeBack] = MAX(tess.multiDrawMaxIndex[mergeBack], MAX(tess.multiDrawMaxIndex[mergeForward], maxIndex)); + tess.multiDrawPrimitives--; + + if (mergeForward != tess.multiDrawPrimitives) + { + tess.multiDrawNumIndexes[mergeForward] = tess.multiDrawNumIndexes[tess.multiDrawPrimitives]; + tess.multiDrawFirstIndex[mergeForward] = tess.multiDrawFirstIndex[tess.multiDrawPrimitives]; + } + backEnd.pc.c_multidrawsMerged += 2; + } + else if (mergeBack == -1 && mergeForward == -1) + { + tess.multiDrawNumIndexes[tess.multiDrawPrimitives] = numIndexes; + tess.multiDrawFirstIndex[tess.multiDrawPrimitives] = firstIndexOffset; + tess.multiDrawLastIndex[tess.multiDrawPrimitives] = lastIndexOffset; + tess.multiDrawMinIndex[tess.multiDrawPrimitives] = minIndex; + tess.multiDrawMaxIndex[tess.multiDrawPrimitives] = maxIndex; + tess.multiDrawPrimitives++; + } + + backEnd.pc.c_multidraws++; + + tess.numIndexes += numIndexes; + tess.numVertexes += numVerts; + + return qtrue; +} + +/* +============= +RB_SurfaceTriangles +============= +*/ +static void RB_SurfaceTriangles( srfTriangles_t *srf ) { + if( RB_SurfaceHelperVBO (srf->vbo, srf->ibo, srf->numVerts, srf->numTriangles * 3, + srf->firstIndex, srf->minIndex, srf->maxIndex, srf->dlightBits, srf->pshadowBits, qtrue ) ) + { + return; + } + + RB_SurfaceHelper(srf->numVerts, srf->verts, srf->numTriangles, + srf->triangles, srf->dlightBits, srf->pshadowBits); +} + + + +/* +============== +RB_SurfaceBeam +============== +*/ +static void RB_SurfaceBeam( void ) +{ +#define NUM_BEAM_SEGS 6 + refEntity_t *e; + int i; + vec3_t perpvec; + vec3_t direction, normalized_direction; + vec3_t start_points[NUM_BEAM_SEGS], end_points[NUM_BEAM_SEGS]; + vec3_t oldorigin, origin; + + e = &backEnd.currentEntity->e; + + oldorigin[0] = e->oldorigin[0]; + oldorigin[1] = e->oldorigin[1]; + oldorigin[2] = e->oldorigin[2]; + + origin[0] = e->origin[0]; + origin[1] = e->origin[1]; + origin[2] = e->origin[2]; + + normalized_direction[0] = direction[0] = oldorigin[0] - origin[0]; + normalized_direction[1] = direction[1] = oldorigin[1] - origin[1]; + normalized_direction[2] = direction[2] = oldorigin[2] - origin[2]; + + if ( VectorNormalize( normalized_direction ) == 0 ) + return; + + PerpendicularVector( perpvec, normalized_direction ); + + VectorScale( perpvec, 4, perpvec ); + + for ( i = 0; i < NUM_BEAM_SEGS ; i++ ) + { + RotatePointAroundVector( start_points[i], normalized_direction, perpvec, (360.0/NUM_BEAM_SEGS)*i ); +// VectorAdd( start_points[i], origin, start_points[i] ); + VectorAdd( start_points[i], direction, end_points[i] ); + } + + GL_Bind( tr.whiteImage ); + + GL_State( GLS_SRCBLEND_ONE | GLS_DSTBLEND_ONE ); + + // FIXME: Quake3 doesn't use this, so I never tested it + tess.numVertexes = 0; + tess.numIndexes = 0; + tess.firstIndex = 0; + tess.minIndex = 0; + tess.maxIndex = 0; + + for ( i = 0; i <= NUM_BEAM_SEGS; i++ ) { + VectorCopy(start_points[ i % NUM_BEAM_SEGS ], tess.xyz[tess.numVertexes++]); + VectorCopy(end_points [ i % NUM_BEAM_SEGS ], tess.xyz[tess.numVertexes++]); + } + + for ( i = 0; i < NUM_BEAM_SEGS; i++ ) { + tess.indexes[tess.numIndexes++] = i * 2; + tess.indexes[tess.numIndexes++] = (i + 1) * 2; + tess.indexes[tess.numIndexes++] = 1 + i * 2; + + tess.indexes[tess.numIndexes++] = 1 + i * 2; + tess.indexes[tess.numIndexes++] = (i + 1) * 2; + tess.indexes[tess.numIndexes++] = 1 + (i + 1) * 2; + } + + tess.minIndex = 0; + tess.maxIndex = tess.numVertexes; + + // FIXME: A lot of this can probably be removed for speed, and refactored into a more convenient function + RB_UpdateVBOs(ATTR_POSITION); + + { + shaderProgram_t *sp = &tr.textureColorShader; + vec4_t color; + + GLSL_VertexAttribsState(ATTR_POSITION); + GLSL_BindProgram(sp); + + GLSL_SetUniformMatrix16(sp, TEXTURECOLOR_UNIFORM_MODELVIEWPROJECTIONMATRIX, glState.modelviewProjection); + + color[0] = 1.0f; + color[1] = 0.0f; + color[2] = 0.0f; + color[3] = 1.0f; + GLSL_SetUniformVec4(sp, TEXTURECOLOR_UNIFORM_COLOR, color); + } + + R_DrawElementsVBO(tess.numIndexes, tess.firstIndex, tess.minIndex, tess.maxIndex); + + tess.numIndexes = 0; + tess.numVertexes = 0; + tess.firstIndex = 0; + tess.minIndex = 0; + tess.maxIndex = 0; +} + +//================================================================================ + +static void DoRailCore( const vec3_t start, const vec3_t end, const vec3_t up, float len, float spanWidth ) +{ + float spanWidth2; + int vbase; + float t = len / 256.0f; + + vbase = tess.numVertexes; + + spanWidth2 = -spanWidth; + + // FIXME: use quad stamp? + VectorMA( start, spanWidth, up, tess.xyz[tess.numVertexes] ); + tess.texCoords[tess.numVertexes][0][0] = 0; + tess.texCoords[tess.numVertexes][0][1] = 0; + tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0] * 0.25 / 255.0f; + tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1] * 0.25 / 255.0f; + tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2] * 0.25 / 255.0f; + tess.numVertexes++; + + VectorMA( start, spanWidth2, up, tess.xyz[tess.numVertexes] ); + tess.texCoords[tess.numVertexes][0][0] = 0; + tess.texCoords[tess.numVertexes][0][1] = 1; + tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0] / 255.0f; + tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1] / 255.0f; + tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2] / 255.0f; + tess.numVertexes++; + + VectorMA( end, spanWidth, up, tess.xyz[tess.numVertexes] ); + + tess.texCoords[tess.numVertexes][0][0] = t; + tess.texCoords[tess.numVertexes][0][1] = 0; + tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0] / 255.0f; + tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1] / 255.0f; + tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2] / 255.0f; + tess.numVertexes++; + + VectorMA( end, spanWidth2, up, tess.xyz[tess.numVertexes] ); + tess.texCoords[tess.numVertexes][0][0] = t; + tess.texCoords[tess.numVertexes][0][1] = 1; + tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0] / 255.0f; + tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1] / 255.0f; + tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2] / 255.0f; + tess.numVertexes++; + + tess.indexes[tess.numIndexes++] = vbase; + tess.indexes[tess.numIndexes++] = vbase + 1; + tess.indexes[tess.numIndexes++] = vbase + 2; + + tess.indexes[tess.numIndexes++] = vbase + 2; + tess.indexes[tess.numIndexes++] = vbase + 1; + tess.indexes[tess.numIndexes++] = vbase + 3; +} + +static void DoRailDiscs( int numSegs, const vec3_t start, const vec3_t dir, const vec3_t right, const vec3_t up ) +{ + int i; + vec3_t pos[4]; + vec3_t v; + int spanWidth = r_railWidth->integer; + float c, s; + float scale; + + if ( numSegs > 1 ) + numSegs--; + if ( !numSegs ) + return; + + scale = 0.25; + + for ( i = 0; i < 4; i++ ) + { + c = cos( DEG2RAD( 45 + i * 90 ) ); + s = sin( DEG2RAD( 45 + i * 90 ) ); + v[0] = ( right[0] * c + up[0] * s ) * scale * spanWidth; + v[1] = ( right[1] * c + up[1] * s ) * scale * spanWidth; + v[2] = ( right[2] * c + up[2] * s ) * scale * spanWidth; + VectorAdd( start, v, pos[i] ); + + if ( numSegs > 1 ) + { + // offset by 1 segment if we're doing a long distance shot + VectorAdd( pos[i], dir, pos[i] ); + } + } + + for ( i = 0; i < numSegs; i++ ) + { + int j; + + RB_CHECKOVERFLOW( 4, 6 ); + + for ( j = 0; j < 4; j++ ) + { + VectorCopy( pos[j], tess.xyz[tess.numVertexes] ); + tess.texCoords[tess.numVertexes][0][0] = ( j < 2 ); + tess.texCoords[tess.numVertexes][0][1] = ( j && j != 3 ); + tess.vertexColors[tess.numVertexes][0] = backEnd.currentEntity->e.shaderRGBA[0] / 255.0f; + tess.vertexColors[tess.numVertexes][1] = backEnd.currentEntity->e.shaderRGBA[1] / 255.0f; + tess.vertexColors[tess.numVertexes][2] = backEnd.currentEntity->e.shaderRGBA[2] / 255.0f; + tess.numVertexes++; + + VectorAdd( pos[j], dir, pos[j] ); + } + + tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 0; + tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 1; + tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 3; + tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 3; + tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 1; + tess.indexes[tess.numIndexes++] = tess.numVertexes - 4 + 2; + } +} + +/* +** RB_SurfaceRailRinges +*/ +static void RB_SurfaceRailRings( void ) { + refEntity_t *e; + int numSegs; + int len; + vec3_t vec; + vec3_t right, up; + vec3_t start, end; + + e = &backEnd.currentEntity->e; + + VectorCopy( e->oldorigin, start ); + VectorCopy( e->origin, end ); + + // compute variables + VectorSubtract( end, start, vec ); + len = VectorNormalize( vec ); + MakeNormalVectors( vec, right, up ); + numSegs = ( len ) / r_railSegmentLength->value; + if ( numSegs <= 0 ) { + numSegs = 1; + } + + VectorScale( vec, r_railSegmentLength->value, vec ); + + DoRailDiscs( numSegs, start, vec, right, up ); +} + +/* +** RB_SurfaceRailCore +*/ +static void RB_SurfaceRailCore( void ) { + refEntity_t *e; + int len; + vec3_t right; + vec3_t vec; + vec3_t start, end; + vec3_t v1, v2; + + e = &backEnd.currentEntity->e; + + VectorCopy( e->oldorigin, start ); + VectorCopy( e->origin, end ); + + VectorSubtract( end, start, vec ); + len = VectorNormalize( vec ); + + // compute side vector + VectorSubtract( start, backEnd.viewParms.or.origin, v1 ); + VectorNormalize( v1 ); + VectorSubtract( end, backEnd.viewParms.or.origin, v2 ); + VectorNormalize( v2 ); + CrossProduct( v1, v2, right ); + VectorNormalize( right ); + + DoRailCore( start, end, right, len, r_railCoreWidth->integer ); +} + +/* +** RB_SurfaceLightningBolt +*/ +static void RB_SurfaceLightningBolt( void ) { + refEntity_t *e; + int len; + vec3_t right; + vec3_t vec; + vec3_t start, end; + vec3_t v1, v2; + int i; + + e = &backEnd.currentEntity->e; + + VectorCopy( e->oldorigin, end ); + VectorCopy( e->origin, start ); + + // compute variables + VectorSubtract( end, start, vec ); + len = VectorNormalize( vec ); + + // compute side vector + VectorSubtract( start, backEnd.viewParms.or.origin, v1 ); + VectorNormalize( v1 ); + VectorSubtract( end, backEnd.viewParms.or.origin, v2 ); + VectorNormalize( v2 ); + CrossProduct( v1, v2, right ); + VectorNormalize( right ); + + for ( i = 0 ; i < 4 ; i++ ) { + vec3_t temp; + + DoRailCore( start, end, right, len, 8 ); + RotatePointAroundVector( temp, vec, right, 45 ); + VectorCopy( temp, right ); + } +} + +/* +** VectorArrayNormalize +* +* The inputs to this routing seem to always be close to length = 1.0 (about 0.6 to 2.0) +* This means that we don't have to worry about zero length or enormously long vectors. +*/ +static void VectorArrayNormalize(vec4_t *normals, unsigned int count) +{ +// assert(count); + +#if idppc + { + register float half = 0.5; + register float one = 1.0; + float *components = (float *)normals; + + // Vanilla PPC code, but since PPC has a reciprocal square root estimate instruction, + // runs *much* faster than calling sqrt(). We'll use a single Newton-Raphson + // refinement step to get a little more precision. This seems to yeild results + // that are correct to 3 decimal places and usually correct to at least 4 (sometimes 5). + // (That is, for the given input range of about 0.6 to 2.0). + do { + float x, y, z; + float B, y0, y1; + + x = components[0]; + y = components[1]; + z = components[2]; + components += 4; + B = x*x + y*y + z*z; + +#ifdef __GNUC__ + asm("frsqrte %0,%1" : "=f" (y0) : "f" (B)); +#else + y0 = __frsqrte(B); +#endif + y1 = y0 + half*y0*(one - B*y0*y0); + + x = x * y1; + y = y * y1; + components[-4] = x; + z = z * y1; + components[-3] = y; + components[-2] = z; + } while(count--); + } +#else // No assembly version for this architecture, or C_ONLY defined + // given the input, it's safe to call VectorNormalizeFast + while (count--) { + VectorNormalizeFast(normals[0]); + normals++; + } +#endif + +} + + + +/* +** LerpMeshVertexes +*/ +#if idppc_altivec +static void LerpMeshVertexes_altivec(md3Surface_t *surf, float backlerp) +{ + short *oldXyz, *newXyz, *oldNormals, *newNormals; + float *outXyz, *outNormal; + float oldXyzScale QALIGN(16); + float newXyzScale QALIGN(16); + float oldNormalScale QALIGN(16); + float newNormalScale QALIGN(16); + int vertNum; + unsigned lat, lng; + int numVerts; + + outXyz = tess.xyz[tess.numVertexes]; + outNormal = tess.normal[tess.numVertexes]; + + newXyz = (short *)((byte *)surf + surf->ofsXyzNormals) + + (backEnd.currentEntity->e.frame * surf->numVerts * 4); + newNormals = newXyz + 3; + + newXyzScale = MD3_XYZ_SCALE * (1.0 - backlerp); + newNormalScale = 1.0 - backlerp; + + numVerts = surf->numVerts; + + if ( backlerp == 0 ) { + vector signed short newNormalsVec0; + vector signed short newNormalsVec1; + vector signed int newNormalsIntVec; + vector float newNormalsFloatVec; + vector float newXyzScaleVec; + vector unsigned char newNormalsLoadPermute; + vector unsigned char newNormalsStorePermute; + vector float zero; + + newNormalsStorePermute = vec_lvsl(0,(float *)&newXyzScaleVec); + newXyzScaleVec = *(vector float *)&newXyzScale; + newXyzScaleVec = vec_perm(newXyzScaleVec,newXyzScaleVec,newNormalsStorePermute); + newXyzScaleVec = vec_splat(newXyzScaleVec,0); + newNormalsLoadPermute = vec_lvsl(0,newXyz); + newNormalsStorePermute = vec_lvsr(0,outXyz); + zero = (vector float)vec_splat_s8(0); + // + // just copy the vertexes + // + for (vertNum=0 ; vertNum < numVerts ; vertNum++, + newXyz += 4, newNormals += 4, + outXyz += 4, outNormal += 4) + { + newNormalsLoadPermute = vec_lvsl(0,newXyz); + newNormalsStorePermute = vec_lvsr(0,outXyz); + newNormalsVec0 = vec_ld(0,newXyz); + newNormalsVec1 = vec_ld(16,newXyz); + newNormalsVec0 = vec_perm(newNormalsVec0,newNormalsVec1,newNormalsLoadPermute); + newNormalsIntVec = vec_unpackh(newNormalsVec0); + newNormalsFloatVec = vec_ctf(newNormalsIntVec,0); + newNormalsFloatVec = vec_madd(newNormalsFloatVec,newXyzScaleVec,zero); + newNormalsFloatVec = vec_perm(newNormalsFloatVec,newNormalsFloatVec,newNormalsStorePermute); + //outXyz[0] = newXyz[0] * newXyzScale; + //outXyz[1] = newXyz[1] * newXyzScale; + //outXyz[2] = newXyz[2] * newXyzScale; + + lat = ( newNormals[0] >> 8 ) & 0xff; + lng = ( newNormals[0] & 0xff ); + lat *= (FUNCTABLE_SIZE/256); + lng *= (FUNCTABLE_SIZE/256); + + // decode X as cos( lat ) * sin( long ) + // decode Y as sin( lat ) * sin( long ) + // decode Z as cos( long ) + + outNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; + outNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; + outNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; + + vec_ste(newNormalsFloatVec,0,outXyz); + vec_ste(newNormalsFloatVec,4,outXyz); + vec_ste(newNormalsFloatVec,8,outXyz); + } + } else { + // + // interpolate and copy the vertex and normal + // + oldXyz = (short *)((byte *)surf + surf->ofsXyzNormals) + + (backEnd.currentEntity->e.oldframe * surf->numVerts * 4); + oldNormals = oldXyz + 3; + + oldXyzScale = MD3_XYZ_SCALE * backlerp; + oldNormalScale = backlerp; + + for (vertNum=0 ; vertNum < numVerts ; vertNum++, + oldXyz += 4, newXyz += 4, oldNormals += 4, newNormals += 4, + outXyz += 4, outNormal += 4) + { + vec3_t uncompressedOldNormal, uncompressedNewNormal; + + // interpolate the xyz + outXyz[0] = oldXyz[0] * oldXyzScale + newXyz[0] * newXyzScale; + outXyz[1] = oldXyz[1] * oldXyzScale + newXyz[1] * newXyzScale; + outXyz[2] = oldXyz[2] * oldXyzScale + newXyz[2] * newXyzScale; + + // FIXME: interpolate lat/long instead? + lat = ( newNormals[0] >> 8 ) & 0xff; + lng = ( newNormals[0] & 0xff ); + lat *= 4; + lng *= 4; + uncompressedNewNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; + uncompressedNewNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; + uncompressedNewNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; + + lat = ( oldNormals[0] >> 8 ) & 0xff; + lng = ( oldNormals[0] & 0xff ); + lat *= 4; + lng *= 4; + + uncompressedOldNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; + uncompressedOldNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; + uncompressedOldNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; + + outNormal[0] = uncompressedOldNormal[0] * oldNormalScale + uncompressedNewNormal[0] * newNormalScale; + outNormal[1] = uncompressedOldNormal[1] * oldNormalScale + uncompressedNewNormal[1] * newNormalScale; + outNormal[2] = uncompressedOldNormal[2] * oldNormalScale + uncompressedNewNormal[2] * newNormalScale; + +// VectorNormalize (outNormal); + } + VectorArrayNormalize((vec4_t *)tess.normal[tess.numVertexes], numVerts); + } +} +#endif + +static void LerpMeshVertexes_scalar(mdvSurface_t *surf, float backlerp) +{ +#if 0 + short *oldXyz, *newXyz, *oldNormals, *newNormals; + float *outXyz, *outNormal; + float oldXyzScale, newXyzScale; + float oldNormalScale, newNormalScale; + int vertNum; + unsigned lat, lng; + int numVerts; + + outXyz = tess.xyz[tess.numVertexes]; + outNormal = tess.normal[tess.numVertexes]; + + newXyz = (short *)((byte *)surf + surf->ofsXyzNormals) + + (backEnd.currentEntity->e.frame * surf->numVerts * 4); + newNormals = newXyz + 3; + + newXyzScale = MD3_XYZ_SCALE * (1.0 - backlerp); + newNormalScale = 1.0 - backlerp; + + numVerts = surf->numVerts; + + if ( backlerp == 0 ) { + // + // just copy the vertexes + // + for (vertNum=0 ; vertNum < numVerts ; vertNum++, + newXyz += 4, newNormals += 4, + outXyz += 4, outNormal += 4) + { + + outXyz[0] = newXyz[0] * newXyzScale; + outXyz[1] = newXyz[1] * newXyzScale; + outXyz[2] = newXyz[2] * newXyzScale; + + lat = ( newNormals[0] >> 8 ) & 0xff; + lng = ( newNormals[0] & 0xff ); + lat *= (FUNCTABLE_SIZE/256); + lng *= (FUNCTABLE_SIZE/256); + + // decode X as cos( lat ) * sin( long ) + // decode Y as sin( lat ) * sin( long ) + // decode Z as cos( long ) + + outNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; + outNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; + outNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; + } + } else { + // + // interpolate and copy the vertex and normal + // + oldXyz = (short *)((byte *)surf + surf->ofsXyzNormals) + + (backEnd.currentEntity->e.oldframe * surf->numVerts * 4); + oldNormals = oldXyz + 3; + + oldXyzScale = MD3_XYZ_SCALE * backlerp; + oldNormalScale = backlerp; + + for (vertNum=0 ; vertNum < numVerts ; vertNum++, + oldXyz += 4, newXyz += 4, oldNormals += 4, newNormals += 4, + outXyz += 4, outNormal += 4) + { + vec3_t uncompressedOldNormal, uncompressedNewNormal; + + // interpolate the xyz + outXyz[0] = oldXyz[0] * oldXyzScale + newXyz[0] * newXyzScale; + outXyz[1] = oldXyz[1] * oldXyzScale + newXyz[1] * newXyzScale; + outXyz[2] = oldXyz[2] * oldXyzScale + newXyz[2] * newXyzScale; + + // FIXME: interpolate lat/long instead? + lat = ( newNormals[0] >> 8 ) & 0xff; + lng = ( newNormals[0] & 0xff ); + lat *= 4; + lng *= 4; + uncompressedNewNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; + uncompressedNewNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; + uncompressedNewNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; + + lat = ( oldNormals[0] >> 8 ) & 0xff; + lng = ( oldNormals[0] & 0xff ); + lat *= 4; + lng *= 4; + + uncompressedOldNormal[0] = tr.sinTable[(lat+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK] * tr.sinTable[lng]; + uncompressedOldNormal[1] = tr.sinTable[lat] * tr.sinTable[lng]; + uncompressedOldNormal[2] = tr.sinTable[(lng+(FUNCTABLE_SIZE/4))&FUNCTABLE_MASK]; + + outNormal[0] = uncompressedOldNormal[0] * oldNormalScale + uncompressedNewNormal[0] * newNormalScale; + outNormal[1] = uncompressedOldNormal[1] * oldNormalScale + uncompressedNewNormal[1] * newNormalScale; + outNormal[2] = uncompressedOldNormal[2] * oldNormalScale + uncompressedNewNormal[2] * newNormalScale; + +// VectorNormalize (outNormal); + } + VectorArrayNormalize((vec4_t *)tess.normal[tess.numVertexes], numVerts); + } +#endif + float *outXyz, *outNormal; + mdvVertex_t *newVerts; + int vertNum; + + newVerts = surf->verts + backEnd.currentEntity->e.frame * surf->numVerts; + + outXyz = tess.xyz[tess.numVertexes]; + outNormal = tess.normal[tess.numVertexes]; + + if (backlerp == 0) + { + // + // just copy the vertexes + // + + for (vertNum=0 ; vertNum < surf->numVerts ; vertNum++) + { + VectorCopy(newVerts->xyz, outXyz); + VectorCopy(newVerts->normal, outNormal); + newVerts++; + outXyz += 4; + outNormal += 4; + } + } + else + { + // + // interpolate and copy the vertex and normal + // + + mdvVertex_t *oldVerts; + + oldVerts = surf->verts + backEnd.currentEntity->e.oldframe * surf->numVerts; + + for (vertNum=0 ; vertNum < surf->numVerts ; vertNum++) + { + VectorLerp(backlerp, newVerts->xyz, oldVerts->xyz, outXyz); + VectorLerp(backlerp, newVerts->normal, oldVerts->normal, outNormal); + //VectorNormalize(outNormal); + newVerts++; + oldVerts++; + outXyz += 4; + outNormal += 4; + } + VectorArrayNormalize((vec4_t *)tess.normal[tess.numVertexes], surf->numVerts); + } + +} + +static void LerpMeshVertexes(mdvSurface_t *surf, float backlerp) +{ +#if 0 +#if idppc_altivec + if (com_altivec->integer) { + // must be in a seperate function or G3 systems will crash. + LerpMeshVertexes_altivec( surf, backlerp ); + return; + } +#endif // idppc_altivec +#endif + LerpMeshVertexes_scalar( surf, backlerp ); +} + + +/* +============= +RB_SurfaceMesh +============= +*/ +static void RB_SurfaceMesh(mdvSurface_t *surface) { + int j; + float backlerp; + srfTriangle_t *triangles; + mdvSt_t *texCoords; + int indexes; + int Bob, Doug; + int numVerts; + + if ( backEnd.currentEntity->e.oldframe == backEnd.currentEntity->e.frame ) { + backlerp = 0; + } else { + backlerp = backEnd.currentEntity->e.backlerp; + } + + RB_CHECKOVERFLOW( surface->numVerts, surface->numTriangles*3 ); + + LerpMeshVertexes (surface, backlerp); + + triangles = surface->triangles; + indexes = surface->numTriangles * 3; + Bob = tess.numIndexes; + Doug = tess.numVertexes; + for (j = 0 ; j < surface->numTriangles ; j++) { + tess.indexes[Bob + j*3 + 0] = Doug + triangles[j].indexes[0]; + tess.indexes[Bob + j*3 + 1] = Doug + triangles[j].indexes[1]; + tess.indexes[Bob + j*3 + 2] = Doug + triangles[j].indexes[2]; + } + tess.numIndexes += indexes; + + texCoords = surface->st; + + numVerts = surface->numVerts; + for ( j = 0; j < numVerts; j++ ) { + tess.texCoords[Doug + j][0][0] = texCoords[j].st[0]; + tess.texCoords[Doug + j][0][1] = texCoords[j].st[1]; + // FIXME: fill in lightmapST for completeness? + } + + tess.numVertexes += surface->numVerts; + +} + + +/* +============== +RB_SurfaceFace +============== +*/ +static void RB_SurfaceFace( srfSurfaceFace_t *srf ) { + if( RB_SurfaceHelperVBO (srf->vbo, srf->ibo, srf->numVerts, srf->numTriangles * 3, + srf->firstIndex, srf->minIndex, srf->maxIndex, srf->dlightBits, srf->pshadowBits, qtrue ) ) + { + return; + } + + RB_SurfaceHelper(srf->numVerts, srf->verts, srf->numTriangles, + srf->triangles, srf->dlightBits, srf->pshadowBits); +} + + +static float LodErrorForVolume( vec3_t local, float radius ) { + vec3_t world; + float d; + + // never let it go negative + if ( r_lodCurveError->value < 0 ) { + return 0; + } + + world[0] = local[0] * backEnd.or.axis[0][0] + local[1] * backEnd.or.axis[1][0] + + local[2] * backEnd.or.axis[2][0] + backEnd.or.origin[0]; + world[1] = local[0] * backEnd.or.axis[0][1] + local[1] * backEnd.or.axis[1][1] + + local[2] * backEnd.or.axis[2][1] + backEnd.or.origin[1]; + world[2] = local[0] * backEnd.or.axis[0][2] + local[1] * backEnd.or.axis[1][2] + + local[2] * backEnd.or.axis[2][2] + backEnd.or.origin[2]; + + VectorSubtract( world, backEnd.viewParms.or.origin, world ); + d = DotProduct( world, backEnd.viewParms.or.axis[0] ); + + if ( d < 0 ) { + d = -d; + } + d -= radius; + if ( d < 1 ) { + d = 1; + } + + return r_lodCurveError->value / d; +} + +/* +============= +RB_SurfaceGrid + +Just copy the grid of points and triangulate +============= +*/ +static void RB_SurfaceGrid( srfGridMesh_t *srf ) { + int i, j; + float *xyz; + float *texCoords, *lightCoords; + float *normal; +#ifdef USE_VERT_TANGENT_SPACE + float *tangent, *bitangent; +#endif + float *color, *lightdir; + srfVert_t *dv; + int rows, irows, vrows; + int used; + int widthTable[MAX_GRID_SIZE]; + int heightTable[MAX_GRID_SIZE]; + float lodError; + int lodWidth, lodHeight; + int numVertexes; + int dlightBits; + int pshadowBits; + //int *vDlightBits; + + if( RB_SurfaceHelperVBO (srf->vbo, srf->ibo, srf->numVerts, srf->numTriangles * 3, + srf->firstIndex, srf->minIndex, srf->maxIndex, srf->dlightBits, srf->pshadowBits, qtrue ) ) + { + return; + } + + dlightBits = srf->dlightBits; + tess.dlightBits |= dlightBits; + + pshadowBits = srf->pshadowBits; + tess.pshadowBits |= pshadowBits; + + // determine the allowable discrepance + lodError = LodErrorForVolume( srf->lodOrigin, srf->lodRadius ); + + // determine which rows and columns of the subdivision + // we are actually going to use + widthTable[0] = 0; + lodWidth = 1; + for ( i = 1 ; i < srf->width-1 ; i++ ) { + if ( srf->widthLodError[i] <= lodError ) { + widthTable[lodWidth] = i; + lodWidth++; + } + } + widthTable[lodWidth] = srf->width-1; + lodWidth++; + + heightTable[0] = 0; + lodHeight = 1; + for ( i = 1 ; i < srf->height-1 ; i++ ) { + if ( srf->heightLodError[i] <= lodError ) { + heightTable[lodHeight] = i; + lodHeight++; + } + } + heightTable[lodHeight] = srf->height-1; + lodHeight++; + + + // very large grids may have more points or indexes than can be fit + // in the tess structure, so we may have to issue it in multiple passes + + used = 0; + while ( used < lodHeight - 1 ) { + // see how many rows of both verts and indexes we can add without overflowing + do { + vrows = ( SHADER_MAX_VERTEXES - tess.numVertexes ) / lodWidth; + irows = ( SHADER_MAX_INDEXES - tess.numIndexes ) / ( lodWidth * 6 ); + + // if we don't have enough space for at least one strip, flush the buffer + if ( vrows < 2 || irows < 1 ) { + RB_EndSurface(); + RB_BeginSurface(tess.shader, tess.fogNum ); + } else { + break; + } + } while ( 1 ); + + rows = irows; + if ( vrows < irows + 1 ) { + rows = vrows - 1; + } + if ( used + rows > lodHeight ) { + rows = lodHeight - used; + } + + numVertexes = tess.numVertexes; + + xyz = tess.xyz[numVertexes]; + normal = tess.normal[numVertexes]; +#ifdef USE_VERT_TANGENT_SPACE + tangent = tess.tangent[numVertexes]; + bitangent = tess.bitangent[numVertexes]; +#endif + texCoords = tess.texCoords[numVertexes][0]; + lightCoords = tess.texCoords[numVertexes][1]; + color = tess.vertexColors[numVertexes]; + lightdir = tess.lightdir[numVertexes]; + //vDlightBits = &tess.vertexDlightBits[numVertexes]; + + for ( i = 0 ; i < rows ; i++ ) { + for ( j = 0 ; j < lodWidth ; j++ ) { + dv = srf->verts + heightTable[ used + i ] * srf->width + + widthTable[ j ]; + + if ( tess.shader->vertexAttribs & ATTR_POSITION ) + { + VectorCopy(dv->xyz, xyz); + xyz += 4; + } + + if ( tess.shader->vertexAttribs & ATTR_NORMAL ) + { + VectorCopy(dv->normal, normal); + normal += 4; + } + +#ifdef USE_VERT_TANGENT_SPACE + if ( tess.shader->vertexAttribs & ATTR_TANGENT ) + { + VectorCopy(dv->tangent, tangent); + tangent += 4; + } + + if ( tess.shader->vertexAttribs & ATTR_BITANGENT ) + { + VectorCopy(dv->bitangent, bitangent); + bitangent += 4; + } +#endif + if ( tess.shader->vertexAttribs & ATTR_TEXCOORD ) + { + VectorCopy2(dv->st, texCoords); + texCoords += 4; + } + + if ( tess.shader->vertexAttribs & ATTR_LIGHTCOORD ) + { + VectorCopy2(dv->lightmap, lightCoords); + lightCoords += 4; + } + + if ( tess.shader->vertexAttribs & ATTR_COLOR ) + { + VectorCopy4(dv->vertexColors, color); + color += 4; + } + + if ( tess.shader->vertexAttribs & ATTR_LIGHTDIRECTION ) + { + VectorCopy(dv->lightdir, lightdir); + lightdir += 4; + } + + //*vDlightBits++ = dlightBits; + } + } + + + // add the indexes + { + int numIndexes; + int w, h; + + h = rows - 1; + w = lodWidth - 1; + numIndexes = tess.numIndexes; + for (i = 0 ; i < h ; i++) { + for (j = 0 ; j < w ; j++) { + int v1, v2, v3, v4; + + // vertex order to be reckognized as tristrips + v1 = numVertexes + i*lodWidth + j + 1; + v2 = v1 - 1; + v3 = v2 + lodWidth; + v4 = v3 + 1; + + tess.indexes[numIndexes] = v2; + tess.indexes[numIndexes+1] = v3; + tess.indexes[numIndexes+2] = v1; + + tess.indexes[numIndexes+3] = v1; + tess.indexes[numIndexes+4] = v3; + tess.indexes[numIndexes+5] = v4; + numIndexes += 6; + } + } + + tess.numIndexes = numIndexes; + } + + tess.numVertexes += rows * lodWidth; + + used += rows - 1; + } +} + + +/* +=========================================================================== + +NULL MODEL + +=========================================================================== +*/ + +/* +=================== +RB_SurfaceAxis + +Draws x/y/z lines from the origin for orientation debugging +=================== +*/ +static void RB_SurfaceAxis( void ) { + // FIXME: implement this +#if 0 + GL_Bind( tr.whiteImage ); + qglLineWidth( 3 ); + qglBegin( GL_LINES ); + qglColor3f( 1,0,0 ); + qglVertex3f( 0,0,0 ); + qglVertex3f( 16,0,0 ); + qglColor3f( 0,1,0 ); + qglVertex3f( 0,0,0 ); + qglVertex3f( 0,16,0 ); + qglColor3f( 0,0,1 ); + qglVertex3f( 0,0,0 ); + qglVertex3f( 0,0,16 ); + qglEnd(); + qglLineWidth( 1 ); +#endif +} + +//=========================================================================== + +/* +==================== +RB_SurfaceEntity + +Entities that have a single procedurally generated surface +==================== +*/ +static void RB_SurfaceEntity( surfaceType_t *surfType ) { + switch( backEnd.currentEntity->e.reType ) { + case RT_SPRITE: + RB_SurfaceSprite(); + break; + case RT_BEAM: + RB_SurfaceBeam(); + break; + case RT_RAIL_CORE: + RB_SurfaceRailCore(); + break; + case RT_RAIL_RINGS: + RB_SurfaceRailRings(); + break; + case RT_LIGHTNING: + RB_SurfaceLightningBolt(); + break; + default: + RB_SurfaceAxis(); + break; + } + return; +} + +static void RB_SurfaceBad( surfaceType_t *surfType ) { + ri.Printf( PRINT_ALL, "Bad surface tesselated.\n" ); +} + +static void RB_SurfaceFlare(srfFlare_t *surf) +{ + if (r_flares->integer) + RB_AddFlare(surf, tess.fogNum, surf->origin, surf->color, surf->normal); +} + +static void RB_SurfaceVBOMesh(srfVBOMesh_t * srf) +{ + RB_SurfaceHelperVBO (srf->vbo, srf->ibo, srf->numVerts, srf->numIndexes, srf->firstIndex, + srf->minIndex, srf->maxIndex, srf->dlightBits, srf->pshadowBits, qfalse ); +} + +void RB_SurfaceVBOMDVMesh(srfVBOMDVMesh_t * surface) +{ + //mdvModel_t *mdvModel; + //mdvSurface_t *mdvSurface; + refEntity_t *refEnt; + + GLimp_LogComment("--- RB_SurfaceVBOMDVMesh ---\n"); + + if(!surface->vbo || !surface->ibo) + return; + + //RB_CheckVBOandIBO(surface->vbo, surface->ibo); + RB_EndSurface(); + RB_BeginSurface(tess.shader, tess.fogNum); + + R_BindVBO(surface->vbo); + R_BindIBO(surface->ibo); + + tess.useInternalVBO = qfalse; + + tess.numIndexes += surface->numIndexes; + tess.numVertexes += surface->numVerts; + tess.minIndex = surface->minIndex; + tess.maxIndex = surface->maxIndex; + + //mdvModel = surface->mdvModel; + //mdvSurface = surface->mdvSurface; + + refEnt = &backEnd.currentEntity->e; + + if(refEnt->oldframe == refEnt->frame) + { + glState.vertexAttribsInterpolation = 0; + } + else + { + glState.vertexAttribsInterpolation = refEnt->backlerp; + } + + glState.vertexAttribsOldFrame = refEnt->oldframe; + glState.vertexAttribsNewFrame = refEnt->frame; + + RB_EndSurface(); + + // So we don't lerp surfaces that shouldn't be lerped + glState.vertexAttribsInterpolation = 0; +} + +static void RB_SurfaceDisplayList( srfDisplayList_t *surf ) { + // all apropriate state must be set in RB_BeginSurface + // this isn't implemented yet... + qglCallList( surf->listNum ); +} + +static void RB_SurfaceSkip( void *surf ) { +} + + +void (*rb_surfaceTable[SF_NUM_SURFACE_TYPES])( void *) = { + (void(*)(void*))RB_SurfaceBad, // SF_BAD, + (void(*)(void*))RB_SurfaceSkip, // SF_SKIP, + (void(*)(void*))RB_SurfaceFace, // SF_FACE, + (void(*)(void*))RB_SurfaceGrid, // SF_GRID, + (void(*)(void*))RB_SurfaceTriangles, // SF_TRIANGLES, + (void(*)(void*))RB_SurfacePolychain, // SF_POLY, + (void(*)(void*))RB_SurfaceMesh, // SF_MDV, + (void(*)(void*))RB_SurfaceAnim, // SF_MD4, +#ifdef RAVENMD4 + (void(*)(void*))RB_MDRSurfaceAnim, // SF_MDR, +#endif + (void(*)(void*))RB_IQMSurfaceAnim, // SF_IQM, + (void(*)(void*))RB_SurfaceFlare, // SF_FLARE, + (void(*)(void*))RB_SurfaceEntity, // SF_ENTITY + (void(*)(void*))RB_SurfaceDisplayList, // SF_DISPLAY_LIST + (void(*)(void*))RB_SurfaceVBOMesh, // SF_VBO_MESH, + (void(*)(void*))RB_SurfaceVBOMDVMesh, // SF_VBO_MDVMESH +}; diff --git a/src/renderergl2/tr_vbo.c b/src/renderergl2/tr_vbo.c new file mode 100644 index 00000000..483df2e3 --- /dev/null +++ b/src/renderergl2/tr_vbo.c @@ -0,0 +1,928 @@ +/* +=========================================================================== +Copyright (C) 2007-2009 Robert Beckebans + +This file is part of XreaL source code. + +XreaL source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +XreaL source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with XreaL source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +// tr_vbo.c +#include "tr_local.h" + +/* +============ +R_CreateVBO +============ +*/ +VBO_t *R_CreateVBO(const char *name, byte * vertexes, int vertexesSize, vboUsage_t usage) +{ + VBO_t *vbo; + int glUsage; + + switch (usage) + { + case VBO_USAGE_STATIC: + glUsage = GL_STATIC_DRAW_ARB; + break; + + case VBO_USAGE_DYNAMIC: + glUsage = GL_DYNAMIC_DRAW_ARB; + break; + + default: + Com_Error(ERR_FATAL, "bad vboUsage_t given: %i", usage); + return NULL; + } + + if(strlen(name) >= MAX_QPATH) + { + ri.Error(ERR_DROP, "R_CreateVBO: \"%s\" is too long", name); + } + + if ( tr.numVBOs == MAX_VBOS ) { + ri.Error( ERR_DROP, "R_CreateVBO: MAX_VBOS hit"); + } + + R_IssuePendingRenderCommands(); + + vbo = tr.vbos[tr.numVBOs] = ri.Hunk_Alloc(sizeof(*vbo), h_low); + tr.numVBOs++; + + memset(vbo, 0, sizeof(*vbo)); + + Q_strncpyz(vbo->name, name, sizeof(vbo->name)); + + vbo->vertexesSize = vertexesSize; + + qglGenBuffersARB(1, &vbo->vertexesVBO); + + qglBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo->vertexesVBO); + qglBufferDataARB(GL_ARRAY_BUFFER_ARB, vertexesSize, vertexes, glUsage); + + qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + + glState.currentVBO = NULL; + + GL_CheckErrors(); + + return vbo; +} + +/* +============ +R_CreateVBO2 +============ +*/ +VBO_t *R_CreateVBO2(const char *name, int numVertexes, srfVert_t * verts, unsigned int stateBits, vboUsage_t usage) +{ + VBO_t *vbo; + int i; + + byte *data; + int dataSize; + int dataOfs; + + int glUsage; + + switch (usage) + { + case VBO_USAGE_STATIC: + glUsage = GL_STATIC_DRAW_ARB; + break; + + case VBO_USAGE_DYNAMIC: + glUsage = GL_DYNAMIC_DRAW_ARB; + break; + + default: + Com_Error(ERR_FATAL, "bad vboUsage_t given: %i", usage); + return NULL; + } + + if(!numVertexes) + return NULL; + + if(strlen(name) >= MAX_QPATH) + { + ri.Error(ERR_DROP, "R_CreateVBO2: \"%s\" is too long", name); + } + + if ( tr.numVBOs == MAX_VBOS ) { + ri.Error( ERR_DROP, "R_CreateVBO2: MAX_VBOS hit"); + } + + R_IssuePendingRenderCommands(); + + vbo = tr.vbos[tr.numVBOs] = ri.Hunk_Alloc(sizeof(*vbo), h_low); + tr.numVBOs++; + + memset(vbo, 0, sizeof(*vbo)); + + Q_strncpyz(vbo->name, name, sizeof(vbo->name)); + + if (usage == VBO_USAGE_STATIC) + { + // since these vertex attributes are never altered, interleave them + vbo->ofs_xyz = 0; + dataSize = sizeof(verts[0].xyz); + + if(stateBits & ATTR_NORMAL) + { + vbo->ofs_normal = dataSize; + dataSize += sizeof(verts[0].normal); + } + +#ifdef USE_VERT_TANGENT_SPACE + if(stateBits & ATTR_TANGENT) + { + vbo->ofs_tangent = dataSize; + dataSize += sizeof(verts[0].tangent); + } + + if(stateBits & ATTR_BITANGENT) + { + vbo->ofs_bitangent = dataSize; + dataSize += sizeof(verts[0].bitangent); + } +#endif + + if(stateBits & ATTR_TEXCOORD) + { + vbo->ofs_st = dataSize; + dataSize += sizeof(verts[0].st); + } + + if(stateBits & ATTR_LIGHTCOORD) + { + vbo->ofs_lightmap = dataSize; + dataSize += sizeof(verts[0].lightmap); + } + + if(stateBits & ATTR_COLOR) + { + vbo->ofs_vertexcolor = dataSize; + dataSize += sizeof(verts[0].vertexColors); + } + + if(stateBits & ATTR_LIGHTDIRECTION) + { + vbo->ofs_lightdir = dataSize; + dataSize += sizeof(verts[0].lightdir); + } + + vbo->stride_xyz = dataSize; + vbo->stride_normal = dataSize; +#ifdef USE_VERT_TANGENT_SPACE + vbo->stride_tangent = dataSize; + vbo->stride_bitangent = dataSize; +#endif + vbo->stride_st = dataSize; + vbo->stride_lightmap = dataSize; + vbo->stride_vertexcolor = dataSize; + vbo->stride_lightdir = dataSize; + + // create VBO + dataSize *= numVertexes; + data = ri.Hunk_AllocateTempMemory(dataSize); + dataOfs = 0; + + //ri.Printf(PRINT_ALL, "CreateVBO: %d, %d %d %d %d %d, %d %d %d %d %d\n", dataSize, vbo->ofs_xyz, vbo->ofs_normal, vbo->ofs_st, vbo->ofs_lightmap, vbo->ofs_vertexcolor, + //vbo->stride_xyz, vbo->stride_normal, vbo->stride_st, vbo->stride_lightmap, vbo->stride_vertexcolor); + + for (i = 0; i < numVertexes; i++) + { + // xyz + memcpy(data + dataOfs, &verts[i].xyz, sizeof(verts[i].xyz)); + dataOfs += sizeof(verts[i].xyz); + + // normal + if(stateBits & ATTR_NORMAL) + { + memcpy(data + dataOfs, &verts[i].normal, sizeof(verts[i].normal)); + dataOfs += sizeof(verts[i].normal); + } + +#ifdef USE_VERT_TANGENT_SPACE + // tangent + if(stateBits & ATTR_TANGENT) + { + memcpy(data + dataOfs, &verts[i].tangent, sizeof(verts[i].tangent)); + dataOfs += sizeof(verts[i].tangent); + } + + // bitangent + if(stateBits & ATTR_BITANGENT) + { + memcpy(data + dataOfs, &verts[i].bitangent, sizeof(verts[i].bitangent)); + dataOfs += sizeof(verts[i].bitangent); + } +#endif + + // vertex texcoords + if(stateBits & ATTR_TEXCOORD) + { + memcpy(data + dataOfs, &verts[i].st, sizeof(verts[i].st)); + dataOfs += sizeof(verts[i].st); + } + + // feed vertex lightmap texcoords + if(stateBits & ATTR_LIGHTCOORD) + { + memcpy(data + dataOfs, &verts[i].lightmap, sizeof(verts[i].lightmap)); + dataOfs += sizeof(verts[i].lightmap); + } + + // feed vertex colors + if(stateBits & ATTR_COLOR) + { + memcpy(data + dataOfs, &verts[i].vertexColors, sizeof(verts[i].vertexColors)); + dataOfs += sizeof(verts[i].vertexColors); + } + + // feed vertex light directions + if(stateBits & ATTR_LIGHTDIRECTION) + { + memcpy(data + dataOfs, &verts[i].lightdir, sizeof(verts[i].lightdir)); + dataOfs += sizeof(verts[i].lightdir); + } + } + } + else + { + // since these vertex attributes may be changed, put them in flat arrays + dataSize = sizeof(verts[0].xyz); + + if(stateBits & ATTR_NORMAL) + { + dataSize += sizeof(verts[0].normal); + } + +#ifdef USE_VERT_TANGENT_SPACE + if(stateBits & ATTR_TANGENT) + { + dataSize += sizeof(verts[0].tangent); + } + + if(stateBits & ATTR_BITANGENT) + { + dataSize += sizeof(verts[0].bitangent); + } +#endif + + if(stateBits & ATTR_TEXCOORD) + { + dataSize += sizeof(verts[0].st); + } + + if(stateBits & ATTR_LIGHTCOORD) + { + dataSize += sizeof(verts[0].lightmap); + } + + if(stateBits & ATTR_COLOR) + { + dataSize += sizeof(verts[0].vertexColors); + } + + if(stateBits & ATTR_LIGHTDIRECTION) + { + dataSize += sizeof(verts[0].lightdir); + } + + // create VBO + dataSize *= numVertexes; + data = ri.Hunk_AllocateTempMemory(dataSize); + dataOfs = 0; + + vbo->ofs_xyz = 0; + vbo->ofs_normal = 0; +#ifdef USE_VERT_TANGENT_SPACE + vbo->ofs_tangent = 0; + vbo->ofs_bitangent = 0; +#endif + vbo->ofs_st = 0; + vbo->ofs_lightmap = 0; + vbo->ofs_vertexcolor = 0; + vbo->ofs_lightdir = 0; + + vbo->stride_xyz = sizeof(verts[0].xyz); + vbo->stride_normal = sizeof(verts[0].normal); +#ifdef USE_VERT_TANGENT_SPACE + vbo->stride_tangent = sizeof(verts[0].tangent); + vbo->stride_bitangent = sizeof(verts[0].bitangent); +#endif + vbo->stride_vertexcolor = sizeof(verts[0].vertexColors); + vbo->stride_st = sizeof(verts[0].st); + vbo->stride_lightmap = sizeof(verts[0].lightmap); + vbo->stride_lightdir = sizeof(verts[0].lightdir); + + //ri.Printf(PRINT_ALL, "2CreateVBO: %d, %d %d %d %d %d, %d %d %d %d %d\n", dataSize, vbo->ofs_xyz, vbo->ofs_normal, vbo->ofs_st, vbo->ofs_lightmap, vbo->ofs_vertexcolor, + //vbo->stride_xyz, vbo->stride_normal, vbo->stride_st, vbo->stride_lightmap, vbo->stride_vertexcolor); + + // xyz + for (i = 0; i < numVertexes; i++) + { + memcpy(data + dataOfs, &verts[i].xyz, sizeof(verts[i].xyz)); + dataOfs += sizeof(verts[i].xyz); + } + + // normal + if(stateBits & ATTR_NORMAL) + { + vbo->ofs_normal = dataOfs; + for (i = 0; i < numVertexes; i++) + { + memcpy(data + dataOfs, &verts[i].normal, sizeof(verts[i].normal)); + dataOfs += sizeof(verts[i].normal); + } + } + +#ifdef USE_VERT_TANGENT_SPACE + // tangent + if(stateBits & ATTR_TANGENT) + { + vbo->ofs_tangent = dataOfs; + for (i = 0; i < numVertexes; i++) + { + memcpy(data + dataOfs, &verts[i].tangent, sizeof(verts[i].tangent)); + dataOfs += sizeof(verts[i].tangent); + } + } + + // bitangent + if(stateBits & ATTR_BITANGENT) + { + vbo->ofs_bitangent = dataOfs; + for (i = 0; i < numVertexes; i++) + { + memcpy(data + dataOfs, &verts[i].bitangent, sizeof(verts[i].bitangent)); + dataOfs += sizeof(verts[i].bitangent); + } + } +#endif + + // vertex texcoords + if(stateBits & ATTR_TEXCOORD) + { + vbo->ofs_st = dataOfs; + for (i = 0; i < numVertexes; i++) + { + memcpy(data + dataOfs, &verts[i].st, sizeof(verts[i].st)); + dataOfs += sizeof(verts[i].st); + } + } + + // feed vertex lightmap texcoords + if(stateBits & ATTR_LIGHTCOORD) + { + vbo->ofs_lightmap = dataOfs; + for (i = 0; i < numVertexes; i++) + { + memcpy(data + dataOfs, &verts[i].lightmap, sizeof(verts[i].lightmap)); + dataOfs += sizeof(verts[i].lightmap); + } + } + + // feed vertex colors + if(stateBits & ATTR_COLOR) + { + vbo->ofs_vertexcolor = dataOfs; + for (i = 0; i < numVertexes; i++) + { + memcpy(data + dataOfs, &verts[i].vertexColors, sizeof(verts[i].vertexColors)); + dataOfs += sizeof(verts[i].vertexColors); + } + } + + // feed vertex lightdirs + if(stateBits & ATTR_LIGHTDIRECTION) + { + vbo->ofs_lightdir = dataOfs; + for (i = 0; i < numVertexes; i++) + { + memcpy(data + dataOfs, &verts[i].lightdir, sizeof(verts[i].lightdir)); + dataOfs += sizeof(verts[i].lightdir); + } + } + } + + + vbo->vertexesSize = dataSize; + + qglGenBuffersARB(1, &vbo->vertexesVBO); + + qglBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo->vertexesVBO); + qglBufferDataARB(GL_ARRAY_BUFFER_ARB, dataSize, data, glUsage); + + qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + + glState.currentVBO = NULL; + + GL_CheckErrors(); + + ri.Hunk_FreeTempMemory(data); + + return vbo; +} + + +/* +============ +R_CreateIBO +============ +*/ +IBO_t *R_CreateIBO(const char *name, byte * indexes, int indexesSize, vboUsage_t usage) +{ + IBO_t *ibo; + int glUsage; + + switch (usage) + { + case VBO_USAGE_STATIC: + glUsage = GL_STATIC_DRAW_ARB; + break; + + case VBO_USAGE_DYNAMIC: + glUsage = GL_DYNAMIC_DRAW_ARB; + break; + + default: + Com_Error(ERR_FATAL, "bad vboUsage_t given: %i", usage); + return NULL; + } + + if(strlen(name) >= MAX_QPATH) + { + ri.Error(ERR_DROP, "R_CreateIBO: \"%s\" is too long", name); + } + + if ( tr.numIBOs == MAX_IBOS ) { + ri.Error( ERR_DROP, "R_CreateIBO: MAX_IBOS hit"); + } + + R_IssuePendingRenderCommands(); + + ibo = tr.ibos[tr.numIBOs] = ri.Hunk_Alloc(sizeof(*ibo), h_low); + tr.numIBOs++; + + Q_strncpyz(ibo->name, name, sizeof(ibo->name)); + + ibo->indexesSize = indexesSize; + + qglGenBuffersARB(1, &ibo->indexesVBO); + + qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ibo->indexesVBO); + qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indexesSize, indexes, glUsage); + + qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); + + glState.currentIBO = NULL; + + GL_CheckErrors(); + + return ibo; +} + +/* +============ +R_CreateIBO2 +============ +*/ +IBO_t *R_CreateIBO2(const char *name, int numTriangles, srfTriangle_t * triangles, vboUsage_t usage) +{ + IBO_t *ibo; + int i, j; + + byte *indexes; + int indexesSize; + int indexesOfs; + + srfTriangle_t *tri; + glIndex_t index; + int glUsage; + + switch (usage) + { + case VBO_USAGE_STATIC: + glUsage = GL_STATIC_DRAW_ARB; + break; + + case VBO_USAGE_DYNAMIC: + glUsage = GL_DYNAMIC_DRAW_ARB; + break; + + default: + Com_Error(ERR_FATAL, "bad vboUsage_t given: %i", usage); + return NULL; + } + + if(!numTriangles) + return NULL; + + if(strlen(name) >= MAX_QPATH) + { + ri.Error(ERR_DROP, "R_CreateIBO2: \"%s\" is too long", name); + } + + if ( tr.numIBOs == MAX_IBOS ) { + ri.Error( ERR_DROP, "R_CreateIBO2: MAX_IBOS hit"); + } + + R_IssuePendingRenderCommands(); + + ibo = tr.ibos[tr.numIBOs] = ri.Hunk_Alloc(sizeof(*ibo), h_low); + tr.numIBOs++; + + Q_strncpyz(ibo->name, name, sizeof(ibo->name)); + + indexesSize = numTriangles * 3 * sizeof(int); + indexes = ri.Hunk_AllocateTempMemory(indexesSize); + indexesOfs = 0; + + for(i = 0, tri = triangles; i < numTriangles; i++, tri++) + { + for(j = 0; j < 3; j++) + { + index = tri->indexes[j]; + memcpy(indexes + indexesOfs, &index, sizeof(glIndex_t)); + indexesOfs += sizeof(glIndex_t); + } + } + + ibo->indexesSize = indexesSize; + + qglGenBuffersARB(1, &ibo->indexesVBO); + + qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ibo->indexesVBO); + qglBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, indexesSize, indexes, glUsage); + + qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); + + glState.currentIBO = NULL; + + GL_CheckErrors(); + + ri.Hunk_FreeTempMemory(indexes); + + return ibo; +} + +/* +============ +R_BindVBO +============ +*/ +void R_BindVBO(VBO_t * vbo) +{ + if(!vbo) + { + //R_BindNullVBO(); + ri.Error(ERR_DROP, "R_BindNullVBO: NULL vbo"); + return; + } + + if(r_logFile->integer) + { + // don't just call LogComment, or we will get a call to va() every frame! + GLimp_LogComment(va("--- R_BindVBO( %s ) ---\n", vbo->name)); + } + + if(glState.currentVBO != vbo) + { + glState.currentVBO = vbo; + glState.vertexAttribPointersSet = 0; + + glState.vertexAttribsInterpolation = 0; + glState.vertexAttribsOldFrame = 0; + glState.vertexAttribsNewFrame = 0; + + qglBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo->vertexesVBO); + + backEnd.pc.c_vboVertexBuffers++; + } +} + +/* +============ +R_BindNullVBO +============ +*/ +void R_BindNullVBO(void) +{ + GLimp_LogComment("--- R_BindNullVBO ---\n"); + + if(glState.currentVBO) + { + qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0); + glState.currentVBO = NULL; + } + + GL_CheckErrors(); +} + +/* +============ +R_BindIBO +============ +*/ +void R_BindIBO(IBO_t * ibo) +{ + if(!ibo) + { + //R_BindNullIBO(); + ri.Error(ERR_DROP, "R_BindIBO: NULL ibo"); + return; + } + + if(r_logFile->integer) + { + // don't just call LogComment, or we will get a call to va() every frame! + GLimp_LogComment(va("--- R_BindIBO( %s ) ---\n", ibo->name)); + } + + if(glState.currentIBO != ibo) + { + qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, ibo->indexesVBO); + + glState.currentIBO = ibo; + + backEnd.pc.c_vboIndexBuffers++; + } +} + +/* +============ +R_BindNullIBO +============ +*/ +void R_BindNullIBO(void) +{ + GLimp_LogComment("--- R_BindNullIBO ---\n"); + + if(glState.currentIBO) + { + qglBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0); + glState.currentIBO = NULL; + glState.vertexAttribPointersSet = 0; + } +} + +/* +============ +R_InitVBOs +============ +*/ +void R_InitVBOs(void) +{ + int dataSize; + int offset; + + ri.Printf(PRINT_ALL, "------- R_InitVBOs -------\n"); + + tr.numVBOs = 0; + tr.numIBOs = 0; + + dataSize = sizeof(tess.xyz[0]); + dataSize += sizeof(tess.normal[0]); +#ifdef USE_VERT_TANGENT_SPACE + dataSize += sizeof(tess.tangent[0]); + dataSize += sizeof(tess.bitangent[0]); +#endif + dataSize += sizeof(tess.vertexColors[0]); + dataSize += sizeof(tess.texCoords[0][0]) * 2; + dataSize += sizeof(tess.lightdir[0]); + dataSize *= SHADER_MAX_VERTEXES; + + tess.vbo = R_CreateVBO("tessVertexArray_VBO", NULL, dataSize, VBO_USAGE_DYNAMIC); + + offset = 0; + + tess.vbo->ofs_xyz = offset; offset += sizeof(tess.xyz[0]) * SHADER_MAX_VERTEXES; + tess.vbo->ofs_normal = offset; offset += sizeof(tess.normal[0]) * SHADER_MAX_VERTEXES; +#ifdef USE_VERT_TANGENT_SPACE + tess.vbo->ofs_tangent = offset; offset += sizeof(tess.tangent[0]) * SHADER_MAX_VERTEXES; + tess.vbo->ofs_bitangent = offset; offset += sizeof(tess.bitangent[0]) * SHADER_MAX_VERTEXES; +#endif + // these next two are actually interleaved + tess.vbo->ofs_st = offset; + tess.vbo->ofs_lightmap = offset + sizeof(tess.texCoords[0][0]); + offset += sizeof(tess.texCoords[0][0]) * 2 * SHADER_MAX_VERTEXES; + + tess.vbo->ofs_vertexcolor = offset; offset += sizeof(tess.vertexColors[0]) * SHADER_MAX_VERTEXES; + tess.vbo->ofs_lightdir = offset; + + tess.vbo->stride_xyz = sizeof(tess.xyz[0]); + tess.vbo->stride_normal = sizeof(tess.normal[0]); +#ifdef USE_VERT_TANGENT_SPACE + tess.vbo->stride_tangent = sizeof(tess.tangent[0]); + tess.vbo->stride_bitangent = sizeof(tess.bitangent[0]); +#endif + tess.vbo->stride_vertexcolor = sizeof(tess.vertexColors[0]); + tess.vbo->stride_st = sizeof(tess.texCoords[0][0]) * 2; + tess.vbo->stride_lightmap = sizeof(tess.texCoords[0][0]) * 2; + tess.vbo->stride_lightdir = sizeof(tess.lightdir[0]); + + dataSize = sizeof(tess.indexes[0]) * SHADER_MAX_INDEXES; + + tess.ibo = R_CreateIBO("tessVertexArray_IBO", NULL, dataSize, VBO_USAGE_DYNAMIC); + + R_BindNullVBO(); + R_BindNullIBO(); + + GL_CheckErrors(); +} + +/* +============ +R_ShutdownVBOs +============ +*/ +void R_ShutdownVBOs(void) +{ + int i; + VBO_t *vbo; + IBO_t *ibo; + + ri.Printf(PRINT_ALL, "------- R_ShutdownVBOs -------\n"); + + R_BindNullVBO(); + R_BindNullIBO(); + + + for(i = 0; i < tr.numVBOs; i++) + { + vbo = tr.vbos[i]; + + if(vbo->vertexesVBO) + { + qglDeleteBuffersARB(1, &vbo->vertexesVBO); + } + + //ri.Free(vbo); + } + + for(i = 0; i < tr.numIBOs; i++) + { + ibo = tr.ibos[i]; + + if(ibo->indexesVBO) + { + qglDeleteBuffersARB(1, &ibo->indexesVBO); + } + + //ri.Free(ibo); + } + + tr.numVBOs = 0; + tr.numIBOs = 0; +} + +/* +============ +R_VBOList_f +============ +*/ +void R_VBOList_f(void) +{ + int i; + VBO_t *vbo; + IBO_t *ibo; + int vertexesSize = 0; + int indexesSize = 0; + + ri.Printf(PRINT_ALL, " size name\n"); + ri.Printf(PRINT_ALL, "----------------------------------------------------------\n"); + + for(i = 0; i < tr.numVBOs; i++) + { + vbo = tr.vbos[i]; + + ri.Printf(PRINT_ALL, "%d.%02d MB %s\n", vbo->vertexesSize / (1024 * 1024), + (vbo->vertexesSize % (1024 * 1024)) * 100 / (1024 * 1024), vbo->name); + + vertexesSize += vbo->vertexesSize; + } + + for(i = 0; i < tr.numIBOs; i++) + { + ibo = tr.ibos[i]; + + ri.Printf(PRINT_ALL, "%d.%02d MB %s\n", ibo->indexesSize / (1024 * 1024), + (ibo->indexesSize % (1024 * 1024)) * 100 / (1024 * 1024), ibo->name); + + indexesSize += ibo->indexesSize; + } + + ri.Printf(PRINT_ALL, " %i total VBOs\n", tr.numVBOs); + ri.Printf(PRINT_ALL, " %d.%02d MB total vertices memory\n", vertexesSize / (1024 * 1024), + (vertexesSize % (1024 * 1024)) * 100 / (1024 * 1024)); + + ri.Printf(PRINT_ALL, " %i total IBOs\n", tr.numIBOs); + ri.Printf(PRINT_ALL, " %d.%02d MB total triangle indices memory\n", indexesSize / (1024 * 1024), + (indexesSize % (1024 * 1024)) * 100 / (1024 * 1024)); +} + + +/* +============== +RB_UpdateVBOs + +Adapted from Tess_UpdateVBOs from xreal + +Tr3B: update the default VBO to replace the client side vertex arrays +============== +*/ +void RB_UpdateVBOs(unsigned int attribBits) +{ + GLimp_LogComment("--- RB_UpdateVBOs ---\n"); + + backEnd.pc.c_dynamicVboDraws++; + + // update the default VBO + if(tess.numVertexes > 0 && tess.numVertexes <= SHADER_MAX_VERTEXES) + { + R_BindVBO(tess.vbo); + + if(attribBits & ATTR_BITS) + { + if(attribBits & ATTR_POSITION) + { + //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_xyz, tess.numVertexes * sizeof(tess.xyz[0])); + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_xyz, tess.numVertexes * sizeof(tess.xyz[0]), tess.xyz); + } + + if(attribBits & ATTR_TEXCOORD || attribBits & ATTR_LIGHTCOORD) + { + // these are interleaved, so we update both if either need it + //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_st, tess.numVertexes * sizeof(tess.texCoords[0][0]) * 2); + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_st, tess.numVertexes * sizeof(tess.texCoords[0][0]) * 2, tess.texCoords); + } + + if(attribBits & ATTR_NORMAL) + { + //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_normal, tess.numVertexes * sizeof(tess.normal[0])); + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_normal, tess.numVertexes * sizeof(tess.normal[0]), tess.normal); + } + +#ifdef USE_VERT_TANGENT_SPACE + if(attribBits & ATTR_TANGENT) + { + //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_tangent, tess.numVertexes * sizeof(tess.tangent[0])); + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_tangent, tess.numVertexes * sizeof(tess.tangent[0]), tess.tangent); + } + + if(attribBits & ATTR_BITANGENT) + { + //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_bitangent, tess.numVertexes * sizeof(tess.bitangent[0])); + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_bitangent, tess.numVertexes * sizeof(tess.bitangent[0]), tess.bitangent); + } +#endif + + if(attribBits & ATTR_COLOR) + { + //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_vertexcolor, tess.numVertexes * sizeof(tess.vertexColors[0])); + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_vertexcolor, tess.numVertexes * sizeof(tess.vertexColors[0]), tess.vertexColors); + } + + if(attribBits & ATTR_LIGHTDIRECTION) + { + //ri.Printf(PRINT_ALL, "offset %d, size %d\n", tess.vbo->ofs_lightdir, tess.numVertexes * sizeof(tess.lightdir[0])); + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_lightdir, tess.numVertexes * sizeof(tess.lightdir[0]), tess.lightdir); + } + } + else + { + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_xyz, tess.numVertexes * sizeof(tess.xyz[0]), tess.xyz); + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_st, tess.numVertexes * sizeof(tess.texCoords[0][0]) * 2, tess.texCoords); + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_normal, tess.numVertexes * sizeof(tess.normal[0]), tess.normal); +#ifdef USE_VERT_TANGENT_SPACE + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_tangent, tess.numVertexes * sizeof(tess.tangent[0]), tess.tangent); + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_bitangent, tess.numVertexes * sizeof(tess.bitangent[0]), tess.bitangent); +#endif + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_vertexcolor, tess.numVertexes * sizeof(tess.vertexColors[0]), tess.vertexColors); + qglBufferSubDataARB(GL_ARRAY_BUFFER_ARB, tess.vbo->ofs_lightdir, tess.numVertexes * sizeof(tess.lightdir[0]), tess.lightdir); + } + + } + + // update the default IBO + if(tess.numIndexes > 0 && tess.numIndexes <= SHADER_MAX_INDEXES) + { + R_BindIBO(tess.ibo); + + qglBufferSubDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, 0, tess.numIndexes * sizeof(tess.indexes[0]), tess.indexes); + } +} diff --git a/src/renderergl2/tr_world.c b/src/renderergl2/tr_world.c new file mode 100644 index 00000000..950ee6a4 --- /dev/null +++ b/src/renderergl2/tr_world.c @@ -0,0 +1,850 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. + +This file is part of Quake III Arena source code. + +Quake III Arena source code is free software; you can redistribute it +and/or modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of the License, +or (at your option) any later version. + +Quake III Arena source code is distributed in the hope that it will be +useful, but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with Quake III Arena source code; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +=========================================================================== +*/ +#include "tr_local.h" + + + +/* +================ +R_CullSurface + +Tries to cull surfaces before they are lighted or +added to the sorting list. +================ +*/ +static qboolean R_CullSurface( msurface_t *surf ) { + if ( r_nocull->integer || surf->cullinfo.type == CULLINFO_NONE) { + return qfalse; + } + + if (surf->cullinfo.type & CULLINFO_PLANE) + { + // Only true for SF_FACE, so treat like its own function + float d; + cullType_t ct; + + if ( !r_facePlaneCull->integer ) { + return qfalse; + } + + ct = surf->shader->cullType; + + if (ct == CT_TWO_SIDED) + { + return qfalse; + } + + // don't cull for depth shadow + /* + if ( tr.viewParms.flags & VPF_DEPTHSHADOW ) + { + return qfalse; + } + */ + + // shadowmaps draw back surfaces + if ( tr.viewParms.flags & (VPF_SHADOWMAP | VPF_DEPTHSHADOW) ) + { + if (ct == CT_FRONT_SIDED) + { + ct = CT_BACK_SIDED; + } + else + { + ct = CT_FRONT_SIDED; + } + } + + // do proper cull for orthographic projection + if (tr.viewParms.flags & VPF_ORTHOGRAPHIC) { + d = DotProduct(tr.viewParms.or.axis[0], surf->cullinfo.plane.normal); + if ( ct == CT_FRONT_SIDED ) { + if (d > 0) + return qtrue; + } else { + if (d < 0) + return qtrue; + } + return qfalse; + } + + d = DotProduct (tr.or.viewOrigin, surf->cullinfo.plane.normal); + + // don't cull exactly on the plane, because there are levels of rounding + // through the BSP, ICD, and hardware that may cause pixel gaps if an + // epsilon isn't allowed here + if ( ct == CT_FRONT_SIDED ) { + if ( d < surf->cullinfo.plane.dist - 8 ) { + return qtrue; + } + } else { + if ( d > surf->cullinfo.plane.dist + 8 ) { + return qtrue; + } + } + + return qfalse; + } + + if (surf->cullinfo.type & CULLINFO_SPHERE) + { + int sphereCull; + + if ( tr.currentEntityNum != REFENTITYNUM_WORLD ) { + sphereCull = R_CullLocalPointAndRadius( surf->cullinfo.localOrigin, surf->cullinfo.radius ); + } else { + sphereCull = R_CullPointAndRadius( surf->cullinfo.localOrigin, surf->cullinfo.radius ); + } + + if ( sphereCull == CULL_OUT ) + { + return qtrue; + } + } + + if (surf->cullinfo.type & CULLINFO_BOX) + { + int boxCull; + + if ( tr.currentEntityNum != REFENTITYNUM_WORLD ) { + boxCull = R_CullLocalBox( surf->cullinfo.bounds ); + } else { + boxCull = R_CullBox( surf->cullinfo.bounds ); + } + + if ( boxCull == CULL_OUT ) + { + return qtrue; + } + } + + return qfalse; +} + + +/* +==================== +R_DlightSurface + +The given surface is going to be drawn, and it touches a leaf +that is touched by one or more dlights, so try to throw out +more dlights if possible. +==================== +*/ +static int R_DlightSurface( msurface_t *surf, int dlightBits ) { + float d; + int i; + dlight_t *dl; + + if ( surf->cullinfo.type & CULLINFO_PLANE ) + { + int i; + for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { + if ( ! ( dlightBits & ( 1 << i ) ) ) { + continue; + } + dl = &tr.refdef.dlights[i]; + d = DotProduct( dl->origin, surf->cullinfo.plane.normal ) - surf->cullinfo.plane.dist; + if ( d < -dl->radius || d > dl->radius ) { + // dlight doesn't reach the plane + dlightBits &= ~( 1 << i ); + } + } + } + + if ( surf->cullinfo.type & CULLINFO_BOX ) + { + for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { + if ( ! ( dlightBits & ( 1 << i ) ) ) { + continue; + } + dl = &tr.refdef.dlights[i]; + if ( dl->origin[0] - dl->radius > surf->cullinfo.bounds[1][0] + || dl->origin[0] + dl->radius < surf->cullinfo.bounds[0][0] + || dl->origin[1] - dl->radius > surf->cullinfo.bounds[1][1] + || dl->origin[1] + dl->radius < surf->cullinfo.bounds[0][1] + || dl->origin[2] - dl->radius > surf->cullinfo.bounds[1][2] + || dl->origin[2] + dl->radius < surf->cullinfo.bounds[0][2] ) { + // dlight doesn't reach the bounds + dlightBits &= ~( 1 << i ); + } + } + } + + if ( surf->cullinfo.type & CULLINFO_SPHERE ) + { + for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { + if ( ! ( dlightBits & ( 1 << i ) ) ) { + continue; + } + dl = &tr.refdef.dlights[i]; + if (!SpheresIntersect(dl->origin, dl->radius, surf->cullinfo.localOrigin, surf->cullinfo.radius)) + { + // dlight doesn't reach the bounds + dlightBits &= ~( 1 << i ); + } + } + } + + if ( *surf->data == SF_FACE ) { + ((srfSurfaceFace_t *)surf->data)->dlightBits = dlightBits; + } else if ( *surf->data == SF_GRID ) { + ((srfGridMesh_t *)surf->data)->dlightBits = dlightBits; + } else if ( *surf->data == SF_TRIANGLES ) { + ((srfTriangles_t *)surf->data)->dlightBits = dlightBits; + } else if ( *surf->data == SF_VBO_MESH ) { + ((srfVBOMesh_t *)surf->data)->dlightBits = dlightBits; + } else { + dlightBits = 0; + } + + if ( dlightBits ) { + tr.pc.c_dlightSurfaces++; + } + + return dlightBits; +} + +/* +==================== +R_PshadowSurface + +Just like R_DlightSurface, cull any we can +==================== +*/ +static int R_PshadowSurface( msurface_t *surf, int pshadowBits ) { + float d; + int i; + pshadow_t *ps; + + if ( surf->cullinfo.type & CULLINFO_PLANE ) + { + int i; + for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) { + if ( ! ( pshadowBits & ( 1 << i ) ) ) { + continue; + } + ps = &tr.refdef.pshadows[i]; + d = DotProduct( ps->lightOrigin, surf->cullinfo.plane.normal ) - surf->cullinfo.plane.dist; + if ( d < -ps->lightRadius || d > ps->lightRadius ) { + // pshadow doesn't reach the plane + pshadowBits &= ~( 1 << i ); + } + } + } + + if ( surf->cullinfo.type & CULLINFO_BOX ) + { + for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) { + if ( ! ( pshadowBits & ( 1 << i ) ) ) { + continue; + } + ps = &tr.refdef.pshadows[i]; + if ( ps->lightOrigin[0] - ps->lightRadius > surf->cullinfo.bounds[1][0] + || ps->lightOrigin[0] + ps->lightRadius < surf->cullinfo.bounds[0][0] + || ps->lightOrigin[1] - ps->lightRadius > surf->cullinfo.bounds[1][1] + || ps->lightOrigin[1] + ps->lightRadius < surf->cullinfo.bounds[0][1] + || ps->lightOrigin[2] - ps->lightRadius > surf->cullinfo.bounds[1][2] + || ps->lightOrigin[2] + ps->lightRadius < surf->cullinfo.bounds[0][2] + || BoxOnPlaneSide(surf->cullinfo.bounds[0], surf->cullinfo.bounds[1], &ps->cullPlane) == 2 ) { + // pshadow doesn't reach the bounds + pshadowBits &= ~( 1 << i ); + } + } + } + + if ( surf->cullinfo.type & CULLINFO_SPHERE ) + { + for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) { + if ( ! ( pshadowBits & ( 1 << i ) ) ) { + continue; + } + ps = &tr.refdef.pshadows[i]; + if (!SpheresIntersect(ps->viewOrigin, ps->viewRadius, surf->cullinfo.localOrigin, surf->cullinfo.radius) + || DotProduct( surf->cullinfo.localOrigin, ps->cullPlane.normal ) - ps->cullPlane.dist < -surf->cullinfo.radius) + { + // pshadow doesn't reach the bounds + pshadowBits &= ~( 1 << i ); + } + } + } + + if ( *surf->data == SF_FACE ) { + ((srfSurfaceFace_t *)surf->data)->pshadowBits = pshadowBits; + } else if ( *surf->data == SF_GRID ) { + ((srfGridMesh_t *)surf->data)->pshadowBits = pshadowBits; + } else if ( *surf->data == SF_TRIANGLES ) { + ((srfTriangles_t *)surf->data)->pshadowBits = pshadowBits; + } else if ( *surf->data == SF_VBO_MESH ) { + ((srfVBOMesh_t *)surf->data)->pshadowBits = pshadowBits; + } else { + pshadowBits = 0; + } + + if ( pshadowBits ) { + //tr.pc.c_dlightSurfaces++; + } + + return pshadowBits; +} + + +/* +====================== +R_AddWorldSurface +====================== +*/ +static void R_AddWorldSurface( msurface_t *surf, int dlightBits, int pshadowBits ) { + // FIXME: bmodel fog? + + // try to cull before dlighting or adding + if ( R_CullSurface( surf ) ) { + return; + } + + // check for dlighting + if ( dlightBits ) { + dlightBits = R_DlightSurface( surf, dlightBits ); + dlightBits = ( dlightBits != 0 ); + } + + // check for pshadows + /*if ( pshadowBits ) */{ + pshadowBits = R_PshadowSurface( surf, pshadowBits); + pshadowBits = ( pshadowBits != 0 ); + } + + R_AddDrawSurf( surf->data, surf->shader, surf->fogIndex, dlightBits, pshadowBits ); +} + +/* +============================================================= + + BRUSH MODELS + +============================================================= +*/ + +/* +================= +R_AddBrushModelSurfaces +================= +*/ +void R_AddBrushModelSurfaces ( trRefEntity_t *ent ) { + bmodel_t *bmodel; + int clip; + model_t *pModel; + int i; + + pModel = R_GetModelByHandle( ent->e.hModel ); + + bmodel = pModel->bmodel; + + clip = R_CullLocalBox( bmodel->bounds ); + if ( clip == CULL_OUT ) { + return; + } + + R_SetupEntityLighting( &tr.refdef, ent ); + R_DlightBmodel( bmodel ); + + for ( i = 0 ; i < bmodel->numSurfaces ; i++ ) { + int surf = bmodel->firstSurface + i; + + if (tr.world->surfacesViewCount[surf] != tr.viewCount) + { + tr.world->surfacesViewCount[surf] = tr.viewCount; + R_AddWorldSurface( tr.world->surfaces + surf, tr.currentEntity->needDlights, 0 ); + } + } +} + + +/* +============================================================= + + WORLD MODEL + +============================================================= +*/ + + +/* +================ +R_RecursiveWorldNode +================ +*/ +static void R_RecursiveWorldNode( mnode_t *node, int planeBits, int dlightBits, int pshadowBits ) { + + do { + int newDlights[2]; + unsigned int newPShadows[2]; + + // if the node wasn't marked as potentially visible, exit + // pvs is skipped for depth shadows + if (!(tr.viewParms.flags & VPF_DEPTHSHADOW) && node->visCounts[tr.visIndex] != tr.visCounts[tr.visIndex]) { + return; + } + + // if the bounding volume is outside the frustum, nothing + // inside can be visible OPTIMIZE: don't do this all the way to leafs? + + if ( !r_nocull->integer ) { + int r; + + if ( planeBits & 1 ) { + r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[0]); + if (r == 2) { + return; // culled + } + if ( r == 1 ) { + planeBits &= ~1; // all descendants will also be in front + } + } + + if ( planeBits & 2 ) { + r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[1]); + if (r == 2) { + return; // culled + } + if ( r == 1 ) { + planeBits &= ~2; // all descendants will also be in front + } + } + + if ( planeBits & 4 ) { + r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[2]); + if (r == 2) { + return; // culled + } + if ( r == 1 ) { + planeBits &= ~4; // all descendants will also be in front + } + } + + if ( planeBits & 8 ) { + r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[3]); + if (r == 2) { + return; // culled + } + if ( r == 1 ) { + planeBits &= ~8; // all descendants will also be in front + } + } + + if ( planeBits & 16 ) { + r = BoxOnPlaneSide(node->mins, node->maxs, &tr.viewParms.frustum[4]); + if (r == 2) { + return; // culled + } + if ( r == 1 ) { + planeBits &= ~16; // all descendants will also be in front + } + } + } + + if ( node->contents != -1 ) { + break; + } + + // node is just a decision point, so go down both sides + // since we don't care about sort orders, just go positive to negative + + // determine which dlights are needed + newDlights[0] = 0; + newDlights[1] = 0; + if ( dlightBits ) { + int i; + + for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { + dlight_t *dl; + float dist; + + if ( dlightBits & ( 1 << i ) ) { + dl = &tr.refdef.dlights[i]; + dist = DotProduct( dl->origin, node->plane->normal ) - node->plane->dist; + + if ( dist > -dl->radius ) { + newDlights[0] |= ( 1 << i ); + } + if ( dist < dl->radius ) { + newDlights[1] |= ( 1 << i ); + } + } + } + } + + newPShadows[0] = 0; + newPShadows[1] = 0; + if ( pshadowBits ) { + int i; + + for ( i = 0 ; i < tr.refdef.num_pshadows ; i++ ) { + pshadow_t *shadow; + float dist; + + if ( pshadowBits & ( 1 << i ) ) { + shadow = &tr.refdef.pshadows[i]; + dist = DotProduct( shadow->lightOrigin, node->plane->normal ) - node->plane->dist; + + if ( dist > -shadow->lightRadius ) { + newPShadows[0] |= ( 1 << i ); + } + if ( dist < shadow->lightRadius ) { + newPShadows[1] |= ( 1 << i ); + } + } + } + } + + // recurse down the children, front side first + R_RecursiveWorldNode (node->children[0], planeBits, newDlights[0], newPShadows[0] ); + + // tail recurse + node = node->children[1]; + dlightBits = newDlights[1]; + pshadowBits = newPShadows[1]; + } while ( 1 ); + + { + // leaf node, so add mark surfaces + int c; + int surf, *view; + + tr.pc.c_leafs++; + + // add to z buffer bounds + if ( node->mins[0] < tr.viewParms.visBounds[0][0] ) { + tr.viewParms.visBounds[0][0] = node->mins[0]; + } + if ( node->mins[1] < tr.viewParms.visBounds[0][1] ) { + tr.viewParms.visBounds[0][1] = node->mins[1]; + } + if ( node->mins[2] < tr.viewParms.visBounds[0][2] ) { + tr.viewParms.visBounds[0][2] = node->mins[2]; + } + + if ( node->maxs[0] > tr.viewParms.visBounds[1][0] ) { + tr.viewParms.visBounds[1][0] = node->maxs[0]; + } + if ( node->maxs[1] > tr.viewParms.visBounds[1][1] ) { + tr.viewParms.visBounds[1][1] = node->maxs[1]; + } + if ( node->maxs[2] > tr.viewParms.visBounds[1][2] ) { + tr.viewParms.visBounds[1][2] = node->maxs[2]; + } + + // add merged and unmerged surfaces + if (tr.world->viewSurfaces) + view = tr.world->viewSurfaces + node->firstmarksurface; + else + view = tr.world->marksurfaces + node->firstmarksurface; + + c = node->nummarksurfaces; + while (c--) { + // just mark it as visible, so we don't jump out of the cache derefencing the surface + surf = *view; + if (surf < 0) + { + if (tr.world->mergedSurfacesViewCount[-surf - 1] != tr.viewCount) + { + tr.world->mergedSurfacesViewCount[-surf - 1] = tr.viewCount; + tr.world->mergedSurfacesDlightBits[-surf - 1] = dlightBits; + tr.world->mergedSurfacesPshadowBits[-surf - 1] = pshadowBits; + } + else + { + tr.world->mergedSurfacesDlightBits[-surf - 1] |= dlightBits; + tr.world->mergedSurfacesPshadowBits[-surf - 1] |= pshadowBits; + } + } + else + { + if (tr.world->surfacesViewCount[surf] != tr.viewCount) + { + tr.world->surfacesViewCount[surf] = tr.viewCount; + tr.world->surfacesDlightBits[surf] = dlightBits; + tr.world->surfacesPshadowBits[surf] = pshadowBits; + } + else + { + tr.world->surfacesDlightBits[surf] |= dlightBits; + tr.world->surfacesPshadowBits[surf] |= pshadowBits; + } + } + view++; + } + } + +} + + +/* +=============== +R_PointInLeaf +=============== +*/ +static mnode_t *R_PointInLeaf( const vec3_t p ) { + mnode_t *node; + float d; + cplane_t *plane; + + if ( !tr.world ) { + ri.Error (ERR_DROP, "R_PointInLeaf: bad model"); + } + + node = tr.world->nodes; + while( 1 ) { + if (node->contents != -1) { + break; + } + plane = node->plane; + d = DotProduct (p,plane->normal) - plane->dist; + if (d > 0) { + node = node->children[0]; + } else { + node = node->children[1]; + } + } + + return node; +} + +/* +============== +R_ClusterPVS +============== +*/ +static const byte *R_ClusterPVS (int cluster) { + if (!tr.world || !tr.world->vis || cluster < 0 || cluster >= tr.world->numClusters ) { + return tr.world->novis; + } + + return tr.world->vis + cluster * tr.world->clusterBytes; +} + +/* +================= +R_inPVS +================= +*/ +qboolean R_inPVS( const vec3_t p1, const vec3_t p2 ) { + mnode_t *leaf; + byte *vis; + + leaf = R_PointInLeaf( p1 ); + vis = ri.CM_ClusterPVS( leaf->cluster ); // why not R_ClusterPVS ?? + leaf = R_PointInLeaf( p2 ); + + if ( !(vis[leaf->cluster>>3] & (1<<(leaf->cluster&7))) ) { + return qfalse; + } + return qtrue; +} + +/* +=============== +R_MarkLeaves + +Mark the leaves and nodes that are in the PVS for the current +cluster +=============== +*/ +static void R_MarkLeaves (void) { + const byte *vis; + mnode_t *leaf, *parent; + int i; + int cluster; + + // lockpvs lets designers walk around to determine the + // extent of the current pvs + if ( r_lockpvs->integer ) { + return; + } + + // current viewcluster + leaf = R_PointInLeaf( tr.viewParms.pvsOrigin ); + cluster = leaf->cluster; + + // if the cluster is the same and the area visibility matrix + // hasn't changed, we don't need to mark everything again + + for(i = 0; i < MAX_VISCOUNTS; i++) + { + if(tr.visClusters[i] == cluster) + { + //tr.visIndex = i; + break; + } + } + + // if r_showcluster was just turned on, remark everything + if(i != MAX_VISCOUNTS && !tr.refdef.areamaskModified && !r_showcluster->modified)// && !r_dynamicBspOcclusionCulling->modified) + { + if(tr.visClusters[i] != tr.visClusters[tr.visIndex] && r_showcluster->integer) + { + ri.Printf(PRINT_ALL, "found cluster:%i area:%i index:%i\n", cluster, leaf->area, i); + } + tr.visIndex = i; + return; + } + + // if the areamask was modified, invalidate all visclusters + // this caused doors to open into undrawn areas + if (tr.refdef.areamaskModified) + { + memset(tr.visClusters, -2, sizeof(tr.visClusters)); + } + + tr.visIndex = (tr.visIndex + 1) % MAX_VISCOUNTS; + tr.visCounts[tr.visIndex]++; + tr.visClusters[tr.visIndex] = cluster; + + if ( r_showcluster->modified || r_showcluster->integer ) { + r_showcluster->modified = qfalse; + if ( r_showcluster->integer ) { + ri.Printf( PRINT_ALL, "cluster:%i area:%i\n", cluster, leaf->area ); + } + } + + // set all nodes to visible if there is no vis + // this caused some levels to simply not render + if (r_novis->integer || !tr.world->vis || tr.visClusters[tr.visIndex] == -1) { + for (i=0 ; inumnodes ; i++) { + if (tr.world->nodes[i].contents != CONTENTS_SOLID) { + tr.world->nodes[i].visCounts[tr.visIndex] = tr.visCounts[tr.visIndex]; + } + } + return; + } + + vis = R_ClusterPVS(tr.visClusters[tr.visIndex]); + + for (i=0,leaf=tr.world->nodes ; inumnodes ; i++, leaf++) { + cluster = leaf->cluster; + if ( cluster < 0 || cluster >= tr.world->numClusters ) { + continue; + } + + // check general pvs + if ( !(vis[cluster>>3] & (1<<(cluster&7))) ) { + continue; + } + + // check for door connection + if ( (tr.refdef.areamask[leaf->area>>3] & (1<<(leaf->area&7)) ) ) { + continue; // not visible + } + + parent = leaf; + do { + if(parent->visCounts[tr.visIndex] == tr.visCounts[tr.visIndex]) + break; + parent->visCounts[tr.visIndex] = tr.visCounts[tr.visIndex]; + parent = parent->parent; + } while (parent); + } +} + + +/* +============= +R_AddWorldSurfaces +============= +*/ +void R_AddWorldSurfaces (void) { + int planeBits, dlightBits, pshadowBits; + + if ( !r_drawworld->integer ) { + return; + } + + if ( tr.refdef.rdflags & RDF_NOWORLDMODEL ) { + return; + } + + tr.currentEntityNum = REFENTITYNUM_WORLD; + tr.shiftedEntityNum = tr.currentEntityNum << QSORT_REFENTITYNUM_SHIFT; + + // determine which leaves are in the PVS / areamask + if (!(tr.viewParms.flags & VPF_DEPTHSHADOW)) + R_MarkLeaves (); + + // clear out the visible min/max + ClearBounds( tr.viewParms.visBounds[0], tr.viewParms.visBounds[1] ); + + // perform frustum culling and flag all the potentially visible surfaces + if ( tr.refdef.num_dlights > 32 ) { + tr.refdef.num_dlights = 32 ; + } + + if ( tr.refdef.num_pshadows > 32 ) { + tr.refdef.num_pshadows = 32 ; + } + + planeBits = (tr.viewParms.flags & VPF_FARPLANEFRUSTUM) ? 31 : 15; + + if ( tr.viewParms.flags & VPF_DEPTHSHADOW ) + { + dlightBits = 0; + pshadowBits = 0; + } + else if ( !(tr.viewParms.flags & VPF_SHADOWMAP) ) + { + dlightBits = ( 1 << tr.refdef.num_dlights ) - 1; + pshadowBits = ( 1 << tr.refdef.num_pshadows ) - 1; + } + else + { + dlightBits = ( 1 << tr.refdef.num_dlights ) - 1; + pshadowBits = 0; + } + + R_RecursiveWorldNode( tr.world->nodes, planeBits, dlightBits, pshadowBits); + + // now add all the potentially visible surfaces + // also mask invisible dlights for next frame + { + int i; + + tr.refdef.dlightMask = 0; + + for (i = 0; i < tr.world->numWorldSurfaces; i++) + { + if (tr.world->surfacesViewCount[i] != tr.viewCount) + continue; + + R_AddWorldSurface( tr.world->surfaces + i, tr.world->surfacesDlightBits[i], tr.world->surfacesPshadowBits[i] ); + tr.refdef.dlightMask |= tr.world->surfacesDlightBits[i]; + } + for (i = 0; i < tr.world->numMergedSurfaces; i++) + { + if (tr.world->mergedSurfacesViewCount[i] != tr.viewCount) + continue; + + R_AddWorldSurface( tr.world->mergedSurfaces + i, tr.world->mergedSurfacesDlightBits[i], tr.world->mergedSurfacesPshadowBits[i] ); + tr.refdef.dlightMask |= tr.world->mergedSurfacesDlightBits[i]; + } + + tr.refdef.dlightMask = ~tr.refdef.dlightMask; + } +} diff --git a/src/sdl/sdl_gamma.c b/src/sdl/sdl_gamma.c index 53dc4026..c341746a 100644 --- a/src/sdl/sdl_gamma.c +++ b/src/sdl/sdl_gamma.c @@ -27,7 +27,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # include #endif -#include "../renderer/tr_local.h" +#include "../renderergl1/tr_local.h" #include "../qcommon/qcommon.h" /* diff --git a/src/sdl/sdl_glimp.c b/src/sdl/sdl_glimp.c index f9c2ce87..4cc0afa6 100644 --- a/src/sdl/sdl_glimp.c +++ b/src/sdl/sdl_glimp.c @@ -32,7 +32,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include -#include "../renderer/tr_local.h" +//FIXME: factor out the bits that are actually needed here into a renderercommon header +#include "../renderergl1/tr_local.h" #include "../sys/sys_local.h" #include "sdl_icon.h" -- cgit